]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
ks8*/ksz8*: Move the Micrel drivers
authorJeff Kirsher <jeffrey.t.kirsher@intel.com>
Sat, 11 Jun 2011 10:26:31 +0000 (03:26 -0700)
committerJeff Kirsher <jeffrey.t.kirsher@intel.com>
Fri, 12 Aug 2011 07:22:18 +0000 (00:22 -0700)
Move the Micrel drivers into drivers/net/ethernet/micrel/ and
make the necessary Kconfig and Makefile changes.

CC: Ben Dooks <ben@simtec.co.uk>
CC: Tristram Ha <Tristram.Ha@micrel.com>
Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
22 files changed:
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/arm/Kconfig
drivers/net/arm/Makefile
drivers/net/arm/ks8695net.c [deleted file]
drivers/net/arm/ks8695net.h [deleted file]
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/micrel/Kconfig [new file with mode: 0644]
drivers/net/ethernet/micrel/Makefile [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8695net.c [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8695net.h [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8842.c [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8851.c [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8851.h [new file with mode: 0644]
drivers/net/ethernet/micrel/ks8851_mll.c [new file with mode: 0644]
drivers/net/ethernet/micrel/ksz884x.c [new file with mode: 0644]
drivers/net/ks8842.c [deleted file]
drivers/net/ks8851.c [deleted file]
drivers/net/ks8851.h [deleted file]
drivers/net/ks8851_mll.c [deleted file]
drivers/net/ksz884x.c [deleted file]

index b645a73fd21274e0ee2024a2f88ce137ea28f518..a6edd3546fe92839ed7cd6d4499e420220510cee 100644 (file)
@@ -467,17 +467,6 @@ config ADAPTEC_STARFIRE
          To compile this driver as a module, choose M here: the module
          will be called starfire.  This is recommended.
 
-config KSZ884X_PCI
-       tristate "Micrel KSZ8841/2 PCI"
-       depends on NET_PCI && PCI
-       select MII
-       select CRC32
-       help
-         This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip.
-
-         To compile this driver as a module, choose M here. The module
-         will be called ksz884x.
-
 config FORCEDETH
        tristate "nForce Ethernet support"
        depends on NET_PCI && PCI
@@ -550,30 +539,6 @@ config TLAN
 
          Please email feedback to <torben.mathiasen@compaq.com>.
 
-config KS8842
-       tristate "Micrel KSZ8841/42 with generic bus interface"
-       depends on HAS_IOMEM && DMA_ENGINE
-       help
-         This platform driver is for KSZ8841(1-port) / KS8842(2-port)
-         ethernet switch chip (managed, VLAN, QoS) from Micrel or
-         Timberdale(FPGA).
-
-config KS8851
-       tristate "Micrel KS8851 SPI"
-       depends on SPI
-       select MII
-       select CRC32
-       help
-         SPI driver for Micrel KS8851 SPI attached network chip.
-
-config KS8851_MLL
-       tristate "Micrel KS8851 MLL"
-       depends on HAS_IOMEM
-       select MII
-       help
-         This platform driver is for Micrel KS8851 Address/data bus
-         multiplexed network chip.
-
 config CPMAC
        tristate "TI AR7 CPMAC Ethernet support (EXPERIMENTAL)"
        depends on NET_ETHERNET && EXPERIMENTAL && AR7
index 39fe73033a029c2e736fa1540a764cafb52efd83..e448e6ed5918f5ce22b820ef421e151c525b6822 100644 (file)
@@ -36,10 +36,6 @@ gelic_wireless-$(CONFIG_GELIC_WIRELESS) += ps3_gelic_wireless.o
 ps3_gelic-objs += ps3_gelic_net.o $(gelic_wireless-y)
 obj-$(CONFIG_TC35815) += tc35815.o
 obj-$(CONFIG_SKFP) += skfp/
-obj-$(CONFIG_KS8842)   += ks8842.o
-obj-$(CONFIG_KS8851)   += ks8851.o
-obj-$(CONFIG_KS8851_MLL)       += ks8851_mll.o
-obj-$(CONFIG_KSZ884X_PCI)      += ksz884x.o
 obj-$(CONFIG_ADAPTEC_STARFIRE) += starfire.o
 obj-$(CONFIG_RIONET) += rionet.o
 obj-$(CONFIG_SH_ETH) += sh_eth.o
index b6f7302ccc64b0de633972f3749e6d26fa9ffce2..4f748ccdefe2497176f01cdcdbf788fbf0d56e34 100644 (file)
@@ -11,14 +11,6 @@ config ARM_AT91_ETHER
          If you wish to compile a kernel for the AT91RM9200 and enable
          ethernet support, then you should always answer Y to this.
 
-config ARM_KS8695_ETHER
-       tristate "KS8695 Ethernet support"
-       depends on ARM && ARCH_KS8695
-       select MII
-       help
-         If you wish to compile a kernel for the KS8695 and want to
-         use the internal ethernet then you should answer Y to this.
-
 config EP93XX_ETH
        tristate "EP93xx Ethernet support"
        depends on ARM && ARCH_EP93XX
index a2532e6d7df5f6d5d8ed982bd40feac896b0f46a..316b06c94af57a753a63b206ecc0ac3c22ee1cb0 100644 (file)
@@ -4,6 +4,5 @@
 #
 
 obj-$(CONFIG_ARM_AT91_ETHER)   += at91_ether.o
-obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
 obj-$(CONFIG_EP93XX_ETH)       += ep93xx_eth.o
 obj-$(CONFIG_W90P910_ETH)      += w90p910_ether.o
diff --git a/drivers/net/arm/ks8695net.c b/drivers/net/arm/ks8695net.c
deleted file mode 100644 (file)
index c827a60..0000000
+++ /dev/null
@@ -1,1656 +0,0 @@
-/*
- * Micrel KS8695 (Centaur) Ethernet.
- *
- * 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.
- *
- * Copyright 2008 Simtec Electronics
- *               Daniel Silverstone <dsilvers@simtec.co.uk>
- *               Vincent Sanders <vince@simtec.co.uk>
- */
-
-#include <linux/dma-mapping.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-#include <linux/ethtool.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-#include <linux/slab.h>
-
-#include <asm/irq.h>
-
-#include <mach/regs-switch.h>
-#include <mach/regs-misc.h>
-#include <asm/mach/irq.h>
-#include <mach/regs-irq.h>
-
-#include "ks8695net.h"
-
-#define MODULENAME     "ks8695_ether"
-#define MODULEVERSION  "1.02"
-
-/*
- * Transmit and device reset timeout, default 5 seconds.
- */
-static int watchdog = 5000;
-
-/* Hardware structures */
-
-/**
- *     struct rx_ring_desc - Receive descriptor ring element
- *     @status: The status of the descriptor element (E.g. who owns it)
- *     @length: The number of bytes in the block pointed to by data_ptr
- *     @data_ptr: The physical address of the data block to receive into
- *     @next_desc: The physical address of the next descriptor element.
- */
-struct rx_ring_desc {
-       __le32  status;
-       __le32  length;
-       __le32  data_ptr;
-       __le32  next_desc;
-};
-
-/**
- *     struct tx_ring_desc - Transmit descriptor ring element
- *     @owner: Who owns the descriptor
- *     @status: The number of bytes in the block pointed to by data_ptr
- *     @data_ptr: The physical address of the data block to receive into
- *     @next_desc: The physical address of the next descriptor element.
- */
-struct tx_ring_desc {
-       __le32  owner;
-       __le32  status;
-       __le32  data_ptr;
-       __le32  next_desc;
-};
-
-/**
- *     struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
- *     @skb: The buffer in the ring
- *     @dma_ptr: The mapped DMA pointer of the buffer
- *     @length: The number of bytes mapped to dma_ptr
- */
-struct ks8695_skbuff {
-       struct sk_buff  *skb;
-       dma_addr_t      dma_ptr;
-       u32             length;
-};
-
-/* Private device structure */
-
-#define MAX_TX_DESC 8
-#define MAX_TX_DESC_MASK 0x7
-#define MAX_RX_DESC 16
-#define MAX_RX_DESC_MASK 0xf
-
-/*napi_weight have better more than rx DMA buffers*/
-#define NAPI_WEIGHT   64
-
-#define MAX_RXBUF_SIZE 0x700
-
-#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
-#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
-#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
-
-/**
- *     enum ks8695_dtype - Device type
- *     @KS8695_DTYPE_WAN: This device is a WAN interface
- *     @KS8695_DTYPE_LAN: This device is a LAN interface
- *     @KS8695_DTYPE_HPNA: This device is an HPNA interface
- */
-enum ks8695_dtype {
-       KS8695_DTYPE_WAN,
-       KS8695_DTYPE_LAN,
-       KS8695_DTYPE_HPNA,
-};
-
-/**
- *     struct ks8695_priv - Private data for the KS8695 Ethernet
- *     @in_suspend: Flag to indicate if we're suspending/resuming
- *     @ndev: The net_device for this interface
- *     @dev: The platform device object for this interface
- *     @dtype: The type of this device
- *     @io_regs: The ioremapped registers for this interface
- *      @napi : Add support NAPI for Rx
- *     @rx_irq_name: The textual name of the RX IRQ from the platform data
- *     @tx_irq_name: The textual name of the TX IRQ from the platform data
- *     @link_irq_name: The textual name of the link IRQ from the
- *                     platform data if available
- *     @rx_irq: The IRQ number for the RX IRQ
- *     @tx_irq: The IRQ number for the TX IRQ
- *     @link_irq: The IRQ number for the link IRQ if available
- *     @regs_req: The resource request for the registers region
- *     @phyiface_req: The resource request for the phy/switch region
- *                    if available
- *     @phyiface_regs: The ioremapped registers for the phy/switch if available
- *     @ring_base: The base pointer of the dma coherent memory for the rings
- *     @ring_base_dma: The DMA mapped equivalent of ring_base
- *     @tx_ring: The pointer in ring_base of the TX ring
- *     @tx_ring_used: The number of slots in the TX ring which are occupied
- *     @tx_ring_next_slot: The next slot to fill in the TX ring
- *     @tx_ring_dma: The DMA mapped equivalent of tx_ring
- *     @tx_buffers: The sk_buff mappings for the TX ring
- *     @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
- *     @rx_ring: The pointer in ring_base of the RX ring
- *     @rx_ring_dma: The DMA mapped equivalent of rx_ring
- *     @rx_buffers: The sk_buff mappings for the RX ring
- *     @next_rx_desc_read: The next RX descriptor to read from on IRQ
- *      @rx_lock: A lock to protect Rx irq function
- *     @msg_enable: The flags for which messages to emit
- */
-struct ks8695_priv {
-       int in_suspend;
-       struct net_device *ndev;
-       struct device *dev;
-       enum ks8695_dtype dtype;
-       void __iomem *io_regs;
-
-       struct napi_struct      napi;
-
-       const char *rx_irq_name, *tx_irq_name, *link_irq_name;
-       int rx_irq, tx_irq, link_irq;
-
-       struct resource *regs_req, *phyiface_req;
-       void __iomem *phyiface_regs;
-
-       void *ring_base;
-       dma_addr_t ring_base_dma;
-
-       struct tx_ring_desc *tx_ring;
-       int tx_ring_used;
-       int tx_ring_next_slot;
-       dma_addr_t tx_ring_dma;
-       struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
-       spinlock_t txq_lock;
-
-       struct rx_ring_desc *rx_ring;
-       dma_addr_t rx_ring_dma;
-       struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
-       int next_rx_desc_read;
-       spinlock_t rx_lock;
-
-       int msg_enable;
-};
-
-/* Register access */
-
-/**
- *     ks8695_readreg - Read from a KS8695 ethernet register
- *     @ksp: The device to read from
- *     @reg: The register to read
- */
-static inline u32
-ks8695_readreg(struct ks8695_priv *ksp, int reg)
-{
-       return readl(ksp->io_regs + reg);
-}
-
-/**
- *     ks8695_writereg - Write to a KS8695 ethernet register
- *     @ksp: The device to write to
- *     @reg: The register to write
- *     @value: The value to write to the register
- */
-static inline void
-ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
-{
-       writel(value, ksp->io_regs + reg);
-}
-
-/* Utility functions */
-
-/**
- *     ks8695_port_type - Retrieve port-type as user-friendly string
- *     @ksp: The device to return the type for
- *
- *     Returns a string indicating which of the WAN, LAN or HPNA
- *     ports this device is likely to represent.
- */
-static const char *
-ks8695_port_type(struct ks8695_priv *ksp)
-{
-       switch (ksp->dtype) {
-       case KS8695_DTYPE_LAN:
-               return "LAN";
-       case KS8695_DTYPE_WAN:
-               return "WAN";
-       case KS8695_DTYPE_HPNA:
-               return "HPNA";
-       }
-
-       return "UNKNOWN";
-}
-
-/**
- *     ks8695_update_mac - Update the MAC registers in the device
- *     @ksp: The device to update
- *
- *     Updates the MAC registers in the KS8695 device from the address in the
- *     net_device structure associated with this interface.
- */
-static void
-ks8695_update_mac(struct ks8695_priv *ksp)
-{
-       /* Update the HW with the MAC from the net_device */
-       struct net_device *ndev = ksp->ndev;
-       u32 machigh, maclow;
-
-       maclow  = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
-                  (ndev->dev_addr[4] <<  8) | (ndev->dev_addr[5] <<  0));
-       machigh = ((ndev->dev_addr[0] <<  8) | (ndev->dev_addr[1] <<  0));
-
-       ks8695_writereg(ksp, KS8695_MAL, maclow);
-       ks8695_writereg(ksp, KS8695_MAH, machigh);
-
-}
-
-/**
- *     ks8695_refill_rxbuffers - Re-fill the RX buffer ring
- *     @ksp: The device to refill
- *
- *     Iterates the RX ring of the device looking for empty slots.
- *     For each empty slot, we allocate and map a new SKB and give it
- *     to the hardware.
- *     This can be called from interrupt context safely.
- */
-static void
-ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
-{
-       /* Run around the RX ring, filling in any missing sk_buff's */
-       int buff_n;
-
-       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
-               if (!ksp->rx_buffers[buff_n].skb) {
-                       struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
-                       dma_addr_t mapping;
-
-                       ksp->rx_buffers[buff_n].skb = skb;
-                       if (skb == NULL) {
-                               /* Failed to allocate one, perhaps
-                                * we'll try again later.
-                                */
-                               break;
-                       }
-
-                       mapping = dma_map_single(ksp->dev, skb->data,
-                                                MAX_RXBUF_SIZE,
-                                                DMA_FROM_DEVICE);
-                       if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
-                               /* Failed to DMA map this SKB, try later */
-                               dev_kfree_skb_irq(skb);
-                               ksp->rx_buffers[buff_n].skb = NULL;
-                               break;
-                       }
-                       ksp->rx_buffers[buff_n].dma_ptr = mapping;
-                       skb->dev = ksp->ndev;
-                       ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
-
-                       /* Record this into the DMA ring */
-                       ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
-                       ksp->rx_ring[buff_n].length =
-                               cpu_to_le32(MAX_RXBUF_SIZE);
-
-                       wmb();
-
-                       /* And give ownership over to the hardware */
-                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
-               }
-       }
-}
-
-/* Maximum number of multicast addresses which the KS8695 HW supports */
-#define KS8695_NR_ADDRESSES    16
-
-/**
- *     ks8695_init_partial_multicast - Init the mcast addr registers
- *     @ksp: The device to initialise
- *     @addr: The multicast address list to use
- *     @nr_addr: The number of addresses in the list
- *
- *     This routine is a helper for ks8695_set_multicast - it writes
- *     the additional-address registers in the KS8695 ethernet device
- *     and cleans up any others left behind.
- */
-static void
-ks8695_init_partial_multicast(struct ks8695_priv *ksp,
-                             struct net_device *ndev)
-{
-       u32 low, high;
-       int i;
-       struct netdev_hw_addr *ha;
-
-       i = 0;
-       netdev_for_each_mc_addr(ha, ndev) {
-               /* Ran out of space in chip? */
-               BUG_ON(i == KS8695_NR_ADDRESSES);
-
-               low = (ha->addr[2] << 24) | (ha->addr[3] << 16) |
-                     (ha->addr[4] << 8) | (ha->addr[5]);
-               high = (ha->addr[0] << 8) | (ha->addr[1]);
-
-               ks8695_writereg(ksp, KS8695_AAL_(i), low);
-               ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
-               i++;
-       }
-
-       /* Clear the remaining Additional Station Addresses */
-       for (; i < KS8695_NR_ADDRESSES; i++) {
-               ks8695_writereg(ksp, KS8695_AAL_(i), 0);
-               ks8695_writereg(ksp, KS8695_AAH_(i), 0);
-       }
-}
-
-/* Interrupt handling */
-
-/**
- *     ks8695_tx_irq - Transmit IRQ handler
- *     @irq: The IRQ which went off (ignored)
- *     @dev_id: The net_device for the interrupt
- *
- *     Process the TX ring, clearing out any transmitted slots.
- *     Allows the net_device to pass us new packets once slots are
- *     freed.
- */
-static irqreturn_t
-ks8695_tx_irq(int irq, void *dev_id)
-{
-       struct net_device *ndev = (struct net_device *)dev_id;
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       int buff_n;
-
-       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
-               if (ksp->tx_buffers[buff_n].skb &&
-                   !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
-                       rmb();
-                       /* An SKB which is not owned by HW is present */
-                       /* Update the stats for the net_device */
-                       ndev->stats.tx_packets++;
-                       ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
-
-                       /* Free the packet from the ring */
-                       ksp->tx_ring[buff_n].data_ptr = 0;
-
-                       /* Free the sk_buff */
-                       dma_unmap_single(ksp->dev,
-                                        ksp->tx_buffers[buff_n].dma_ptr,
-                                        ksp->tx_buffers[buff_n].length,
-                                        DMA_TO_DEVICE);
-                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
-                       ksp->tx_buffers[buff_n].skb = NULL;
-                       ksp->tx_ring_used--;
-               }
-       }
-
-       netif_wake_queue(ndev);
-
-       return IRQ_HANDLED;
-}
-
-/**
- *     ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit
- *     @ksp: Private data for the KS8695 Ethernet
- *
- *    For KS8695 document:
- *    Interrupt Enable Register (offset 0xE204)
- *        Bit29 : WAN MAC Receive Interrupt Enable
- *        Bit16 : LAN MAC Receive Interrupt Enable
- *    Interrupt Status Register (Offset 0xF208)
- *        Bit29: WAN MAC Receive Status
- *        Bit16: LAN MAC Receive Status
- *    So, this Rx interrrupt enable/status bit number is equal
- *    as Rx IRQ number.
- */
-static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
-{
-       return ksp->rx_irq;
-}
-
-/**
- *     ks8695_rx_irq - Receive IRQ handler
- *     @irq: The IRQ which went off (ignored)
- *     @dev_id: The net_device for the interrupt
- *
- *     Inform NAPI that packet reception needs to be scheduled
- */
-
-static irqreturn_t
-ks8695_rx_irq(int irq, void *dev_id)
-{
-       struct net_device *ndev = (struct net_device *)dev_id;
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       spin_lock(&ksp->rx_lock);
-
-       if (napi_schedule_prep(&ksp->napi)) {
-               unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN);
-               unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
-               /*disable rx interrupt*/
-               status &= ~mask_bit;
-               writel(status , KS8695_IRQ_VA + KS8695_INTEN);
-               __napi_schedule(&ksp->napi);
-       }
-
-       spin_unlock(&ksp->rx_lock);
-       return IRQ_HANDLED;
-}
-
-/**
- *     ks8695_rx - Receive packets called by NAPI poll method
- *     @ksp: Private data for the KS8695 Ethernet
- *     @budget: Number of packets allowed to process
- */
-static int ks8695_rx(struct ks8695_priv *ksp, int budget)
-{
-       struct net_device *ndev = ksp->ndev;
-       struct sk_buff *skb;
-       int buff_n;
-       u32 flags;
-       int pktlen;
-       int received = 0;
-
-       buff_n = ksp->next_rx_desc_read;
-       while (received < budget
-                       && ksp->rx_buffers[buff_n].skb
-                       && (!(ksp->rx_ring[buff_n].status &
-                                       cpu_to_le32(RDES_OWN)))) {
-                       rmb();
-                       flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
-
-                       /* Found an SKB which we own, this means we
-                        * received a packet
-                        */
-                       if ((flags & (RDES_FS | RDES_LS)) !=
-                           (RDES_FS | RDES_LS)) {
-                               /* This packet is not the first and
-                                * the last segment.  Therefore it is
-                                * a "spanning" packet and we can't
-                                * handle it
-                                */
-                               goto rx_failure;
-                       }
-
-                       if (flags & (RDES_ES | RDES_RE)) {
-                               /* It's an error packet */
-                               ndev->stats.rx_errors++;
-                               if (flags & RDES_TL)
-                                       ndev->stats.rx_length_errors++;
-                               if (flags & RDES_RF)
-                                       ndev->stats.rx_length_errors++;
-                               if (flags & RDES_CE)
-                                       ndev->stats.rx_crc_errors++;
-                               if (flags & RDES_RE)
-                                       ndev->stats.rx_missed_errors++;
-
-                               goto rx_failure;
-                       }
-
-                       pktlen = flags & RDES_FLEN;
-                       pktlen -= 4; /* Drop the CRC */
-
-                       /* Retrieve the sk_buff */
-                       skb = ksp->rx_buffers[buff_n].skb;
-
-                       /* Clear it from the ring */
-                       ksp->rx_buffers[buff_n].skb = NULL;
-                       ksp->rx_ring[buff_n].data_ptr = 0;
-
-                       /* Unmap the SKB */
-                       dma_unmap_single(ksp->dev,
-                                        ksp->rx_buffers[buff_n].dma_ptr,
-                                        ksp->rx_buffers[buff_n].length,
-                                        DMA_FROM_DEVICE);
-
-                       /* Relinquish the SKB to the network layer */
-                       skb_put(skb, pktlen);
-                       skb->protocol = eth_type_trans(skb, ndev);
-                       netif_receive_skb(skb);
-
-                       /* Record stats */
-                       ndev->stats.rx_packets++;
-                       ndev->stats.rx_bytes += pktlen;
-                       goto rx_finished;
-
-rx_failure:
-                       /* This ring entry is an error, but we can
-                        * re-use the skb
-                        */
-                       /* Give the ring entry back to the hardware */
-                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
-rx_finished:
-                       received++;
-                       buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
-       }
-
-       /* And note which RX descriptor we last did */
-       ksp->next_rx_desc_read = buff_n;
-
-       /* And refill the buffers */
-       ks8695_refill_rxbuffers(ksp);
-
-       /* Kick the RX DMA engine, in case it became suspended */
-       ks8695_writereg(ksp, KS8695_DRSC, 0);
-
-       return received;
-}
-
-
-/**
- *     ks8695_poll - Receive packet by NAPI poll method
- *     @ksp: Private data for the KS8695 Ethernet
- *     @budget: The remaining number packets for network subsystem
- *
- *     Invoked by the network core when it requests for new
- *     packets from the driver
- */
-static int ks8695_poll(struct napi_struct *napi, int budget)
-{
-       struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
-       unsigned long  work_done;
-
-       unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
-       unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
-
-       work_done = ks8695_rx(ksp, budget);
-
-       if (work_done < budget) {
-               unsigned long flags;
-               spin_lock_irqsave(&ksp->rx_lock, flags);
-               __napi_complete(napi);
-               /*enable rx interrupt*/
-               writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
-               spin_unlock_irqrestore(&ksp->rx_lock, flags);
-       }
-       return work_done;
-}
-
-/**
- *     ks8695_link_irq - Link change IRQ handler
- *     @irq: The IRQ which went off (ignored)
- *     @dev_id: The net_device for the interrupt
- *
- *     The WAN interface can generate an IRQ when the link changes,
- *     report this to the net layer and the user.
- */
-static irqreturn_t
-ks8695_link_irq(int irq, void *dev_id)
-{
-       struct net_device *ndev = (struct net_device *)dev_id;
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-       if (ctrl & WMC_WLS) {
-               netif_carrier_on(ndev);
-               if (netif_msg_link(ksp))
-                       dev_info(ksp->dev,
-                                "%s: Link is now up (10%sMbps/%s-duplex)\n",
-                                ndev->name,
-                                (ctrl & WMC_WSS) ? "0" : "",
-                                (ctrl & WMC_WDS) ? "Full" : "Half");
-       } else {
-               netif_carrier_off(ndev);
-               if (netif_msg_link(ksp))
-                       dev_info(ksp->dev, "%s: Link is now down.\n",
-                                ndev->name);
-       }
-
-       return IRQ_HANDLED;
-}
-
-
-/* KS8695 Device functions */
-
-/**
- *     ks8695_reset - Reset a KS8695 ethernet interface
- *     @ksp: The interface to reset
- *
- *     Perform an engine reset of the interface and re-program it
- *     with sensible defaults.
- */
-static void
-ks8695_reset(struct ks8695_priv *ksp)
-{
-       int reset_timeout = watchdog;
-       /* Issue the reset via the TX DMA control register */
-       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
-       while (reset_timeout--) {
-               if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
-                       break;
-               msleep(1);
-       }
-
-       if (reset_timeout < 0) {
-               dev_crit(ksp->dev,
-                        "Timeout waiting for DMA engines to reset\n");
-               /* And blithely carry on */
-       }
-
-       /* Definitely wait long enough before attempting to program
-        * the engines
-        */
-       msleep(10);
-
-       /* RX: unicast and broadcast */
-       ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
-       /* TX: pad and add CRC */
-       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
-}
-
-/**
- *     ks8695_shutdown - Shut down a KS8695 ethernet interface
- *     @ksp: The interface to shut down
- *
- *     This disables packet RX/TX, cleans up IRQs, drains the rings,
- *     and basically places the interface into a clean shutdown
- *     state.
- */
-static void
-ks8695_shutdown(struct ks8695_priv *ksp)
-{
-       u32 ctrl;
-       int buff_n;
-
-       /* Disable packet transmission */
-       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-       ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
-
-       /* Disable packet reception */
-       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-       ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
-
-       /* Release the IRQs */
-       free_irq(ksp->rx_irq, ksp->ndev);
-       free_irq(ksp->tx_irq, ksp->ndev);
-       if (ksp->link_irq != -1)
-               free_irq(ksp->link_irq, ksp->ndev);
-
-       /* Throw away any pending TX packets */
-       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
-               if (ksp->tx_buffers[buff_n].skb) {
-                       /* Remove this SKB from the TX ring */
-                       ksp->tx_ring[buff_n].owner = 0;
-                       ksp->tx_ring[buff_n].status = 0;
-                       ksp->tx_ring[buff_n].data_ptr = 0;
-
-                       /* Unmap and bin this SKB */
-                       dma_unmap_single(ksp->dev,
-                                        ksp->tx_buffers[buff_n].dma_ptr,
-                                        ksp->tx_buffers[buff_n].length,
-                                        DMA_TO_DEVICE);
-                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
-                       ksp->tx_buffers[buff_n].skb = NULL;
-               }
-       }
-
-       /* Purge the RX buffers */
-       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
-               if (ksp->rx_buffers[buff_n].skb) {
-                       /* Remove the SKB from the RX ring */
-                       ksp->rx_ring[buff_n].status = 0;
-                       ksp->rx_ring[buff_n].data_ptr = 0;
-
-                       /* Unmap and bin the SKB */
-                       dma_unmap_single(ksp->dev,
-                                        ksp->rx_buffers[buff_n].dma_ptr,
-                                        ksp->rx_buffers[buff_n].length,
-                                        DMA_FROM_DEVICE);
-                       dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
-                       ksp->rx_buffers[buff_n].skb = NULL;
-               }
-       }
-}
-
-
-/**
- *     ks8695_setup_irq - IRQ setup helper function
- *     @irq: The IRQ number to claim
- *     @irq_name: The name to give the IRQ claimant
- *     @handler: The function to call to handle the IRQ
- *     @ndev: The net_device to pass in as the dev_id argument to the handler
- *
- *     Return 0 on success.
- */
-static int
-ks8695_setup_irq(int irq, const char *irq_name,
-                irq_handler_t handler, struct net_device *ndev)
-{
-       int ret;
-
-       ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
-
-       if (ret) {
-               dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
-               return ret;
-       }
-
-       return 0;
-}
-
-/**
- *     ks8695_init_net - Initialise a KS8695 ethernet interface
- *     @ksp: The interface to initialise
- *
- *     This routine fills the RX ring, initialises the DMA engines,
- *     allocates the IRQs and then starts the packet TX and RX
- *     engines.
- */
-static int
-ks8695_init_net(struct ks8695_priv *ksp)
-{
-       int ret;
-       u32 ctrl;
-
-       ks8695_refill_rxbuffers(ksp);
-
-       /* Initialise the DMA engines */
-       ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
-       ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
-
-       /* Request the IRQs */
-       ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
-                              ks8695_rx_irq, ksp->ndev);
-       if (ret)
-               return ret;
-       ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
-                              ks8695_tx_irq, ksp->ndev);
-       if (ret)
-               return ret;
-       if (ksp->link_irq != -1) {
-               ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
-                                      ks8695_link_irq, ksp->ndev);
-               if (ret)
-                       return ret;
-       }
-
-       /* Set up the ring indices */
-       ksp->next_rx_desc_read = 0;
-       ksp->tx_ring_next_slot = 0;
-       ksp->tx_ring_used = 0;
-
-       /* Bring up transmission */
-       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-       /* Enable packet transmission */
-       ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
-
-       /* Bring up the reception */
-       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-       /* Enable packet reception */
-       ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
-       /* And start the DMA engine */
-       ks8695_writereg(ksp, KS8695_DRSC, 0);
-
-       /* All done */
-       return 0;
-}
-
-/**
- *     ks8695_release_device - HW resource release for KS8695 e-net
- *     @ksp: The device to be freed
- *
- *     This unallocates io memory regions, dma-coherent regions etc
- *     which were allocated in ks8695_probe.
- */
-static void
-ks8695_release_device(struct ks8695_priv *ksp)
-{
-       /* Unmap the registers */
-       iounmap(ksp->io_regs);
-       if (ksp->phyiface_regs)
-               iounmap(ksp->phyiface_regs);
-
-       /* And release the request */
-       release_resource(ksp->regs_req);
-       kfree(ksp->regs_req);
-       if (ksp->phyiface_req) {
-               release_resource(ksp->phyiface_req);
-               kfree(ksp->phyiface_req);
-       }
-
-       /* Free the ring buffers */
-       dma_free_coherent(ksp->dev, RING_DMA_SIZE,
-                         ksp->ring_base, ksp->ring_base_dma);
-}
-
-/* Ethtool support */
-
-/**
- *     ks8695_get_msglevel - Get the messages enabled for emission
- *     @ndev: The network device to read from
- */
-static u32
-ks8695_get_msglevel(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       return ksp->msg_enable;
-}
-
-/**
- *     ks8695_set_msglevel - Set the messages enabled for emission
- *     @ndev: The network device to configure
- *     @value: The messages to set for emission
- */
-static void
-ks8695_set_msglevel(struct net_device *ndev, u32 value)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       ksp->msg_enable = value;
-}
-
-/**
- *     ks8695_wan_get_settings - Get device-specific settings.
- *     @ndev: The network device to read settings from
- *     @cmd: The ethtool structure to read into
- */
-static int
-ks8695_wan_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       /* All ports on the KS8695 support these... */
-       cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
-                         SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
-                         SUPPORTED_TP | SUPPORTED_MII);
-       cmd->transceiver = XCVR_INTERNAL;
-
-       cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
-       cmd->port = PORT_MII;
-       cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
-       cmd->phy_address = 0;
-
-       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-       if ((ctrl & WMC_WAND) == 0) {
-               /* auto-negotiation is enabled */
-               cmd->advertising |= ADVERTISED_Autoneg;
-               if (ctrl & WMC_WANA100F)
-                       cmd->advertising |= ADVERTISED_100baseT_Full;
-               if (ctrl & WMC_WANA100H)
-                       cmd->advertising |= ADVERTISED_100baseT_Half;
-               if (ctrl & WMC_WANA10F)
-                       cmd->advertising |= ADVERTISED_10baseT_Full;
-               if (ctrl & WMC_WANA10H)
-                       cmd->advertising |= ADVERTISED_10baseT_Half;
-               if (ctrl & WMC_WANAP)
-                       cmd->advertising |= ADVERTISED_Pause;
-               cmd->autoneg = AUTONEG_ENABLE;
-
-               ethtool_cmd_speed_set(cmd,
-                                     (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10);
-               cmd->duplex = (ctrl & WMC_WDS) ?
-                       DUPLEX_FULL : DUPLEX_HALF;
-       } else {
-               /* auto-negotiation is disabled */
-               cmd->autoneg = AUTONEG_DISABLE;
-
-               ethtool_cmd_speed_set(cmd, ((ctrl & WMC_WANF100) ?
-                                           SPEED_100 : SPEED_10));
-               cmd->duplex = (ctrl & WMC_WANFF) ?
-                       DUPLEX_FULL : DUPLEX_HALF;
-       }
-
-       return 0;
-}
-
-/**
- *     ks8695_wan_set_settings - Set device-specific settings.
- *     @ndev: The network device to configure
- *     @cmd: The settings to configure
- */
-static int
-ks8695_wan_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
-               return -EINVAL;
-       if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
-               return -EINVAL;
-       if (cmd->port != PORT_MII)
-               return -EINVAL;
-       if (cmd->transceiver != XCVR_INTERNAL)
-               return -EINVAL;
-       if ((cmd->autoneg != AUTONEG_DISABLE) &&
-           (cmd->autoneg != AUTONEG_ENABLE))
-               return -EINVAL;
-
-       if (cmd->autoneg == AUTONEG_ENABLE) {
-               if ((cmd->advertising & (ADVERTISED_10baseT_Half |
-                               ADVERTISED_10baseT_Full |
-                               ADVERTISED_100baseT_Half |
-                               ADVERTISED_100baseT_Full)) == 0)
-                       return -EINVAL;
-
-               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-
-               ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
-                         WMC_WANA10F | WMC_WANA10H);
-               if (cmd->advertising & ADVERTISED_100baseT_Full)
-                       ctrl |= WMC_WANA100F;
-               if (cmd->advertising & ADVERTISED_100baseT_Half)
-                       ctrl |= WMC_WANA100H;
-               if (cmd->advertising & ADVERTISED_10baseT_Full)
-                       ctrl |= WMC_WANA10F;
-               if (cmd->advertising & ADVERTISED_10baseT_Half)
-                       ctrl |= WMC_WANA10H;
-
-               /* force a re-negotiation */
-               ctrl |= WMC_WANR;
-               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
-       } else {
-               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-
-               /* disable auto-negotiation */
-               ctrl |= WMC_WAND;
-               ctrl &= ~(WMC_WANF100 | WMC_WANFF);
-
-               if (cmd->speed == SPEED_100)
-                       ctrl |= WMC_WANF100;
-               if (cmd->duplex == DUPLEX_FULL)
-                       ctrl |= WMC_WANFF;
-
-               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
-       }
-
-       return 0;
-}
-
-/**
- *     ks8695_wan_nwayreset - Restart the autonegotiation on the port.
- *     @ndev: The network device to restart autoneotiation on
- */
-static int
-ks8695_wan_nwayreset(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-
-       if ((ctrl & WMC_WAND) == 0)
-               writel(ctrl | WMC_WANR,
-                      ksp->phyiface_regs + KS8695_WMC);
-       else
-               /* auto-negotiation not enabled */
-               return -EINVAL;
-
-       return 0;
-}
-
-/**
- *     ks8695_wan_get_pause - Retrieve network pause/flow-control advertising
- *     @ndev: The device to retrieve settings from
- *     @param: The structure to fill out with the information
- */
-static void
-ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
-
-       /* advertise Pause */
-       param->autoneg = (ctrl & WMC_WANAP);
-
-       /* current Rx Flow-control */
-       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-       param->rx_pause = (ctrl & DRXC_RFCE);
-
-       /* current Tx Flow-control */
-       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
-       param->tx_pause = (ctrl & DTXC_TFCE);
-}
-
-/**
- *     ks8695_get_drvinfo - Retrieve driver information
- *     @ndev: The network device to retrieve info about
- *     @info: The info structure to fill out.
- */
-static void
-ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
-{
-       strlcpy(info->driver, MODULENAME, sizeof(info->driver));
-       strlcpy(info->version, MODULEVERSION, sizeof(info->version));
-       strlcpy(info->bus_info, dev_name(ndev->dev.parent),
-               sizeof(info->bus_info));
-}
-
-static const struct ethtool_ops ks8695_ethtool_ops = {
-       .get_msglevel   = ks8695_get_msglevel,
-       .set_msglevel   = ks8695_set_msglevel,
-       .get_drvinfo    = ks8695_get_drvinfo,
-};
-
-static const struct ethtool_ops ks8695_wan_ethtool_ops = {
-       .get_msglevel   = ks8695_get_msglevel,
-       .set_msglevel   = ks8695_set_msglevel,
-       .get_settings   = ks8695_wan_get_settings,
-       .set_settings   = ks8695_wan_set_settings,
-       .nway_reset     = ks8695_wan_nwayreset,
-       .get_link       = ethtool_op_get_link,
-       .get_pauseparam = ks8695_wan_get_pause,
-       .get_drvinfo    = ks8695_get_drvinfo,
-};
-
-/* Network device interface functions */
-
-/**
- *     ks8695_set_mac - Update MAC in net dev and HW
- *     @ndev: The network device to update
- *     @addr: The new MAC address to set
- */
-static int
-ks8695_set_mac(struct net_device *ndev, void *addr)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       struct sockaddr *address = addr;
-
-       if (!is_valid_ether_addr(address->sa_data))
-               return -EADDRNOTAVAIL;
-
-       memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
-
-       ks8695_update_mac(ksp);
-
-       dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
-               ndev->name, ndev->dev_addr);
-
-       return 0;
-}
-
-/**
- *     ks8695_set_multicast - Set up the multicast behaviour of the interface
- *     @ndev: The net_device to configure
- *
- *     This routine, called by the net layer, configures promiscuity
- *     and multicast reception behaviour for the interface.
- */
-static void
-ks8695_set_multicast(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       u32 ctrl;
-
-       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
-
-       if (ndev->flags & IFF_PROMISC) {
-               /* enable promiscuous mode */
-               ctrl |= DRXC_RA;
-       } else if (ndev->flags & ~IFF_PROMISC) {
-               /* disable promiscuous mode */
-               ctrl &= ~DRXC_RA;
-       }
-
-       if (ndev->flags & IFF_ALLMULTI) {
-               /* enable all multicast mode */
-               ctrl |= DRXC_RM;
-       } else if (netdev_mc_count(ndev) > KS8695_NR_ADDRESSES) {
-               /* more specific multicast addresses than can be
-                * handled in hardware
-                */
-               ctrl |= DRXC_RM;
-       } else {
-               /* enable specific multicasts */
-               ctrl &= ~DRXC_RM;
-               ks8695_init_partial_multicast(ksp, ndev);
-       }
-
-       ks8695_writereg(ksp, KS8695_DRXC, ctrl);
-}
-
-/**
- *     ks8695_timeout - Handle a network tx/rx timeout.
- *     @ndev: The net_device which timed out.
- *
- *     A network transaction timed out, reset the device.
- */
-static void
-ks8695_timeout(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       netif_stop_queue(ndev);
-       ks8695_shutdown(ksp);
-
-       ks8695_reset(ksp);
-
-       ks8695_update_mac(ksp);
-
-       /* We ignore the return from this since it managed to init
-        * before it probably will be okay to init again.
-        */
-       ks8695_init_net(ksp);
-
-       /* Reconfigure promiscuity etc */
-       ks8695_set_multicast(ndev);
-
-       /* And start the TX queue once more */
-       netif_start_queue(ndev);
-}
-
-/**
- *     ks8695_start_xmit - Start a packet transmission
- *     @skb: The packet to transmit
- *     @ndev: The network device to send the packet on
- *
- *     This routine, called by the net layer, takes ownership of the
- *     sk_buff and adds it to the TX ring. It then kicks the TX DMA
- *     engine to ensure transmission begins.
- */
-static int
-ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       int buff_n;
-       dma_addr_t dmap;
-
-       spin_lock_irq(&ksp->txq_lock);
-
-       if (ksp->tx_ring_used == MAX_TX_DESC) {
-               /* Somehow we got entered when we have no room */
-               spin_unlock_irq(&ksp->txq_lock);
-               return NETDEV_TX_BUSY;
-       }
-
-       buff_n = ksp->tx_ring_next_slot;
-
-       BUG_ON(ksp->tx_buffers[buff_n].skb);
-
-       dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
-       if (unlikely(dma_mapping_error(ksp->dev, dmap))) {
-               /* Failed to DMA map this SKB, give it back for now */
-               spin_unlock_irq(&ksp->txq_lock);
-               dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
-                       "transmission, trying later\n", ndev->name);
-               return NETDEV_TX_BUSY;
-       }
-
-       ksp->tx_buffers[buff_n].dma_ptr = dmap;
-       /* Mapped okay, store the buffer pointer and length for later */
-       ksp->tx_buffers[buff_n].skb = skb;
-       ksp->tx_buffers[buff_n].length = skb->len;
-
-       /* Fill out the TX descriptor */
-       ksp->tx_ring[buff_n].data_ptr =
-               cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
-       ksp->tx_ring[buff_n].status =
-               cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
-                           (skb->len & TDES_TBS));
-
-       wmb();
-
-       /* Hand it over to the hardware */
-       ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
-
-       if (++ksp->tx_ring_used == MAX_TX_DESC)
-               netif_stop_queue(ndev);
-
-       /* Kick the TX DMA in case it decided to go IDLE */
-       ks8695_writereg(ksp, KS8695_DTSC, 0);
-
-       /* And update the next ring slot */
-       ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
-
-       spin_unlock_irq(&ksp->txq_lock);
-       return NETDEV_TX_OK;
-}
-
-/**
- *     ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
- *     @ndev: The net_device to stop
- *
- *     This disables the TX queue and cleans up a KS8695 ethernet
- *     device.
- */
-static int
-ks8695_stop(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       netif_stop_queue(ndev);
-       napi_disable(&ksp->napi);
-
-       ks8695_shutdown(ksp);
-
-       return 0;
-}
-
-/**
- *     ks8695_open - Open (bring up) a KS8695 ethernet interface
- *     @ndev: The net_device to open
- *
- *     This resets, configures the MAC, initialises the RX ring and
- *     DMA engines and starts the TX queue for a KS8695 ethernet
- *     device.
- */
-static int
-ks8695_open(struct net_device *ndev)
-{
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-       int ret;
-
-       if (!is_valid_ether_addr(ndev->dev_addr))
-               return -EADDRNOTAVAIL;
-
-       ks8695_reset(ksp);
-
-       ks8695_update_mac(ksp);
-
-       ret = ks8695_init_net(ksp);
-       if (ret) {
-               ks8695_shutdown(ksp);
-               return ret;
-       }
-
-       napi_enable(&ksp->napi);
-       netif_start_queue(ndev);
-
-       return 0;
-}
-
-/* Platform device driver */
-
-/**
- *     ks8695_init_switch - Init LAN switch to known good defaults.
- *     @ksp: The device to initialise
- *
- *     This initialises the LAN switch in the KS8695 to a known-good
- *     set of defaults.
- */
-static void __devinit
-ks8695_init_switch(struct ks8695_priv *ksp)
-{
-       u32 ctrl;
-
-       /* Default value for SEC0 according to datasheet */
-       ctrl = 0x40819e00;
-
-       /* LED0 = Speed  LED1 = Link/Activity */
-       ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
-       ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
-
-       /* Enable Switch */
-       ctrl |= SEC0_ENABLE;
-
-       writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
-
-       /* Defaults for SEC1 */
-       writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
-}
-
-/**
- *     ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
- *     @ksp: The device to initialise
- *
- *     This initialises a KS8695's WAN phy to sensible values for
- *     autonegotiation etc.
- */
-static void __devinit
-ks8695_init_wan_phy(struct ks8695_priv *ksp)
-{
-       u32 ctrl;
-
-       /* Support auto-negotiation */
-       ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
-               WMC_WANA10F | WMC_WANA10H);
-
-       /* LED0 = Activity , LED1 = Link */
-       ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
-
-       /* Restart Auto-negotiation */
-       ctrl |= WMC_WANR;
-
-       writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
-
-       writel(0, ksp->phyiface_regs + KS8695_WPPM);
-       writel(0, ksp->phyiface_regs + KS8695_PPS);
-}
-
-static const struct net_device_ops ks8695_netdev_ops = {
-       .ndo_open               = ks8695_open,
-       .ndo_stop               = ks8695_stop,
-       .ndo_start_xmit         = ks8695_start_xmit,
-       .ndo_tx_timeout         = ks8695_timeout,
-       .ndo_set_mac_address    = ks8695_set_mac,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_set_multicast_list = ks8695_set_multicast,
-};
-
-/**
- *     ks8695_probe - Probe and initialise a KS8695 ethernet interface
- *     @pdev: The platform device to probe
- *
- *     Initialise a KS8695 ethernet device from platform data.
- *
- *     This driver requires at least one IORESOURCE_MEM for the
- *     registers and two IORESOURCE_IRQ for the RX and TX IRQs
- *     respectively. It can optionally take an additional
- *     IORESOURCE_MEM for the switch or phy in the case of the lan or
- *     wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
- *     port.
- */
-static int __devinit
-ks8695_probe(struct platform_device *pdev)
-{
-       struct ks8695_priv *ksp;
-       struct net_device *ndev;
-       struct resource *regs_res, *phyiface_res;
-       struct resource *rxirq_res, *txirq_res, *linkirq_res;
-       int ret = 0;
-       int buff_n;
-       u32 machigh, maclow;
-
-       /* Initialise a net_device */
-       ndev = alloc_etherdev(sizeof(struct ks8695_priv));
-       if (!ndev) {
-               dev_err(&pdev->dev, "could not allocate device.\n");
-               return -ENOMEM;
-       }
-
-       SET_NETDEV_DEV(ndev, &pdev->dev);
-
-       dev_dbg(&pdev->dev, "ks8695_probe() called\n");
-
-       /* Configure our private structure a little */
-       ksp = netdev_priv(ndev);
-
-       ksp->dev = &pdev->dev;
-       ksp->ndev = ndev;
-       ksp->msg_enable = NETIF_MSG_LINK;
-
-       /* Retrieve resources */
-       regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
-       rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
-       linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
-
-       if (!(regs_res && rxirq_res && txirq_res)) {
-               dev_err(ksp->dev, "insufficient resources\n");
-               ret = -ENOENT;
-               goto failure;
-       }
-
-       ksp->regs_req = request_mem_region(regs_res->start,
-                                          resource_size(regs_res),
-                                          pdev->name);
-
-       if (!ksp->regs_req) {
-               dev_err(ksp->dev, "cannot claim register space\n");
-               ret = -EIO;
-               goto failure;
-       }
-
-       ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
-
-       if (!ksp->io_regs) {
-               dev_err(ksp->dev, "failed to ioremap registers\n");
-               ret = -EINVAL;
-               goto failure;
-       }
-
-       if (phyiface_res) {
-               ksp->phyiface_req =
-                       request_mem_region(phyiface_res->start,
-                                          resource_size(phyiface_res),
-                                          phyiface_res->name);
-
-               if (!ksp->phyiface_req) {
-                       dev_err(ksp->dev,
-                               "cannot claim switch register space\n");
-                       ret = -EIO;
-                       goto failure;
-               }
-
-               ksp->phyiface_regs = ioremap(phyiface_res->start,
-                                            resource_size(phyiface_res));
-
-               if (!ksp->phyiface_regs) {
-                       dev_err(ksp->dev,
-                               "failed to ioremap switch registers\n");
-                       ret = -EINVAL;
-                       goto failure;
-               }
-       }
-
-       ksp->rx_irq = rxirq_res->start;
-       ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
-       ksp->tx_irq = txirq_res->start;
-       ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
-       ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
-       ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
-               linkirq_res->name : "Ethernet Link";
-
-       /* driver system setup */
-       ndev->netdev_ops = &ks8695_netdev_ops;
-       ndev->watchdog_timeo     = msecs_to_jiffies(watchdog);
-
-       netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT);
-
-       /* Retrieve the default MAC addr from the chip. */
-       /* The bootloader should have left it in there for us. */
-
-       machigh = ks8695_readreg(ksp, KS8695_MAH);
-       maclow = ks8695_readreg(ksp, KS8695_MAL);
-
-       ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
-       ndev->dev_addr[1] = machigh & 0xFF;
-       ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
-       ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
-       ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
-       ndev->dev_addr[5] = maclow & 0xFF;
-
-       if (!is_valid_ether_addr(ndev->dev_addr))
-               dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
-                        "set using ifconfig\n", ndev->name);
-
-       /* In order to be efficient memory-wise, we allocate both
-        * rings in one go.
-        */
-       ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
-                                           &ksp->ring_base_dma, GFP_KERNEL);
-       if (!ksp->ring_base) {
-               ret = -ENOMEM;
-               goto failure;
-       }
-
-       /* Specify the TX DMA ring buffer */
-       ksp->tx_ring = ksp->ring_base;
-       ksp->tx_ring_dma = ksp->ring_base_dma;
-
-       /* And initialise the queue's lock */
-       spin_lock_init(&ksp->txq_lock);
-       spin_lock_init(&ksp->rx_lock);
-
-       /* Specify the RX DMA ring buffer */
-       ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
-       ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
-
-       /* Zero the descriptor rings */
-       memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
-       memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
-
-       /* Build the rings */
-       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
-               ksp->tx_ring[buff_n].next_desc =
-                       cpu_to_le32(ksp->tx_ring_dma +
-                                   (sizeof(struct tx_ring_desc) *
-                                    ((buff_n + 1) & MAX_TX_DESC_MASK)));
-       }
-
-       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
-               ksp->rx_ring[buff_n].next_desc =
-                       cpu_to_le32(ksp->rx_ring_dma +
-                                   (sizeof(struct rx_ring_desc) *
-                                    ((buff_n + 1) & MAX_RX_DESC_MASK)));
-       }
-
-       /* Initialise the port (physically) */
-       if (ksp->phyiface_regs && ksp->link_irq == -1) {
-               ks8695_init_switch(ksp);
-               ksp->dtype = KS8695_DTYPE_LAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
-       } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
-               ks8695_init_wan_phy(ksp);
-               ksp->dtype = KS8695_DTYPE_WAN;
-               SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
-       } else {
-               /* No initialisation since HPNA does not have a PHY */
-               ksp->dtype = KS8695_DTYPE_HPNA;
-               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
-       }
-
-       /* And bring up the net_device with the net core */
-       platform_set_drvdata(pdev, ndev);
-       ret = register_netdev(ndev);
-
-       if (ret == 0) {
-               dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
-                        ks8695_port_type(ksp), ndev->dev_addr);
-       } else {
-               /* Report the failure to register the net_device */
-               dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
-               goto failure;
-       }
-
-       /* All is well */
-       return 0;
-
-       /* Error exit path */
-failure:
-       ks8695_release_device(ksp);
-       free_netdev(ndev);
-
-       return ret;
-}
-
-/**
- *     ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
- *     @pdev: The device to suspend
- *     @state: The suspend state
- *
- *     This routine detaches and shuts down a KS8695 ethernet device.
- */
-static int
-ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
-{
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       ksp->in_suspend = 1;
-
-       if (netif_running(ndev)) {
-               netif_device_detach(ndev);
-               ks8695_shutdown(ksp);
-       }
-
-       return 0;
-}
-
-/**
- *     ks8695_drv_resume - Resume a KS8695 ethernet platform device.
- *     @pdev: The device to resume
- *
- *     This routine re-initialises and re-attaches a KS8695 ethernet
- *     device.
- */
-static int
-ks8695_drv_resume(struct platform_device *pdev)
-{
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       if (netif_running(ndev)) {
-               ks8695_reset(ksp);
-               ks8695_init_net(ksp);
-               ks8695_set_multicast(ndev);
-               netif_device_attach(ndev);
-       }
-
-       ksp->in_suspend = 0;
-
-       return 0;
-}
-
-/**
- *     ks8695_drv_remove - Remove a KS8695 net device on driver unload.
- *     @pdev: The platform device to remove
- *
- *     This unregisters and releases a KS8695 ethernet device.
- */
-static int __devexit
-ks8695_drv_remove(struct platform_device *pdev)
-{
-       struct net_device *ndev = platform_get_drvdata(pdev);
-       struct ks8695_priv *ksp = netdev_priv(ndev);
-
-       platform_set_drvdata(pdev, NULL);
-       netif_napi_del(&ksp->napi);
-
-       unregister_netdev(ndev);
-       ks8695_release_device(ksp);
-       free_netdev(ndev);
-
-       dev_dbg(&pdev->dev, "released and freed device\n");
-       return 0;
-}
-
-static struct platform_driver ks8695_driver = {
-       .driver = {
-               .name   = MODULENAME,
-               .owner  = THIS_MODULE,
-       },
-       .probe          = ks8695_probe,
-       .remove         = __devexit_p(ks8695_drv_remove),
-       .suspend        = ks8695_drv_suspend,
-       .resume         = ks8695_drv_resume,
-};
-
-/* Module interface */
-
-static int __init
-ks8695_init(void)
-{
-       printk(KERN_INFO "%s Ethernet driver, V%s\n",
-              MODULENAME, MODULEVERSION);
-
-       return platform_driver_register(&ks8695_driver);
-}
-
-static void __exit
-ks8695_cleanup(void)
-{
-       platform_driver_unregister(&ks8695_driver);
-}
-
-module_init(ks8695_init);
-module_exit(ks8695_cleanup);
-
-MODULE_AUTHOR("Simtec Electronics");
-MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" MODULENAME);
-
-module_param(watchdog, int, 0400);
-MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
diff --git a/drivers/net/arm/ks8695net.h b/drivers/net/arm/ks8695net.h
deleted file mode 100644 (file)
index 80eff6e..0000000
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Micrel KS8695 (Centaur) Ethernet.
- *
- * Copyright 2008 Simtec Electronics
- *               Daniel Silverstone <dsilvers@simtec.co.uk>
- *               Vincent Sanders <vince@simtec.co.uk>
- */
-
-#ifndef KS8695NET_H
-#define KS8695NET_H
-
-/* Receive descriptor flags */
-#define RDES_OWN       (1 << 31)       /* Ownership */
-#define RDES_FS                (1 << 30)       /* First Descriptor */
-#define RDES_LS                (1 << 29)       /* Last Descriptor */
-#define RDES_IPE       (1 << 28)       /* IP Checksum error */
-#define RDES_TCPE      (1 << 27)       /* TCP Checksum error */
-#define RDES_UDPE      (1 << 26)       /* UDP Checksum error */
-#define RDES_ES                (1 << 25)       /* Error summary */
-#define RDES_MF                (1 << 24)       /* Multicast Frame */
-#define RDES_RE                (1 << 19)       /* MII Error reported */
-#define RDES_TL                (1 << 18)       /* Frame too Long */
-#define RDES_RF                (1 << 17)       /* Runt Frame */
-#define RDES_CE                (1 << 16)       /* CRC error */
-#define RDES_FT                (1 << 15)       /* Frame Type */
-#define RDES_FLEN      (0x7ff)         /* Frame Length */
-
-#define RDES_RER       (1 << 25)       /* Receive End of Ring */
-#define RDES_RBS       (0x7ff)         /* Receive Buffer Size */
-
-/* Transmit descriptor flags */
-
-#define TDES_OWN       (1 << 31)       /* Ownership */
-
-#define TDES_IC                (1 << 31)       /* Interrupt on Completion */
-#define TDES_FS                (1 << 30)       /* First Segment */
-#define TDES_LS                (1 << 29)       /* Last Segment */
-#define TDES_IPCKG     (1 << 28)       /* IP Checksum generate */
-#define TDES_TCPCKG    (1 << 27)       /* TCP Checksum generate */
-#define TDES_UDPCKG    (1 << 26)       /* UDP Checksum generate */
-#define TDES_TER       (1 << 25)       /* Transmit End of Ring */
-#define TDES_TBS       (0x7ff)         /* Transmit Buffer Size */
-
-/*
- * Network controller register offsets
- */
-#define KS8695_DTXC            (0x00)          /* DMA Transmit Control */
-#define KS8695_DRXC            (0x04)          /* DMA Receive Control */
-#define KS8695_DTSC            (0x08)          /* DMA Transmit Start Command */
-#define KS8695_DRSC            (0x0c)          /* DMA Receive Start Command */
-#define KS8695_TDLB            (0x10)          /* Transmit Descriptor List
-                                                * Base Address
-                                                */
-#define KS8695_RDLB            (0x14)          /* Receive Descriptor List
-                                                * Base Address
-                                                */
-#define KS8695_MAL             (0x18)          /* MAC Station Address Low */
-#define KS8695_MAH             (0x1c)          /* MAC Station Address High */
-#define KS8695_AAL_(n)         (0x80 + ((n)*8))        /* MAC Additional
-                                                        * Station Address
-                                                        * (0..15) Low
-                                                        */
-#define KS8695_AAH_(n)         (0x84 + ((n)*8))        /* MAC Additional
-                                                        * Station Address
-                                                        * (0..15) High
-                                                        */
-
-
-/* DMA Transmit Control Register */
-#define DTXC_TRST              (1    << 31)    /* Soft Reset */
-#define DTXC_TBS               (0x3f << 24)    /* Transmit Burst Size */
-#define DTXC_TUCG              (1    << 18)    /* Transmit UDP
-                                                * Checksum Generate
-                                                */
-#define DTXC_TTCG              (1    << 17)    /* Transmit TCP
-                                                * Checksum Generate
-                                                */
-#define DTXC_TICG              (1    << 16)    /* Transmit IP
-                                                * Checksum Generate
-                                                */
-#define DTXC_TFCE              (1    <<  9)    /* Transmit Flow
-                                                * Control Enable
-                                                */
-#define DTXC_TLB               (1    <<  8)    /* Loopback mode */
-#define DTXC_TEP               (1    <<  2)    /* Transmit Enable Padding */
-#define DTXC_TAC               (1    <<  1)    /* Transmit Add CRC */
-#define DTXC_TE                        (1    <<  0)    /* TX Enable */
-
-/* DMA Receive Control Register */
-#define DRXC_RBS               (0x3f << 24)    /* Receive Burst Size */
-#define DRXC_RUCC              (1    << 18)    /* Receive UDP Checksum check */
-#define DRXC_RTCG              (1    << 17)    /* Receive TCP Checksum check */
-#define DRXC_RICG              (1    << 16)    /* Receive IP Checksum check */
-#define DRXC_RFCE              (1    <<  9)    /* Receive Flow Control
-                                                * Enable
-                                                */
-#define DRXC_RB                        (1    <<  6)    /* Receive Broadcast */
-#define DRXC_RM                        (1    <<  5)    /* Receive Multicast */
-#define DRXC_RU                        (1    <<  4)    /* Receive Unicast */
-#define DRXC_RERR              (1    <<  3)    /* Receive Error Frame */
-#define DRXC_RA                        (1    <<  2)    /* Receive All */
-#define DRXC_RE                        (1    <<  0)    /* RX Enable */
-
-/* Additional Station Address High */
-#define AAH_E                  (1    << 31)    /* Address Enabled */
-
-#endif /* KS8695NET_H */
index 924c287aaaa9720b89ff7a345958f16848f00526..d90f47f3b78287dab12c15c2be780ee12c68b701 100644 (file)
@@ -32,6 +32,7 @@ source "drivers/net/ethernet/i825xx/Kconfig"
 source "drivers/net/ethernet/xscale/Kconfig"
 source "drivers/net/ethernet/marvell/Kconfig"
 source "drivers/net/ethernet/mellanox/Kconfig"
+source "drivers/net/ethernet/micrel/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
 source "drivers/net/ethernet/natsemi/Kconfig"
 source "drivers/net/ethernet/8390/Kconfig"
index 025d7b763b91339088032352902c35937ec5d4d4..cf27ae0eb3ecea9b80cb59ccb10ecba7cb0513c3 100644 (file)
@@ -24,6 +24,7 @@ obj-$(CONFIG_NET_VENDOR_I825XX) += i825xx/
 obj-$(CONFIG_NET_VENDOR_XSCALE) += xscale/
 obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/
 obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/
+obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
 obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
 obj-$(CONFIG_NET_VENDOR_OKI) += oki-semi/
diff --git a/drivers/net/ethernet/micrel/Kconfig b/drivers/net/ethernet/micrel/Kconfig
new file mode 100644 (file)
index 0000000..4227de6
--- /dev/null
@@ -0,0 +1,64 @@
+#
+# Micrel device configuration
+#
+
+config NET_VENDOR_MICREL
+       bool "Micrel devices"
+       depends on (HAS_IOMEM && DMA_ENGINE) || SPI || PCI || HAS_IOMEM || \
+                  (ARM && ARCH_KS8695)
+       ---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 Micrel devices. If you say Y, you will be asked
+         for your specific card in the following questions.
+
+if NET_VENDOR_MICREL
+
+config ARM_KS8695_ETHER
+       tristate "KS8695 Ethernet support"
+       depends on ARM && ARCH_KS8695
+       select MII
+       ---help---
+         If you wish to compile a kernel for the KS8695 and want to
+         use the internal ethernet then you should answer Y to this.
+
+config KS8842
+       tristate "Micrel KSZ8841/42 with generic bus interface"
+       depends on HAS_IOMEM && DMA_ENGINE
+       ---help---
+         This platform driver is for KSZ8841(1-port) / KS8842(2-port)
+         ethernet switch chip (managed, VLAN, QoS) from Micrel or
+         Timberdale(FPGA).
+
+config KS8851
+       tristate "Micrel KS8851 SPI"
+       depends on SPI
+       select MII
+       select CRC32
+       ---help---
+         SPI driver for Micrel KS8851 SPI attached network chip.
+
+config KS8851_MLL
+       tristate "Micrel KS8851 MLL"
+       depends on HAS_IOMEM
+       select MII
+       ---help---
+         This platform driver is for Micrel KS8851 Address/data bus
+         multiplexed network chip.
+
+config KSZ884X_PCI
+       tristate "Micrel KSZ8841/2 PCI"
+       depends on PCI
+       select MII
+       select CRC32
+       ---help---
+         This PCI driver is for Micrel KSZ8841/KSZ8842 PCI Ethernet chip.
+
+         To compile this driver as a module, choose M here. The module
+         will be called ksz884x.
+
+endif # NET_VENDOR_MICREL
diff --git a/drivers/net/ethernet/micrel/Makefile b/drivers/net/ethernet/micrel/Makefile
new file mode 100644 (file)
index 0000000..c83e4bc
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the Micrel network device drivers.
+#
+
+obj-$(CONFIG_ARM_KS8695_ETHER) += ks8695net.o
+obj-$(CONFIG_KS8842) += ks8842.o
+obj-$(CONFIG_KS8851) += ks8851.o
+obj-$(CONFIG_KS8851_MLL) += ks8851_mll.o
+obj-$(CONFIG_KSZ884X_PCI) += ksz884x.o
diff --git a/drivers/net/ethernet/micrel/ks8695net.c b/drivers/net/ethernet/micrel/ks8695net.c
new file mode 100644 (file)
index 0000000..c827a60
--- /dev/null
@@ -0,0 +1,1656 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * 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.
+ *
+ * Copyright 2008 Simtec Electronics
+ *               Daniel Silverstone <dsilvers@simtec.co.uk>
+ *               Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/skbuff.h>
+#include <linux/spinlock.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+
+#include <mach/regs-switch.h>
+#include <mach/regs-misc.h>
+#include <asm/mach/irq.h>
+#include <mach/regs-irq.h>
+
+#include "ks8695net.h"
+
+#define MODULENAME     "ks8695_ether"
+#define MODULEVERSION  "1.02"
+
+/*
+ * Transmit and device reset timeout, default 5 seconds.
+ */
+static int watchdog = 5000;
+
+/* Hardware structures */
+
+/**
+ *     struct rx_ring_desc - Receive descriptor ring element
+ *     @status: The status of the descriptor element (E.g. who owns it)
+ *     @length: The number of bytes in the block pointed to by data_ptr
+ *     @data_ptr: The physical address of the data block to receive into
+ *     @next_desc: The physical address of the next descriptor element.
+ */
+struct rx_ring_desc {
+       __le32  status;
+       __le32  length;
+       __le32  data_ptr;
+       __le32  next_desc;
+};
+
+/**
+ *     struct tx_ring_desc - Transmit descriptor ring element
+ *     @owner: Who owns the descriptor
+ *     @status: The number of bytes in the block pointed to by data_ptr
+ *     @data_ptr: The physical address of the data block to receive into
+ *     @next_desc: The physical address of the next descriptor element.
+ */
+struct tx_ring_desc {
+       __le32  owner;
+       __le32  status;
+       __le32  data_ptr;
+       __le32  next_desc;
+};
+
+/**
+ *     struct ks8695_skbuff - sk_buff wrapper for rx/tx rings.
+ *     @skb: The buffer in the ring
+ *     @dma_ptr: The mapped DMA pointer of the buffer
+ *     @length: The number of bytes mapped to dma_ptr
+ */
+struct ks8695_skbuff {
+       struct sk_buff  *skb;
+       dma_addr_t      dma_ptr;
+       u32             length;
+};
+
+/* Private device structure */
+
+#define MAX_TX_DESC 8
+#define MAX_TX_DESC_MASK 0x7
+#define MAX_RX_DESC 16
+#define MAX_RX_DESC_MASK 0xf
+
+/*napi_weight have better more than rx DMA buffers*/
+#define NAPI_WEIGHT   64
+
+#define MAX_RXBUF_SIZE 0x700
+
+#define TX_RING_DMA_SIZE (sizeof(struct tx_ring_desc) * MAX_TX_DESC)
+#define RX_RING_DMA_SIZE (sizeof(struct rx_ring_desc) * MAX_RX_DESC)
+#define RING_DMA_SIZE (TX_RING_DMA_SIZE + RX_RING_DMA_SIZE)
+
+/**
+ *     enum ks8695_dtype - Device type
+ *     @KS8695_DTYPE_WAN: This device is a WAN interface
+ *     @KS8695_DTYPE_LAN: This device is a LAN interface
+ *     @KS8695_DTYPE_HPNA: This device is an HPNA interface
+ */
+enum ks8695_dtype {
+       KS8695_DTYPE_WAN,
+       KS8695_DTYPE_LAN,
+       KS8695_DTYPE_HPNA,
+};
+
+/**
+ *     struct ks8695_priv - Private data for the KS8695 Ethernet
+ *     @in_suspend: Flag to indicate if we're suspending/resuming
+ *     @ndev: The net_device for this interface
+ *     @dev: The platform device object for this interface
+ *     @dtype: The type of this device
+ *     @io_regs: The ioremapped registers for this interface
+ *      @napi : Add support NAPI for Rx
+ *     @rx_irq_name: The textual name of the RX IRQ from the platform data
+ *     @tx_irq_name: The textual name of the TX IRQ from the platform data
+ *     @link_irq_name: The textual name of the link IRQ from the
+ *                     platform data if available
+ *     @rx_irq: The IRQ number for the RX IRQ
+ *     @tx_irq: The IRQ number for the TX IRQ
+ *     @link_irq: The IRQ number for the link IRQ if available
+ *     @regs_req: The resource request for the registers region
+ *     @phyiface_req: The resource request for the phy/switch region
+ *                    if available
+ *     @phyiface_regs: The ioremapped registers for the phy/switch if available
+ *     @ring_base: The base pointer of the dma coherent memory for the rings
+ *     @ring_base_dma: The DMA mapped equivalent of ring_base
+ *     @tx_ring: The pointer in ring_base of the TX ring
+ *     @tx_ring_used: The number of slots in the TX ring which are occupied
+ *     @tx_ring_next_slot: The next slot to fill in the TX ring
+ *     @tx_ring_dma: The DMA mapped equivalent of tx_ring
+ *     @tx_buffers: The sk_buff mappings for the TX ring
+ *     @txq_lock: A lock to protect the tx_buffers tx_ring_used etc variables
+ *     @rx_ring: The pointer in ring_base of the RX ring
+ *     @rx_ring_dma: The DMA mapped equivalent of rx_ring
+ *     @rx_buffers: The sk_buff mappings for the RX ring
+ *     @next_rx_desc_read: The next RX descriptor to read from on IRQ
+ *      @rx_lock: A lock to protect Rx irq function
+ *     @msg_enable: The flags for which messages to emit
+ */
+struct ks8695_priv {
+       int in_suspend;
+       struct net_device *ndev;
+       struct device *dev;
+       enum ks8695_dtype dtype;
+       void __iomem *io_regs;
+
+       struct napi_struct      napi;
+
+       const char *rx_irq_name, *tx_irq_name, *link_irq_name;
+       int rx_irq, tx_irq, link_irq;
+
+       struct resource *regs_req, *phyiface_req;
+       void __iomem *phyiface_regs;
+
+       void *ring_base;
+       dma_addr_t ring_base_dma;
+
+       struct tx_ring_desc *tx_ring;
+       int tx_ring_used;
+       int tx_ring_next_slot;
+       dma_addr_t tx_ring_dma;
+       struct ks8695_skbuff tx_buffers[MAX_TX_DESC];
+       spinlock_t txq_lock;
+
+       struct rx_ring_desc *rx_ring;
+       dma_addr_t rx_ring_dma;
+       struct ks8695_skbuff rx_buffers[MAX_RX_DESC];
+       int next_rx_desc_read;
+       spinlock_t rx_lock;
+
+       int msg_enable;
+};
+
+/* Register access */
+
+/**
+ *     ks8695_readreg - Read from a KS8695 ethernet register
+ *     @ksp: The device to read from
+ *     @reg: The register to read
+ */
+static inline u32
+ks8695_readreg(struct ks8695_priv *ksp, int reg)
+{
+       return readl(ksp->io_regs + reg);
+}
+
+/**
+ *     ks8695_writereg - Write to a KS8695 ethernet register
+ *     @ksp: The device to write to
+ *     @reg: The register to write
+ *     @value: The value to write to the register
+ */
+static inline void
+ks8695_writereg(struct ks8695_priv *ksp, int reg, u32 value)
+{
+       writel(value, ksp->io_regs + reg);
+}
+
+/* Utility functions */
+
+/**
+ *     ks8695_port_type - Retrieve port-type as user-friendly string
+ *     @ksp: The device to return the type for
+ *
+ *     Returns a string indicating which of the WAN, LAN or HPNA
+ *     ports this device is likely to represent.
+ */
+static const char *
+ks8695_port_type(struct ks8695_priv *ksp)
+{
+       switch (ksp->dtype) {
+       case KS8695_DTYPE_LAN:
+               return "LAN";
+       case KS8695_DTYPE_WAN:
+               return "WAN";
+       case KS8695_DTYPE_HPNA:
+               return "HPNA";
+       }
+
+       return "UNKNOWN";
+}
+
+/**
+ *     ks8695_update_mac - Update the MAC registers in the device
+ *     @ksp: The device to update
+ *
+ *     Updates the MAC registers in the KS8695 device from the address in the
+ *     net_device structure associated with this interface.
+ */
+static void
+ks8695_update_mac(struct ks8695_priv *ksp)
+{
+       /* Update the HW with the MAC from the net_device */
+       struct net_device *ndev = ksp->ndev;
+       u32 machigh, maclow;
+
+       maclow  = ((ndev->dev_addr[2] << 24) | (ndev->dev_addr[3] << 16) |
+                  (ndev->dev_addr[4] <<  8) | (ndev->dev_addr[5] <<  0));
+       machigh = ((ndev->dev_addr[0] <<  8) | (ndev->dev_addr[1] <<  0));
+
+       ks8695_writereg(ksp, KS8695_MAL, maclow);
+       ks8695_writereg(ksp, KS8695_MAH, machigh);
+
+}
+
+/**
+ *     ks8695_refill_rxbuffers - Re-fill the RX buffer ring
+ *     @ksp: The device to refill
+ *
+ *     Iterates the RX ring of the device looking for empty slots.
+ *     For each empty slot, we allocate and map a new SKB and give it
+ *     to the hardware.
+ *     This can be called from interrupt context safely.
+ */
+static void
+ks8695_refill_rxbuffers(struct ks8695_priv *ksp)
+{
+       /* Run around the RX ring, filling in any missing sk_buff's */
+       int buff_n;
+
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               if (!ksp->rx_buffers[buff_n].skb) {
+                       struct sk_buff *skb = dev_alloc_skb(MAX_RXBUF_SIZE);
+                       dma_addr_t mapping;
+
+                       ksp->rx_buffers[buff_n].skb = skb;
+                       if (skb == NULL) {
+                               /* Failed to allocate one, perhaps
+                                * we'll try again later.
+                                */
+                               break;
+                       }
+
+                       mapping = dma_map_single(ksp->dev, skb->data,
+                                                MAX_RXBUF_SIZE,
+                                                DMA_FROM_DEVICE);
+                       if (unlikely(dma_mapping_error(ksp->dev, mapping))) {
+                               /* Failed to DMA map this SKB, try later */
+                               dev_kfree_skb_irq(skb);
+                               ksp->rx_buffers[buff_n].skb = NULL;
+                               break;
+                       }
+                       ksp->rx_buffers[buff_n].dma_ptr = mapping;
+                       skb->dev = ksp->ndev;
+                       ksp->rx_buffers[buff_n].length = MAX_RXBUF_SIZE;
+
+                       /* Record this into the DMA ring */
+                       ksp->rx_ring[buff_n].data_ptr = cpu_to_le32(mapping);
+                       ksp->rx_ring[buff_n].length =
+                               cpu_to_le32(MAX_RXBUF_SIZE);
+
+                       wmb();
+
+                       /* And give ownership over to the hardware */
+                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+               }
+       }
+}
+
+/* Maximum number of multicast addresses which the KS8695 HW supports */
+#define KS8695_NR_ADDRESSES    16
+
+/**
+ *     ks8695_init_partial_multicast - Init the mcast addr registers
+ *     @ksp: The device to initialise
+ *     @addr: The multicast address list to use
+ *     @nr_addr: The number of addresses in the list
+ *
+ *     This routine is a helper for ks8695_set_multicast - it writes
+ *     the additional-address registers in the KS8695 ethernet device
+ *     and cleans up any others left behind.
+ */
+static void
+ks8695_init_partial_multicast(struct ks8695_priv *ksp,
+                             struct net_device *ndev)
+{
+       u32 low, high;
+       int i;
+       struct netdev_hw_addr *ha;
+
+       i = 0;
+       netdev_for_each_mc_addr(ha, ndev) {
+               /* Ran out of space in chip? */
+               BUG_ON(i == KS8695_NR_ADDRESSES);
+
+               low = (ha->addr[2] << 24) | (ha->addr[3] << 16) |
+                     (ha->addr[4] << 8) | (ha->addr[5]);
+               high = (ha->addr[0] << 8) | (ha->addr[1]);
+
+               ks8695_writereg(ksp, KS8695_AAL_(i), low);
+               ks8695_writereg(ksp, KS8695_AAH_(i), AAH_E | high);
+               i++;
+       }
+
+       /* Clear the remaining Additional Station Addresses */
+       for (; i < KS8695_NR_ADDRESSES; i++) {
+               ks8695_writereg(ksp, KS8695_AAL_(i), 0);
+               ks8695_writereg(ksp, KS8695_AAH_(i), 0);
+       }
+}
+
+/* Interrupt handling */
+
+/**
+ *     ks8695_tx_irq - Transmit IRQ handler
+ *     @irq: The IRQ which went off (ignored)
+ *     @dev_id: The net_device for the interrupt
+ *
+ *     Process the TX ring, clearing out any transmitted slots.
+ *     Allows the net_device to pass us new packets once slots are
+ *     freed.
+ */
+static irqreturn_t
+ks8695_tx_irq(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int buff_n;
+
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               if (ksp->tx_buffers[buff_n].skb &&
+                   !(ksp->tx_ring[buff_n].owner & cpu_to_le32(TDES_OWN))) {
+                       rmb();
+                       /* An SKB which is not owned by HW is present */
+                       /* Update the stats for the net_device */
+                       ndev->stats.tx_packets++;
+                       ndev->stats.tx_bytes += ksp->tx_buffers[buff_n].length;
+
+                       /* Free the packet from the ring */
+                       ksp->tx_ring[buff_n].data_ptr = 0;
+
+                       /* Free the sk_buff */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->tx_buffers[buff_n].dma_ptr,
+                                        ksp->tx_buffers[buff_n].length,
+                                        DMA_TO_DEVICE);
+                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+                       ksp->tx_buffers[buff_n].skb = NULL;
+                       ksp->tx_ring_used--;
+               }
+       }
+
+       netif_wake_queue(ndev);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ *     ks8695_get_rx_enable_bit - Get rx interrupt enable/status bit
+ *     @ksp: Private data for the KS8695 Ethernet
+ *
+ *    For KS8695 document:
+ *    Interrupt Enable Register (offset 0xE204)
+ *        Bit29 : WAN MAC Receive Interrupt Enable
+ *        Bit16 : LAN MAC Receive Interrupt Enable
+ *    Interrupt Status Register (Offset 0xF208)
+ *        Bit29: WAN MAC Receive Status
+ *        Bit16: LAN MAC Receive Status
+ *    So, this Rx interrrupt enable/status bit number is equal
+ *    as Rx IRQ number.
+ */
+static inline u32 ks8695_get_rx_enable_bit(struct ks8695_priv *ksp)
+{
+       return ksp->rx_irq;
+}
+
+/**
+ *     ks8695_rx_irq - Receive IRQ handler
+ *     @irq: The IRQ which went off (ignored)
+ *     @dev_id: The net_device for the interrupt
+ *
+ *     Inform NAPI that packet reception needs to be scheduled
+ */
+
+static irqreturn_t
+ks8695_rx_irq(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       spin_lock(&ksp->rx_lock);
+
+       if (napi_schedule_prep(&ksp->napi)) {
+               unsigned long status = readl(KS8695_IRQ_VA + KS8695_INTEN);
+               unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+               /*disable rx interrupt*/
+               status &= ~mask_bit;
+               writel(status , KS8695_IRQ_VA + KS8695_INTEN);
+               __napi_schedule(&ksp->napi);
+       }
+
+       spin_unlock(&ksp->rx_lock);
+       return IRQ_HANDLED;
+}
+
+/**
+ *     ks8695_rx - Receive packets called by NAPI poll method
+ *     @ksp: Private data for the KS8695 Ethernet
+ *     @budget: Number of packets allowed to process
+ */
+static int ks8695_rx(struct ks8695_priv *ksp, int budget)
+{
+       struct net_device *ndev = ksp->ndev;
+       struct sk_buff *skb;
+       int buff_n;
+       u32 flags;
+       int pktlen;
+       int received = 0;
+
+       buff_n = ksp->next_rx_desc_read;
+       while (received < budget
+                       && ksp->rx_buffers[buff_n].skb
+                       && (!(ksp->rx_ring[buff_n].status &
+                                       cpu_to_le32(RDES_OWN)))) {
+                       rmb();
+                       flags = le32_to_cpu(ksp->rx_ring[buff_n].status);
+
+                       /* Found an SKB which we own, this means we
+                        * received a packet
+                        */
+                       if ((flags & (RDES_FS | RDES_LS)) !=
+                           (RDES_FS | RDES_LS)) {
+                               /* This packet is not the first and
+                                * the last segment.  Therefore it is
+                                * a "spanning" packet and we can't
+                                * handle it
+                                */
+                               goto rx_failure;
+                       }
+
+                       if (flags & (RDES_ES | RDES_RE)) {
+                               /* It's an error packet */
+                               ndev->stats.rx_errors++;
+                               if (flags & RDES_TL)
+                                       ndev->stats.rx_length_errors++;
+                               if (flags & RDES_RF)
+                                       ndev->stats.rx_length_errors++;
+                               if (flags & RDES_CE)
+                                       ndev->stats.rx_crc_errors++;
+                               if (flags & RDES_RE)
+                                       ndev->stats.rx_missed_errors++;
+
+                               goto rx_failure;
+                       }
+
+                       pktlen = flags & RDES_FLEN;
+                       pktlen -= 4; /* Drop the CRC */
+
+                       /* Retrieve the sk_buff */
+                       skb = ksp->rx_buffers[buff_n].skb;
+
+                       /* Clear it from the ring */
+                       ksp->rx_buffers[buff_n].skb = NULL;
+                       ksp->rx_ring[buff_n].data_ptr = 0;
+
+                       /* Unmap the SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->rx_buffers[buff_n].dma_ptr,
+                                        ksp->rx_buffers[buff_n].length,
+                                        DMA_FROM_DEVICE);
+
+                       /* Relinquish the SKB to the network layer */
+                       skb_put(skb, pktlen);
+                       skb->protocol = eth_type_trans(skb, ndev);
+                       netif_receive_skb(skb);
+
+                       /* Record stats */
+                       ndev->stats.rx_packets++;
+                       ndev->stats.rx_bytes += pktlen;
+                       goto rx_finished;
+
+rx_failure:
+                       /* This ring entry is an error, but we can
+                        * re-use the skb
+                        */
+                       /* Give the ring entry back to the hardware */
+                       ksp->rx_ring[buff_n].status = cpu_to_le32(RDES_OWN);
+rx_finished:
+                       received++;
+                       buff_n = (buff_n + 1) & MAX_RX_DESC_MASK;
+       }
+
+       /* And note which RX descriptor we last did */
+       ksp->next_rx_desc_read = buff_n;
+
+       /* And refill the buffers */
+       ks8695_refill_rxbuffers(ksp);
+
+       /* Kick the RX DMA engine, in case it became suspended */
+       ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+       return received;
+}
+
+
+/**
+ *     ks8695_poll - Receive packet by NAPI poll method
+ *     @ksp: Private data for the KS8695 Ethernet
+ *     @budget: The remaining number packets for network subsystem
+ *
+ *     Invoked by the network core when it requests for new
+ *     packets from the driver
+ */
+static int ks8695_poll(struct napi_struct *napi, int budget)
+{
+       struct ks8695_priv *ksp = container_of(napi, struct ks8695_priv, napi);
+       unsigned long  work_done;
+
+       unsigned long isr = readl(KS8695_IRQ_VA + KS8695_INTEN);
+       unsigned long mask_bit = 1 << ks8695_get_rx_enable_bit(ksp);
+
+       work_done = ks8695_rx(ksp, budget);
+
+       if (work_done < budget) {
+               unsigned long flags;
+               spin_lock_irqsave(&ksp->rx_lock, flags);
+               __napi_complete(napi);
+               /*enable rx interrupt*/
+               writel(isr | mask_bit, KS8695_IRQ_VA + KS8695_INTEN);
+               spin_unlock_irqrestore(&ksp->rx_lock, flags);
+       }
+       return work_done;
+}
+
+/**
+ *     ks8695_link_irq - Link change IRQ handler
+ *     @irq: The IRQ which went off (ignored)
+ *     @dev_id: The net_device for the interrupt
+ *
+ *     The WAN interface can generate an IRQ when the link changes,
+ *     report this to the net layer and the user.
+ */
+static irqreturn_t
+ks8695_link_irq(int irq, void *dev_id)
+{
+       struct net_device *ndev = (struct net_device *)dev_id;
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       if (ctrl & WMC_WLS) {
+               netif_carrier_on(ndev);
+               if (netif_msg_link(ksp))
+                       dev_info(ksp->dev,
+                                "%s: Link is now up (10%sMbps/%s-duplex)\n",
+                                ndev->name,
+                                (ctrl & WMC_WSS) ? "0" : "",
+                                (ctrl & WMC_WDS) ? "Full" : "Half");
+       } else {
+               netif_carrier_off(ndev);
+               if (netif_msg_link(ksp))
+                       dev_info(ksp->dev, "%s: Link is now down.\n",
+                                ndev->name);
+       }
+
+       return IRQ_HANDLED;
+}
+
+
+/* KS8695 Device functions */
+
+/**
+ *     ks8695_reset - Reset a KS8695 ethernet interface
+ *     @ksp: The interface to reset
+ *
+ *     Perform an engine reset of the interface and re-program it
+ *     with sensible defaults.
+ */
+static void
+ks8695_reset(struct ks8695_priv *ksp)
+{
+       int reset_timeout = watchdog;
+       /* Issue the reset via the TX DMA control register */
+       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TRST);
+       while (reset_timeout--) {
+               if (!(ks8695_readreg(ksp, KS8695_DTXC) & DTXC_TRST))
+                       break;
+               msleep(1);
+       }
+
+       if (reset_timeout < 0) {
+               dev_crit(ksp->dev,
+                        "Timeout waiting for DMA engines to reset\n");
+               /* And blithely carry on */
+       }
+
+       /* Definitely wait long enough before attempting to program
+        * the engines
+        */
+       msleep(10);
+
+       /* RX: unicast and broadcast */
+       ks8695_writereg(ksp, KS8695_DRXC, DRXC_RU | DRXC_RB);
+       /* TX: pad and add CRC */
+       ks8695_writereg(ksp, KS8695_DTXC, DTXC_TEP | DTXC_TAC);
+}
+
+/**
+ *     ks8695_shutdown - Shut down a KS8695 ethernet interface
+ *     @ksp: The interface to shut down
+ *
+ *     This disables packet RX/TX, cleans up IRQs, drains the rings,
+ *     and basically places the interface into a clean shutdown
+ *     state.
+ */
+static void
+ks8695_shutdown(struct ks8695_priv *ksp)
+{
+       u32 ctrl;
+       int buff_n;
+
+       /* Disable packet transmission */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       ks8695_writereg(ksp, KS8695_DTXC, ctrl & ~DTXC_TE);
+
+       /* Disable packet reception */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl & ~DRXC_RE);
+
+       /* Release the IRQs */
+       free_irq(ksp->rx_irq, ksp->ndev);
+       free_irq(ksp->tx_irq, ksp->ndev);
+       if (ksp->link_irq != -1)
+               free_irq(ksp->link_irq, ksp->ndev);
+
+       /* Throw away any pending TX packets */
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               if (ksp->tx_buffers[buff_n].skb) {
+                       /* Remove this SKB from the TX ring */
+                       ksp->tx_ring[buff_n].owner = 0;
+                       ksp->tx_ring[buff_n].status = 0;
+                       ksp->tx_ring[buff_n].data_ptr = 0;
+
+                       /* Unmap and bin this SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->tx_buffers[buff_n].dma_ptr,
+                                        ksp->tx_buffers[buff_n].length,
+                                        DMA_TO_DEVICE);
+                       dev_kfree_skb_irq(ksp->tx_buffers[buff_n].skb);
+                       ksp->tx_buffers[buff_n].skb = NULL;
+               }
+       }
+
+       /* Purge the RX buffers */
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               if (ksp->rx_buffers[buff_n].skb) {
+                       /* Remove the SKB from the RX ring */
+                       ksp->rx_ring[buff_n].status = 0;
+                       ksp->rx_ring[buff_n].data_ptr = 0;
+
+                       /* Unmap and bin the SKB */
+                       dma_unmap_single(ksp->dev,
+                                        ksp->rx_buffers[buff_n].dma_ptr,
+                                        ksp->rx_buffers[buff_n].length,
+                                        DMA_FROM_DEVICE);
+                       dev_kfree_skb_irq(ksp->rx_buffers[buff_n].skb);
+                       ksp->rx_buffers[buff_n].skb = NULL;
+               }
+       }
+}
+
+
+/**
+ *     ks8695_setup_irq - IRQ setup helper function
+ *     @irq: The IRQ number to claim
+ *     @irq_name: The name to give the IRQ claimant
+ *     @handler: The function to call to handle the IRQ
+ *     @ndev: The net_device to pass in as the dev_id argument to the handler
+ *
+ *     Return 0 on success.
+ */
+static int
+ks8695_setup_irq(int irq, const char *irq_name,
+                irq_handler_t handler, struct net_device *ndev)
+{
+       int ret;
+
+       ret = request_irq(irq, handler, IRQF_SHARED, irq_name, ndev);
+
+       if (ret) {
+               dev_err(&ndev->dev, "failure to request IRQ %d\n", irq);
+               return ret;
+       }
+
+       return 0;
+}
+
+/**
+ *     ks8695_init_net - Initialise a KS8695 ethernet interface
+ *     @ksp: The interface to initialise
+ *
+ *     This routine fills the RX ring, initialises the DMA engines,
+ *     allocates the IRQs and then starts the packet TX and RX
+ *     engines.
+ */
+static int
+ks8695_init_net(struct ks8695_priv *ksp)
+{
+       int ret;
+       u32 ctrl;
+
+       ks8695_refill_rxbuffers(ksp);
+
+       /* Initialise the DMA engines */
+       ks8695_writereg(ksp, KS8695_RDLB, (u32) ksp->rx_ring_dma);
+       ks8695_writereg(ksp, KS8695_TDLB, (u32) ksp->tx_ring_dma);
+
+       /* Request the IRQs */
+       ret = ks8695_setup_irq(ksp->rx_irq, ksp->rx_irq_name,
+                              ks8695_rx_irq, ksp->ndev);
+       if (ret)
+               return ret;
+       ret = ks8695_setup_irq(ksp->tx_irq, ksp->tx_irq_name,
+                              ks8695_tx_irq, ksp->ndev);
+       if (ret)
+               return ret;
+       if (ksp->link_irq != -1) {
+               ret = ks8695_setup_irq(ksp->link_irq, ksp->link_irq_name,
+                                      ks8695_link_irq, ksp->ndev);
+               if (ret)
+                       return ret;
+       }
+
+       /* Set up the ring indices */
+       ksp->next_rx_desc_read = 0;
+       ksp->tx_ring_next_slot = 0;
+       ksp->tx_ring_used = 0;
+
+       /* Bring up transmission */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       /* Enable packet transmission */
+       ks8695_writereg(ksp, KS8695_DTXC, ctrl | DTXC_TE);
+
+       /* Bring up the reception */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       /* Enable packet reception */
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl | DRXC_RE);
+       /* And start the DMA engine */
+       ks8695_writereg(ksp, KS8695_DRSC, 0);
+
+       /* All done */
+       return 0;
+}
+
+/**
+ *     ks8695_release_device - HW resource release for KS8695 e-net
+ *     @ksp: The device to be freed
+ *
+ *     This unallocates io memory regions, dma-coherent regions etc
+ *     which were allocated in ks8695_probe.
+ */
+static void
+ks8695_release_device(struct ks8695_priv *ksp)
+{
+       /* Unmap the registers */
+       iounmap(ksp->io_regs);
+       if (ksp->phyiface_regs)
+               iounmap(ksp->phyiface_regs);
+
+       /* And release the request */
+       release_resource(ksp->regs_req);
+       kfree(ksp->regs_req);
+       if (ksp->phyiface_req) {
+               release_resource(ksp->phyiface_req);
+               kfree(ksp->phyiface_req);
+       }
+
+       /* Free the ring buffers */
+       dma_free_coherent(ksp->dev, RING_DMA_SIZE,
+                         ksp->ring_base, ksp->ring_base_dma);
+}
+
+/* Ethtool support */
+
+/**
+ *     ks8695_get_msglevel - Get the messages enabled for emission
+ *     @ndev: The network device to read from
+ */
+static u32
+ks8695_get_msglevel(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       return ksp->msg_enable;
+}
+
+/**
+ *     ks8695_set_msglevel - Set the messages enabled for emission
+ *     @ndev: The network device to configure
+ *     @value: The messages to set for emission
+ */
+static void
+ks8695_set_msglevel(struct net_device *ndev, u32 value)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       ksp->msg_enable = value;
+}
+
+/**
+ *     ks8695_wan_get_settings - Get device-specific settings.
+ *     @ndev: The network device to read settings from
+ *     @cmd: The ethtool structure to read into
+ */
+static int
+ks8695_wan_get_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       /* All ports on the KS8695 support these... */
+       cmd->supported = (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
+                         SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
+                         SUPPORTED_TP | SUPPORTED_MII);
+       cmd->transceiver = XCVR_INTERNAL;
+
+       cmd->advertising = ADVERTISED_TP | ADVERTISED_MII;
+       cmd->port = PORT_MII;
+       cmd->supported |= (SUPPORTED_Autoneg | SUPPORTED_Pause);
+       cmd->phy_address = 0;
+
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+       if ((ctrl & WMC_WAND) == 0) {
+               /* auto-negotiation is enabled */
+               cmd->advertising |= ADVERTISED_Autoneg;
+               if (ctrl & WMC_WANA100F)
+                       cmd->advertising |= ADVERTISED_100baseT_Full;
+               if (ctrl & WMC_WANA100H)
+                       cmd->advertising |= ADVERTISED_100baseT_Half;
+               if (ctrl & WMC_WANA10F)
+                       cmd->advertising |= ADVERTISED_10baseT_Full;
+               if (ctrl & WMC_WANA10H)
+                       cmd->advertising |= ADVERTISED_10baseT_Half;
+               if (ctrl & WMC_WANAP)
+                       cmd->advertising |= ADVERTISED_Pause;
+               cmd->autoneg = AUTONEG_ENABLE;
+
+               ethtool_cmd_speed_set(cmd,
+                                     (ctrl & WMC_WSS) ? SPEED_100 : SPEED_10);
+               cmd->duplex = (ctrl & WMC_WDS) ?
+                       DUPLEX_FULL : DUPLEX_HALF;
+       } else {
+               /* auto-negotiation is disabled */
+               cmd->autoneg = AUTONEG_DISABLE;
+
+               ethtool_cmd_speed_set(cmd, ((ctrl & WMC_WANF100) ?
+                                           SPEED_100 : SPEED_10));
+               cmd->duplex = (ctrl & WMC_WANFF) ?
+                       DUPLEX_FULL : DUPLEX_HALF;
+       }
+
+       return 0;
+}
+
+/**
+ *     ks8695_wan_set_settings - Set device-specific settings.
+ *     @ndev: The network device to configure
+ *     @cmd: The settings to configure
+ */
+static int
+ks8695_wan_set_settings(struct net_device *ndev, struct ethtool_cmd *cmd)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       if ((cmd->speed != SPEED_10) && (cmd->speed != SPEED_100))
+               return -EINVAL;
+       if ((cmd->duplex != DUPLEX_HALF) && (cmd->duplex != DUPLEX_FULL))
+               return -EINVAL;
+       if (cmd->port != PORT_MII)
+               return -EINVAL;
+       if (cmd->transceiver != XCVR_INTERNAL)
+               return -EINVAL;
+       if ((cmd->autoneg != AUTONEG_DISABLE) &&
+           (cmd->autoneg != AUTONEG_ENABLE))
+               return -EINVAL;
+
+       if (cmd->autoneg == AUTONEG_ENABLE) {
+               if ((cmd->advertising & (ADVERTISED_10baseT_Half |
+                               ADVERTISED_10baseT_Full |
+                               ADVERTISED_100baseT_Half |
+                               ADVERTISED_100baseT_Full)) == 0)
+                       return -EINVAL;
+
+               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+               ctrl &= ~(WMC_WAND | WMC_WANA100F | WMC_WANA100H |
+                         WMC_WANA10F | WMC_WANA10H);
+               if (cmd->advertising & ADVERTISED_100baseT_Full)
+                       ctrl |= WMC_WANA100F;
+               if (cmd->advertising & ADVERTISED_100baseT_Half)
+                       ctrl |= WMC_WANA100H;
+               if (cmd->advertising & ADVERTISED_10baseT_Full)
+                       ctrl |= WMC_WANA10F;
+               if (cmd->advertising & ADVERTISED_10baseT_Half)
+                       ctrl |= WMC_WANA10H;
+
+               /* force a re-negotiation */
+               ctrl |= WMC_WANR;
+               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+       } else {
+               ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+               /* disable auto-negotiation */
+               ctrl |= WMC_WAND;
+               ctrl &= ~(WMC_WANF100 | WMC_WANFF);
+
+               if (cmd->speed == SPEED_100)
+                       ctrl |= WMC_WANF100;
+               if (cmd->duplex == DUPLEX_FULL)
+                       ctrl |= WMC_WANFF;
+
+               writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+       }
+
+       return 0;
+}
+
+/**
+ *     ks8695_wan_nwayreset - Restart the autonegotiation on the port.
+ *     @ndev: The network device to restart autoneotiation on
+ */
+static int
+ks8695_wan_nwayreset(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+       if ((ctrl & WMC_WAND) == 0)
+               writel(ctrl | WMC_WANR,
+                      ksp->phyiface_regs + KS8695_WMC);
+       else
+               /* auto-negotiation not enabled */
+               return -EINVAL;
+
+       return 0;
+}
+
+/**
+ *     ks8695_wan_get_pause - Retrieve network pause/flow-control advertising
+ *     @ndev: The device to retrieve settings from
+ *     @param: The structure to fill out with the information
+ */
+static void
+ks8695_wan_get_pause(struct net_device *ndev, struct ethtool_pauseparam *param)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       ctrl = readl(ksp->phyiface_regs + KS8695_WMC);
+
+       /* advertise Pause */
+       param->autoneg = (ctrl & WMC_WANAP);
+
+       /* current Rx Flow-control */
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+       param->rx_pause = (ctrl & DRXC_RFCE);
+
+       /* current Tx Flow-control */
+       ctrl = ks8695_readreg(ksp, KS8695_DTXC);
+       param->tx_pause = (ctrl & DTXC_TFCE);
+}
+
+/**
+ *     ks8695_get_drvinfo - Retrieve driver information
+ *     @ndev: The network device to retrieve info about
+ *     @info: The info structure to fill out.
+ */
+static void
+ks8695_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *info)
+{
+       strlcpy(info->driver, MODULENAME, sizeof(info->driver));
+       strlcpy(info->version, MODULEVERSION, sizeof(info->version));
+       strlcpy(info->bus_info, dev_name(ndev->dev.parent),
+               sizeof(info->bus_info));
+}
+
+static const struct ethtool_ops ks8695_ethtool_ops = {
+       .get_msglevel   = ks8695_get_msglevel,
+       .set_msglevel   = ks8695_set_msglevel,
+       .get_drvinfo    = ks8695_get_drvinfo,
+};
+
+static const struct ethtool_ops ks8695_wan_ethtool_ops = {
+       .get_msglevel   = ks8695_get_msglevel,
+       .set_msglevel   = ks8695_set_msglevel,
+       .get_settings   = ks8695_wan_get_settings,
+       .set_settings   = ks8695_wan_set_settings,
+       .nway_reset     = ks8695_wan_nwayreset,
+       .get_link       = ethtool_op_get_link,
+       .get_pauseparam = ks8695_wan_get_pause,
+       .get_drvinfo    = ks8695_get_drvinfo,
+};
+
+/* Network device interface functions */
+
+/**
+ *     ks8695_set_mac - Update MAC in net dev and HW
+ *     @ndev: The network device to update
+ *     @addr: The new MAC address to set
+ */
+static int
+ks8695_set_mac(struct net_device *ndev, void *addr)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       struct sockaddr *address = addr;
+
+       if (!is_valid_ether_addr(address->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(ndev->dev_addr, address->sa_data, ndev->addr_len);
+
+       ks8695_update_mac(ksp);
+
+       dev_dbg(ksp->dev, "%s: Updated MAC address to %pM\n",
+               ndev->name, ndev->dev_addr);
+
+       return 0;
+}
+
+/**
+ *     ks8695_set_multicast - Set up the multicast behaviour of the interface
+ *     @ndev: The net_device to configure
+ *
+ *     This routine, called by the net layer, configures promiscuity
+ *     and multicast reception behaviour for the interface.
+ */
+static void
+ks8695_set_multicast(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       u32 ctrl;
+
+       ctrl = ks8695_readreg(ksp, KS8695_DRXC);
+
+       if (ndev->flags & IFF_PROMISC) {
+               /* enable promiscuous mode */
+               ctrl |= DRXC_RA;
+       } else if (ndev->flags & ~IFF_PROMISC) {
+               /* disable promiscuous mode */
+               ctrl &= ~DRXC_RA;
+       }
+
+       if (ndev->flags & IFF_ALLMULTI) {
+               /* enable all multicast mode */
+               ctrl |= DRXC_RM;
+       } else if (netdev_mc_count(ndev) > KS8695_NR_ADDRESSES) {
+               /* more specific multicast addresses than can be
+                * handled in hardware
+                */
+               ctrl |= DRXC_RM;
+       } else {
+               /* enable specific multicasts */
+               ctrl &= ~DRXC_RM;
+               ks8695_init_partial_multicast(ksp, ndev);
+       }
+
+       ks8695_writereg(ksp, KS8695_DRXC, ctrl);
+}
+
+/**
+ *     ks8695_timeout - Handle a network tx/rx timeout.
+ *     @ndev: The net_device which timed out.
+ *
+ *     A network transaction timed out, reset the device.
+ */
+static void
+ks8695_timeout(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       ks8695_shutdown(ksp);
+
+       ks8695_reset(ksp);
+
+       ks8695_update_mac(ksp);
+
+       /* We ignore the return from this since it managed to init
+        * before it probably will be okay to init again.
+        */
+       ks8695_init_net(ksp);
+
+       /* Reconfigure promiscuity etc */
+       ks8695_set_multicast(ndev);
+
+       /* And start the TX queue once more */
+       netif_start_queue(ndev);
+}
+
+/**
+ *     ks8695_start_xmit - Start a packet transmission
+ *     @skb: The packet to transmit
+ *     @ndev: The network device to send the packet on
+ *
+ *     This routine, called by the net layer, takes ownership of the
+ *     sk_buff and adds it to the TX ring. It then kicks the TX DMA
+ *     engine to ensure transmission begins.
+ */
+static int
+ks8695_start_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int buff_n;
+       dma_addr_t dmap;
+
+       spin_lock_irq(&ksp->txq_lock);
+
+       if (ksp->tx_ring_used == MAX_TX_DESC) {
+               /* Somehow we got entered when we have no room */
+               spin_unlock_irq(&ksp->txq_lock);
+               return NETDEV_TX_BUSY;
+       }
+
+       buff_n = ksp->tx_ring_next_slot;
+
+       BUG_ON(ksp->tx_buffers[buff_n].skb);
+
+       dmap = dma_map_single(ksp->dev, skb->data, skb->len, DMA_TO_DEVICE);
+       if (unlikely(dma_mapping_error(ksp->dev, dmap))) {
+               /* Failed to DMA map this SKB, give it back for now */
+               spin_unlock_irq(&ksp->txq_lock);
+               dev_dbg(ksp->dev, "%s: Could not map DMA memory for "\
+                       "transmission, trying later\n", ndev->name);
+               return NETDEV_TX_BUSY;
+       }
+
+       ksp->tx_buffers[buff_n].dma_ptr = dmap;
+       /* Mapped okay, store the buffer pointer and length for later */
+       ksp->tx_buffers[buff_n].skb = skb;
+       ksp->tx_buffers[buff_n].length = skb->len;
+
+       /* Fill out the TX descriptor */
+       ksp->tx_ring[buff_n].data_ptr =
+               cpu_to_le32(ksp->tx_buffers[buff_n].dma_ptr);
+       ksp->tx_ring[buff_n].status =
+               cpu_to_le32(TDES_IC | TDES_FS | TDES_LS |
+                           (skb->len & TDES_TBS));
+
+       wmb();
+
+       /* Hand it over to the hardware */
+       ksp->tx_ring[buff_n].owner = cpu_to_le32(TDES_OWN);
+
+       if (++ksp->tx_ring_used == MAX_TX_DESC)
+               netif_stop_queue(ndev);
+
+       /* Kick the TX DMA in case it decided to go IDLE */
+       ks8695_writereg(ksp, KS8695_DTSC, 0);
+
+       /* And update the next ring slot */
+       ksp->tx_ring_next_slot = (buff_n + 1) & MAX_TX_DESC_MASK;
+
+       spin_unlock_irq(&ksp->txq_lock);
+       return NETDEV_TX_OK;
+}
+
+/**
+ *     ks8695_stop - Stop (shutdown) a KS8695 ethernet interface
+ *     @ndev: The net_device to stop
+ *
+ *     This disables the TX queue and cleans up a KS8695 ethernet
+ *     device.
+ */
+static int
+ks8695_stop(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       netif_stop_queue(ndev);
+       napi_disable(&ksp->napi);
+
+       ks8695_shutdown(ksp);
+
+       return 0;
+}
+
+/**
+ *     ks8695_open - Open (bring up) a KS8695 ethernet interface
+ *     @ndev: The net_device to open
+ *
+ *     This resets, configures the MAC, initialises the RX ring and
+ *     DMA engines and starts the TX queue for a KS8695 ethernet
+ *     device.
+ */
+static int
+ks8695_open(struct net_device *ndev)
+{
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+       int ret;
+
+       if (!is_valid_ether_addr(ndev->dev_addr))
+               return -EADDRNOTAVAIL;
+
+       ks8695_reset(ksp);
+
+       ks8695_update_mac(ksp);
+
+       ret = ks8695_init_net(ksp);
+       if (ret) {
+               ks8695_shutdown(ksp);
+               return ret;
+       }
+
+       napi_enable(&ksp->napi);
+       netif_start_queue(ndev);
+
+       return 0;
+}
+
+/* Platform device driver */
+
+/**
+ *     ks8695_init_switch - Init LAN switch to known good defaults.
+ *     @ksp: The device to initialise
+ *
+ *     This initialises the LAN switch in the KS8695 to a known-good
+ *     set of defaults.
+ */
+static void __devinit
+ks8695_init_switch(struct ks8695_priv *ksp)
+{
+       u32 ctrl;
+
+       /* Default value for SEC0 according to datasheet */
+       ctrl = 0x40819e00;
+
+       /* LED0 = Speed  LED1 = Link/Activity */
+       ctrl &= ~(SEC0_LLED1S | SEC0_LLED0S);
+       ctrl |= (LLED0S_LINK | LLED1S_LINK_ACTIVITY);
+
+       /* Enable Switch */
+       ctrl |= SEC0_ENABLE;
+
+       writel(ctrl, ksp->phyiface_regs + KS8695_SEC0);
+
+       /* Defaults for SEC1 */
+       writel(0x9400100, ksp->phyiface_regs + KS8695_SEC1);
+}
+
+/**
+ *     ks8695_init_wan_phy - Initialise the WAN PHY to sensible defaults
+ *     @ksp: The device to initialise
+ *
+ *     This initialises a KS8695's WAN phy to sensible values for
+ *     autonegotiation etc.
+ */
+static void __devinit
+ks8695_init_wan_phy(struct ks8695_priv *ksp)
+{
+       u32 ctrl;
+
+       /* Support auto-negotiation */
+       ctrl = (WMC_WANAP | WMC_WANA100F | WMC_WANA100H |
+               WMC_WANA10F | WMC_WANA10H);
+
+       /* LED0 = Activity , LED1 = Link */
+       ctrl |= (WLED0S_ACTIVITY | WLED1S_LINK);
+
+       /* Restart Auto-negotiation */
+       ctrl |= WMC_WANR;
+
+       writel(ctrl, ksp->phyiface_regs + KS8695_WMC);
+
+       writel(0, ksp->phyiface_regs + KS8695_WPPM);
+       writel(0, ksp->phyiface_regs + KS8695_PPS);
+}
+
+static const struct net_device_ops ks8695_netdev_ops = {
+       .ndo_open               = ks8695_open,
+       .ndo_stop               = ks8695_stop,
+       .ndo_start_xmit         = ks8695_start_xmit,
+       .ndo_tx_timeout         = ks8695_timeout,
+       .ndo_set_mac_address    = ks8695_set_mac,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_multicast_list = ks8695_set_multicast,
+};
+
+/**
+ *     ks8695_probe - Probe and initialise a KS8695 ethernet interface
+ *     @pdev: The platform device to probe
+ *
+ *     Initialise a KS8695 ethernet device from platform data.
+ *
+ *     This driver requires at least one IORESOURCE_MEM for the
+ *     registers and two IORESOURCE_IRQ for the RX and TX IRQs
+ *     respectively. It can optionally take an additional
+ *     IORESOURCE_MEM for the switch or phy in the case of the lan or
+ *     wan ports, and an IORESOURCE_IRQ for the link IRQ for the wan
+ *     port.
+ */
+static int __devinit
+ks8695_probe(struct platform_device *pdev)
+{
+       struct ks8695_priv *ksp;
+       struct net_device *ndev;
+       struct resource *regs_res, *phyiface_res;
+       struct resource *rxirq_res, *txirq_res, *linkirq_res;
+       int ret = 0;
+       int buff_n;
+       u32 machigh, maclow;
+
+       /* Initialise a net_device */
+       ndev = alloc_etherdev(sizeof(struct ks8695_priv));
+       if (!ndev) {
+               dev_err(&pdev->dev, "could not allocate device.\n");
+               return -ENOMEM;
+       }
+
+       SET_NETDEV_DEV(ndev, &pdev->dev);
+
+       dev_dbg(&pdev->dev, "ks8695_probe() called\n");
+
+       /* Configure our private structure a little */
+       ksp = netdev_priv(ndev);
+
+       ksp->dev = &pdev->dev;
+       ksp->ndev = ndev;
+       ksp->msg_enable = NETIF_MSG_LINK;
+
+       /* Retrieve resources */
+       regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phyiface_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+       rxirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       txirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+       linkirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 2);
+
+       if (!(regs_res && rxirq_res && txirq_res)) {
+               dev_err(ksp->dev, "insufficient resources\n");
+               ret = -ENOENT;
+               goto failure;
+       }
+
+       ksp->regs_req = request_mem_region(regs_res->start,
+                                          resource_size(regs_res),
+                                          pdev->name);
+
+       if (!ksp->regs_req) {
+               dev_err(ksp->dev, "cannot claim register space\n");
+               ret = -EIO;
+               goto failure;
+       }
+
+       ksp->io_regs = ioremap(regs_res->start, resource_size(regs_res));
+
+       if (!ksp->io_regs) {
+               dev_err(ksp->dev, "failed to ioremap registers\n");
+               ret = -EINVAL;
+               goto failure;
+       }
+
+       if (phyiface_res) {
+               ksp->phyiface_req =
+                       request_mem_region(phyiface_res->start,
+                                          resource_size(phyiface_res),
+                                          phyiface_res->name);
+
+               if (!ksp->phyiface_req) {
+                       dev_err(ksp->dev,
+                               "cannot claim switch register space\n");
+                       ret = -EIO;
+                       goto failure;
+               }
+
+               ksp->phyiface_regs = ioremap(phyiface_res->start,
+                                            resource_size(phyiface_res));
+
+               if (!ksp->phyiface_regs) {
+                       dev_err(ksp->dev,
+                               "failed to ioremap switch registers\n");
+                       ret = -EINVAL;
+                       goto failure;
+               }
+       }
+
+       ksp->rx_irq = rxirq_res->start;
+       ksp->rx_irq_name = rxirq_res->name ? rxirq_res->name : "Ethernet RX";
+       ksp->tx_irq = txirq_res->start;
+       ksp->tx_irq_name = txirq_res->name ? txirq_res->name : "Ethernet TX";
+       ksp->link_irq = (linkirq_res ? linkirq_res->start : -1);
+       ksp->link_irq_name = (linkirq_res && linkirq_res->name) ?
+               linkirq_res->name : "Ethernet Link";
+
+       /* driver system setup */
+       ndev->netdev_ops = &ks8695_netdev_ops;
+       ndev->watchdog_timeo     = msecs_to_jiffies(watchdog);
+
+       netif_napi_add(ndev, &ksp->napi, ks8695_poll, NAPI_WEIGHT);
+
+       /* Retrieve the default MAC addr from the chip. */
+       /* The bootloader should have left it in there for us. */
+
+       machigh = ks8695_readreg(ksp, KS8695_MAH);
+       maclow = ks8695_readreg(ksp, KS8695_MAL);
+
+       ndev->dev_addr[0] = (machigh >> 8) & 0xFF;
+       ndev->dev_addr[1] = machigh & 0xFF;
+       ndev->dev_addr[2] = (maclow >> 24) & 0xFF;
+       ndev->dev_addr[3] = (maclow >> 16) & 0xFF;
+       ndev->dev_addr[4] = (maclow >> 8) & 0xFF;
+       ndev->dev_addr[5] = maclow & 0xFF;
+
+       if (!is_valid_ether_addr(ndev->dev_addr))
+               dev_warn(ksp->dev, "%s: Invalid ethernet MAC address. Please "
+                        "set using ifconfig\n", ndev->name);
+
+       /* In order to be efficient memory-wise, we allocate both
+        * rings in one go.
+        */
+       ksp->ring_base = dma_alloc_coherent(&pdev->dev, RING_DMA_SIZE,
+                                           &ksp->ring_base_dma, GFP_KERNEL);
+       if (!ksp->ring_base) {
+               ret = -ENOMEM;
+               goto failure;
+       }
+
+       /* Specify the TX DMA ring buffer */
+       ksp->tx_ring = ksp->ring_base;
+       ksp->tx_ring_dma = ksp->ring_base_dma;
+
+       /* And initialise the queue's lock */
+       spin_lock_init(&ksp->txq_lock);
+       spin_lock_init(&ksp->rx_lock);
+
+       /* Specify the RX DMA ring buffer */
+       ksp->rx_ring = ksp->ring_base + TX_RING_DMA_SIZE;
+       ksp->rx_ring_dma = ksp->ring_base_dma + TX_RING_DMA_SIZE;
+
+       /* Zero the descriptor rings */
+       memset(ksp->tx_ring, 0, TX_RING_DMA_SIZE);
+       memset(ksp->rx_ring, 0, RX_RING_DMA_SIZE);
+
+       /* Build the rings */
+       for (buff_n = 0; buff_n < MAX_TX_DESC; ++buff_n) {
+               ksp->tx_ring[buff_n].next_desc =
+                       cpu_to_le32(ksp->tx_ring_dma +
+                                   (sizeof(struct tx_ring_desc) *
+                                    ((buff_n + 1) & MAX_TX_DESC_MASK)));
+       }
+
+       for (buff_n = 0; buff_n < MAX_RX_DESC; ++buff_n) {
+               ksp->rx_ring[buff_n].next_desc =
+                       cpu_to_le32(ksp->rx_ring_dma +
+                                   (sizeof(struct rx_ring_desc) *
+                                    ((buff_n + 1) & MAX_RX_DESC_MASK)));
+       }
+
+       /* Initialise the port (physically) */
+       if (ksp->phyiface_regs && ksp->link_irq == -1) {
+               ks8695_init_switch(ksp);
+               ksp->dtype = KS8695_DTYPE_LAN;
+               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+       } else if (ksp->phyiface_regs && ksp->link_irq != -1) {
+               ks8695_init_wan_phy(ksp);
+               ksp->dtype = KS8695_DTYPE_WAN;
+               SET_ETHTOOL_OPS(ndev, &ks8695_wan_ethtool_ops);
+       } else {
+               /* No initialisation since HPNA does not have a PHY */
+               ksp->dtype = KS8695_DTYPE_HPNA;
+               SET_ETHTOOL_OPS(ndev, &ks8695_ethtool_ops);
+       }
+
+       /* And bring up the net_device with the net core */
+       platform_set_drvdata(pdev, ndev);
+       ret = register_netdev(ndev);
+
+       if (ret == 0) {
+               dev_info(ksp->dev, "ks8695 ethernet (%s) MAC: %pM\n",
+                        ks8695_port_type(ksp), ndev->dev_addr);
+       } else {
+               /* Report the failure to register the net_device */
+               dev_err(ksp->dev, "ks8695net: failed to register netdev.\n");
+               goto failure;
+       }
+
+       /* All is well */
+       return 0;
+
+       /* Error exit path */
+failure:
+       ks8695_release_device(ksp);
+       free_netdev(ndev);
+
+       return ret;
+}
+
+/**
+ *     ks8695_drv_suspend - Suspend a KS8695 ethernet platform device.
+ *     @pdev: The device to suspend
+ *     @state: The suspend state
+ *
+ *     This routine detaches and shuts down a KS8695 ethernet device.
+ */
+static int
+ks8695_drv_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       ksp->in_suspend = 1;
+
+       if (netif_running(ndev)) {
+               netif_device_detach(ndev);
+               ks8695_shutdown(ksp);
+       }
+
+       return 0;
+}
+
+/**
+ *     ks8695_drv_resume - Resume a KS8695 ethernet platform device.
+ *     @pdev: The device to resume
+ *
+ *     This routine re-initialises and re-attaches a KS8695 ethernet
+ *     device.
+ */
+static int
+ks8695_drv_resume(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       if (netif_running(ndev)) {
+               ks8695_reset(ksp);
+               ks8695_init_net(ksp);
+               ks8695_set_multicast(ndev);
+               netif_device_attach(ndev);
+       }
+
+       ksp->in_suspend = 0;
+
+       return 0;
+}
+
+/**
+ *     ks8695_drv_remove - Remove a KS8695 net device on driver unload.
+ *     @pdev: The platform device to remove
+ *
+ *     This unregisters and releases a KS8695 ethernet device.
+ */
+static int __devexit
+ks8695_drv_remove(struct platform_device *pdev)
+{
+       struct net_device *ndev = platform_get_drvdata(pdev);
+       struct ks8695_priv *ksp = netdev_priv(ndev);
+
+       platform_set_drvdata(pdev, NULL);
+       netif_napi_del(&ksp->napi);
+
+       unregister_netdev(ndev);
+       ks8695_release_device(ksp);
+       free_netdev(ndev);
+
+       dev_dbg(&pdev->dev, "released and freed device\n");
+       return 0;
+}
+
+static struct platform_driver ks8695_driver = {
+       .driver = {
+               .name   = MODULENAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ks8695_probe,
+       .remove         = __devexit_p(ks8695_drv_remove),
+       .suspend        = ks8695_drv_suspend,
+       .resume         = ks8695_drv_resume,
+};
+
+/* Module interface */
+
+static int __init
+ks8695_init(void)
+{
+       printk(KERN_INFO "%s Ethernet driver, V%s\n",
+              MODULENAME, MODULEVERSION);
+
+       return platform_driver_register(&ks8695_driver);
+}
+
+static void __exit
+ks8695_cleanup(void)
+{
+       platform_driver_unregister(&ks8695_driver);
+}
+
+module_init(ks8695_init);
+module_exit(ks8695_cleanup);
+
+MODULE_AUTHOR("Simtec Electronics");
+MODULE_DESCRIPTION("Micrel KS8695 (Centaur) Ethernet driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" MODULENAME);
+
+module_param(watchdog, int, 0400);
+MODULE_PARM_DESC(watchdog, "transmit timeout in milliseconds");
diff --git a/drivers/net/ethernet/micrel/ks8695net.h b/drivers/net/ethernet/micrel/ks8695net.h
new file mode 100644 (file)
index 0000000..80eff6e
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Micrel KS8695 (Centaur) Ethernet.
+ *
+ * Copyright 2008 Simtec Electronics
+ *               Daniel Silverstone <dsilvers@simtec.co.uk>
+ *               Vincent Sanders <vince@simtec.co.uk>
+ */
+
+#ifndef KS8695NET_H
+#define KS8695NET_H
+
+/* Receive descriptor flags */
+#define RDES_OWN       (1 << 31)       /* Ownership */
+#define RDES_FS                (1 << 30)       /* First Descriptor */
+#define RDES_LS                (1 << 29)       /* Last Descriptor */
+#define RDES_IPE       (1 << 28)       /* IP Checksum error */
+#define RDES_TCPE      (1 << 27)       /* TCP Checksum error */
+#define RDES_UDPE      (1 << 26)       /* UDP Checksum error */
+#define RDES_ES                (1 << 25)       /* Error summary */
+#define RDES_MF                (1 << 24)       /* Multicast Frame */
+#define RDES_RE                (1 << 19)       /* MII Error reported */
+#define RDES_TL                (1 << 18)       /* Frame too Long */
+#define RDES_RF                (1 << 17)       /* Runt Frame */
+#define RDES_CE                (1 << 16)       /* CRC error */
+#define RDES_FT                (1 << 15)       /* Frame Type */
+#define RDES_FLEN      (0x7ff)         /* Frame Length */
+
+#define RDES_RER       (1 << 25)       /* Receive End of Ring */
+#define RDES_RBS       (0x7ff)         /* Receive Buffer Size */
+
+/* Transmit descriptor flags */
+
+#define TDES_OWN       (1 << 31)       /* Ownership */
+
+#define TDES_IC                (1 << 31)       /* Interrupt on Completion */
+#define TDES_FS                (1 << 30)       /* First Segment */
+#define TDES_LS                (1 << 29)       /* Last Segment */
+#define TDES_IPCKG     (1 << 28)       /* IP Checksum generate */
+#define TDES_TCPCKG    (1 << 27)       /* TCP Checksum generate */
+#define TDES_UDPCKG    (1 << 26)       /* UDP Checksum generate */
+#define TDES_TER       (1 << 25)       /* Transmit End of Ring */
+#define TDES_TBS       (0x7ff)         /* Transmit Buffer Size */
+
+/*
+ * Network controller register offsets
+ */
+#define KS8695_DTXC            (0x00)          /* DMA Transmit Control */
+#define KS8695_DRXC            (0x04)          /* DMA Receive Control */
+#define KS8695_DTSC            (0x08)          /* DMA Transmit Start Command */
+#define KS8695_DRSC            (0x0c)          /* DMA Receive Start Command */
+#define KS8695_TDLB            (0x10)          /* Transmit Descriptor List
+                                                * Base Address
+                                                */
+#define KS8695_RDLB            (0x14)          /* Receive Descriptor List
+                                                * Base Address
+                                                */
+#define KS8695_MAL             (0x18)          /* MAC Station Address Low */
+#define KS8695_MAH             (0x1c)          /* MAC Station Address High */
+#define KS8695_AAL_(n)         (0x80 + ((n)*8))        /* MAC Additional
+                                                        * Station Address
+                                                        * (0..15) Low
+                                                        */
+#define KS8695_AAH_(n)         (0x84 + ((n)*8))        /* MAC Additional
+                                                        * Station Address
+                                                        * (0..15) High
+                                                        */
+
+
+/* DMA Transmit Control Register */
+#define DTXC_TRST              (1    << 31)    /* Soft Reset */
+#define DTXC_TBS               (0x3f << 24)    /* Transmit Burst Size */
+#define DTXC_TUCG              (1    << 18)    /* Transmit UDP
+                                                * Checksum Generate
+                                                */
+#define DTXC_TTCG              (1    << 17)    /* Transmit TCP
+                                                * Checksum Generate
+                                                */
+#define DTXC_TICG              (1    << 16)    /* Transmit IP
+                                                * Checksum Generate
+                                                */
+#define DTXC_TFCE              (1    <<  9)    /* Transmit Flow
+                                                * Control Enable
+                                                */
+#define DTXC_TLB               (1    <<  8)    /* Loopback mode */
+#define DTXC_TEP               (1    <<  2)    /* Transmit Enable Padding */
+#define DTXC_TAC               (1    <<  1)    /* Transmit Add CRC */
+#define DTXC_TE                        (1    <<  0)    /* TX Enable */
+
+/* DMA Receive Control Register */
+#define DRXC_RBS               (0x3f << 24)    /* Receive Burst Size */
+#define DRXC_RUCC              (1    << 18)    /* Receive UDP Checksum check */
+#define DRXC_RTCG              (1    << 17)    /* Receive TCP Checksum check */
+#define DRXC_RICG              (1    << 16)    /* Receive IP Checksum check */
+#define DRXC_RFCE              (1    <<  9)    /* Receive Flow Control
+                                                * Enable
+                                                */
+#define DRXC_RB                        (1    <<  6)    /* Receive Broadcast */
+#define DRXC_RM                        (1    <<  5)    /* Receive Multicast */
+#define DRXC_RU                        (1    <<  4)    /* Receive Unicast */
+#define DRXC_RERR              (1    <<  3)    /* Receive Error Frame */
+#define DRXC_RA                        (1    <<  2)    /* Receive All */
+#define DRXC_RE                        (1    <<  0)    /* RX Enable */
+
+/* Additional Station Address High */
+#define AAH_E                  (1    << 31)    /* Address Enabled */
+
+#endif /* KS8695NET_H */
diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c
new file mode 100644 (file)
index 0000000..4a6ae05
--- /dev/null
@@ -0,0 +1,1284 @@
+/*
+ * ks8842.c timberdale KS8842 ethernet driver
+ * Copyright (c) 2009 Intel Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Supports:
+ * The Micrel KS8842 behind the timberdale FPGA
+ * The genuine Micrel KS8841/42 device with ISA 16/32bit bus interface
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/ks8842.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/scatterlist.h>
+
+#define DRV_NAME "ks8842"
+
+/* Timberdale specific Registers */
+#define REG_TIMB_RST           0x1c
+#define REG_TIMB_FIFO          0x20
+#define REG_TIMB_ISR           0x24
+#define REG_TIMB_IER           0x28
+#define REG_TIMB_IAR           0x2C
+#define REQ_TIMB_DMA_RESUME    0x30
+
+/* KS8842 registers */
+
+#define REG_SELECT_BANK 0x0e
+
+/* bank 0 registers */
+#define REG_QRFCR      0x04
+
+/* bank 2 registers */
+#define REG_MARL       0x00
+#define REG_MARM       0x02
+#define REG_MARH       0x04
+
+/* bank 3 registers */
+#define REG_GRR                0x06
+
+/* bank 16 registers */
+#define REG_TXCR       0x00
+#define REG_TXSR       0x02
+#define REG_RXCR       0x04
+#define REG_TXMIR      0x08
+#define REG_RXMIR      0x0A
+
+/* bank 17 registers */
+#define REG_TXQCR      0x00
+#define REG_RXQCR      0x02
+#define REG_TXFDPR     0x04
+#define REG_RXFDPR     0x06
+#define REG_QMU_DATA_LO 0x08
+#define REG_QMU_DATA_HI 0x0A
+
+/* bank 18 registers */
+#define REG_IER                0x00
+#define IRQ_LINK_CHANGE        0x8000
+#define IRQ_TX         0x4000
+#define IRQ_RX         0x2000
+#define IRQ_RX_OVERRUN 0x0800
+#define IRQ_TX_STOPPED 0x0200
+#define IRQ_RX_STOPPED 0x0100
+#define IRQ_RX_ERROR   0x0080
+#define ENABLED_IRQS   (IRQ_LINK_CHANGE | IRQ_TX | IRQ_RX | IRQ_RX_STOPPED | \
+               IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR)
+/* When running via timberdale in DMA mode, the RX interrupt should be
+   enabled in the KS8842, but not in the FPGA IP, since the IP handles
+   RX DMA internally.
+   TX interrupts are not needed it is handled by the FPGA the driver is
+   notified via DMA callbacks.
+*/
+#define ENABLED_IRQS_DMA_IP    (IRQ_LINK_CHANGE | IRQ_RX_STOPPED | \
+       IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR)
+#define ENABLED_IRQS_DMA       (ENABLED_IRQS_DMA_IP | IRQ_RX)
+#define REG_ISR                0x02
+#define REG_RXSR       0x04
+#define RXSR_VALID     0x8000
+#define RXSR_BROADCAST 0x80
+#define RXSR_MULTICAST 0x40
+#define RXSR_UNICAST   0x20
+#define RXSR_FRAMETYPE 0x08
+#define RXSR_TOO_LONG  0x04
+#define RXSR_RUNT      0x02
+#define RXSR_CRC_ERROR 0x01
+#define RXSR_ERROR     (RXSR_TOO_LONG | RXSR_RUNT | RXSR_CRC_ERROR)
+
+/* bank 32 registers */
+#define REG_SW_ID_AND_ENABLE   0x00
+#define REG_SGCR1              0x02
+#define REG_SGCR2              0x04
+#define REG_SGCR3              0x06
+
+/* bank 39 registers */
+#define REG_MACAR1             0x00
+#define REG_MACAR2             0x02
+#define REG_MACAR3             0x04
+
+/* bank 45 registers */
+#define REG_P1MBCR             0x00
+#define REG_P1MBSR             0x02
+
+/* bank 46 registers */
+#define REG_P2MBCR             0x00
+#define REG_P2MBSR             0x02
+
+/* bank 48 registers */
+#define REG_P1CR2              0x02
+
+/* bank 49 registers */
+#define REG_P1CR4              0x02
+#define REG_P1SR               0x04
+
+/* flags passed by platform_device for configuration */
+#define        MICREL_KS884X           0x01    /* 0=Timeberdale(FPGA), 1=Micrel */
+#define        KS884X_16BIT            0x02    /*  1=16bit, 0=32bit */
+
+#define DMA_BUFFER_SIZE                2048
+
+struct ks8842_tx_dma_ctl {
+       struct dma_chan *chan;
+       struct dma_async_tx_descriptor *adesc;
+       void *buf;
+       struct scatterlist sg;
+       int channel;
+};
+
+struct ks8842_rx_dma_ctl {
+       struct dma_chan *chan;
+       struct dma_async_tx_descriptor *adesc;
+       struct sk_buff  *skb;
+       struct scatterlist sg;
+       struct tasklet_struct tasklet;
+       int channel;
+};
+
+#define KS8842_USE_DMA(adapter) (((adapter)->dma_tx.channel != -1) && \
+        ((adapter)->dma_rx.channel != -1))
+
+struct ks8842_adapter {
+       void __iomem    *hw_addr;
+       int             irq;
+       unsigned long   conf_flags;     /* copy of platform_device config */
+       struct tasklet_struct   tasklet;
+       spinlock_t      lock; /* spinlock to be interrupt safe */
+       struct work_struct timeout_work;
+       struct net_device *netdev;
+       struct device *dev;
+       struct ks8842_tx_dma_ctl        dma_tx;
+       struct ks8842_rx_dma_ctl        dma_rx;
+};
+
+static void ks8842_dma_rx_cb(void *data);
+static void ks8842_dma_tx_cb(void *data);
+
+static inline void ks8842_resume_dma(struct ks8842_adapter *adapter)
+{
+       iowrite32(1, adapter->hw_addr + REQ_TIMB_DMA_RESUME);
+}
+
+static inline void ks8842_select_bank(struct ks8842_adapter *adapter, u16 bank)
+{
+       iowrite16(bank, adapter->hw_addr + REG_SELECT_BANK);
+}
+
+static inline void ks8842_write8(struct ks8842_adapter *adapter, u16 bank,
+       u8 value, int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       iowrite8(value, adapter->hw_addr + offset);
+}
+
+static inline void ks8842_write16(struct ks8842_adapter *adapter, u16 bank,
+       u16 value, int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       iowrite16(value, adapter->hw_addr + offset);
+}
+
+static inline void ks8842_enable_bits(struct ks8842_adapter *adapter, u16 bank,
+       u16 bits, int offset)
+{
+       u16 reg;
+       ks8842_select_bank(adapter, bank);
+       reg = ioread16(adapter->hw_addr + offset);
+       reg |= bits;
+       iowrite16(reg, adapter->hw_addr + offset);
+}
+
+static inline void ks8842_clear_bits(struct ks8842_adapter *adapter, u16 bank,
+       u16 bits, int offset)
+{
+       u16 reg;
+       ks8842_select_bank(adapter, bank);
+       reg = ioread16(adapter->hw_addr + offset);
+       reg &= ~bits;
+       iowrite16(reg, adapter->hw_addr + offset);
+}
+
+static inline void ks8842_write32(struct ks8842_adapter *adapter, u16 bank,
+       u32 value, int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       iowrite32(value, adapter->hw_addr + offset);
+}
+
+static inline u8 ks8842_read8(struct ks8842_adapter *adapter, u16 bank,
+       int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       return ioread8(adapter->hw_addr + offset);
+}
+
+static inline u16 ks8842_read16(struct ks8842_adapter *adapter, u16 bank,
+       int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       return ioread16(adapter->hw_addr + offset);
+}
+
+static inline u32 ks8842_read32(struct ks8842_adapter *adapter, u16 bank,
+       int offset)
+{
+       ks8842_select_bank(adapter, bank);
+       return ioread32(adapter->hw_addr + offset);
+}
+
+static void ks8842_reset(struct ks8842_adapter *adapter)
+{
+       if (adapter->conf_flags & MICREL_KS884X) {
+               ks8842_write16(adapter, 3, 1, REG_GRR);
+               msleep(10);
+               iowrite16(0, adapter->hw_addr + REG_GRR);
+       } else {
+               /* The KS8842 goes haywire when doing softare reset
+               * a work around in the timberdale IP is implemented to
+               * do a hardware reset instead
+               ks8842_write16(adapter, 3, 1, REG_GRR);
+               msleep(10);
+               iowrite16(0, adapter->hw_addr + REG_GRR);
+               */
+               iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
+               msleep(20);
+       }
+}
+
+static void ks8842_update_link_status(struct net_device *netdev,
+       struct ks8842_adapter *adapter)
+{
+       /* check the status of the link */
+       if (ks8842_read16(adapter, 45, REG_P1MBSR) & 0x4) {
+               netif_carrier_on(netdev);
+               netif_wake_queue(netdev);
+       } else {
+               netif_stop_queue(netdev);
+               netif_carrier_off(netdev);
+       }
+}
+
+static void ks8842_enable_tx(struct ks8842_adapter *adapter)
+{
+       ks8842_enable_bits(adapter, 16, 0x01, REG_TXCR);
+}
+
+static void ks8842_disable_tx(struct ks8842_adapter *adapter)
+{
+       ks8842_clear_bits(adapter, 16, 0x01, REG_TXCR);
+}
+
+static void ks8842_enable_rx(struct ks8842_adapter *adapter)
+{
+       ks8842_enable_bits(adapter, 16, 0x01, REG_RXCR);
+}
+
+static void ks8842_disable_rx(struct ks8842_adapter *adapter)
+{
+       ks8842_clear_bits(adapter, 16, 0x01, REG_RXCR);
+}
+
+static void ks8842_reset_hw(struct ks8842_adapter *adapter)
+{
+       /* reset the HW */
+       ks8842_reset(adapter);
+
+       /* Enable QMU Transmit flow control / transmit padding / Transmit CRC */
+       ks8842_write16(adapter, 16, 0x000E, REG_TXCR);
+
+       /* enable the receiver, uni + multi + broadcast + flow ctrl
+               + crc strip */
+       ks8842_write16(adapter, 16, 0x8 | 0x20 | 0x40 | 0x80 | 0x400,
+               REG_RXCR);
+
+       /* TX frame pointer autoincrement */
+       ks8842_write16(adapter, 17, 0x4000, REG_TXFDPR);
+
+       /* RX frame pointer autoincrement */
+       ks8842_write16(adapter, 17, 0x4000, REG_RXFDPR);
+
+       /* RX 2 kb high watermark */
+       ks8842_write16(adapter, 0, 0x1000, REG_QRFCR);
+
+       /* aggressive back off in half duplex */
+       ks8842_enable_bits(adapter, 32, 1 << 8, REG_SGCR1);
+
+       /* enable no excessive collison drop */
+       ks8842_enable_bits(adapter, 32, 1 << 3, REG_SGCR2);
+
+       /* Enable port 1 force flow control / back pressure / transmit / recv */
+       ks8842_write16(adapter, 48, 0x1E07, REG_P1CR2);
+
+       /* restart port auto-negotiation */
+       ks8842_enable_bits(adapter, 49, 1 << 13, REG_P1CR4);
+
+       /* Enable the transmitter */
+       ks8842_enable_tx(adapter);
+
+       /* Enable the receiver */
+       ks8842_enable_rx(adapter);
+
+       /* clear all interrupts */
+       ks8842_write16(adapter, 18, 0xffff, REG_ISR);
+
+       /* enable interrupts */
+       if (KS8842_USE_DMA(adapter)) {
+               /* When running in DMA Mode the RX interrupt is not enabled in
+                  timberdale because RX data is received by DMA callbacks
+                  it must still be enabled in the KS8842 because it indicates
+                  to timberdale when there is RX data for it's DMA FIFOs */
+               iowrite16(ENABLED_IRQS_DMA_IP, adapter->hw_addr + REG_TIMB_IER);
+               ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER);
+       } else {
+               if (!(adapter->conf_flags & MICREL_KS884X))
+                       iowrite16(ENABLED_IRQS,
+                               adapter->hw_addr + REG_TIMB_IER);
+               ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER);
+       }
+       /* enable the switch */
+       ks8842_write16(adapter, 32, 0x1, REG_SW_ID_AND_ENABLE);
+}
+
+static void ks8842_read_mac_addr(struct ks8842_adapter *adapter, u8 *dest)
+{
+       int i;
+       u16 mac;
+
+       for (i = 0; i < ETH_ALEN; i++)
+               dest[ETH_ALEN - i - 1] = ks8842_read8(adapter, 2, REG_MARL + i);
+
+       if (adapter->conf_flags & MICREL_KS884X) {
+               /*
+               the sequence of saving mac addr between MAC and Switch is
+               different.
+               */
+
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+       } else {
+
+               /* make sure the switch port uses the same MAC as the QMU */
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+       }
+}
+
+static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac)
+{
+       unsigned long flags;
+       unsigned i;
+
+       spin_lock_irqsave(&adapter->lock, flags);
+       for (i = 0; i < ETH_ALEN; i++) {
+               ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i);
+               if (!(adapter->conf_flags & MICREL_KS884X))
+                       ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
+                               REG_MACAR1 + i);
+       }
+
+       if (adapter->conf_flags & MICREL_KS884X) {
+               /*
+               the sequence of saving mac addr between MAC and Switch is
+               different.
+               */
+
+               u16 mac;
+
+               mac = ks8842_read16(adapter, 2, REG_MARL);
+               ks8842_write16(adapter, 39, mac, REG_MACAR3);
+               mac = ks8842_read16(adapter, 2, REG_MARM);
+               ks8842_write16(adapter, 39, mac, REG_MACAR2);
+               mac = ks8842_read16(adapter, 2, REG_MARH);
+               ks8842_write16(adapter, 39, mac, REG_MACAR1);
+       }
+       spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+static inline u16 ks8842_tx_fifo_space(struct ks8842_adapter *adapter)
+{
+       return ks8842_read16(adapter, 16, REG_TXMIR) & 0x1fff;
+}
+
+static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct ks8842_tx_dma_ctl *ctl = &adapter->dma_tx;
+       u8 *buf = ctl->buf;
+
+       if (ctl->adesc) {
+               netdev_dbg(netdev, "%s: TX ongoing\n", __func__);
+               /* transfer ongoing */
+               return NETDEV_TX_BUSY;
+       }
+
+       sg_dma_len(&ctl->sg) = skb->len + sizeof(u32);
+
+       /* copy data to the TX buffer */
+       /* the control word, enable IRQ, port 1 and the length */
+       *buf++ = 0x00;
+       *buf++ = 0x01; /* Port 1 */
+       *buf++ = skb->len & 0xff;
+       *buf++ = (skb->len >> 8) & 0xff;
+       skb_copy_from_linear_data(skb, buf, skb->len);
+
+       dma_sync_single_range_for_device(adapter->dev,
+               sg_dma_address(&ctl->sg), 0, sg_dma_len(&ctl->sg),
+               DMA_TO_DEVICE);
+
+       /* make sure the length is a multiple of 4 */
+       if (sg_dma_len(&ctl->sg) % 4)
+               sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4;
+
+       ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
+               &ctl->sg, 1, DMA_TO_DEVICE,
+               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+       if (!ctl->adesc)
+               return NETDEV_TX_BUSY;
+
+       ctl->adesc->callback_param = netdev;
+       ctl->adesc->callback = ks8842_dma_tx_cb;
+       ctl->adesc->tx_submit(ctl->adesc);
+
+       netdev->stats.tx_bytes += skb->len;
+
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       int len = skb->len;
+
+       netdev_dbg(netdev, "%s: len %u head %p data %p tail %p end %p\n",
+               __func__, skb->len, skb->head, skb->data,
+               skb_tail_pointer(skb), skb_end_pointer(skb));
+
+       /* check FIFO buffer space, we need space for CRC and command bits */
+       if (ks8842_tx_fifo_space(adapter) < len + 8)
+               return NETDEV_TX_BUSY;
+
+       if (adapter->conf_flags & KS884X_16BIT) {
+               u16 *ptr16 = (u16 *)skb->data;
+               ks8842_write16(adapter, 17, 0x8000 | 0x100, REG_QMU_DATA_LO);
+               ks8842_write16(adapter, 17, (u16)len, REG_QMU_DATA_HI);
+               netdev->stats.tx_bytes += len;
+
+               /* copy buffer */
+               while (len > 0) {
+                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_LO);
+                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_HI);
+                       len -= sizeof(u32);
+               }
+       } else {
+
+               u32 *ptr = (u32 *)skb->data;
+               u32 ctrl;
+               /* the control word, enable IRQ, port 1 and the length */
+               ctrl = 0x8000 | 0x100 | (len << 16);
+               ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
+
+               netdev->stats.tx_bytes += len;
+
+               /* copy buffer */
+               while (len > 0) {
+                       iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
+                       len -= sizeof(u32);
+                       ptr++;
+               }
+       }
+
+       /* enqueue packet */
+       ks8842_write16(adapter, 17, 1, REG_TXQCR);
+
+       dev_kfree_skb(skb);
+
+       return NETDEV_TX_OK;
+}
+
+static void ks8842_update_rx_err_counters(struct net_device *netdev, u32 status)
+{
+       netdev_dbg(netdev, "RX error, status: %x\n", status);
+
+       netdev->stats.rx_errors++;
+       if (status & RXSR_TOO_LONG)
+               netdev->stats.rx_length_errors++;
+       if (status & RXSR_CRC_ERROR)
+               netdev->stats.rx_crc_errors++;
+       if (status & RXSR_RUNT)
+               netdev->stats.rx_frame_errors++;
+}
+
+static void ks8842_update_rx_counters(struct net_device *netdev, u32 status,
+       int len)
+{
+       netdev_dbg(netdev, "RX packet, len: %d\n", len);
+
+       netdev->stats.rx_packets++;
+       netdev->stats.rx_bytes += len;
+       if (status & RXSR_MULTICAST)
+               netdev->stats.multicast++;
+}
+
+static int __ks8842_start_new_rx_dma(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx;
+       struct scatterlist *sg = &ctl->sg;
+       int err;
+
+       ctl->skb = netdev_alloc_skb(netdev, DMA_BUFFER_SIZE);
+       if (ctl->skb) {
+               sg_init_table(sg, 1);
+               sg_dma_address(sg) = dma_map_single(adapter->dev,
+                       ctl->skb->data, DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+               err = dma_mapping_error(adapter->dev, sg_dma_address(sg));
+               if (unlikely(err)) {
+                       sg_dma_address(sg) = 0;
+                       goto out;
+               }
+
+               sg_dma_len(sg) = DMA_BUFFER_SIZE;
+
+               ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
+                       sg, 1, DMA_FROM_DEVICE,
+                       DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
+
+               if (!ctl->adesc)
+                       goto out;
+
+               ctl->adesc->callback_param = netdev;
+               ctl->adesc->callback = ks8842_dma_rx_cb;
+               ctl->adesc->tx_submit(ctl->adesc);
+       } else {
+               err = -ENOMEM;
+               sg_dma_address(sg) = 0;
+               goto out;
+       }
+
+       return err;
+out:
+       if (sg_dma_address(sg))
+               dma_unmap_single(adapter->dev, sg_dma_address(sg),
+                       DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+       sg_dma_address(sg) = 0;
+       if (ctl->skb)
+               dev_kfree_skb(ctl->skb);
+
+       ctl->skb = NULL;
+
+       printk(KERN_ERR DRV_NAME": Failed to start RX DMA: %d\n", err);
+       return err;
+}
+
+static void ks8842_rx_frame_dma_tasklet(unsigned long arg)
+{
+       struct net_device *netdev = (struct net_device *)arg;
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx;
+       struct sk_buff *skb = ctl->skb;
+       dma_addr_t addr = sg_dma_address(&ctl->sg);
+       u32 status;
+
+       ctl->adesc = NULL;
+
+       /* kick next transfer going */
+       __ks8842_start_new_rx_dma(netdev);
+
+       /* now handle the data we got */
+       dma_unmap_single(adapter->dev, addr, DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+
+       status = *((u32 *)skb->data);
+
+       netdev_dbg(netdev, "%s - rx_data: status: %x\n",
+               __func__, status & 0xffff);
+
+       /* check the status */
+       if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
+               int len = (status >> 16) & 0x7ff;
+
+               ks8842_update_rx_counters(netdev, status, len);
+
+               /* reserve 4 bytes which is the status word */
+               skb_reserve(skb, 4);
+               skb_put(skb, len);
+
+               skb->protocol = eth_type_trans(skb, netdev);
+               netif_rx(skb);
+       } else {
+               ks8842_update_rx_err_counters(netdev, status);
+               dev_kfree_skb(skb);
+       }
+}
+
+static void ks8842_rx_frame(struct net_device *netdev,
+       struct ks8842_adapter *adapter)
+{
+       u32 status;
+       int len;
+
+       if (adapter->conf_flags & KS884X_16BIT) {
+               status = ks8842_read16(adapter, 17, REG_QMU_DATA_LO);
+               len = ks8842_read16(adapter, 17, REG_QMU_DATA_HI);
+               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
+                          __func__, status);
+       } else {
+               status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
+               len = (status >> 16) & 0x7ff;
+               status &= 0xffff;
+               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
+                          __func__, status);
+       }
+
+       /* check the status */
+       if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
+               struct sk_buff *skb = netdev_alloc_skb_ip_align(netdev, len + 3);
+
+               if (skb) {
+
+                       ks8842_update_rx_counters(netdev, status, len);
+
+                       if (adapter->conf_flags & KS884X_16BIT) {
+                               u16 *data16 = (u16 *)skb_put(skb, len);
+                               ks8842_select_bank(adapter, 17);
+                               while (len > 0) {
+                                       *data16++ = ioread16(adapter->hw_addr +
+                                               REG_QMU_DATA_LO);
+                                       *data16++ = ioread16(adapter->hw_addr +
+                                               REG_QMU_DATA_HI);
+                                       len -= sizeof(u32);
+                               }
+                       } else {
+                               u32 *data = (u32 *)skb_put(skb, len);
+
+                               ks8842_select_bank(adapter, 17);
+                               while (len > 0) {
+                                       *data++ = ioread32(adapter->hw_addr +
+                                               REG_QMU_DATA_LO);
+                                       len -= sizeof(u32);
+                               }
+                       }
+                       skb->protocol = eth_type_trans(skb, netdev);
+                       netif_rx(skb);
+               } else
+                       netdev->stats.rx_dropped++;
+       } else
+               ks8842_update_rx_err_counters(netdev, status);
+
+       /* set high watermark to 3K */
+       ks8842_clear_bits(adapter, 0, 1 << 12, REG_QRFCR);
+
+       /* release the frame */
+       ks8842_write16(adapter, 17, 0x01, REG_RXQCR);
+
+       /* set high watermark to 2K */
+       ks8842_enable_bits(adapter, 0, 1 << 12, REG_QRFCR);
+}
+
+void ks8842_handle_rx(struct net_device *netdev, struct ks8842_adapter *adapter)
+{
+       u16 rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
+       netdev_dbg(netdev, "%s Entry - rx_data: %d\n", __func__, rx_data);
+       while (rx_data) {
+               ks8842_rx_frame(netdev, adapter);
+               rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
+       }
+}
+
+void ks8842_handle_tx(struct net_device *netdev, struct ks8842_adapter *adapter)
+{
+       u16 sr = ks8842_read16(adapter, 16, REG_TXSR);
+       netdev_dbg(netdev, "%s - entry, sr: %x\n", __func__, sr);
+       netdev->stats.tx_packets++;
+       if (netif_queue_stopped(netdev))
+               netif_wake_queue(netdev);
+}
+
+void ks8842_handle_rx_overrun(struct net_device *netdev,
+       struct ks8842_adapter *adapter)
+{
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+       netdev->stats.rx_errors++;
+       netdev->stats.rx_fifo_errors++;
+}
+
+void ks8842_tasklet(unsigned long arg)
+{
+       struct net_device *netdev = (struct net_device *)arg;
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       u16 isr;
+       unsigned long flags;
+       u16 entry_bank;
+
+       /* read current bank to be able to set it back */
+       spin_lock_irqsave(&adapter->lock, flags);
+       entry_bank = ioread16(adapter->hw_addr + REG_SELECT_BANK);
+       spin_unlock_irqrestore(&adapter->lock, flags);
+
+       isr = ks8842_read16(adapter, 18, REG_ISR);
+       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
+
+       /* when running in DMA mode, do not ack RX interrupts, it is handled
+          internally by timberdale, otherwise it's DMA FIFO:s would stop
+       */
+       if (KS8842_USE_DMA(adapter))
+               isr &= ~IRQ_RX;
+
+       /* Ack */
+       ks8842_write16(adapter, 18, isr, REG_ISR);
+
+       if (!(adapter->conf_flags & MICREL_KS884X))
+               /* Ack in the timberdale IP as well */
+               iowrite32(0x1, adapter->hw_addr + REG_TIMB_IAR);
+
+       if (!netif_running(netdev))
+               return;
+
+       if (isr & IRQ_LINK_CHANGE)
+               ks8842_update_link_status(netdev, adapter);
+
+       /* should not get IRQ_RX when running DMA mode */
+       if (isr & (IRQ_RX | IRQ_RX_ERROR) && !KS8842_USE_DMA(adapter))
+               ks8842_handle_rx(netdev, adapter);
+
+       /* should only happen when in PIO mode */
+       if (isr & IRQ_TX)
+               ks8842_handle_tx(netdev, adapter);
+
+       if (isr & IRQ_RX_OVERRUN)
+               ks8842_handle_rx_overrun(netdev, adapter);
+
+       if (isr & IRQ_TX_STOPPED) {
+               ks8842_disable_tx(adapter);
+               ks8842_enable_tx(adapter);
+       }
+
+       if (isr & IRQ_RX_STOPPED) {
+               ks8842_disable_rx(adapter);
+               ks8842_enable_rx(adapter);
+       }
+
+       /* re-enable interrupts, put back the bank selection register */
+       spin_lock_irqsave(&adapter->lock, flags);
+       if (KS8842_USE_DMA(adapter))
+               ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER);
+       else
+               ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER);
+       iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK);
+
+       /* Make sure timberdale continues DMA operations, they are stopped while
+          we are handling the ks8842 because we might change bank */
+       if (KS8842_USE_DMA(adapter))
+               ks8842_resume_dma(adapter);
+
+       spin_unlock_irqrestore(&adapter->lock, flags);
+}
+
+static irqreturn_t ks8842_irq(int irq, void *devid)
+{
+       struct net_device *netdev = devid;
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       u16 isr;
+       u16 entry_bank = ioread16(adapter->hw_addr + REG_SELECT_BANK);
+       irqreturn_t ret = IRQ_NONE;
+
+       isr = ks8842_read16(adapter, 18, REG_ISR);
+       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
+
+       if (isr) {
+               if (KS8842_USE_DMA(adapter))
+                       /* disable all but RX IRQ, since the FPGA relies on it*/
+                       ks8842_write16(adapter, 18, IRQ_RX, REG_IER);
+               else
+                       /* disable IRQ */
+                       ks8842_write16(adapter, 18, 0x00, REG_IER);
+
+               /* schedule tasklet */
+               tasklet_schedule(&adapter->tasklet);
+
+               ret = IRQ_HANDLED;
+       }
+
+       iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK);
+
+       /* After an interrupt, tell timberdale to continue DMA operations.
+          DMA is disabled while we are handling the ks8842 because we might
+          change bank */
+       ks8842_resume_dma(adapter);
+
+       return ret;
+}
+
+static void ks8842_dma_rx_cb(void *data)
+{
+       struct net_device       *netdev = data;
+       struct ks8842_adapter   *adapter = netdev_priv(netdev);
+
+       netdev_dbg(netdev, "RX DMA finished\n");
+       /* schedule tasklet */
+       if (adapter->dma_rx.adesc)
+               tasklet_schedule(&adapter->dma_rx.tasklet);
+}
+
+static void ks8842_dma_tx_cb(void *data)
+{
+       struct net_device               *netdev = data;
+       struct ks8842_adapter           *adapter = netdev_priv(netdev);
+       struct ks8842_tx_dma_ctl        *ctl = &adapter->dma_tx;
+
+       netdev_dbg(netdev, "TX DMA finished\n");
+
+       if (!ctl->adesc)
+               return;
+
+       netdev->stats.tx_packets++;
+       ctl->adesc = NULL;
+
+       if (netif_queue_stopped(netdev))
+               netif_wake_queue(netdev);
+}
+
+static void ks8842_stop_dma(struct ks8842_adapter *adapter)
+{
+       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
+       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
+
+       tx_ctl->adesc = NULL;
+       if (tx_ctl->chan)
+               tx_ctl->chan->device->device_control(tx_ctl->chan,
+                       DMA_TERMINATE_ALL, 0);
+
+       rx_ctl->adesc = NULL;
+       if (rx_ctl->chan)
+               rx_ctl->chan->device->device_control(rx_ctl->chan,
+                       DMA_TERMINATE_ALL, 0);
+
+       if (sg_dma_address(&rx_ctl->sg))
+               dma_unmap_single(adapter->dev, sg_dma_address(&rx_ctl->sg),
+                       DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
+       sg_dma_address(&rx_ctl->sg) = 0;
+
+       dev_kfree_skb(rx_ctl->skb);
+       rx_ctl->skb = NULL;
+}
+
+static void ks8842_dealloc_dma_bufs(struct ks8842_adapter *adapter)
+{
+       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
+       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
+
+       ks8842_stop_dma(adapter);
+
+       if (tx_ctl->chan)
+               dma_release_channel(tx_ctl->chan);
+       tx_ctl->chan = NULL;
+
+       if (rx_ctl->chan)
+               dma_release_channel(rx_ctl->chan);
+       rx_ctl->chan = NULL;
+
+       tasklet_kill(&rx_ctl->tasklet);
+
+       if (sg_dma_address(&tx_ctl->sg))
+               dma_unmap_single(adapter->dev, sg_dma_address(&tx_ctl->sg),
+                       DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+       sg_dma_address(&tx_ctl->sg) = 0;
+
+       kfree(tx_ctl->buf);
+       tx_ctl->buf = NULL;
+}
+
+static bool ks8842_dma_filter_fn(struct dma_chan *chan, void *filter_param)
+{
+       return chan->chan_id == (long)filter_param;
+}
+
+static int ks8842_alloc_dma_bufs(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
+       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
+       int err;
+
+       dma_cap_mask_t mask;
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_PRIVATE, mask);
+
+       sg_init_table(&tx_ctl->sg, 1);
+
+       tx_ctl->chan = dma_request_channel(mask, ks8842_dma_filter_fn,
+                                          (void *)(long)tx_ctl->channel);
+       if (!tx_ctl->chan) {
+               err = -ENODEV;
+               goto err;
+       }
+
+       /* allocate DMA buffer */
+       tx_ctl->buf = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL);
+       if (!tx_ctl->buf) {
+               err = -ENOMEM;
+               goto err;
+       }
+
+       sg_dma_address(&tx_ctl->sg) = dma_map_single(adapter->dev,
+               tx_ctl->buf, DMA_BUFFER_SIZE, DMA_TO_DEVICE);
+       err = dma_mapping_error(adapter->dev,
+               sg_dma_address(&tx_ctl->sg));
+       if (err) {
+               sg_dma_address(&tx_ctl->sg) = 0;
+               goto err;
+       }
+
+       rx_ctl->chan = dma_request_channel(mask, ks8842_dma_filter_fn,
+                                          (void *)(long)rx_ctl->channel);
+       if (!rx_ctl->chan) {
+               err = -ENODEV;
+               goto err;
+       }
+
+       tasklet_init(&rx_ctl->tasklet, ks8842_rx_frame_dma_tasklet,
+               (unsigned long)netdev);
+
+       return 0;
+err:
+       ks8842_dealloc_dma_bufs(adapter);
+       return err;
+}
+
+/* Netdevice operations */
+
+static int ks8842_open(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       int err;
+
+       netdev_dbg(netdev, "%s - entry\n", __func__);
+
+       if (KS8842_USE_DMA(adapter)) {
+               err = ks8842_alloc_dma_bufs(netdev);
+
+               if (!err) {
+                       /* start RX dma */
+                       err = __ks8842_start_new_rx_dma(netdev);
+                       if (err)
+                               ks8842_dealloc_dma_bufs(adapter);
+               }
+
+               if (err) {
+                       printk(KERN_WARNING DRV_NAME
+                               ": Failed to initiate DMA, running PIO\n");
+                       ks8842_dealloc_dma_bufs(adapter);
+                       adapter->dma_rx.channel = -1;
+                       adapter->dma_tx.channel = -1;
+               }
+       }
+
+       /* reset the HW */
+       ks8842_reset_hw(adapter);
+
+       ks8842_write_mac_addr(adapter, netdev->dev_addr);
+
+       ks8842_update_link_status(netdev, adapter);
+
+       err = request_irq(adapter->irq, ks8842_irq, IRQF_SHARED, DRV_NAME,
+               netdev);
+       if (err) {
+               pr_err("Failed to request IRQ: %d: %d\n", adapter->irq, err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int ks8842_close(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+
+       netdev_dbg(netdev, "%s - entry\n", __func__);
+
+       cancel_work_sync(&adapter->timeout_work);
+
+       if (KS8842_USE_DMA(adapter))
+               ks8842_dealloc_dma_bufs(adapter);
+
+       /* free the irq */
+       free_irq(adapter->irq, netdev);
+
+       /* disable the switch */
+       ks8842_write16(adapter, 32, 0x0, REG_SW_ID_AND_ENABLE);
+
+       return 0;
+}
+
+static netdev_tx_t ks8842_xmit_frame(struct sk_buff *skb,
+                                    struct net_device *netdev)
+{
+       int ret;
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+
+       if (KS8842_USE_DMA(adapter)) {
+               unsigned long flags;
+               ret = ks8842_tx_frame_dma(skb, netdev);
+               /* for now only allow one transfer at the time */
+               spin_lock_irqsave(&adapter->lock, flags);
+               if (adapter->dma_tx.adesc)
+                       netif_stop_queue(netdev);
+               spin_unlock_irqrestore(&adapter->lock, flags);
+               return ret;
+       }
+
+       ret = ks8842_tx_frame(skb, netdev);
+
+       if (ks8842_tx_fifo_space(adapter) <  netdev->mtu + 8)
+               netif_stop_queue(netdev);
+
+       return ret;
+}
+
+static int ks8842_set_mac(struct net_device *netdev, void *p)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct sockaddr *addr = p;
+       char *mac = (u8 *)addr->sa_data;
+
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+
+       if (!is_valid_ether_addr(addr->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(netdev->dev_addr, mac, netdev->addr_len);
+
+       ks8842_write_mac_addr(adapter, mac);
+       return 0;
+}
+
+static void ks8842_tx_timeout_work(struct work_struct *work)
+{
+       struct ks8842_adapter *adapter =
+               container_of(work, struct ks8842_adapter, timeout_work);
+       struct net_device *netdev = adapter->netdev;
+       unsigned long flags;
+
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+
+       spin_lock_irqsave(&adapter->lock, flags);
+
+       if (KS8842_USE_DMA(adapter))
+               ks8842_stop_dma(adapter);
+
+       /* disable interrupts */
+       ks8842_write16(adapter, 18, 0, REG_IER);
+       ks8842_write16(adapter, 18, 0xFFFF, REG_ISR);
+
+       netif_stop_queue(netdev);
+
+       spin_unlock_irqrestore(&adapter->lock, flags);
+
+       ks8842_reset_hw(adapter);
+
+       ks8842_write_mac_addr(adapter, netdev->dev_addr);
+
+       ks8842_update_link_status(netdev, adapter);
+
+       if (KS8842_USE_DMA(adapter))
+               __ks8842_start_new_rx_dma(netdev);
+}
+
+static void ks8842_tx_timeout(struct net_device *netdev)
+{
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+
+       netdev_dbg(netdev, "%s: entry\n", __func__);
+
+       schedule_work(&adapter->timeout_work);
+}
+
+static const struct net_device_ops ks8842_netdev_ops = {
+       .ndo_open               = ks8842_open,
+       .ndo_stop               = ks8842_close,
+       .ndo_start_xmit         = ks8842_xmit_frame,
+       .ndo_set_mac_address    = ks8842_set_mac,
+       .ndo_tx_timeout         = ks8842_tx_timeout,
+       .ndo_validate_addr      = eth_validate_addr
+};
+
+static const struct ethtool_ops ks8842_ethtool_ops = {
+       .get_link               = ethtool_op_get_link,
+};
+
+static int __devinit ks8842_probe(struct platform_device *pdev)
+{
+       int err = -ENOMEM;
+       struct resource *iomem;
+       struct net_device *netdev;
+       struct ks8842_adapter *adapter;
+       struct ks8842_platform_data *pdata = pdev->dev.platform_data;
+       u16 id;
+       unsigned i;
+
+       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!request_mem_region(iomem->start, resource_size(iomem), DRV_NAME))
+               goto err_mem_region;
+
+       netdev = alloc_etherdev(sizeof(struct ks8842_adapter));
+       if (!netdev)
+               goto err_alloc_etherdev;
+
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+
+       adapter = netdev_priv(netdev);
+       adapter->netdev = netdev;
+       INIT_WORK(&adapter->timeout_work, ks8842_tx_timeout_work);
+       adapter->hw_addr = ioremap(iomem->start, resource_size(iomem));
+       adapter->conf_flags = iomem->flags;
+
+       if (!adapter->hw_addr)
+               goto err_ioremap;
+
+       adapter->irq = platform_get_irq(pdev, 0);
+       if (adapter->irq < 0) {
+               err = adapter->irq;
+               goto err_get_irq;
+       }
+
+       adapter->dev = (pdev->dev.parent) ? pdev->dev.parent : &pdev->dev;
+
+       /* DMA is only supported when accessed via timberdale */
+       if (!(adapter->conf_flags & MICREL_KS884X) && pdata &&
+               (pdata->tx_dma_channel != -1) &&
+               (pdata->rx_dma_channel != -1)) {
+               adapter->dma_rx.channel = pdata->rx_dma_channel;
+               adapter->dma_tx.channel = pdata->tx_dma_channel;
+       } else {
+               adapter->dma_rx.channel = -1;
+               adapter->dma_tx.channel = -1;
+       }
+
+       tasklet_init(&adapter->tasklet, ks8842_tasklet, (unsigned long)netdev);
+       spin_lock_init(&adapter->lock);
+
+       netdev->netdev_ops = &ks8842_netdev_ops;
+       netdev->ethtool_ops = &ks8842_ethtool_ops;
+
+       /* Check if a mac address was given */
+       i = netdev->addr_len;
+       if (pdata) {
+               for (i = 0; i < netdev->addr_len; i++)
+                       if (pdata->macaddr[i] != 0)
+                               break;
+
+               if (i < netdev->addr_len)
+                       /* an address was passed, use it */
+                       memcpy(netdev->dev_addr, pdata->macaddr,
+                               netdev->addr_len);
+       }
+
+       if (i == netdev->addr_len) {
+               ks8842_read_mac_addr(adapter, netdev->dev_addr);
+
+               if (!is_valid_ether_addr(netdev->dev_addr))
+                       random_ether_addr(netdev->dev_addr);
+       }
+
+       id = ks8842_read16(adapter, 32, REG_SW_ID_AND_ENABLE);
+
+       strcpy(netdev->name, "eth%d");
+       err = register_netdev(netdev);
+       if (err)
+               goto err_register;
+
+       platform_set_drvdata(pdev, netdev);
+
+       pr_info("Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n",
+               (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7);
+
+       return 0;
+
+err_register:
+err_get_irq:
+       iounmap(adapter->hw_addr);
+err_ioremap:
+       free_netdev(netdev);
+err_alloc_etherdev:
+       release_mem_region(iomem->start, resource_size(iomem));
+err_mem_region:
+       return err;
+}
+
+static int __devexit ks8842_remove(struct platform_device *pdev)
+{
+       struct net_device *netdev = platform_get_drvdata(pdev);
+       struct ks8842_adapter *adapter = netdev_priv(netdev);
+       struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       unregister_netdev(netdev);
+       tasklet_kill(&adapter->tasklet);
+       iounmap(adapter->hw_addr);
+       free_netdev(netdev);
+       release_mem_region(iomem->start, resource_size(iomem));
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+
+static struct platform_driver ks8842_platform_driver = {
+       .driver = {
+               .name   = DRV_NAME,
+               .owner  = THIS_MODULE,
+       },
+       .probe          = ks8842_probe,
+       .remove         = ks8842_remove,
+};
+
+static int __init ks8842_init(void)
+{
+       return platform_driver_register(&ks8842_platform_driver);
+}
+
+static void __exit ks8842_exit(void)
+{
+       platform_driver_unregister(&ks8842_platform_driver);
+}
+
+module_init(ks8842_init);
+module_exit(ks8842_exit);
+
+MODULE_DESCRIPTION("Timberdale KS8842 ethernet driver");
+MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:ks8842");
+
diff --git a/drivers/net/ethernet/micrel/ks8851.c b/drivers/net/ethernet/micrel/ks8851.c
new file mode 100644 (file)
index 0000000..f56743a
--- /dev/null
@@ -0,0 +1,1737 @@
+/* drivers/net/ks8851.c
+ *
+ * Copyright 2009 Simtec Electronics
+ *     http://www.simtec.co.uk/
+ *     Ben Dooks <ben@simtec.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#define DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/cache.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+
+#include <linux/spi/spi.h>
+
+#include "ks8851.h"
+
+/**
+ * struct ks8851_rxctrl - KS8851 driver rx control
+ * @mchash: Multicast hash-table data.
+ * @rxcr1: KS_RXCR1 register setting
+ * @rxcr2: KS_RXCR2 register setting
+ *
+ * Representation of the settings needs to control the receive filtering
+ * such as the multicast hash-filter and the receive register settings. This
+ * is used to make the job of working out if the receive settings change and
+ * then issuing the new settings to the worker that will send the necessary
+ * commands.
+ */
+struct ks8851_rxctrl {
+       u16     mchash[4];
+       u16     rxcr1;
+       u16     rxcr2;
+};
+
+/**
+ * union ks8851_tx_hdr - tx header data
+ * @txb: The header as bytes
+ * @txw: The header as 16bit, little-endian words
+ *
+ * A dual representation of the tx header data to allow
+ * access to individual bytes, and to allow 16bit accesses
+ * with 16bit alignment.
+ */
+union ks8851_tx_hdr {
+       u8      txb[6];
+       __le16  txw[3];
+};
+
+/**
+ * struct ks8851_net - KS8851 driver private data
+ * @netdev: The network device we're bound to
+ * @spidev: The spi device we're bound to.
+ * @lock: Lock to ensure that the device is not accessed when busy.
+ * @statelock: Lock on this structure for tx list.
+ * @mii: The MII state information for the mii calls.
+ * @rxctrl: RX settings for @rxctrl_work.
+ * @tx_work: Work queue for tx packets
+ * @irq_work: Work queue for servicing interrupts
+ * @rxctrl_work: Work queue for updating RX mode and multicast lists
+ * @txq: Queue of packets for transmission.
+ * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1.
+ * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2.
+ * @txh: Space for generating packet TX header in DMA-able data
+ * @rxd: Space for receiving SPI data, in DMA-able space.
+ * @txd: Space for transmitting SPI data, in DMA-able space.
+ * @msg_enable: The message flags controlling driver output (see ethtool).
+ * @fid: Incrementing frame id tag.
+ * @rc_ier: Cached copy of KS_IER.
+ * @rc_ccr: Cached copy of KS_CCR.
+ * @rc_rxqcr: Cached copy of KS_RXQCR.
+ * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom
+ *
+ * The @lock ensures that the chip is protected when certain operations are
+ * in progress. When the read or write packet transfer is in progress, most
+ * of the chip registers are not ccessible until the transfer is finished and
+ * the DMA has been de-asserted.
+ *
+ * The @statelock is used to protect information in the structure which may
+ * need to be accessed via several sources, such as the network driver layer
+ * or one of the work queues.
+ *
+ * We align the buffers we may use for rx/tx to ensure that if the SPI driver
+ * wants to DMA map them, it will not have any problems with data the driver
+ * modifies.
+ */
+struct ks8851_net {
+       struct net_device       *netdev;
+       struct spi_device       *spidev;
+       struct mutex            lock;
+       spinlock_t              statelock;
+
+       union ks8851_tx_hdr     txh ____cacheline_aligned;
+       u8                      rxd[8];
+       u8                      txd[8];
+
+       u32                     msg_enable ____cacheline_aligned;
+       u16                     tx_space;
+       u8                      fid;
+
+       u16                     rc_ier;
+       u16                     rc_rxqcr;
+       u16                     rc_ccr;
+       u16                     eeprom_size;
+
+       struct mii_if_info      mii;
+       struct ks8851_rxctrl    rxctrl;
+
+       struct work_struct      tx_work;
+       struct work_struct      irq_work;
+       struct work_struct      rxctrl_work;
+
+       struct sk_buff_head     txq;
+
+       struct spi_message      spi_msg1;
+       struct spi_message      spi_msg2;
+       struct spi_transfer     spi_xfer1;
+       struct spi_transfer     spi_xfer2[2];
+};
+
+static int msg_enable;
+
+/* shift for byte-enable data */
+#define BYTE_EN(_x)    ((_x) << 2)
+
+/* turn register number and byte-enable mask into data for start of packet */
+#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg)  << (8+2) | (_reg) >> 6)
+
+/* SPI register read/write calls.
+ *
+ * All these calls issue SPI transactions to access the chip's registers. They
+ * all require that the necessary lock is held to prevent accesses when the
+ * chip is busy transferring packet data (RX/TX FIFO accesses).
+ */
+
+/**
+ * ks8851_wrreg16 - write 16bit register value to chip
+ * @ks: The chip state
+ * @reg: The register address
+ * @val: The value to write
+ *
+ * Issue a write to put the value @val into the register specified in @reg.
+ */
+static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val)
+{
+       struct spi_transfer *xfer = &ks->spi_xfer1;
+       struct spi_message *msg = &ks->spi_msg1;
+       __le16 txb[2];
+       int ret;
+
+       txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR);
+       txb[1] = cpu_to_le16(val);
+
+       xfer->tx_buf = txb;
+       xfer->rx_buf = NULL;
+       xfer->len = 4;
+
+       ret = spi_sync(ks->spidev, msg);
+       if (ret < 0)
+               netdev_err(ks->netdev, "spi_sync() failed\n");
+}
+
+/**
+ * ks8851_wrreg8 - write 8bit register value to chip
+ * @ks: The chip state
+ * @reg: The register address
+ * @val: The value to write
+ *
+ * Issue a write to put the value @val into the register specified in @reg.
+ */
+static void ks8851_wrreg8(struct ks8851_net *ks, unsigned reg, unsigned val)
+{
+       struct spi_transfer *xfer = &ks->spi_xfer1;
+       struct spi_message *msg = &ks->spi_msg1;
+       __le16 txb[2];
+       int ret;
+       int bit;
+
+       bit = 1 << (reg & 3);
+
+       txb[0] = cpu_to_le16(MK_OP(bit, reg) | KS_SPIOP_WR);
+       txb[1] = val;
+
+       xfer->tx_buf = txb;
+       xfer->rx_buf = NULL;
+       xfer->len = 3;
+
+       ret = spi_sync(ks->spidev, msg);
+       if (ret < 0)
+               netdev_err(ks->netdev, "spi_sync() failed\n");
+}
+
+/**
+ * ks8851_rx_1msg - select whether to use one or two messages for spi read
+ * @ks: The device structure
+ *
+ * Return whether to generate a single message with a tx and rx buffer
+ * supplied to spi_sync(), or alternatively send the tx and rx buffers
+ * as separate messages.
+ *
+ * Depending on the hardware in use, a single message may be more efficient
+ * on interrupts or work done by the driver.
+ *
+ * This currently always returns true until we add some per-device data passed
+ * from the platform code to specify which mode is better.
+ */
+static inline bool ks8851_rx_1msg(struct ks8851_net *ks)
+{
+       return true;
+}
+
+/**
+ * ks8851_rdreg - issue read register command and return the data
+ * @ks: The device state
+ * @op: The register address and byte enables in message format.
+ * @rxb: The RX buffer to return the result into
+ * @rxl: The length of data expected.
+ *
+ * This is the low level read call that issues the necessary spi message(s)
+ * to read data from the register specified in @op.
+ */
+static void ks8851_rdreg(struct ks8851_net *ks, unsigned op,
+                        u8 *rxb, unsigned rxl)
+{
+       struct spi_transfer *xfer;
+       struct spi_message *msg;
+       __le16 *txb = (__le16 *)ks->txd;
+       u8 *trx = ks->rxd;
+       int ret;
+
+       txb[0] = cpu_to_le16(op | KS_SPIOP_RD);
+
+       if (ks8851_rx_1msg(ks)) {
+               msg = &ks->spi_msg1;
+               xfer = &ks->spi_xfer1;
+
+               xfer->tx_buf = txb;
+               xfer->rx_buf = trx;
+               xfer->len = rxl + 2;
+       } else {
+               msg = &ks->spi_msg2;
+               xfer = ks->spi_xfer2;
+
+               xfer->tx_buf = txb;
+               xfer->rx_buf = NULL;
+               xfer->len = 2;
+
+               xfer++;
+               xfer->tx_buf = NULL;
+               xfer->rx_buf = trx;
+               xfer->len = rxl;
+       }
+
+       ret = spi_sync(ks->spidev, msg);
+       if (ret < 0)
+               netdev_err(ks->netdev, "read: spi_sync() failed\n");
+       else if (ks8851_rx_1msg(ks))
+               memcpy(rxb, trx + 2, rxl);
+       else
+               memcpy(rxb, trx, rxl);
+}
+
+/**
+ * ks8851_rdreg8 - read 8 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 8bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg)
+{
+       u8 rxb[1];
+
+       ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1);
+       return rxb[0];
+}
+
+/**
+ * ks8851_rdreg16 - read 16 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 16bit register from the chip, returning the result
+*/
+static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg)
+{
+       __le16 rx = 0;
+
+       ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2);
+       return le16_to_cpu(rx);
+}
+
+/**
+ * ks8851_rdreg32 - read 32 bit register from device
+ * @ks: The chip information
+ * @reg: The register address
+ *
+ * Read a 32bit register from the chip.
+ *
+ * Note, this read requires the address be aligned to 4 bytes.
+*/
+static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg)
+{
+       __le32 rx = 0;
+
+       WARN_ON(reg & 3);
+
+       ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4);
+       return le32_to_cpu(rx);
+}
+
+/**
+ * ks8851_soft_reset - issue one of the soft reset to the device
+ * @ks: The device state.
+ * @op: The bit(s) to set in the GRR
+ *
+ * Issue the relevant soft-reset command to the device's GRR register
+ * specified by @op.
+ *
+ * Note, the delays are in there as a caution to ensure that the reset
+ * has time to take effect and then complete. Since the datasheet does
+ * not currently specify the exact sequence, we have chosen something
+ * that seems to work with our device.
+ */
+static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op)
+{
+       ks8851_wrreg16(ks, KS_GRR, op);
+       mdelay(1);      /* wait a short time to effect reset */
+       ks8851_wrreg16(ks, KS_GRR, 0);
+       mdelay(1);      /* wait for condition to clear */
+}
+
+/**
+ * ks8851_write_mac_addr - write mac address to device registers
+ * @dev: The network device
+ *
+ * Update the KS8851 MAC address registers from the address in @dev.
+ *
+ * This call assumes that the chip is not running, so there is no need to
+ * shutdown the RXQ process whilst setting this.
+*/
+static int ks8851_write_mac_addr(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int i;
+
+       mutex_lock(&ks->lock);
+
+       for (i = 0; i < ETH_ALEN; i++)
+               ks8851_wrreg8(ks, KS_MAR(i), dev->dev_addr[i]);
+
+       mutex_unlock(&ks->lock);
+
+       return 0;
+}
+
+/**
+ * ks8851_init_mac - initialise the mac address
+ * @ks: The device structure
+ *
+ * Get or create the initial mac address for the device and then set that
+ * into the station address register. Currently we assume that the device
+ * does not have a valid mac address in it, and so we use random_ether_addr()
+ * to create a new one.
+ *
+ * In future, the driver should check to see if the device has an EEPROM
+ * attached and whether that has a valid ethernet address in it.
+ */
+static void ks8851_init_mac(struct ks8851_net *ks)
+{
+       struct net_device *dev = ks->netdev;
+
+       random_ether_addr(dev->dev_addr);
+       ks8851_write_mac_addr(dev);
+}
+
+/**
+ * ks8851_irq - device interrupt handler
+ * @irq: Interrupt number passed from the IRQ hnalder.
+ * @pw: The private word passed to register_irq(), our struct ks8851_net.
+ *
+ * Disable the interrupt from happening again until we've processed the
+ * current status by scheduling ks8851_irq_work().
+ */
+static irqreturn_t ks8851_irq(int irq, void *pw)
+{
+       struct ks8851_net *ks = pw;
+
+       disable_irq_nosync(irq);
+       schedule_work(&ks->irq_work);
+       return IRQ_HANDLED;
+}
+
+/**
+ * ks8851_rdfifo - read data from the receive fifo
+ * @ks: The device state.
+ * @buff: The buffer address
+ * @len: The length of the data to read
+ *
+ * Issue an RXQ FIFO read command and read the @len amount of data from
+ * the FIFO into the buffer specified by @buff.
+ */
+static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len)
+{
+       struct spi_transfer *xfer = ks->spi_xfer2;
+       struct spi_message *msg = &ks->spi_msg2;
+       u8 txb[1];
+       int ret;
+
+       netif_dbg(ks, rx_status, ks->netdev,
+                 "%s: %d@%p\n", __func__, len, buff);
+
+       /* set the operation we're issuing */
+       txb[0] = KS_SPIOP_RXFIFO;
+
+       xfer->tx_buf = txb;
+       xfer->rx_buf = NULL;
+       xfer->len = 1;
+
+       xfer++;
+       xfer->rx_buf = buff;
+       xfer->tx_buf = NULL;
+       xfer->len = len;
+
+       ret = spi_sync(ks->spidev, msg);
+       if (ret < 0)
+               netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_dbg_dumpkkt - dump initial packet contents to debug
+ * @ks: The device state
+ * @rxpkt: The data for the received packet
+ *
+ * Dump the initial data from the packet to dev_dbg().
+*/
+static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt)
+{
+       netdev_dbg(ks->netdev,
+                  "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
+                  rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7],
+                  rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11],
+                  rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]);
+}
+
+/**
+ * ks8851_rx_pkts - receive packets from the host
+ * @ks: The device information.
+ *
+ * This is called from the IRQ work queue when the system detects that there
+ * are packets in the receive queue. Find out how many packets there are and
+ * read them from the FIFO.
+ */
+static void ks8851_rx_pkts(struct ks8851_net *ks)
+{
+       struct sk_buff *skb;
+       unsigned rxfc;
+       unsigned rxlen;
+       unsigned rxstat;
+       u32 rxh;
+       u8 *rxpkt;
+
+       rxfc = ks8851_rdreg8(ks, KS_RXFC);
+
+       netif_dbg(ks, rx_status, ks->netdev,
+                 "%s: %d packets\n", __func__, rxfc);
+
+       /* Currently we're issuing a read per packet, but we could possibly
+        * improve the code by issuing a single read, getting the receive
+        * header, allocating the packet and then reading the packet data
+        * out in one go.
+        *
+        * This form of operation would require us to hold the SPI bus'
+        * chipselect low during the entie transaction to avoid any
+        * reset to the data stream coming from the chip.
+        */
+
+       for (; rxfc != 0; rxfc--) {
+               rxh = ks8851_rdreg32(ks, KS_RXFHSR);
+               rxstat = rxh & 0xffff;
+               rxlen = rxh >> 16;
+
+               netif_dbg(ks, rx_status, ks->netdev,
+                         "rx: stat 0x%04x, len 0x%04x\n", rxstat, rxlen);
+
+               /* the length of the packet includes the 32bit CRC */
+
+               /* set dma read address */
+               ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00);
+
+               /* start the packet dma process, and set auto-dequeue rx */
+               ks8851_wrreg16(ks, KS_RXQCR,
+                              ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE);
+
+               if (rxlen > 4) {
+                       unsigned int rxalign;
+
+                       rxlen -= 4;
+                       rxalign = ALIGN(rxlen, 4);
+                       skb = netdev_alloc_skb_ip_align(ks->netdev, rxalign);
+                       if (skb) {
+
+                               /* 4 bytes of status header + 4 bytes of
+                                * garbage: we put them before ethernet
+                                * header, so that they are copied,
+                                * but ignored.
+                                */
+
+                               rxpkt = skb_put(skb, rxlen) - 8;
+
+                               ks8851_rdfifo(ks, rxpkt, rxalign + 8);
+
+                               if (netif_msg_pktdata(ks))
+                                       ks8851_dbg_dumpkkt(ks, rxpkt);
+
+                               skb->protocol = eth_type_trans(skb, ks->netdev);
+                               netif_rx(skb);
+
+                               ks->netdev->stats.rx_packets++;
+                               ks->netdev->stats.rx_bytes += rxlen;
+                       }
+               }
+
+               ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+       }
+}
+
+/**
+ * ks8851_irq_work - work queue handler for dealing with interrupt requests
+ * @work: The work structure that was scheduled by schedule_work()
+ *
+ * This is the handler invoked when the ks8851_irq() is called to find out
+ * what happened, as we cannot allow ourselves to sleep whilst waiting for
+ * anything other process has the chip's lock.
+ *
+ * Read the interrupt status, work out what needs to be done and then clear
+ * any of the interrupts that are not needed.
+ */
+static void ks8851_irq_work(struct work_struct *work)
+{
+       struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work);
+       unsigned status;
+       unsigned handled = 0;
+
+       mutex_lock(&ks->lock);
+
+       status = ks8851_rdreg16(ks, KS_ISR);
+
+       netif_dbg(ks, intr, ks->netdev,
+                 "%s: status 0x%04x\n", __func__, status);
+
+       if (status & IRQ_LCI) {
+               /* should do something about checking link status */
+               handled |= IRQ_LCI;
+       }
+
+       if (status & IRQ_LDI) {
+               u16 pmecr = ks8851_rdreg16(ks, KS_PMECR);
+               pmecr &= ~PMECR_WKEVT_MASK;
+               ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
+
+               handled |= IRQ_LDI;
+       }
+
+       if (status & IRQ_RXPSI)
+               handled |= IRQ_RXPSI;
+
+       if (status & IRQ_TXI) {
+               handled |= IRQ_TXI;
+
+               /* no lock here, tx queue should have been stopped */
+
+               /* update our idea of how much tx space is available to the
+                * system */
+               ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR);
+
+               netif_dbg(ks, intr, ks->netdev,
+                         "%s: txspace %d\n", __func__, ks->tx_space);
+       }
+
+       if (status & IRQ_RXI)
+               handled |= IRQ_RXI;
+
+       if (status & IRQ_SPIBEI) {
+               dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__);
+               handled |= IRQ_SPIBEI;
+       }
+
+       ks8851_wrreg16(ks, KS_ISR, handled);
+
+       if (status & IRQ_RXI) {
+               /* the datasheet says to disable the rx interrupt during
+                * packet read-out, however we're masking the interrupt
+                * from the device so do not bother masking just the RX
+                * from the device. */
+
+               ks8851_rx_pkts(ks);
+       }
+
+       /* if something stopped the rx process, probably due to wanting
+        * to change the rx settings, then do something about restarting
+        * it. */
+       if (status & IRQ_RXPSI) {
+               struct ks8851_rxctrl *rxc = &ks->rxctrl;
+
+               /* update the multicast hash table */
+               ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]);
+               ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]);
+               ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]);
+               ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]);
+
+               ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2);
+               ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1);
+       }
+
+       mutex_unlock(&ks->lock);
+
+       if (status & IRQ_TXI)
+               netif_wake_queue(ks->netdev);
+
+       enable_irq(ks->netdev->irq);
+}
+
+/**
+ * calc_txlen - calculate size of message to send packet
+ * @len: Length of data
+ *
+ * Returns the size of the TXFIFO message needed to send
+ * this packet.
+ */
+static inline unsigned calc_txlen(unsigned len)
+{
+       return ALIGN(len + 4, 4);
+}
+
+/**
+ * ks8851_wrpkt - write packet to TX FIFO
+ * @ks: The device state.
+ * @txp: The sk_buff to transmit.
+ * @irq: IRQ on completion of the packet.
+ *
+ * Send the @txp to the chip. This means creating the relevant packet header
+ * specifying the length of the packet and the other information the chip
+ * needs, such as IRQ on completion. Send the header and the packet data to
+ * the device.
+ */
+static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq)
+{
+       struct spi_transfer *xfer = ks->spi_xfer2;
+       struct spi_message *msg = &ks->spi_msg2;
+       unsigned fid = 0;
+       int ret;
+
+       netif_dbg(ks, tx_queued, ks->netdev, "%s: skb %p, %d@%p, irq %d\n",
+                 __func__, txp, txp->len, txp->data, irq);
+
+       fid = ks->fid++;
+       fid &= TXFR_TXFID_MASK;
+
+       if (irq)
+               fid |= TXFR_TXIC;       /* irq on completion */
+
+       /* start header at txb[1] to align txw entries */
+       ks->txh.txb[1] = KS_SPIOP_TXFIFO;
+       ks->txh.txw[1] = cpu_to_le16(fid);
+       ks->txh.txw[2] = cpu_to_le16(txp->len);
+
+       xfer->tx_buf = &ks->txh.txb[1];
+       xfer->rx_buf = NULL;
+       xfer->len = 5;
+
+       xfer++;
+       xfer->tx_buf = txp->data;
+       xfer->rx_buf = NULL;
+       xfer->len = ALIGN(txp->len, 4);
+
+       ret = spi_sync(ks->spidev, msg);
+       if (ret < 0)
+               netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__);
+}
+
+/**
+ * ks8851_done_tx - update and then free skbuff after transmitting
+ * @ks: The device state
+ * @txb: The buffer transmitted
+ */
+static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb)
+{
+       struct net_device *dev = ks->netdev;
+
+       dev->stats.tx_bytes += txb->len;
+       dev->stats.tx_packets++;
+
+       dev_kfree_skb(txb);
+}
+
+/**
+ * ks8851_tx_work - process tx packet(s)
+ * @work: The work strucutre what was scheduled.
+ *
+ * This is called when a number of packets have been scheduled for
+ * transmission and need to be sent to the device.
+ */
+static void ks8851_tx_work(struct work_struct *work)
+{
+       struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work);
+       struct sk_buff *txb;
+       bool last = skb_queue_empty(&ks->txq);
+
+       mutex_lock(&ks->lock);
+
+       while (!last) {
+               txb = skb_dequeue(&ks->txq);
+               last = skb_queue_empty(&ks->txq);
+
+               if (txb != NULL) {
+                       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA);
+                       ks8851_wrpkt(ks, txb, last);
+                       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+                       ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
+
+                       ks8851_done_tx(ks, txb);
+               }
+       }
+
+       mutex_unlock(&ks->lock);
+}
+
+/**
+ * ks8851_set_powermode - set power mode of the device
+ * @ks: The device state
+ * @pwrmode: The power mode value to write to KS_PMECR.
+ *
+ * Change the power mode of the chip.
+ */
+static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode)
+{
+       unsigned pmecr;
+
+       netif_dbg(ks, hw, ks->netdev, "setting power mode %d\n", pwrmode);
+
+       pmecr = ks8851_rdreg16(ks, KS_PMECR);
+       pmecr &= ~PMECR_PM_MASK;
+       pmecr |= pwrmode;
+
+       ks8851_wrreg16(ks, KS_PMECR, pmecr);
+}
+
+/**
+ * ks8851_net_open - open network device
+ * @dev: The network device being opened.
+ *
+ * Called when the network device is marked active, such as a user executing
+ * 'ifconfig up' on the device.
+ */
+static int ks8851_net_open(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+
+       /* lock the card, even if we may not actually be doing anything
+        * else at the moment */
+       mutex_lock(&ks->lock);
+
+       netif_dbg(ks, ifup, ks->netdev, "opening\n");
+
+       /* bring chip out of any power saving mode it was in */
+       ks8851_set_powermode(ks, PMECR_PM_NORMAL);
+
+       /* issue a soft reset to the RX/TX QMU to put it into a known
+        * state. */
+       ks8851_soft_reset(ks, GRR_QMU);
+
+       /* setup transmission parameters */
+
+       ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */
+                                    TXCR_TXPE | /* pad to min length */
+                                    TXCR_TXCRC | /* add CRC */
+                                    TXCR_TXFCE)); /* enable flow control */
+
+       /* auto-increment tx data, reset tx pointer */
+       ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
+
+       /* setup receiver control */
+
+       ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /*  from mac filter */
+                                     RXCR1_RXFCE | /* enable flow control */
+                                     RXCR1_RXBE | /* broadcast enable */
+                                     RXCR1_RXUE | /* unicast enable */
+                                     RXCR1_RXE)); /* enable rx block */
+
+       /* transfer entire frames out in one go */
+       ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME);
+
+       /* set receive counter timeouts */
+       ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */
+       ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */
+       ks8851_wrreg16(ks, KS_RXFCTR, 10);  /* 10 frames to IRQ */
+
+       ks->rc_rxqcr = (RXQCR_RXFCTE |  /* IRQ on frame count exceeded */
+                       RXQCR_RXDBCTE | /* IRQ on byte count exceeded */
+                       RXQCR_RXDTTE);  /* IRQ on time exceeded */
+
+       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+
+       /* clear then enable interrupts */
+
+#define STD_IRQ (IRQ_LCI |     /* Link Change */       \
+                IRQ_TXI |      /* TX done */           \
+                IRQ_RXI |      /* RX done */           \
+                IRQ_SPIBEI |   /* SPI bus error */     \
+                IRQ_TXPSI |    /* TX process stop */   \
+                IRQ_RXPSI)     /* RX process stop */
+
+       ks->rc_ier = STD_IRQ;
+       ks8851_wrreg16(ks, KS_ISR, STD_IRQ);
+       ks8851_wrreg16(ks, KS_IER, STD_IRQ);
+
+       netif_start_queue(ks->netdev);
+
+       netif_dbg(ks, ifup, ks->netdev, "network device up\n");
+
+       mutex_unlock(&ks->lock);
+       return 0;
+}
+
+/**
+ * ks8851_net_stop - close network device
+ * @dev: The device being closed.
+ *
+ * Called to close down a network device which has been active. Cancell any
+ * work, shutdown the RX and TX process and then place the chip into a low
+ * power state whilst it is not being used.
+ */
+static int ks8851_net_stop(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+
+       netif_info(ks, ifdown, dev, "shutting down\n");
+
+       netif_stop_queue(dev);
+
+       mutex_lock(&ks->lock);
+
+       /* stop any outstanding work */
+       flush_work(&ks->irq_work);
+       flush_work(&ks->tx_work);
+       flush_work(&ks->rxctrl_work);
+
+       /* turn off the IRQs and ack any outstanding */
+       ks8851_wrreg16(ks, KS_IER, 0x0000);
+       ks8851_wrreg16(ks, KS_ISR, 0xffff);
+
+       /* shutdown RX process */
+       ks8851_wrreg16(ks, KS_RXCR1, 0x0000);
+
+       /* shutdown TX process */
+       ks8851_wrreg16(ks, KS_TXCR, 0x0000);
+
+       /* set powermode to soft power down to save power */
+       ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
+
+       /* ensure any queued tx buffers are dumped */
+       while (!skb_queue_empty(&ks->txq)) {
+               struct sk_buff *txb = skb_dequeue(&ks->txq);
+
+               netif_dbg(ks, ifdown, ks->netdev,
+                         "%s: freeing txb %p\n", __func__, txb);
+
+               dev_kfree_skb(txb);
+       }
+
+       mutex_unlock(&ks->lock);
+       return 0;
+}
+
+/**
+ * ks8851_start_xmit - transmit packet
+ * @skb: The buffer to transmit
+ * @dev: The device used to transmit the packet.
+ *
+ * Called by the network layer to transmit the @skb. Queue the packet for
+ * the device and schedule the necessary work to transmit the packet when
+ * it is free.
+ *
+ * We do this to firstly avoid sleeping with the network device locked,
+ * and secondly so we can round up more than one packet to transmit which
+ * means we can try and avoid generating too many transmit done interrupts.
+ */
+static netdev_tx_t ks8851_start_xmit(struct sk_buff *skb,
+                                    struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       unsigned needed = calc_txlen(skb->len);
+       netdev_tx_t ret = NETDEV_TX_OK;
+
+       netif_dbg(ks, tx_queued, ks->netdev,
+                 "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data);
+
+       spin_lock(&ks->statelock);
+
+       if (needed > ks->tx_space) {
+               netif_stop_queue(dev);
+               ret = NETDEV_TX_BUSY;
+       } else {
+               ks->tx_space -= needed;
+               skb_queue_tail(&ks->txq, skb);
+       }
+
+       spin_unlock(&ks->statelock);
+       schedule_work(&ks->tx_work);
+
+       return ret;
+}
+
+/**
+ * ks8851_rxctrl_work - work handler to change rx mode
+ * @work: The work structure this belongs to.
+ *
+ * Lock the device and issue the necessary changes to the receive mode from
+ * the network device layer. This is done so that we can do this without
+ * having to sleep whilst holding the network device lock.
+ *
+ * Since the recommendation from Micrel is that the RXQ is shutdown whilst the
+ * receive parameters are programmed, we issue a write to disable the RXQ and
+ * then wait for the interrupt handler to be triggered once the RXQ shutdown is
+ * complete. The interrupt handler then writes the new values into the chip.
+ */
+static void ks8851_rxctrl_work(struct work_struct *work)
+{
+       struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work);
+
+       mutex_lock(&ks->lock);
+
+       /* need to shutdown RXQ before modifying filter parameters */
+       ks8851_wrreg16(ks, KS_RXCR1, 0x00);
+
+       mutex_unlock(&ks->lock);
+}
+
+static void ks8851_set_rx_mode(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       struct ks8851_rxctrl rxctrl;
+
+       memset(&rxctrl, 0, sizeof(rxctrl));
+
+       if (dev->flags & IFF_PROMISC) {
+               /* interface to receive everything */
+
+               rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF;
+       } else if (dev->flags & IFF_ALLMULTI) {
+               /* accept all multicast packets */
+
+               rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE |
+                               RXCR1_RXPAFMA | RXCR1_RXMAFMA);
+       } else if (dev->flags & IFF_MULTICAST && !netdev_mc_empty(dev)) {
+               struct netdev_hw_addr *ha;
+               u32 crc;
+
+               /* accept some multicast */
+
+               netdev_for_each_mc_addr(ha, dev) {
+                       crc = ether_crc(ETH_ALEN, ha->addr);
+                       crc >>= (32 - 6);  /* get top six bits */
+
+                       rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf));
+               }
+
+               rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXPAFMA;
+       } else {
+               /* just accept broadcast / unicast */
+               rxctrl.rxcr1 = RXCR1_RXPAFMA;
+       }
+
+       rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */
+                        RXCR1_RXBE | /* broadcast enable */
+                        RXCR1_RXE | /* RX process enable */
+                        RXCR1_RXFCE); /* enable flow control */
+
+       rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME;
+
+       /* schedule work to do the actual set of the data if needed */
+
+       spin_lock(&ks->statelock);
+
+       if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) {
+               memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl));
+               schedule_work(&ks->rxctrl_work);
+       }
+
+       spin_unlock(&ks->statelock);
+}
+
+static int ks8851_set_mac_address(struct net_device *dev, void *addr)
+{
+       struct sockaddr *sa = addr;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       if (!is_valid_ether_addr(sa->sa_data))
+               return -EADDRNOTAVAIL;
+
+       memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
+       return ks8851_write_mac_addr(dev);
+}
+
+static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
+}
+
+static const struct net_device_ops ks8851_netdev_ops = {
+       .ndo_open               = ks8851_net_open,
+       .ndo_stop               = ks8851_net_stop,
+       .ndo_do_ioctl           = ks8851_net_ioctl,
+       .ndo_start_xmit         = ks8851_start_xmit,
+       .ndo_set_mac_address    = ks8851_set_mac_address,
+       .ndo_set_rx_mode        = ks8851_set_rx_mode,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+/* Companion eeprom access */
+
+enum { /* EEPROM programming states */
+       EEPROM_CONTROL,
+       EEPROM_ADDRESS,
+       EEPROM_DATA,
+       EEPROM_COMPLETE
+};
+
+/**
+ * ks8851_eeprom_read - read a 16bits word in ks8851 companion EEPROM
+ * @dev: The network device the PHY is on.
+ * @addr: EEPROM address to read
+ *
+ * eeprom_size: used to define the data coding length. Can be changed
+ * through debug-fs.
+ *
+ * Programs a read on the EEPROM using ks8851 EEPROM SW access feature.
+ * Warning: The READ feature is not supported on ks8851 revision 0.
+ *
+ * Rough programming model:
+ *  - on period start: set clock high and read value on bus
+ *  - on period / 2: set clock low and program value on bus
+ *  - start on period / 2
+ */
+unsigned int ks8851_eeprom_read(struct net_device *dev, unsigned int addr)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int eepcr;
+       int ctrl = EEPROM_OP_READ;
+       int state = EEPROM_CONTROL;
+       int bit_count = EEPROM_OP_LEN - 1;
+       unsigned int data = 0;
+       int dummy;
+       unsigned int addr_len;
+
+       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
+
+       /* start transaction: chip select high, authorize write */
+       mutex_lock(&ks->lock);
+       eepcr = EEPCR_EESA | EEPCR_EESRWA;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr |= EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       while (state != EEPROM_COMPLETE) {
+               /* falling clock period starts... */
+               /* set EED_IO pin for control and address */
+               eepcr &= ~EEPCR_EEDO;
+               switch (state) {
+               case EEPROM_CONTROL:
+                       eepcr |= ((ctrl >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               bit_count = addr_len - 1;
+                               state = EEPROM_ADDRESS;
+                       }
+                       break;
+               case EEPROM_ADDRESS:
+                       eepcr |= ((addr >> bit_count) & 1) << 2;
+                       bit_count--;
+                       break;
+               case EEPROM_DATA:
+                       /* Change to receive mode */
+                       eepcr &= ~EEPCR_EESRWA;
+                       break;
+               }
+
+               /* lower clock  */
+               eepcr &= ~EEPCR_EESCK;
+
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* waitread period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+
+               /* rising clock period starts... */
+
+               /* raise clock */
+               mutex_lock(&ks->lock);
+               eepcr |= EEPCR_EESCK;
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* Manage read */
+               switch (state) {
+               case EEPROM_ADDRESS:
+                       if (bit_count < 0) {
+                               bit_count = EEPROM_DATA_LEN - 1;
+                               state = EEPROM_DATA;
+                       }
+                       break;
+               case EEPROM_DATA:
+                       mutex_lock(&ks->lock);
+                       dummy = ks8851_rdreg16(ks, KS_EEPCR);
+                       mutex_unlock(&ks->lock);
+                       data |= ((dummy >> EEPCR_EESB_OFFSET) & 1) << bit_count;
+                       if (bit_count-- <= 0)
+                               state = EEPROM_COMPLETE;
+                       break;
+               }
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+       }
+
+       /* close transaction */
+       mutex_lock(&ks->lock);
+       eepcr &= ~EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr = 0;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       return data;
+}
+
+/**
+ * ks8851_eeprom_write - write a 16bits word in ks8851 companion EEPROM
+ * @dev: The network device the PHY is on.
+ * @op: operand (can be WRITE, EWEN, EWDS)
+ * @addr: EEPROM address to write
+ * @data: data to write
+ *
+ * eeprom_size: used to define the data coding length. Can be changed
+ * through debug-fs.
+ *
+ * Programs a write on the EEPROM using ks8851 EEPROM SW access feature.
+ *
+ * Note that a write enable is required before writing data.
+ *
+ * Rough programming model:
+ *  - on period start: set clock high
+ *  - on period / 2: set clock low and program value on bus
+ *  - start on period / 2
+ */
+void ks8851_eeprom_write(struct net_device *dev, unsigned int op,
+                                       unsigned int addr, unsigned int data)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int eepcr;
+       int state = EEPROM_CONTROL;
+       int bit_count = EEPROM_OP_LEN - 1;
+       unsigned int addr_len;
+
+       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
+
+       switch (op) {
+       case EEPROM_OP_EWEN:
+               addr = 0x30;
+       break;
+       case EEPROM_OP_EWDS:
+               addr = 0;
+               break;
+       }
+
+       /* start transaction: chip select high, authorize write */
+       mutex_lock(&ks->lock);
+       eepcr = EEPCR_EESA | EEPCR_EESRWA;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr |= EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       while (state != EEPROM_COMPLETE) {
+               /* falling clock period starts... */
+               /* set EED_IO pin for control and address */
+               eepcr &= ~EEPCR_EEDO;
+               switch (state) {
+               case EEPROM_CONTROL:
+                       eepcr |= ((op >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               bit_count = addr_len - 1;
+                               state = EEPROM_ADDRESS;
+                       }
+                       break;
+               case EEPROM_ADDRESS:
+                       eepcr |= ((addr >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               if (op == EEPROM_OP_WRITE) {
+                                       bit_count = EEPROM_DATA_LEN - 1;
+                                       state = EEPROM_DATA;
+                               } else {
+                                       state = EEPROM_COMPLETE;
+                               }
+                       }
+                       break;
+               case EEPROM_DATA:
+                       eepcr |= ((data >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0)
+                               state = EEPROM_COMPLETE;
+                       break;
+               }
+
+               /* lower clock  */
+               eepcr &= ~EEPCR_EESCK;
+
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+
+               /* rising clock period starts... */
+
+               /* raise clock */
+               eepcr |= EEPCR_EESCK;
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+       }
+
+       /* close transaction */
+       mutex_lock(&ks->lock);
+       eepcr &= ~EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr = 0;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+}
+
+/* ethtool support */
+
+static void ks8851_get_drvinfo(struct net_device *dev,
+                              struct ethtool_drvinfo *di)
+{
+       strlcpy(di->driver, "KS8851", sizeof(di->driver));
+       strlcpy(di->version, "1.00", sizeof(di->version));
+       strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
+}
+
+static u32 ks8851_get_msglevel(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return ks->msg_enable;
+}
+
+static void ks8851_set_msglevel(struct net_device *dev, u32 to)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       ks->msg_enable = to;
+}
+
+static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return mii_ethtool_gset(&ks->mii, cmd);
+}
+
+static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return mii_ethtool_sset(&ks->mii, cmd);
+}
+
+static u32 ks8851_get_link(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return mii_link_ok(&ks->mii);
+}
+
+static int ks8851_nway_reset(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return mii_nway_restart(&ks->mii);
+}
+
+static int ks8851_get_eeprom_len(struct net_device *dev)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       return ks->eeprom_size;
+}
+
+static int ks8851_get_eeprom(struct net_device *dev,
+                           struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       u16 *eeprom_buff;
+       int first_word;
+       int last_word;
+       int ret_val = 0;
+       u16 i;
+
+       if (eeprom->len == 0)
+               return -EINVAL;
+
+       if (eeprom->len > ks->eeprom_size)
+               return -EINVAL;
+
+       eeprom->magic = ks8851_rdreg16(ks, KS_CIDER);
+
+       first_word = eeprom->offset >> 1;
+       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+       eeprom_buff = kmalloc(sizeof(u16) *
+                       (last_word - first_word + 1), GFP_KERNEL);
+       if (!eeprom_buff)
+               return -ENOMEM;
+
+       for (i = 0; i < last_word - first_word + 1; i++)
+               eeprom_buff[i] = ks8851_eeprom_read(dev, first_word + 1);
+
+       /* Device's eeprom is little-endian, word addressable */
+       for (i = 0; i < last_word - first_word + 1; i++)
+               le16_to_cpus(&eeprom_buff[i]);
+
+       memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+       kfree(eeprom_buff);
+
+       return ret_val;
+}
+
+static int ks8851_set_eeprom(struct net_device *dev,
+                           struct ethtool_eeprom *eeprom, u8 *bytes)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       u16 *eeprom_buff;
+       void *ptr;
+       int max_len;
+       int first_word;
+       int last_word;
+       int ret_val = 0;
+       u16 i;
+
+       if (eeprom->len == 0)
+               return -EOPNOTSUPP;
+
+       if (eeprom->len > ks->eeprom_size)
+               return -EINVAL;
+
+       if (eeprom->magic != ks8851_rdreg16(ks, KS_CIDER))
+               return -EFAULT;
+
+       first_word = eeprom->offset >> 1;
+       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+       max_len = (last_word - first_word + 1) * 2;
+       eeprom_buff = kmalloc(max_len, GFP_KERNEL);
+       if (!eeprom_buff)
+               return -ENOMEM;
+
+       ptr = (void *)eeprom_buff;
+
+       if (eeprom->offset & 1) {
+               /* need read/modify/write of first changed EEPROM word */
+               /* only the second byte of the word is being modified */
+               eeprom_buff[0] = ks8851_eeprom_read(dev, first_word);
+               ptr++;
+       }
+       if ((eeprom->offset + eeprom->len) & 1)
+               /* need read/modify/write of last changed EEPROM word */
+               /* only the first byte of the word is being modified */
+               eeprom_buff[last_word - first_word] =
+                                       ks8851_eeprom_read(dev, last_word);
+
+
+       /* Device's eeprom is little-endian, word addressable */
+       le16_to_cpus(&eeprom_buff[0]);
+       le16_to_cpus(&eeprom_buff[last_word - first_word]);
+
+       memcpy(ptr, bytes, eeprom->len);
+
+       for (i = 0; i < last_word - first_word + 1; i++)
+               eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
+
+       ks8851_eeprom_write(dev, EEPROM_OP_EWEN, 0, 0);
+
+       for (i = 0; i < last_word - first_word + 1; i++) {
+               ks8851_eeprom_write(dev, EEPROM_OP_WRITE, first_word + i,
+                                                       eeprom_buff[i]);
+               mdelay(EEPROM_WRITE_TIME);
+       }
+
+       ks8851_eeprom_write(dev, EEPROM_OP_EWDS, 0, 0);
+
+       kfree(eeprom_buff);
+       return ret_val;
+}
+
+static const struct ethtool_ops ks8851_ethtool_ops = {
+       .get_drvinfo    = ks8851_get_drvinfo,
+       .get_msglevel   = ks8851_get_msglevel,
+       .set_msglevel   = ks8851_set_msglevel,
+       .get_settings   = ks8851_get_settings,
+       .set_settings   = ks8851_set_settings,
+       .get_link       = ks8851_get_link,
+       .nway_reset     = ks8851_nway_reset,
+       .get_eeprom_len = ks8851_get_eeprom_len,
+       .get_eeprom     = ks8851_get_eeprom,
+       .set_eeprom     = ks8851_set_eeprom,
+};
+
+/* MII interface controls */
+
+/**
+ * ks8851_phy_reg - convert MII register into a KS8851 register
+ * @reg: MII register number.
+ *
+ * Return the KS8851 register number for the corresponding MII PHY register
+ * if possible. Return zero if the MII register has no direct mapping to the
+ * KS8851 register set.
+ */
+static int ks8851_phy_reg(int reg)
+{
+       switch (reg) {
+       case MII_BMCR:
+               return KS_P1MBCR;
+       case MII_BMSR:
+               return KS_P1MBSR;
+       case MII_PHYSID1:
+               return KS_PHY1ILR;
+       case MII_PHYSID2:
+               return KS_PHY1IHR;
+       case MII_ADVERTISE:
+               return KS_P1ANAR;
+       case MII_LPA:
+               return KS_P1ANLPR;
+       }
+
+       return 0x0;
+}
+
+/**
+ * ks8851_phy_read - MII interface PHY register read.
+ * @dev: The network device the PHY is on.
+ * @phy_addr: Address of PHY (ignored as we only have one)
+ * @reg: The register to read.
+ *
+ * This call reads data from the PHY register specified in @reg. Since the
+ * device does not support all the MII registers, the non-existent values
+ * are always returned as zero.
+ *
+ * We return zero for unsupported registers as the MII code does not check
+ * the value returned for any error status, and simply returns it to the
+ * caller. The mii-tool that the driver was tested with takes any -ve error
+ * as real PHY capabilities, thus displaying incorrect data to the user.
+ */
+static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int ksreg;
+       int result;
+
+       ksreg = ks8851_phy_reg(reg);
+       if (!ksreg)
+               return 0x0;     /* no error return allowed, so use zero */
+
+       mutex_lock(&ks->lock);
+       result = ks8851_rdreg16(ks, ksreg);
+       mutex_unlock(&ks->lock);
+
+       return result;
+}
+
+static void ks8851_phy_write(struct net_device *dev,
+                            int phy, int reg, int value)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int ksreg;
+
+       ksreg = ks8851_phy_reg(reg);
+       if (ksreg) {
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, ksreg, value);
+               mutex_unlock(&ks->lock);
+       }
+}
+
+/**
+ * ks8851_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks8851_read_selftest(struct ks8851_net *ks)
+{
+       unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
+       int ret = 0;
+       unsigned rd;
+
+       rd = ks8851_rdreg16(ks, KS_MBIR);
+
+       if ((rd & both_done) != both_done) {
+               netdev_warn(ks->netdev, "Memory selftest not finished\n");
+               return 0;
+       }
+
+       if (rd & MBIR_TXMBFA) {
+               netdev_err(ks->netdev, "TX memory selftest fail\n");
+               ret |= 1;
+       }
+
+       if (rd & MBIR_RXMBFA) {
+               netdev_err(ks->netdev, "RX memory selftest fail\n");
+               ret |= 2;
+       }
+
+       return 0;
+}
+
+/* driver bus management functions */
+
+#ifdef CONFIG_PM
+static int ks8851_suspend(struct spi_device *spi, pm_message_t state)
+{
+       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
+       struct net_device *dev = ks->netdev;
+
+       if (netif_running(dev)) {
+               netif_device_detach(dev);
+               ks8851_net_stop(dev);
+       }
+
+       return 0;
+}
+
+static int ks8851_resume(struct spi_device *spi)
+{
+       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
+       struct net_device *dev = ks->netdev;
+
+       if (netif_running(dev)) {
+               ks8851_net_open(dev);
+               netif_device_attach(dev);
+       }
+
+       return 0;
+}
+#else
+#define ks8851_suspend NULL
+#define ks8851_resume NULL
+#endif
+
+static int __devinit ks8851_probe(struct spi_device *spi)
+{
+       struct net_device *ndev;
+       struct ks8851_net *ks;
+       int ret;
+
+       ndev = alloc_etherdev(sizeof(struct ks8851_net));
+       if (!ndev) {
+               dev_err(&spi->dev, "failed to alloc ethernet device\n");
+               return -ENOMEM;
+       }
+
+       spi->bits_per_word = 8;
+
+       ks = netdev_priv(ndev);
+
+       ks->netdev = ndev;
+       ks->spidev = spi;
+       ks->tx_space = 6144;
+
+       mutex_init(&ks->lock);
+       spin_lock_init(&ks->statelock);
+
+       INIT_WORK(&ks->tx_work, ks8851_tx_work);
+       INIT_WORK(&ks->irq_work, ks8851_irq_work);
+       INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
+
+       /* initialise pre-made spi transfer messages */
+
+       spi_message_init(&ks->spi_msg1);
+       spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1);
+
+       spi_message_init(&ks->spi_msg2);
+       spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2);
+       spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2);
+
+       /* setup mii state */
+       ks->mii.dev             = ndev;
+       ks->mii.phy_id          = 1,
+       ks->mii.phy_id_mask     = 1;
+       ks->mii.reg_num_mask    = 0xf;
+       ks->mii.mdio_read       = ks8851_phy_read;
+       ks->mii.mdio_write      = ks8851_phy_write;
+
+       dev_info(&spi->dev, "message enable is %d\n", msg_enable);
+
+       /* set the default message enable */
+       ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
+                                                    NETIF_MSG_PROBE |
+                                                    NETIF_MSG_LINK));
+
+       skb_queue_head_init(&ks->txq);
+
+       SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
+       SET_NETDEV_DEV(ndev, &spi->dev);
+
+       dev_set_drvdata(&spi->dev, ks);
+
+       ndev->if_port = IF_PORT_100BASET;
+       ndev->netdev_ops = &ks8851_netdev_ops;
+       ndev->irq = spi->irq;
+
+       /* issue a global soft reset to reset the device. */
+       ks8851_soft_reset(ks, GRR_GSR);
+
+       /* simple check for a valid chip being connected to the bus */
+
+       if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
+               dev_err(&spi->dev, "failed to read device ID\n");
+               ret = -ENODEV;
+               goto err_id;
+       }
+
+       /* cache the contents of the CCR register for EEPROM, etc. */
+       ks->rc_ccr = ks8851_rdreg16(ks, KS_CCR);
+
+       if (ks->rc_ccr & CCR_EEPROM)
+               ks->eeprom_size = 128;
+       else
+               ks->eeprom_size = 0;
+
+       ks8851_read_selftest(ks);
+       ks8851_init_mac(ks);
+
+       ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW,
+                         ndev->name, ks);
+       if (ret < 0) {
+               dev_err(&spi->dev, "failed to get irq\n");
+               goto err_irq;
+       }
+
+       ret = register_netdev(ndev);
+       if (ret) {
+               dev_err(&spi->dev, "failed to register network device\n");
+               goto err_netdev;
+       }
+
+       netdev_info(ndev, "revision %d, MAC %pM, IRQ %d\n",
+                   CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)),
+                   ndev->dev_addr, ndev->irq);
+
+       return 0;
+
+
+err_netdev:
+       free_irq(ndev->irq, ndev);
+
+err_id:
+err_irq:
+       free_netdev(ndev);
+       return ret;
+}
+
+static int __devexit ks8851_remove(struct spi_device *spi)
+{
+       struct ks8851_net *priv = dev_get_drvdata(&spi->dev);
+
+       if (netif_msg_drv(priv))
+               dev_info(&spi->dev, "remove\n");
+
+       unregister_netdev(priv->netdev);
+       free_irq(spi->irq, priv);
+       free_netdev(priv->netdev);
+
+       return 0;
+}
+
+static struct spi_driver ks8851_driver = {
+       .driver = {
+               .name = "ks8851",
+               .owner = THIS_MODULE,
+       },
+       .probe = ks8851_probe,
+       .remove = __devexit_p(ks8851_remove),
+       .suspend = ks8851_suspend,
+       .resume = ks8851_resume,
+};
+
+static int __init ks8851_init(void)
+{
+       return spi_register_driver(&ks8851_driver);
+}
+
+static void __exit ks8851_exit(void)
+{
+       spi_unregister_driver(&ks8851_driver);
+}
+
+module_init(ks8851_init);
+module_exit(ks8851_exit);
+
+MODULE_DESCRIPTION("KS8851 Network driver");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_LICENSE("GPL");
+
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
+MODULE_ALIAS("spi:ks8851");
diff --git a/drivers/net/ethernet/micrel/ks8851.h b/drivers/net/ethernet/micrel/ks8851.h
new file mode 100644 (file)
index 0000000..537fb06
--- /dev/null
@@ -0,0 +1,309 @@
+/* drivers/net/ks8851.h
+ *
+ * Copyright 2009 Simtec Electronics
+ *      Ben Dooks <ben@simtec.co.uk>
+ *
+ * KS8851 register definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#define KS_CCR                                 0x08
+#define CCR_EEPROM                             (1 << 9)
+#define CCR_SPI                                        (1 << 8)
+#define CCR_32PIN                              (1 << 0)
+
+/* MAC address registers */
+#define KS_MAR(_m)                             0x15 - (_m)
+#define KS_MARL                                        0x10
+#define KS_MARM                                        0x12
+#define KS_MARH                                        0x14
+
+#define KS_OBCR                                        0x20
+#define OBCR_ODS_16mA                          (1 << 6)
+
+#define KS_EEPCR                               0x22
+#define EEPCR_EESRWA                           (1 << 5)
+#define EEPCR_EESA                             (1 << 4)
+#define EEPCR_EESB_OFFSET                      3
+#define EEPCR_EESB                             (1 << EEPCR_EESB_OFFSET)
+#define EEPCR_EEDO                             (1 << 2)
+#define EEPCR_EESCK                            (1 << 1)
+#define EEPCR_EECS                             (1 << 0)
+
+#define EEPROM_OP_LEN                          3       /* bits:*/
+#define EEPROM_OP_READ                         0x06
+#define EEPROM_OP_EWEN                         0x04
+#define EEPROM_OP_WRITE                                0x05
+#define EEPROM_OP_EWDS                         0x14
+
+#define EEPROM_DATA_LEN                                16      /* 16 bits EEPROM */
+#define EEPROM_WRITE_TIME                      4       /* wrt ack time in ms */
+#define EEPROM_SK_PERIOD                       400     /* in us */
+
+#define KS_MBIR                                        0x24
+#define MBIR_TXMBF                             (1 << 12)
+#define MBIR_TXMBFA                            (1 << 11)
+#define MBIR_RXMBF                             (1 << 4)
+#define MBIR_RXMBFA                            (1 << 3)
+
+#define KS_GRR                                 0x26
+#define GRR_QMU                                        (1 << 1)
+#define GRR_GSR                                        (1 << 0)
+
+#define KS_WFCR                                        0x2A
+#define WFCR_MPRXE                             (1 << 7)
+#define WFCR_WF3E                              (1 << 3)
+#define WFCR_WF2E                              (1 << 2)
+#define WFCR_WF1E                              (1 << 1)
+#define WFCR_WF0E                              (1 << 0)
+
+#define KS_WF0CRC0                             0x30
+#define KS_WF0CRC1                             0x32
+#define KS_WF0BM0                              0x34
+#define KS_WF0BM1                              0x36
+#define KS_WF0BM2                              0x38
+#define KS_WF0BM3                              0x3A
+
+#define KS_WF1CRC0                             0x40
+#define KS_WF1CRC1                             0x42
+#define KS_WF1BM0                              0x44
+#define KS_WF1BM1                              0x46
+#define KS_WF1BM2                              0x48
+#define KS_WF1BM3                              0x4A
+
+#define KS_WF2CRC0                             0x50
+#define KS_WF2CRC1                             0x52
+#define KS_WF2BM0                              0x54
+#define KS_WF2BM1                              0x56
+#define KS_WF2BM2                              0x58
+#define KS_WF2BM3                              0x5A
+
+#define KS_WF3CRC0                             0x60
+#define KS_WF3CRC1                             0x62
+#define KS_WF3BM0                              0x64
+#define KS_WF3BM1                              0x66
+#define KS_WF3BM2                              0x68
+#define KS_WF3BM3                              0x6A
+
+#define KS_TXCR                                        0x70
+#define TXCR_TCGICMP                           (1 << 8)
+#define TXCR_TCGUDP                            (1 << 7)
+#define TXCR_TCGTCP                            (1 << 6)
+#define TXCR_TCGIP                             (1 << 5)
+#define TXCR_FTXQ                              (1 << 4)
+#define TXCR_TXFCE                             (1 << 3)
+#define TXCR_TXPE                              (1 << 2)
+#define TXCR_TXCRC                             (1 << 1)
+#define TXCR_TXE                               (1 << 0)
+
+#define KS_TXSR                                        0x72
+#define TXSR_TXLC                              (1 << 13)
+#define TXSR_TXMC                              (1 << 12)
+#define TXSR_TXFID_MASK                                (0x3f << 0)
+#define TXSR_TXFID_SHIFT                       (0)
+#define TXSR_TXFID_GET(_v)                     (((_v) >> 0) & 0x3f)
+
+#define KS_RXCR1                               0x74
+#define RXCR1_FRXQ                             (1 << 15)
+#define RXCR1_RXUDPFCC                         (1 << 14)
+#define RXCR1_RXTCPFCC                         (1 << 13)
+#define RXCR1_RXIPFCC                          (1 << 12)
+#define RXCR1_RXPAFMA                          (1 << 11)
+#define RXCR1_RXFCE                            (1 << 10)
+#define RXCR1_RXEFE                            (1 << 9)
+#define RXCR1_RXMAFMA                          (1 << 8)
+#define RXCR1_RXBE                             (1 << 7)
+#define RXCR1_RXME                             (1 << 6)
+#define RXCR1_RXUE                             (1 << 5)
+#define RXCR1_RXAE                             (1 << 4)
+#define RXCR1_RXINVF                           (1 << 1)
+#define RXCR1_RXE                              (1 << 0)
+
+#define KS_RXCR2                               0x76
+#define RXCR2_SRDBL_MASK                       (0x7 << 5)
+#define RXCR2_SRDBL_SHIFT                      (5)
+#define RXCR2_SRDBL_4B                         (0x0 << 5)
+#define RXCR2_SRDBL_8B                         (0x1 << 5)
+#define RXCR2_SRDBL_16B                                (0x2 << 5)
+#define RXCR2_SRDBL_32B                                (0x3 << 5)
+#define RXCR2_SRDBL_FRAME                      (0x4 << 5)
+#define RXCR2_IUFFP                            (1 << 4)
+#define RXCR2_RXIUFCEZ                         (1 << 3)
+#define RXCR2_UDPLFE                           (1 << 2)
+#define RXCR2_RXICMPFCC                                (1 << 1)
+#define RXCR2_RXSAF                            (1 << 0)
+
+#define KS_TXMIR                               0x78
+
+#define KS_RXFHSR                              0x7C
+#define RXFSHR_RXFV                            (1 << 15)
+#define RXFSHR_RXICMPFCS                       (1 << 13)
+#define RXFSHR_RXIPFCS                         (1 << 12)
+#define RXFSHR_RXTCPFCS                                (1 << 11)
+#define RXFSHR_RXUDPFCS                                (1 << 10)
+#define RXFSHR_RXBF                            (1 << 7)
+#define RXFSHR_RXMF                            (1 << 6)
+#define RXFSHR_RXUF                            (1 << 5)
+#define RXFSHR_RXMR                            (1 << 4)
+#define RXFSHR_RXFT                            (1 << 3)
+#define RXFSHR_RXFTL                           (1 << 2)
+#define RXFSHR_RXRF                            (1 << 1)
+#define RXFSHR_RXCE                            (1 << 0)
+
+#define KS_RXFHBCR                             0x7E
+#define KS_TXQCR                               0x80
+#define TXQCR_AETFE                            (1 << 2)
+#define TXQCR_TXQMAM                           (1 << 1)
+#define TXQCR_METFE                            (1 << 0)
+
+#define KS_RXQCR                               0x82
+#define RXQCR_RXDTTS                           (1 << 12)
+#define RXQCR_RXDBCTS                          (1 << 11)
+#define RXQCR_RXFCTS                           (1 << 10)
+#define RXQCR_RXIPHTOE                         (1 << 9)
+#define RXQCR_RXDTTE                           (1 << 7)
+#define RXQCR_RXDBCTE                          (1 << 6)
+#define RXQCR_RXFCTE                           (1 << 5)
+#define RXQCR_ADRFE                            (1 << 4)
+#define RXQCR_SDA                              (1 << 3)
+#define RXQCR_RRXEF                            (1 << 0)
+
+#define KS_TXFDPR                              0x84
+#define TXFDPR_TXFPAI                          (1 << 14)
+#define TXFDPR_TXFP_MASK                       (0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT                      (0)
+
+#define KS_RXFDPR                              0x86
+#define RXFDPR_RXFPAI                          (1 << 14)
+
+#define KS_RXDTTR                              0x8C
+#define KS_RXDBCTR                             0x8E
+
+#define KS_IER                                 0x90
+#define KS_ISR                                 0x92
+#define IRQ_LCI                                        (1 << 15)
+#define IRQ_TXI                                        (1 << 14)
+#define IRQ_RXI                                        (1 << 13)
+#define IRQ_RXOI                               (1 << 11)
+#define IRQ_TXPSI                              (1 << 9)
+#define IRQ_RXPSI                              (1 << 8)
+#define IRQ_TXSAI                              (1 << 6)
+#define IRQ_RXWFDI                             (1 << 5)
+#define IRQ_RXMPDI                             (1 << 4)
+#define IRQ_LDI                                        (1 << 3)
+#define IRQ_EDI                                        (1 << 2)
+#define IRQ_SPIBEI                             (1 << 1)
+#define IRQ_DEDI                               (1 << 0)
+
+#define KS_RXFCTR                              0x9C
+#define KS_RXFC                                        0x9D
+#define RXFCTR_RXFC_MASK                       (0xff << 8)
+#define RXFCTR_RXFC_SHIFT                      (8)
+#define RXFCTR_RXFC_GET(_v)                    (((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK                      (0xff << 0)
+#define RXFCTR_RXFCT_SHIFT                     (0)
+
+#define KS_TXNTFSR                             0x9E
+
+#define KS_MAHTR0                              0xA0
+#define KS_MAHTR1                              0xA2
+#define KS_MAHTR2                              0xA4
+#define KS_MAHTR3                              0xA6
+
+#define KS_FCLWR                               0xB0
+#define KS_FCHWR                               0xB2
+#define KS_FCOWR                               0xB4
+
+#define KS_CIDER                               0xC0
+#define CIDER_ID                               0x8870
+#define CIDER_REV_MASK                         (0x7 << 1)
+#define CIDER_REV_SHIFT                                (1)
+#define CIDER_REV_GET(_v)                      (((_v) >> 1) & 0x7)
+
+#define KS_CGCR                                        0xC6
+
+#define KS_IACR                                        0xC8
+#define IACR_RDEN                              (1 << 12)
+#define IACR_TSEL_MASK                         (0x3 << 10)
+#define IACR_TSEL_SHIFT                                (10)
+#define IACR_TSEL_MIB                          (0x3 << 10)
+#define IACR_ADDR_MASK                         (0x1f << 0)
+#define IACR_ADDR_SHIFT                                (0)
+
+#define KS_IADLR                               0xD0
+#define KS_IAHDR                               0xD2
+
+#define KS_PMECR                               0xD4
+#define PMECR_PME_DELAY                                (1 << 14)
+#define PMECR_PME_POL                          (1 << 12)
+#define PMECR_WOL_WAKEUP                       (1 << 11)
+#define PMECR_WOL_MAGICPKT                     (1 << 10)
+#define PMECR_WOL_LINKUP                       (1 << 9)
+#define PMECR_WOL_ENERGY                       (1 << 8)
+#define PMECR_AUTO_WAKE_EN                     (1 << 7)
+#define PMECR_WAKEUP_NORMAL                    (1 << 6)
+#define PMECR_WKEVT_MASK                       (0xf << 2)
+#define PMECR_WKEVT_SHIFT                      (2)
+#define PMECR_WKEVT_GET(_v)                    (((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY                     (0x1 << 2)
+#define PMECR_WKEVT_LINK                       (0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT                   (0x4 << 2)
+#define PMECR_WKEVT_FRAME                      (0x8 << 2)
+#define PMECR_PM_MASK                          (0x3 << 0)
+#define PMECR_PM_SHIFT                         (0)
+#define PMECR_PM_NORMAL                                (0x0 << 0)
+#define PMECR_PM_ENERGY                                (0x1 << 0)
+#define PMECR_PM_SOFTDOWN                      (0x2 << 0)
+#define PMECR_PM_POWERSAVE                     (0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR                              0xE4
+#define KS_P1MBSR                              0xE6
+#define KS_PHY1ILR                             0xE8
+#define KS_PHY1IHR                             0xEA
+#define KS_P1ANAR                              0xEC
+#define KS_P1ANLPR                             0xEE
+
+#define KS_P1SCLMD                             0xF4
+#define P1SCLMD_LEDOFF                         (1 << 15)
+#define P1SCLMD_TXIDS                          (1 << 14)
+#define P1SCLMD_RESTARTAN                      (1 << 13)
+#define P1SCLMD_DISAUTOMDIX                    (1 << 10)
+#define P1SCLMD_FORCEMDIX                      (1 << 9)
+#define P1SCLMD_AUTONEGEN                      (1 << 7)
+#define P1SCLMD_FORCE100                       (1 << 6)
+#define P1SCLMD_FORCEFDX                       (1 << 5)
+#define P1SCLMD_ADV_FLOW                       (1 << 4)
+#define P1SCLMD_ADV_100BT_FDX                  (1 << 3)
+#define P1SCLMD_ADV_100BT_HDX                  (1 << 2)
+#define P1SCLMD_ADV_10BT_FDX                   (1 << 1)
+#define P1SCLMD_ADV_10BT_HDX                   (1 << 0)
+
+#define KS_P1CR                                        0xF6
+#define P1CR_HP_MDIX                           (1 << 15)
+#define P1CR_REV_POL                           (1 << 13)
+#define P1CR_OP_100M                           (1 << 10)
+#define P1CR_OP_FDX                            (1 << 9)
+#define P1CR_OP_MDI                            (1 << 7)
+#define P1CR_AN_DONE                           (1 << 6)
+#define P1CR_LINK_GOOD                         (1 << 5)
+#define P1CR_PNTR_FLOW                         (1 << 4)
+#define P1CR_PNTR_100BT_FDX                    (1 << 3)
+#define P1CR_PNTR_100BT_HDX                    (1 << 2)
+#define P1CR_PNTR_10BT_FDX                     (1 << 1)
+#define P1CR_PNTR_10BT_HDX                     (1 << 0)
+
+/* TX Frame control */
+
+#define TXFR_TXIC                              (1 << 15)
+#define TXFR_TXFID_MASK                                (0x3f << 0)
+#define TXFR_TXFID_SHIFT                       (0)
+
+/* SPI frame opcodes */
+#define KS_SPIOP_RD                            (0x00)
+#define KS_SPIOP_WR                            (0x40)
+#define KS_SPIOP_RXFIFO                                (0x80)
+#define KS_SPIOP_TXFIFO                                (0xC0)
diff --git a/drivers/net/ethernet/micrel/ks8851_mll.c b/drivers/net/ethernet/micrel/ks8851_mll.c
new file mode 100644 (file)
index 0000000..d19c849
--- /dev/null
@@ -0,0 +1,1680 @@
+/**
+ * drivers/net/ks8851_mll.c
+ * Copyright (c) 2009 Micrel Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/**
+ * Supports:
+ * KS8851 16bit MLL chip from Micrel Inc.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/cache.h>
+#include <linux/crc32.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+
+#define        DRV_NAME        "ks8851_mll"
+
+static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 };
+#define MAX_RECV_FRAMES                        32
+#define MAX_BUF_SIZE                   2048
+#define TX_BUF_SIZE                    2000
+#define RX_BUF_SIZE                    2000
+
+#define KS_CCR                         0x08
+#define CCR_EEPROM                     (1 << 9)
+#define CCR_SPI                                (1 << 8)
+#define CCR_8BIT                       (1 << 7)
+#define CCR_16BIT                      (1 << 6)
+#define CCR_32BIT                      (1 << 5)
+#define CCR_SHARED                     (1 << 4)
+#define CCR_32PIN                      (1 << 0)
+
+/* MAC address registers */
+#define KS_MARL                                0x10
+#define KS_MARM                                0x12
+#define KS_MARH                                0x14
+
+#define KS_OBCR                                0x20
+#define OBCR_ODS_16MA                  (1 << 6)
+
+#define KS_EEPCR                       0x22
+#define EEPCR_EESA                     (1 << 4)
+#define EEPCR_EESB                     (1 << 3)
+#define EEPCR_EEDO                     (1 << 2)
+#define EEPCR_EESCK                    (1 << 1)
+#define EEPCR_EECS                     (1 << 0)
+
+#define KS_MBIR                                0x24
+#define MBIR_TXMBF                     (1 << 12)
+#define MBIR_TXMBFA                    (1 << 11)
+#define MBIR_RXMBF                     (1 << 4)
+#define MBIR_RXMBFA                    (1 << 3)
+
+#define KS_GRR                         0x26
+#define GRR_QMU                                (1 << 1)
+#define GRR_GSR                                (1 << 0)
+
+#define KS_WFCR                                0x2A
+#define WFCR_MPRXE                     (1 << 7)
+#define WFCR_WF3E                      (1 << 3)
+#define WFCR_WF2E                      (1 << 2)
+#define WFCR_WF1E                      (1 << 1)
+#define WFCR_WF0E                      (1 << 0)
+
+#define KS_WF0CRC0                     0x30
+#define KS_WF0CRC1                     0x32
+#define KS_WF0BM0                      0x34
+#define KS_WF0BM1                      0x36
+#define KS_WF0BM2                      0x38
+#define KS_WF0BM3                      0x3A
+
+#define KS_WF1CRC0                     0x40
+#define KS_WF1CRC1                     0x42
+#define KS_WF1BM0                      0x44
+#define KS_WF1BM1                      0x46
+#define KS_WF1BM2                      0x48
+#define KS_WF1BM3                      0x4A
+
+#define KS_WF2CRC0                     0x50
+#define KS_WF2CRC1                     0x52
+#define KS_WF2BM0                      0x54
+#define KS_WF2BM1                      0x56
+#define KS_WF2BM2                      0x58
+#define KS_WF2BM3                      0x5A
+
+#define KS_WF3CRC0                     0x60
+#define KS_WF3CRC1                     0x62
+#define KS_WF3BM0                      0x64
+#define KS_WF3BM1                      0x66
+#define KS_WF3BM2                      0x68
+#define KS_WF3BM3                      0x6A
+
+#define KS_TXCR                                0x70
+#define TXCR_TCGICMP                   (1 << 8)
+#define TXCR_TCGUDP                    (1 << 7)
+#define TXCR_TCGTCP                    (1 << 6)
+#define TXCR_TCGIP                     (1 << 5)
+#define TXCR_FTXQ                      (1 << 4)
+#define TXCR_TXFCE                     (1 << 3)
+#define TXCR_TXPE                      (1 << 2)
+#define TXCR_TXCRC                     (1 << 1)
+#define TXCR_TXE                       (1 << 0)
+
+#define KS_TXSR                                0x72
+#define TXSR_TXLC                      (1 << 13)
+#define TXSR_TXMC                      (1 << 12)
+#define TXSR_TXFID_MASK                        (0x3f << 0)
+#define TXSR_TXFID_SHIFT               (0)
+#define TXSR_TXFID_GET(_v)             (((_v) >> 0) & 0x3f)
+
+
+#define KS_RXCR1                       0x74
+#define RXCR1_FRXQ                     (1 << 15)
+#define RXCR1_RXUDPFCC                 (1 << 14)
+#define RXCR1_RXTCPFCC                 (1 << 13)
+#define RXCR1_RXIPFCC                  (1 << 12)
+#define RXCR1_RXPAFMA                  (1 << 11)
+#define RXCR1_RXFCE                    (1 << 10)
+#define RXCR1_RXEFE                    (1 << 9)
+#define RXCR1_RXMAFMA                  (1 << 8)
+#define RXCR1_RXBE                     (1 << 7)
+#define RXCR1_RXME                     (1 << 6)
+#define RXCR1_RXUE                     (1 << 5)
+#define RXCR1_RXAE                     (1 << 4)
+#define RXCR1_RXINVF                   (1 << 1)
+#define RXCR1_RXE                      (1 << 0)
+#define RXCR1_FILTER_MASK              (RXCR1_RXINVF | RXCR1_RXAE | \
+                                        RXCR1_RXMAFMA | RXCR1_RXPAFMA)
+
+#define KS_RXCR2                       0x76
+#define RXCR2_SRDBL_MASK               (0x7 << 5)
+#define RXCR2_SRDBL_SHIFT              (5)
+#define RXCR2_SRDBL_4B                 (0x0 << 5)
+#define RXCR2_SRDBL_8B                 (0x1 << 5)
+#define RXCR2_SRDBL_16B                        (0x2 << 5)
+#define RXCR2_SRDBL_32B                        (0x3 << 5)
+/* #define RXCR2_SRDBL_FRAME           (0x4 << 5) */
+#define RXCR2_IUFFP                    (1 << 4)
+#define RXCR2_RXIUFCEZ                 (1 << 3)
+#define RXCR2_UDPLFE                   (1 << 2)
+#define RXCR2_RXICMPFCC                        (1 << 1)
+#define RXCR2_RXSAF                    (1 << 0)
+
+#define KS_TXMIR                       0x78
+
+#define KS_RXFHSR                      0x7C
+#define RXFSHR_RXFV                    (1 << 15)
+#define RXFSHR_RXICMPFCS               (1 << 13)
+#define RXFSHR_RXIPFCS                 (1 << 12)
+#define RXFSHR_RXTCPFCS                        (1 << 11)
+#define RXFSHR_RXUDPFCS                        (1 << 10)
+#define RXFSHR_RXBF                    (1 << 7)
+#define RXFSHR_RXMF                    (1 << 6)
+#define RXFSHR_RXUF                    (1 << 5)
+#define RXFSHR_RXMR                    (1 << 4)
+#define RXFSHR_RXFT                    (1 << 3)
+#define RXFSHR_RXFTL                   (1 << 2)
+#define RXFSHR_RXRF                    (1 << 1)
+#define RXFSHR_RXCE                    (1 << 0)
+#define        RXFSHR_ERR                      (RXFSHR_RXCE | RXFSHR_RXRF |\
+                                       RXFSHR_RXFTL | RXFSHR_RXMR |\
+                                       RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\
+                                       RXFSHR_RXTCPFCS)
+#define KS_RXFHBCR                     0x7E
+#define RXFHBCR_CNT_MASK               0x0FFF
+
+#define KS_TXQCR                       0x80
+#define TXQCR_AETFE                    (1 << 2)
+#define TXQCR_TXQMAM                   (1 << 1)
+#define TXQCR_METFE                    (1 << 0)
+
+#define KS_RXQCR                       0x82
+#define RXQCR_RXDTTS                   (1 << 12)
+#define RXQCR_RXDBCTS                  (1 << 11)
+#define RXQCR_RXFCTS                   (1 << 10)
+#define RXQCR_RXIPHTOE                 (1 << 9)
+#define RXQCR_RXDTTE                   (1 << 7)
+#define RXQCR_RXDBCTE                  (1 << 6)
+#define RXQCR_RXFCTE                   (1 << 5)
+#define RXQCR_ADRFE                    (1 << 4)
+#define RXQCR_SDA                      (1 << 3)
+#define RXQCR_RRXEF                    (1 << 0)
+#define RXQCR_CMD_CNTL                 (RXQCR_RXFCTE|RXQCR_ADRFE)
+
+#define KS_TXFDPR                      0x84
+#define TXFDPR_TXFPAI                  (1 << 14)
+#define TXFDPR_TXFP_MASK               (0x7ff << 0)
+#define TXFDPR_TXFP_SHIFT              (0)
+
+#define KS_RXFDPR                      0x86
+#define RXFDPR_RXFPAI                  (1 << 14)
+
+#define KS_RXDTTR                      0x8C
+#define KS_RXDBCTR                     0x8E
+
+#define KS_IER                         0x90
+#define KS_ISR                         0x92
+#define IRQ_LCI                                (1 << 15)
+#define IRQ_TXI                                (1 << 14)
+#define IRQ_RXI                                (1 << 13)
+#define IRQ_RXOI                       (1 << 11)
+#define IRQ_TXPSI                      (1 << 9)
+#define IRQ_RXPSI                      (1 << 8)
+#define IRQ_TXSAI                      (1 << 6)
+#define IRQ_RXWFDI                     (1 << 5)
+#define IRQ_RXMPDI                     (1 << 4)
+#define IRQ_LDI                                (1 << 3)
+#define IRQ_EDI                                (1 << 2)
+#define IRQ_SPIBEI                     (1 << 1)
+#define IRQ_DEDI                       (1 << 0)
+
+#define KS_RXFCTR                      0x9C
+#define RXFCTR_THRESHOLD_MASK          0x00FF
+
+#define KS_RXFC                                0x9D
+#define RXFCTR_RXFC_MASK               (0xff << 8)
+#define RXFCTR_RXFC_SHIFT              (8)
+#define RXFCTR_RXFC_GET(_v)            (((_v) >> 8) & 0xff)
+#define RXFCTR_RXFCT_MASK              (0xff << 0)
+#define RXFCTR_RXFCT_SHIFT             (0)
+
+#define KS_TXNTFSR                     0x9E
+
+#define KS_MAHTR0                      0xA0
+#define KS_MAHTR1                      0xA2
+#define KS_MAHTR2                      0xA4
+#define KS_MAHTR3                      0xA6
+
+#define KS_FCLWR                       0xB0
+#define KS_FCHWR                       0xB2
+#define KS_FCOWR                       0xB4
+
+#define KS_CIDER                       0xC0
+#define CIDER_ID                       0x8870
+#define CIDER_REV_MASK                 (0x7 << 1)
+#define CIDER_REV_SHIFT                        (1)
+#define CIDER_REV_GET(_v)              (((_v) >> 1) & 0x7)
+
+#define KS_CGCR                                0xC6
+#define KS_IACR                                0xC8
+#define IACR_RDEN                      (1 << 12)
+#define IACR_TSEL_MASK                 (0x3 << 10)
+#define IACR_TSEL_SHIFT                        (10)
+#define IACR_TSEL_MIB                  (0x3 << 10)
+#define IACR_ADDR_MASK                 (0x1f << 0)
+#define IACR_ADDR_SHIFT                        (0)
+
+#define KS_IADLR                       0xD0
+#define KS_IAHDR                       0xD2
+
+#define KS_PMECR                       0xD4
+#define PMECR_PME_DELAY                        (1 << 14)
+#define PMECR_PME_POL                  (1 << 12)
+#define PMECR_WOL_WAKEUP               (1 << 11)
+#define PMECR_WOL_MAGICPKT             (1 << 10)
+#define PMECR_WOL_LINKUP               (1 << 9)
+#define PMECR_WOL_ENERGY               (1 << 8)
+#define PMECR_AUTO_WAKE_EN             (1 << 7)
+#define PMECR_WAKEUP_NORMAL            (1 << 6)
+#define PMECR_WKEVT_MASK               (0xf << 2)
+#define PMECR_WKEVT_SHIFT              (2)
+#define PMECR_WKEVT_GET(_v)            (((_v) >> 2) & 0xf)
+#define PMECR_WKEVT_ENERGY             (0x1 << 2)
+#define PMECR_WKEVT_LINK               (0x2 << 2)
+#define PMECR_WKEVT_MAGICPKT           (0x4 << 2)
+#define PMECR_WKEVT_FRAME              (0x8 << 2)
+#define PMECR_PM_MASK                  (0x3 << 0)
+#define PMECR_PM_SHIFT                 (0)
+#define PMECR_PM_NORMAL                        (0x0 << 0)
+#define PMECR_PM_ENERGY                        (0x1 << 0)
+#define PMECR_PM_SOFTDOWN              (0x2 << 0)
+#define PMECR_PM_POWERSAVE             (0x3 << 0)
+
+/* Standard MII PHY data */
+#define KS_P1MBCR                      0xE4
+#define P1MBCR_FORCE_FDX               (1 << 8)
+
+#define KS_P1MBSR                      0xE6
+#define P1MBSR_AN_COMPLETE             (1 << 5)
+#define P1MBSR_AN_CAPABLE              (1 << 3)
+#define P1MBSR_LINK_UP                 (1 << 2)
+
+#define KS_PHY1ILR                     0xE8
+#define KS_PHY1IHR                     0xEA
+#define KS_P1ANAR                      0xEC
+#define KS_P1ANLPR                     0xEE
+
+#define KS_P1SCLMD                     0xF4
+#define P1SCLMD_LEDOFF                 (1 << 15)
+#define P1SCLMD_TXIDS                  (1 << 14)
+#define P1SCLMD_RESTARTAN              (1 << 13)
+#define P1SCLMD_DISAUTOMDIX            (1 << 10)
+#define P1SCLMD_FORCEMDIX              (1 << 9)
+#define P1SCLMD_AUTONEGEN              (1 << 7)
+#define P1SCLMD_FORCE100               (1 << 6)
+#define P1SCLMD_FORCEFDX               (1 << 5)
+#define P1SCLMD_ADV_FLOW               (1 << 4)
+#define P1SCLMD_ADV_100BT_FDX          (1 << 3)
+#define P1SCLMD_ADV_100BT_HDX          (1 << 2)
+#define P1SCLMD_ADV_10BT_FDX           (1 << 1)
+#define P1SCLMD_ADV_10BT_HDX           (1 << 0)
+
+#define KS_P1CR                                0xF6
+#define P1CR_HP_MDIX                   (1 << 15)
+#define P1CR_REV_POL                   (1 << 13)
+#define P1CR_OP_100M                   (1 << 10)
+#define P1CR_OP_FDX                    (1 << 9)
+#define P1CR_OP_MDI                    (1 << 7)
+#define P1CR_AN_DONE                   (1 << 6)
+#define P1CR_LINK_GOOD                 (1 << 5)
+#define P1CR_PNTR_FLOW                 (1 << 4)
+#define P1CR_PNTR_100BT_FDX            (1 << 3)
+#define P1CR_PNTR_100BT_HDX            (1 << 2)
+#define P1CR_PNTR_10BT_FDX             (1 << 1)
+#define P1CR_PNTR_10BT_HDX             (1 << 0)
+
+/* TX Frame control */
+
+#define TXFR_TXIC                      (1 << 15)
+#define TXFR_TXFID_MASK                        (0x3f << 0)
+#define TXFR_TXFID_SHIFT               (0)
+
+#define KS_P1SR                                0xF8
+#define P1SR_HP_MDIX                   (1 << 15)
+#define P1SR_REV_POL                   (1 << 13)
+#define P1SR_OP_100M                   (1 << 10)
+#define P1SR_OP_FDX                    (1 << 9)
+#define P1SR_OP_MDI                    (1 << 7)
+#define P1SR_AN_DONE                   (1 << 6)
+#define P1SR_LINK_GOOD                 (1 << 5)
+#define P1SR_PNTR_FLOW                 (1 << 4)
+#define P1SR_PNTR_100BT_FDX            (1 << 3)
+#define P1SR_PNTR_100BT_HDX            (1 << 2)
+#define P1SR_PNTR_10BT_FDX             (1 << 1)
+#define P1SR_PNTR_10BT_HDX             (1 << 0)
+
+#define        ENUM_BUS_NONE                   0
+#define        ENUM_BUS_8BIT                   1
+#define        ENUM_BUS_16BIT                  2
+#define        ENUM_BUS_32BIT                  3
+
+#define MAX_MCAST_LST                  32
+#define HW_MCAST_SIZE                  8
+
+/**
+ * union ks_tx_hdr - tx header data
+ * @txb: The header as bytes
+ * @txw: The header as 16bit, little-endian words
+ *
+ * A dual representation of the tx header data to allow
+ * access to individual bytes, and to allow 16bit accesses
+ * with 16bit alignment.
+ */
+union ks_tx_hdr {
+       u8      txb[4];
+       __le16  txw[2];
+};
+
+/**
+ * struct ks_net - KS8851 driver private data
+ * @net_device         : The network device we're bound to
+ * @hw_addr    : start address of data register.
+ * @hw_addr_cmd        : start address of command register.
+ * @txh        : temporaly buffer to save status/length.
+ * @lock       : Lock to ensure that the device is not accessed when busy.
+ * @pdev       : Pointer to platform device.
+ * @mii                : The MII state information for the mii calls.
+ * @frame_head_info    : frame header information for multi-pkt rx.
+ * @statelock  : Lock on this structure for tx list.
+ * @msg_enable : The message flags controlling driver output (see ethtool).
+ * @frame_cnt          : number of frames received.
+ * @bus_width          : i/o bus width.
+ * @irq        : irq number assigned to this device.
+ * @rc_rxqcr   : Cached copy of KS_RXQCR.
+ * @rc_txcr    : Cached copy of KS_TXCR.
+ * @rc_ier     : Cached copy of KS_IER.
+ * @sharedbus          : Multipex(addr and data bus) mode indicator.
+ * @cmd_reg_cache      : command register cached.
+ * @cmd_reg_cache_int  : command register cached. Used in the irq handler.
+ * @promiscuous        : promiscuous mode indicator.
+ * @all_mcast          : mutlicast indicator.
+ * @mcast_lst_size     : size of multicast list.
+ * @mcast_lst          : multicast list.
+ * @mcast_bits         : multicast enabed.
+ * @mac_addr                   : MAC address assigned to this device.
+ * @fid                : frame id.
+ * @extra_byte         : number of extra byte prepended rx pkt.
+ * @enabled                    : indicator this device works.
+ *
+ * The @lock ensures that the chip is protected when certain operations are
+ * in progress. When the read or write packet transfer is in progress, most
+ * of the chip registers are not accessible until the transfer is finished and
+ * the DMA has been de-asserted.
+ *
+ * The @statelock is used to protect information in the structure which may
+ * need to be accessed via several sources, such as the network driver layer
+ * or one of the work queues.
+ *
+ */
+
+/* Receive multiplex framer header info */
+struct type_frame_head {
+       u16     sts;         /* Frame status */
+       u16     len;         /* Byte count */
+};
+
+struct ks_net {
+       struct net_device       *netdev;
+       void __iomem            *hw_addr;
+       void __iomem            *hw_addr_cmd;
+       union ks_tx_hdr         txh ____cacheline_aligned;
+       struct mutex            lock; /* spinlock to be interrupt safe */
+       struct platform_device *pdev;
+       struct mii_if_info      mii;
+       struct type_frame_head  *frame_head_info;
+       spinlock_t              statelock;
+       u32                     msg_enable;
+       u32                     frame_cnt;
+       int                     bus_width;
+       int                     irq;
+
+       u16                     rc_rxqcr;
+       u16                     rc_txcr;
+       u16                     rc_ier;
+       u16                     sharedbus;
+       u16                     cmd_reg_cache;
+       u16                     cmd_reg_cache_int;
+       u16                     promiscuous;
+       u16                     all_mcast;
+       u16                     mcast_lst_size;
+       u8                      mcast_lst[MAX_MCAST_LST][ETH_ALEN];
+       u8                      mcast_bits[HW_MCAST_SIZE];
+       u8                      mac_addr[6];
+       u8                      fid;
+       u8                      extra_byte;
+       u8                      enabled;
+};
+
+static int msg_enable;
+
+#define BE3             0x8000      /* Byte Enable 3 */
+#define BE2             0x4000      /* Byte Enable 2 */
+#define BE1             0x2000      /* Byte Enable 1 */
+#define BE0             0x1000      /* Byte Enable 0 */
+
+/**
+ * register read/write calls.
+ *
+ * All these calls issue transactions to access the chip's registers. They
+ * all require that the necessary lock is held to prevent accesses when the
+ * chip is busy transferring packet data (RX/TX FIFO accesses).
+ */
+
+/**
+ * ks_rdreg8 - read 8 bit register from device
+ * @ks   : The chip information
+ * @offset: The register address
+ *
+ * Read a 8bit register from the chip, returning the result
+ */
+static u8 ks_rdreg8(struct ks_net *ks, int offset)
+{
+       u16 data;
+       u8 shift_bit = offset & 0x03;
+       u8 shift_data = (offset & 1) << 3;
+       ks->cmd_reg_cache = (u16) offset | (u16)(BE0 << shift_bit);
+       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
+       data  = ioread16(ks->hw_addr);
+       return (u8)(data >> shift_data);
+}
+
+/**
+ * ks_rdreg16 - read 16 bit register from device
+ * @ks   : The chip information
+ * @offset: The register address
+ *
+ * Read a 16bit register from the chip, returning the result
+ */
+
+static u16 ks_rdreg16(struct ks_net *ks, int offset)
+{
+       ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
+       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
+       return ioread16(ks->hw_addr);
+}
+
+/**
+ * ks_wrreg8 - write 8bit register value to chip
+ * @ks: The chip information
+ * @offset: The register address
+ * @value: The value to write
+ *
+ */
+static void ks_wrreg8(struct ks_net *ks, int offset, u8 value)
+{
+       u8  shift_bit = (offset & 0x03);
+       u16 value_write = (u16)(value << ((offset & 1) << 3));
+       ks->cmd_reg_cache = (u16)offset | (BE0 << shift_bit);
+       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
+       iowrite16(value_write, ks->hw_addr);
+}
+
+/**
+ * ks_wrreg16 - write 16bit register value to chip
+ * @ks: The chip information
+ * @offset: The register address
+ * @value: The value to write
+ *
+ */
+
+static void ks_wrreg16(struct ks_net *ks, int offset, u16 value)
+{
+       ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
+       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
+       iowrite16(value, ks->hw_addr);
+}
+
+/**
+ * ks_inblk - read a block of data from QMU. This is called after sudo DMA mode enabled.
+ * @ks: The chip state
+ * @wptr: buffer address to save data
+ * @len: length in byte to read
+ *
+ */
+static inline void ks_inblk(struct ks_net *ks, u16 *wptr, u32 len)
+{
+       len >>= 1;
+       while (len--)
+               *wptr++ = (u16)ioread16(ks->hw_addr);
+}
+
+/**
+ * ks_outblk - write data to QMU. This is called after sudo DMA mode enabled.
+ * @ks: The chip information
+ * @wptr: buffer address
+ * @len: length in byte to write
+ *
+ */
+static inline void ks_outblk(struct ks_net *ks, u16 *wptr, u32 len)
+{
+       len >>= 1;
+       while (len--)
+               iowrite16(*wptr++, ks->hw_addr);
+}
+
+static void ks_disable_int(struct ks_net *ks)
+{
+       ks_wrreg16(ks, KS_IER, 0x0000);
+}  /* ks_disable_int */
+
+static void ks_enable_int(struct ks_net *ks)
+{
+       ks_wrreg16(ks, KS_IER, ks->rc_ier);
+}  /* ks_enable_int */
+
+/**
+ * ks_tx_fifo_space - return the available hardware buffer size.
+ * @ks: The chip information
+ *
+ */
+static inline u16 ks_tx_fifo_space(struct ks_net *ks)
+{
+       return ks_rdreg16(ks, KS_TXMIR) & 0x1fff;
+}
+
+/**
+ * ks_save_cmd_reg - save the command register from the cache.
+ * @ks: The chip information
+ *
+ */
+static inline void ks_save_cmd_reg(struct ks_net *ks)
+{
+       /*ks8851 MLL has a bug to read back the command register.
+       * So rely on software to save the content of command register.
+       */
+       ks->cmd_reg_cache_int = ks->cmd_reg_cache;
+}
+
+/**
+ * ks_restore_cmd_reg - restore the command register from the cache and
+ *     write to hardware register.
+ * @ks: The chip information
+ *
+ */
+static inline void ks_restore_cmd_reg(struct ks_net *ks)
+{
+       ks->cmd_reg_cache = ks->cmd_reg_cache_int;
+       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
+}
+
+/**
+ * ks_set_powermode - set power mode of the device
+ * @ks: The chip information
+ * @pwrmode: The power mode value to write to KS_PMECR.
+ *
+ * Change the power mode of the chip.
+ */
+static void ks_set_powermode(struct ks_net *ks, unsigned pwrmode)
+{
+       unsigned pmecr;
+
+       netif_dbg(ks, hw, ks->netdev, "setting power mode %d\n", pwrmode);
+
+       ks_rdreg16(ks, KS_GRR);
+       pmecr = ks_rdreg16(ks, KS_PMECR);
+       pmecr &= ~PMECR_PM_MASK;
+       pmecr |= pwrmode;
+
+       ks_wrreg16(ks, KS_PMECR, pmecr);
+}
+
+/**
+ * ks_read_config - read chip configuration of bus width.
+ * @ks: The chip information
+ *
+ */
+static void ks_read_config(struct ks_net *ks)
+{
+       u16 reg_data = 0;
+
+       /* Regardless of bus width, 8 bit read should always work.*/
+       reg_data = ks_rdreg8(ks, KS_CCR) & 0x00FF;
+       reg_data |= ks_rdreg8(ks, KS_CCR+1) << 8;
+
+       /* addr/data bus are multiplexed */
+       ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED;
+
+       /* There are garbage data when reading data from QMU,
+       depending on bus-width.
+       */
+
+       if (reg_data & CCR_8BIT) {
+               ks->bus_width = ENUM_BUS_8BIT;
+               ks->extra_byte = 1;
+       } else if (reg_data & CCR_16BIT) {
+               ks->bus_width = ENUM_BUS_16BIT;
+               ks->extra_byte = 2;
+       } else {
+               ks->bus_width = ENUM_BUS_32BIT;
+               ks->extra_byte = 4;
+       }
+}
+
+/**
+ * ks_soft_reset - issue one of the soft reset to the device
+ * @ks: The device state.
+ * @op: The bit(s) to set in the GRR
+ *
+ * Issue the relevant soft-reset command to the device's GRR register
+ * specified by @op.
+ *
+ * Note, the delays are in there as a caution to ensure that the reset
+ * has time to take effect and then complete. Since the datasheet does
+ * not currently specify the exact sequence, we have chosen something
+ * that seems to work with our device.
+ */
+static void ks_soft_reset(struct ks_net *ks, unsigned op)
+{
+       /* Disable interrupt first */
+       ks_wrreg16(ks, KS_IER, 0x0000);
+       ks_wrreg16(ks, KS_GRR, op);
+       mdelay(10);     /* wait a short time to effect reset */
+       ks_wrreg16(ks, KS_GRR, 0);
+       mdelay(1);      /* wait for condition to clear */
+}
+
+
+void ks_enable_qmu(struct ks_net *ks)
+{
+       u16 w;
+
+       w = ks_rdreg16(ks, KS_TXCR);
+       /* Enables QMU Transmit (TXCR). */
+       ks_wrreg16(ks, KS_TXCR, w | TXCR_TXE);
+
+       /*
+        * RX Frame Count Threshold Enable and Auto-Dequeue RXQ Frame
+        * Enable
+        */
+
+       w = ks_rdreg16(ks, KS_RXQCR);
+       ks_wrreg16(ks, KS_RXQCR, w | RXQCR_RXFCTE);
+
+       /* Enables QMU Receive (RXCR1). */
+       w = ks_rdreg16(ks, KS_RXCR1);
+       ks_wrreg16(ks, KS_RXCR1, w | RXCR1_RXE);
+       ks->enabled = true;
+}  /* ks_enable_qmu */
+
+static void ks_disable_qmu(struct ks_net *ks)
+{
+       u16     w;
+
+       w = ks_rdreg16(ks, KS_TXCR);
+
+       /* Disables QMU Transmit (TXCR). */
+       w  &= ~TXCR_TXE;
+       ks_wrreg16(ks, KS_TXCR, w);
+
+       /* Disables QMU Receive (RXCR1). */
+       w = ks_rdreg16(ks, KS_RXCR1);
+       w &= ~RXCR1_RXE ;
+       ks_wrreg16(ks, KS_RXCR1, w);
+
+       ks->enabled = false;
+
+}  /* ks_disable_qmu */
+
+/**
+ * ks_read_qmu - read 1 pkt data from the QMU.
+ * @ks: The chip information
+ * @buf: buffer address to save 1 pkt
+ * @len: Pkt length
+ * Here is the sequence to read 1 pkt:
+ *     1. set sudo DMA mode
+ *     2. read prepend data
+ *     3. read pkt data
+ *     4. reset sudo DMA Mode
+ */
+static inline void ks_read_qmu(struct ks_net *ks, u16 *buf, u32 len)
+{
+       u32 r =  ks->extra_byte & 0x1 ;
+       u32 w = ks->extra_byte - r;
+
+       /* 1. set sudo DMA mode */
+       ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
+       ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
+
+       /* 2. read prepend data */
+       /**
+        * read 4 + extra bytes and discard them.
+        * extra bytes for dummy, 2 for status, 2 for len
+        */
+
+       /* use likely(r) for 8 bit access for performance */
+       if (unlikely(r))
+               ioread8(ks->hw_addr);
+       ks_inblk(ks, buf, w + 2 + 2);
+
+       /* 3. read pkt data */
+       ks_inblk(ks, buf, ALIGN(len, 4));
+
+       /* 4. reset sudo DMA Mode */
+       ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr);
+}
+
+/**
+ * ks_rcv - read multiple pkts data from the QMU.
+ * @ks: The chip information
+ * @netdev: The network device being opened.
+ *
+ * Read all of header information before reading pkt content.
+ * It is not allowed only port of pkts in QMU after issuing
+ * interrupt ack.
+ */
+static void ks_rcv(struct ks_net *ks, struct net_device *netdev)
+{
+       u32     i;
+       struct type_frame_head *frame_hdr = ks->frame_head_info;
+       struct sk_buff *skb;
+
+       ks->frame_cnt = ks_rdreg16(ks, KS_RXFCTR) >> 8;
+
+       /* read all header information */
+       for (i = 0; i < ks->frame_cnt; i++) {
+               /* Checking Received packet status */
+               frame_hdr->sts = ks_rdreg16(ks, KS_RXFHSR);
+               /* Get packet len from hardware */
+               frame_hdr->len = ks_rdreg16(ks, KS_RXFHBCR);
+               frame_hdr++;
+       }
+
+       frame_hdr = ks->frame_head_info;
+       while (ks->frame_cnt--) {
+               skb = dev_alloc_skb(frame_hdr->len + 16);
+               if (likely(skb && (frame_hdr->sts & RXFSHR_RXFV) &&
+                       (frame_hdr->len < RX_BUF_SIZE) && frame_hdr->len)) {
+                       skb_reserve(skb, 2);
+                       /* read data block including CRC 4 bytes */
+                       ks_read_qmu(ks, (u16 *)skb->data, frame_hdr->len);
+                       skb_put(skb, frame_hdr->len);
+                       skb->protocol = eth_type_trans(skb, netdev);
+                       netif_rx(skb);
+               } else {
+                       pr_err("%s: err:skb alloc\n", __func__);
+                       ks_wrreg16(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF));
+                       if (skb)
+                               dev_kfree_skb_irq(skb);
+               }
+               frame_hdr++;
+       }
+}
+
+/**
+ * ks_update_link_status - link status update.
+ * @netdev: The network device being opened.
+ * @ks: The chip information
+ *
+ */
+
+static void ks_update_link_status(struct net_device *netdev, struct ks_net *ks)
+{
+       /* check the status of the link */
+       u32 link_up_status;
+       if (ks_rdreg16(ks, KS_P1SR) & P1SR_LINK_GOOD) {
+               netif_carrier_on(netdev);
+               link_up_status = true;
+       } else {
+               netif_carrier_off(netdev);
+               link_up_status = false;
+       }
+       netif_dbg(ks, link, ks->netdev,
+                 "%s: %s\n", __func__, link_up_status ? "UP" : "DOWN");
+}
+
+/**
+ * ks_irq - device interrupt handler
+ * @irq: Interrupt number passed from the IRQ hnalder.
+ * @pw: The private word passed to register_irq(), our struct ks_net.
+ *
+ * This is the handler invoked to find out what happened
+ *
+ * Read the interrupt status, work out what needs to be done and then clear
+ * any of the interrupts that are not needed.
+ */
+
+static irqreturn_t ks_irq(int irq, void *pw)
+{
+       struct net_device *netdev = pw;
+       struct ks_net *ks = netdev_priv(netdev);
+       u16 status;
+
+       /*this should be the first in IRQ handler */
+       ks_save_cmd_reg(ks);
+
+       status = ks_rdreg16(ks, KS_ISR);
+       if (unlikely(!status)) {
+               ks_restore_cmd_reg(ks);
+               return IRQ_NONE;
+       }
+
+       ks_wrreg16(ks, KS_ISR, status);
+
+       if (likely(status & IRQ_RXI))
+               ks_rcv(ks, netdev);
+
+       if (unlikely(status & IRQ_LCI))
+               ks_update_link_status(netdev, ks);
+
+       if (unlikely(status & IRQ_TXI))
+               netif_wake_queue(netdev);
+
+       if (unlikely(status & IRQ_LDI)) {
+
+               u16 pmecr = ks_rdreg16(ks, KS_PMECR);
+               pmecr &= ~PMECR_WKEVT_MASK;
+               ks_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
+       }
+
+       /* this should be the last in IRQ handler*/
+       ks_restore_cmd_reg(ks);
+       return IRQ_HANDLED;
+}
+
+
+/**
+ * ks_net_open - open network device
+ * @netdev: The network device being opened.
+ *
+ * Called when the network device is marked active, such as a user executing
+ * 'ifconfig up' on the device.
+ */
+static int ks_net_open(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       int err;
+
+#define        KS_INT_FLAGS    (IRQF_DISABLED|IRQF_TRIGGER_LOW)
+       /* lock the card, even if we may not actually do anything
+        * else at the moment.
+        */
+
+       netif_dbg(ks, ifup, ks->netdev, "%s - entry\n", __func__);
+
+       /* reset the HW */
+       err = request_irq(ks->irq, ks_irq, KS_INT_FLAGS, DRV_NAME, netdev);
+
+       if (err) {
+               pr_err("Failed to request IRQ: %d: %d\n", ks->irq, err);
+               return err;
+       }
+
+       /* wake up powermode to normal mode */
+       ks_set_powermode(ks, PMECR_PM_NORMAL);
+       mdelay(1);      /* wait for normal mode to take effect */
+
+       ks_wrreg16(ks, KS_ISR, 0xffff);
+       ks_enable_int(ks);
+       ks_enable_qmu(ks);
+       netif_start_queue(ks->netdev);
+
+       netif_dbg(ks, ifup, ks->netdev, "network device up\n");
+
+       return 0;
+}
+
+/**
+ * ks_net_stop - close network device
+ * @netdev: The device being closed.
+ *
+ * Called to close down a network device which has been active. Cancell any
+ * work, shutdown the RX and TX process and then place the chip into a low
+ * power state whilst it is not being used.
+ */
+static int ks_net_stop(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+
+       netif_info(ks, ifdown, netdev, "shutting down\n");
+
+       netif_stop_queue(netdev);
+
+       mutex_lock(&ks->lock);
+
+       /* turn off the IRQs and ack any outstanding */
+       ks_wrreg16(ks, KS_IER, 0x0000);
+       ks_wrreg16(ks, KS_ISR, 0xffff);
+
+       /* shutdown RX/TX QMU */
+       ks_disable_qmu(ks);
+
+       /* set powermode to soft power down to save power */
+       ks_set_powermode(ks, PMECR_PM_SOFTDOWN);
+       free_irq(ks->irq, netdev);
+       mutex_unlock(&ks->lock);
+       return 0;
+}
+
+
+/**
+ * ks_write_qmu - write 1 pkt data to the QMU.
+ * @ks: The chip information
+ * @pdata: buffer address to save 1 pkt
+ * @len: Pkt length in byte
+ * Here is the sequence to write 1 pkt:
+ *     1. set sudo DMA mode
+ *     2. write status/length
+ *     3. write pkt data
+ *     4. reset sudo DMA Mode
+ *     5. reset sudo DMA mode
+ *     6. Wait until pkt is out
+ */
+static void ks_write_qmu(struct ks_net *ks, u8 *pdata, u16 len)
+{
+       /* start header at txb[0] to align txw entries */
+       ks->txh.txw[0] = 0;
+       ks->txh.txw[1] = cpu_to_le16(len);
+
+       /* 1. set sudo-DMA mode */
+       ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
+       /* 2. write status/lenth info */
+       ks_outblk(ks, ks->txh.txw, 4);
+       /* 3. write pkt data */
+       ks_outblk(ks, (u16 *)pdata, ALIGN(len, 4));
+       /* 4. reset sudo-DMA mode */
+       ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr);
+       /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */
+       ks_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
+       /* 6. wait until TXQCR_METFE is auto-cleared */
+       while (ks_rdreg16(ks, KS_TXQCR) & TXQCR_METFE)
+               ;
+}
+
+/**
+ * ks_start_xmit - transmit packet
+ * @skb                : The buffer to transmit
+ * @netdev     : The device used to transmit the packet.
+ *
+ * Called by the network layer to transmit the @skb.
+ * spin_lock_irqsave is required because tx and rx should be mutual exclusive.
+ * So while tx is in-progress, prevent IRQ interrupt from happenning.
+ */
+static int ks_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+       int retv = NETDEV_TX_OK;
+       struct ks_net *ks = netdev_priv(netdev);
+
+       disable_irq(netdev->irq);
+       ks_disable_int(ks);
+       spin_lock(&ks->statelock);
+
+       /* Extra space are required:
+       *  4 byte for alignment, 4 for status/length, 4 for CRC
+       */
+
+       if (likely(ks_tx_fifo_space(ks) >= skb->len + 12)) {
+               ks_write_qmu(ks, skb->data, skb->len);
+               dev_kfree_skb(skb);
+       } else
+               retv = NETDEV_TX_BUSY;
+       spin_unlock(&ks->statelock);
+       ks_enable_int(ks);
+       enable_irq(netdev->irq);
+       return retv;
+}
+
+/**
+ * ks_start_rx - ready to serve pkts
+ * @ks         : The chip information
+ *
+ */
+static void ks_start_rx(struct ks_net *ks)
+{
+       u16 cntl;
+
+       /* Enables QMU Receive (RXCR1). */
+       cntl = ks_rdreg16(ks, KS_RXCR1);
+       cntl |= RXCR1_RXE ;
+       ks_wrreg16(ks, KS_RXCR1, cntl);
+}  /* ks_start_rx */
+
+/**
+ * ks_stop_rx - stop to serve pkts
+ * @ks         : The chip information
+ *
+ */
+static void ks_stop_rx(struct ks_net *ks)
+{
+       u16 cntl;
+
+       /* Disables QMU Receive (RXCR1). */
+       cntl = ks_rdreg16(ks, KS_RXCR1);
+       cntl &= ~RXCR1_RXE ;
+       ks_wrreg16(ks, KS_RXCR1, cntl);
+
+}  /* ks_stop_rx */
+
+static unsigned long const ethernet_polynomial = 0x04c11db7U;
+
+static unsigned long ether_gen_crc(int length, u8 *data)
+{
+       long crc = -1;
+       while (--length >= 0) {
+               u8 current_octet = *data++;
+               int bit;
+
+               for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
+                       crc = (crc << 1) ^
+                               ((crc < 0) ^ (current_octet & 1) ?
+                       ethernet_polynomial : 0);
+               }
+       }
+       return (unsigned long)crc;
+}  /* ether_gen_crc */
+
+/**
+* ks_set_grpaddr - set multicast information
+* @ks : The chip information
+*/
+
+static void ks_set_grpaddr(struct ks_net *ks)
+{
+       u8      i;
+       u32     index, position, value;
+
+       memset(ks->mcast_bits, 0, sizeof(u8) * HW_MCAST_SIZE);
+
+       for (i = 0; i < ks->mcast_lst_size; i++) {
+               position = (ether_gen_crc(6, ks->mcast_lst[i]) >> 26) & 0x3f;
+               index = position >> 3;
+               value = 1 << (position & 7);
+               ks->mcast_bits[index] |= (u8)value;
+       }
+
+       for (i  = 0; i < HW_MCAST_SIZE; i++) {
+               if (i & 1) {
+                       ks_wrreg16(ks, (u16)((KS_MAHTR0 + i) & ~1),
+                               (ks->mcast_bits[i] << 8) |
+                               ks->mcast_bits[i - 1]);
+               }
+       }
+}  /* ks_set_grpaddr */
+
+/*
+* ks_clear_mcast - clear multicast information
+*
+* @ks : The chip information
+* This routine removes all mcast addresses set in the hardware.
+*/
+
+static void ks_clear_mcast(struct ks_net *ks)
+{
+       u16     i, mcast_size;
+       for (i = 0; i < HW_MCAST_SIZE; i++)
+               ks->mcast_bits[i] = 0;
+
+       mcast_size = HW_MCAST_SIZE >> 2;
+       for (i = 0; i < mcast_size; i++)
+               ks_wrreg16(ks, KS_MAHTR0 + (2*i), 0);
+}
+
+static void ks_set_promis(struct ks_net *ks, u16 promiscuous_mode)
+{
+       u16             cntl;
+       ks->promiscuous = promiscuous_mode;
+       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
+       cntl = ks_rdreg16(ks, KS_RXCR1);
+
+       cntl &= ~RXCR1_FILTER_MASK;
+       if (promiscuous_mode)
+               /* Enable Promiscuous mode */
+               cntl |= RXCR1_RXAE | RXCR1_RXINVF;
+       else
+               /* Disable Promiscuous mode (default normal mode) */
+               cntl |= RXCR1_RXPAFMA;
+
+       ks_wrreg16(ks, KS_RXCR1, cntl);
+
+       if (ks->enabled)
+               ks_start_rx(ks);
+
+}  /* ks_set_promis */
+
+static void ks_set_mcast(struct ks_net *ks, u16 mcast)
+{
+       u16     cntl;
+
+       ks->all_mcast = mcast;
+       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
+       cntl = ks_rdreg16(ks, KS_RXCR1);
+       cntl &= ~RXCR1_FILTER_MASK;
+       if (mcast)
+               /* Enable "Perfect with Multicast address passed mode" */
+               cntl |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA);
+       else
+               /**
+                * Disable "Perfect with Multicast address passed
+                * mode" (normal mode).
+                */
+               cntl |= RXCR1_RXPAFMA;
+
+       ks_wrreg16(ks, KS_RXCR1, cntl);
+
+       if (ks->enabled)
+               ks_start_rx(ks);
+}  /* ks_set_mcast */
+
+static void ks_set_rx_mode(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       struct netdev_hw_addr *ha;
+
+       /* Turn on/off promiscuous mode. */
+       if ((netdev->flags & IFF_PROMISC) == IFF_PROMISC)
+               ks_set_promis(ks,
+                       (u16)((netdev->flags & IFF_PROMISC) == IFF_PROMISC));
+       /* Turn on/off all mcast mode. */
+       else if ((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI)
+               ks_set_mcast(ks,
+                       (u16)((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI));
+       else
+               ks_set_promis(ks, false);
+
+       if ((netdev->flags & IFF_MULTICAST) && netdev_mc_count(netdev)) {
+               if (netdev_mc_count(netdev) <= MAX_MCAST_LST) {
+                       int i = 0;
+
+                       netdev_for_each_mc_addr(ha, netdev) {
+                               if (i >= MAX_MCAST_LST)
+                                       break;
+                               memcpy(ks->mcast_lst[i++], ha->addr, ETH_ALEN);
+                       }
+                       ks->mcast_lst_size = (u8)i;
+                       ks_set_grpaddr(ks);
+               } else {
+                       /**
+                        * List too big to support so
+                        * turn on all mcast mode.
+                        */
+                       ks->mcast_lst_size = MAX_MCAST_LST;
+                       ks_set_mcast(ks, true);
+               }
+       } else {
+               ks->mcast_lst_size = 0;
+               ks_clear_mcast(ks);
+       }
+} /* ks_set_rx_mode */
+
+static void ks_set_mac(struct ks_net *ks, u8 *data)
+{
+       u16 *pw = (u16 *)data;
+       u16 w, u;
+
+       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
+
+       u = *pw++;
+       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
+       ks_wrreg16(ks, KS_MARH, w);
+
+       u = *pw++;
+       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
+       ks_wrreg16(ks, KS_MARM, w);
+
+       u = *pw;
+       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
+       ks_wrreg16(ks, KS_MARL, w);
+
+       memcpy(ks->mac_addr, data, 6);
+
+       if (ks->enabled)
+               ks_start_rx(ks);
+}
+
+static int ks_set_mac_address(struct net_device *netdev, void *paddr)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       struct sockaddr *addr = paddr;
+       u8 *da;
+
+       memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+
+       da = (u8 *)netdev->dev_addr;
+
+       ks_set_mac(ks, da);
+       return 0;
+}
+
+static int ks_net_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+
+       if (!netif_running(netdev))
+               return -EINVAL;
+
+       return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
+}
+
+static const struct net_device_ops ks_netdev_ops = {
+       .ndo_open               = ks_net_open,
+       .ndo_stop               = ks_net_stop,
+       .ndo_do_ioctl           = ks_net_ioctl,
+       .ndo_start_xmit         = ks_start_xmit,
+       .ndo_set_mac_address    = ks_set_mac_address,
+       .ndo_set_rx_mode        = ks_set_rx_mode,
+       .ndo_change_mtu         = eth_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+/* ethtool support */
+
+static void ks_get_drvinfo(struct net_device *netdev,
+                              struct ethtool_drvinfo *di)
+{
+       strlcpy(di->driver, DRV_NAME, sizeof(di->driver));
+       strlcpy(di->version, "1.00", sizeof(di->version));
+       strlcpy(di->bus_info, dev_name(netdev->dev.parent),
+               sizeof(di->bus_info));
+}
+
+static u32 ks_get_msglevel(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       return ks->msg_enable;
+}
+
+static void ks_set_msglevel(struct net_device *netdev, u32 to)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       ks->msg_enable = to;
+}
+
+static int ks_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       return mii_ethtool_gset(&ks->mii, cmd);
+}
+
+static int ks_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       return mii_ethtool_sset(&ks->mii, cmd);
+}
+
+static u32 ks_get_link(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       return mii_link_ok(&ks->mii);
+}
+
+static int ks_nway_reset(struct net_device *netdev)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       return mii_nway_restart(&ks->mii);
+}
+
+static const struct ethtool_ops ks_ethtool_ops = {
+       .get_drvinfo    = ks_get_drvinfo,
+       .get_msglevel   = ks_get_msglevel,
+       .set_msglevel   = ks_set_msglevel,
+       .get_settings   = ks_get_settings,
+       .set_settings   = ks_set_settings,
+       .get_link       = ks_get_link,
+       .nway_reset     = ks_nway_reset,
+};
+
+/* MII interface controls */
+
+/**
+ * ks_phy_reg - convert MII register into a KS8851 register
+ * @reg: MII register number.
+ *
+ * Return the KS8851 register number for the corresponding MII PHY register
+ * if possible. Return zero if the MII register has no direct mapping to the
+ * KS8851 register set.
+ */
+static int ks_phy_reg(int reg)
+{
+       switch (reg) {
+       case MII_BMCR:
+               return KS_P1MBCR;
+       case MII_BMSR:
+               return KS_P1MBSR;
+       case MII_PHYSID1:
+               return KS_PHY1ILR;
+       case MII_PHYSID2:
+               return KS_PHY1IHR;
+       case MII_ADVERTISE:
+               return KS_P1ANAR;
+       case MII_LPA:
+               return KS_P1ANLPR;
+       }
+
+       return 0x0;
+}
+
+/**
+ * ks_phy_read - MII interface PHY register read.
+ * @netdev: The network device the PHY is on.
+ * @phy_addr: Address of PHY (ignored as we only have one)
+ * @reg: The register to read.
+ *
+ * This call reads data from the PHY register specified in @reg. Since the
+ * device does not support all the MII registers, the non-existent values
+ * are always returned as zero.
+ *
+ * We return zero for unsupported registers as the MII code does not check
+ * the value returned for any error status, and simply returns it to the
+ * caller. The mii-tool that the driver was tested with takes any -ve error
+ * as real PHY capabilities, thus displaying incorrect data to the user.
+ */
+static int ks_phy_read(struct net_device *netdev, int phy_addr, int reg)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       int ksreg;
+       int result;
+
+       ksreg = ks_phy_reg(reg);
+       if (!ksreg)
+               return 0x0;     /* no error return allowed, so use zero */
+
+       mutex_lock(&ks->lock);
+       result = ks_rdreg16(ks, ksreg);
+       mutex_unlock(&ks->lock);
+
+       return result;
+}
+
+static void ks_phy_write(struct net_device *netdev,
+                            int phy, int reg, int value)
+{
+       struct ks_net *ks = netdev_priv(netdev);
+       int ksreg;
+
+       ksreg = ks_phy_reg(reg);
+       if (ksreg) {
+               mutex_lock(&ks->lock);
+               ks_wrreg16(ks, ksreg, value);
+               mutex_unlock(&ks->lock);
+       }
+}
+
+/**
+ * ks_read_selftest - read the selftest memory info.
+ * @ks: The device state
+ *
+ * Read and check the TX/RX memory selftest information.
+ */
+static int ks_read_selftest(struct ks_net *ks)
+{
+       unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
+       int ret = 0;
+       unsigned rd;
+
+       rd = ks_rdreg16(ks, KS_MBIR);
+
+       if ((rd & both_done) != both_done) {
+               netdev_warn(ks->netdev, "Memory selftest not finished\n");
+               return 0;
+       }
+
+       if (rd & MBIR_TXMBFA) {
+               netdev_err(ks->netdev, "TX memory selftest fails\n");
+               ret |= 1;
+       }
+
+       if (rd & MBIR_RXMBFA) {
+               netdev_err(ks->netdev, "RX memory selftest fails\n");
+               ret |= 2;
+       }
+
+       netdev_info(ks->netdev, "the selftest passes\n");
+       return ret;
+}
+
+static void ks_setup(struct ks_net *ks)
+{
+       u16     w;
+
+       /**
+        * Configure QMU Transmit
+        */
+
+       /* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */
+       ks_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
+
+       /* Setup Receive Frame Data Pointer Auto-Increment */
+       ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
+
+       /* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */
+       ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK);
+
+       /* Setup RxQ Command Control (RXQCR) */
+       ks->rc_rxqcr = RXQCR_CMD_CNTL;
+       ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
+
+       /**
+        * set the force mode to half duplex, default is full duplex
+        *  because if the auto-negotiation fails, most switch uses
+        *  half-duplex.
+        */
+
+       w = ks_rdreg16(ks, KS_P1MBCR);
+       w &= ~P1MBCR_FORCE_FDX;
+       ks_wrreg16(ks, KS_P1MBCR, w);
+
+       w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP;
+       ks_wrreg16(ks, KS_TXCR, w);
+
+       w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC;
+
+       if (ks->promiscuous)         /* bPromiscuous */
+               w |= (RXCR1_RXAE | RXCR1_RXINVF);
+       else if (ks->all_mcast) /* Multicast address passed mode */
+               w |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA);
+       else                                   /* Normal mode */
+               w |= RXCR1_RXPAFMA;
+
+       ks_wrreg16(ks, KS_RXCR1, w);
+}  /*ks_setup */
+
+
+static void ks_setup_int(struct ks_net *ks)
+{
+       ks->rc_ier = 0x00;
+       /* Clear the interrupts status of the hardware. */
+       ks_wrreg16(ks, KS_ISR, 0xffff);
+
+       /* Enables the interrupts of the hardware. */
+       ks->rc_ier = (IRQ_LCI | IRQ_TXI | IRQ_RXI);
+}  /* ks_setup_int */
+
+static int ks_hw_init(struct ks_net *ks)
+{
+#define        MHEADER_SIZE    (sizeof(struct type_frame_head) * MAX_RECV_FRAMES)
+       ks->promiscuous = 0;
+       ks->all_mcast = 0;
+       ks->mcast_lst_size = 0;
+
+       ks->frame_head_info = (struct type_frame_head *) \
+               kmalloc(MHEADER_SIZE, GFP_KERNEL);
+       if (!ks->frame_head_info) {
+               pr_err("Error: Fail to allocate frame memory\n");
+               return false;
+       }
+
+       ks_set_mac(ks, KS_DEFAULT_MAC_ADDRESS);
+       return true;
+}
+
+
+static int __devinit ks8851_probe(struct platform_device *pdev)
+{
+       int err = -ENOMEM;
+       struct resource *io_d, *io_c;
+       struct net_device *netdev;
+       struct ks_net *ks;
+       u16 id, data;
+
+       io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+       if (!request_mem_region(io_d->start, resource_size(io_d), DRV_NAME))
+               goto err_mem_region;
+
+       if (!request_mem_region(io_c->start, resource_size(io_c), DRV_NAME))
+               goto err_mem_region1;
+
+       netdev = alloc_etherdev(sizeof(struct ks_net));
+       if (!netdev)
+               goto err_alloc_etherdev;
+
+       SET_NETDEV_DEV(netdev, &pdev->dev);
+
+       ks = netdev_priv(netdev);
+       ks->netdev = netdev;
+       ks->hw_addr = ioremap(io_d->start, resource_size(io_d));
+
+       if (!ks->hw_addr)
+               goto err_ioremap;
+
+       ks->hw_addr_cmd = ioremap(io_c->start, resource_size(io_c));
+       if (!ks->hw_addr_cmd)
+               goto err_ioremap1;
+
+       ks->irq = platform_get_irq(pdev, 0);
+
+       if (ks->irq < 0) {
+               err = ks->irq;
+               goto err_get_irq;
+       }
+
+       ks->pdev = pdev;
+
+       mutex_init(&ks->lock);
+       spin_lock_init(&ks->statelock);
+
+       netdev->netdev_ops = &ks_netdev_ops;
+       netdev->ethtool_ops = &ks_ethtool_ops;
+
+       /* setup mii state */
+       ks->mii.dev             = netdev;
+       ks->mii.phy_id          = 1,
+       ks->mii.phy_id_mask     = 1;
+       ks->mii.reg_num_mask    = 0xf;
+       ks->mii.mdio_read       = ks_phy_read;
+       ks->mii.mdio_write      = ks_phy_write;
+
+       netdev_info(netdev, "message enable is %d\n", msg_enable);
+       /* set the default message enable */
+       ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
+                                                    NETIF_MSG_PROBE |
+                                                    NETIF_MSG_LINK));
+       ks_read_config(ks);
+
+       /* simple check for a valid chip being connected to the bus */
+       if ((ks_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
+               netdev_err(netdev, "failed to read device ID\n");
+               err = -ENODEV;
+               goto err_register;
+       }
+
+       if (ks_read_selftest(ks)) {
+               netdev_err(netdev, "failed to read device ID\n");
+               err = -ENODEV;
+               goto err_register;
+       }
+
+       err = register_netdev(netdev);
+       if (err)
+               goto err_register;
+
+       platform_set_drvdata(pdev, netdev);
+
+       ks_soft_reset(ks, GRR_GSR);
+       ks_hw_init(ks);
+       ks_disable_qmu(ks);
+       ks_setup(ks);
+       ks_setup_int(ks);
+       memcpy(netdev->dev_addr, ks->mac_addr, 6);
+
+       data = ks_rdreg16(ks, KS_OBCR);
+       ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA);
+
+       /**
+        * If you want to use the default MAC addr,
+        * comment out the 2 functions below.
+        */
+
+       random_ether_addr(netdev->dev_addr);
+       ks_set_mac(ks, netdev->dev_addr);
+
+       id = ks_rdreg16(ks, KS_CIDER);
+
+       netdev_info(netdev, "Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n",
+                   (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7);
+       return 0;
+
+err_register:
+err_get_irq:
+       iounmap(ks->hw_addr_cmd);
+err_ioremap1:
+       iounmap(ks->hw_addr);
+err_ioremap:
+       free_netdev(netdev);
+err_alloc_etherdev:
+       release_mem_region(io_c->start, resource_size(io_c));
+err_mem_region1:
+       release_mem_region(io_d->start, resource_size(io_d));
+err_mem_region:
+       return err;
+}
+
+static int __devexit ks8851_remove(struct platform_device *pdev)
+{
+       struct net_device *netdev = platform_get_drvdata(pdev);
+       struct ks_net *ks = netdev_priv(netdev);
+       struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       kfree(ks->frame_head_info);
+       unregister_netdev(netdev);
+       iounmap(ks->hw_addr);
+       free_netdev(netdev);
+       release_mem_region(iomem->start, resource_size(iomem));
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+
+}
+
+static struct platform_driver ks8851_platform_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = ks8851_probe,
+       .remove = __devexit_p(ks8851_remove),
+};
+
+static int __init ks8851_init(void)
+{
+       return platform_driver_register(&ks8851_platform_driver);
+}
+
+static void __exit ks8851_exit(void)
+{
+       platform_driver_unregister(&ks8851_platform_driver);
+}
+
+module_init(ks8851_init);
+module_exit(ks8851_exit);
+
+MODULE_DESCRIPTION("KS8851 MLL Network driver");
+MODULE_AUTHOR("David Choi <david.choi@micrel.com>");
+MODULE_LICENSE("GPL");
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
+
diff --git a/drivers/net/ethernet/micrel/ksz884x.c b/drivers/net/ethernet/micrel/ksz884x.c
new file mode 100644 (file)
index 0000000..27418d3
--- /dev/null
@@ -0,0 +1,7289 @@
+/**
+ * drivers/net/ksx884x.c - Micrel KSZ8841/2 PCI Ethernet driver
+ *
+ * Copyright (c) 2009-2010 Micrel, Inc.
+ *     Tristram Ha <Tristram.Ha@micrel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/mii.h>
+#include <linux/platform_device.h>
+#include <linux/ethtool.h>
+#include <linux/etherdevice.h>
+#include <linux/in.h>
+#include <linux/ip.h>
+#include <linux/if_vlan.h>
+#include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+
+/* DMA Registers */
+
+#define KS_DMA_TX_CTRL                 0x0000
+#define DMA_TX_ENABLE                  0x00000001
+#define DMA_TX_CRC_ENABLE              0x00000002
+#define DMA_TX_PAD_ENABLE              0x00000004
+#define DMA_TX_LOOPBACK                        0x00000100
+#define DMA_TX_FLOW_ENABLE             0x00000200
+#define DMA_TX_CSUM_IP                 0x00010000
+#define DMA_TX_CSUM_TCP                        0x00020000
+#define DMA_TX_CSUM_UDP                        0x00040000
+#define DMA_TX_BURST_SIZE              0x3F000000
+
+#define KS_DMA_RX_CTRL                 0x0004
+#define DMA_RX_ENABLE                  0x00000001
+#define KS884X_DMA_RX_MULTICAST                0x00000002
+#define DMA_RX_PROMISCUOUS             0x00000004
+#define DMA_RX_ERROR                   0x00000008
+#define DMA_RX_UNICAST                 0x00000010
+#define DMA_RX_ALL_MULTICAST           0x00000020
+#define DMA_RX_BROADCAST               0x00000040
+#define DMA_RX_FLOW_ENABLE             0x00000200
+#define DMA_RX_CSUM_IP                 0x00010000
+#define DMA_RX_CSUM_TCP                        0x00020000
+#define DMA_RX_CSUM_UDP                        0x00040000
+#define DMA_RX_BURST_SIZE              0x3F000000
+
+#define DMA_BURST_SHIFT                        24
+#define DMA_BURST_DEFAULT              8
+
+#define KS_DMA_TX_START                        0x0008
+#define KS_DMA_RX_START                        0x000C
+#define DMA_START                      0x00000001
+
+#define KS_DMA_TX_ADDR                 0x0010
+#define KS_DMA_RX_ADDR                 0x0014
+
+#define DMA_ADDR_LIST_MASK             0xFFFFFFFC
+#define DMA_ADDR_LIST_SHIFT            2
+
+/* MTR0 */
+#define KS884X_MULTICAST_0_OFFSET      0x0020
+#define KS884X_MULTICAST_1_OFFSET      0x0021
+#define KS884X_MULTICAST_2_OFFSET      0x0022
+#define KS884x_MULTICAST_3_OFFSET      0x0023
+/* MTR1 */
+#define KS884X_MULTICAST_4_OFFSET      0x0024
+#define KS884X_MULTICAST_5_OFFSET      0x0025
+#define KS884X_MULTICAST_6_OFFSET      0x0026
+#define KS884X_MULTICAST_7_OFFSET      0x0027
+
+/* Interrupt Registers */
+
+/* INTEN */
+#define KS884X_INTERRUPTS_ENABLE       0x0028
+/* INTST */
+#define KS884X_INTERRUPTS_STATUS       0x002C
+
+#define KS884X_INT_RX_STOPPED          0x02000000
+#define KS884X_INT_TX_STOPPED          0x04000000
+#define KS884X_INT_RX_OVERRUN          0x08000000
+#define KS884X_INT_TX_EMPTY            0x10000000
+#define KS884X_INT_RX                  0x20000000
+#define KS884X_INT_TX                  0x40000000
+#define KS884X_INT_PHY                 0x80000000
+
+#define KS884X_INT_RX_MASK             \
+       (KS884X_INT_RX | KS884X_INT_RX_OVERRUN)
+#define KS884X_INT_TX_MASK             \
+       (KS884X_INT_TX | KS884X_INT_TX_EMPTY)
+#define KS884X_INT_MASK        (KS884X_INT_RX | KS884X_INT_TX | KS884X_INT_PHY)
+
+/* MAC Additional Station Address */
+
+/* MAAL0 */
+#define KS_ADD_ADDR_0_LO               0x0080
+/* MAAH0 */
+#define KS_ADD_ADDR_0_HI               0x0084
+/* MAAL1 */
+#define KS_ADD_ADDR_1_LO               0x0088
+/* MAAH1 */
+#define KS_ADD_ADDR_1_HI               0x008C
+/* MAAL2 */
+#define KS_ADD_ADDR_2_LO               0x0090
+/* MAAH2 */
+#define KS_ADD_ADDR_2_HI               0x0094
+/* MAAL3 */
+#define KS_ADD_ADDR_3_LO               0x0098
+/* MAAH3 */
+#define KS_ADD_ADDR_3_HI               0x009C
+/* MAAL4 */
+#define KS_ADD_ADDR_4_LO               0x00A0
+/* MAAH4 */
+#define KS_ADD_ADDR_4_HI               0x00A4
+/* MAAL5 */
+#define KS_ADD_ADDR_5_LO               0x00A8
+/* MAAH5 */
+#define KS_ADD_ADDR_5_HI               0x00AC
+/* MAAL6 */
+#define KS_ADD_ADDR_6_LO               0x00B0
+/* MAAH6 */
+#define KS_ADD_ADDR_6_HI               0x00B4
+/* MAAL7 */
+#define KS_ADD_ADDR_7_LO               0x00B8
+/* MAAH7 */
+#define KS_ADD_ADDR_7_HI               0x00BC
+/* MAAL8 */
+#define KS_ADD_ADDR_8_LO               0x00C0
+/* MAAH8 */
+#define KS_ADD_ADDR_8_HI               0x00C4
+/* MAAL9 */
+#define KS_ADD_ADDR_9_LO               0x00C8
+/* MAAH9 */
+#define KS_ADD_ADDR_9_HI               0x00CC
+/* MAAL10 */
+#define KS_ADD_ADDR_A_LO               0x00D0
+/* MAAH10 */
+#define KS_ADD_ADDR_A_HI               0x00D4
+/* MAAL11 */
+#define KS_ADD_ADDR_B_LO               0x00D8
+/* MAAH11 */
+#define KS_ADD_ADDR_B_HI               0x00DC
+/* MAAL12 */
+#define KS_ADD_ADDR_C_LO               0x00E0
+/* MAAH12 */
+#define KS_ADD_ADDR_C_HI               0x00E4
+/* MAAL13 */
+#define KS_ADD_ADDR_D_LO               0x00E8
+/* MAAH13 */
+#define KS_ADD_ADDR_D_HI               0x00EC
+/* MAAL14 */
+#define KS_ADD_ADDR_E_LO               0x00F0
+/* MAAH14 */
+#define KS_ADD_ADDR_E_HI               0x00F4
+/* MAAL15 */
+#define KS_ADD_ADDR_F_LO               0x00F8
+/* MAAH15 */
+#define KS_ADD_ADDR_F_HI               0x00FC
+
+#define ADD_ADDR_HI_MASK               0x0000FFFF
+#define ADD_ADDR_ENABLE                        0x80000000
+#define ADD_ADDR_INCR                  8
+
+/* Miscellaneous Registers */
+
+/* MARL */
+#define KS884X_ADDR_0_OFFSET           0x0200
+#define KS884X_ADDR_1_OFFSET           0x0201
+/* MARM */
+#define KS884X_ADDR_2_OFFSET           0x0202
+#define KS884X_ADDR_3_OFFSET           0x0203
+/* MARH */
+#define KS884X_ADDR_4_OFFSET           0x0204
+#define KS884X_ADDR_5_OFFSET           0x0205
+
+/* OBCR */
+#define KS884X_BUS_CTRL_OFFSET         0x0210
+
+#define BUS_SPEED_125_MHZ              0x0000
+#define BUS_SPEED_62_5_MHZ             0x0001
+#define BUS_SPEED_41_66_MHZ            0x0002
+#define BUS_SPEED_25_MHZ               0x0003
+
+/* EEPCR */
+#define KS884X_EEPROM_CTRL_OFFSET      0x0212
+
+#define EEPROM_CHIP_SELECT             0x0001
+#define EEPROM_SERIAL_CLOCK            0x0002
+#define EEPROM_DATA_OUT                        0x0004
+#define EEPROM_DATA_IN                 0x0008
+#define EEPROM_ACCESS_ENABLE           0x0010
+
+/* MBIR */
+#define KS884X_MEM_INFO_OFFSET         0x0214
+
+#define RX_MEM_TEST_FAILED             0x0008
+#define RX_MEM_TEST_FINISHED           0x0010
+#define TX_MEM_TEST_FAILED             0x0800
+#define TX_MEM_TEST_FINISHED           0x1000
+
+/* GCR */
+#define KS884X_GLOBAL_CTRL_OFFSET      0x0216
+#define GLOBAL_SOFTWARE_RESET          0x0001
+
+#define KS8841_POWER_MANAGE_OFFSET     0x0218
+
+/* WFCR */
+#define KS8841_WOL_CTRL_OFFSET         0x021A
+#define KS8841_WOL_MAGIC_ENABLE                0x0080
+#define KS8841_WOL_FRAME3_ENABLE       0x0008
+#define KS8841_WOL_FRAME2_ENABLE       0x0004
+#define KS8841_WOL_FRAME1_ENABLE       0x0002
+#define KS8841_WOL_FRAME0_ENABLE       0x0001
+
+/* WF0 */
+#define KS8841_WOL_FRAME_CRC_OFFSET    0x0220
+#define KS8841_WOL_FRAME_BYTE0_OFFSET  0x0224
+#define KS8841_WOL_FRAME_BYTE2_OFFSET  0x0228
+
+/* IACR */
+#define KS884X_IACR_P                  0x04A0
+#define KS884X_IACR_OFFSET             KS884X_IACR_P
+
+/* IADR1 */
+#define KS884X_IADR1_P                 0x04A2
+#define KS884X_IADR2_P                 0x04A4
+#define KS884X_IADR3_P                 0x04A6
+#define KS884X_IADR4_P                 0x04A8
+#define KS884X_IADR5_P                 0x04AA
+
+#define KS884X_ACC_CTRL_SEL_OFFSET     KS884X_IACR_P
+#define KS884X_ACC_CTRL_INDEX_OFFSET   (KS884X_ACC_CTRL_SEL_OFFSET + 1)
+
+#define KS884X_ACC_DATA_0_OFFSET       KS884X_IADR4_P
+#define KS884X_ACC_DATA_1_OFFSET       (KS884X_ACC_DATA_0_OFFSET + 1)
+#define KS884X_ACC_DATA_2_OFFSET       KS884X_IADR5_P
+#define KS884X_ACC_DATA_3_OFFSET       (KS884X_ACC_DATA_2_OFFSET + 1)
+#define KS884X_ACC_DATA_4_OFFSET       KS884X_IADR2_P
+#define KS884X_ACC_DATA_5_OFFSET       (KS884X_ACC_DATA_4_OFFSET + 1)
+#define KS884X_ACC_DATA_6_OFFSET       KS884X_IADR3_P
+#define KS884X_ACC_DATA_7_OFFSET       (KS884X_ACC_DATA_6_OFFSET + 1)
+#define KS884X_ACC_DATA_8_OFFSET       KS884X_IADR1_P
+
+/* P1MBCR */
+#define KS884X_P1MBCR_P                        0x04D0
+#define KS884X_P1MBSR_P                        0x04D2
+#define KS884X_PHY1ILR_P               0x04D4
+#define KS884X_PHY1IHR_P               0x04D6
+#define KS884X_P1ANAR_P                        0x04D8
+#define KS884X_P1ANLPR_P               0x04DA
+
+/* P2MBCR */
+#define KS884X_P2MBCR_P                        0x04E0
+#define KS884X_P2MBSR_P                        0x04E2
+#define KS884X_PHY2ILR_P               0x04E4
+#define KS884X_PHY2IHR_P               0x04E6
+#define KS884X_P2ANAR_P                        0x04E8
+#define KS884X_P2ANLPR_P               0x04EA
+
+#define KS884X_PHY_1_CTRL_OFFSET       KS884X_P1MBCR_P
+#define PHY_CTRL_INTERVAL              (KS884X_P2MBCR_P - KS884X_P1MBCR_P)
+
+#define KS884X_PHY_CTRL_OFFSET         0x00
+
+/* Mode Control Register */
+#define PHY_REG_CTRL                   0
+
+#define PHY_RESET                      0x8000
+#define PHY_LOOPBACK                   0x4000
+#define PHY_SPEED_100MBIT              0x2000
+#define PHY_AUTO_NEG_ENABLE            0x1000
+#define PHY_POWER_DOWN                 0x0800
+#define PHY_MII_DISABLE                        0x0400
+#define PHY_AUTO_NEG_RESTART           0x0200
+#define PHY_FULL_DUPLEX                        0x0100
+#define PHY_COLLISION_TEST             0x0080
+#define PHY_HP_MDIX                    0x0020
+#define PHY_FORCE_MDIX                 0x0010
+#define PHY_AUTO_MDIX_DISABLE          0x0008
+#define PHY_REMOTE_FAULT_DISABLE       0x0004
+#define PHY_TRANSMIT_DISABLE           0x0002
+#define PHY_LED_DISABLE                        0x0001
+
+#define KS884X_PHY_STATUS_OFFSET       0x02
+
+/* Mode Status Register */
+#define PHY_REG_STATUS                 1
+
+#define PHY_100BT4_CAPABLE             0x8000
+#define PHY_100BTX_FD_CAPABLE          0x4000
+#define PHY_100BTX_CAPABLE             0x2000
+#define PHY_10BT_FD_CAPABLE            0x1000
+#define PHY_10BT_CAPABLE               0x0800
+#define PHY_MII_SUPPRESS_CAPABLE       0x0040
+#define PHY_AUTO_NEG_ACKNOWLEDGE       0x0020
+#define PHY_REMOTE_FAULT               0x0010
+#define PHY_AUTO_NEG_CAPABLE           0x0008
+#define PHY_LINK_STATUS                        0x0004
+#define PHY_JABBER_DETECT              0x0002
+#define PHY_EXTENDED_CAPABILITY                0x0001
+
+#define KS884X_PHY_ID_1_OFFSET         0x04
+#define KS884X_PHY_ID_2_OFFSET         0x06
+
+/* PHY Identifier Registers */
+#define PHY_REG_ID_1                   2
+#define PHY_REG_ID_2                   3
+
+#define KS884X_PHY_AUTO_NEG_OFFSET     0x08
+
+/* Auto-Negotiation Advertisement Register */
+#define PHY_REG_AUTO_NEGOTIATION       4
+
+#define PHY_AUTO_NEG_NEXT_PAGE         0x8000
+#define PHY_AUTO_NEG_REMOTE_FAULT      0x2000
+/* Not supported. */
+#define PHY_AUTO_NEG_ASYM_PAUSE                0x0800
+#define PHY_AUTO_NEG_SYM_PAUSE         0x0400
+#define PHY_AUTO_NEG_100BT4            0x0200
+#define PHY_AUTO_NEG_100BTX_FD         0x0100
+#define PHY_AUTO_NEG_100BTX            0x0080
+#define PHY_AUTO_NEG_10BT_FD           0x0040
+#define PHY_AUTO_NEG_10BT              0x0020
+#define PHY_AUTO_NEG_SELECTOR          0x001F
+#define PHY_AUTO_NEG_802_3             0x0001
+
+#define PHY_AUTO_NEG_PAUSE  (PHY_AUTO_NEG_SYM_PAUSE | PHY_AUTO_NEG_ASYM_PAUSE)
+
+#define KS884X_PHY_REMOTE_CAP_OFFSET   0x0A
+
+/* Auto-Negotiation Link Partner Ability Register */
+#define PHY_REG_REMOTE_CAPABILITY      5
+
+#define PHY_REMOTE_NEXT_PAGE           0x8000
+#define PHY_REMOTE_ACKNOWLEDGE         0x4000
+#define PHY_REMOTE_REMOTE_FAULT                0x2000
+#define PHY_REMOTE_SYM_PAUSE           0x0400
+#define PHY_REMOTE_100BTX_FD           0x0100
+#define PHY_REMOTE_100BTX              0x0080
+#define PHY_REMOTE_10BT_FD             0x0040
+#define PHY_REMOTE_10BT                        0x0020
+
+/* P1VCT */
+#define KS884X_P1VCT_P                 0x04F0
+#define KS884X_P1PHYCTRL_P             0x04F2
+
+/* P2VCT */
+#define KS884X_P2VCT_P                 0x04F4
+#define KS884X_P2PHYCTRL_P             0x04F6
+
+#define KS884X_PHY_SPECIAL_OFFSET      KS884X_P1VCT_P
+#define PHY_SPECIAL_INTERVAL           (KS884X_P2VCT_P - KS884X_P1VCT_P)
+
+#define KS884X_PHY_LINK_MD_OFFSET      0x00
+
+#define PHY_START_CABLE_DIAG           0x8000
+#define PHY_CABLE_DIAG_RESULT          0x6000
+#define PHY_CABLE_STAT_NORMAL          0x0000
+#define PHY_CABLE_STAT_OPEN            0x2000
+#define PHY_CABLE_STAT_SHORT           0x4000
+#define PHY_CABLE_STAT_FAILED          0x6000
+#define PHY_CABLE_10M_SHORT            0x1000
+#define PHY_CABLE_FAULT_COUNTER                0x01FF
+
+#define KS884X_PHY_PHY_CTRL_OFFSET     0x02
+
+#define PHY_STAT_REVERSED_POLARITY     0x0020
+#define PHY_STAT_MDIX                  0x0010
+#define PHY_FORCE_LINK                 0x0008
+#define PHY_POWER_SAVING_DISABLE       0x0004
+#define PHY_REMOTE_LOOPBACK            0x0002
+
+/* SIDER */
+#define KS884X_SIDER_P                 0x0400
+#define KS884X_CHIP_ID_OFFSET          KS884X_SIDER_P
+#define KS884X_FAMILY_ID_OFFSET                (KS884X_CHIP_ID_OFFSET + 1)
+
+#define REG_FAMILY_ID                  0x88
+
+#define REG_CHIP_ID_41                 0x8810
+#define REG_CHIP_ID_42                 0x8800
+
+#define KS884X_CHIP_ID_MASK_41         0xFF10
+#define KS884X_CHIP_ID_MASK            0xFFF0
+#define KS884X_CHIP_ID_SHIFT           4
+#define KS884X_REVISION_MASK           0x000E
+#define KS884X_REVISION_SHIFT          1
+#define KS8842_START                   0x0001
+
+#define CHIP_IP_41_M                   0x8810
+#define CHIP_IP_42_M                   0x8800
+#define CHIP_IP_61_M                   0x8890
+#define CHIP_IP_62_M                   0x8880
+
+#define CHIP_IP_41_P                   0x8850
+#define CHIP_IP_42_P                   0x8840
+#define CHIP_IP_61_P                   0x88D0
+#define CHIP_IP_62_P                   0x88C0
+
+/* SGCR1 */
+#define KS8842_SGCR1_P                 0x0402
+#define KS8842_SWITCH_CTRL_1_OFFSET    KS8842_SGCR1_P
+
+#define SWITCH_PASS_ALL                        0x8000
+#define SWITCH_TX_FLOW_CTRL            0x2000
+#define SWITCH_RX_FLOW_CTRL            0x1000
+#define SWITCH_CHECK_LENGTH            0x0800
+#define SWITCH_AGING_ENABLE            0x0400
+#define SWITCH_FAST_AGING              0x0200
+#define SWITCH_AGGR_BACKOFF            0x0100
+#define SWITCH_PASS_PAUSE              0x0008
+#define SWITCH_LINK_AUTO_AGING         0x0001
+
+/* SGCR2 */
+#define KS8842_SGCR2_P                 0x0404
+#define KS8842_SWITCH_CTRL_2_OFFSET    KS8842_SGCR2_P
+
+#define SWITCH_VLAN_ENABLE             0x8000
+#define SWITCH_IGMP_SNOOP              0x4000
+#define IPV6_MLD_SNOOP_ENABLE          0x2000
+#define IPV6_MLD_SNOOP_OPTION          0x1000
+#define PRIORITY_SCHEME_SELECT         0x0800
+#define SWITCH_MIRROR_RX_TX            0x0100
+#define UNICAST_VLAN_BOUNDARY          0x0080
+#define MULTICAST_STORM_DISABLE                0x0040
+#define SWITCH_BACK_PRESSURE           0x0020
+#define FAIR_FLOW_CTRL                 0x0010
+#define NO_EXC_COLLISION_DROP          0x0008
+#define SWITCH_HUGE_PACKET             0x0004
+#define SWITCH_LEGAL_PACKET            0x0002
+#define SWITCH_BUF_RESERVE             0x0001
+
+/* SGCR3 */
+#define KS8842_SGCR3_P                 0x0406
+#define KS8842_SWITCH_CTRL_3_OFFSET    KS8842_SGCR3_P
+
+#define BROADCAST_STORM_RATE_LO                0xFF00
+#define SWITCH_REPEATER                        0x0080
+#define SWITCH_HALF_DUPLEX             0x0040
+#define SWITCH_FLOW_CTRL               0x0020
+#define SWITCH_10_MBIT                 0x0010
+#define SWITCH_REPLACE_NULL_VID                0x0008
+#define BROADCAST_STORM_RATE_HI                0x0007
+
+#define BROADCAST_STORM_RATE           0x07FF
+
+/* SGCR4 */
+#define KS8842_SGCR4_P                 0x0408
+
+/* SGCR5 */
+#define KS8842_SGCR5_P                 0x040A
+#define KS8842_SWITCH_CTRL_5_OFFSET    KS8842_SGCR5_P
+
+#define LED_MODE                       0x8200
+#define LED_SPEED_DUPLEX_ACT           0x0000
+#define LED_SPEED_DUPLEX_LINK_ACT      0x8000
+#define LED_DUPLEX_10_100              0x0200
+
+/* SGCR6 */
+#define KS8842_SGCR6_P                 0x0410
+#define KS8842_SWITCH_CTRL_6_OFFSET    KS8842_SGCR6_P
+
+#define KS8842_PRIORITY_MASK           3
+#define KS8842_PRIORITY_SHIFT          2
+
+/* SGCR7 */
+#define KS8842_SGCR7_P                 0x0412
+#define KS8842_SWITCH_CTRL_7_OFFSET    KS8842_SGCR7_P
+
+#define SWITCH_UNK_DEF_PORT_ENABLE     0x0008
+#define SWITCH_UNK_DEF_PORT_3          0x0004
+#define SWITCH_UNK_DEF_PORT_2          0x0002
+#define SWITCH_UNK_DEF_PORT_1          0x0001
+
+/* MACAR1 */
+#define KS8842_MACAR1_P                        0x0470
+#define KS8842_MACAR2_P                        0x0472
+#define KS8842_MACAR3_P                        0x0474
+#define KS8842_MAC_ADDR_1_OFFSET       KS8842_MACAR1_P
+#define KS8842_MAC_ADDR_0_OFFSET       (KS8842_MAC_ADDR_1_OFFSET + 1)
+#define KS8842_MAC_ADDR_3_OFFSET       KS8842_MACAR2_P
+#define KS8842_MAC_ADDR_2_OFFSET       (KS8842_MAC_ADDR_3_OFFSET + 1)
+#define KS8842_MAC_ADDR_5_OFFSET       KS8842_MACAR3_P
+#define KS8842_MAC_ADDR_4_OFFSET       (KS8842_MAC_ADDR_5_OFFSET + 1)
+
+/* TOSR1 */
+#define KS8842_TOSR1_P                 0x0480
+#define KS8842_TOSR2_P                 0x0482
+#define KS8842_TOSR3_P                 0x0484
+#define KS8842_TOSR4_P                 0x0486
+#define KS8842_TOSR5_P                 0x0488
+#define KS8842_TOSR6_P                 0x048A
+#define KS8842_TOSR7_P                 0x0490
+#define KS8842_TOSR8_P                 0x0492
+#define KS8842_TOS_1_OFFSET            KS8842_TOSR1_P
+#define KS8842_TOS_2_OFFSET            KS8842_TOSR2_P
+#define KS8842_TOS_3_OFFSET            KS8842_TOSR3_P
+#define KS8842_TOS_4_OFFSET            KS8842_TOSR4_P
+#define KS8842_TOS_5_OFFSET            KS8842_TOSR5_P
+#define KS8842_TOS_6_OFFSET            KS8842_TOSR6_P
+
+#define KS8842_TOS_7_OFFSET            KS8842_TOSR7_P
+#define KS8842_TOS_8_OFFSET            KS8842_TOSR8_P
+
+/* P1CR1 */
+#define KS8842_P1CR1_P                 0x0500
+#define KS8842_P1CR2_P                 0x0502
+#define KS8842_P1VIDR_P                        0x0504
+#define KS8842_P1CR3_P                 0x0506
+#define KS8842_P1IRCR_P                        0x0508
+#define KS8842_P1ERCR_P                        0x050A
+#define KS884X_P1SCSLMD_P              0x0510
+#define KS884X_P1CR4_P                 0x0512
+#define KS884X_P1SR_P                  0x0514
+
+/* P2CR1 */
+#define KS8842_P2CR1_P                 0x0520
+#define KS8842_P2CR2_P                 0x0522
+#define KS8842_P2VIDR_P                        0x0524
+#define KS8842_P2CR3_P                 0x0526
+#define KS8842_P2IRCR_P                        0x0528
+#define KS8842_P2ERCR_P                        0x052A
+#define KS884X_P2SCSLMD_P              0x0530
+#define KS884X_P2CR4_P                 0x0532
+#define KS884X_P2SR_P                  0x0534
+
+/* P3CR1 */
+#define KS8842_P3CR1_P                 0x0540
+#define KS8842_P3CR2_P                 0x0542
+#define KS8842_P3VIDR_P                        0x0544
+#define KS8842_P3CR3_P                 0x0546
+#define KS8842_P3IRCR_P                        0x0548
+#define KS8842_P3ERCR_P                        0x054A
+
+#define KS8842_PORT_1_CTRL_1           KS8842_P1CR1_P
+#define KS8842_PORT_2_CTRL_1           KS8842_P2CR1_P
+#define KS8842_PORT_3_CTRL_1           KS8842_P3CR1_P
+
+#define PORT_CTRL_ADDR(port, addr)             \
+       (addr = KS8842_PORT_1_CTRL_1 + (port) * \
+               (KS8842_PORT_2_CTRL_1 - KS8842_PORT_1_CTRL_1))
+
+#define KS8842_PORT_CTRL_1_OFFSET      0x00
+
+#define PORT_BROADCAST_STORM           0x0080
+#define PORT_DIFFSERV_ENABLE           0x0040
+#define PORT_802_1P_ENABLE             0x0020
+#define PORT_BASED_PRIORITY_MASK       0x0018
+#define PORT_BASED_PRIORITY_BASE       0x0003
+#define PORT_BASED_PRIORITY_SHIFT      3
+#define PORT_BASED_PRIORITY_0          0x0000
+#define PORT_BASED_PRIORITY_1          0x0008
+#define PORT_BASED_PRIORITY_2          0x0010
+#define PORT_BASED_PRIORITY_3          0x0018
+#define PORT_INSERT_TAG                        0x0004
+#define PORT_REMOVE_TAG                        0x0002
+#define PORT_PRIO_QUEUE_ENABLE         0x0001
+
+#define KS8842_PORT_CTRL_2_OFFSET      0x02
+
+#define PORT_INGRESS_VLAN_FILTER       0x4000
+#define PORT_DISCARD_NON_VID           0x2000
+#define PORT_FORCE_FLOW_CTRL           0x1000
+#define PORT_BACK_PRESSURE             0x0800
+#define PORT_TX_ENABLE                 0x0400
+#define PORT_RX_ENABLE                 0x0200
+#define PORT_LEARN_DISABLE             0x0100
+#define PORT_MIRROR_SNIFFER            0x0080
+#define PORT_MIRROR_RX                 0x0040
+#define PORT_MIRROR_TX                 0x0020
+#define PORT_USER_PRIORITY_CEILING     0x0008
+#define PORT_VLAN_MEMBERSHIP           0x0007
+
+#define KS8842_PORT_CTRL_VID_OFFSET    0x04
+
+#define PORT_DEFAULT_VID               0x0001
+
+#define KS8842_PORT_CTRL_3_OFFSET      0x06
+
+#define PORT_INGRESS_LIMIT_MODE                0x000C
+#define PORT_INGRESS_ALL               0x0000
+#define PORT_INGRESS_UNICAST           0x0004
+#define PORT_INGRESS_MULTICAST         0x0008
+#define PORT_INGRESS_BROADCAST         0x000C
+#define PORT_COUNT_IFG                 0x0002
+#define PORT_COUNT_PREAMBLE            0x0001
+
+#define KS8842_PORT_IN_RATE_OFFSET     0x08
+#define KS8842_PORT_OUT_RATE_OFFSET    0x0A
+
+#define PORT_PRIORITY_RATE             0x0F
+#define PORT_PRIORITY_RATE_SHIFT       4
+
+#define KS884X_PORT_LINK_MD            0x10
+
+#define PORT_CABLE_10M_SHORT           0x8000
+#define PORT_CABLE_DIAG_RESULT         0x6000
+#define PORT_CABLE_STAT_NORMAL         0x0000
+#define PORT_CABLE_STAT_OPEN           0x2000
+#define PORT_CABLE_STAT_SHORT          0x4000
+#define PORT_CABLE_STAT_FAILED         0x6000
+#define PORT_START_CABLE_DIAG          0x1000
+#define PORT_FORCE_LINK                        0x0800
+#define PORT_POWER_SAVING_DISABLE      0x0400
+#define PORT_PHY_REMOTE_LOOPBACK       0x0200
+#define PORT_CABLE_FAULT_COUNTER       0x01FF
+
+#define KS884X_PORT_CTRL_4_OFFSET      0x12
+
+#define PORT_LED_OFF                   0x8000
+#define PORT_TX_DISABLE                        0x4000
+#define PORT_AUTO_NEG_RESTART          0x2000
+#define PORT_REMOTE_FAULT_DISABLE      0x1000
+#define PORT_POWER_DOWN                        0x0800
+#define PORT_AUTO_MDIX_DISABLE         0x0400
+#define PORT_FORCE_MDIX                        0x0200
+#define PORT_LOOPBACK                  0x0100
+#define PORT_AUTO_NEG_ENABLE           0x0080
+#define PORT_FORCE_100_MBIT            0x0040
+#define PORT_FORCE_FULL_DUPLEX         0x0020
+#define PORT_AUTO_NEG_SYM_PAUSE                0x0010
+#define PORT_AUTO_NEG_100BTX_FD                0x0008
+#define PORT_AUTO_NEG_100BTX           0x0004
+#define PORT_AUTO_NEG_10BT_FD          0x0002
+#define PORT_AUTO_NEG_10BT             0x0001
+
+#define KS884X_PORT_STATUS_OFFSET      0x14
+
+#define PORT_HP_MDIX                   0x8000
+#define PORT_REVERSED_POLARITY         0x2000
+#define PORT_RX_FLOW_CTRL              0x0800
+#define PORT_TX_FLOW_CTRL              0x1000
+#define PORT_STATUS_SPEED_100MBIT      0x0400
+#define PORT_STATUS_FULL_DUPLEX                0x0200
+#define PORT_REMOTE_FAULT              0x0100
+#define PORT_MDIX_STATUS               0x0080
+#define PORT_AUTO_NEG_COMPLETE         0x0040
+#define PORT_STATUS_LINK_GOOD          0x0020
+#define PORT_REMOTE_SYM_PAUSE          0x0010
+#define PORT_REMOTE_100BTX_FD          0x0008
+#define PORT_REMOTE_100BTX             0x0004
+#define PORT_REMOTE_10BT_FD            0x0002
+#define PORT_REMOTE_10BT               0x0001
+
+/*
+#define STATIC_MAC_TABLE_ADDR          00-0000FFFF-FFFFFFFF
+#define STATIC_MAC_TABLE_FWD_PORTS     00-00070000-00000000
+#define STATIC_MAC_TABLE_VALID         00-00080000-00000000
+#define STATIC_MAC_TABLE_OVERRIDE      00-00100000-00000000
+#define STATIC_MAC_TABLE_USE_FID       00-00200000-00000000
+#define STATIC_MAC_TABLE_FID           00-03C00000-00000000
+*/
+
+#define STATIC_MAC_TABLE_ADDR          0x0000FFFF
+#define STATIC_MAC_TABLE_FWD_PORTS     0x00070000
+#define STATIC_MAC_TABLE_VALID         0x00080000
+#define STATIC_MAC_TABLE_OVERRIDE      0x00100000
+#define STATIC_MAC_TABLE_USE_FID       0x00200000
+#define STATIC_MAC_TABLE_FID           0x03C00000
+
+#define STATIC_MAC_FWD_PORTS_SHIFT     16
+#define STATIC_MAC_FID_SHIFT           22
+
+/*
+#define VLAN_TABLE_VID                 00-00000000-00000FFF
+#define VLAN_TABLE_FID                 00-00000000-0000F000
+#define VLAN_TABLE_MEMBERSHIP          00-00000000-00070000
+#define VLAN_TABLE_VALID               00-00000000-00080000
+*/
+
+#define VLAN_TABLE_VID                 0x00000FFF
+#define VLAN_TABLE_FID                 0x0000F000
+#define VLAN_TABLE_MEMBERSHIP          0x00070000
+#define VLAN_TABLE_VALID               0x00080000
+
+#define VLAN_TABLE_FID_SHIFT           12
+#define VLAN_TABLE_MEMBERSHIP_SHIFT    16
+
+/*
+#define DYNAMIC_MAC_TABLE_ADDR         00-0000FFFF-FFFFFFFF
+#define DYNAMIC_MAC_TABLE_FID          00-000F0000-00000000
+#define DYNAMIC_MAC_TABLE_SRC_PORT     00-00300000-00000000
+#define DYNAMIC_MAC_TABLE_TIMESTAMP    00-00C00000-00000000
+#define DYNAMIC_MAC_TABLE_ENTRIES      03-FF000000-00000000
+#define DYNAMIC_MAC_TABLE_MAC_EMPTY    04-00000000-00000000
+#define DYNAMIC_MAC_TABLE_RESERVED     78-00000000-00000000
+#define DYNAMIC_MAC_TABLE_NOT_READY    80-00000000-00000000
+*/
+
+#define DYNAMIC_MAC_TABLE_ADDR         0x0000FFFF
+#define DYNAMIC_MAC_TABLE_FID          0x000F0000
+#define DYNAMIC_MAC_TABLE_SRC_PORT     0x00300000
+#define DYNAMIC_MAC_TABLE_TIMESTAMP    0x00C00000
+#define DYNAMIC_MAC_TABLE_ENTRIES      0xFF000000
+
+#define DYNAMIC_MAC_TABLE_ENTRIES_H    0x03
+#define DYNAMIC_MAC_TABLE_MAC_EMPTY    0x04
+#define DYNAMIC_MAC_TABLE_RESERVED     0x78
+#define DYNAMIC_MAC_TABLE_NOT_READY    0x80
+
+#define DYNAMIC_MAC_FID_SHIFT          16
+#define DYNAMIC_MAC_SRC_PORT_SHIFT     20
+#define DYNAMIC_MAC_TIMESTAMP_SHIFT    22
+#define DYNAMIC_MAC_ENTRIES_SHIFT      24
+#define DYNAMIC_MAC_ENTRIES_H_SHIFT    8
+
+/*
+#define MIB_COUNTER_VALUE              00-00000000-3FFFFFFF
+#define MIB_COUNTER_VALID              00-00000000-40000000
+#define MIB_COUNTER_OVERFLOW           00-00000000-80000000
+*/
+
+#define MIB_COUNTER_VALUE              0x3FFFFFFF
+#define MIB_COUNTER_VALID              0x40000000
+#define MIB_COUNTER_OVERFLOW           0x80000000
+
+#define MIB_PACKET_DROPPED             0x0000FFFF
+
+#define KS_MIB_PACKET_DROPPED_TX_0     0x100
+#define KS_MIB_PACKET_DROPPED_TX_1     0x101
+#define KS_MIB_PACKET_DROPPED_TX       0x102
+#define KS_MIB_PACKET_DROPPED_RX_0     0x103
+#define KS_MIB_PACKET_DROPPED_RX_1     0x104
+#define KS_MIB_PACKET_DROPPED_RX       0x105
+
+/* Change default LED mode. */
+#define SET_DEFAULT_LED                        LED_SPEED_DUPLEX_ACT
+
+#define MAC_ADDR_LEN                   6
+#define MAC_ADDR_ORDER(i)              (MAC_ADDR_LEN - 1 - (i))
+
+#define MAX_ETHERNET_BODY_SIZE         1500
+#define ETHERNET_HEADER_SIZE           14
+
+#define MAX_ETHERNET_PACKET_SIZE       \
+       (MAX_ETHERNET_BODY_SIZE + ETHERNET_HEADER_SIZE)
+
+#define REGULAR_RX_BUF_SIZE            (MAX_ETHERNET_PACKET_SIZE + 4)
+#define MAX_RX_BUF_SIZE                        (1912 + 4)
+
+#define ADDITIONAL_ENTRIES             16
+#define MAX_MULTICAST_LIST             32
+
+#define HW_MULTICAST_SIZE              8
+
+#define HW_TO_DEV_PORT(port)           (port - 1)
+
+enum {
+       media_connected,
+       media_disconnected
+};
+
+enum {
+       OID_COUNTER_UNKOWN,
+
+       OID_COUNTER_FIRST,
+
+       /* total transmit errors */
+       OID_COUNTER_XMIT_ERROR,
+
+       /* total receive errors */
+       OID_COUNTER_RCV_ERROR,
+
+       OID_COUNTER_LAST
+};
+
+/*
+ * Hardware descriptor definitions
+ */
+
+#define DESC_ALIGNMENT                 16
+#define BUFFER_ALIGNMENT               8
+
+#define NUM_OF_RX_DESC                 64
+#define NUM_OF_TX_DESC                 64
+
+#define KS_DESC_RX_FRAME_LEN           0x000007FF
+#define KS_DESC_RX_FRAME_TYPE          0x00008000
+#define KS_DESC_RX_ERROR_CRC           0x00010000
+#define KS_DESC_RX_ERROR_RUNT          0x00020000
+#define KS_DESC_RX_ERROR_TOO_LONG      0x00040000
+#define KS_DESC_RX_ERROR_PHY           0x00080000
+#define KS884X_DESC_RX_PORT_MASK       0x00300000
+#define KS_DESC_RX_MULTICAST           0x01000000
+#define KS_DESC_RX_ERROR               0x02000000
+#define KS_DESC_RX_ERROR_CSUM_UDP      0x04000000
+#define KS_DESC_RX_ERROR_CSUM_TCP      0x08000000
+#define KS_DESC_RX_ERROR_CSUM_IP       0x10000000
+#define KS_DESC_RX_LAST                        0x20000000
+#define KS_DESC_RX_FIRST               0x40000000
+#define KS_DESC_RX_ERROR_COND          \
+       (KS_DESC_RX_ERROR_CRC |         \
+       KS_DESC_RX_ERROR_RUNT |         \
+       KS_DESC_RX_ERROR_PHY |          \
+       KS_DESC_RX_ERROR_TOO_LONG)
+
+#define KS_DESC_HW_OWNED               0x80000000
+
+#define KS_DESC_BUF_SIZE               0x000007FF
+#define KS884X_DESC_TX_PORT_MASK       0x00300000
+#define KS_DESC_END_OF_RING            0x02000000
+#define KS_DESC_TX_CSUM_GEN_UDP                0x04000000
+#define KS_DESC_TX_CSUM_GEN_TCP                0x08000000
+#define KS_DESC_TX_CSUM_GEN_IP         0x10000000
+#define KS_DESC_TX_LAST                        0x20000000
+#define KS_DESC_TX_FIRST               0x40000000
+#define KS_DESC_TX_INTERRUPT           0x80000000
+
+#define KS_DESC_PORT_SHIFT             20
+
+#define KS_DESC_RX_MASK                        (KS_DESC_BUF_SIZE)
+
+#define KS_DESC_TX_MASK                        \
+       (KS_DESC_TX_INTERRUPT |         \
+       KS_DESC_TX_FIRST |              \
+       KS_DESC_TX_LAST |               \
+       KS_DESC_TX_CSUM_GEN_IP |        \
+       KS_DESC_TX_CSUM_GEN_TCP |       \
+       KS_DESC_TX_CSUM_GEN_UDP |       \
+       KS_DESC_BUF_SIZE)
+
+struct ksz_desc_rx_stat {
+#ifdef __BIG_ENDIAN_BITFIELD
+       u32 hw_owned:1;
+       u32 first_desc:1;
+       u32 last_desc:1;
+       u32 csum_err_ip:1;
+       u32 csum_err_tcp:1;
+       u32 csum_err_udp:1;
+       u32 error:1;
+       u32 multicast:1;
+       u32 src_port:4;
+       u32 err_phy:1;
+       u32 err_too_long:1;
+       u32 err_runt:1;
+       u32 err_crc:1;
+       u32 frame_type:1;
+       u32 reserved1:4;
+       u32 frame_len:11;
+#else
+       u32 frame_len:11;
+       u32 reserved1:4;
+       u32 frame_type:1;
+       u32 err_crc:1;
+       u32 err_runt:1;
+       u32 err_too_long:1;
+       u32 err_phy:1;
+       u32 src_port:4;
+       u32 multicast:1;
+       u32 error:1;
+       u32 csum_err_udp:1;
+       u32 csum_err_tcp:1;
+       u32 csum_err_ip:1;
+       u32 last_desc:1;
+       u32 first_desc:1;
+       u32 hw_owned:1;
+#endif
+};
+
+struct ksz_desc_tx_stat {
+#ifdef __BIG_ENDIAN_BITFIELD
+       u32 hw_owned:1;
+       u32 reserved1:31;
+#else
+       u32 reserved1:31;
+       u32 hw_owned:1;
+#endif
+};
+
+struct ksz_desc_rx_buf {
+#ifdef __BIG_ENDIAN_BITFIELD
+       u32 reserved4:6;
+       u32 end_of_ring:1;
+       u32 reserved3:14;
+       u32 buf_size:11;
+#else
+       u32 buf_size:11;
+       u32 reserved3:14;
+       u32 end_of_ring:1;
+       u32 reserved4:6;
+#endif
+};
+
+struct ksz_desc_tx_buf {
+#ifdef __BIG_ENDIAN_BITFIELD
+       u32 intr:1;
+       u32 first_seg:1;
+       u32 last_seg:1;
+       u32 csum_gen_ip:1;
+       u32 csum_gen_tcp:1;
+       u32 csum_gen_udp:1;
+       u32 end_of_ring:1;
+       u32 reserved4:1;
+       u32 dest_port:4;
+       u32 reserved3:9;
+       u32 buf_size:11;
+#else
+       u32 buf_size:11;
+       u32 reserved3:9;
+       u32 dest_port:4;
+       u32 reserved4:1;
+       u32 end_of_ring:1;
+       u32 csum_gen_udp:1;
+       u32 csum_gen_tcp:1;
+       u32 csum_gen_ip:1;
+       u32 last_seg:1;
+       u32 first_seg:1;
+       u32 intr:1;
+#endif
+};
+
+union desc_stat {
+       struct ksz_desc_rx_stat rx;
+       struct ksz_desc_tx_stat tx;
+       u32 data;
+};
+
+union desc_buf {
+       struct ksz_desc_rx_buf rx;
+       struct ksz_desc_tx_buf tx;
+       u32 data;
+};
+
+/**
+ * struct ksz_hw_desc - Hardware descriptor data structure
+ * @ctrl:      Descriptor control value.
+ * @buf:       Descriptor buffer value.
+ * @addr:      Physical address of memory buffer.
+ * @next:      Pointer to next hardware descriptor.
+ */
+struct ksz_hw_desc {
+       union desc_stat ctrl;
+       union desc_buf buf;
+       u32 addr;
+       u32 next;
+};
+
+/**
+ * struct ksz_sw_desc - Software descriptor data structure
+ * @ctrl:      Descriptor control value.
+ * @buf:       Descriptor buffer value.
+ * @buf_size:  Current buffers size value in hardware descriptor.
+ */
+struct ksz_sw_desc {
+       union desc_stat ctrl;
+       union desc_buf buf;
+       u32 buf_size;
+};
+
+/**
+ * struct ksz_dma_buf - OS dependent DMA buffer data structure
+ * @skb:       Associated socket buffer.
+ * @dma:       Associated physical DMA address.
+ * len:                Actual len used.
+ */
+struct ksz_dma_buf {
+       struct sk_buff *skb;
+       dma_addr_t dma;
+       int len;
+};
+
+/**
+ * struct ksz_desc - Descriptor structure
+ * @phw:       Hardware descriptor pointer to uncached physical memory.
+ * @sw:                Cached memory to hold hardware descriptor values for
+ *             manipulation.
+ * @dma_buf:   Operating system dependent data structure to hold physical
+ *             memory buffer allocation information.
+ */
+struct ksz_desc {
+       struct ksz_hw_desc *phw;
+       struct ksz_sw_desc sw;
+       struct ksz_dma_buf dma_buf;
+};
+
+#define DMA_BUFFER(desc)  ((struct ksz_dma_buf *)(&(desc)->dma_buf))
+
+/**
+ * struct ksz_desc_info - Descriptor information data structure
+ * @ring:      First descriptor in the ring.
+ * @cur:       Current descriptor being manipulated.
+ * @ring_virt: First hardware descriptor in the ring.
+ * @ring_phys: The physical address of the first descriptor of the ring.
+ * @size:      Size of hardware descriptor.
+ * @alloc:     Number of descriptors allocated.
+ * @avail:     Number of descriptors available for use.
+ * @last:      Index for last descriptor released to hardware.
+ * @next:      Index for next descriptor available for use.
+ * @mask:      Mask for index wrapping.
+ */
+struct ksz_desc_info {
+       struct ksz_desc *ring;
+       struct ksz_desc *cur;
+       struct ksz_hw_desc *ring_virt;
+       u32 ring_phys;
+       int size;
+       int alloc;
+       int avail;
+       int last;
+       int next;
+       int mask;
+};
+
+/*
+ * KSZ8842 switch definitions
+ */
+
+enum {
+       TABLE_STATIC_MAC = 0,
+       TABLE_VLAN,
+       TABLE_DYNAMIC_MAC,
+       TABLE_MIB
+};
+
+#define LEARNED_MAC_TABLE_ENTRIES      1024
+#define STATIC_MAC_TABLE_ENTRIES       8
+
+/**
+ * struct ksz_mac_table - Static MAC table data structure
+ * @mac_addr:  MAC address to filter.
+ * @vid:       VID value.
+ * @fid:       FID value.
+ * @ports:     Port membership.
+ * @override:  Override setting.
+ * @use_fid:   FID use setting.
+ * @valid:     Valid setting indicating the entry is being used.
+ */
+struct ksz_mac_table {
+       u8 mac_addr[MAC_ADDR_LEN];
+       u16 vid;
+       u8 fid;
+       u8 ports;
+       u8 override:1;
+       u8 use_fid:1;
+       u8 valid:1;
+};
+
+#define VLAN_TABLE_ENTRIES             16
+
+/**
+ * struct ksz_vlan_table - VLAN table data structure
+ * @vid:       VID value.
+ * @fid:       FID value.
+ * @member:    Port membership.
+ */
+struct ksz_vlan_table {
+       u16 vid;
+       u8 fid;
+       u8 member;
+};
+
+#define DIFFSERV_ENTRIES               64
+#define PRIO_802_1P_ENTRIES            8
+#define PRIO_QUEUES                    4
+
+#define SWITCH_PORT_NUM                        2
+#define TOTAL_PORT_NUM                 (SWITCH_PORT_NUM + 1)
+#define HOST_MASK                      (1 << SWITCH_PORT_NUM)
+#define PORT_MASK                      7
+
+#define MAIN_PORT                      0
+#define OTHER_PORT                     1
+#define HOST_PORT                      SWITCH_PORT_NUM
+
+#define PORT_COUNTER_NUM               0x20
+#define TOTAL_PORT_COUNTER_NUM         (PORT_COUNTER_NUM + 2)
+
+#define MIB_COUNTER_RX_LO_PRIORITY     0x00
+#define MIB_COUNTER_RX_HI_PRIORITY     0x01
+#define MIB_COUNTER_RX_UNDERSIZE       0x02
+#define MIB_COUNTER_RX_FRAGMENT                0x03
+#define MIB_COUNTER_RX_OVERSIZE                0x04
+#define MIB_COUNTER_RX_JABBER          0x05
+#define MIB_COUNTER_RX_SYMBOL_ERR      0x06
+#define MIB_COUNTER_RX_CRC_ERR         0x07
+#define MIB_COUNTER_RX_ALIGNMENT_ERR   0x08
+#define MIB_COUNTER_RX_CTRL_8808       0x09
+#define MIB_COUNTER_RX_PAUSE           0x0A
+#define MIB_COUNTER_RX_BROADCAST       0x0B
+#define MIB_COUNTER_RX_MULTICAST       0x0C
+#define MIB_COUNTER_RX_UNICAST         0x0D
+#define MIB_COUNTER_RX_OCTET_64                0x0E
+#define MIB_COUNTER_RX_OCTET_65_127    0x0F
+#define MIB_COUNTER_RX_OCTET_128_255   0x10
+#define MIB_COUNTER_RX_OCTET_256_511   0x11
+#define MIB_COUNTER_RX_OCTET_512_1023  0x12
+#define MIB_COUNTER_RX_OCTET_1024_1522 0x13
+#define MIB_COUNTER_TX_LO_PRIORITY     0x14
+#define MIB_COUNTER_TX_HI_PRIORITY     0x15
+#define MIB_COUNTER_TX_LATE_COLLISION  0x16
+#define MIB_COUNTER_TX_PAUSE           0x17
+#define MIB_COUNTER_TX_BROADCAST       0x18
+#define MIB_COUNTER_TX_MULTICAST       0x19
+#define MIB_COUNTER_TX_UNICAST         0x1A
+#define MIB_COUNTER_TX_DEFERRED                0x1B
+#define MIB_COUNTER_TX_TOTAL_COLLISION 0x1C
+#define MIB_COUNTER_TX_EXCESS_COLLISION        0x1D
+#define MIB_COUNTER_TX_SINGLE_COLLISION        0x1E
+#define MIB_COUNTER_TX_MULTI_COLLISION 0x1F
+
+#define MIB_COUNTER_RX_DROPPED_PACKET  0x20
+#define MIB_COUNTER_TX_DROPPED_PACKET  0x21
+
+/**
+ * struct ksz_port_mib - Port MIB data structure
+ * @cnt_ptr:   Current pointer to MIB counter index.
+ * @link_down: Indication the link has just gone down.
+ * @state:     Connection status of the port.
+ * @mib_start: The starting counter index.  Some ports do not start at 0.
+ * @counter:   64-bit MIB counter value.
+ * @dropped:   Temporary buffer to remember last read packet dropped values.
+ *
+ * MIB counters needs to be read periodically so that counters do not get
+ * overflowed and give incorrect values.  A right balance is needed to
+ * satisfy this condition and not waste too much CPU time.
+ *
+ * It is pointless to read MIB counters when the port is disconnected.  The
+ * @state provides the connection status so that MIB counters are read only
+ * when the port is connected.  The @link_down indicates the port is just
+ * disconnected so that all MIB counters are read one last time to update the
+ * information.
+ */
+struct ksz_port_mib {
+       u8 cnt_ptr;
+       u8 link_down;
+       u8 state;
+       u8 mib_start;
+
+       u64 counter[TOTAL_PORT_COUNTER_NUM];
+       u32 dropped[2];
+};
+
+/**
+ * struct ksz_port_cfg - Port configuration data structure
+ * @vid:       VID value.
+ * @member:    Port membership.
+ * @port_prio: Port priority.
+ * @rx_rate:   Receive priority rate.
+ * @tx_rate:   Transmit priority rate.
+ * @stp_state: Current Spanning Tree Protocol state.
+ */
+struct ksz_port_cfg {
+       u16 vid;
+       u8 member;
+       u8 port_prio;
+       u32 rx_rate[PRIO_QUEUES];
+       u32 tx_rate[PRIO_QUEUES];
+       int stp_state;
+};
+
+/**
+ * struct ksz_switch - KSZ8842 switch data structure
+ * @mac_table: MAC table entries information.
+ * @vlan_table:        VLAN table entries information.
+ * @port_cfg:  Port configuration information.
+ * @diffserv:  DiffServ priority settings.  Possible values from 6-bit of ToS
+ *             (bit7 ~ bit2) field.
+ * @p_802_1p:  802.1P priority settings.  Possible values from 3-bit of 802.1p
+ *             Tag priority field.
+ * @br_addr:   Bridge address.  Used for STP.
+ * @other_addr:        Other MAC address.  Used for multiple network device mode.
+ * @broad_per: Broadcast storm percentage.
+ * @member:    Current port membership.  Used for STP.
+ */
+struct ksz_switch {
+       struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES];
+       struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES];
+       struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];
+
+       u8 diffserv[DIFFSERV_ENTRIES];
+       u8 p_802_1p[PRIO_802_1P_ENTRIES];
+
+       u8 br_addr[MAC_ADDR_LEN];
+       u8 other_addr[MAC_ADDR_LEN];
+
+       u8 broad_per;
+       u8 member;
+};
+
+#define TX_RATE_UNIT                   10000
+
+/**
+ * struct ksz_port_info - Port information data structure
+ * @state:     Connection status of the port.
+ * @tx_rate:   Transmit rate divided by 10000 to get Mbit.
+ * @duplex:    Duplex mode.
+ * @advertised:        Advertised auto-negotiation setting.  Used to determine link.
+ * @partner:   Auto-negotiation partner setting.  Used to determine link.
+ * @port_id:   Port index to access actual hardware register.
+ * @pdev:      Pointer to OS dependent network device.
+ */
+struct ksz_port_info {
+       uint state;
+       uint tx_rate;
+       u8 duplex;
+       u8 advertised;
+       u8 partner;
+       u8 port_id;
+       void *pdev;
+};
+
+#define MAX_TX_HELD_SIZE               52000
+
+/* Hardware features and bug fixes. */
+#define LINK_INT_WORKING               (1 << 0)
+#define SMALL_PACKET_TX_BUG            (1 << 1)
+#define HALF_DUPLEX_SIGNAL_BUG         (1 << 2)
+#define RX_HUGE_FRAME                  (1 << 4)
+#define STP_SUPPORT                    (1 << 8)
+
+/* Software overrides. */
+#define PAUSE_FLOW_CTRL                        (1 << 0)
+#define FAST_AGING                     (1 << 1)
+
+/**
+ * struct ksz_hw - KSZ884X hardware data structure
+ * @io:                        Virtual address assigned.
+ * @ksz_switch:                Pointer to KSZ8842 switch.
+ * @port_info:         Port information.
+ * @port_mib:          Port MIB information.
+ * @dev_count:         Number of network devices this hardware supports.
+ * @dst_ports:         Destination ports in switch for transmission.
+ * @id:                        Hardware ID.  Used for display only.
+ * @mib_cnt:           Number of MIB counters this hardware has.
+ * @mib_port_cnt:      Number of ports with MIB counters.
+ * @tx_cfg:            Cached transmit control settings.
+ * @rx_cfg:            Cached receive control settings.
+ * @intr_mask:         Current interrupt mask.
+ * @intr_set:          Current interrup set.
+ * @intr_blocked:      Interrupt blocked.
+ * @rx_desc_info:      Receive descriptor information.
+ * @tx_desc_info:      Transmit descriptor information.
+ * @tx_int_cnt:                Transmit interrupt count.  Used for TX optimization.
+ * @tx_int_mask:       Transmit interrupt mask.  Used for TX optimization.
+ * @tx_size:           Transmit data size.  Used for TX optimization.
+ *                     The maximum is defined by MAX_TX_HELD_SIZE.
+ * @perm_addr:         Permanent MAC address.
+ * @override_addr:     Overrided MAC address.
+ * @address:           Additional MAC address entries.
+ * @addr_list_size:    Additional MAC address list size.
+ * @mac_override:      Indication of MAC address overrided.
+ * @promiscuous:       Counter to keep track of promiscuous mode set.
+ * @all_multi:         Counter to keep track of all multicast mode set.
+ * @multi_list:                Multicast address entries.
+ * @multi_bits:                Cached multicast hash table settings.
+ * @multi_list_size:   Multicast address list size.
+ * @enabled:           Indication of hardware enabled.
+ * @rx_stop:           Indication of receive process stop.
+ * @features:          Hardware features to enable.
+ * @overrides:         Hardware features to override.
+ * @parent:            Pointer to parent, network device private structure.
+ */
+struct ksz_hw {
+       void __iomem *io;
+
+       struct ksz_switch *ksz_switch;
+       struct ksz_port_info port_info[SWITCH_PORT_NUM];
+       struct ksz_port_mib port_mib[TOTAL_PORT_NUM];
+       int dev_count;
+       int dst_ports;
+       int id;
+       int mib_cnt;
+       int mib_port_cnt;
+
+       u32 tx_cfg;
+       u32 rx_cfg;
+       u32 intr_mask;
+       u32 intr_set;
+       uint intr_blocked;
+
+       struct ksz_desc_info rx_desc_info;
+       struct ksz_desc_info tx_desc_info;
+
+       int tx_int_cnt;
+       int tx_int_mask;
+       int tx_size;
+
+       u8 perm_addr[MAC_ADDR_LEN];
+       u8 override_addr[MAC_ADDR_LEN];
+       u8 address[ADDITIONAL_ENTRIES][MAC_ADDR_LEN];
+       u8 addr_list_size;
+       u8 mac_override;
+       u8 promiscuous;
+       u8 all_multi;
+       u8 multi_list[MAX_MULTICAST_LIST][MAC_ADDR_LEN];
+       u8 multi_bits[HW_MULTICAST_SIZE];
+       u8 multi_list_size;
+
+       u8 enabled;
+       u8 rx_stop;
+       u8 reserved2[1];
+
+       uint features;
+       uint overrides;
+
+       void *parent;
+};
+
+enum {
+       PHY_NO_FLOW_CTRL,
+       PHY_FLOW_CTRL,
+       PHY_TX_ONLY,
+       PHY_RX_ONLY
+};
+
+/**
+ * struct ksz_port - Virtual port data structure
+ * @duplex:            Duplex mode setting.  1 for half duplex, 2 for full
+ *                     duplex, and 0 for auto, which normally results in full
+ *                     duplex.
+ * @speed:             Speed setting.  10 for 10 Mbit, 100 for 100 Mbit, and
+ *                     0 for auto, which normally results in 100 Mbit.
+ * @force_link:                Force link setting.  0 for auto-negotiation, and 1 for
+ *                     force.
+ * @flow_ctrl:         Flow control setting.  PHY_NO_FLOW_CTRL for no flow
+ *                     control, and PHY_FLOW_CTRL for flow control.
+ *                     PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100
+ *                     Mbit PHY.
+ * @first_port:                Index of first port this port supports.
+ * @mib_port_cnt:      Number of ports with MIB counters.
+ * @port_cnt:          Number of ports this port supports.
+ * @counter:           Port statistics counter.
+ * @hw:                        Pointer to hardware structure.
+ * @linked:            Pointer to port information linked to this port.
+ */
+struct ksz_port {
+       u8 duplex;
+       u8 speed;
+       u8 force_link;
+       u8 flow_ctrl;
+
+       int first_port;
+       int mib_port_cnt;
+       int port_cnt;
+       u64 counter[OID_COUNTER_LAST];
+
+       struct ksz_hw *hw;
+       struct ksz_port_info *linked;
+};
+
+/**
+ * struct ksz_timer_info - Timer information data structure
+ * @timer:     Kernel timer.
+ * @cnt:       Running timer counter.
+ * @max:       Number of times to run timer; -1 for infinity.
+ * @period:    Timer period in jiffies.
+ */
+struct ksz_timer_info {
+       struct timer_list timer;
+       int cnt;
+       int max;
+       int period;
+};
+
+/**
+ * struct ksz_shared_mem - OS dependent shared memory data structure
+ * @dma_addr:  Physical DMA address allocated.
+ * @alloc_size:        Allocation size.
+ * @phys:      Actual physical address used.
+ * @alloc_virt:        Virtual address allocated.
+ * @virt:      Actual virtual address used.
+ */
+struct ksz_shared_mem {
+       dma_addr_t dma_addr;
+       uint alloc_size;
+       uint phys;
+       u8 *alloc_virt;
+       u8 *virt;
+};
+
+/**
+ * struct ksz_counter_info - OS dependent counter information data structure
+ * @counter:   Wait queue to wakeup after counters are read.
+ * @time:      Next time in jiffies to read counter.
+ * @read:      Indication of counters read in full or not.
+ */
+struct ksz_counter_info {
+       wait_queue_head_t counter;
+       unsigned long time;
+       int read;
+};
+
+/**
+ * struct dev_info - Network device information data structure
+ * @dev:               Pointer to network device.
+ * @pdev:              Pointer to PCI device.
+ * @hw:                        Hardware structure.
+ * @desc_pool:         Physical memory used for descriptor pool.
+ * @hwlock:            Spinlock to prevent hardware from accessing.
+ * @lock:              Mutex lock to prevent device from accessing.
+ * @dev_rcv:           Receive process function used.
+ * @last_skb:          Socket buffer allocated for descriptor rx fragments.
+ * @skb_index:         Buffer index for receiving fragments.
+ * @skb_len:           Buffer length for receiving fragments.
+ * @mib_read:          Workqueue to read MIB counters.
+ * @mib_timer_info:    Timer to read MIB counters.
+ * @counter:           Used for MIB reading.
+ * @mtu:               Current MTU used.  The default is REGULAR_RX_BUF_SIZE;
+ *                     the maximum is MAX_RX_BUF_SIZE.
+ * @opened:            Counter to keep track of device open.
+ * @rx_tasklet:                Receive processing tasklet.
+ * @tx_tasklet:                Transmit processing tasklet.
+ * @wol_enable:                Wake-on-LAN enable set by ethtool.
+ * @wol_support:       Wake-on-LAN support used by ethtool.
+ * @pme_wait:          Used for KSZ8841 power management.
+ */
+struct dev_info {
+       struct net_device *dev;
+       struct pci_dev *pdev;
+
+       struct ksz_hw hw;
+       struct ksz_shared_mem desc_pool;
+
+       spinlock_t hwlock;
+       struct mutex lock;
+
+       int (*dev_rcv)(struct dev_info *);
+
+       struct sk_buff *last_skb;
+       int skb_index;
+       int skb_len;
+
+       struct work_struct mib_read;
+       struct ksz_timer_info mib_timer_info;
+       struct ksz_counter_info counter[TOTAL_PORT_NUM];
+
+       int mtu;
+       int opened;
+
+       struct tasklet_struct rx_tasklet;
+       struct tasklet_struct tx_tasklet;
+
+       int wol_enable;
+       int wol_support;
+       unsigned long pme_wait;
+};
+
+/**
+ * struct dev_priv - Network device private data structure
+ * @adapter:           Adapter device information.
+ * @port:              Port information.
+ * @monitor_time_info: Timer to monitor ports.
+ * @proc_sem:          Semaphore for proc accessing.
+ * @id:                        Device ID.
+ * @mii_if:            MII interface information.
+ * @advertising:       Temporary variable to store advertised settings.
+ * @msg_enable:                The message flags controlling driver output.
+ * @media_state:       The connection status of the device.
+ * @multicast:         The all multicast state of the device.
+ * @promiscuous:       The promiscuous state of the device.
+ */
+struct dev_priv {
+       struct dev_info *adapter;
+       struct ksz_port port;
+       struct ksz_timer_info monitor_timer_info;
+
+       struct semaphore proc_sem;
+       int id;
+
+       struct mii_if_info mii_if;
+       u32 advertising;
+
+       u32 msg_enable;
+       int media_state;
+       int multicast;
+       int promiscuous;
+};
+
+#define DRV_NAME               "KSZ884X PCI"
+#define DEVICE_NAME            "KSZ884x PCI"
+#define DRV_VERSION            "1.0.0"
+#define DRV_RELDATE            "Feb 8, 2010"
+
+static char version[] __devinitdata =
+       "Micrel " DEVICE_NAME " " DRV_VERSION " (" DRV_RELDATE ")";
+
+static u8 DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x88, 0x42, 0x01 };
+
+/*
+ * Interrupt processing primary routines
+ */
+
+static inline void hw_ack_intr(struct ksz_hw *hw, uint interrupt)
+{
+       writel(interrupt, hw->io + KS884X_INTERRUPTS_STATUS);
+}
+
+static inline void hw_dis_intr(struct ksz_hw *hw)
+{
+       hw->intr_blocked = hw->intr_mask;
+       writel(0, hw->io + KS884X_INTERRUPTS_ENABLE);
+       hw->intr_set = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_set_intr(struct ksz_hw *hw, uint interrupt)
+{
+       hw->intr_set = interrupt;
+       writel(interrupt, hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_ena_intr(struct ksz_hw *hw)
+{
+       hw->intr_blocked = 0;
+       hw_set_intr(hw, hw->intr_mask);
+}
+
+static inline void hw_dis_intr_bit(struct ksz_hw *hw, uint bit)
+{
+       hw->intr_mask &= ~(bit);
+}
+
+static inline void hw_turn_off_intr(struct ksz_hw *hw, uint interrupt)
+{
+       u32 read_intr;
+
+       read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+       hw->intr_set = read_intr & ~interrupt;
+       writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
+       hw_dis_intr_bit(hw, interrupt);
+}
+
+/**
+ * hw_turn_on_intr - turn on specified interrupts
+ * @hw:        The hardware instance.
+ * @bit:       The interrupt bits to be on.
+ *
+ * This routine turns on the specified interrupts in the interrupt mask so that
+ * those interrupts will be enabled.
+ */
+static void hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
+{
+       hw->intr_mask |= bit;
+
+       if (!hw->intr_blocked)
+               hw_set_intr(hw, hw->intr_mask);
+}
+
+static inline void hw_ena_intr_bit(struct ksz_hw *hw, uint interrupt)
+{
+       u32 read_intr;
+
+       read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
+       hw->intr_set = read_intr | interrupt;
+       writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
+}
+
+static inline void hw_read_intr(struct ksz_hw *hw, uint *status)
+{
+       *status = readl(hw->io + KS884X_INTERRUPTS_STATUS);
+       *status = *status & hw->intr_set;
+}
+
+static inline void hw_restore_intr(struct ksz_hw *hw, uint interrupt)
+{
+       if (interrupt)
+               hw_ena_intr(hw);
+}
+
+/**
+ * hw_block_intr - block hardware interrupts
+ *
+ * This function blocks all interrupts of the hardware and returns the current
+ * interrupt enable mask so that interrupts can be restored later.
+ *
+ * Return the current interrupt enable mask.
+ */
+static uint hw_block_intr(struct ksz_hw *hw)
+{
+       uint interrupt = 0;
+
+       if (!hw->intr_blocked) {
+               hw_dis_intr(hw);
+               interrupt = hw->intr_blocked;
+       }
+       return interrupt;
+}
+
+/*
+ * Hardware descriptor routines
+ */
+
+static inline void reset_desc(struct ksz_desc *desc, union desc_stat status)
+{
+       status.rx.hw_owned = 0;
+       desc->phw->ctrl.data = cpu_to_le32(status.data);
+}
+
+static inline void release_desc(struct ksz_desc *desc)
+{
+       desc->sw.ctrl.tx.hw_owned = 1;
+       if (desc->sw.buf_size != desc->sw.buf.data) {
+               desc->sw.buf_size = desc->sw.buf.data;
+               desc->phw->buf.data = cpu_to_le32(desc->sw.buf.data);
+       }
+       desc->phw->ctrl.data = cpu_to_le32(desc->sw.ctrl.data);
+}
+
+static void get_rx_pkt(struct ksz_desc_info *info, struct ksz_desc **desc)
+{
+       *desc = &info->ring[info->last];
+       info->last++;
+       info->last &= info->mask;
+       info->avail--;
+       (*desc)->sw.buf.data &= ~KS_DESC_RX_MASK;
+}
+
+static inline void set_rx_buf(struct ksz_desc *desc, u32 addr)
+{
+       desc->phw->addr = cpu_to_le32(addr);
+}
+
+static inline void set_rx_len(struct ksz_desc *desc, u32 len)
+{
+       desc->sw.buf.rx.buf_size = len;
+}
+
+static inline void get_tx_pkt(struct ksz_desc_info *info,
+       struct ksz_desc **desc)
+{
+       *desc = &info->ring[info->next];
+       info->next++;
+       info->next &= info->mask;
+       info->avail--;
+       (*desc)->sw.buf.data &= ~KS_DESC_TX_MASK;
+}
+
+static inline void set_tx_buf(struct ksz_desc *desc, u32 addr)
+{
+       desc->phw->addr = cpu_to_le32(addr);
+}
+
+static inline void set_tx_len(struct ksz_desc *desc, u32 len)
+{
+       desc->sw.buf.tx.buf_size = len;
+}
+
+/* Switch functions */
+
+#define TABLE_READ                     0x10
+#define TABLE_SEL_SHIFT                        2
+
+#define HW_DELAY(hw, reg)                      \
+       do {                                    \
+               u16 dummy;                      \
+               dummy = readw(hw->io + reg);    \
+       } while (0)
+
+/**
+ * sw_r_table - read 4 bytes of data from switch table
+ * @hw:                The hardware instance.
+ * @table:     The table selector.
+ * @addr:      The address of the table entry.
+ * @data:      Buffer to store the read data.
+ *
+ * This routine reads 4 bytes of data from the table of the switch.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
+{
+       u16 ctrl_addr;
+       uint interrupt;
+
+       ctrl_addr = (((table << TABLE_SEL_SHIFT) | TABLE_READ) << 8) | addr;
+
+       interrupt = hw_block_intr(hw);
+
+       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+       HW_DELAY(hw, KS884X_IACR_OFFSET);
+       *data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+       hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * sw_w_table_64 - write 8 bytes of data to the switch table
+ * @hw:                The hardware instance.
+ * @table:     The table selector.
+ * @addr:      The address of the table entry.
+ * @data_hi:   The high part of data to be written (bit63 ~ bit32).
+ * @data_lo:   The low part of data to be written (bit31 ~ bit0).
+ *
+ * This routine writes 8 bytes of data to the table of the switch.
+ * Hardware interrupts are disabled to minimize corruption of written data.
+ */
+static void sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
+       u32 data_lo)
+{
+       u16 ctrl_addr;
+       uint interrupt;
+
+       ctrl_addr = ((table << TABLE_SEL_SHIFT) << 8) | addr;
+
+       interrupt = hw_block_intr(hw);
+
+       writel(data_hi, hw->io + KS884X_ACC_DATA_4_OFFSET);
+       writel(data_lo, hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+       HW_DELAY(hw, KS884X_IACR_OFFSET);
+
+       hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * sw_w_sta_mac_table - write to the static MAC table
+ * @hw:        The hardware instance.
+ * @addr:      The address of the table entry.
+ * @mac_addr:  The MAC address.
+ * @ports:     The port members.
+ * @override:  The flag to override the port receive/transmit settings.
+ * @valid:     The flag to indicate entry is valid.
+ * @use_fid:   The flag to indicate the FID is valid.
+ * @fid:       The FID value.
+ *
+ * This routine writes an entry of the static MAC table of the switch.  It
+ * calls sw_w_table_64() to write the data.
+ */
+static void sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
+       u8 ports, int override, int valid, int use_fid, u8 fid)
+{
+       u32 data_hi;
+       u32 data_lo;
+
+       data_lo = ((u32) mac_addr[2] << 24) |
+               ((u32) mac_addr[3] << 16) |
+               ((u32) mac_addr[4] << 8) | mac_addr[5];
+       data_hi = ((u32) mac_addr[0] << 8) | mac_addr[1];
+       data_hi |= (u32) ports << STATIC_MAC_FWD_PORTS_SHIFT;
+
+       if (override)
+               data_hi |= STATIC_MAC_TABLE_OVERRIDE;
+       if (use_fid) {
+               data_hi |= STATIC_MAC_TABLE_USE_FID;
+               data_hi |= (u32) fid << STATIC_MAC_FID_SHIFT;
+       }
+       if (valid)
+               data_hi |= STATIC_MAC_TABLE_VALID;
+
+       sw_w_table_64(hw, TABLE_STATIC_MAC, addr, data_hi, data_lo);
+}
+
+/**
+ * sw_r_vlan_table - read from the VLAN table
+ * @hw:        The hardware instance.
+ * @addr:      The address of the table entry.
+ * @vid:       Buffer to store the VID.
+ * @fid:       Buffer to store the VID.
+ * @member:    Buffer to store the port membership.
+ *
+ * This function reads an entry of the VLAN table of the switch.  It calls
+ * sw_r_table() to get the data.
+ *
+ * Return 0 if the entry is valid; otherwise -1.
+ */
+static int sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
+       u8 *member)
+{
+       u32 data;
+
+       sw_r_table(hw, TABLE_VLAN, addr, &data);
+       if (data & VLAN_TABLE_VALID) {
+               *vid = (u16)(data & VLAN_TABLE_VID);
+               *fid = (u8)((data & VLAN_TABLE_FID) >> VLAN_TABLE_FID_SHIFT);
+               *member = (u8)((data & VLAN_TABLE_MEMBERSHIP) >>
+                       VLAN_TABLE_MEMBERSHIP_SHIFT);
+               return 0;
+       }
+       return -1;
+}
+
+/**
+ * port_r_mib_cnt - read MIB counter
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @addr:      The address of the counter.
+ * @cnt:       Buffer to store the counter.
+ *
+ * This routine reads a MIB counter of the port.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
+{
+       u32 data;
+       u16 ctrl_addr;
+       uint interrupt;
+       int timeout;
+
+       ctrl_addr = addr + PORT_COUNTER_NUM * port;
+
+       interrupt = hw_block_intr(hw);
+
+       ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ) << 8);
+       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+       HW_DELAY(hw, KS884X_IACR_OFFSET);
+
+       for (timeout = 100; timeout > 0; timeout--) {
+               data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+               if (data & MIB_COUNTER_VALID) {
+                       if (data & MIB_COUNTER_OVERFLOW)
+                               *cnt += MIB_COUNTER_VALUE + 1;
+                       *cnt += data & MIB_COUNTER_VALUE;
+                       break;
+               }
+       }
+
+       hw_restore_intr(hw, interrupt);
+}
+
+/**
+ * port_r_mib_pkt - read dropped packet counts
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @cnt:       Buffer to store the receive and transmit dropped packet counts.
+ *
+ * This routine reads the dropped packet counts of the port.
+ * Hardware interrupts are disabled to minimize corruption of read data.
+ */
+static void port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
+{
+       u32 cur;
+       u32 data;
+       u16 ctrl_addr;
+       uint interrupt;
+       int index;
+
+       index = KS_MIB_PACKET_DROPPED_RX_0 + port;
+       do {
+               interrupt = hw_block_intr(hw);
+
+               ctrl_addr = (u16) index;
+               ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ)
+                       << 8);
+               writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
+               HW_DELAY(hw, KS884X_IACR_OFFSET);
+               data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
+
+               hw_restore_intr(hw, interrupt);
+
+               data &= MIB_PACKET_DROPPED;
+               cur = *last;
+               if (data != cur) {
+                       *last = data;
+                       if (data < cur)
+                               data += MIB_PACKET_DROPPED + 1;
+                       data -= cur;
+                       *cnt += data;
+               }
+               ++last;
+               ++cnt;
+               index -= KS_MIB_PACKET_DROPPED_TX -
+                       KS_MIB_PACKET_DROPPED_TX_0 + 1;
+       } while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
+}
+
+/**
+ * port_r_cnt - read MIB counters periodically
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine is used to read the counters of the port periodically to avoid
+ * counter overflow.  The hardware should be acquired first before calling this
+ * routine.
+ *
+ * Return non-zero when not all counters not read.
+ */
+static int port_r_cnt(struct ksz_hw *hw, int port)
+{
+       struct ksz_port_mib *mib = &hw->port_mib[port];
+
+       if (mib->mib_start < PORT_COUNTER_NUM)
+               while (mib->cnt_ptr < PORT_COUNTER_NUM) {
+                       port_r_mib_cnt(hw, port, mib->cnt_ptr,
+                               &mib->counter[mib->cnt_ptr]);
+                       ++mib->cnt_ptr;
+               }
+       if (hw->mib_cnt > PORT_COUNTER_NUM)
+               port_r_mib_pkt(hw, port, mib->dropped,
+                       &mib->counter[PORT_COUNTER_NUM]);
+       mib->cnt_ptr = 0;
+       return 0;
+}
+
+/**
+ * port_init_cnt - initialize MIB counter values
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine is used to initialize all counters to zero if the hardware
+ * cannot do it after reset.
+ */
+static void port_init_cnt(struct ksz_hw *hw, int port)
+{
+       struct ksz_port_mib *mib = &hw->port_mib[port];
+
+       mib->cnt_ptr = 0;
+       if (mib->mib_start < PORT_COUNTER_NUM)
+               do {
+                       port_r_mib_cnt(hw, port, mib->cnt_ptr,
+                               &mib->counter[mib->cnt_ptr]);
+                       ++mib->cnt_ptr;
+               } while (mib->cnt_ptr < PORT_COUNTER_NUM);
+       if (hw->mib_cnt > PORT_COUNTER_NUM)
+               port_r_mib_pkt(hw, port, mib->dropped,
+                       &mib->counter[PORT_COUNTER_NUM]);
+       memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
+       mib->cnt_ptr = 0;
+}
+
+/*
+ * Port functions
+ */
+
+/**
+ * port_chk - check port register bits
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the port register.
+ * @bits:      The data bits to check.
+ *
+ * This function checks whether the specified bits of the port register are set
+ * or not.
+ *
+ * Return 0 if the bits are not set.
+ */
+static int port_chk(struct ksz_hw *hw, int port, int offset, u16 bits)
+{
+       u32 addr;
+       u16 data;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += offset;
+       data = readw(hw->io + addr);
+       return (data & bits) == bits;
+}
+
+/**
+ * port_cfg - set port register bits
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the port register.
+ * @bits:      The data bits to set.
+ * @set:       The flag indicating whether the bits are to be set or not.
+ *
+ * This routine sets or resets the specified bits of the port register.
+ */
+static void port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits,
+       int set)
+{
+       u32 addr;
+       u16 data;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += offset;
+       data = readw(hw->io + addr);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       writew(data, hw->io + addr);
+}
+
+/**
+ * port_chk_shift - check port bit
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the register.
+ * @shift:     Number of bits to shift.
+ *
+ * This function checks whether the specified port is set in the register or
+ * not.
+ *
+ * Return 0 if the port is not set.
+ */
+static int port_chk_shift(struct ksz_hw *hw, int port, u32 addr, int shift)
+{
+       u16 data;
+       u16 bit = 1 << port;
+
+       data = readw(hw->io + addr);
+       data >>= shift;
+       return (data & bit) == bit;
+}
+
+/**
+ * port_cfg_shift - set port bit
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the register.
+ * @shift:     Number of bits to shift.
+ * @set:       The flag indicating whether the port is to be set or not.
+ *
+ * This routine sets or resets the specified port in the register.
+ */
+static void port_cfg_shift(struct ksz_hw *hw, int port, u32 addr, int shift,
+       int set)
+{
+       u16 data;
+       u16 bits = 1 << port;
+
+       data = readw(hw->io + addr);
+       bits <<= shift;
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       writew(data, hw->io + addr);
+}
+
+/**
+ * port_r8 - read byte from port register
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the port register.
+ * @data:      Buffer to store the data.
+ *
+ * This routine reads a byte from the port register.
+ */
+static void port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
+{
+       u32 addr;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += offset;
+       *data = readb(hw->io + addr);
+}
+
+/**
+ * port_r16 - read word from port register.
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the port register.
+ * @data:      Buffer to store the data.
+ *
+ * This routine reads a word from the port register.
+ */
+static void port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
+{
+       u32 addr;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += offset;
+       *data = readw(hw->io + addr);
+}
+
+/**
+ * port_w16 - write word to port register.
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @offset:    The offset of the port register.
+ * @data:      Data to write.
+ *
+ * This routine writes a word to the port register.
+ */
+static void port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
+{
+       u32 addr;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += offset;
+       writew(data, hw->io + addr);
+}
+
+/**
+ * sw_chk - check switch register bits
+ * @hw:        The hardware instance.
+ * @addr:      The address of the switch register.
+ * @bits:      The data bits to check.
+ *
+ * This function checks whether the specified bits of the switch register are
+ * set or not.
+ *
+ * Return 0 if the bits are not set.
+ */
+static int sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
+{
+       u16 data;
+
+       data = readw(hw->io + addr);
+       return (data & bits) == bits;
+}
+
+/**
+ * sw_cfg - set switch register bits
+ * @hw:        The hardware instance.
+ * @addr:      The address of the switch register.
+ * @bits:      The data bits to set.
+ * @set:       The flag indicating whether the bits are to be set or not.
+ *
+ * This function sets or resets the specified bits of the switch register.
+ */
+static void sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
+{
+       u16 data;
+
+       data = readw(hw->io + addr);
+       if (set)
+               data |= bits;
+       else
+               data &= ~bits;
+       writew(data, hw->io + addr);
+}
+
+/* Bandwidth */
+
+static inline void port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
+}
+
+static inline int port_chk_broad_storm(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM);
+}
+
+/* Driver set switch broadcast storm protection at 10% rate. */
+#define BROADCAST_STORM_PROTECTION_RATE        10
+
+/* 148,800 frames * 67 ms / 100 */
+#define BROADCAST_STORM_VALUE          9969
+
+/**
+ * sw_cfg_broad_storm - configure broadcast storm threshold
+ * @hw:        The hardware instance.
+ * @percent:   Broadcast storm threshold in percent of transmit rate.
+ *
+ * This routine configures the broadcast storm threshold of the switch.
+ */
+static void sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
+{
+       u16 data;
+       u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);
+
+       if (value > BROADCAST_STORM_RATE)
+               value = BROADCAST_STORM_RATE;
+
+       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+       data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
+       data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
+       writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+}
+
+/**
+ * sw_get_board_storm - get broadcast storm threshold
+ * @hw:        The hardware instance.
+ * @percent:   Buffer to store the broadcast storm threshold percentage.
+ *
+ * This routine retrieves the broadcast storm threshold of the switch.
+ */
+static void sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
+{
+       int num;
+       u16 data;
+
+       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+       num = (data & BROADCAST_STORM_RATE_HI);
+       num <<= 8;
+       num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
+       num = (num * 100 + BROADCAST_STORM_VALUE / 2) / BROADCAST_STORM_VALUE;
+       *percent = (u8) num;
+}
+
+/**
+ * sw_dis_broad_storm - disable broadstorm
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine disables the broadcast storm limit function of the switch.
+ */
+static void sw_dis_broad_storm(struct ksz_hw *hw, int port)
+{
+       port_cfg_broad_storm(hw, port, 0);
+}
+
+/**
+ * sw_ena_broad_storm - enable broadcast storm
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine enables the broadcast storm limit function of the switch.
+ */
+static void sw_ena_broad_storm(struct ksz_hw *hw, int port)
+{
+       sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
+       port_cfg_broad_storm(hw, port, 1);
+}
+
+/**
+ * sw_init_broad_storm - initialize broadcast storm
+ * @hw:        The hardware instance.
+ *
+ * This routine initializes the broadcast storm limit function of the switch.
+ */
+static void sw_init_broad_storm(struct ksz_hw *hw)
+{
+       int port;
+
+       hw->ksz_switch->broad_per = 1;
+       sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
+       for (port = 0; port < TOTAL_PORT_NUM; port++)
+               sw_dis_broad_storm(hw, port);
+       sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
+}
+
+/**
+ * hw_cfg_broad_storm - configure broadcast storm
+ * @hw:        The hardware instance.
+ * @percent:   Broadcast storm threshold in percent of transmit rate.
+ *
+ * This routine configures the broadcast storm threshold of the switch.
+ * It is called by user functions.  The hardware should be acquired first.
+ */
+static void hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
+{
+       if (percent > 100)
+               percent = 100;
+
+       sw_cfg_broad_storm(hw, percent);
+       sw_get_broad_storm(hw, &percent);
+       hw->ksz_switch->broad_per = percent;
+}
+
+/**
+ * sw_dis_prio_rate - disable switch priority rate
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine disables the priority rate function of the switch.
+ */
+static void sw_dis_prio_rate(struct ksz_hw *hw, int port)
+{
+       u32 addr;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += KS8842_PORT_IN_RATE_OFFSET;
+       writel(0, hw->io + addr);
+}
+
+/**
+ * sw_init_prio_rate - initialize switch prioirty rate
+ * @hw:        The hardware instance.
+ *
+ * This routine initializes the priority rate function of the switch.
+ */
+static void sw_init_prio_rate(struct ksz_hw *hw)
+{
+       int port;
+       int prio;
+       struct ksz_switch *sw = hw->ksz_switch;
+
+       for (port = 0; port < TOTAL_PORT_NUM; port++) {
+               for (prio = 0; prio < PRIO_QUEUES; prio++) {
+                       sw->port_cfg[port].rx_rate[prio] =
+                       sw->port_cfg[port].tx_rate[prio] = 0;
+               }
+               sw_dis_prio_rate(hw, port);
+       }
+}
+
+/* Communication */
+
+static inline void port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
+}
+
+static inline void port_cfg_force_flow_ctrl(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL, set);
+}
+
+static inline int port_chk_back_pressure(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE);
+}
+
+static inline int port_chk_force_flow_ctrl(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL);
+}
+
+/* Spanning Tree */
+
+static inline void port_cfg_dis_learn(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_LEARN_DISABLE, set);
+}
+
+static inline void port_cfg_rx(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_RX_ENABLE, set);
+}
+
+static inline void port_cfg_tx(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_TX_ENABLE, set);
+}
+
+static inline void sw_cfg_fast_aging(struct ksz_hw *hw, int set)
+{
+       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET, SWITCH_FAST_AGING, set);
+}
+
+static inline void sw_flush_dyn_mac_table(struct ksz_hw *hw)
+{
+       if (!(hw->overrides & FAST_AGING)) {
+               sw_cfg_fast_aging(hw, 1);
+               mdelay(1);
+               sw_cfg_fast_aging(hw, 0);
+       }
+}
+
+/* VLAN */
+
+static inline void port_cfg_ins_tag(struct ksz_hw *hw, int p, int insert)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG, insert);
+}
+
+static inline void port_cfg_rmv_tag(struct ksz_hw *hw, int p, int remove)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG, remove);
+}
+
+static inline int port_chk_ins_tag(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG);
+}
+
+static inline int port_chk_rmv_tag(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG);
+}
+
+static inline void port_cfg_dis_non_vid(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID, set);
+}
+
+static inline void port_cfg_in_filter(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER, set);
+}
+
+static inline int port_chk_dis_non_vid(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID);
+}
+
+static inline int port_chk_in_filter(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER);
+}
+
+/* Mirroring */
+
+static inline void port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
+}
+
+static inline void port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
+}
+
+static inline void port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
+}
+
+static inline void sw_cfg_mirror_rx_tx(struct ksz_hw *hw, int set)
+{
+       sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, SWITCH_MIRROR_RX_TX, set);
+}
+
+static void sw_init_mirror(struct ksz_hw *hw)
+{
+       int port;
+
+       for (port = 0; port < TOTAL_PORT_NUM; port++) {
+               port_cfg_mirror_sniffer(hw, port, 0);
+               port_cfg_mirror_rx(hw, port, 0);
+               port_cfg_mirror_tx(hw, port, 0);
+       }
+       sw_cfg_mirror_rx_tx(hw, 0);
+}
+
+static inline void sw_cfg_unk_def_deliver(struct ksz_hw *hw, int set)
+{
+       sw_cfg(hw, KS8842_SWITCH_CTRL_7_OFFSET,
+               SWITCH_UNK_DEF_PORT_ENABLE, set);
+}
+
+static inline int sw_cfg_chk_unk_def_deliver(struct ksz_hw *hw)
+{
+       return sw_chk(hw, KS8842_SWITCH_CTRL_7_OFFSET,
+               SWITCH_UNK_DEF_PORT_ENABLE);
+}
+
+static inline void sw_cfg_unk_def_port(struct ksz_hw *hw, int port, int set)
+{
+       port_cfg_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0, set);
+}
+
+static inline int sw_chk_unk_def_port(struct ksz_hw *hw, int port)
+{
+       return port_chk_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0);
+}
+
+/* Priority */
+
+static inline void port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
+}
+
+static inline void port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
+}
+
+static inline void port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
+}
+
+static inline void port_cfg_prio(struct ksz_hw *hw, int p, int set)
+{
+       port_cfg(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
+}
+
+static inline int port_chk_diffserv(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE);
+}
+
+static inline int port_chk_802_1p(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE);
+}
+
+static inline int port_chk_replace_vid(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING);
+}
+
+static inline int port_chk_prio(struct ksz_hw *hw, int p)
+{
+       return port_chk(hw, p,
+               KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE);
+}
+
+/**
+ * sw_dis_diffserv - disable switch DiffServ priority
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine disables the DiffServ priority function of the switch.
+ */
+static void sw_dis_diffserv(struct ksz_hw *hw, int port)
+{
+       port_cfg_diffserv(hw, port, 0);
+}
+
+/**
+ * sw_dis_802_1p - disable switch 802.1p priority
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine disables the 802.1p priority function of the switch.
+ */
+static void sw_dis_802_1p(struct ksz_hw *hw, int port)
+{
+       port_cfg_802_1p(hw, port, 0);
+}
+
+/**
+ * sw_cfg_replace_null_vid -
+ * @hw:        The hardware instance.
+ * @set:       The flag to disable or enable.
+ *
+ */
+static void sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
+{
+       sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
+}
+
+/**
+ * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @set:       The flag to disable or enable.
+ *
+ * This routine enables the 802.1p priority re-mapping function of the switch.
+ * That allows 802.1p priority field to be replaced with the port's default
+ * tag's priority value if the ingress packet's 802.1p priority has a higher
+ * priority than port's default tag's priority.
+ */
+static void sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
+{
+       port_cfg_replace_vid(hw, port, set);
+}
+
+/**
+ * sw_cfg_port_based - configure switch port based priority
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @prio:      The priority to set.
+ *
+ * This routine configures the port based priority of the switch.
+ */
+static void sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
+{
+       u16 data;
+
+       if (prio > PORT_BASED_PRIORITY_BASE)
+               prio = PORT_BASED_PRIORITY_BASE;
+
+       hw->ksz_switch->port_cfg[port].port_prio = prio;
+
+       port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
+       data &= ~PORT_BASED_PRIORITY_MASK;
+       data |= prio << PORT_BASED_PRIORITY_SHIFT;
+       port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
+}
+
+/**
+ * sw_dis_multi_queue - disable transmit multiple queues
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ *
+ * This routine disables the transmit multiple queues selection of the switch
+ * port.  Only single transmit queue on the port.
+ */
+static void sw_dis_multi_queue(struct ksz_hw *hw, int port)
+{
+       port_cfg_prio(hw, port, 0);
+}
+
+/**
+ * sw_init_prio - initialize switch priority
+ * @hw:        The hardware instance.
+ *
+ * This routine initializes the switch QoS priority functions.
+ */
+static void sw_init_prio(struct ksz_hw *hw)
+{
+       int port;
+       int tos;
+       struct ksz_switch *sw = hw->ksz_switch;
+
+       /*
+        * Init all the 802.1p tag priority value to be assigned to different
+        * priority queue.
+        */
+       sw->p_802_1p[0] = 0;
+       sw->p_802_1p[1] = 0;
+       sw->p_802_1p[2] = 1;
+       sw->p_802_1p[3] = 1;
+       sw->p_802_1p[4] = 2;
+       sw->p_802_1p[5] = 2;
+       sw->p_802_1p[6] = 3;
+       sw->p_802_1p[7] = 3;
+
+       /*
+        * Init all the DiffServ priority value to be assigned to priority
+        * queue 0.
+        */
+       for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
+               sw->diffserv[tos] = 0;
+
+       /* All QoS functions disabled. */
+       for (port = 0; port < TOTAL_PORT_NUM; port++) {
+               sw_dis_multi_queue(hw, port);
+               sw_dis_diffserv(hw, port);
+               sw_dis_802_1p(hw, port);
+               sw_cfg_replace_vid(hw, port, 0);
+
+               sw->port_cfg[port].port_prio = 0;
+               sw_cfg_port_based(hw, port, sw->port_cfg[port].port_prio);
+       }
+       sw_cfg_replace_null_vid(hw, 0);
+}
+
+/**
+ * port_get_def_vid - get port default VID.
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @vid:       Buffer to store the VID.
+ *
+ * This routine retrieves the default VID of the port.
+ */
+static void port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
+{
+       u32 addr;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += KS8842_PORT_CTRL_VID_OFFSET;
+       *vid = readw(hw->io + addr);
+}
+
+/**
+ * sw_init_vlan - initialize switch VLAN
+ * @hw:        The hardware instance.
+ *
+ * This routine initializes the VLAN function of the switch.
+ */
+static void sw_init_vlan(struct ksz_hw *hw)
+{
+       int port;
+       int entry;
+       struct ksz_switch *sw = hw->ksz_switch;
+
+       /* Read 16 VLAN entries from device's VLAN table. */
+       for (entry = 0; entry < VLAN_TABLE_ENTRIES; entry++) {
+               sw_r_vlan_table(hw, entry,
+                       &sw->vlan_table[entry].vid,
+                       &sw->vlan_table[entry].fid,
+                       &sw->vlan_table[entry].member);
+       }
+
+       for (port = 0; port < TOTAL_PORT_NUM; port++) {
+               port_get_def_vid(hw, port, &sw->port_cfg[port].vid);
+               sw->port_cfg[port].member = PORT_MASK;
+       }
+}
+
+/**
+ * sw_cfg_port_base_vlan - configure port-based VLAN membership
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @member:    The port-based VLAN membership.
+ *
+ * This routine configures the port-based VLAN membership of the port.
+ */
+static void sw_cfg_port_base_vlan(struct ksz_hw *hw, int port, u8 member)
+{
+       u32 addr;
+       u8 data;
+
+       PORT_CTRL_ADDR(port, addr);
+       addr += KS8842_PORT_CTRL_2_OFFSET;
+
+       data = readb(hw->io + addr);
+       data &= ~PORT_VLAN_MEMBERSHIP;
+       data |= (member & PORT_MASK);
+       writeb(data, hw->io + addr);
+
+       hw->ksz_switch->port_cfg[port].member = member;
+}
+
+/**
+ * sw_get_addr - get the switch MAC address.
+ * @hw:        The hardware instance.
+ * @mac_addr:  Buffer to store the MAC address.
+ *
+ * This function retrieves the MAC address of the switch.
+ */
+static inline void sw_get_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+       int i;
+
+       for (i = 0; i < 6; i += 2) {
+               mac_addr[i] = readb(hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
+               mac_addr[1 + i] = readb(hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
+       }
+}
+
+/**
+ * sw_set_addr - configure switch MAC address
+ * @hw:        The hardware instance.
+ * @mac_addr:  The MAC address.
+ *
+ * This function configures the MAC address of the switch.
+ */
+static void sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+       int i;
+
+       for (i = 0; i < 6; i += 2) {
+               writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
+               writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
+       }
+}
+
+/**
+ * sw_set_global_ctrl - set switch global control
+ * @hw:        The hardware instance.
+ *
+ * This routine sets the global control of the switch function.
+ */
+static void sw_set_global_ctrl(struct ksz_hw *hw)
+{
+       u16 data;
+
+       /* Enable switch MII flow control. */
+       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+       data |= SWITCH_FLOW_CTRL;
+       writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
+
+       data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
+
+       /* Enable aggressive back off algorithm in half duplex mode. */
+       data |= SWITCH_AGGR_BACKOFF;
+
+       /* Enable automatic fast aging when link changed detected. */
+       data |= SWITCH_AGING_ENABLE;
+       data |= SWITCH_LINK_AUTO_AGING;
+
+       if (hw->overrides & FAST_AGING)
+               data |= SWITCH_FAST_AGING;
+       else
+               data &= ~SWITCH_FAST_AGING;
+       writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
+
+       data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+
+       /* Enable no excessive collision drop. */
+       data |= NO_EXC_COLLISION_DROP;
+       writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+}
+
+enum {
+       STP_STATE_DISABLED = 0,
+       STP_STATE_LISTENING,
+       STP_STATE_LEARNING,
+       STP_STATE_FORWARDING,
+       STP_STATE_BLOCKED,
+       STP_STATE_SIMPLE
+};
+
+/**
+ * port_set_stp_state - configure port spanning tree state
+ * @hw:        The hardware instance.
+ * @port:      The port index.
+ * @state:     The spanning tree state.
+ *
+ * This routine configures the spanning tree state of the port.
+ */
+static void port_set_stp_state(struct ksz_hw *hw, int port, int state)
+{
+       u16 data;
+
+       port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data);
+       switch (state) {
+       case STP_STATE_DISABLED:
+               data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
+               data |= PORT_LEARN_DISABLE;
+               break;
+       case STP_STATE_LISTENING:
+/*
+ * No need to turn on transmit because of port direct mode.
+ * Turning on receive is required if static MAC table is not setup.
+ */
+               data &= ~PORT_TX_ENABLE;
+               data |= PORT_RX_ENABLE;
+               data |= PORT_LEARN_DISABLE;
+               break;
+       case STP_STATE_LEARNING:
+               data &= ~PORT_TX_ENABLE;
+               data |= PORT_RX_ENABLE;
+               data &= ~PORT_LEARN_DISABLE;
+               break;
+       case STP_STATE_FORWARDING:
+               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+               data &= ~PORT_LEARN_DISABLE;
+               break;
+       case STP_STATE_BLOCKED:
+/*
+ * Need to setup static MAC table with override to keep receiving BPDU
+ * messages.  See sw_init_stp routine.
+ */
+               data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
+               data |= PORT_LEARN_DISABLE;
+               break;
+       case STP_STATE_SIMPLE:
+               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
+               data |= PORT_LEARN_DISABLE;
+               break;
+       }
+       port_w16(hw, port, KS8842_PORT_CTRL_2_OFFSET, data);
+       hw->ksz_switch->port_cfg[port].stp_state = state;
+}
+
+#define STP_ENTRY                      0
+#define BROADCAST_ENTRY                        1
+#define BRIDGE_ADDR_ENTRY              2
+#define IPV6_ADDR_ENTRY                        3
+
+/**
+ * sw_clr_sta_mac_table - clear static MAC table
+ * @hw:        The hardware instance.
+ *
+ * This routine clears the static MAC table.
+ */
+static void sw_clr_sta_mac_table(struct ksz_hw *hw)
+{
+       struct ksz_mac_table *entry;
+       int i;
+
+       for (i = 0; i < STATIC_MAC_TABLE_ENTRIES; i++) {
+               entry = &hw->ksz_switch->mac_table[i];
+               sw_w_sta_mac_table(hw, i,
+                       entry->mac_addr, entry->ports,
+                       entry->override, 0,
+                       entry->use_fid, entry->fid);
+       }
+}
+
+/**
+ * sw_init_stp - initialize switch spanning tree support
+ * @hw:        The hardware instance.
+ *
+ * This routine initializes the spanning tree support of the switch.
+ */
+static void sw_init_stp(struct ksz_hw *hw)
+{
+       struct ksz_mac_table *entry;
+
+       entry = &hw->ksz_switch->mac_table[STP_ENTRY];
+       entry->mac_addr[0] = 0x01;
+       entry->mac_addr[1] = 0x80;
+       entry->mac_addr[2] = 0xC2;
+       entry->mac_addr[3] = 0x00;
+       entry->mac_addr[4] = 0x00;
+       entry->mac_addr[5] = 0x00;
+       entry->ports = HOST_MASK;
+       entry->override = 1;
+       entry->valid = 1;
+       sw_w_sta_mac_table(hw, STP_ENTRY,
+               entry->mac_addr, entry->ports,
+               entry->override, entry->valid,
+               entry->use_fid, entry->fid);
+}
+
+/**
+ * sw_block_addr - block certain packets from the host port
+ * @hw:        The hardware instance.
+ *
+ * This routine blocks certain packets from reaching to the host port.
+ */
+static void sw_block_addr(struct ksz_hw *hw)
+{
+       struct ksz_mac_table *entry;
+       int i;
+
+       for (i = BROADCAST_ENTRY; i <= IPV6_ADDR_ENTRY; i++) {
+               entry = &hw->ksz_switch->mac_table[i];
+               entry->valid = 0;
+               sw_w_sta_mac_table(hw, i,
+                       entry->mac_addr, entry->ports,
+                       entry->override, entry->valid,
+                       entry->use_fid, entry->fid);
+       }
+}
+
+#define PHY_LINK_SUPPORT               \
+       (PHY_AUTO_NEG_ASYM_PAUSE |      \
+       PHY_AUTO_NEG_SYM_PAUSE |        \
+       PHY_AUTO_NEG_100BT4 |           \
+       PHY_AUTO_NEG_100BTX_FD |        \
+       PHY_AUTO_NEG_100BTX |           \
+       PHY_AUTO_NEG_10BT_FD |          \
+       PHY_AUTO_NEG_10BT)
+
+static inline void hw_r_phy_ctrl(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_ctrl(struct ksz_hw *hw, int phy, u16 data)
+{
+       writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_link_stat(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_STATUS_OFFSET);
+}
+
+static inline void hw_r_phy_auto_neg(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
+}
+
+static inline void hw_w_phy_auto_neg(struct ksz_hw *hw, int phy, u16 data)
+{
+       writew(data, hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
+}
+
+static inline void hw_r_phy_rem_cap(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_REMOTE_CAP_OFFSET);
+}
+
+static inline void hw_r_phy_crossover(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_crossover(struct ksz_hw *hw, int phy, u16 data)
+{
+       writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_polarity(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_w_phy_polarity(struct ksz_hw *hw, int phy, u16 data)
+{
+       writew(data, hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
+}
+
+static inline void hw_r_phy_link_md(struct ksz_hw *hw, int phy, u16 *data)
+{
+       *data = readw(hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
+}
+
+static inline void hw_w_phy_link_md(struct ksz_hw *hw, int phy, u16 data)
+{
+       writew(data, hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
+}
+
+/**
+ * hw_r_phy - read data from PHY register
+ * @hw:        The hardware instance.
+ * @port:      Port to read.
+ * @reg:       PHY register to read.
+ * @val:       Buffer to store the read data.
+ *
+ * This routine reads data from the PHY register.
+ */
+static void hw_r_phy(struct ksz_hw *hw, int port, u16 reg, u16 *val)
+{
+       int phy;
+
+       phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
+       *val = readw(hw->io + phy);
+}
+
+/**
+ * port_w_phy - write data to PHY register
+ * @hw:        The hardware instance.
+ * @port:      Port to write.
+ * @reg:       PHY register to write.
+ * @val:       Word data to write.
+ *
+ * This routine writes data to the PHY register.
+ */
+static void hw_w_phy(struct ksz_hw *hw, int port, u16 reg, u16 val)
+{
+       int phy;
+
+       phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
+       writew(val, hw->io + phy);
+}
+
+/*
+ * EEPROM access functions
+ */
+
+#define AT93C_CODE                     0
+#define AT93C_WR_OFF                   0x00
+#define AT93C_WR_ALL                   0x10
+#define AT93C_ER_ALL                   0x20
+#define AT93C_WR_ON                    0x30
+
+#define AT93C_WRITE                    1
+#define AT93C_READ                     2
+#define AT93C_ERASE                    3
+
+#define EEPROM_DELAY                   4
+
+static inline void drop_gpio(struct ksz_hw *hw, u8 gpio)
+{
+       u16 data;
+
+       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+       data &= ~gpio;
+       writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
+}
+
+static inline void raise_gpio(struct ksz_hw *hw, u8 gpio)
+{
+       u16 data;
+
+       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+       data |= gpio;
+       writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
+}
+
+static inline u8 state_gpio(struct ksz_hw *hw, u8 gpio)
+{
+       u16 data;
+
+       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
+       return (u8)(data & gpio);
+}
+
+static void eeprom_clk(struct ksz_hw *hw)
+{
+       raise_gpio(hw, EEPROM_SERIAL_CLOCK);
+       udelay(EEPROM_DELAY);
+       drop_gpio(hw, EEPROM_SERIAL_CLOCK);
+       udelay(EEPROM_DELAY);
+}
+
+static u16 spi_r(struct ksz_hw *hw)
+{
+       int i;
+       u16 temp = 0;
+
+       for (i = 15; i >= 0; i--) {
+               raise_gpio(hw, EEPROM_SERIAL_CLOCK);
+               udelay(EEPROM_DELAY);
+
+               temp |= (state_gpio(hw, EEPROM_DATA_IN)) ? 1 << i : 0;
+
+               drop_gpio(hw, EEPROM_SERIAL_CLOCK);
+               udelay(EEPROM_DELAY);
+       }
+       return temp;
+}
+
+static void spi_w(struct ksz_hw *hw, u16 data)
+{
+       int i;
+
+       for (i = 15; i >= 0; i--) {
+               (data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+                       drop_gpio(hw, EEPROM_DATA_OUT);
+               eeprom_clk(hw);
+       }
+}
+
+static void spi_reg(struct ksz_hw *hw, u8 data, u8 reg)
+{
+       int i;
+
+       /* Initial start bit */
+       raise_gpio(hw, EEPROM_DATA_OUT);
+       eeprom_clk(hw);
+
+       /* AT93C operation */
+       for (i = 1; i >= 0; i--) {
+               (data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+                       drop_gpio(hw, EEPROM_DATA_OUT);
+               eeprom_clk(hw);
+       }
+
+       /* Address location */
+       for (i = 5; i >= 0; i--) {
+               (reg & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
+                       drop_gpio(hw, EEPROM_DATA_OUT);
+               eeprom_clk(hw);
+       }
+}
+
+#define EEPROM_DATA_RESERVED           0
+#define EEPROM_DATA_MAC_ADDR_0         1
+#define EEPROM_DATA_MAC_ADDR_1         2
+#define EEPROM_DATA_MAC_ADDR_2         3
+#define EEPROM_DATA_SUBSYS_ID          4
+#define EEPROM_DATA_SUBSYS_VEN_ID      5
+#define EEPROM_DATA_PM_CAP             6
+
+/* User defined EEPROM data */
+#define EEPROM_DATA_OTHER_MAC_ADDR     9
+
+/**
+ * eeprom_read - read from AT93C46 EEPROM
+ * @hw:        The hardware instance.
+ * @reg:       The register offset.
+ *
+ * This function reads a word from the AT93C46 EEPROM.
+ *
+ * Return the data value.
+ */
+static u16 eeprom_read(struct ksz_hw *hw, u8 reg)
+{
+       u16 data;
+
+       raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+       spi_reg(hw, AT93C_READ, reg);
+       data = spi_r(hw);
+
+       drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+       return data;
+}
+
+/**
+ * eeprom_write - write to AT93C46 EEPROM
+ * @hw:        The hardware instance.
+ * @reg:       The register offset.
+ * @data:      The data value.
+ *
+ * This procedure writes a word to the AT93C46 EEPROM.
+ */
+static void eeprom_write(struct ksz_hw *hw, u8 reg, u16 data)
+{
+       int timeout;
+
+       raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+
+       /* Enable write. */
+       spi_reg(hw, AT93C_CODE, AT93C_WR_ON);
+       drop_gpio(hw, EEPROM_CHIP_SELECT);
+       udelay(1);
+
+       /* Erase the register. */
+       raise_gpio(hw, EEPROM_CHIP_SELECT);
+       spi_reg(hw, AT93C_ERASE, reg);
+       drop_gpio(hw, EEPROM_CHIP_SELECT);
+       udelay(1);
+
+       /* Check operation complete. */
+       raise_gpio(hw, EEPROM_CHIP_SELECT);
+       timeout = 8;
+       mdelay(2);
+       do {
+               mdelay(1);
+       } while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
+       drop_gpio(hw, EEPROM_CHIP_SELECT);
+       udelay(1);
+
+       /* Write the register. */
+       raise_gpio(hw, EEPROM_CHIP_SELECT);
+       spi_reg(hw, AT93C_WRITE, reg);
+       spi_w(hw, data);
+       drop_gpio(hw, EEPROM_CHIP_SELECT);
+       udelay(1);
+
+       /* Check operation complete. */
+       raise_gpio(hw, EEPROM_CHIP_SELECT);
+       timeout = 8;
+       mdelay(2);
+       do {
+               mdelay(1);
+       } while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
+       drop_gpio(hw, EEPROM_CHIP_SELECT);
+       udelay(1);
+
+       /* Disable write. */
+       raise_gpio(hw, EEPROM_CHIP_SELECT);
+       spi_reg(hw, AT93C_CODE, AT93C_WR_OFF);
+
+       drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
+}
+
+/*
+ * Link detection routines
+ */
+
+static u16 advertised_flow_ctrl(struct ksz_port *port, u16 ctrl)
+{
+       ctrl &= ~PORT_AUTO_NEG_SYM_PAUSE;
+       switch (port->flow_ctrl) {
+       case PHY_FLOW_CTRL:
+               ctrl |= PORT_AUTO_NEG_SYM_PAUSE;
+               break;
+       /* Not supported. */
+       case PHY_TX_ONLY:
+       case PHY_RX_ONLY:
+       default:
+               break;
+       }
+       return ctrl;
+}
+
+static void set_flow_ctrl(struct ksz_hw *hw, int rx, int tx)
+{
+       u32 rx_cfg;
+       u32 tx_cfg;
+
+       rx_cfg = hw->rx_cfg;
+       tx_cfg = hw->tx_cfg;
+       if (rx)
+               hw->rx_cfg |= DMA_RX_FLOW_ENABLE;
+       else
+               hw->rx_cfg &= ~DMA_RX_FLOW_ENABLE;
+       if (tx)
+               hw->tx_cfg |= DMA_TX_FLOW_ENABLE;
+       else
+               hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
+       if (hw->enabled) {
+               if (rx_cfg != hw->rx_cfg)
+                       writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+               if (tx_cfg != hw->tx_cfg)
+                       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+       }
+}
+
+static void determine_flow_ctrl(struct ksz_hw *hw, struct ksz_port *port,
+       u16 local, u16 remote)
+{
+       int rx;
+       int tx;
+
+       if (hw->overrides & PAUSE_FLOW_CTRL)
+               return;
+
+       rx = tx = 0;
+       if (port->force_link)
+               rx = tx = 1;
+       if (remote & PHY_AUTO_NEG_SYM_PAUSE) {
+               if (local & PHY_AUTO_NEG_SYM_PAUSE) {
+                       rx = tx = 1;
+               } else if ((remote & PHY_AUTO_NEG_ASYM_PAUSE) &&
+                               (local & PHY_AUTO_NEG_PAUSE) ==
+                               PHY_AUTO_NEG_ASYM_PAUSE) {
+                       tx = 1;
+               }
+       } else if (remote & PHY_AUTO_NEG_ASYM_PAUSE) {
+               if ((local & PHY_AUTO_NEG_PAUSE) == PHY_AUTO_NEG_PAUSE)
+                       rx = 1;
+       }
+       if (!hw->ksz_switch)
+               set_flow_ctrl(hw, rx, tx);
+}
+
+static inline void port_cfg_change(struct ksz_hw *hw, struct ksz_port *port,
+       struct ksz_port_info *info, u16 link_status)
+{
+       if ((hw->features & HALF_DUPLEX_SIGNAL_BUG) &&
+                       !(hw->overrides & PAUSE_FLOW_CTRL)) {
+               u32 cfg = hw->tx_cfg;
+
+               /* Disable flow control in the half duplex mode. */
+               if (1 == info->duplex)
+                       hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
+               if (hw->enabled && cfg != hw->tx_cfg)
+                       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+       }
+}
+
+/**
+ * port_get_link_speed - get current link status
+ * @port:      The port instance.
+ *
+ * This routine reads PHY registers to determine the current link status of the
+ * switch ports.
+ */
+static void port_get_link_speed(struct ksz_port *port)
+{
+       uint interrupt;
+       struct ksz_port_info *info;
+       struct ksz_port_info *linked = NULL;
+       struct ksz_hw *hw = port->hw;
+       u16 data;
+       u16 status;
+       u8 local;
+       u8 remote;
+       int i;
+       int p;
+       int change = 0;
+
+       interrupt = hw_block_intr(hw);
+
+       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+               info = &hw->port_info[p];
+               port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
+               port_r16(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
+
+               /*
+                * Link status is changing all the time even when there is no
+                * cable connection!
+                */
+               remote = status & (PORT_AUTO_NEG_COMPLETE |
+                       PORT_STATUS_LINK_GOOD);
+               local = (u8) data;
+
+               /* No change to status. */
+               if (local == info->advertised && remote == info->partner)
+                       continue;
+
+               info->advertised = local;
+               info->partner = remote;
+               if (status & PORT_STATUS_LINK_GOOD) {
+
+                       /* Remember the first linked port. */
+                       if (!linked)
+                               linked = info;
+
+                       info->tx_rate = 10 * TX_RATE_UNIT;
+                       if (status & PORT_STATUS_SPEED_100MBIT)
+                               info->tx_rate = 100 * TX_RATE_UNIT;
+
+                       info->duplex = 1;
+                       if (status & PORT_STATUS_FULL_DUPLEX)
+                               info->duplex = 2;
+
+                       if (media_connected != info->state) {
+                               hw_r_phy(hw, p, KS884X_PHY_AUTO_NEG_OFFSET,
+                                       &data);
+                               hw_r_phy(hw, p, KS884X_PHY_REMOTE_CAP_OFFSET,
+                                       &status);
+                               determine_flow_ctrl(hw, port, data, status);
+                               if (hw->ksz_switch) {
+                                       port_cfg_back_pressure(hw, p,
+                                               (1 == info->duplex));
+                               }
+                               change |= 1 << i;
+                               port_cfg_change(hw, port, info, status);
+                       }
+                       info->state = media_connected;
+               } else {
+                       if (media_disconnected != info->state) {
+                               change |= 1 << i;
+
+                               /* Indicate the link just goes down. */
+                               hw->port_mib[p].link_down = 1;
+                       }
+                       info->state = media_disconnected;
+               }
+               hw->port_mib[p].state = (u8) info->state;
+       }
+
+       if (linked && media_disconnected == port->linked->state)
+               port->linked = linked;
+
+       hw_restore_intr(hw, interrupt);
+}
+
+#define PHY_RESET_TIMEOUT              10
+
+/**
+ * port_set_link_speed - set port speed
+ * @port:      The port instance.
+ *
+ * This routine sets the link speed of the switch ports.
+ */
+static void port_set_link_speed(struct ksz_port *port)
+{
+       struct ksz_port_info *info;
+       struct ksz_hw *hw = port->hw;
+       u16 data;
+       u16 cfg;
+       u8 status;
+       int i;
+       int p;
+
+       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+               info = &hw->port_info[p];
+
+               port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
+               port_r8(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
+
+               cfg = 0;
+               if (status & PORT_STATUS_LINK_GOOD)
+                       cfg = data;
+
+               data |= PORT_AUTO_NEG_ENABLE;
+               data = advertised_flow_ctrl(port, data);
+
+               data |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |
+                       PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;
+
+               /* Check if manual configuration is specified by the user. */
+               if (port->speed || port->duplex) {
+                       if (10 == port->speed)
+                               data &= ~(PORT_AUTO_NEG_100BTX_FD |
+                                       PORT_AUTO_NEG_100BTX);
+                       else if (100 == port->speed)
+                               data &= ~(PORT_AUTO_NEG_10BT_FD |
+                                       PORT_AUTO_NEG_10BT);
+                       if (1 == port->duplex)
+                               data &= ~(PORT_AUTO_NEG_100BTX_FD |
+                                       PORT_AUTO_NEG_10BT_FD);
+                       else if (2 == port->duplex)
+                               data &= ~(PORT_AUTO_NEG_100BTX |
+                                       PORT_AUTO_NEG_10BT);
+               }
+               if (data != cfg) {
+                       data |= PORT_AUTO_NEG_RESTART;
+                       port_w16(hw, p, KS884X_PORT_CTRL_4_OFFSET, data);
+               }
+       }
+}
+
+/**
+ * port_force_link_speed - force port speed
+ * @port:      The port instance.
+ *
+ * This routine forces the link speed of the switch ports.
+ */
+static void port_force_link_speed(struct ksz_port *port)
+{
+       struct ksz_hw *hw = port->hw;
+       u16 data;
+       int i;
+       int phy;
+       int p;
+
+       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+               phy = KS884X_PHY_1_CTRL_OFFSET + p * PHY_CTRL_INTERVAL;
+               hw_r_phy_ctrl(hw, phy, &data);
+
+               data &= ~PHY_AUTO_NEG_ENABLE;
+
+               if (10 == port->speed)
+                       data &= ~PHY_SPEED_100MBIT;
+               else if (100 == port->speed)
+                       data |= PHY_SPEED_100MBIT;
+               if (1 == port->duplex)
+                       data &= ~PHY_FULL_DUPLEX;
+               else if (2 == port->duplex)
+                       data |= PHY_FULL_DUPLEX;
+               hw_w_phy_ctrl(hw, phy, data);
+       }
+}
+
+static void port_set_power_saving(struct ksz_port *port, int enable)
+{
+       struct ksz_hw *hw = port->hw;
+       int i;
+       int p;
+
+       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++)
+               port_cfg(hw, p,
+                       KS884X_PORT_CTRL_4_OFFSET, PORT_POWER_DOWN, enable);
+}
+
+/*
+ * KSZ8841 power management functions
+ */
+
+/**
+ * hw_chk_wol_pme_status - check PMEN pin
+ * @hw:        The hardware instance.
+ *
+ * This function is used to check PMEN pin is asserted.
+ *
+ * Return 1 if PMEN pin is asserted; otherwise, 0.
+ */
+static int hw_chk_wol_pme_status(struct ksz_hw *hw)
+{
+       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+       struct pci_dev *pdev = hw_priv->pdev;
+       u16 data;
+
+       if (!pdev->pm_cap)
+               return 0;
+       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+       return (data & PCI_PM_CTRL_PME_STATUS) == PCI_PM_CTRL_PME_STATUS;
+}
+
+/**
+ * hw_clr_wol_pme_status - clear PMEN pin
+ * @hw:        The hardware instance.
+ *
+ * This routine is used to clear PME_Status to deassert PMEN pin.
+ */
+static void hw_clr_wol_pme_status(struct ksz_hw *hw)
+{
+       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+       struct pci_dev *pdev = hw_priv->pdev;
+       u16 data;
+
+       if (!pdev->pm_cap)
+               return;
+
+       /* Clear PME_Status to deassert PMEN pin. */
+       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+       data |= PCI_PM_CTRL_PME_STATUS;
+       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
+}
+
+/**
+ * hw_cfg_wol_pme - enable or disable Wake-on-LAN
+ * @hw:        The hardware instance.
+ * @set:       The flag indicating whether to enable or disable.
+ *
+ * This routine is used to enable or disable Wake-on-LAN.
+ */
+static void hw_cfg_wol_pme(struct ksz_hw *hw, int set)
+{
+       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
+       struct pci_dev *pdev = hw_priv->pdev;
+       u16 data;
+
+       if (!pdev->pm_cap)
+               return;
+       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
+       data &= ~PCI_PM_CTRL_STATE_MASK;
+       if (set)
+               data |= PCI_PM_CTRL_PME_ENABLE | PCI_D3hot;
+       else
+               data &= ~PCI_PM_CTRL_PME_ENABLE;
+       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
+}
+
+/**
+ * hw_cfg_wol - configure Wake-on-LAN features
+ * @hw:        The hardware instance.
+ * @frame:     The pattern frame bit.
+ * @set:       The flag indicating whether to enable or disable.
+ *
+ * This routine is used to enable or disable certain Wake-on-LAN features.
+ */
+static void hw_cfg_wol(struct ksz_hw *hw, u16 frame, int set)
+{
+       u16 data;
+
+       data = readw(hw->io + KS8841_WOL_CTRL_OFFSET);
+       if (set)
+               data |= frame;
+       else
+               data &= ~frame;
+       writew(data, hw->io + KS8841_WOL_CTRL_OFFSET);
+}
+
+/**
+ * hw_set_wol_frame - program Wake-on-LAN pattern
+ * @hw:        The hardware instance.
+ * @i:         The frame index.
+ * @mask_size: The size of the mask.
+ * @mask:      Mask to ignore certain bytes in the pattern.
+ * @frame_size:        The size of the frame.
+ * @pattern:   The frame data.
+ *
+ * This routine is used to program Wake-on-LAN pattern.
+ */
+static void hw_set_wol_frame(struct ksz_hw *hw, int i, uint mask_size,
+       const u8 *mask, uint frame_size, const u8 *pattern)
+{
+       int bits;
+       int from;
+       int len;
+       int to;
+       u32 crc;
+       u8 data[64];
+       u8 val = 0;
+
+       if (frame_size > mask_size * 8)
+               frame_size = mask_size * 8;
+       if (frame_size > 64)
+               frame_size = 64;
+
+       i *= 0x10;
+       writel(0, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i);
+       writel(0, hw->io + KS8841_WOL_FRAME_BYTE2_OFFSET + i);
+
+       bits = len = from = to = 0;
+       do {
+               if (bits) {
+                       if ((val & 1))
+                               data[to++] = pattern[from];
+                       val >>= 1;
+                       ++from;
+                       --bits;
+               } else {
+                       val = mask[len];
+                       writeb(val, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i
+                               + len);
+                       ++len;
+                       if (val)
+                               bits = 8;
+                       else
+                               from += 8;
+               }
+       } while (from < (int) frame_size);
+       if (val) {
+               bits = mask[len - 1];
+               val <<= (from % 8);
+               bits &= ~val;
+               writeb(bits, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i + len -
+                       1);
+       }
+       crc = ether_crc(to, data);
+       writel(crc, hw->io + KS8841_WOL_FRAME_CRC_OFFSET + i);
+}
+
+/**
+ * hw_add_wol_arp - add ARP pattern
+ * @hw:        The hardware instance.
+ * @ip_addr:   The IPv4 address assigned to the device.
+ *
+ * This routine is used to add ARP pattern for waking up the host.
+ */
+static void hw_add_wol_arp(struct ksz_hw *hw, const u8 *ip_addr)
+{
+       static const u8 mask[6] = { 0x3F, 0xF0, 0x3F, 0x00, 0xC0, 0x03 };
+       u8 pattern[42] = {
+               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x08, 0x06,
+               0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00 };
+
+       memcpy(&pattern[38], ip_addr, 4);
+       hw_set_wol_frame(hw, 3, 6, mask, 42, pattern);
+}
+
+/**
+ * hw_add_wol_bcast - add broadcast pattern
+ * @hw:        The hardware instance.
+ *
+ * This routine is used to add broadcast pattern for waking up the host.
+ */
+static void hw_add_wol_bcast(struct ksz_hw *hw)
+{
+       static const u8 mask[] = { 0x3F };
+       static const u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+       hw_set_wol_frame(hw, 2, 1, mask, MAC_ADDR_LEN, pattern);
+}
+
+/**
+ * hw_add_wol_mcast - add multicast pattern
+ * @hw:        The hardware instance.
+ *
+ * This routine is used to add multicast pattern for waking up the host.
+ *
+ * It is assumed the multicast packet is the ICMPv6 neighbor solicitation used
+ * by IPv6 ping command.  Note that multicast packets are filtred through the
+ * multicast hash table, so not all multicast packets can wake up the host.
+ */
+static void hw_add_wol_mcast(struct ksz_hw *hw)
+{
+       static const u8 mask[] = { 0x3F };
+       u8 pattern[] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
+
+       memcpy(&pattern[3], &hw->override_addr[3], 3);
+       hw_set_wol_frame(hw, 1, 1, mask, 6, pattern);
+}
+
+/**
+ * hw_add_wol_ucast - add unicast pattern
+ * @hw:        The hardware instance.
+ *
+ * This routine is used to add unicast pattern to wakeup the host.
+ *
+ * It is assumed the unicast packet is directed to the device, as the hardware
+ * can only receive them in normal case.
+ */
+static void hw_add_wol_ucast(struct ksz_hw *hw)
+{
+       static const u8 mask[] = { 0x3F };
+
+       hw_set_wol_frame(hw, 0, 1, mask, MAC_ADDR_LEN, hw->override_addr);
+}
+
+/**
+ * hw_enable_wol - enable Wake-on-LAN
+ * @hw:        The hardware instance.
+ * @wol_enable:        The Wake-on-LAN settings.
+ * @net_addr:  The IPv4 address assigned to the device.
+ *
+ * This routine is used to enable Wake-on-LAN depending on driver settings.
+ */
+static void hw_enable_wol(struct ksz_hw *hw, u32 wol_enable, const u8 *net_addr)
+{
+       hw_cfg_wol(hw, KS8841_WOL_MAGIC_ENABLE, (wol_enable & WAKE_MAGIC));
+       hw_cfg_wol(hw, KS8841_WOL_FRAME0_ENABLE, (wol_enable & WAKE_UCAST));
+       hw_add_wol_ucast(hw);
+       hw_cfg_wol(hw, KS8841_WOL_FRAME1_ENABLE, (wol_enable & WAKE_MCAST));
+       hw_add_wol_mcast(hw);
+       hw_cfg_wol(hw, KS8841_WOL_FRAME2_ENABLE, (wol_enable & WAKE_BCAST));
+       hw_cfg_wol(hw, KS8841_WOL_FRAME3_ENABLE, (wol_enable & WAKE_ARP));
+       hw_add_wol_arp(hw, net_addr);
+}
+
+/**
+ * hw_init - check driver is correct for the hardware
+ * @hw:        The hardware instance.
+ *
+ * This function checks the hardware is correct for this driver and sets the
+ * hardware up for proper initialization.
+ *
+ * Return number of ports or 0 if not right.
+ */
+static int hw_init(struct ksz_hw *hw)
+{
+       int rc = 0;
+       u16 data;
+       u16 revision;
+
+       /* Set bus speed to 125MHz. */
+       writew(BUS_SPEED_125_MHZ, hw->io + KS884X_BUS_CTRL_OFFSET);
+
+       /* Check KSZ884x chip ID. */
+       data = readw(hw->io + KS884X_CHIP_ID_OFFSET);
+
+       revision = (data & KS884X_REVISION_MASK) >> KS884X_REVISION_SHIFT;
+       data &= KS884X_CHIP_ID_MASK_41;
+       if (REG_CHIP_ID_41 == data)
+               rc = 1;
+       else if (REG_CHIP_ID_42 == data)
+               rc = 2;
+       else
+               return 0;
+
+       /* Setup hardware features or bug workarounds. */
+       if (revision <= 1) {
+               hw->features |= SMALL_PACKET_TX_BUG;
+               if (1 == rc)
+                       hw->features |= HALF_DUPLEX_SIGNAL_BUG;
+       }
+       return rc;
+}
+
+/**
+ * hw_reset - reset the hardware
+ * @hw:        The hardware instance.
+ *
+ * This routine resets the hardware.
+ */
+static void hw_reset(struct ksz_hw *hw)
+{
+       writew(GLOBAL_SOFTWARE_RESET, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
+
+       /* Wait for device to reset. */
+       mdelay(10);
+
+       /* Write 0 to clear device reset. */
+       writew(0, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
+}
+
+/**
+ * hw_setup - setup the hardware
+ * @hw:        The hardware instance.
+ *
+ * This routine setup the hardware for proper operation.
+ */
+static void hw_setup(struct ksz_hw *hw)
+{
+#if SET_DEFAULT_LED
+       u16 data;
+
+       /* Change default LED mode. */
+       data = readw(hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
+       data &= ~LED_MODE;
+       data |= SET_DEFAULT_LED;
+       writew(data, hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
+#endif
+
+       /* Setup transmit control. */
+       hw->tx_cfg = (DMA_TX_PAD_ENABLE | DMA_TX_CRC_ENABLE |
+               (DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_TX_ENABLE);
+
+       /* Setup receive control. */
+       hw->rx_cfg = (DMA_RX_BROADCAST | DMA_RX_UNICAST |
+               (DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_RX_ENABLE);
+       hw->rx_cfg |= KS884X_DMA_RX_MULTICAST;
+
+       /* Hardware cannot handle UDP packet in IP fragments. */
+       hw->rx_cfg |= (DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
+
+       if (hw->all_multi)
+               hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
+       if (hw->promiscuous)
+               hw->rx_cfg |= DMA_RX_PROMISCUOUS;
+}
+
+/**
+ * hw_setup_intr - setup interrupt mask
+ * @hw:        The hardware instance.
+ *
+ * This routine setup the interrupt mask for proper operation.
+ */
+static void hw_setup_intr(struct ksz_hw *hw)
+{
+       hw->intr_mask = KS884X_INT_MASK | KS884X_INT_RX_OVERRUN;
+}
+
+static void ksz_check_desc_num(struct ksz_desc_info *info)
+{
+#define MIN_DESC_SHIFT  2
+
+       int alloc = info->alloc;
+       int shift;
+
+       shift = 0;
+       while (!(alloc & 1)) {
+               shift++;
+               alloc >>= 1;
+       }
+       if (alloc != 1 || shift < MIN_DESC_SHIFT) {
+               pr_alert("Hardware descriptor numbers not right!\n");
+               while (alloc) {
+                       shift++;
+                       alloc >>= 1;
+               }
+               if (shift < MIN_DESC_SHIFT)
+                       shift = MIN_DESC_SHIFT;
+               alloc = 1 << shift;
+               info->alloc = alloc;
+       }
+       info->mask = info->alloc - 1;
+}
+
+static void hw_init_desc(struct ksz_desc_info *desc_info, int transmit)
+{
+       int i;
+       u32 phys = desc_info->ring_phys;
+       struct ksz_hw_desc *desc = desc_info->ring_virt;
+       struct ksz_desc *cur = desc_info->ring;
+       struct ksz_desc *previous = NULL;
+
+       for (i = 0; i < desc_info->alloc; i++) {
+               cur->phw = desc++;
+               phys += desc_info->size;
+               previous = cur++;
+               previous->phw->next = cpu_to_le32(phys);
+       }
+       previous->phw->next = cpu_to_le32(desc_info->ring_phys);
+       previous->sw.buf.rx.end_of_ring = 1;
+       previous->phw->buf.data = cpu_to_le32(previous->sw.buf.data);
+
+       desc_info->avail = desc_info->alloc;
+       desc_info->last = desc_info->next = 0;
+
+       desc_info->cur = desc_info->ring;
+}
+
+/**
+ * hw_set_desc_base - set descriptor base addresses
+ * @hw:        The hardware instance.
+ * @tx_addr:   The transmit descriptor base.
+ * @rx_addr:   The receive descriptor base.
+ *
+ * This routine programs the descriptor base addresses after reset.
+ */
+static void hw_set_desc_base(struct ksz_hw *hw, u32 tx_addr, u32 rx_addr)
+{
+       /* Set base address of Tx/Rx descriptors. */
+       writel(tx_addr, hw->io + KS_DMA_TX_ADDR);
+       writel(rx_addr, hw->io + KS_DMA_RX_ADDR);
+}
+
+static void hw_reset_pkts(struct ksz_desc_info *info)
+{
+       info->cur = info->ring;
+       info->avail = info->alloc;
+       info->last = info->next = 0;
+}
+
+static inline void hw_resume_rx(struct ksz_hw *hw)
+{
+       writel(DMA_START, hw->io + KS_DMA_RX_START);
+}
+
+/**
+ * hw_start_rx - start receiving
+ * @hw:        The hardware instance.
+ *
+ * This routine starts the receive function of the hardware.
+ */
+static void hw_start_rx(struct ksz_hw *hw)
+{
+       writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+
+       /* Notify when the receive stops. */
+       hw->intr_mask |= KS884X_INT_RX_STOPPED;
+
+       writel(DMA_START, hw->io + KS_DMA_RX_START);
+       hw_ack_intr(hw, KS884X_INT_RX_STOPPED);
+       hw->rx_stop++;
+
+       /* Variable overflows. */
+       if (0 == hw->rx_stop)
+               hw->rx_stop = 2;
+}
+
+/*
+ * hw_stop_rx - stop receiving
+ * @hw:        The hardware instance.
+ *
+ * This routine stops the receive function of the hardware.
+ */
+static void hw_stop_rx(struct ksz_hw *hw)
+{
+       hw->rx_stop = 0;
+       hw_turn_off_intr(hw, KS884X_INT_RX_STOPPED);
+       writel((hw->rx_cfg & ~DMA_RX_ENABLE), hw->io + KS_DMA_RX_CTRL);
+}
+
+/**
+ * hw_start_tx - start transmitting
+ * @hw:        The hardware instance.
+ *
+ * This routine starts the transmit function of the hardware.
+ */
+static void hw_start_tx(struct ksz_hw *hw)
+{
+       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
+}
+
+/**
+ * hw_stop_tx - stop transmitting
+ * @hw:        The hardware instance.
+ *
+ * This routine stops the transmit function of the hardware.
+ */
+static void hw_stop_tx(struct ksz_hw *hw)
+{
+       writel((hw->tx_cfg & ~DMA_TX_ENABLE), hw->io + KS_DMA_TX_CTRL);
+}
+
+/**
+ * hw_disable - disable hardware
+ * @hw:        The hardware instance.
+ *
+ * This routine disables the hardware.
+ */
+static void hw_disable(struct ksz_hw *hw)
+{
+       hw_stop_rx(hw);
+       hw_stop_tx(hw);
+       hw->enabled = 0;
+}
+
+/**
+ * hw_enable - enable hardware
+ * @hw:        The hardware instance.
+ *
+ * This routine enables the hardware.
+ */
+static void hw_enable(struct ksz_hw *hw)
+{
+       hw_start_tx(hw);
+       hw_start_rx(hw);
+       hw->enabled = 1;
+}
+
+/**
+ * hw_alloc_pkt - allocate enough descriptors for transmission
+ * @hw:        The hardware instance.
+ * @length:    The length of the packet.
+ * @physical:  Number of descriptors required.
+ *
+ * This function allocates descriptors for transmission.
+ *
+ * Return 0 if not successful; 1 for buffer copy; or number of descriptors.
+ */
+static int hw_alloc_pkt(struct ksz_hw *hw, int length, int physical)
+{
+       /* Always leave one descriptor free. */
+       if (hw->tx_desc_info.avail <= 1)
+               return 0;
+
+       /* Allocate a descriptor for transmission and mark it current. */
+       get_tx_pkt(&hw->tx_desc_info, &hw->tx_desc_info.cur);
+       hw->tx_desc_info.cur->sw.buf.tx.first_seg = 1;
+
+       /* Keep track of number of transmit descriptors used so far. */
+       ++hw->tx_int_cnt;
+       hw->tx_size += length;
+
+       /* Cannot hold on too much data. */
+       if (hw->tx_size >= MAX_TX_HELD_SIZE)
+               hw->tx_int_cnt = hw->tx_int_mask + 1;
+
+       if (physical > hw->tx_desc_info.avail)
+               return 1;
+
+       return hw->tx_desc_info.avail;
+}
+
+/**
+ * hw_send_pkt - mark packet for transmission
+ * @hw:        The hardware instance.
+ *
+ * This routine marks the packet for transmission in PCI version.
+ */
+static void hw_send_pkt(struct ksz_hw *hw)
+{
+       struct ksz_desc *cur = hw->tx_desc_info.cur;
+
+       cur->sw.buf.tx.last_seg = 1;
+
+       /* Interrupt only after specified number of descriptors used. */
+       if (hw->tx_int_cnt > hw->tx_int_mask) {
+               cur->sw.buf.tx.intr = 1;
+               hw->tx_int_cnt = 0;
+               hw->tx_size = 0;
+       }
+
+       /* KSZ8842 supports port directed transmission. */
+       cur->sw.buf.tx.dest_port = hw->dst_ports;
+
+       release_desc(cur);
+
+       writel(0, hw->io + KS_DMA_TX_START);
+}
+
+static int empty_addr(u8 *addr)
+{
+       u32 *addr1 = (u32 *) addr;
+       u16 *addr2 = (u16 *) &addr[4];
+
+       return 0 == *addr1 && 0 == *addr2;
+}
+
+/**
+ * hw_set_addr - set MAC address
+ * @hw:        The hardware instance.
+ *
+ * This routine programs the MAC address of the hardware when the address is
+ * overrided.
+ */
+static void hw_set_addr(struct ksz_hw *hw)
+{
+       int i;
+
+       for (i = 0; i < MAC_ADDR_LEN; i++)
+               writeb(hw->override_addr[MAC_ADDR_ORDER(i)],
+                       hw->io + KS884X_ADDR_0_OFFSET + i);
+
+       sw_set_addr(hw, hw->override_addr);
+}
+
+/**
+ * hw_read_addr - read MAC address
+ * @hw:        The hardware instance.
+ *
+ * This routine retrieves the MAC address of the hardware.
+ */
+static void hw_read_addr(struct ksz_hw *hw)
+{
+       int i;
+
+       for (i = 0; i < MAC_ADDR_LEN; i++)
+               hw->perm_addr[MAC_ADDR_ORDER(i)] = readb(hw->io +
+                       KS884X_ADDR_0_OFFSET + i);
+
+       if (!hw->mac_override) {
+               memcpy(hw->override_addr, hw->perm_addr, MAC_ADDR_LEN);
+               if (empty_addr(hw->override_addr)) {
+                       memcpy(hw->perm_addr, DEFAULT_MAC_ADDRESS,
+                               MAC_ADDR_LEN);
+                       memcpy(hw->override_addr, DEFAULT_MAC_ADDRESS,
+                               MAC_ADDR_LEN);
+                       hw->override_addr[5] += hw->id;
+                       hw_set_addr(hw);
+               }
+       }
+}
+
+static void hw_ena_add_addr(struct ksz_hw *hw, int index, u8 *mac_addr)
+{
+       int i;
+       u32 mac_addr_lo;
+       u32 mac_addr_hi;
+
+       mac_addr_hi = 0;
+       for (i = 0; i < 2; i++) {
+               mac_addr_hi <<= 8;
+               mac_addr_hi |= mac_addr[i];
+       }
+       mac_addr_hi |= ADD_ADDR_ENABLE;
+       mac_addr_lo = 0;
+       for (i = 2; i < 6; i++) {
+               mac_addr_lo <<= 8;
+               mac_addr_lo |= mac_addr[i];
+       }
+       index *= ADD_ADDR_INCR;
+
+       writel(mac_addr_lo, hw->io + index + KS_ADD_ADDR_0_LO);
+       writel(mac_addr_hi, hw->io + index + KS_ADD_ADDR_0_HI);
+}
+
+static void hw_set_add_addr(struct ksz_hw *hw)
+{
+       int i;
+
+       for (i = 0; i < ADDITIONAL_ENTRIES; i++) {
+               if (empty_addr(hw->address[i]))
+                       writel(0, hw->io + ADD_ADDR_INCR * i +
+                               KS_ADD_ADDR_0_HI);
+               else
+                       hw_ena_add_addr(hw, i, hw->address[i]);
+       }
+}
+
+static int hw_add_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+       int i;
+       int j = ADDITIONAL_ENTRIES;
+
+       if (!memcmp(hw->override_addr, mac_addr, MAC_ADDR_LEN))
+               return 0;
+       for (i = 0; i < hw->addr_list_size; i++) {
+               if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN))
+                       return 0;
+               if (ADDITIONAL_ENTRIES == j && empty_addr(hw->address[i]))
+                       j = i;
+       }
+       if (j < ADDITIONAL_ENTRIES) {
+               memcpy(hw->address[j], mac_addr, MAC_ADDR_LEN);
+               hw_ena_add_addr(hw, j, hw->address[j]);
+               return 0;
+       }
+       return -1;
+}
+
+static int hw_del_addr(struct ksz_hw *hw, u8 *mac_addr)
+{
+       int i;
+
+       for (i = 0; i < hw->addr_list_size; i++) {
+               if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN)) {
+                       memset(hw->address[i], 0, MAC_ADDR_LEN);
+                       writel(0, hw->io + ADD_ADDR_INCR * i +
+                               KS_ADD_ADDR_0_HI);
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+/**
+ * hw_clr_multicast - clear multicast addresses
+ * @hw:        The hardware instance.
+ *
+ * This routine removes all multicast addresses set in the hardware.
+ */
+static void hw_clr_multicast(struct ksz_hw *hw)
+{
+       int i;
+
+       for (i = 0; i < HW_MULTICAST_SIZE; i++) {
+               hw->multi_bits[i] = 0;
+
+               writeb(0, hw->io + KS884X_MULTICAST_0_OFFSET + i);
+       }
+}
+
+/**
+ * hw_set_grp_addr - set multicast addresses
+ * @hw:        The hardware instance.
+ *
+ * This routine programs multicast addresses for the hardware to accept those
+ * addresses.
+ */
+static void hw_set_grp_addr(struct ksz_hw *hw)
+{
+       int i;
+       int index;
+       int position;
+       int value;
+
+       memset(hw->multi_bits, 0, sizeof(u8) * HW_MULTICAST_SIZE);
+
+       for (i = 0; i < hw->multi_list_size; i++) {
+               position = (ether_crc(6, hw->multi_list[i]) >> 26) & 0x3f;
+               index = position >> 3;
+               value = 1 << (position & 7);
+               hw->multi_bits[index] |= (u8) value;
+       }
+
+       for (i = 0; i < HW_MULTICAST_SIZE; i++)
+               writeb(hw->multi_bits[i], hw->io + KS884X_MULTICAST_0_OFFSET +
+                       i);
+}
+
+/**
+ * hw_set_multicast - enable or disable all multicast receiving
+ * @hw:        The hardware instance.
+ * @multicast: To turn on or off the all multicast feature.
+ *
+ * This routine enables/disables the hardware to accept all multicast packets.
+ */
+static void hw_set_multicast(struct ksz_hw *hw, u8 multicast)
+{
+       /* Stop receiving for reconfiguration. */
+       hw_stop_rx(hw);
+
+       if (multicast)
+               hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
+       else
+               hw->rx_cfg &= ~DMA_RX_ALL_MULTICAST;
+
+       if (hw->enabled)
+               hw_start_rx(hw);
+}
+
+/**
+ * hw_set_promiscuous - enable or disable promiscuous receiving
+ * @hw:        The hardware instance.
+ * @prom:      To turn on or off the promiscuous feature.
+ *
+ * This routine enables/disables the hardware to accept all packets.
+ */
+static void hw_set_promiscuous(struct ksz_hw *hw, u8 prom)
+{
+       /* Stop receiving for reconfiguration. */
+       hw_stop_rx(hw);
+
+       if (prom)
+               hw->rx_cfg |= DMA_RX_PROMISCUOUS;
+       else
+               hw->rx_cfg &= ~DMA_RX_PROMISCUOUS;
+
+       if (hw->enabled)
+               hw_start_rx(hw);
+}
+
+/**
+ * sw_enable - enable the switch
+ * @hw:        The hardware instance.
+ * @enable:    The flag to enable or disable the switch
+ *
+ * This routine is used to enable/disable the switch in KSZ8842.
+ */
+static void sw_enable(struct ksz_hw *hw, int enable)
+{
+       int port;
+
+       for (port = 0; port < SWITCH_PORT_NUM; port++) {
+               if (hw->dev_count > 1) {
+                       /* Set port-base vlan membership with host port. */
+                       sw_cfg_port_base_vlan(hw, port,
+                               HOST_MASK | (1 << port));
+                       port_set_stp_state(hw, port, STP_STATE_DISABLED);
+               } else {
+                       sw_cfg_port_base_vlan(hw, port, PORT_MASK);
+                       port_set_stp_state(hw, port, STP_STATE_FORWARDING);
+               }
+       }
+       if (hw->dev_count > 1)
+               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
+       else
+               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_FORWARDING);
+
+       if (enable)
+               enable = KS8842_START;
+       writew(enable, hw->io + KS884X_CHIP_ID_OFFSET);
+}
+
+/**
+ * sw_setup - setup the switch
+ * @hw:        The hardware instance.
+ *
+ * This routine setup the hardware switch engine for default operation.
+ */
+static void sw_setup(struct ksz_hw *hw)
+{
+       int port;
+
+       sw_set_global_ctrl(hw);
+
+       /* Enable switch broadcast storm protection at 10% percent rate. */
+       sw_init_broad_storm(hw);
+       hw_cfg_broad_storm(hw, BROADCAST_STORM_PROTECTION_RATE);
+       for (port = 0; port < SWITCH_PORT_NUM; port++)
+               sw_ena_broad_storm(hw, port);
+
+       sw_init_prio(hw);
+
+       sw_init_mirror(hw);
+
+       sw_init_prio_rate(hw);
+
+       sw_init_vlan(hw);
+
+       if (hw->features & STP_SUPPORT)
+               sw_init_stp(hw);
+       if (!sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                       SWITCH_TX_FLOW_CTRL | SWITCH_RX_FLOW_CTRL))
+               hw->overrides |= PAUSE_FLOW_CTRL;
+       sw_enable(hw, 1);
+}
+
+/**
+ * ksz_start_timer - start kernel timer
+ * @info:      Kernel timer information.
+ * @time:      The time tick.
+ *
+ * This routine starts the kernel timer after the specified time tick.
+ */
+static void ksz_start_timer(struct ksz_timer_info *info, int time)
+{
+       info->cnt = 0;
+       info->timer.expires = jiffies + time;
+       add_timer(&info->timer);
+
+       /* infinity */
+       info->max = -1;
+}
+
+/**
+ * ksz_stop_timer - stop kernel timer
+ * @info:      Kernel timer information.
+ *
+ * This routine stops the kernel timer.
+ */
+static void ksz_stop_timer(struct ksz_timer_info *info)
+{
+       if (info->max) {
+               info->max = 0;
+               del_timer_sync(&info->timer);
+       }
+}
+
+static void ksz_init_timer(struct ksz_timer_info *info, int period,
+       void (*function)(unsigned long), void *data)
+{
+       info->max = 0;
+       info->period = period;
+       init_timer(&info->timer);
+       info->timer.function = function;
+       info->timer.data = (unsigned long) data;
+}
+
+static void ksz_update_timer(struct ksz_timer_info *info)
+{
+       ++info->cnt;
+       if (info->max > 0) {
+               if (info->cnt < info->max) {
+                       info->timer.expires = jiffies + info->period;
+                       add_timer(&info->timer);
+               } else
+                       info->max = 0;
+       } else if (info->max < 0) {
+               info->timer.expires = jiffies + info->period;
+               add_timer(&info->timer);
+       }
+}
+
+/**
+ * ksz_alloc_soft_desc - allocate software descriptors
+ * @desc_info: Descriptor information structure.
+ * @transmit:  Indication that descriptors are for transmit.
+ *
+ * This local function allocates software descriptors for manipulation in
+ * memory.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_soft_desc(struct ksz_desc_info *desc_info, int transmit)
+{
+       desc_info->ring = kmalloc(sizeof(struct ksz_desc) * desc_info->alloc,
+               GFP_KERNEL);
+       if (!desc_info->ring)
+               return 1;
+       memset((void *) desc_info->ring, 0,
+               sizeof(struct ksz_desc) * desc_info->alloc);
+       hw_init_desc(desc_info, transmit);
+       return 0;
+}
+
+/**
+ * ksz_alloc_desc - allocate hardware descriptors
+ * @adapter:   Adapter information structure.
+ *
+ * This local function allocates hardware descriptors for receiving and
+ * transmitting.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_desc(struct dev_info *adapter)
+{
+       struct ksz_hw *hw = &adapter->hw;
+       int offset;
+
+       /* Allocate memory for RX & TX descriptors. */
+       adapter->desc_pool.alloc_size =
+               hw->rx_desc_info.size * hw->rx_desc_info.alloc +
+               hw->tx_desc_info.size * hw->tx_desc_info.alloc +
+               DESC_ALIGNMENT;
+
+       adapter->desc_pool.alloc_virt =
+               pci_alloc_consistent(
+                       adapter->pdev, adapter->desc_pool.alloc_size,
+                       &adapter->desc_pool.dma_addr);
+       if (adapter->desc_pool.alloc_virt == NULL) {
+               adapter->desc_pool.alloc_size = 0;
+               return 1;
+       }
+       memset(adapter->desc_pool.alloc_virt, 0, adapter->desc_pool.alloc_size);
+
+       /* Align to the next cache line boundary. */
+       offset = (((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT) ?
+               (DESC_ALIGNMENT -
+               ((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT)) : 0);
+       adapter->desc_pool.virt = adapter->desc_pool.alloc_virt + offset;
+       adapter->desc_pool.phys = adapter->desc_pool.dma_addr + offset;
+
+       /* Allocate receive/transmit descriptors. */
+       hw->rx_desc_info.ring_virt = (struct ksz_hw_desc *)
+               adapter->desc_pool.virt;
+       hw->rx_desc_info.ring_phys = adapter->desc_pool.phys;
+       offset = hw->rx_desc_info.alloc * hw->rx_desc_info.size;
+       hw->tx_desc_info.ring_virt = (struct ksz_hw_desc *)
+               (adapter->desc_pool.virt + offset);
+       hw->tx_desc_info.ring_phys = adapter->desc_pool.phys + offset;
+
+       if (ksz_alloc_soft_desc(&hw->rx_desc_info, 0))
+               return 1;
+       if (ksz_alloc_soft_desc(&hw->tx_desc_info, 1))
+               return 1;
+
+       return 0;
+}
+
+/**
+ * free_dma_buf - release DMA buffer resources
+ * @adapter:   Adapter information structure.
+ *
+ * This routine is just a helper function to release the DMA buffer resources.
+ */
+static void free_dma_buf(struct dev_info *adapter, struct ksz_dma_buf *dma_buf,
+       int direction)
+{
+       pci_unmap_single(adapter->pdev, dma_buf->dma, dma_buf->len, direction);
+       dev_kfree_skb(dma_buf->skb);
+       dma_buf->skb = NULL;
+       dma_buf->dma = 0;
+}
+
+/**
+ * ksz_init_rx_buffers - initialize receive descriptors
+ * @adapter:   Adapter information structure.
+ *
+ * This routine initializes DMA buffers for receiving.
+ */
+static void ksz_init_rx_buffers(struct dev_info *adapter)
+{
+       int i;
+       struct ksz_desc *desc;
+       struct ksz_dma_buf *dma_buf;
+       struct ksz_hw *hw = &adapter->hw;
+       struct ksz_desc_info *info = &hw->rx_desc_info;
+
+       for (i = 0; i < hw->rx_desc_info.alloc; i++) {
+               get_rx_pkt(info, &desc);
+
+               dma_buf = DMA_BUFFER(desc);
+               if (dma_buf->skb && dma_buf->len != adapter->mtu)
+                       free_dma_buf(adapter, dma_buf, PCI_DMA_FROMDEVICE);
+               dma_buf->len = adapter->mtu;
+               if (!dma_buf->skb)
+                       dma_buf->skb = alloc_skb(dma_buf->len, GFP_ATOMIC);
+               if (dma_buf->skb && !dma_buf->dma) {
+                       dma_buf->skb->dev = adapter->dev;
+                       dma_buf->dma = pci_map_single(
+                               adapter->pdev,
+                               skb_tail_pointer(dma_buf->skb),
+                               dma_buf->len,
+                               PCI_DMA_FROMDEVICE);
+               }
+
+               /* Set descriptor. */
+               set_rx_buf(desc, dma_buf->dma);
+               set_rx_len(desc, dma_buf->len);
+               release_desc(desc);
+       }
+}
+
+/**
+ * ksz_alloc_mem - allocate memory for hardware descriptors
+ * @adapter:   Adapter information structure.
+ *
+ * This function allocates memory for use by hardware descriptors for receiving
+ * and transmitting.
+ *
+ * Return 0 if successful.
+ */
+static int ksz_alloc_mem(struct dev_info *adapter)
+{
+       struct ksz_hw *hw = &adapter->hw;
+
+       /* Determine the number of receive and transmit descriptors. */
+       hw->rx_desc_info.alloc = NUM_OF_RX_DESC;
+       hw->tx_desc_info.alloc = NUM_OF_TX_DESC;
+
+       /* Determine how many descriptors to skip transmit interrupt. */
+       hw->tx_int_cnt = 0;
+       hw->tx_int_mask = NUM_OF_TX_DESC / 4;
+       if (hw->tx_int_mask > 8)
+               hw->tx_int_mask = 8;
+       while (hw->tx_int_mask) {
+               hw->tx_int_cnt++;
+               hw->tx_int_mask >>= 1;
+       }
+       if (hw->tx_int_cnt) {
+               hw->tx_int_mask = (1 << (hw->tx_int_cnt - 1)) - 1;
+               hw->tx_int_cnt = 0;
+       }
+
+       /* Determine the descriptor size. */
+       hw->rx_desc_info.size =
+               (((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
+               DESC_ALIGNMENT) * DESC_ALIGNMENT);
+       hw->tx_desc_info.size =
+               (((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
+               DESC_ALIGNMENT) * DESC_ALIGNMENT);
+       if (hw->rx_desc_info.size != sizeof(struct ksz_hw_desc))
+               pr_alert("Hardware descriptor size not right!\n");
+       ksz_check_desc_num(&hw->rx_desc_info);
+       ksz_check_desc_num(&hw->tx_desc_info);
+
+       /* Allocate descriptors. */
+       if (ksz_alloc_desc(adapter))
+               return 1;
+
+       return 0;
+}
+
+/**
+ * ksz_free_desc - free software and hardware descriptors
+ * @adapter:   Adapter information structure.
+ *
+ * This local routine frees the software and hardware descriptors allocated by
+ * ksz_alloc_desc().
+ */
+static void ksz_free_desc(struct dev_info *adapter)
+{
+       struct ksz_hw *hw = &adapter->hw;
+
+       /* Reset descriptor. */
+       hw->rx_desc_info.ring_virt = NULL;
+       hw->tx_desc_info.ring_virt = NULL;
+       hw->rx_desc_info.ring_phys = 0;
+       hw->tx_desc_info.ring_phys = 0;
+
+       /* Free memory. */
+       if (adapter->desc_pool.alloc_virt)
+               pci_free_consistent(
+                       adapter->pdev,
+                       adapter->desc_pool.alloc_size,
+                       adapter->desc_pool.alloc_virt,
+                       adapter->desc_pool.dma_addr);
+
+       /* Reset resource pool. */
+       adapter->desc_pool.alloc_size = 0;
+       adapter->desc_pool.alloc_virt = NULL;
+
+       kfree(hw->rx_desc_info.ring);
+       hw->rx_desc_info.ring = NULL;
+       kfree(hw->tx_desc_info.ring);
+       hw->tx_desc_info.ring = NULL;
+}
+
+/**
+ * ksz_free_buffers - free buffers used in the descriptors
+ * @adapter:   Adapter information structure.
+ * @desc_info: Descriptor information structure.
+ *
+ * This local routine frees buffers used in the DMA buffers.
+ */
+static void ksz_free_buffers(struct dev_info *adapter,
+       struct ksz_desc_info *desc_info, int direction)
+{
+       int i;
+       struct ksz_dma_buf *dma_buf;
+       struct ksz_desc *desc = desc_info->ring;
+
+       for (i = 0; i < desc_info->alloc; i++) {
+               dma_buf = DMA_BUFFER(desc);
+               if (dma_buf->skb)
+                       free_dma_buf(adapter, dma_buf, direction);
+               desc++;
+       }
+}
+
+/**
+ * ksz_free_mem - free all resources used by descriptors
+ * @adapter:   Adapter information structure.
+ *
+ * This local routine frees all the resources allocated by ksz_alloc_mem().
+ */
+static void ksz_free_mem(struct dev_info *adapter)
+{
+       /* Free transmit buffers. */
+       ksz_free_buffers(adapter, &adapter->hw.tx_desc_info,
+               PCI_DMA_TODEVICE);
+
+       /* Free receive buffers. */
+       ksz_free_buffers(adapter, &adapter->hw.rx_desc_info,
+               PCI_DMA_FROMDEVICE);
+
+       /* Free descriptors. */
+       ksz_free_desc(adapter);
+}
+
+static void get_mib_counters(struct ksz_hw *hw, int first, int cnt,
+       u64 *counter)
+{
+       int i;
+       int mib;
+       int port;
+       struct ksz_port_mib *port_mib;
+
+       memset(counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
+       for (i = 0, port = first; i < cnt; i++, port++) {
+               port_mib = &hw->port_mib[port];
+               for (mib = port_mib->mib_start; mib < hw->mib_cnt; mib++)
+                       counter[mib] += port_mib->counter[mib];
+       }
+}
+
+/**
+ * send_packet - send packet
+ * @skb:       Socket buffer.
+ * @dev:       Network device.
+ *
+ * This routine is used to send a packet out to the network.
+ */
+static void send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+       struct ksz_desc *desc;
+       struct ksz_desc *first;
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_desc_info *info = &hw->tx_desc_info;
+       struct ksz_dma_buf *dma_buf;
+       int len;
+       int last_frag = skb_shinfo(skb)->nr_frags;
+
+       /*
+        * KSZ8842 with multiple device interfaces needs to be told which port
+        * to send.
+        */
+       if (hw->dev_count > 1)
+               hw->dst_ports = 1 << priv->port.first_port;
+
+       /* Hardware will pad the length to 60. */
+       len = skb->len;
+
+       /* Remember the very first descriptor. */
+       first = info->cur;
+       desc = first;
+
+       dma_buf = DMA_BUFFER(desc);
+       if (last_frag) {
+               int frag;
+               skb_frag_t *this_frag;
+
+               dma_buf->len = skb_headlen(skb);
+
+               dma_buf->dma = pci_map_single(
+                       hw_priv->pdev, skb->data, dma_buf->len,
+                       PCI_DMA_TODEVICE);
+               set_tx_buf(desc, dma_buf->dma);
+               set_tx_len(desc, dma_buf->len);
+
+               frag = 0;
+               do {
+                       this_frag = &skb_shinfo(skb)->frags[frag];
+
+                       /* Get a new descriptor. */
+                       get_tx_pkt(info, &desc);
+
+                       /* Keep track of descriptors used so far. */
+                       ++hw->tx_int_cnt;
+
+                       dma_buf = DMA_BUFFER(desc);
+                       dma_buf->len = this_frag->size;
+
+                       dma_buf->dma = pci_map_single(
+                               hw_priv->pdev,
+                               page_address(this_frag->page) +
+                               this_frag->page_offset,
+                               dma_buf->len,
+                               PCI_DMA_TODEVICE);
+                       set_tx_buf(desc, dma_buf->dma);
+                       set_tx_len(desc, dma_buf->len);
+
+                       frag++;
+                       if (frag == last_frag)
+                               break;
+
+                       /* Do not release the last descriptor here. */
+                       release_desc(desc);
+               } while (1);
+
+               /* current points to the last descriptor. */
+               info->cur = desc;
+
+               /* Release the first descriptor. */
+               release_desc(first);
+       } else {
+               dma_buf->len = len;
+
+               dma_buf->dma = pci_map_single(
+                       hw_priv->pdev, skb->data, dma_buf->len,
+                       PCI_DMA_TODEVICE);
+               set_tx_buf(desc, dma_buf->dma);
+               set_tx_len(desc, dma_buf->len);
+       }
+
+       if (skb->ip_summed == CHECKSUM_PARTIAL) {
+               (desc)->sw.buf.tx.csum_gen_tcp = 1;
+               (desc)->sw.buf.tx.csum_gen_udp = 1;
+       }
+
+       /*
+        * The last descriptor holds the packet so that it can be returned to
+        * network subsystem after all descriptors are transmitted.
+        */
+       dma_buf->skb = skb;
+
+       hw_send_pkt(hw);
+
+       /* Update transmit statistics. */
+       dev->stats.tx_packets++;
+       dev->stats.tx_bytes += len;
+}
+
+/**
+ * transmit_cleanup - clean up transmit descriptors
+ * @dev:       Network device.
+ *
+ * This routine is called to clean up the transmitted buffers.
+ */
+static void transmit_cleanup(struct dev_info *hw_priv, int normal)
+{
+       int last;
+       union desc_stat status;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_desc_info *info = &hw->tx_desc_info;
+       struct ksz_desc *desc;
+       struct ksz_dma_buf *dma_buf;
+       struct net_device *dev = NULL;
+
+       spin_lock(&hw_priv->hwlock);
+       last = info->last;
+
+       while (info->avail < info->alloc) {
+               /* Get next descriptor which is not hardware owned. */
+               desc = &info->ring[last];
+               status.data = le32_to_cpu(desc->phw->ctrl.data);
+               if (status.tx.hw_owned) {
+                       if (normal)
+                               break;
+                       else
+                               reset_desc(desc, status);
+               }
+
+               dma_buf = DMA_BUFFER(desc);
+               pci_unmap_single(
+                       hw_priv->pdev, dma_buf->dma, dma_buf->len,
+                       PCI_DMA_TODEVICE);
+
+               /* This descriptor contains the last buffer in the packet. */
+               if (dma_buf->skb) {
+                       dev = dma_buf->skb->dev;
+
+                       /* Release the packet back to network subsystem. */
+                       dev_kfree_skb_irq(dma_buf->skb);
+                       dma_buf->skb = NULL;
+               }
+
+               /* Free the transmitted descriptor. */
+               last++;
+               last &= info->mask;
+               info->avail++;
+       }
+       info->last = last;
+       spin_unlock(&hw_priv->hwlock);
+
+       /* Notify the network subsystem that the packet has been sent. */
+       if (dev)
+               dev->trans_start = jiffies;
+}
+
+/**
+ * transmit_done - transmit done processing
+ * @dev:       Network device.
+ *
+ * This routine is called when the transmit interrupt is triggered, indicating
+ * either a packet is sent successfully or there are transmit errors.
+ */
+static void tx_done(struct dev_info *hw_priv)
+{
+       struct ksz_hw *hw = &hw_priv->hw;
+       int port;
+
+       transmit_cleanup(hw_priv, 1);
+
+       for (port = 0; port < hw->dev_count; port++) {
+               struct net_device *dev = hw->port_info[port].pdev;
+
+               if (netif_running(dev) && netif_queue_stopped(dev))
+                       netif_wake_queue(dev);
+       }
+}
+
+static inline void copy_old_skb(struct sk_buff *old, struct sk_buff *skb)
+{
+       skb->dev = old->dev;
+       skb->protocol = old->protocol;
+       skb->ip_summed = old->ip_summed;
+       skb->csum = old->csum;
+       skb_set_network_header(skb, ETH_HLEN);
+
+       dev_kfree_skb(old);
+}
+
+/**
+ * netdev_tx - send out packet
+ * @skb:       Socket buffer.
+ * @dev:       Network device.
+ *
+ * This function is used by the upper network layer to send out a packet.
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static netdev_tx_t netdev_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int left;
+       int num = 1;
+       int rc = 0;
+
+       if (hw->features & SMALL_PACKET_TX_BUG) {
+               struct sk_buff *org_skb = skb;
+
+               if (skb->len <= 48) {
+                       if (skb_end_pointer(skb) - skb->data >= 50) {
+                               memset(&skb->data[skb->len], 0, 50 - skb->len);
+                               skb->len = 50;
+                       } else {
+                               skb = dev_alloc_skb(50);
+                               if (!skb)
+                                       return NETDEV_TX_BUSY;
+                               memcpy(skb->data, org_skb->data, org_skb->len);
+                               memset(&skb->data[org_skb->len], 0,
+                                       50 - org_skb->len);
+                               skb->len = 50;
+                               copy_old_skb(org_skb, skb);
+                       }
+               }
+       }
+
+       spin_lock_irq(&hw_priv->hwlock);
+
+       num = skb_shinfo(skb)->nr_frags + 1;
+       left = hw_alloc_pkt(hw, skb->len, num);
+       if (left) {
+               if (left < num ||
+                               ((CHECKSUM_PARTIAL == skb->ip_summed) &&
+                               (ETH_P_IPV6 == htons(skb->protocol)))) {
+                       struct sk_buff *org_skb = skb;
+
+                       skb = dev_alloc_skb(org_skb->len);
+                       if (!skb) {
+                               rc = NETDEV_TX_BUSY;
+                               goto unlock;
+                       }
+                       skb_copy_and_csum_dev(org_skb, skb->data);
+                       org_skb->ip_summed = CHECKSUM_NONE;
+                       skb->len = org_skb->len;
+                       copy_old_skb(org_skb, skb);
+               }
+               send_packet(skb, dev);
+               if (left <= num)
+                       netif_stop_queue(dev);
+       } else {
+               /* Stop the transmit queue until packet is allocated. */
+               netif_stop_queue(dev);
+               rc = NETDEV_TX_BUSY;
+       }
+unlock:
+       spin_unlock_irq(&hw_priv->hwlock);
+
+       return rc;
+}
+
+/**
+ * netdev_tx_timeout - transmit timeout processing
+ * @dev:       Network device.
+ *
+ * This routine is called when the transmit timer expires.  That indicates the
+ * hardware is not running correctly because transmit interrupts are not
+ * triggered to free up resources so that the transmit routine can continue
+ * sending out packets.  The hardware is reset to correct the problem.
+ */
+static void netdev_tx_timeout(struct net_device *dev)
+{
+       static unsigned long last_reset;
+
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int port;
+
+       if (hw->dev_count > 1) {
+               /*
+                * Only reset the hardware if time between calls is long
+                * enough.
+                */
+               if (jiffies - last_reset <= dev->watchdog_timeo)
+                       hw_priv = NULL;
+       }
+
+       last_reset = jiffies;
+       if (hw_priv) {
+               hw_dis_intr(hw);
+               hw_disable(hw);
+
+               transmit_cleanup(hw_priv, 0);
+               hw_reset_pkts(&hw->rx_desc_info);
+               hw_reset_pkts(&hw->tx_desc_info);
+               ksz_init_rx_buffers(hw_priv);
+
+               hw_reset(hw);
+
+               hw_set_desc_base(hw,
+                       hw->tx_desc_info.ring_phys,
+                       hw->rx_desc_info.ring_phys);
+               hw_set_addr(hw);
+               if (hw->all_multi)
+                       hw_set_multicast(hw, hw->all_multi);
+               else if (hw->multi_list_size)
+                       hw_set_grp_addr(hw);
+
+               if (hw->dev_count > 1) {
+                       hw_set_add_addr(hw);
+                       for (port = 0; port < SWITCH_PORT_NUM; port++) {
+                               struct net_device *port_dev;
+
+                               port_set_stp_state(hw, port,
+                                       STP_STATE_DISABLED);
+
+                               port_dev = hw->port_info[port].pdev;
+                               if (netif_running(port_dev))
+                                       port_set_stp_state(hw, port,
+                                               STP_STATE_SIMPLE);
+                       }
+               }
+
+               hw_enable(hw);
+               hw_ena_intr(hw);
+       }
+
+       dev->trans_start = jiffies;
+       netif_wake_queue(dev);
+}
+
+static inline void csum_verified(struct sk_buff *skb)
+{
+       unsigned short protocol;
+       struct iphdr *iph;
+
+       protocol = skb->protocol;
+       skb_reset_network_header(skb);
+       iph = (struct iphdr *) skb_network_header(skb);
+       if (protocol == htons(ETH_P_8021Q)) {
+               protocol = iph->tot_len;
+               skb_set_network_header(skb, VLAN_HLEN);
+               iph = (struct iphdr *) skb_network_header(skb);
+       }
+       if (protocol == htons(ETH_P_IP)) {
+               if (iph->protocol == IPPROTO_TCP)
+                       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       }
+}
+
+static inline int rx_proc(struct net_device *dev, struct ksz_hw* hw,
+       struct ksz_desc *desc, union desc_stat status)
+{
+       int packet_len;
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_dma_buf *dma_buf;
+       struct sk_buff *skb;
+       int rx_status;
+
+       /* Received length includes 4-byte CRC. */
+       packet_len = status.rx.frame_len - 4;
+
+       dma_buf = DMA_BUFFER(desc);
+       pci_dma_sync_single_for_cpu(
+               hw_priv->pdev, dma_buf->dma, packet_len + 4,
+               PCI_DMA_FROMDEVICE);
+
+       do {
+               /* skb->data != skb->head */
+               skb = dev_alloc_skb(packet_len + 2);
+               if (!skb) {
+                       dev->stats.rx_dropped++;
+                       return -ENOMEM;
+               }
+
+               /*
+                * Align socket buffer in 4-byte boundary for better
+                * performance.
+                */
+               skb_reserve(skb, 2);
+
+               memcpy(skb_put(skb, packet_len),
+                       dma_buf->skb->data, packet_len);
+       } while (0);
+
+       skb->protocol = eth_type_trans(skb, dev);
+
+       if (hw->rx_cfg & (DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP))
+               csum_verified(skb);
+
+       /* Update receive statistics. */
+       dev->stats.rx_packets++;
+       dev->stats.rx_bytes += packet_len;
+
+       /* Notify upper layer for received packet. */
+       rx_status = netif_rx(skb);
+
+       return 0;
+}
+
+static int dev_rcv_packets(struct dev_info *hw_priv)
+{
+       int next;
+       union desc_stat status;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct net_device *dev = hw->port_info[0].pdev;
+       struct ksz_desc_info *info = &hw->rx_desc_info;
+       int left = info->alloc;
+       struct ksz_desc *desc;
+       int received = 0;
+
+       next = info->next;
+       while (left--) {
+               /* Get next descriptor which is not hardware owned. */
+               desc = &info->ring[next];
+               status.data = le32_to_cpu(desc->phw->ctrl.data);
+               if (status.rx.hw_owned)
+                       break;
+
+               /* Status valid only when last descriptor bit is set. */
+               if (status.rx.last_desc && status.rx.first_desc) {
+                       if (rx_proc(dev, hw, desc, status))
+                               goto release_packet;
+                       received++;
+               }
+
+release_packet:
+               release_desc(desc);
+               next++;
+               next &= info->mask;
+       }
+       info->next = next;
+
+       return received;
+}
+
+static int port_rcv_packets(struct dev_info *hw_priv)
+{
+       int next;
+       union desc_stat status;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct net_device *dev = hw->port_info[0].pdev;
+       struct ksz_desc_info *info = &hw->rx_desc_info;
+       int left = info->alloc;
+       struct ksz_desc *desc;
+       int received = 0;
+
+       next = info->next;
+       while (left--) {
+               /* Get next descriptor which is not hardware owned. */
+               desc = &info->ring[next];
+               status.data = le32_to_cpu(desc->phw->ctrl.data);
+               if (status.rx.hw_owned)
+                       break;
+
+               if (hw->dev_count > 1) {
+                       /* Get received port number. */
+                       int p = HW_TO_DEV_PORT(status.rx.src_port);
+
+                       dev = hw->port_info[p].pdev;
+                       if (!netif_running(dev))
+                               goto release_packet;
+               }
+
+               /* Status valid only when last descriptor bit is set. */
+               if (status.rx.last_desc && status.rx.first_desc) {
+                       if (rx_proc(dev, hw, desc, status))
+                               goto release_packet;
+                       received++;
+               }
+
+release_packet:
+               release_desc(desc);
+               next++;
+               next &= info->mask;
+       }
+       info->next = next;
+
+       return received;
+}
+
+static int dev_rcv_special(struct dev_info *hw_priv)
+{
+       int next;
+       union desc_stat status;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct net_device *dev = hw->port_info[0].pdev;
+       struct ksz_desc_info *info = &hw->rx_desc_info;
+       int left = info->alloc;
+       struct ksz_desc *desc;
+       int received = 0;
+
+       next = info->next;
+       while (left--) {
+               /* Get next descriptor which is not hardware owned. */
+               desc = &info->ring[next];
+               status.data = le32_to_cpu(desc->phw->ctrl.data);
+               if (status.rx.hw_owned)
+                       break;
+
+               if (hw->dev_count > 1) {
+                       /* Get received port number. */
+                       int p = HW_TO_DEV_PORT(status.rx.src_port);
+
+                       dev = hw->port_info[p].pdev;
+                       if (!netif_running(dev))
+                               goto release_packet;
+               }
+
+               /* Status valid only when last descriptor bit is set. */
+               if (status.rx.last_desc && status.rx.first_desc) {
+                       /*
+                        * Receive without error.  With receive errors
+                        * disabled, packets with receive errors will be
+                        * dropped, so no need to check the error bit.
+                        */
+                       if (!status.rx.error || (status.data &
+                                       KS_DESC_RX_ERROR_COND) ==
+                                       KS_DESC_RX_ERROR_TOO_LONG) {
+                               if (rx_proc(dev, hw, desc, status))
+                                       goto release_packet;
+                               received++;
+                       } else {
+                               struct dev_priv *priv = netdev_priv(dev);
+
+                               /* Update receive error statistics. */
+                               priv->port.counter[OID_COUNTER_RCV_ERROR]++;
+                       }
+               }
+
+release_packet:
+               release_desc(desc);
+               next++;
+               next &= info->mask;
+       }
+       info->next = next;
+
+       return received;
+}
+
+static void rx_proc_task(unsigned long data)
+{
+       struct dev_info *hw_priv = (struct dev_info *) data;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       if (!hw->enabled)
+               return;
+       if (unlikely(!hw_priv->dev_rcv(hw_priv))) {
+
+               /* In case receive process is suspended because of overrun. */
+               hw_resume_rx(hw);
+
+               /* tasklets are interruptible. */
+               spin_lock_irq(&hw_priv->hwlock);
+               hw_turn_on_intr(hw, KS884X_INT_RX_MASK);
+               spin_unlock_irq(&hw_priv->hwlock);
+       } else {
+               hw_ack_intr(hw, KS884X_INT_RX);
+               tasklet_schedule(&hw_priv->rx_tasklet);
+       }
+}
+
+static void tx_proc_task(unsigned long data)
+{
+       struct dev_info *hw_priv = (struct dev_info *) data;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       hw_ack_intr(hw, KS884X_INT_TX_MASK);
+
+       tx_done(hw_priv);
+
+       /* tasklets are interruptible. */
+       spin_lock_irq(&hw_priv->hwlock);
+       hw_turn_on_intr(hw, KS884X_INT_TX);
+       spin_unlock_irq(&hw_priv->hwlock);
+}
+
+static inline void handle_rx_stop(struct ksz_hw *hw)
+{
+       /* Receive just has been stopped. */
+       if (0 == hw->rx_stop)
+               hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
+       else if (hw->rx_stop > 1) {
+               if (hw->enabled && (hw->rx_cfg & DMA_RX_ENABLE)) {
+                       hw_start_rx(hw);
+               } else {
+                       hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
+                       hw->rx_stop = 0;
+               }
+       } else
+               /* Receive just has been started. */
+               hw->rx_stop++;
+}
+
+/**
+ * netdev_intr - interrupt handling
+ * @irq:       Interrupt number.
+ * @dev_id:    Network device.
+ *
+ * This function is called by upper network layer to signal interrupt.
+ *
+ * Return IRQ_HANDLED if interrupt is handled.
+ */
+static irqreturn_t netdev_intr(int irq, void *dev_id)
+{
+       uint int_enable = 0;
+       struct net_device *dev = (struct net_device *) dev_id;
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       hw_read_intr(hw, &int_enable);
+
+       /* Not our interrupt! */
+       if (!int_enable)
+               return IRQ_NONE;
+
+       do {
+               hw_ack_intr(hw, int_enable);
+               int_enable &= hw->intr_mask;
+
+               if (unlikely(int_enable & KS884X_INT_TX_MASK)) {
+                       hw_dis_intr_bit(hw, KS884X_INT_TX_MASK);
+                       tasklet_schedule(&hw_priv->tx_tasklet);
+               }
+
+               if (likely(int_enable & KS884X_INT_RX)) {
+                       hw_dis_intr_bit(hw, KS884X_INT_RX);
+                       tasklet_schedule(&hw_priv->rx_tasklet);
+               }
+
+               if (unlikely(int_enable & KS884X_INT_RX_OVERRUN)) {
+                       dev->stats.rx_fifo_errors++;
+                       hw_resume_rx(hw);
+               }
+
+               if (unlikely(int_enable & KS884X_INT_PHY)) {
+                       struct ksz_port *port = &priv->port;
+
+                       hw->features |= LINK_INT_WORKING;
+                       port_get_link_speed(port);
+               }
+
+               if (unlikely(int_enable & KS884X_INT_RX_STOPPED)) {
+                       handle_rx_stop(hw);
+                       break;
+               }
+
+               if (unlikely(int_enable & KS884X_INT_TX_STOPPED)) {
+                       u32 data;
+
+                       hw->intr_mask &= ~KS884X_INT_TX_STOPPED;
+                       pr_info("Tx stopped\n");
+                       data = readl(hw->io + KS_DMA_TX_CTRL);
+                       if (!(data & DMA_TX_ENABLE))
+                               pr_info("Tx disabled\n");
+                       break;
+               }
+       } while (0);
+
+       hw_ena_intr(hw);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Linux network device functions
+ */
+
+static unsigned long next_jiffies;
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void netdev_netpoll(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+
+       hw_dis_intr(&hw_priv->hw);
+       netdev_intr(dev->irq, dev);
+}
+#endif
+
+static void bridge_change(struct ksz_hw *hw)
+{
+       int port;
+       u8  member;
+       struct ksz_switch *sw = hw->ksz_switch;
+
+       /* No ports in forwarding state. */
+       if (!sw->member) {
+               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
+               sw_block_addr(hw);
+       }
+       for (port = 0; port < SWITCH_PORT_NUM; port++) {
+               if (STP_STATE_FORWARDING == sw->port_cfg[port].stp_state)
+                       member = HOST_MASK | sw->member;
+               else
+                       member = HOST_MASK | (1 << port);
+               if (member != sw->port_cfg[port].member)
+                       sw_cfg_port_base_vlan(hw, port, member);
+       }
+}
+
+/**
+ * netdev_close - close network device
+ * @dev:       Network device.
+ *
+ * This function process the close operation of network device.  This is caused
+ * by the user command "ifconfig ethX down."
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int netdev_close(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_port *port = &priv->port;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int pi;
+
+       netif_stop_queue(dev);
+
+       ksz_stop_timer(&priv->monitor_timer_info);
+
+       /* Need to shut the port manually in multiple device interfaces mode. */
+       if (hw->dev_count > 1) {
+               port_set_stp_state(hw, port->first_port, STP_STATE_DISABLED);
+
+               /* Port is closed.  Need to change bridge setting. */
+               if (hw->features & STP_SUPPORT) {
+                       pi = 1 << port->first_port;
+                       if (hw->ksz_switch->member & pi) {
+                               hw->ksz_switch->member &= ~pi;
+                               bridge_change(hw);
+                       }
+               }
+       }
+       if (port->first_port > 0)
+               hw_del_addr(hw, dev->dev_addr);
+       if (!hw_priv->wol_enable)
+               port_set_power_saving(port, true);
+
+       if (priv->multicast)
+               --hw->all_multi;
+       if (priv->promiscuous)
+               --hw->promiscuous;
+
+       hw_priv->opened--;
+       if (!(hw_priv->opened)) {
+               ksz_stop_timer(&hw_priv->mib_timer_info);
+               flush_work(&hw_priv->mib_read);
+
+               hw_dis_intr(hw);
+               hw_disable(hw);
+               hw_clr_multicast(hw);
+
+               /* Delay for receive task to stop scheduling itself. */
+               msleep(2000 / HZ);
+
+               tasklet_disable(&hw_priv->rx_tasklet);
+               tasklet_disable(&hw_priv->tx_tasklet);
+               free_irq(dev->irq, hw_priv->dev);
+
+               transmit_cleanup(hw_priv, 0);
+               hw_reset_pkts(&hw->rx_desc_info);
+               hw_reset_pkts(&hw->tx_desc_info);
+
+               /* Clean out static MAC table when the switch is shutdown. */
+               if (hw->features & STP_SUPPORT)
+                       sw_clr_sta_mac_table(hw);
+       }
+
+       return 0;
+}
+
+static void hw_cfg_huge_frame(struct dev_info *hw_priv, struct ksz_hw *hw)
+{
+       if (hw->ksz_switch) {
+               u32 data;
+
+               data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+               if (hw->features & RX_HUGE_FRAME)
+                       data |= SWITCH_HUGE_PACKET;
+               else
+                       data &= ~SWITCH_HUGE_PACKET;
+               writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
+       }
+       if (hw->features & RX_HUGE_FRAME) {
+               hw->rx_cfg |= DMA_RX_ERROR;
+               hw_priv->dev_rcv = dev_rcv_special;
+       } else {
+               hw->rx_cfg &= ~DMA_RX_ERROR;
+               if (hw->dev_count > 1)
+                       hw_priv->dev_rcv = port_rcv_packets;
+               else
+                       hw_priv->dev_rcv = dev_rcv_packets;
+       }
+}
+
+static int prepare_hardware(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int rc = 0;
+
+       /* Remember the network device that requests interrupts. */
+       hw_priv->dev = dev;
+       rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev);
+       if (rc)
+               return rc;
+       tasklet_enable(&hw_priv->rx_tasklet);
+       tasklet_enable(&hw_priv->tx_tasklet);
+
+       hw->promiscuous = 0;
+       hw->all_multi = 0;
+       hw->multi_list_size = 0;
+
+       hw_reset(hw);
+
+       hw_set_desc_base(hw,
+               hw->tx_desc_info.ring_phys, hw->rx_desc_info.ring_phys);
+       hw_set_addr(hw);
+       hw_cfg_huge_frame(hw_priv, hw);
+       ksz_init_rx_buffers(hw_priv);
+       return 0;
+}
+
+static void set_media_state(struct net_device *dev, int media_state)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+
+       if (media_state == priv->media_state)
+               netif_carrier_on(dev);
+       else
+               netif_carrier_off(dev);
+       netif_info(priv, link, dev, "link %s\n",
+                  media_state == priv->media_state ? "on" : "off");
+}
+
+/**
+ * netdev_open - open network device
+ * @dev:       Network device.
+ *
+ * This function process the open operation of network device.  This is caused
+ * by the user command "ifconfig ethX up."
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int netdev_open(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port *port = &priv->port;
+       int i;
+       int p;
+       int rc = 0;
+
+       priv->multicast = 0;
+       priv->promiscuous = 0;
+
+       /* Reset device statistics. */
+       memset(&dev->stats, 0, sizeof(struct net_device_stats));
+       memset((void *) port->counter, 0,
+               (sizeof(u64) * OID_COUNTER_LAST));
+
+       if (!(hw_priv->opened)) {
+               rc = prepare_hardware(dev);
+               if (rc)
+                       return rc;
+               for (i = 0; i < hw->mib_port_cnt; i++) {
+                       if (next_jiffies < jiffies)
+                               next_jiffies = jiffies + HZ * 2;
+                       else
+                               next_jiffies += HZ * 1;
+                       hw_priv->counter[i].time = next_jiffies;
+                       hw->port_mib[i].state = media_disconnected;
+                       port_init_cnt(hw, i);
+               }
+               if (hw->ksz_switch)
+                       hw->port_mib[HOST_PORT].state = media_connected;
+               else {
+                       hw_add_wol_bcast(hw);
+                       hw_cfg_wol_pme(hw, 0);
+                       hw_clr_wol_pme_status(&hw_priv->hw);
+               }
+       }
+       port_set_power_saving(port, false);
+
+       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
+               /*
+                * Initialize to invalid value so that link detection
+                * is done.
+                */
+               hw->port_info[p].partner = 0xFF;
+               hw->port_info[p].state = media_disconnected;
+       }
+
+       /* Need to open the port in multiple device interfaces mode. */
+       if (hw->dev_count > 1) {
+               port_set_stp_state(hw, port->first_port, STP_STATE_SIMPLE);
+               if (port->first_port > 0)
+                       hw_add_addr(hw, dev->dev_addr);
+       }
+
+       port_get_link_speed(port);
+       if (port->force_link)
+               port_force_link_speed(port);
+       else
+               port_set_link_speed(port);
+
+       if (!(hw_priv->opened)) {
+               hw_setup_intr(hw);
+               hw_enable(hw);
+               hw_ena_intr(hw);
+
+               if (hw->mib_port_cnt)
+                       ksz_start_timer(&hw_priv->mib_timer_info,
+                               hw_priv->mib_timer_info.period);
+       }
+
+       hw_priv->opened++;
+
+       ksz_start_timer(&priv->monitor_timer_info,
+               priv->monitor_timer_info.period);
+
+       priv->media_state = port->linked->state;
+
+       set_media_state(dev, media_connected);
+       netif_start_queue(dev);
+
+       return 0;
+}
+
+/* RX errors = rx_errors */
+/* RX dropped = rx_dropped */
+/* RX overruns = rx_fifo_errors */
+/* RX frame = rx_crc_errors + rx_frame_errors + rx_length_errors */
+/* TX errors = tx_errors */
+/* TX dropped = tx_dropped */
+/* TX overruns = tx_fifo_errors */
+/* TX carrier = tx_aborted_errors + tx_carrier_errors + tx_window_errors */
+/* collisions = collisions */
+
+/**
+ * netdev_query_statistics - query network device statistics
+ * @dev:       Network device.
+ *
+ * This function returns the statistics of the network device.  The device
+ * needs not be opened.
+ *
+ * Return network device statistics.
+ */
+static struct net_device_stats *netdev_query_statistics(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct ksz_port *port = &priv->port;
+       struct ksz_hw *hw = &priv->adapter->hw;
+       struct ksz_port_mib *mib;
+       int i;
+       int p;
+
+       dev->stats.rx_errors = port->counter[OID_COUNTER_RCV_ERROR];
+       dev->stats.tx_errors = port->counter[OID_COUNTER_XMIT_ERROR];
+
+       /* Reset to zero to add count later. */
+       dev->stats.multicast = 0;
+       dev->stats.collisions = 0;
+       dev->stats.rx_length_errors = 0;
+       dev->stats.rx_crc_errors = 0;
+       dev->stats.rx_frame_errors = 0;
+       dev->stats.tx_window_errors = 0;
+
+       for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
+               mib = &hw->port_mib[p];
+
+               dev->stats.multicast += (unsigned long)
+                       mib->counter[MIB_COUNTER_RX_MULTICAST];
+
+               dev->stats.collisions += (unsigned long)
+                       mib->counter[MIB_COUNTER_TX_TOTAL_COLLISION];
+
+               dev->stats.rx_length_errors += (unsigned long)(
+                       mib->counter[MIB_COUNTER_RX_UNDERSIZE] +
+                       mib->counter[MIB_COUNTER_RX_FRAGMENT] +
+                       mib->counter[MIB_COUNTER_RX_OVERSIZE] +
+                       mib->counter[MIB_COUNTER_RX_JABBER]);
+               dev->stats.rx_crc_errors += (unsigned long)
+                       mib->counter[MIB_COUNTER_RX_CRC_ERR];
+               dev->stats.rx_frame_errors += (unsigned long)(
+                       mib->counter[MIB_COUNTER_RX_ALIGNMENT_ERR] +
+                       mib->counter[MIB_COUNTER_RX_SYMBOL_ERR]);
+
+               dev->stats.tx_window_errors += (unsigned long)
+                       mib->counter[MIB_COUNTER_TX_LATE_COLLISION];
+       }
+
+       return &dev->stats;
+}
+
+/**
+ * netdev_set_mac_address - set network device MAC address
+ * @dev:       Network device.
+ * @addr:      Buffer of MAC address.
+ *
+ * This function is used to set the MAC address of the network device.
+ *
+ * Return 0 to indicate success.
+ */
+static int netdev_set_mac_address(struct net_device *dev, void *addr)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct sockaddr *mac = addr;
+       uint interrupt;
+
+       if (priv->port.first_port > 0)
+               hw_del_addr(hw, dev->dev_addr);
+       else {
+               hw->mac_override = 1;
+               memcpy(hw->override_addr, mac->sa_data, MAC_ADDR_LEN);
+       }
+
+       memcpy(dev->dev_addr, mac->sa_data, MAX_ADDR_LEN);
+
+       interrupt = hw_block_intr(hw);
+
+       if (priv->port.first_port > 0)
+               hw_add_addr(hw, dev->dev_addr);
+       else
+               hw_set_addr(hw);
+       hw_restore_intr(hw, interrupt);
+
+       return 0;
+}
+
+static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
+       struct ksz_hw *hw, int promiscuous)
+{
+       if (promiscuous != priv->promiscuous) {
+               u8 prev_state = hw->promiscuous;
+
+               if (promiscuous)
+                       ++hw->promiscuous;
+               else
+                       --hw->promiscuous;
+               priv->promiscuous = promiscuous;
+
+               /* Turn on/off promiscuous mode. */
+               if (hw->promiscuous <= 1 && prev_state <= 1)
+                       hw_set_promiscuous(hw, hw->promiscuous);
+
+               /*
+                * Port is not in promiscuous mode, meaning it is released
+                * from the bridge.
+                */
+               if ((hw->features & STP_SUPPORT) && !promiscuous &&
+                   (dev->priv_flags & IFF_BRIDGE_PORT)) {
+                       struct ksz_switch *sw = hw->ksz_switch;
+                       int port = priv->port.first_port;
+
+                       port_set_stp_state(hw, port, STP_STATE_DISABLED);
+                       port = 1 << port;
+                       if (sw->member & port) {
+                               sw->member &= ~port;
+                               bridge_change(hw);
+                       }
+               }
+       }
+}
+
+static void dev_set_multicast(struct dev_priv *priv, struct ksz_hw *hw,
+       int multicast)
+{
+       if (multicast != priv->multicast) {
+               u8 all_multi = hw->all_multi;
+
+               if (multicast)
+                       ++hw->all_multi;
+               else
+                       --hw->all_multi;
+               priv->multicast = multicast;
+
+               /* Turn on/off all multicast mode. */
+               if (hw->all_multi <= 1 && all_multi <= 1)
+                       hw_set_multicast(hw, hw->all_multi);
+       }
+}
+
+/**
+ * netdev_set_rx_mode
+ * @dev:       Network device.
+ *
+ * This routine is used to set multicast addresses or put the network device
+ * into promiscuous mode.
+ */
+static void netdev_set_rx_mode(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct netdev_hw_addr *ha;
+       int multicast = (dev->flags & IFF_ALLMULTI);
+
+       dev_set_promiscuous(dev, priv, hw, (dev->flags & IFF_PROMISC));
+
+       if (hw_priv->hw.dev_count > 1)
+               multicast |= (dev->flags & IFF_MULTICAST);
+       dev_set_multicast(priv, hw, multicast);
+
+       /* Cannot use different hashes in multiple device interfaces mode. */
+       if (hw_priv->hw.dev_count > 1)
+               return;
+
+       if ((dev->flags & IFF_MULTICAST) && !netdev_mc_empty(dev)) {
+               int i = 0;
+
+               /* List too big to support so turn on all multicast mode. */
+               if (netdev_mc_count(dev) > MAX_MULTICAST_LIST) {
+                       if (MAX_MULTICAST_LIST != hw->multi_list_size) {
+                               hw->multi_list_size = MAX_MULTICAST_LIST;
+                               ++hw->all_multi;
+                               hw_set_multicast(hw, hw->all_multi);
+                       }
+                       return;
+               }
+
+               netdev_for_each_mc_addr(ha, dev) {
+                       if (i >= MAX_MULTICAST_LIST)
+                               break;
+                       memcpy(hw->multi_list[i++], ha->addr, MAC_ADDR_LEN);
+               }
+               hw->multi_list_size = (u8) i;
+               hw_set_grp_addr(hw);
+       } else {
+               if (MAX_MULTICAST_LIST == hw->multi_list_size) {
+                       --hw->all_multi;
+                       hw_set_multicast(hw, hw->all_multi);
+               }
+               hw->multi_list_size = 0;
+               hw_clr_multicast(hw);
+       }
+}
+
+static int netdev_change_mtu(struct net_device *dev, int new_mtu)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int hw_mtu;
+
+       if (netif_running(dev))
+               return -EBUSY;
+
+       /* Cannot use different MTU in multiple device interfaces mode. */
+       if (hw->dev_count > 1)
+               if (dev != hw_priv->dev)
+                       return 0;
+       if (new_mtu < 60)
+               return -EINVAL;
+
+       if (dev->mtu != new_mtu) {
+               hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4;
+               if (hw_mtu > MAX_RX_BUF_SIZE)
+                       return -EINVAL;
+               if (hw_mtu > REGULAR_RX_BUF_SIZE) {
+                       hw->features |= RX_HUGE_FRAME;
+                       hw_mtu = MAX_RX_BUF_SIZE;
+               } else {
+                       hw->features &= ~RX_HUGE_FRAME;
+                       hw_mtu = REGULAR_RX_BUF_SIZE;
+               }
+               hw_mtu = (hw_mtu + 3) & ~3;
+               hw_priv->mtu = hw_mtu;
+               dev->mtu = new_mtu;
+       }
+       return 0;
+}
+
+/**
+ * netdev_ioctl - I/O control processing
+ * @dev:       Network device.
+ * @ifr:       Interface request structure.
+ * @cmd:       I/O control code.
+ *
+ * This function is used to process I/O control calls.
+ *
+ * Return 0 to indicate success.
+ */
+static int netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port *port = &priv->port;
+       int rc;
+       int result = 0;
+       struct mii_ioctl_data *data = if_mii(ifr);
+
+       if (down_interruptible(&priv->proc_sem))
+               return -ERESTARTSYS;
+
+       /* assume success */
+       rc = 0;
+       switch (cmd) {
+       /* Get address of MII PHY in use. */
+       case SIOCGMIIPHY:
+               data->phy_id = priv->id;
+
+               /* Fallthrough... */
+
+       /* Read MII PHY register. */
+       case SIOCGMIIREG:
+               if (data->phy_id != priv->id || data->reg_num >= 6)
+                       result = -EIO;
+               else
+                       hw_r_phy(hw, port->linked->port_id, data->reg_num,
+                               &data->val_out);
+               break;
+
+       /* Write MII PHY register. */
+       case SIOCSMIIREG:
+               if (!capable(CAP_NET_ADMIN))
+                       result = -EPERM;
+               else if (data->phy_id != priv->id || data->reg_num >= 6)
+                       result = -EIO;
+               else
+                       hw_w_phy(hw, port->linked->port_id, data->reg_num,
+                               data->val_in);
+               break;
+
+       default:
+               result = -EOPNOTSUPP;
+       }
+
+       up(&priv->proc_sem);
+
+       return result;
+}
+
+/*
+ * MII support
+ */
+
+/**
+ * mdio_read - read PHY register
+ * @dev:       Network device.
+ * @phy_id:    The PHY id.
+ * @reg_num:   The register number.
+ *
+ * This function returns the PHY register value.
+ *
+ * Return the register value.
+ */
+static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct ksz_port *port = &priv->port;
+       struct ksz_hw *hw = port->hw;
+       u16 val_out;
+
+       hw_r_phy(hw, port->linked->port_id, reg_num << 1, &val_out);
+       return val_out;
+}
+
+/**
+ * mdio_write - set PHY register
+ * @dev:       Network device.
+ * @phy_id:    The PHY id.
+ * @reg_num:   The register number.
+ * @val:       The register value.
+ *
+ * This procedure sets the PHY register value.
+ */
+static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct ksz_port *port = &priv->port;
+       struct ksz_hw *hw = port->hw;
+       int i;
+       int pi;
+
+       for (i = 0, pi = port->first_port; i < port->port_cnt; i++, pi++)
+               hw_w_phy(hw, pi, reg_num << 1, val);
+}
+
+/*
+ * ethtool support
+ */
+
+#define EEPROM_SIZE                    0x40
+
+static u16 eeprom_data[EEPROM_SIZE] = { 0 };
+
+#define ADVERTISED_ALL                 \
+       (ADVERTISED_10baseT_Half |      \
+       ADVERTISED_10baseT_Full |       \
+       ADVERTISED_100baseT_Half |      \
+       ADVERTISED_100baseT_Full)
+
+/* These functions use the MII functions in mii.c. */
+
+/**
+ * netdev_get_settings - get network device settings
+ * @dev:       Network device.
+ * @cmd:       Ethtool command.
+ *
+ * This function queries the PHY and returns its state in the ethtool command.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+
+       mutex_lock(&hw_priv->lock);
+       mii_ethtool_gset(&priv->mii_if, cmd);
+       cmd->advertising |= SUPPORTED_TP;
+       mutex_unlock(&hw_priv->lock);
+
+       /* Save advertised settings for workaround in next function. */
+       priv->advertising = cmd->advertising;
+       return 0;
+}
+
+/**
+ * netdev_set_settings - set network device settings
+ * @dev:       Network device.
+ * @cmd:       Ethtool command.
+ *
+ * This function sets the PHY according to the ethtool command.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_port *port = &priv->port;
+       u32 speed = ethtool_cmd_speed(cmd);
+       int rc;
+
+       /*
+        * ethtool utility does not change advertised setting if auto
+        * negotiation is not specified explicitly.
+        */
+       if (cmd->autoneg && priv->advertising == cmd->advertising) {
+               cmd->advertising |= ADVERTISED_ALL;
+               if (10 == speed)
+                       cmd->advertising &=
+                               ~(ADVERTISED_100baseT_Full |
+                               ADVERTISED_100baseT_Half);
+               else if (100 == speed)
+                       cmd->advertising &=
+                               ~(ADVERTISED_10baseT_Full |
+                               ADVERTISED_10baseT_Half);
+               if (0 == cmd->duplex)
+                       cmd->advertising &=
+                               ~(ADVERTISED_100baseT_Full |
+                               ADVERTISED_10baseT_Full);
+               else if (1 == cmd->duplex)
+                       cmd->advertising &=
+                               ~(ADVERTISED_100baseT_Half |
+                               ADVERTISED_10baseT_Half);
+       }
+       mutex_lock(&hw_priv->lock);
+       if (cmd->autoneg &&
+                       (cmd->advertising & ADVERTISED_ALL) ==
+                       ADVERTISED_ALL) {
+               port->duplex = 0;
+               port->speed = 0;
+               port->force_link = 0;
+       } else {
+               port->duplex = cmd->duplex + 1;
+               if (1000 != speed)
+                       port->speed = speed;
+               if (cmd->autoneg)
+                       port->force_link = 0;
+               else
+                       port->force_link = 1;
+       }
+       rc = mii_ethtool_sset(&priv->mii_if, cmd);
+       mutex_unlock(&hw_priv->lock);
+       return rc;
+}
+
+/**
+ * netdev_nway_reset - restart auto-negotiation
+ * @dev:       Network device.
+ *
+ * This function restarts the PHY for auto-negotiation.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_nway_reset(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       int rc;
+
+       mutex_lock(&hw_priv->lock);
+       rc = mii_nway_restart(&priv->mii_if);
+       mutex_unlock(&hw_priv->lock);
+       return rc;
+}
+
+/**
+ * netdev_get_link - get network device link status
+ * @dev:       Network device.
+ *
+ * This function gets the link status from the PHY.
+ *
+ * Return true if PHY is linked and false otherwise.
+ */
+static u32 netdev_get_link(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       int rc;
+
+       rc = mii_link_ok(&priv->mii_if);
+       return rc;
+}
+
+/**
+ * netdev_get_drvinfo - get network driver information
+ * @dev:       Network device.
+ * @info:      Ethtool driver info data structure.
+ *
+ * This procedure returns the driver information.
+ */
+static void netdev_get_drvinfo(struct net_device *dev,
+       struct ethtool_drvinfo *info)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+
+       strcpy(info->driver, DRV_NAME);
+       strcpy(info->version, DRV_VERSION);
+       strcpy(info->bus_info, pci_name(hw_priv->pdev));
+}
+
+/**
+ * netdev_get_regs_len - get length of register dump
+ * @dev:       Network device.
+ *
+ * This function returns the length of the register dump.
+ *
+ * Return length of the register dump.
+ */
+static struct hw_regs {
+       int start;
+       int end;
+} hw_regs_range[] = {
+       { KS_DMA_TX_CTRL,       KS884X_INTERRUPTS_STATUS },
+       { KS_ADD_ADDR_0_LO,     KS_ADD_ADDR_F_HI },
+       { KS884X_ADDR_0_OFFSET, KS8841_WOL_FRAME_BYTE2_OFFSET },
+       { KS884X_SIDER_P,       KS8842_SGCR7_P },
+       { KS8842_MACAR1_P,      KS8842_TOSR8_P },
+       { KS884X_P1MBCR_P,      KS8842_P3ERCR_P },
+       { 0, 0 }
+};
+
+static int netdev_get_regs_len(struct net_device *dev)
+{
+       struct hw_regs *range = hw_regs_range;
+       int regs_len = 0x10 * sizeof(u32);
+
+       while (range->end > range->start) {
+               regs_len += (range->end - range->start + 3) / 4 * 4;
+               range++;
+       }
+       return regs_len;
+}
+
+/**
+ * netdev_get_regs - get register dump
+ * @dev:       Network device.
+ * @regs:      Ethtool registers data structure.
+ * @ptr:       Buffer to store the register values.
+ *
+ * This procedure dumps the register values in the provided buffer.
+ */
+static void netdev_get_regs(struct net_device *dev, struct ethtool_regs *regs,
+       void *ptr)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       int *buf = (int *) ptr;
+       struct hw_regs *range = hw_regs_range;
+       int len;
+
+       mutex_lock(&hw_priv->lock);
+       regs->version = 0;
+       for (len = 0; len < 0x40; len += 4) {
+               pci_read_config_dword(hw_priv->pdev, len, buf);
+               buf++;
+       }
+       while (range->end > range->start) {
+               for (len = range->start; len < range->end; len += 4) {
+                       *buf = readl(hw->io + len);
+                       buf++;
+               }
+               range++;
+       }
+       mutex_unlock(&hw_priv->lock);
+}
+
+#define WOL_SUPPORT                    \
+       (WAKE_PHY | WAKE_MAGIC |        \
+       WAKE_UCAST | WAKE_MCAST |       \
+       WAKE_BCAST | WAKE_ARP)
+
+/**
+ * netdev_get_wol - get Wake-on-LAN support
+ * @dev:       Network device.
+ * @wol:       Ethtool Wake-on-LAN data structure.
+ *
+ * This procedure returns Wake-on-LAN support.
+ */
+static void netdev_get_wol(struct net_device *dev,
+       struct ethtool_wolinfo *wol)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+
+       wol->supported = hw_priv->wol_support;
+       wol->wolopts = hw_priv->wol_enable;
+       memset(&wol->sopass, 0, sizeof(wol->sopass));
+}
+
+/**
+ * netdev_set_wol - set Wake-on-LAN support
+ * @dev:       Network device.
+ * @wol:       Ethtool Wake-on-LAN data structure.
+ *
+ * This function sets Wake-on-LAN support.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_wol(struct net_device *dev,
+       struct ethtool_wolinfo *wol)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+
+       /* Need to find a way to retrieve the device IP address. */
+       static const u8 net_addr[] = { 192, 168, 1, 1 };
+
+       if (wol->wolopts & ~hw_priv->wol_support)
+               return -EINVAL;
+
+       hw_priv->wol_enable = wol->wolopts;
+
+       /* Link wakeup cannot really be disabled. */
+       if (wol->wolopts)
+               hw_priv->wol_enable |= WAKE_PHY;
+       hw_enable_wol(&hw_priv->hw, hw_priv->wol_enable, net_addr);
+       return 0;
+}
+
+/**
+ * netdev_get_msglevel - get debug message level
+ * @dev:       Network device.
+ *
+ * This function returns current debug message level.
+ *
+ * Return current debug message flags.
+ */
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+
+       return priv->msg_enable;
+}
+
+/**
+ * netdev_set_msglevel - set debug message level
+ * @dev:       Network device.
+ * @value:     Debug message flags.
+ *
+ * This procedure sets debug message level.
+ */
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+
+       priv->msg_enable = value;
+}
+
+/**
+ * netdev_get_eeprom_len - get EEPROM length
+ * @dev:       Network device.
+ *
+ * This function returns the length of the EEPROM.
+ *
+ * Return length of the EEPROM.
+ */
+static int netdev_get_eeprom_len(struct net_device *dev)
+{
+       return EEPROM_SIZE * 2;
+}
+
+/**
+ * netdev_get_eeprom - get EEPROM data
+ * @dev:       Network device.
+ * @eeprom:    Ethtool EEPROM data structure.
+ * @data:      Buffer to store the EEPROM data.
+ *
+ * This function dumps the EEPROM data in the provided buffer.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+#define EEPROM_MAGIC                   0x10A18842
+
+static int netdev_get_eeprom(struct net_device *dev,
+       struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       u8 *eeprom_byte = (u8 *) eeprom_data;
+       int i;
+       int len;
+
+       len = (eeprom->offset + eeprom->len + 1) / 2;
+       for (i = eeprom->offset / 2; i < len; i++)
+               eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
+       eeprom->magic = EEPROM_MAGIC;
+       memcpy(data, &eeprom_byte[eeprom->offset], eeprom->len);
+
+       return 0;
+}
+
+/**
+ * netdev_set_eeprom - write EEPROM data
+ * @dev:       Network device.
+ * @eeprom:    Ethtool EEPROM data structure.
+ * @data:      Data buffer.
+ *
+ * This function modifies the EEPROM data one byte at a time.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_eeprom(struct net_device *dev,
+       struct ethtool_eeprom *eeprom, u8 *data)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       u16 eeprom_word[EEPROM_SIZE];
+       u8 *eeprom_byte = (u8 *) eeprom_word;
+       int i;
+       int len;
+
+       if (eeprom->magic != EEPROM_MAGIC)
+               return -EINVAL;
+
+       len = (eeprom->offset + eeprom->len + 1) / 2;
+       for (i = eeprom->offset / 2; i < len; i++)
+               eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
+       memcpy(eeprom_word, eeprom_data, EEPROM_SIZE * 2);
+       memcpy(&eeprom_byte[eeprom->offset], data, eeprom->len);
+       for (i = 0; i < EEPROM_SIZE; i++)
+               if (eeprom_word[i] != eeprom_data[i]) {
+                       eeprom_data[i] = eeprom_word[i];
+                       eeprom_write(&hw_priv->hw, i, eeprom_data[i]);
+       }
+
+       return 0;
+}
+
+/**
+ * netdev_get_pauseparam - get flow control parameters
+ * @dev:       Network device.
+ * @pause:     Ethtool PAUSE settings data structure.
+ *
+ * This procedure returns the PAUSE control flow settings.
+ */
+static void netdev_get_pauseparam(struct net_device *dev,
+       struct ethtool_pauseparam *pause)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       pause->autoneg = (hw->overrides & PAUSE_FLOW_CTRL) ? 0 : 1;
+       if (!hw->ksz_switch) {
+               pause->rx_pause =
+                       (hw->rx_cfg & DMA_RX_FLOW_ENABLE) ? 1 : 0;
+               pause->tx_pause =
+                       (hw->tx_cfg & DMA_TX_FLOW_ENABLE) ? 1 : 0;
+       } else {
+               pause->rx_pause =
+                       (sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_RX_FLOW_CTRL)) ? 1 : 0;
+               pause->tx_pause =
+                       (sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_TX_FLOW_CTRL)) ? 1 : 0;
+       }
+}
+
+/**
+ * netdev_set_pauseparam - set flow control parameters
+ * @dev:       Network device.
+ * @pause:     Ethtool PAUSE settings data structure.
+ *
+ * This function sets the PAUSE control flow settings.
+ * Not implemented yet.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_pauseparam(struct net_device *dev,
+       struct ethtool_pauseparam *pause)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port *port = &priv->port;
+
+       mutex_lock(&hw_priv->lock);
+       if (pause->autoneg) {
+               if (!pause->rx_pause && !pause->tx_pause)
+                       port->flow_ctrl = PHY_NO_FLOW_CTRL;
+               else
+                       port->flow_ctrl = PHY_FLOW_CTRL;
+               hw->overrides &= ~PAUSE_FLOW_CTRL;
+               port->force_link = 0;
+               if (hw->ksz_switch) {
+                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_RX_FLOW_CTRL, 1);
+                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_TX_FLOW_CTRL, 1);
+               }
+               port_set_link_speed(port);
+       } else {
+               hw->overrides |= PAUSE_FLOW_CTRL;
+               if (hw->ksz_switch) {
+                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_RX_FLOW_CTRL, pause->rx_pause);
+                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
+                               SWITCH_TX_FLOW_CTRL, pause->tx_pause);
+               } else
+                       set_flow_ctrl(hw, pause->rx_pause, pause->tx_pause);
+       }
+       mutex_unlock(&hw_priv->lock);
+
+       return 0;
+}
+
+/**
+ * netdev_get_ringparam - get tx/rx ring parameters
+ * @dev:       Network device.
+ * @pause:     Ethtool RING settings data structure.
+ *
+ * This procedure returns the TX/RX ring settings.
+ */
+static void netdev_get_ringparam(struct net_device *dev,
+       struct ethtool_ringparam *ring)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       ring->tx_max_pending = (1 << 9);
+       ring->tx_pending = hw->tx_desc_info.alloc;
+       ring->rx_max_pending = (1 << 9);
+       ring->rx_pending = hw->rx_desc_info.alloc;
+}
+
+#define STATS_LEN                      (TOTAL_PORT_COUNTER_NUM)
+
+static struct {
+       char string[ETH_GSTRING_LEN];
+} ethtool_stats_keys[STATS_LEN] = {
+       { "rx_lo_priority_octets" },
+       { "rx_hi_priority_octets" },
+       { "rx_undersize_packets" },
+       { "rx_fragments" },
+       { "rx_oversize_packets" },
+       { "rx_jabbers" },
+       { "rx_symbol_errors" },
+       { "rx_crc_errors" },
+       { "rx_align_errors" },
+       { "rx_mac_ctrl_packets" },
+       { "rx_pause_packets" },
+       { "rx_bcast_packets" },
+       { "rx_mcast_packets" },
+       { "rx_ucast_packets" },
+       { "rx_64_or_less_octet_packets" },
+       { "rx_65_to_127_octet_packets" },
+       { "rx_128_to_255_octet_packets" },
+       { "rx_256_to_511_octet_packets" },
+       { "rx_512_to_1023_octet_packets" },
+       { "rx_1024_to_1522_octet_packets" },
+
+       { "tx_lo_priority_octets" },
+       { "tx_hi_priority_octets" },
+       { "tx_late_collisions" },
+       { "tx_pause_packets" },
+       { "tx_bcast_packets" },
+       { "tx_mcast_packets" },
+       { "tx_ucast_packets" },
+       { "tx_deferred" },
+       { "tx_total_collisions" },
+       { "tx_excessive_collisions" },
+       { "tx_single_collisions" },
+       { "tx_mult_collisions" },
+
+       { "rx_discards" },
+       { "tx_discards" },
+};
+
+/**
+ * netdev_get_strings - get statistics identity strings
+ * @dev:       Network device.
+ * @stringset: String set identifier.
+ * @buf:       Buffer to store the strings.
+ *
+ * This procedure returns the strings used to identify the statistics.
+ */
+static void netdev_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       if (ETH_SS_STATS == stringset)
+               memcpy(buf, &ethtool_stats_keys,
+                       ETH_GSTRING_LEN * hw->mib_cnt);
+}
+
+/**
+ * netdev_get_sset_count - get statistics size
+ * @dev:       Network device.
+ * @sset:      The statistics set number.
+ *
+ * This function returns the size of the statistics to be reported.
+ *
+ * Return size of the statistics to be reported.
+ */
+static int netdev_get_sset_count(struct net_device *dev, int sset)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       switch (sset) {
+       case ETH_SS_STATS:
+               return hw->mib_cnt;
+       default:
+               return -EOPNOTSUPP;
+       }
+}
+
+/**
+ * netdev_get_ethtool_stats - get network device statistics
+ * @dev:       Network device.
+ * @stats:     Ethtool statistics data structure.
+ * @data:      Buffer to store the statistics.
+ *
+ * This procedure returns the statistics.
+ */
+static void netdev_get_ethtool_stats(struct net_device *dev,
+       struct ethtool_stats *stats, u64 *data)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port *port = &priv->port;
+       int n_stats = stats->n_stats;
+       int i;
+       int n;
+       int p;
+       int rc;
+       u64 counter[TOTAL_PORT_COUNTER_NUM];
+
+       mutex_lock(&hw_priv->lock);
+       n = SWITCH_PORT_NUM;
+       for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
+               if (media_connected == hw->port_mib[p].state) {
+                       hw_priv->counter[p].read = 1;
+
+                       /* Remember first port that requests read. */
+                       if (n == SWITCH_PORT_NUM)
+                               n = p;
+               }
+       }
+       mutex_unlock(&hw_priv->lock);
+
+       if (n < SWITCH_PORT_NUM)
+               schedule_work(&hw_priv->mib_read);
+
+       if (1 == port->mib_port_cnt && n < SWITCH_PORT_NUM) {
+               p = n;
+               rc = wait_event_interruptible_timeout(
+                       hw_priv->counter[p].counter,
+                       2 == hw_priv->counter[p].read,
+                       HZ * 1);
+       } else
+               for (i = 0, p = n; i < port->mib_port_cnt - n; i++, p++) {
+                       if (0 == i) {
+                               rc = wait_event_interruptible_timeout(
+                                       hw_priv->counter[p].counter,
+                                       2 == hw_priv->counter[p].read,
+                                       HZ * 2);
+                       } else if (hw->port_mib[p].cnt_ptr) {
+                               rc = wait_event_interruptible_timeout(
+                                       hw_priv->counter[p].counter,
+                                       2 == hw_priv->counter[p].read,
+                                       HZ * 1);
+                       }
+               }
+
+       get_mib_counters(hw, port->first_port, port->mib_port_cnt, counter);
+       n = hw->mib_cnt;
+       if (n > n_stats)
+               n = n_stats;
+       n_stats -= n;
+       for (i = 0; i < n; i++)
+               *data++ = counter[i];
+}
+
+/**
+ * netdev_set_features - set receive checksum support
+ * @dev:       Network device.
+ * @features:  New device features (offloads).
+ *
+ * This function sets receive checksum support setting.
+ *
+ * Return 0 if successful; otherwise an error code.
+ */
+static int netdev_set_features(struct net_device *dev, u32 features)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       mutex_lock(&hw_priv->lock);
+
+       /* see note in hw_setup() */
+       if (features & NETIF_F_RXCSUM)
+               hw->rx_cfg |= DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP;
+       else
+               hw->rx_cfg &= ~(DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
+
+       if (hw->enabled)
+               writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
+
+       mutex_unlock(&hw_priv->lock);
+
+       return 0;
+}
+
+static struct ethtool_ops netdev_ethtool_ops = {
+       .get_settings           = netdev_get_settings,
+       .set_settings           = netdev_set_settings,
+       .nway_reset             = netdev_nway_reset,
+       .get_link               = netdev_get_link,
+       .get_drvinfo            = netdev_get_drvinfo,
+       .get_regs_len           = netdev_get_regs_len,
+       .get_regs               = netdev_get_regs,
+       .get_wol                = netdev_get_wol,
+       .set_wol                = netdev_set_wol,
+       .get_msglevel           = netdev_get_msglevel,
+       .set_msglevel           = netdev_set_msglevel,
+       .get_eeprom_len         = netdev_get_eeprom_len,
+       .get_eeprom             = netdev_get_eeprom,
+       .set_eeprom             = netdev_set_eeprom,
+       .get_pauseparam         = netdev_get_pauseparam,
+       .set_pauseparam         = netdev_set_pauseparam,
+       .get_ringparam          = netdev_get_ringparam,
+       .get_strings            = netdev_get_strings,
+       .get_sset_count         = netdev_get_sset_count,
+       .get_ethtool_stats      = netdev_get_ethtool_stats,
+};
+
+/*
+ * Hardware monitoring
+ */
+
+static void update_link(struct net_device *dev, struct dev_priv *priv,
+       struct ksz_port *port)
+{
+       if (priv->media_state != port->linked->state) {
+               priv->media_state = port->linked->state;
+               if (netif_running(dev))
+                       set_media_state(dev, media_connected);
+       }
+}
+
+static void mib_read_work(struct work_struct *work)
+{
+       struct dev_info *hw_priv =
+               container_of(work, struct dev_info, mib_read);
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port_mib *mib;
+       int i;
+
+       next_jiffies = jiffies;
+       for (i = 0; i < hw->mib_port_cnt; i++) {
+               mib = &hw->port_mib[i];
+
+               /* Reading MIB counters or requested to read. */
+               if (mib->cnt_ptr || 1 == hw_priv->counter[i].read) {
+
+                       /* Need to process receive interrupt. */
+                       if (port_r_cnt(hw, i))
+                               break;
+                       hw_priv->counter[i].read = 0;
+
+                       /* Finish reading counters. */
+                       if (0 == mib->cnt_ptr) {
+                               hw_priv->counter[i].read = 2;
+                               wake_up_interruptible(
+                                       &hw_priv->counter[i].counter);
+                       }
+               } else if (jiffies >= hw_priv->counter[i].time) {
+                       /* Only read MIB counters when the port is connected. */
+                       if (media_connected == mib->state)
+                               hw_priv->counter[i].read = 1;
+                       next_jiffies += HZ * 1 * hw->mib_port_cnt;
+                       hw_priv->counter[i].time = next_jiffies;
+
+               /* Port is just disconnected. */
+               } else if (mib->link_down) {
+                       mib->link_down = 0;
+
+                       /* Read counters one last time after link is lost. */
+                       hw_priv->counter[i].read = 1;
+               }
+       }
+}
+
+static void mib_monitor(unsigned long ptr)
+{
+       struct dev_info *hw_priv = (struct dev_info *) ptr;
+
+       mib_read_work(&hw_priv->mib_read);
+
+       /* This is used to verify Wake-on-LAN is working. */
+       if (hw_priv->pme_wait) {
+               if (hw_priv->pme_wait <= jiffies) {
+                       hw_clr_wol_pme_status(&hw_priv->hw);
+                       hw_priv->pme_wait = 0;
+               }
+       } else if (hw_chk_wol_pme_status(&hw_priv->hw)) {
+
+               /* PME is asserted.  Wait 2 seconds to clear it. */
+               hw_priv->pme_wait = jiffies + HZ * 2;
+       }
+
+       ksz_update_timer(&hw_priv->mib_timer_info);
+}
+
+/**
+ * dev_monitor - periodic monitoring
+ * @ptr:       Network device pointer.
+ *
+ * This routine is run in a kernel timer to monitor the network device.
+ */
+static void dev_monitor(unsigned long ptr)
+{
+       struct net_device *dev = (struct net_device *) ptr;
+       struct dev_priv *priv = netdev_priv(dev);
+       struct dev_info *hw_priv = priv->adapter;
+       struct ksz_hw *hw = &hw_priv->hw;
+       struct ksz_port *port = &priv->port;
+
+       if (!(hw->features & LINK_INT_WORKING))
+               port_get_link_speed(port);
+       update_link(dev, priv, port);
+
+       ksz_update_timer(&priv->monitor_timer_info);
+}
+
+/*
+ * Linux network device interface functions
+ */
+
+/* Driver exported variables */
+
+static int msg_enable;
+
+static char *macaddr = ":";
+static char *mac1addr = ":";
+
+/*
+ * This enables multiple network device mode for KSZ8842, which contains a
+ * switch with two physical ports.  Some users like to take control of the
+ * ports for running Spanning Tree Protocol.  The driver will create an
+ * additional eth? device for the other port.
+ *
+ * Some limitations are the network devices cannot have different MTU and
+ * multicast hash tables.
+ */
+static int multi_dev;
+
+/*
+ * As most users select multiple network device mode to use Spanning Tree
+ * Protocol, this enables a feature in which most unicast and multicast packets
+ * are forwarded inside the switch and not passed to the host.  Only packets
+ * that need the host's attention are passed to it.  This prevents the host
+ * wasting CPU time to examine each and every incoming packets and do the
+ * forwarding itself.
+ *
+ * As the hack requires the private bridge header, the driver cannot compile
+ * with just the kernel headers.
+ *
+ * Enabling STP support also turns on multiple network device mode.
+ */
+static int stp;
+
+/*
+ * This enables fast aging in the KSZ8842 switch.  Not sure what situation
+ * needs that.  However, fast aging is used to flush the dynamic MAC table when
+ * STP suport is enabled.
+ */
+static int fast_aging;
+
+/**
+ * netdev_init - initialize network device.
+ * @dev:       Network device.
+ *
+ * This function initializes the network device.
+ *
+ * Return 0 if successful; otherwise an error code indicating failure.
+ */
+static int __init netdev_init(struct net_device *dev)
+{
+       struct dev_priv *priv = netdev_priv(dev);
+
+       /* 500 ms timeout */
+       ksz_init_timer(&priv->monitor_timer_info, 500 * HZ / 1000,
+               dev_monitor, dev);
+
+       /* 500 ms timeout */
+       dev->watchdog_timeo = HZ / 2;
+
+       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_RXCSUM;
+
+       /*
+        * Hardware does not really support IPv6 checksum generation, but
+        * driver actually runs faster with this on.
+        */
+       dev->hw_features |= NETIF_F_IPV6_CSUM;
+
+       dev->features |= dev->hw_features;
+
+       sema_init(&priv->proc_sem, 1);
+
+       priv->mii_if.phy_id_mask = 0x1;
+       priv->mii_if.reg_num_mask = 0x7;
+       priv->mii_if.dev = dev;
+       priv->mii_if.mdio_read = mdio_read;
+       priv->mii_if.mdio_write = mdio_write;
+       priv->mii_if.phy_id = priv->port.first_port + 1;
+
+       priv->msg_enable = netif_msg_init(msg_enable,
+               (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK));
+
+       return 0;
+}
+
+static const struct net_device_ops netdev_ops = {
+       .ndo_init               = netdev_init,
+       .ndo_open               = netdev_open,
+       .ndo_stop               = netdev_close,
+       .ndo_get_stats          = netdev_query_statistics,
+       .ndo_start_xmit         = netdev_tx,
+       .ndo_tx_timeout         = netdev_tx_timeout,
+       .ndo_change_mtu         = netdev_change_mtu,
+       .ndo_set_features       = netdev_set_features,
+       .ndo_set_mac_address    = netdev_set_mac_address,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_do_ioctl           = netdev_ioctl,
+       .ndo_set_rx_mode        = netdev_set_rx_mode,
+#ifdef CONFIG_NET_POLL_CONTROLLER
+       .ndo_poll_controller    = netdev_netpoll,
+#endif
+};
+
+static void netdev_free(struct net_device *dev)
+{
+       if (dev->watchdog_timeo)
+               unregister_netdev(dev);
+
+       free_netdev(dev);
+}
+
+struct platform_info {
+       struct dev_info dev_info;
+       struct net_device *netdev[SWITCH_PORT_NUM];
+};
+
+static int net_device_present;
+
+static void get_mac_addr(struct dev_info *hw_priv, u8 *macaddr, int port)
+{
+       int i;
+       int j;
+       int got_num;
+       int num;
+
+       i = j = num = got_num = 0;
+       while (j < MAC_ADDR_LEN) {
+               if (macaddr[i]) {
+                       int digit;
+
+                       got_num = 1;
+                       digit = hex_to_bin(macaddr[i]);
+                       if (digit >= 0)
+                               num = num * 16 + digit;
+                       else if (':' == macaddr[i])
+                               got_num = 2;
+                       else
+                               break;
+               } else if (got_num)
+                       got_num = 2;
+               else
+                       break;
+               if (2 == got_num) {
+                       if (MAIN_PORT == port) {
+                               hw_priv->hw.override_addr[j++] = (u8) num;
+                               hw_priv->hw.override_addr[5] +=
+                                       hw_priv->hw.id;
+                       } else {
+                               hw_priv->hw.ksz_switch->other_addr[j++] =
+                                       (u8) num;
+                               hw_priv->hw.ksz_switch->other_addr[5] +=
+                                       hw_priv->hw.id;
+                       }
+                       num = got_num = 0;
+               }
+               i++;
+       }
+       if (MAC_ADDR_LEN == j) {
+               if (MAIN_PORT == port)
+                       hw_priv->hw.mac_override = 1;
+       }
+}
+
+#define KS884X_DMA_MASK                        (~0x0UL)
+
+static void read_other_addr(struct ksz_hw *hw)
+{
+       int i;
+       u16 data[3];
+       struct ksz_switch *sw = hw->ksz_switch;
+
+       for (i = 0; i < 3; i++)
+               data[i] = eeprom_read(hw, i + EEPROM_DATA_OTHER_MAC_ADDR);
+       if ((data[0] || data[1] || data[2]) && data[0] != 0xffff) {
+               sw->other_addr[5] = (u8) data[0];
+               sw->other_addr[4] = (u8)(data[0] >> 8);
+               sw->other_addr[3] = (u8) data[1];
+               sw->other_addr[2] = (u8)(data[1] >> 8);
+               sw->other_addr[1] = (u8) data[2];
+               sw->other_addr[0] = (u8)(data[2] >> 8);
+       }
+}
+
+#ifndef PCI_VENDOR_ID_MICREL_KS
+#define PCI_VENDOR_ID_MICREL_KS                0x16c6
+#endif
+
+static int __devinit pcidev_init(struct pci_dev *pdev,
+       const struct pci_device_id *id)
+{
+       struct net_device *dev;
+       struct dev_priv *priv;
+       struct dev_info *hw_priv;
+       struct ksz_hw *hw;
+       struct platform_info *info;
+       struct ksz_port *port;
+       unsigned long reg_base;
+       unsigned long reg_len;
+       int cnt;
+       int i;
+       int mib_port_count;
+       int pi;
+       int port_count;
+       int result;
+       char banner[sizeof(version)];
+       struct ksz_switch *sw = NULL;
+
+       result = pci_enable_device(pdev);
+       if (result)
+               return result;
+
+       result = -ENODEV;
+
+       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
+                       pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
+               return result;
+
+       reg_base = pci_resource_start(pdev, 0);
+       reg_len = pci_resource_len(pdev, 0);
+       if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0)
+               return result;
+
+       if (!request_mem_region(reg_base, reg_len, DRV_NAME))
+               return result;
+       pci_set_master(pdev);
+
+       result = -ENOMEM;
+
+       info = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
+       if (!info)
+               goto pcidev_init_dev_err;
+
+       hw_priv = &info->dev_info;
+       hw_priv->pdev = pdev;
+
+       hw = &hw_priv->hw;
+
+       hw->io = ioremap(reg_base, reg_len);
+       if (!hw->io)
+               goto pcidev_init_io_err;
+
+       cnt = hw_init(hw);
+       if (!cnt) {
+               if (msg_enable & NETIF_MSG_PROBE)
+                       pr_alert("chip not detected\n");
+               result = -ENODEV;
+               goto pcidev_init_alloc_err;
+       }
+
+       snprintf(banner, sizeof(banner), "%s", version);
+       banner[13] = cnt + '0';         /* Replace x in "Micrel KSZ884x" */
+       dev_info(&hw_priv->pdev->dev, "%s\n", banner);
+       dev_dbg(&hw_priv->pdev->dev, "Mem = %p; IRQ = %d\n", hw->io, pdev->irq);
+
+       /* Assume device is KSZ8841. */
+       hw->dev_count = 1;
+       port_count = 1;
+       mib_port_count = 1;
+       hw->addr_list_size = 0;
+       hw->mib_cnt = PORT_COUNTER_NUM;
+       hw->mib_port_cnt = 1;
+
+       /* KSZ8842 has a switch with multiple ports. */
+       if (2 == cnt) {
+               if (fast_aging)
+                       hw->overrides |= FAST_AGING;
+
+               hw->mib_cnt = TOTAL_PORT_COUNTER_NUM;
+
+               /* Multiple network device interfaces are required. */
+               if (multi_dev) {
+                       hw->dev_count = SWITCH_PORT_NUM;
+                       hw->addr_list_size = SWITCH_PORT_NUM - 1;
+               }
+
+               /* Single network device has multiple ports. */
+               if (1 == hw->dev_count) {
+                       port_count = SWITCH_PORT_NUM;
+                       mib_port_count = SWITCH_PORT_NUM;
+               }
+               hw->mib_port_cnt = TOTAL_PORT_NUM;
+               hw->ksz_switch = kzalloc(sizeof(struct ksz_switch), GFP_KERNEL);
+               if (!hw->ksz_switch)
+                       goto pcidev_init_alloc_err;
+
+               sw = hw->ksz_switch;
+       }
+       for (i = 0; i < hw->mib_port_cnt; i++)
+               hw->port_mib[i].mib_start = 0;
+
+       hw->parent = hw_priv;
+
+       /* Default MTU is 1500. */
+       hw_priv->mtu = (REGULAR_RX_BUF_SIZE + 3) & ~3;
+
+       if (ksz_alloc_mem(hw_priv))
+               goto pcidev_init_mem_err;
+
+       hw_priv->hw.id = net_device_present;
+
+       spin_lock_init(&hw_priv->hwlock);
+       mutex_init(&hw_priv->lock);
+
+       /* tasklet is enabled. */
+       tasklet_init(&hw_priv->rx_tasklet, rx_proc_task,
+               (unsigned long) hw_priv);
+       tasklet_init(&hw_priv->tx_tasklet, tx_proc_task,
+               (unsigned long) hw_priv);
+
+       /* tasklet_enable will decrement the atomic counter. */
+       tasklet_disable(&hw_priv->rx_tasklet);
+       tasklet_disable(&hw_priv->tx_tasklet);
+
+       for (i = 0; i < TOTAL_PORT_NUM; i++)
+               init_waitqueue_head(&hw_priv->counter[i].counter);
+
+       if (macaddr[0] != ':')
+               get_mac_addr(hw_priv, macaddr, MAIN_PORT);
+
+       /* Read MAC address and initialize override address if not overrided. */
+       hw_read_addr(hw);
+
+       /* Multiple device interfaces mode requires a second MAC address. */
+       if (hw->dev_count > 1) {
+               memcpy(sw->other_addr, hw->override_addr, MAC_ADDR_LEN);
+               read_other_addr(hw);
+               if (mac1addr[0] != ':')
+                       get_mac_addr(hw_priv, mac1addr, OTHER_PORT);
+       }
+
+       hw_setup(hw);
+       if (hw->ksz_switch)
+               sw_setup(hw);
+       else {
+               hw_priv->wol_support = WOL_SUPPORT;
+               hw_priv->wol_enable = 0;
+       }
+
+       INIT_WORK(&hw_priv->mib_read, mib_read_work);
+
+       /* 500 ms timeout */
+       ksz_init_timer(&hw_priv->mib_timer_info, 500 * HZ / 1000,
+               mib_monitor, hw_priv);
+
+       for (i = 0; i < hw->dev_count; i++) {
+               dev = alloc_etherdev(sizeof(struct dev_priv));
+               if (!dev)
+                       goto pcidev_init_reg_err;
+               info->netdev[i] = dev;
+
+               priv = netdev_priv(dev);
+               priv->adapter = hw_priv;
+               priv->id = net_device_present++;
+
+               port = &priv->port;
+               port->port_cnt = port_count;
+               port->mib_port_cnt = mib_port_count;
+               port->first_port = i;
+               port->flow_ctrl = PHY_FLOW_CTRL;
+
+               port->hw = hw;
+               port->linked = &hw->port_info[port->first_port];
+
+               for (cnt = 0, pi = i; cnt < port_count; cnt++, pi++) {
+                       hw->port_info[pi].port_id = pi;
+                       hw->port_info[pi].pdev = dev;
+                       hw->port_info[pi].state = media_disconnected;
+               }
+
+               dev->mem_start = (unsigned long) hw->io;
+               dev->mem_end = dev->mem_start + reg_len - 1;
+               dev->irq = pdev->irq;
+               if (MAIN_PORT == i)
+                       memcpy(dev->dev_addr, hw_priv->hw.override_addr,
+                               MAC_ADDR_LEN);
+               else {
+                       memcpy(dev->dev_addr, sw->other_addr,
+                               MAC_ADDR_LEN);
+                       if (!memcmp(sw->other_addr, hw->override_addr,
+                                       MAC_ADDR_LEN))
+                               dev->dev_addr[5] += port->first_port;
+               }
+
+               dev->netdev_ops = &netdev_ops;
+               SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
+               if (register_netdev(dev))
+                       goto pcidev_init_reg_err;
+               port_set_power_saving(port, true);
+       }
+
+       pci_dev_get(hw_priv->pdev);
+       pci_set_drvdata(pdev, info);
+       return 0;
+
+pcidev_init_reg_err:
+       for (i = 0; i < hw->dev_count; i++) {
+               if (info->netdev[i]) {
+                       netdev_free(info->netdev[i]);
+                       info->netdev[i] = NULL;
+               }
+       }
+
+pcidev_init_mem_err:
+       ksz_free_mem(hw_priv);
+       kfree(hw->ksz_switch);
+
+pcidev_init_alloc_err:
+       iounmap(hw->io);
+
+pcidev_init_io_err:
+       kfree(info);
+
+pcidev_init_dev_err:
+       release_mem_region(reg_base, reg_len);
+
+       return result;
+}
+
+static void pcidev_exit(struct pci_dev *pdev)
+{
+       int i;
+       struct platform_info *info = pci_get_drvdata(pdev);
+       struct dev_info *hw_priv = &info->dev_info;
+
+       pci_set_drvdata(pdev, NULL);
+
+       release_mem_region(pci_resource_start(pdev, 0),
+               pci_resource_len(pdev, 0));
+       for (i = 0; i < hw_priv->hw.dev_count; i++) {
+               if (info->netdev[i])
+                       netdev_free(info->netdev[i]);
+       }
+       if (hw_priv->hw.io)
+               iounmap(hw_priv->hw.io);
+       ksz_free_mem(hw_priv);
+       kfree(hw_priv->hw.ksz_switch);
+       pci_dev_put(hw_priv->pdev);
+       kfree(info);
+}
+
+#ifdef CONFIG_PM
+static int pcidev_resume(struct pci_dev *pdev)
+{
+       int i;
+       struct platform_info *info = pci_get_drvdata(pdev);
+       struct dev_info *hw_priv = &info->dev_info;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_restore_state(pdev);
+       pci_enable_wake(pdev, PCI_D0, 0);
+
+       if (hw_priv->wol_enable)
+               hw_cfg_wol_pme(hw, 0);
+       for (i = 0; i < hw->dev_count; i++) {
+               if (info->netdev[i]) {
+                       struct net_device *dev = info->netdev[i];
+
+                       if (netif_running(dev)) {
+                               netdev_open(dev);
+                               netif_device_attach(dev);
+                       }
+               }
+       }
+       return 0;
+}
+
+static int pcidev_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       int i;
+       struct platform_info *info = pci_get_drvdata(pdev);
+       struct dev_info *hw_priv = &info->dev_info;
+       struct ksz_hw *hw = &hw_priv->hw;
+
+       /* Need to find a way to retrieve the device IP address. */
+       static const u8 net_addr[] = { 192, 168, 1, 1 };
+
+       for (i = 0; i < hw->dev_count; i++) {
+               if (info->netdev[i]) {
+                       struct net_device *dev = info->netdev[i];
+
+                       if (netif_running(dev)) {
+                               netif_device_detach(dev);
+                               netdev_close(dev);
+                       }
+               }
+       }
+       if (hw_priv->wol_enable) {
+               hw_enable_wol(hw, hw_priv->wol_enable, net_addr);
+               hw_cfg_wol_pme(hw, 1);
+       }
+
+       pci_save_state(pdev);
+       pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+       pci_set_power_state(pdev, pci_choose_state(pdev, state));
+       return 0;
+}
+#endif
+
+static char pcidev_name[] = "ksz884xp";
+
+static struct pci_device_id pcidev_table[] = {
+       { PCI_VENDOR_ID_MICREL_KS, 0x8841,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { PCI_VENDOR_ID_MICREL_KS, 0x8842,
+               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+       { 0 }
+};
+
+MODULE_DEVICE_TABLE(pci, pcidev_table);
+
+static struct pci_driver pci_device_driver = {
+#ifdef CONFIG_PM
+       .suspend        = pcidev_suspend,
+       .resume         = pcidev_resume,
+#endif
+       .name           = pcidev_name,
+       .id_table       = pcidev_table,
+       .probe          = pcidev_init,
+       .remove         = pcidev_exit
+};
+
+static int __init ksz884x_init_module(void)
+{
+       return pci_register_driver(&pci_device_driver);
+}
+
+static void __exit ksz884x_cleanup_module(void)
+{
+       pci_unregister_driver(&pci_device_driver);
+}
+
+module_init(ksz884x_init_module);
+module_exit(ksz884x_cleanup_module);
+
+MODULE_DESCRIPTION("KSZ8841/2 PCI network driver");
+MODULE_AUTHOR("Tristram Ha <Tristram.Ha@micrel.com>");
+MODULE_LICENSE("GPL");
+
+module_param_named(message, msg_enable, int, 0);
+MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
+
+module_param(macaddr, charp, 0);
+module_param(mac1addr, charp, 0);
+module_param(fast_aging, int, 0);
+module_param(multi_dev, int, 0);
+module_param(stp, int, 0);
+MODULE_PARM_DESC(macaddr, "MAC address");
+MODULE_PARM_DESC(mac1addr, "Second MAC address");
+MODULE_PARM_DESC(fast_aging, "Fast aging");
+MODULE_PARM_DESC(multi_dev, "Multiple device interfaces");
+MODULE_PARM_DESC(stp, "STP support");
diff --git a/drivers/net/ks8842.c b/drivers/net/ks8842.c
deleted file mode 100644 (file)
index 4a6ae05..0000000
+++ /dev/null
@@ -1,1284 +0,0 @@
-/*
- * ks8842.c timberdale KS8842 ethernet driver
- * Copyright (c) 2009 Intel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/* Supports:
- * The Micrel KS8842 behind the timberdale FPGA
- * The genuine Micrel KS8841/42 device with ISA 16/32bit bus interface
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/ks8842.h>
-#include <linux/dmaengine.h>
-#include <linux/dma-mapping.h>
-#include <linux/scatterlist.h>
-
-#define DRV_NAME "ks8842"
-
-/* Timberdale specific Registers */
-#define REG_TIMB_RST           0x1c
-#define REG_TIMB_FIFO          0x20
-#define REG_TIMB_ISR           0x24
-#define REG_TIMB_IER           0x28
-#define REG_TIMB_IAR           0x2C
-#define REQ_TIMB_DMA_RESUME    0x30
-
-/* KS8842 registers */
-
-#define REG_SELECT_BANK 0x0e
-
-/* bank 0 registers */
-#define REG_QRFCR      0x04
-
-/* bank 2 registers */
-#define REG_MARL       0x00
-#define REG_MARM       0x02
-#define REG_MARH       0x04
-
-/* bank 3 registers */
-#define REG_GRR                0x06
-
-/* bank 16 registers */
-#define REG_TXCR       0x00
-#define REG_TXSR       0x02
-#define REG_RXCR       0x04
-#define REG_TXMIR      0x08
-#define REG_RXMIR      0x0A
-
-/* bank 17 registers */
-#define REG_TXQCR      0x00
-#define REG_RXQCR      0x02
-#define REG_TXFDPR     0x04
-#define REG_RXFDPR     0x06
-#define REG_QMU_DATA_LO 0x08
-#define REG_QMU_DATA_HI 0x0A
-
-/* bank 18 registers */
-#define REG_IER                0x00
-#define IRQ_LINK_CHANGE        0x8000
-#define IRQ_TX         0x4000
-#define IRQ_RX         0x2000
-#define IRQ_RX_OVERRUN 0x0800
-#define IRQ_TX_STOPPED 0x0200
-#define IRQ_RX_STOPPED 0x0100
-#define IRQ_RX_ERROR   0x0080
-#define ENABLED_IRQS   (IRQ_LINK_CHANGE | IRQ_TX | IRQ_RX | IRQ_RX_STOPPED | \
-               IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR)
-/* When running via timberdale in DMA mode, the RX interrupt should be
-   enabled in the KS8842, but not in the FPGA IP, since the IP handles
-   RX DMA internally.
-   TX interrupts are not needed it is handled by the FPGA the driver is
-   notified via DMA callbacks.
-*/
-#define ENABLED_IRQS_DMA_IP    (IRQ_LINK_CHANGE | IRQ_RX_STOPPED | \
-       IRQ_TX_STOPPED | IRQ_RX_OVERRUN | IRQ_RX_ERROR)
-#define ENABLED_IRQS_DMA       (ENABLED_IRQS_DMA_IP | IRQ_RX)
-#define REG_ISR                0x02
-#define REG_RXSR       0x04
-#define RXSR_VALID     0x8000
-#define RXSR_BROADCAST 0x80
-#define RXSR_MULTICAST 0x40
-#define RXSR_UNICAST   0x20
-#define RXSR_FRAMETYPE 0x08
-#define RXSR_TOO_LONG  0x04
-#define RXSR_RUNT      0x02
-#define RXSR_CRC_ERROR 0x01
-#define RXSR_ERROR     (RXSR_TOO_LONG | RXSR_RUNT | RXSR_CRC_ERROR)
-
-/* bank 32 registers */
-#define REG_SW_ID_AND_ENABLE   0x00
-#define REG_SGCR1              0x02
-#define REG_SGCR2              0x04
-#define REG_SGCR3              0x06
-
-/* bank 39 registers */
-#define REG_MACAR1             0x00
-#define REG_MACAR2             0x02
-#define REG_MACAR3             0x04
-
-/* bank 45 registers */
-#define REG_P1MBCR             0x00
-#define REG_P1MBSR             0x02
-
-/* bank 46 registers */
-#define REG_P2MBCR             0x00
-#define REG_P2MBSR             0x02
-
-/* bank 48 registers */
-#define REG_P1CR2              0x02
-
-/* bank 49 registers */
-#define REG_P1CR4              0x02
-#define REG_P1SR               0x04
-
-/* flags passed by platform_device for configuration */
-#define        MICREL_KS884X           0x01    /* 0=Timeberdale(FPGA), 1=Micrel */
-#define        KS884X_16BIT            0x02    /*  1=16bit, 0=32bit */
-
-#define DMA_BUFFER_SIZE                2048
-
-struct ks8842_tx_dma_ctl {
-       struct dma_chan *chan;
-       struct dma_async_tx_descriptor *adesc;
-       void *buf;
-       struct scatterlist sg;
-       int channel;
-};
-
-struct ks8842_rx_dma_ctl {
-       struct dma_chan *chan;
-       struct dma_async_tx_descriptor *adesc;
-       struct sk_buff  *skb;
-       struct scatterlist sg;
-       struct tasklet_struct tasklet;
-       int channel;
-};
-
-#define KS8842_USE_DMA(adapter) (((adapter)->dma_tx.channel != -1) && \
-        ((adapter)->dma_rx.channel != -1))
-
-struct ks8842_adapter {
-       void __iomem    *hw_addr;
-       int             irq;
-       unsigned long   conf_flags;     /* copy of platform_device config */
-       struct tasklet_struct   tasklet;
-       spinlock_t      lock; /* spinlock to be interrupt safe */
-       struct work_struct timeout_work;
-       struct net_device *netdev;
-       struct device *dev;
-       struct ks8842_tx_dma_ctl        dma_tx;
-       struct ks8842_rx_dma_ctl        dma_rx;
-};
-
-static void ks8842_dma_rx_cb(void *data);
-static void ks8842_dma_tx_cb(void *data);
-
-static inline void ks8842_resume_dma(struct ks8842_adapter *adapter)
-{
-       iowrite32(1, adapter->hw_addr + REQ_TIMB_DMA_RESUME);
-}
-
-static inline void ks8842_select_bank(struct ks8842_adapter *adapter, u16 bank)
-{
-       iowrite16(bank, adapter->hw_addr + REG_SELECT_BANK);
-}
-
-static inline void ks8842_write8(struct ks8842_adapter *adapter, u16 bank,
-       u8 value, int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       iowrite8(value, adapter->hw_addr + offset);
-}
-
-static inline void ks8842_write16(struct ks8842_adapter *adapter, u16 bank,
-       u16 value, int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       iowrite16(value, adapter->hw_addr + offset);
-}
-
-static inline void ks8842_enable_bits(struct ks8842_adapter *adapter, u16 bank,
-       u16 bits, int offset)
-{
-       u16 reg;
-       ks8842_select_bank(adapter, bank);
-       reg = ioread16(adapter->hw_addr + offset);
-       reg |= bits;
-       iowrite16(reg, adapter->hw_addr + offset);
-}
-
-static inline void ks8842_clear_bits(struct ks8842_adapter *adapter, u16 bank,
-       u16 bits, int offset)
-{
-       u16 reg;
-       ks8842_select_bank(adapter, bank);
-       reg = ioread16(adapter->hw_addr + offset);
-       reg &= ~bits;
-       iowrite16(reg, adapter->hw_addr + offset);
-}
-
-static inline void ks8842_write32(struct ks8842_adapter *adapter, u16 bank,
-       u32 value, int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       iowrite32(value, adapter->hw_addr + offset);
-}
-
-static inline u8 ks8842_read8(struct ks8842_adapter *adapter, u16 bank,
-       int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       return ioread8(adapter->hw_addr + offset);
-}
-
-static inline u16 ks8842_read16(struct ks8842_adapter *adapter, u16 bank,
-       int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       return ioread16(adapter->hw_addr + offset);
-}
-
-static inline u32 ks8842_read32(struct ks8842_adapter *adapter, u16 bank,
-       int offset)
-{
-       ks8842_select_bank(adapter, bank);
-       return ioread32(adapter->hw_addr + offset);
-}
-
-static void ks8842_reset(struct ks8842_adapter *adapter)
-{
-       if (adapter->conf_flags & MICREL_KS884X) {
-               ks8842_write16(adapter, 3, 1, REG_GRR);
-               msleep(10);
-               iowrite16(0, adapter->hw_addr + REG_GRR);
-       } else {
-               /* The KS8842 goes haywire when doing softare reset
-               * a work around in the timberdale IP is implemented to
-               * do a hardware reset instead
-               ks8842_write16(adapter, 3, 1, REG_GRR);
-               msleep(10);
-               iowrite16(0, adapter->hw_addr + REG_GRR);
-               */
-               iowrite32(0x1, adapter->hw_addr + REG_TIMB_RST);
-               msleep(20);
-       }
-}
-
-static void ks8842_update_link_status(struct net_device *netdev,
-       struct ks8842_adapter *adapter)
-{
-       /* check the status of the link */
-       if (ks8842_read16(adapter, 45, REG_P1MBSR) & 0x4) {
-               netif_carrier_on(netdev);
-               netif_wake_queue(netdev);
-       } else {
-               netif_stop_queue(netdev);
-               netif_carrier_off(netdev);
-       }
-}
-
-static void ks8842_enable_tx(struct ks8842_adapter *adapter)
-{
-       ks8842_enable_bits(adapter, 16, 0x01, REG_TXCR);
-}
-
-static void ks8842_disable_tx(struct ks8842_adapter *adapter)
-{
-       ks8842_clear_bits(adapter, 16, 0x01, REG_TXCR);
-}
-
-static void ks8842_enable_rx(struct ks8842_adapter *adapter)
-{
-       ks8842_enable_bits(adapter, 16, 0x01, REG_RXCR);
-}
-
-static void ks8842_disable_rx(struct ks8842_adapter *adapter)
-{
-       ks8842_clear_bits(adapter, 16, 0x01, REG_RXCR);
-}
-
-static void ks8842_reset_hw(struct ks8842_adapter *adapter)
-{
-       /* reset the HW */
-       ks8842_reset(adapter);
-
-       /* Enable QMU Transmit flow control / transmit padding / Transmit CRC */
-       ks8842_write16(adapter, 16, 0x000E, REG_TXCR);
-
-       /* enable the receiver, uni + multi + broadcast + flow ctrl
-               + crc strip */
-       ks8842_write16(adapter, 16, 0x8 | 0x20 | 0x40 | 0x80 | 0x400,
-               REG_RXCR);
-
-       /* TX frame pointer autoincrement */
-       ks8842_write16(adapter, 17, 0x4000, REG_TXFDPR);
-
-       /* RX frame pointer autoincrement */
-       ks8842_write16(adapter, 17, 0x4000, REG_RXFDPR);
-
-       /* RX 2 kb high watermark */
-       ks8842_write16(adapter, 0, 0x1000, REG_QRFCR);
-
-       /* aggressive back off in half duplex */
-       ks8842_enable_bits(adapter, 32, 1 << 8, REG_SGCR1);
-
-       /* enable no excessive collison drop */
-       ks8842_enable_bits(adapter, 32, 1 << 3, REG_SGCR2);
-
-       /* Enable port 1 force flow control / back pressure / transmit / recv */
-       ks8842_write16(adapter, 48, 0x1E07, REG_P1CR2);
-
-       /* restart port auto-negotiation */
-       ks8842_enable_bits(adapter, 49, 1 << 13, REG_P1CR4);
-
-       /* Enable the transmitter */
-       ks8842_enable_tx(adapter);
-
-       /* Enable the receiver */
-       ks8842_enable_rx(adapter);
-
-       /* clear all interrupts */
-       ks8842_write16(adapter, 18, 0xffff, REG_ISR);
-
-       /* enable interrupts */
-       if (KS8842_USE_DMA(adapter)) {
-               /* When running in DMA Mode the RX interrupt is not enabled in
-                  timberdale because RX data is received by DMA callbacks
-                  it must still be enabled in the KS8842 because it indicates
-                  to timberdale when there is RX data for it's DMA FIFOs */
-               iowrite16(ENABLED_IRQS_DMA_IP, adapter->hw_addr + REG_TIMB_IER);
-               ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER);
-       } else {
-               if (!(adapter->conf_flags & MICREL_KS884X))
-                       iowrite16(ENABLED_IRQS,
-                               adapter->hw_addr + REG_TIMB_IER);
-               ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER);
-       }
-       /* enable the switch */
-       ks8842_write16(adapter, 32, 0x1, REG_SW_ID_AND_ENABLE);
-}
-
-static void ks8842_read_mac_addr(struct ks8842_adapter *adapter, u8 *dest)
-{
-       int i;
-       u16 mac;
-
-       for (i = 0; i < ETH_ALEN; i++)
-               dest[ETH_ALEN - i - 1] = ks8842_read8(adapter, 2, REG_MARL + i);
-
-       if (adapter->conf_flags & MICREL_KS884X) {
-               /*
-               the sequence of saving mac addr between MAC and Switch is
-               different.
-               */
-
-               mac = ks8842_read16(adapter, 2, REG_MARL);
-               ks8842_write16(adapter, 39, mac, REG_MACAR3);
-               mac = ks8842_read16(adapter, 2, REG_MARM);
-               ks8842_write16(adapter, 39, mac, REG_MACAR2);
-               mac = ks8842_read16(adapter, 2, REG_MARH);
-               ks8842_write16(adapter, 39, mac, REG_MACAR1);
-       } else {
-
-               /* make sure the switch port uses the same MAC as the QMU */
-               mac = ks8842_read16(adapter, 2, REG_MARL);
-               ks8842_write16(adapter, 39, mac, REG_MACAR1);
-               mac = ks8842_read16(adapter, 2, REG_MARM);
-               ks8842_write16(adapter, 39, mac, REG_MACAR2);
-               mac = ks8842_read16(adapter, 2, REG_MARH);
-               ks8842_write16(adapter, 39, mac, REG_MACAR3);
-       }
-}
-
-static void ks8842_write_mac_addr(struct ks8842_adapter *adapter, u8 *mac)
-{
-       unsigned long flags;
-       unsigned i;
-
-       spin_lock_irqsave(&adapter->lock, flags);
-       for (i = 0; i < ETH_ALEN; i++) {
-               ks8842_write8(adapter, 2, mac[ETH_ALEN - i - 1], REG_MARL + i);
-               if (!(adapter->conf_flags & MICREL_KS884X))
-                       ks8842_write8(adapter, 39, mac[ETH_ALEN - i - 1],
-                               REG_MACAR1 + i);
-       }
-
-       if (adapter->conf_flags & MICREL_KS884X) {
-               /*
-               the sequence of saving mac addr between MAC and Switch is
-               different.
-               */
-
-               u16 mac;
-
-               mac = ks8842_read16(adapter, 2, REG_MARL);
-               ks8842_write16(adapter, 39, mac, REG_MACAR3);
-               mac = ks8842_read16(adapter, 2, REG_MARM);
-               ks8842_write16(adapter, 39, mac, REG_MACAR2);
-               mac = ks8842_read16(adapter, 2, REG_MARH);
-               ks8842_write16(adapter, 39, mac, REG_MACAR1);
-       }
-       spin_unlock_irqrestore(&adapter->lock, flags);
-}
-
-static inline u16 ks8842_tx_fifo_space(struct ks8842_adapter *adapter)
-{
-       return ks8842_read16(adapter, 16, REG_TXMIR) & 0x1fff;
-}
-
-static int ks8842_tx_frame_dma(struct sk_buff *skb, struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct ks8842_tx_dma_ctl *ctl = &adapter->dma_tx;
-       u8 *buf = ctl->buf;
-
-       if (ctl->adesc) {
-               netdev_dbg(netdev, "%s: TX ongoing\n", __func__);
-               /* transfer ongoing */
-               return NETDEV_TX_BUSY;
-       }
-
-       sg_dma_len(&ctl->sg) = skb->len + sizeof(u32);
-
-       /* copy data to the TX buffer */
-       /* the control word, enable IRQ, port 1 and the length */
-       *buf++ = 0x00;
-       *buf++ = 0x01; /* Port 1 */
-       *buf++ = skb->len & 0xff;
-       *buf++ = (skb->len >> 8) & 0xff;
-       skb_copy_from_linear_data(skb, buf, skb->len);
-
-       dma_sync_single_range_for_device(adapter->dev,
-               sg_dma_address(&ctl->sg), 0, sg_dma_len(&ctl->sg),
-               DMA_TO_DEVICE);
-
-       /* make sure the length is a multiple of 4 */
-       if (sg_dma_len(&ctl->sg) % 4)
-               sg_dma_len(&ctl->sg) += 4 - sg_dma_len(&ctl->sg) % 4;
-
-       ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
-               &ctl->sg, 1, DMA_TO_DEVICE,
-               DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
-       if (!ctl->adesc)
-               return NETDEV_TX_BUSY;
-
-       ctl->adesc->callback_param = netdev;
-       ctl->adesc->callback = ks8842_dma_tx_cb;
-       ctl->adesc->tx_submit(ctl->adesc);
-
-       netdev->stats.tx_bytes += skb->len;
-
-       dev_kfree_skb(skb);
-
-       return NETDEV_TX_OK;
-}
-
-static int ks8842_tx_frame(struct sk_buff *skb, struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       int len = skb->len;
-
-       netdev_dbg(netdev, "%s: len %u head %p data %p tail %p end %p\n",
-               __func__, skb->len, skb->head, skb->data,
-               skb_tail_pointer(skb), skb_end_pointer(skb));
-
-       /* check FIFO buffer space, we need space for CRC and command bits */
-       if (ks8842_tx_fifo_space(adapter) < len + 8)
-               return NETDEV_TX_BUSY;
-
-       if (adapter->conf_flags & KS884X_16BIT) {
-               u16 *ptr16 = (u16 *)skb->data;
-               ks8842_write16(adapter, 17, 0x8000 | 0x100, REG_QMU_DATA_LO);
-               ks8842_write16(adapter, 17, (u16)len, REG_QMU_DATA_HI);
-               netdev->stats.tx_bytes += len;
-
-               /* copy buffer */
-               while (len > 0) {
-                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_LO);
-                       iowrite16(*ptr16++, adapter->hw_addr + REG_QMU_DATA_HI);
-                       len -= sizeof(u32);
-               }
-       } else {
-
-               u32 *ptr = (u32 *)skb->data;
-               u32 ctrl;
-               /* the control word, enable IRQ, port 1 and the length */
-               ctrl = 0x8000 | 0x100 | (len << 16);
-               ks8842_write32(adapter, 17, ctrl, REG_QMU_DATA_LO);
-
-               netdev->stats.tx_bytes += len;
-
-               /* copy buffer */
-               while (len > 0) {
-                       iowrite32(*ptr, adapter->hw_addr + REG_QMU_DATA_LO);
-                       len -= sizeof(u32);
-                       ptr++;
-               }
-       }
-
-       /* enqueue packet */
-       ks8842_write16(adapter, 17, 1, REG_TXQCR);
-
-       dev_kfree_skb(skb);
-
-       return NETDEV_TX_OK;
-}
-
-static void ks8842_update_rx_err_counters(struct net_device *netdev, u32 status)
-{
-       netdev_dbg(netdev, "RX error, status: %x\n", status);
-
-       netdev->stats.rx_errors++;
-       if (status & RXSR_TOO_LONG)
-               netdev->stats.rx_length_errors++;
-       if (status & RXSR_CRC_ERROR)
-               netdev->stats.rx_crc_errors++;
-       if (status & RXSR_RUNT)
-               netdev->stats.rx_frame_errors++;
-}
-
-static void ks8842_update_rx_counters(struct net_device *netdev, u32 status,
-       int len)
-{
-       netdev_dbg(netdev, "RX packet, len: %d\n", len);
-
-       netdev->stats.rx_packets++;
-       netdev->stats.rx_bytes += len;
-       if (status & RXSR_MULTICAST)
-               netdev->stats.multicast++;
-}
-
-static int __ks8842_start_new_rx_dma(struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx;
-       struct scatterlist *sg = &ctl->sg;
-       int err;
-
-       ctl->skb = netdev_alloc_skb(netdev, DMA_BUFFER_SIZE);
-       if (ctl->skb) {
-               sg_init_table(sg, 1);
-               sg_dma_address(sg) = dma_map_single(adapter->dev,
-                       ctl->skb->data, DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-               err = dma_mapping_error(adapter->dev, sg_dma_address(sg));
-               if (unlikely(err)) {
-                       sg_dma_address(sg) = 0;
-                       goto out;
-               }
-
-               sg_dma_len(sg) = DMA_BUFFER_SIZE;
-
-               ctl->adesc = ctl->chan->device->device_prep_slave_sg(ctl->chan,
-                       sg, 1, DMA_FROM_DEVICE,
-                       DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP);
-
-               if (!ctl->adesc)
-                       goto out;
-
-               ctl->adesc->callback_param = netdev;
-               ctl->adesc->callback = ks8842_dma_rx_cb;
-               ctl->adesc->tx_submit(ctl->adesc);
-       } else {
-               err = -ENOMEM;
-               sg_dma_address(sg) = 0;
-               goto out;
-       }
-
-       return err;
-out:
-       if (sg_dma_address(sg))
-               dma_unmap_single(adapter->dev, sg_dma_address(sg),
-                       DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-       sg_dma_address(sg) = 0;
-       if (ctl->skb)
-               dev_kfree_skb(ctl->skb);
-
-       ctl->skb = NULL;
-
-       printk(KERN_ERR DRV_NAME": Failed to start RX DMA: %d\n", err);
-       return err;
-}
-
-static void ks8842_rx_frame_dma_tasklet(unsigned long arg)
-{
-       struct net_device *netdev = (struct net_device *)arg;
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct ks8842_rx_dma_ctl *ctl = &adapter->dma_rx;
-       struct sk_buff *skb = ctl->skb;
-       dma_addr_t addr = sg_dma_address(&ctl->sg);
-       u32 status;
-
-       ctl->adesc = NULL;
-
-       /* kick next transfer going */
-       __ks8842_start_new_rx_dma(netdev);
-
-       /* now handle the data we got */
-       dma_unmap_single(adapter->dev, addr, DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-
-       status = *((u32 *)skb->data);
-
-       netdev_dbg(netdev, "%s - rx_data: status: %x\n",
-               __func__, status & 0xffff);
-
-       /* check the status */
-       if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
-               int len = (status >> 16) & 0x7ff;
-
-               ks8842_update_rx_counters(netdev, status, len);
-
-               /* reserve 4 bytes which is the status word */
-               skb_reserve(skb, 4);
-               skb_put(skb, len);
-
-               skb->protocol = eth_type_trans(skb, netdev);
-               netif_rx(skb);
-       } else {
-               ks8842_update_rx_err_counters(netdev, status);
-               dev_kfree_skb(skb);
-       }
-}
-
-static void ks8842_rx_frame(struct net_device *netdev,
-       struct ks8842_adapter *adapter)
-{
-       u32 status;
-       int len;
-
-       if (adapter->conf_flags & KS884X_16BIT) {
-               status = ks8842_read16(adapter, 17, REG_QMU_DATA_LO);
-               len = ks8842_read16(adapter, 17, REG_QMU_DATA_HI);
-               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
-                          __func__, status);
-       } else {
-               status = ks8842_read32(adapter, 17, REG_QMU_DATA_LO);
-               len = (status >> 16) & 0x7ff;
-               status &= 0xffff;
-               netdev_dbg(netdev, "%s - rx_data: status: %x\n",
-                          __func__, status);
-       }
-
-       /* check the status */
-       if ((status & RXSR_VALID) && !(status & RXSR_ERROR)) {
-               struct sk_buff *skb = netdev_alloc_skb_ip_align(netdev, len + 3);
-
-               if (skb) {
-
-                       ks8842_update_rx_counters(netdev, status, len);
-
-                       if (adapter->conf_flags & KS884X_16BIT) {
-                               u16 *data16 = (u16 *)skb_put(skb, len);
-                               ks8842_select_bank(adapter, 17);
-                               while (len > 0) {
-                                       *data16++ = ioread16(adapter->hw_addr +
-                                               REG_QMU_DATA_LO);
-                                       *data16++ = ioread16(adapter->hw_addr +
-                                               REG_QMU_DATA_HI);
-                                       len -= sizeof(u32);
-                               }
-                       } else {
-                               u32 *data = (u32 *)skb_put(skb, len);
-
-                               ks8842_select_bank(adapter, 17);
-                               while (len > 0) {
-                                       *data++ = ioread32(adapter->hw_addr +
-                                               REG_QMU_DATA_LO);
-                                       len -= sizeof(u32);
-                               }
-                       }
-                       skb->protocol = eth_type_trans(skb, netdev);
-                       netif_rx(skb);
-               } else
-                       netdev->stats.rx_dropped++;
-       } else
-               ks8842_update_rx_err_counters(netdev, status);
-
-       /* set high watermark to 3K */
-       ks8842_clear_bits(adapter, 0, 1 << 12, REG_QRFCR);
-
-       /* release the frame */
-       ks8842_write16(adapter, 17, 0x01, REG_RXQCR);
-
-       /* set high watermark to 2K */
-       ks8842_enable_bits(adapter, 0, 1 << 12, REG_QRFCR);
-}
-
-void ks8842_handle_rx(struct net_device *netdev, struct ks8842_adapter *adapter)
-{
-       u16 rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
-       netdev_dbg(netdev, "%s Entry - rx_data: %d\n", __func__, rx_data);
-       while (rx_data) {
-               ks8842_rx_frame(netdev, adapter);
-               rx_data = ks8842_read16(adapter, 16, REG_RXMIR) & 0x1fff;
-       }
-}
-
-void ks8842_handle_tx(struct net_device *netdev, struct ks8842_adapter *adapter)
-{
-       u16 sr = ks8842_read16(adapter, 16, REG_TXSR);
-       netdev_dbg(netdev, "%s - entry, sr: %x\n", __func__, sr);
-       netdev->stats.tx_packets++;
-       if (netif_queue_stopped(netdev))
-               netif_wake_queue(netdev);
-}
-
-void ks8842_handle_rx_overrun(struct net_device *netdev,
-       struct ks8842_adapter *adapter)
-{
-       netdev_dbg(netdev, "%s: entry\n", __func__);
-       netdev->stats.rx_errors++;
-       netdev->stats.rx_fifo_errors++;
-}
-
-void ks8842_tasklet(unsigned long arg)
-{
-       struct net_device *netdev = (struct net_device *)arg;
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       u16 isr;
-       unsigned long flags;
-       u16 entry_bank;
-
-       /* read current bank to be able to set it back */
-       spin_lock_irqsave(&adapter->lock, flags);
-       entry_bank = ioread16(adapter->hw_addr + REG_SELECT_BANK);
-       spin_unlock_irqrestore(&adapter->lock, flags);
-
-       isr = ks8842_read16(adapter, 18, REG_ISR);
-       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
-
-       /* when running in DMA mode, do not ack RX interrupts, it is handled
-          internally by timberdale, otherwise it's DMA FIFO:s would stop
-       */
-       if (KS8842_USE_DMA(adapter))
-               isr &= ~IRQ_RX;
-
-       /* Ack */
-       ks8842_write16(adapter, 18, isr, REG_ISR);
-
-       if (!(adapter->conf_flags & MICREL_KS884X))
-               /* Ack in the timberdale IP as well */
-               iowrite32(0x1, adapter->hw_addr + REG_TIMB_IAR);
-
-       if (!netif_running(netdev))
-               return;
-
-       if (isr & IRQ_LINK_CHANGE)
-               ks8842_update_link_status(netdev, adapter);
-
-       /* should not get IRQ_RX when running DMA mode */
-       if (isr & (IRQ_RX | IRQ_RX_ERROR) && !KS8842_USE_DMA(adapter))
-               ks8842_handle_rx(netdev, adapter);
-
-       /* should only happen when in PIO mode */
-       if (isr & IRQ_TX)
-               ks8842_handle_tx(netdev, adapter);
-
-       if (isr & IRQ_RX_OVERRUN)
-               ks8842_handle_rx_overrun(netdev, adapter);
-
-       if (isr & IRQ_TX_STOPPED) {
-               ks8842_disable_tx(adapter);
-               ks8842_enable_tx(adapter);
-       }
-
-       if (isr & IRQ_RX_STOPPED) {
-               ks8842_disable_rx(adapter);
-               ks8842_enable_rx(adapter);
-       }
-
-       /* re-enable interrupts, put back the bank selection register */
-       spin_lock_irqsave(&adapter->lock, flags);
-       if (KS8842_USE_DMA(adapter))
-               ks8842_write16(adapter, 18, ENABLED_IRQS_DMA, REG_IER);
-       else
-               ks8842_write16(adapter, 18, ENABLED_IRQS, REG_IER);
-       iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK);
-
-       /* Make sure timberdale continues DMA operations, they are stopped while
-          we are handling the ks8842 because we might change bank */
-       if (KS8842_USE_DMA(adapter))
-               ks8842_resume_dma(adapter);
-
-       spin_unlock_irqrestore(&adapter->lock, flags);
-}
-
-static irqreturn_t ks8842_irq(int irq, void *devid)
-{
-       struct net_device *netdev = devid;
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       u16 isr;
-       u16 entry_bank = ioread16(adapter->hw_addr + REG_SELECT_BANK);
-       irqreturn_t ret = IRQ_NONE;
-
-       isr = ks8842_read16(adapter, 18, REG_ISR);
-       netdev_dbg(netdev, "%s - ISR: 0x%x\n", __func__, isr);
-
-       if (isr) {
-               if (KS8842_USE_DMA(adapter))
-                       /* disable all but RX IRQ, since the FPGA relies on it*/
-                       ks8842_write16(adapter, 18, IRQ_RX, REG_IER);
-               else
-                       /* disable IRQ */
-                       ks8842_write16(adapter, 18, 0x00, REG_IER);
-
-               /* schedule tasklet */
-               tasklet_schedule(&adapter->tasklet);
-
-               ret = IRQ_HANDLED;
-       }
-
-       iowrite16(entry_bank, adapter->hw_addr + REG_SELECT_BANK);
-
-       /* After an interrupt, tell timberdale to continue DMA operations.
-          DMA is disabled while we are handling the ks8842 because we might
-          change bank */
-       ks8842_resume_dma(adapter);
-
-       return ret;
-}
-
-static void ks8842_dma_rx_cb(void *data)
-{
-       struct net_device       *netdev = data;
-       struct ks8842_adapter   *adapter = netdev_priv(netdev);
-
-       netdev_dbg(netdev, "RX DMA finished\n");
-       /* schedule tasklet */
-       if (adapter->dma_rx.adesc)
-               tasklet_schedule(&adapter->dma_rx.tasklet);
-}
-
-static void ks8842_dma_tx_cb(void *data)
-{
-       struct net_device               *netdev = data;
-       struct ks8842_adapter           *adapter = netdev_priv(netdev);
-       struct ks8842_tx_dma_ctl        *ctl = &adapter->dma_tx;
-
-       netdev_dbg(netdev, "TX DMA finished\n");
-
-       if (!ctl->adesc)
-               return;
-
-       netdev->stats.tx_packets++;
-       ctl->adesc = NULL;
-
-       if (netif_queue_stopped(netdev))
-               netif_wake_queue(netdev);
-}
-
-static void ks8842_stop_dma(struct ks8842_adapter *adapter)
-{
-       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
-       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
-
-       tx_ctl->adesc = NULL;
-       if (tx_ctl->chan)
-               tx_ctl->chan->device->device_control(tx_ctl->chan,
-                       DMA_TERMINATE_ALL, 0);
-
-       rx_ctl->adesc = NULL;
-       if (rx_ctl->chan)
-               rx_ctl->chan->device->device_control(rx_ctl->chan,
-                       DMA_TERMINATE_ALL, 0);
-
-       if (sg_dma_address(&rx_ctl->sg))
-               dma_unmap_single(adapter->dev, sg_dma_address(&rx_ctl->sg),
-                       DMA_BUFFER_SIZE, DMA_FROM_DEVICE);
-       sg_dma_address(&rx_ctl->sg) = 0;
-
-       dev_kfree_skb(rx_ctl->skb);
-       rx_ctl->skb = NULL;
-}
-
-static void ks8842_dealloc_dma_bufs(struct ks8842_adapter *adapter)
-{
-       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
-       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
-
-       ks8842_stop_dma(adapter);
-
-       if (tx_ctl->chan)
-               dma_release_channel(tx_ctl->chan);
-       tx_ctl->chan = NULL;
-
-       if (rx_ctl->chan)
-               dma_release_channel(rx_ctl->chan);
-       rx_ctl->chan = NULL;
-
-       tasklet_kill(&rx_ctl->tasklet);
-
-       if (sg_dma_address(&tx_ctl->sg))
-               dma_unmap_single(adapter->dev, sg_dma_address(&tx_ctl->sg),
-                       DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-       sg_dma_address(&tx_ctl->sg) = 0;
-
-       kfree(tx_ctl->buf);
-       tx_ctl->buf = NULL;
-}
-
-static bool ks8842_dma_filter_fn(struct dma_chan *chan, void *filter_param)
-{
-       return chan->chan_id == (long)filter_param;
-}
-
-static int ks8842_alloc_dma_bufs(struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct ks8842_tx_dma_ctl *tx_ctl = &adapter->dma_tx;
-       struct ks8842_rx_dma_ctl *rx_ctl = &adapter->dma_rx;
-       int err;
-
-       dma_cap_mask_t mask;
-
-       dma_cap_zero(mask);
-       dma_cap_set(DMA_SLAVE, mask);
-       dma_cap_set(DMA_PRIVATE, mask);
-
-       sg_init_table(&tx_ctl->sg, 1);
-
-       tx_ctl->chan = dma_request_channel(mask, ks8842_dma_filter_fn,
-                                          (void *)(long)tx_ctl->channel);
-       if (!tx_ctl->chan) {
-               err = -ENODEV;
-               goto err;
-       }
-
-       /* allocate DMA buffer */
-       tx_ctl->buf = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL);
-       if (!tx_ctl->buf) {
-               err = -ENOMEM;
-               goto err;
-       }
-
-       sg_dma_address(&tx_ctl->sg) = dma_map_single(adapter->dev,
-               tx_ctl->buf, DMA_BUFFER_SIZE, DMA_TO_DEVICE);
-       err = dma_mapping_error(adapter->dev,
-               sg_dma_address(&tx_ctl->sg));
-       if (err) {
-               sg_dma_address(&tx_ctl->sg) = 0;
-               goto err;
-       }
-
-       rx_ctl->chan = dma_request_channel(mask, ks8842_dma_filter_fn,
-                                          (void *)(long)rx_ctl->channel);
-       if (!rx_ctl->chan) {
-               err = -ENODEV;
-               goto err;
-       }
-
-       tasklet_init(&rx_ctl->tasklet, ks8842_rx_frame_dma_tasklet,
-               (unsigned long)netdev);
-
-       return 0;
-err:
-       ks8842_dealloc_dma_bufs(adapter);
-       return err;
-}
-
-/* Netdevice operations */
-
-static int ks8842_open(struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       int err;
-
-       netdev_dbg(netdev, "%s - entry\n", __func__);
-
-       if (KS8842_USE_DMA(adapter)) {
-               err = ks8842_alloc_dma_bufs(netdev);
-
-               if (!err) {
-                       /* start RX dma */
-                       err = __ks8842_start_new_rx_dma(netdev);
-                       if (err)
-                               ks8842_dealloc_dma_bufs(adapter);
-               }
-
-               if (err) {
-                       printk(KERN_WARNING DRV_NAME
-                               ": Failed to initiate DMA, running PIO\n");
-                       ks8842_dealloc_dma_bufs(adapter);
-                       adapter->dma_rx.channel = -1;
-                       adapter->dma_tx.channel = -1;
-               }
-       }
-
-       /* reset the HW */
-       ks8842_reset_hw(adapter);
-
-       ks8842_write_mac_addr(adapter, netdev->dev_addr);
-
-       ks8842_update_link_status(netdev, adapter);
-
-       err = request_irq(adapter->irq, ks8842_irq, IRQF_SHARED, DRV_NAME,
-               netdev);
-       if (err) {
-               pr_err("Failed to request IRQ: %d: %d\n", adapter->irq, err);
-               return err;
-       }
-
-       return 0;
-}
-
-static int ks8842_close(struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-
-       netdev_dbg(netdev, "%s - entry\n", __func__);
-
-       cancel_work_sync(&adapter->timeout_work);
-
-       if (KS8842_USE_DMA(adapter))
-               ks8842_dealloc_dma_bufs(adapter);
-
-       /* free the irq */
-       free_irq(adapter->irq, netdev);
-
-       /* disable the switch */
-       ks8842_write16(adapter, 32, 0x0, REG_SW_ID_AND_ENABLE);
-
-       return 0;
-}
-
-static netdev_tx_t ks8842_xmit_frame(struct sk_buff *skb,
-                                    struct net_device *netdev)
-{
-       int ret;
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-
-       netdev_dbg(netdev, "%s: entry\n", __func__);
-
-       if (KS8842_USE_DMA(adapter)) {
-               unsigned long flags;
-               ret = ks8842_tx_frame_dma(skb, netdev);
-               /* for now only allow one transfer at the time */
-               spin_lock_irqsave(&adapter->lock, flags);
-               if (adapter->dma_tx.adesc)
-                       netif_stop_queue(netdev);
-               spin_unlock_irqrestore(&adapter->lock, flags);
-               return ret;
-       }
-
-       ret = ks8842_tx_frame(skb, netdev);
-
-       if (ks8842_tx_fifo_space(adapter) <  netdev->mtu + 8)
-               netif_stop_queue(netdev);
-
-       return ret;
-}
-
-static int ks8842_set_mac(struct net_device *netdev, void *p)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct sockaddr *addr = p;
-       char *mac = (u8 *)addr->sa_data;
-
-       netdev_dbg(netdev, "%s: entry\n", __func__);
-
-       if (!is_valid_ether_addr(addr->sa_data))
-               return -EADDRNOTAVAIL;
-
-       memcpy(netdev->dev_addr, mac, netdev->addr_len);
-
-       ks8842_write_mac_addr(adapter, mac);
-       return 0;
-}
-
-static void ks8842_tx_timeout_work(struct work_struct *work)
-{
-       struct ks8842_adapter *adapter =
-               container_of(work, struct ks8842_adapter, timeout_work);
-       struct net_device *netdev = adapter->netdev;
-       unsigned long flags;
-
-       netdev_dbg(netdev, "%s: entry\n", __func__);
-
-       spin_lock_irqsave(&adapter->lock, flags);
-
-       if (KS8842_USE_DMA(adapter))
-               ks8842_stop_dma(adapter);
-
-       /* disable interrupts */
-       ks8842_write16(adapter, 18, 0, REG_IER);
-       ks8842_write16(adapter, 18, 0xFFFF, REG_ISR);
-
-       netif_stop_queue(netdev);
-
-       spin_unlock_irqrestore(&adapter->lock, flags);
-
-       ks8842_reset_hw(adapter);
-
-       ks8842_write_mac_addr(adapter, netdev->dev_addr);
-
-       ks8842_update_link_status(netdev, adapter);
-
-       if (KS8842_USE_DMA(adapter))
-               __ks8842_start_new_rx_dma(netdev);
-}
-
-static void ks8842_tx_timeout(struct net_device *netdev)
-{
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-
-       netdev_dbg(netdev, "%s: entry\n", __func__);
-
-       schedule_work(&adapter->timeout_work);
-}
-
-static const struct net_device_ops ks8842_netdev_ops = {
-       .ndo_open               = ks8842_open,
-       .ndo_stop               = ks8842_close,
-       .ndo_start_xmit         = ks8842_xmit_frame,
-       .ndo_set_mac_address    = ks8842_set_mac,
-       .ndo_tx_timeout         = ks8842_tx_timeout,
-       .ndo_validate_addr      = eth_validate_addr
-};
-
-static const struct ethtool_ops ks8842_ethtool_ops = {
-       .get_link               = ethtool_op_get_link,
-};
-
-static int __devinit ks8842_probe(struct platform_device *pdev)
-{
-       int err = -ENOMEM;
-       struct resource *iomem;
-       struct net_device *netdev;
-       struct ks8842_adapter *adapter;
-       struct ks8842_platform_data *pdata = pdev->dev.platform_data;
-       u16 id;
-       unsigned i;
-
-       iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!request_mem_region(iomem->start, resource_size(iomem), DRV_NAME))
-               goto err_mem_region;
-
-       netdev = alloc_etherdev(sizeof(struct ks8842_adapter));
-       if (!netdev)
-               goto err_alloc_etherdev;
-
-       SET_NETDEV_DEV(netdev, &pdev->dev);
-
-       adapter = netdev_priv(netdev);
-       adapter->netdev = netdev;
-       INIT_WORK(&adapter->timeout_work, ks8842_tx_timeout_work);
-       adapter->hw_addr = ioremap(iomem->start, resource_size(iomem));
-       adapter->conf_flags = iomem->flags;
-
-       if (!adapter->hw_addr)
-               goto err_ioremap;
-
-       adapter->irq = platform_get_irq(pdev, 0);
-       if (adapter->irq < 0) {
-               err = adapter->irq;
-               goto err_get_irq;
-       }
-
-       adapter->dev = (pdev->dev.parent) ? pdev->dev.parent : &pdev->dev;
-
-       /* DMA is only supported when accessed via timberdale */
-       if (!(adapter->conf_flags & MICREL_KS884X) && pdata &&
-               (pdata->tx_dma_channel != -1) &&
-               (pdata->rx_dma_channel != -1)) {
-               adapter->dma_rx.channel = pdata->rx_dma_channel;
-               adapter->dma_tx.channel = pdata->tx_dma_channel;
-       } else {
-               adapter->dma_rx.channel = -1;
-               adapter->dma_tx.channel = -1;
-       }
-
-       tasklet_init(&adapter->tasklet, ks8842_tasklet, (unsigned long)netdev);
-       spin_lock_init(&adapter->lock);
-
-       netdev->netdev_ops = &ks8842_netdev_ops;
-       netdev->ethtool_ops = &ks8842_ethtool_ops;
-
-       /* Check if a mac address was given */
-       i = netdev->addr_len;
-       if (pdata) {
-               for (i = 0; i < netdev->addr_len; i++)
-                       if (pdata->macaddr[i] != 0)
-                               break;
-
-               if (i < netdev->addr_len)
-                       /* an address was passed, use it */
-                       memcpy(netdev->dev_addr, pdata->macaddr,
-                               netdev->addr_len);
-       }
-
-       if (i == netdev->addr_len) {
-               ks8842_read_mac_addr(adapter, netdev->dev_addr);
-
-               if (!is_valid_ether_addr(netdev->dev_addr))
-                       random_ether_addr(netdev->dev_addr);
-       }
-
-       id = ks8842_read16(adapter, 32, REG_SW_ID_AND_ENABLE);
-
-       strcpy(netdev->name, "eth%d");
-       err = register_netdev(netdev);
-       if (err)
-               goto err_register;
-
-       platform_set_drvdata(pdev, netdev);
-
-       pr_info("Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n",
-               (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7);
-
-       return 0;
-
-err_register:
-err_get_irq:
-       iounmap(adapter->hw_addr);
-err_ioremap:
-       free_netdev(netdev);
-err_alloc_etherdev:
-       release_mem_region(iomem->start, resource_size(iomem));
-err_mem_region:
-       return err;
-}
-
-static int __devexit ks8842_remove(struct platform_device *pdev)
-{
-       struct net_device *netdev = platform_get_drvdata(pdev);
-       struct ks8842_adapter *adapter = netdev_priv(netdev);
-       struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       unregister_netdev(netdev);
-       tasklet_kill(&adapter->tasklet);
-       iounmap(adapter->hw_addr);
-       free_netdev(netdev);
-       release_mem_region(iomem->start, resource_size(iomem));
-       platform_set_drvdata(pdev, NULL);
-       return 0;
-}
-
-
-static struct platform_driver ks8842_platform_driver = {
-       .driver = {
-               .name   = DRV_NAME,
-               .owner  = THIS_MODULE,
-       },
-       .probe          = ks8842_probe,
-       .remove         = ks8842_remove,
-};
-
-static int __init ks8842_init(void)
-{
-       return platform_driver_register(&ks8842_platform_driver);
-}
-
-static void __exit ks8842_exit(void)
-{
-       platform_driver_unregister(&ks8842_platform_driver);
-}
-
-module_init(ks8842_init);
-module_exit(ks8842_exit);
-
-MODULE_DESCRIPTION("Timberdale KS8842 ethernet driver");
-MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_ALIAS("platform:ks8842");
-
diff --git a/drivers/net/ks8851.c b/drivers/net/ks8851.c
deleted file mode 100644 (file)
index f56743a..0000000
+++ /dev/null
@@ -1,1737 +0,0 @@
-/* drivers/net/ks8851.c
- *
- * Copyright 2009 Simtec Electronics
- *     http://www.simtec.co.uk/
- *     Ben Dooks <ben@simtec.co.uk>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#define DEBUG
-
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/cache.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-
-#include <linux/spi/spi.h>
-
-#include "ks8851.h"
-
-/**
- * struct ks8851_rxctrl - KS8851 driver rx control
- * @mchash: Multicast hash-table data.
- * @rxcr1: KS_RXCR1 register setting
- * @rxcr2: KS_RXCR2 register setting
- *
- * Representation of the settings needs to control the receive filtering
- * such as the multicast hash-filter and the receive register settings. This
- * is used to make the job of working out if the receive settings change and
- * then issuing the new settings to the worker that will send the necessary
- * commands.
- */
-struct ks8851_rxctrl {
-       u16     mchash[4];
-       u16     rxcr1;
-       u16     rxcr2;
-};
-
-/**
- * union ks8851_tx_hdr - tx header data
- * @txb: The header as bytes
- * @txw: The header as 16bit, little-endian words
- *
- * A dual representation of the tx header data to allow
- * access to individual bytes, and to allow 16bit accesses
- * with 16bit alignment.
- */
-union ks8851_tx_hdr {
-       u8      txb[6];
-       __le16  txw[3];
-};
-
-/**
- * struct ks8851_net - KS8851 driver private data
- * @netdev: The network device we're bound to
- * @spidev: The spi device we're bound to.
- * @lock: Lock to ensure that the device is not accessed when busy.
- * @statelock: Lock on this structure for tx list.
- * @mii: The MII state information for the mii calls.
- * @rxctrl: RX settings for @rxctrl_work.
- * @tx_work: Work queue for tx packets
- * @irq_work: Work queue for servicing interrupts
- * @rxctrl_work: Work queue for updating RX mode and multicast lists
- * @txq: Queue of packets for transmission.
- * @spi_msg1: pre-setup SPI transfer with one message, @spi_xfer1.
- * @spi_msg2: pre-setup SPI transfer with two messages, @spi_xfer2.
- * @txh: Space for generating packet TX header in DMA-able data
- * @rxd: Space for receiving SPI data, in DMA-able space.
- * @txd: Space for transmitting SPI data, in DMA-able space.
- * @msg_enable: The message flags controlling driver output (see ethtool).
- * @fid: Incrementing frame id tag.
- * @rc_ier: Cached copy of KS_IER.
- * @rc_ccr: Cached copy of KS_CCR.
- * @rc_rxqcr: Cached copy of KS_RXQCR.
- * @eeprom_size: Companion eeprom size in Bytes, 0 if no eeprom
- *
- * The @lock ensures that the chip is protected when certain operations are
- * in progress. When the read or write packet transfer is in progress, most
- * of the chip registers are not ccessible until the transfer is finished and
- * the DMA has been de-asserted.
- *
- * The @statelock is used to protect information in the structure which may
- * need to be accessed via several sources, such as the network driver layer
- * or one of the work queues.
- *
- * We align the buffers we may use for rx/tx to ensure that if the SPI driver
- * wants to DMA map them, it will not have any problems with data the driver
- * modifies.
- */
-struct ks8851_net {
-       struct net_device       *netdev;
-       struct spi_device       *spidev;
-       struct mutex            lock;
-       spinlock_t              statelock;
-
-       union ks8851_tx_hdr     txh ____cacheline_aligned;
-       u8                      rxd[8];
-       u8                      txd[8];
-
-       u32                     msg_enable ____cacheline_aligned;
-       u16                     tx_space;
-       u8                      fid;
-
-       u16                     rc_ier;
-       u16                     rc_rxqcr;
-       u16                     rc_ccr;
-       u16                     eeprom_size;
-
-       struct mii_if_info      mii;
-       struct ks8851_rxctrl    rxctrl;
-
-       struct work_struct      tx_work;
-       struct work_struct      irq_work;
-       struct work_struct      rxctrl_work;
-
-       struct sk_buff_head     txq;
-
-       struct spi_message      spi_msg1;
-       struct spi_message      spi_msg2;
-       struct spi_transfer     spi_xfer1;
-       struct spi_transfer     spi_xfer2[2];
-};
-
-static int msg_enable;
-
-/* shift for byte-enable data */
-#define BYTE_EN(_x)    ((_x) << 2)
-
-/* turn register number and byte-enable mask into data for start of packet */
-#define MK_OP(_byteen, _reg) (BYTE_EN(_byteen) | (_reg)  << (8+2) | (_reg) >> 6)
-
-/* SPI register read/write calls.
- *
- * All these calls issue SPI transactions to access the chip's registers. They
- * all require that the necessary lock is held to prevent accesses when the
- * chip is busy transferring packet data (RX/TX FIFO accesses).
- */
-
-/**
- * ks8851_wrreg16 - write 16bit register value to chip
- * @ks: The chip state
- * @reg: The register address
- * @val: The value to write
- *
- * Issue a write to put the value @val into the register specified in @reg.
- */
-static void ks8851_wrreg16(struct ks8851_net *ks, unsigned reg, unsigned val)
-{
-       struct spi_transfer *xfer = &ks->spi_xfer1;
-       struct spi_message *msg = &ks->spi_msg1;
-       __le16 txb[2];
-       int ret;
-
-       txb[0] = cpu_to_le16(MK_OP(reg & 2 ? 0xC : 0x03, reg) | KS_SPIOP_WR);
-       txb[1] = cpu_to_le16(val);
-
-       xfer->tx_buf = txb;
-       xfer->rx_buf = NULL;
-       xfer->len = 4;
-
-       ret = spi_sync(ks->spidev, msg);
-       if (ret < 0)
-               netdev_err(ks->netdev, "spi_sync() failed\n");
-}
-
-/**
- * ks8851_wrreg8 - write 8bit register value to chip
- * @ks: The chip state
- * @reg: The register address
- * @val: The value to write
- *
- * Issue a write to put the value @val into the register specified in @reg.
- */
-static void ks8851_wrreg8(struct ks8851_net *ks, unsigned reg, unsigned val)
-{
-       struct spi_transfer *xfer = &ks->spi_xfer1;
-       struct spi_message *msg = &ks->spi_msg1;
-       __le16 txb[2];
-       int ret;
-       int bit;
-
-       bit = 1 << (reg & 3);
-
-       txb[0] = cpu_to_le16(MK_OP(bit, reg) | KS_SPIOP_WR);
-       txb[1] = val;
-
-       xfer->tx_buf = txb;
-       xfer->rx_buf = NULL;
-       xfer->len = 3;
-
-       ret = spi_sync(ks->spidev, msg);
-       if (ret < 0)
-               netdev_err(ks->netdev, "spi_sync() failed\n");
-}
-
-/**
- * ks8851_rx_1msg - select whether to use one or two messages for spi read
- * @ks: The device structure
- *
- * Return whether to generate a single message with a tx and rx buffer
- * supplied to spi_sync(), or alternatively send the tx and rx buffers
- * as separate messages.
- *
- * Depending on the hardware in use, a single message may be more efficient
- * on interrupts or work done by the driver.
- *
- * This currently always returns true until we add some per-device data passed
- * from the platform code to specify which mode is better.
- */
-static inline bool ks8851_rx_1msg(struct ks8851_net *ks)
-{
-       return true;
-}
-
-/**
- * ks8851_rdreg - issue read register command and return the data
- * @ks: The device state
- * @op: The register address and byte enables in message format.
- * @rxb: The RX buffer to return the result into
- * @rxl: The length of data expected.
- *
- * This is the low level read call that issues the necessary spi message(s)
- * to read data from the register specified in @op.
- */
-static void ks8851_rdreg(struct ks8851_net *ks, unsigned op,
-                        u8 *rxb, unsigned rxl)
-{
-       struct spi_transfer *xfer;
-       struct spi_message *msg;
-       __le16 *txb = (__le16 *)ks->txd;
-       u8 *trx = ks->rxd;
-       int ret;
-
-       txb[0] = cpu_to_le16(op | KS_SPIOP_RD);
-
-       if (ks8851_rx_1msg(ks)) {
-               msg = &ks->spi_msg1;
-               xfer = &ks->spi_xfer1;
-
-               xfer->tx_buf = txb;
-               xfer->rx_buf = trx;
-               xfer->len = rxl + 2;
-       } else {
-               msg = &ks->spi_msg2;
-               xfer = ks->spi_xfer2;
-
-               xfer->tx_buf = txb;
-               xfer->rx_buf = NULL;
-               xfer->len = 2;
-
-               xfer++;
-               xfer->tx_buf = NULL;
-               xfer->rx_buf = trx;
-               xfer->len = rxl;
-       }
-
-       ret = spi_sync(ks->spidev, msg);
-       if (ret < 0)
-               netdev_err(ks->netdev, "read: spi_sync() failed\n");
-       else if (ks8851_rx_1msg(ks))
-               memcpy(rxb, trx + 2, rxl);
-       else
-               memcpy(rxb, trx, rxl);
-}
-
-/**
- * ks8851_rdreg8 - read 8 bit register from device
- * @ks: The chip information
- * @reg: The register address
- *
- * Read a 8bit register from the chip, returning the result
-*/
-static unsigned ks8851_rdreg8(struct ks8851_net *ks, unsigned reg)
-{
-       u8 rxb[1];
-
-       ks8851_rdreg(ks, MK_OP(1 << (reg & 3), reg), rxb, 1);
-       return rxb[0];
-}
-
-/**
- * ks8851_rdreg16 - read 16 bit register from device
- * @ks: The chip information
- * @reg: The register address
- *
- * Read a 16bit register from the chip, returning the result
-*/
-static unsigned ks8851_rdreg16(struct ks8851_net *ks, unsigned reg)
-{
-       __le16 rx = 0;
-
-       ks8851_rdreg(ks, MK_OP(reg & 2 ? 0xC : 0x3, reg), (u8 *)&rx, 2);
-       return le16_to_cpu(rx);
-}
-
-/**
- * ks8851_rdreg32 - read 32 bit register from device
- * @ks: The chip information
- * @reg: The register address
- *
- * Read a 32bit register from the chip.
- *
- * Note, this read requires the address be aligned to 4 bytes.
-*/
-static unsigned ks8851_rdreg32(struct ks8851_net *ks, unsigned reg)
-{
-       __le32 rx = 0;
-
-       WARN_ON(reg & 3);
-
-       ks8851_rdreg(ks, MK_OP(0xf, reg), (u8 *)&rx, 4);
-       return le32_to_cpu(rx);
-}
-
-/**
- * ks8851_soft_reset - issue one of the soft reset to the device
- * @ks: The device state.
- * @op: The bit(s) to set in the GRR
- *
- * Issue the relevant soft-reset command to the device's GRR register
- * specified by @op.
- *
- * Note, the delays are in there as a caution to ensure that the reset
- * has time to take effect and then complete. Since the datasheet does
- * not currently specify the exact sequence, we have chosen something
- * that seems to work with our device.
- */
-static void ks8851_soft_reset(struct ks8851_net *ks, unsigned op)
-{
-       ks8851_wrreg16(ks, KS_GRR, op);
-       mdelay(1);      /* wait a short time to effect reset */
-       ks8851_wrreg16(ks, KS_GRR, 0);
-       mdelay(1);      /* wait for condition to clear */
-}
-
-/**
- * ks8851_write_mac_addr - write mac address to device registers
- * @dev: The network device
- *
- * Update the KS8851 MAC address registers from the address in @dev.
- *
- * This call assumes that the chip is not running, so there is no need to
- * shutdown the RXQ process whilst setting this.
-*/
-static int ks8851_write_mac_addr(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       int i;
-
-       mutex_lock(&ks->lock);
-
-       for (i = 0; i < ETH_ALEN; i++)
-               ks8851_wrreg8(ks, KS_MAR(i), dev->dev_addr[i]);
-
-       mutex_unlock(&ks->lock);
-
-       return 0;
-}
-
-/**
- * ks8851_init_mac - initialise the mac address
- * @ks: The device structure
- *
- * Get or create the initial mac address for the device and then set that
- * into the station address register. Currently we assume that the device
- * does not have a valid mac address in it, and so we use random_ether_addr()
- * to create a new one.
- *
- * In future, the driver should check to see if the device has an EEPROM
- * attached and whether that has a valid ethernet address in it.
- */
-static void ks8851_init_mac(struct ks8851_net *ks)
-{
-       struct net_device *dev = ks->netdev;
-
-       random_ether_addr(dev->dev_addr);
-       ks8851_write_mac_addr(dev);
-}
-
-/**
- * ks8851_irq - device interrupt handler
- * @irq: Interrupt number passed from the IRQ hnalder.
- * @pw: The private word passed to register_irq(), our struct ks8851_net.
- *
- * Disable the interrupt from happening again until we've processed the
- * current status by scheduling ks8851_irq_work().
- */
-static irqreturn_t ks8851_irq(int irq, void *pw)
-{
-       struct ks8851_net *ks = pw;
-
-       disable_irq_nosync(irq);
-       schedule_work(&ks->irq_work);
-       return IRQ_HANDLED;
-}
-
-/**
- * ks8851_rdfifo - read data from the receive fifo
- * @ks: The device state.
- * @buff: The buffer address
- * @len: The length of the data to read
- *
- * Issue an RXQ FIFO read command and read the @len amount of data from
- * the FIFO into the buffer specified by @buff.
- */
-static void ks8851_rdfifo(struct ks8851_net *ks, u8 *buff, unsigned len)
-{
-       struct spi_transfer *xfer = ks->spi_xfer2;
-       struct spi_message *msg = &ks->spi_msg2;
-       u8 txb[1];
-       int ret;
-
-       netif_dbg(ks, rx_status, ks->netdev,
-                 "%s: %d@%p\n", __func__, len, buff);
-
-       /* set the operation we're issuing */
-       txb[0] = KS_SPIOP_RXFIFO;
-
-       xfer->tx_buf = txb;
-       xfer->rx_buf = NULL;
-       xfer->len = 1;
-
-       xfer++;
-       xfer->rx_buf = buff;
-       xfer->tx_buf = NULL;
-       xfer->len = len;
-
-       ret = spi_sync(ks->spidev, msg);
-       if (ret < 0)
-               netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__);
-}
-
-/**
- * ks8851_dbg_dumpkkt - dump initial packet contents to debug
- * @ks: The device state
- * @rxpkt: The data for the received packet
- *
- * Dump the initial data from the packet to dev_dbg().
-*/
-static void ks8851_dbg_dumpkkt(struct ks8851_net *ks, u8 *rxpkt)
-{
-       netdev_dbg(ks->netdev,
-                  "pkt %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x\n",
-                  rxpkt[4], rxpkt[5], rxpkt[6], rxpkt[7],
-                  rxpkt[8], rxpkt[9], rxpkt[10], rxpkt[11],
-                  rxpkt[12], rxpkt[13], rxpkt[14], rxpkt[15]);
-}
-
-/**
- * ks8851_rx_pkts - receive packets from the host
- * @ks: The device information.
- *
- * This is called from the IRQ work queue when the system detects that there
- * are packets in the receive queue. Find out how many packets there are and
- * read them from the FIFO.
- */
-static void ks8851_rx_pkts(struct ks8851_net *ks)
-{
-       struct sk_buff *skb;
-       unsigned rxfc;
-       unsigned rxlen;
-       unsigned rxstat;
-       u32 rxh;
-       u8 *rxpkt;
-
-       rxfc = ks8851_rdreg8(ks, KS_RXFC);
-
-       netif_dbg(ks, rx_status, ks->netdev,
-                 "%s: %d packets\n", __func__, rxfc);
-
-       /* Currently we're issuing a read per packet, but we could possibly
-        * improve the code by issuing a single read, getting the receive
-        * header, allocating the packet and then reading the packet data
-        * out in one go.
-        *
-        * This form of operation would require us to hold the SPI bus'
-        * chipselect low during the entie transaction to avoid any
-        * reset to the data stream coming from the chip.
-        */
-
-       for (; rxfc != 0; rxfc--) {
-               rxh = ks8851_rdreg32(ks, KS_RXFHSR);
-               rxstat = rxh & 0xffff;
-               rxlen = rxh >> 16;
-
-               netif_dbg(ks, rx_status, ks->netdev,
-                         "rx: stat 0x%04x, len 0x%04x\n", rxstat, rxlen);
-
-               /* the length of the packet includes the 32bit CRC */
-
-               /* set dma read address */
-               ks8851_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI | 0x00);
-
-               /* start the packet dma process, and set auto-dequeue rx */
-               ks8851_wrreg16(ks, KS_RXQCR,
-                              ks->rc_rxqcr | RXQCR_SDA | RXQCR_ADRFE);
-
-               if (rxlen > 4) {
-                       unsigned int rxalign;
-
-                       rxlen -= 4;
-                       rxalign = ALIGN(rxlen, 4);
-                       skb = netdev_alloc_skb_ip_align(ks->netdev, rxalign);
-                       if (skb) {
-
-                               /* 4 bytes of status header + 4 bytes of
-                                * garbage: we put them before ethernet
-                                * header, so that they are copied,
-                                * but ignored.
-                                */
-
-                               rxpkt = skb_put(skb, rxlen) - 8;
-
-                               ks8851_rdfifo(ks, rxpkt, rxalign + 8);
-
-                               if (netif_msg_pktdata(ks))
-                                       ks8851_dbg_dumpkkt(ks, rxpkt);
-
-                               skb->protocol = eth_type_trans(skb, ks->netdev);
-                               netif_rx(skb);
-
-                               ks->netdev->stats.rx_packets++;
-                               ks->netdev->stats.rx_bytes += rxlen;
-                       }
-               }
-
-               ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
-       }
-}
-
-/**
- * ks8851_irq_work - work queue handler for dealing with interrupt requests
- * @work: The work structure that was scheduled by schedule_work()
- *
- * This is the handler invoked when the ks8851_irq() is called to find out
- * what happened, as we cannot allow ourselves to sleep whilst waiting for
- * anything other process has the chip's lock.
- *
- * Read the interrupt status, work out what needs to be done and then clear
- * any of the interrupts that are not needed.
- */
-static void ks8851_irq_work(struct work_struct *work)
-{
-       struct ks8851_net *ks = container_of(work, struct ks8851_net, irq_work);
-       unsigned status;
-       unsigned handled = 0;
-
-       mutex_lock(&ks->lock);
-
-       status = ks8851_rdreg16(ks, KS_ISR);
-
-       netif_dbg(ks, intr, ks->netdev,
-                 "%s: status 0x%04x\n", __func__, status);
-
-       if (status & IRQ_LCI) {
-               /* should do something about checking link status */
-               handled |= IRQ_LCI;
-       }
-
-       if (status & IRQ_LDI) {
-               u16 pmecr = ks8851_rdreg16(ks, KS_PMECR);
-               pmecr &= ~PMECR_WKEVT_MASK;
-               ks8851_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
-
-               handled |= IRQ_LDI;
-       }
-
-       if (status & IRQ_RXPSI)
-               handled |= IRQ_RXPSI;
-
-       if (status & IRQ_TXI) {
-               handled |= IRQ_TXI;
-
-               /* no lock here, tx queue should have been stopped */
-
-               /* update our idea of how much tx space is available to the
-                * system */
-               ks->tx_space = ks8851_rdreg16(ks, KS_TXMIR);
-
-               netif_dbg(ks, intr, ks->netdev,
-                         "%s: txspace %d\n", __func__, ks->tx_space);
-       }
-
-       if (status & IRQ_RXI)
-               handled |= IRQ_RXI;
-
-       if (status & IRQ_SPIBEI) {
-               dev_err(&ks->spidev->dev, "%s: spi bus error\n", __func__);
-               handled |= IRQ_SPIBEI;
-       }
-
-       ks8851_wrreg16(ks, KS_ISR, handled);
-
-       if (status & IRQ_RXI) {
-               /* the datasheet says to disable the rx interrupt during
-                * packet read-out, however we're masking the interrupt
-                * from the device so do not bother masking just the RX
-                * from the device. */
-
-               ks8851_rx_pkts(ks);
-       }
-
-       /* if something stopped the rx process, probably due to wanting
-        * to change the rx settings, then do something about restarting
-        * it. */
-       if (status & IRQ_RXPSI) {
-               struct ks8851_rxctrl *rxc = &ks->rxctrl;
-
-               /* update the multicast hash table */
-               ks8851_wrreg16(ks, KS_MAHTR0, rxc->mchash[0]);
-               ks8851_wrreg16(ks, KS_MAHTR1, rxc->mchash[1]);
-               ks8851_wrreg16(ks, KS_MAHTR2, rxc->mchash[2]);
-               ks8851_wrreg16(ks, KS_MAHTR3, rxc->mchash[3]);
-
-               ks8851_wrreg16(ks, KS_RXCR2, rxc->rxcr2);
-               ks8851_wrreg16(ks, KS_RXCR1, rxc->rxcr1);
-       }
-
-       mutex_unlock(&ks->lock);
-
-       if (status & IRQ_TXI)
-               netif_wake_queue(ks->netdev);
-
-       enable_irq(ks->netdev->irq);
-}
-
-/**
- * calc_txlen - calculate size of message to send packet
- * @len: Length of data
- *
- * Returns the size of the TXFIFO message needed to send
- * this packet.
- */
-static inline unsigned calc_txlen(unsigned len)
-{
-       return ALIGN(len + 4, 4);
-}
-
-/**
- * ks8851_wrpkt - write packet to TX FIFO
- * @ks: The device state.
- * @txp: The sk_buff to transmit.
- * @irq: IRQ on completion of the packet.
- *
- * Send the @txp to the chip. This means creating the relevant packet header
- * specifying the length of the packet and the other information the chip
- * needs, such as IRQ on completion. Send the header and the packet data to
- * the device.
- */
-static void ks8851_wrpkt(struct ks8851_net *ks, struct sk_buff *txp, bool irq)
-{
-       struct spi_transfer *xfer = ks->spi_xfer2;
-       struct spi_message *msg = &ks->spi_msg2;
-       unsigned fid = 0;
-       int ret;
-
-       netif_dbg(ks, tx_queued, ks->netdev, "%s: skb %p, %d@%p, irq %d\n",
-                 __func__, txp, txp->len, txp->data, irq);
-
-       fid = ks->fid++;
-       fid &= TXFR_TXFID_MASK;
-
-       if (irq)
-               fid |= TXFR_TXIC;       /* irq on completion */
-
-       /* start header at txb[1] to align txw entries */
-       ks->txh.txb[1] = KS_SPIOP_TXFIFO;
-       ks->txh.txw[1] = cpu_to_le16(fid);
-       ks->txh.txw[2] = cpu_to_le16(txp->len);
-
-       xfer->tx_buf = &ks->txh.txb[1];
-       xfer->rx_buf = NULL;
-       xfer->len = 5;
-
-       xfer++;
-       xfer->tx_buf = txp->data;
-       xfer->rx_buf = NULL;
-       xfer->len = ALIGN(txp->len, 4);
-
-       ret = spi_sync(ks->spidev, msg);
-       if (ret < 0)
-               netdev_err(ks->netdev, "%s: spi_sync() failed\n", __func__);
-}
-
-/**
- * ks8851_done_tx - update and then free skbuff after transmitting
- * @ks: The device state
- * @txb: The buffer transmitted
- */
-static void ks8851_done_tx(struct ks8851_net *ks, struct sk_buff *txb)
-{
-       struct net_device *dev = ks->netdev;
-
-       dev->stats.tx_bytes += txb->len;
-       dev->stats.tx_packets++;
-
-       dev_kfree_skb(txb);
-}
-
-/**
- * ks8851_tx_work - process tx packet(s)
- * @work: The work strucutre what was scheduled.
- *
- * This is called when a number of packets have been scheduled for
- * transmission and need to be sent to the device.
- */
-static void ks8851_tx_work(struct work_struct *work)
-{
-       struct ks8851_net *ks = container_of(work, struct ks8851_net, tx_work);
-       struct sk_buff *txb;
-       bool last = skb_queue_empty(&ks->txq);
-
-       mutex_lock(&ks->lock);
-
-       while (!last) {
-               txb = skb_dequeue(&ks->txq);
-               last = skb_queue_empty(&ks->txq);
-
-               if (txb != NULL) {
-                       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr | RXQCR_SDA);
-                       ks8851_wrpkt(ks, txb, last);
-                       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
-                       ks8851_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
-
-                       ks8851_done_tx(ks, txb);
-               }
-       }
-
-       mutex_unlock(&ks->lock);
-}
-
-/**
- * ks8851_set_powermode - set power mode of the device
- * @ks: The device state
- * @pwrmode: The power mode value to write to KS_PMECR.
- *
- * Change the power mode of the chip.
- */
-static void ks8851_set_powermode(struct ks8851_net *ks, unsigned pwrmode)
-{
-       unsigned pmecr;
-
-       netif_dbg(ks, hw, ks->netdev, "setting power mode %d\n", pwrmode);
-
-       pmecr = ks8851_rdreg16(ks, KS_PMECR);
-       pmecr &= ~PMECR_PM_MASK;
-       pmecr |= pwrmode;
-
-       ks8851_wrreg16(ks, KS_PMECR, pmecr);
-}
-
-/**
- * ks8851_net_open - open network device
- * @dev: The network device being opened.
- *
- * Called when the network device is marked active, such as a user executing
- * 'ifconfig up' on the device.
- */
-static int ks8851_net_open(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-
-       /* lock the card, even if we may not actually be doing anything
-        * else at the moment */
-       mutex_lock(&ks->lock);
-
-       netif_dbg(ks, ifup, ks->netdev, "opening\n");
-
-       /* bring chip out of any power saving mode it was in */
-       ks8851_set_powermode(ks, PMECR_PM_NORMAL);
-
-       /* issue a soft reset to the RX/TX QMU to put it into a known
-        * state. */
-       ks8851_soft_reset(ks, GRR_QMU);
-
-       /* setup transmission parameters */
-
-       ks8851_wrreg16(ks, KS_TXCR, (TXCR_TXE | /* enable transmit process */
-                                    TXCR_TXPE | /* pad to min length */
-                                    TXCR_TXCRC | /* add CRC */
-                                    TXCR_TXFCE)); /* enable flow control */
-
-       /* auto-increment tx data, reset tx pointer */
-       ks8851_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
-
-       /* setup receiver control */
-
-       ks8851_wrreg16(ks, KS_RXCR1, (RXCR1_RXPAFMA | /*  from mac filter */
-                                     RXCR1_RXFCE | /* enable flow control */
-                                     RXCR1_RXBE | /* broadcast enable */
-                                     RXCR1_RXUE | /* unicast enable */
-                                     RXCR1_RXE)); /* enable rx block */
-
-       /* transfer entire frames out in one go */
-       ks8851_wrreg16(ks, KS_RXCR2, RXCR2_SRDBL_FRAME);
-
-       /* set receive counter timeouts */
-       ks8851_wrreg16(ks, KS_RXDTTR, 1000); /* 1ms after first frame to IRQ */
-       ks8851_wrreg16(ks, KS_RXDBCTR, 4096); /* >4Kbytes in buffer to IRQ */
-       ks8851_wrreg16(ks, KS_RXFCTR, 10);  /* 10 frames to IRQ */
-
-       ks->rc_rxqcr = (RXQCR_RXFCTE |  /* IRQ on frame count exceeded */
-                       RXQCR_RXDBCTE | /* IRQ on byte count exceeded */
-                       RXQCR_RXDTTE);  /* IRQ on time exceeded */
-
-       ks8851_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
-
-       /* clear then enable interrupts */
-
-#define STD_IRQ (IRQ_LCI |     /* Link Change */       \
-                IRQ_TXI |      /* TX done */           \
-                IRQ_RXI |      /* RX done */           \
-                IRQ_SPIBEI |   /* SPI bus error */     \
-                IRQ_TXPSI |    /* TX process stop */   \
-                IRQ_RXPSI)     /* RX process stop */
-
-       ks->rc_ier = STD_IRQ;
-       ks8851_wrreg16(ks, KS_ISR, STD_IRQ);
-       ks8851_wrreg16(ks, KS_IER, STD_IRQ);
-
-       netif_start_queue(ks->netdev);
-
-       netif_dbg(ks, ifup, ks->netdev, "network device up\n");
-
-       mutex_unlock(&ks->lock);
-       return 0;
-}
-
-/**
- * ks8851_net_stop - close network device
- * @dev: The device being closed.
- *
- * Called to close down a network device which has been active. Cancell any
- * work, shutdown the RX and TX process and then place the chip into a low
- * power state whilst it is not being used.
- */
-static int ks8851_net_stop(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-
-       netif_info(ks, ifdown, dev, "shutting down\n");
-
-       netif_stop_queue(dev);
-
-       mutex_lock(&ks->lock);
-
-       /* stop any outstanding work */
-       flush_work(&ks->irq_work);
-       flush_work(&ks->tx_work);
-       flush_work(&ks->rxctrl_work);
-
-       /* turn off the IRQs and ack any outstanding */
-       ks8851_wrreg16(ks, KS_IER, 0x0000);
-       ks8851_wrreg16(ks, KS_ISR, 0xffff);
-
-       /* shutdown RX process */
-       ks8851_wrreg16(ks, KS_RXCR1, 0x0000);
-
-       /* shutdown TX process */
-       ks8851_wrreg16(ks, KS_TXCR, 0x0000);
-
-       /* set powermode to soft power down to save power */
-       ks8851_set_powermode(ks, PMECR_PM_SOFTDOWN);
-
-       /* ensure any queued tx buffers are dumped */
-       while (!skb_queue_empty(&ks->txq)) {
-               struct sk_buff *txb = skb_dequeue(&ks->txq);
-
-               netif_dbg(ks, ifdown, ks->netdev,
-                         "%s: freeing txb %p\n", __func__, txb);
-
-               dev_kfree_skb(txb);
-       }
-
-       mutex_unlock(&ks->lock);
-       return 0;
-}
-
-/**
- * ks8851_start_xmit - transmit packet
- * @skb: The buffer to transmit
- * @dev: The device used to transmit the packet.
- *
- * Called by the network layer to transmit the @skb. Queue the packet for
- * the device and schedule the necessary work to transmit the packet when
- * it is free.
- *
- * We do this to firstly avoid sleeping with the network device locked,
- * and secondly so we can round up more than one packet to transmit which
- * means we can try and avoid generating too many transmit done interrupts.
- */
-static netdev_tx_t ks8851_start_xmit(struct sk_buff *skb,
-                                    struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       unsigned needed = calc_txlen(skb->len);
-       netdev_tx_t ret = NETDEV_TX_OK;
-
-       netif_dbg(ks, tx_queued, ks->netdev,
-                 "%s: skb %p, %d@%p\n", __func__, skb, skb->len, skb->data);
-
-       spin_lock(&ks->statelock);
-
-       if (needed > ks->tx_space) {
-               netif_stop_queue(dev);
-               ret = NETDEV_TX_BUSY;
-       } else {
-               ks->tx_space -= needed;
-               skb_queue_tail(&ks->txq, skb);
-       }
-
-       spin_unlock(&ks->statelock);
-       schedule_work(&ks->tx_work);
-
-       return ret;
-}
-
-/**
- * ks8851_rxctrl_work - work handler to change rx mode
- * @work: The work structure this belongs to.
- *
- * Lock the device and issue the necessary changes to the receive mode from
- * the network device layer. This is done so that we can do this without
- * having to sleep whilst holding the network device lock.
- *
- * Since the recommendation from Micrel is that the RXQ is shutdown whilst the
- * receive parameters are programmed, we issue a write to disable the RXQ and
- * then wait for the interrupt handler to be triggered once the RXQ shutdown is
- * complete. The interrupt handler then writes the new values into the chip.
- */
-static void ks8851_rxctrl_work(struct work_struct *work)
-{
-       struct ks8851_net *ks = container_of(work, struct ks8851_net, rxctrl_work);
-
-       mutex_lock(&ks->lock);
-
-       /* need to shutdown RXQ before modifying filter parameters */
-       ks8851_wrreg16(ks, KS_RXCR1, 0x00);
-
-       mutex_unlock(&ks->lock);
-}
-
-static void ks8851_set_rx_mode(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       struct ks8851_rxctrl rxctrl;
-
-       memset(&rxctrl, 0, sizeof(rxctrl));
-
-       if (dev->flags & IFF_PROMISC) {
-               /* interface to receive everything */
-
-               rxctrl.rxcr1 = RXCR1_RXAE | RXCR1_RXINVF;
-       } else if (dev->flags & IFF_ALLMULTI) {
-               /* accept all multicast packets */
-
-               rxctrl.rxcr1 = (RXCR1_RXME | RXCR1_RXAE |
-                               RXCR1_RXPAFMA | RXCR1_RXMAFMA);
-       } else if (dev->flags & IFF_MULTICAST && !netdev_mc_empty(dev)) {
-               struct netdev_hw_addr *ha;
-               u32 crc;
-
-               /* accept some multicast */
-
-               netdev_for_each_mc_addr(ha, dev) {
-                       crc = ether_crc(ETH_ALEN, ha->addr);
-                       crc >>= (32 - 6);  /* get top six bits */
-
-                       rxctrl.mchash[crc >> 4] |= (1 << (crc & 0xf));
-               }
-
-               rxctrl.rxcr1 = RXCR1_RXME | RXCR1_RXPAFMA;
-       } else {
-               /* just accept broadcast / unicast */
-               rxctrl.rxcr1 = RXCR1_RXPAFMA;
-       }
-
-       rxctrl.rxcr1 |= (RXCR1_RXUE | /* unicast enable */
-                        RXCR1_RXBE | /* broadcast enable */
-                        RXCR1_RXE | /* RX process enable */
-                        RXCR1_RXFCE); /* enable flow control */
-
-       rxctrl.rxcr2 |= RXCR2_SRDBL_FRAME;
-
-       /* schedule work to do the actual set of the data if needed */
-
-       spin_lock(&ks->statelock);
-
-       if (memcmp(&rxctrl, &ks->rxctrl, sizeof(rxctrl)) != 0) {
-               memcpy(&ks->rxctrl, &rxctrl, sizeof(ks->rxctrl));
-               schedule_work(&ks->rxctrl_work);
-       }
-
-       spin_unlock(&ks->statelock);
-}
-
-static int ks8851_set_mac_address(struct net_device *dev, void *addr)
-{
-       struct sockaddr *sa = addr;
-
-       if (netif_running(dev))
-               return -EBUSY;
-
-       if (!is_valid_ether_addr(sa->sa_data))
-               return -EADDRNOTAVAIL;
-
-       memcpy(dev->dev_addr, sa->sa_data, ETH_ALEN);
-       return ks8851_write_mac_addr(dev);
-}
-
-static int ks8851_net_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-
-       if (!netif_running(dev))
-               return -EINVAL;
-
-       return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
-}
-
-static const struct net_device_ops ks8851_netdev_ops = {
-       .ndo_open               = ks8851_net_open,
-       .ndo_stop               = ks8851_net_stop,
-       .ndo_do_ioctl           = ks8851_net_ioctl,
-       .ndo_start_xmit         = ks8851_start_xmit,
-       .ndo_set_mac_address    = ks8851_set_mac_address,
-       .ndo_set_rx_mode        = ks8851_set_rx_mode,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-/* Companion eeprom access */
-
-enum { /* EEPROM programming states */
-       EEPROM_CONTROL,
-       EEPROM_ADDRESS,
-       EEPROM_DATA,
-       EEPROM_COMPLETE
-};
-
-/**
- * ks8851_eeprom_read - read a 16bits word in ks8851 companion EEPROM
- * @dev: The network device the PHY is on.
- * @addr: EEPROM address to read
- *
- * eeprom_size: used to define the data coding length. Can be changed
- * through debug-fs.
- *
- * Programs a read on the EEPROM using ks8851 EEPROM SW access feature.
- * Warning: The READ feature is not supported on ks8851 revision 0.
- *
- * Rough programming model:
- *  - on period start: set clock high and read value on bus
- *  - on period / 2: set clock low and program value on bus
- *  - start on period / 2
- */
-unsigned int ks8851_eeprom_read(struct net_device *dev, unsigned int addr)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       int eepcr;
-       int ctrl = EEPROM_OP_READ;
-       int state = EEPROM_CONTROL;
-       int bit_count = EEPROM_OP_LEN - 1;
-       unsigned int data = 0;
-       int dummy;
-       unsigned int addr_len;
-
-       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
-
-       /* start transaction: chip select high, authorize write */
-       mutex_lock(&ks->lock);
-       eepcr = EEPCR_EESA | EEPCR_EESRWA;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       eepcr |= EEPCR_EECS;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       mutex_unlock(&ks->lock);
-
-       while (state != EEPROM_COMPLETE) {
-               /* falling clock period starts... */
-               /* set EED_IO pin for control and address */
-               eepcr &= ~EEPCR_EEDO;
-               switch (state) {
-               case EEPROM_CONTROL:
-                       eepcr |= ((ctrl >> bit_count) & 1) << 2;
-                       if (bit_count-- <= 0) {
-                               bit_count = addr_len - 1;
-                               state = EEPROM_ADDRESS;
-                       }
-                       break;
-               case EEPROM_ADDRESS:
-                       eepcr |= ((addr >> bit_count) & 1) << 2;
-                       bit_count--;
-                       break;
-               case EEPROM_DATA:
-                       /* Change to receive mode */
-                       eepcr &= ~EEPCR_EESRWA;
-                       break;
-               }
-
-               /* lower clock  */
-               eepcr &= ~EEPCR_EESCK;
-
-               mutex_lock(&ks->lock);
-               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-               mutex_unlock(&ks->lock);
-
-               /* waitread period / 2 */
-               udelay(EEPROM_SK_PERIOD / 2);
-
-               /* rising clock period starts... */
-
-               /* raise clock */
-               mutex_lock(&ks->lock);
-               eepcr |= EEPCR_EESCK;
-               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-               mutex_unlock(&ks->lock);
-
-               /* Manage read */
-               switch (state) {
-               case EEPROM_ADDRESS:
-                       if (bit_count < 0) {
-                               bit_count = EEPROM_DATA_LEN - 1;
-                               state = EEPROM_DATA;
-                       }
-                       break;
-               case EEPROM_DATA:
-                       mutex_lock(&ks->lock);
-                       dummy = ks8851_rdreg16(ks, KS_EEPCR);
-                       mutex_unlock(&ks->lock);
-                       data |= ((dummy >> EEPCR_EESB_OFFSET) & 1) << bit_count;
-                       if (bit_count-- <= 0)
-                               state = EEPROM_COMPLETE;
-                       break;
-               }
-
-               /* wait period / 2 */
-               udelay(EEPROM_SK_PERIOD / 2);
-       }
-
-       /* close transaction */
-       mutex_lock(&ks->lock);
-       eepcr &= ~EEPCR_EECS;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       eepcr = 0;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       mutex_unlock(&ks->lock);
-
-       return data;
-}
-
-/**
- * ks8851_eeprom_write - write a 16bits word in ks8851 companion EEPROM
- * @dev: The network device the PHY is on.
- * @op: operand (can be WRITE, EWEN, EWDS)
- * @addr: EEPROM address to write
- * @data: data to write
- *
- * eeprom_size: used to define the data coding length. Can be changed
- * through debug-fs.
- *
- * Programs a write on the EEPROM using ks8851 EEPROM SW access feature.
- *
- * Note that a write enable is required before writing data.
- *
- * Rough programming model:
- *  - on period start: set clock high
- *  - on period / 2: set clock low and program value on bus
- *  - start on period / 2
- */
-void ks8851_eeprom_write(struct net_device *dev, unsigned int op,
-                                       unsigned int addr, unsigned int data)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       int eepcr;
-       int state = EEPROM_CONTROL;
-       int bit_count = EEPROM_OP_LEN - 1;
-       unsigned int addr_len;
-
-       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
-
-       switch (op) {
-       case EEPROM_OP_EWEN:
-               addr = 0x30;
-       break;
-       case EEPROM_OP_EWDS:
-               addr = 0;
-               break;
-       }
-
-       /* start transaction: chip select high, authorize write */
-       mutex_lock(&ks->lock);
-       eepcr = EEPCR_EESA | EEPCR_EESRWA;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       eepcr |= EEPCR_EECS;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       mutex_unlock(&ks->lock);
-
-       while (state != EEPROM_COMPLETE) {
-               /* falling clock period starts... */
-               /* set EED_IO pin for control and address */
-               eepcr &= ~EEPCR_EEDO;
-               switch (state) {
-               case EEPROM_CONTROL:
-                       eepcr |= ((op >> bit_count) & 1) << 2;
-                       if (bit_count-- <= 0) {
-                               bit_count = addr_len - 1;
-                               state = EEPROM_ADDRESS;
-                       }
-                       break;
-               case EEPROM_ADDRESS:
-                       eepcr |= ((addr >> bit_count) & 1) << 2;
-                       if (bit_count-- <= 0) {
-                               if (op == EEPROM_OP_WRITE) {
-                                       bit_count = EEPROM_DATA_LEN - 1;
-                                       state = EEPROM_DATA;
-                               } else {
-                                       state = EEPROM_COMPLETE;
-                               }
-                       }
-                       break;
-               case EEPROM_DATA:
-                       eepcr |= ((data >> bit_count) & 1) << 2;
-                       if (bit_count-- <= 0)
-                               state = EEPROM_COMPLETE;
-                       break;
-               }
-
-               /* lower clock  */
-               eepcr &= ~EEPCR_EESCK;
-
-               mutex_lock(&ks->lock);
-               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-               mutex_unlock(&ks->lock);
-
-               /* wait period / 2 */
-               udelay(EEPROM_SK_PERIOD / 2);
-
-               /* rising clock period starts... */
-
-               /* raise clock */
-               eepcr |= EEPCR_EESCK;
-               mutex_lock(&ks->lock);
-               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-               mutex_unlock(&ks->lock);
-
-               /* wait period / 2 */
-               udelay(EEPROM_SK_PERIOD / 2);
-       }
-
-       /* close transaction */
-       mutex_lock(&ks->lock);
-       eepcr &= ~EEPCR_EECS;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       eepcr = 0;
-       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
-       mutex_unlock(&ks->lock);
-
-}
-
-/* ethtool support */
-
-static void ks8851_get_drvinfo(struct net_device *dev,
-                              struct ethtool_drvinfo *di)
-{
-       strlcpy(di->driver, "KS8851", sizeof(di->driver));
-       strlcpy(di->version, "1.00", sizeof(di->version));
-       strlcpy(di->bus_info, dev_name(dev->dev.parent), sizeof(di->bus_info));
-}
-
-static u32 ks8851_get_msglevel(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return ks->msg_enable;
-}
-
-static void ks8851_set_msglevel(struct net_device *dev, u32 to)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       ks->msg_enable = to;
-}
-
-static int ks8851_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return mii_ethtool_gset(&ks->mii, cmd);
-}
-
-static int ks8851_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return mii_ethtool_sset(&ks->mii, cmd);
-}
-
-static u32 ks8851_get_link(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return mii_link_ok(&ks->mii);
-}
-
-static int ks8851_nway_reset(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return mii_nway_restart(&ks->mii);
-}
-
-static int ks8851_get_eeprom_len(struct net_device *dev)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       return ks->eeprom_size;
-}
-
-static int ks8851_get_eeprom(struct net_device *dev,
-                           struct ethtool_eeprom *eeprom, u8 *bytes)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       u16 *eeprom_buff;
-       int first_word;
-       int last_word;
-       int ret_val = 0;
-       u16 i;
-
-       if (eeprom->len == 0)
-               return -EINVAL;
-
-       if (eeprom->len > ks->eeprom_size)
-               return -EINVAL;
-
-       eeprom->magic = ks8851_rdreg16(ks, KS_CIDER);
-
-       first_word = eeprom->offset >> 1;
-       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
-
-       eeprom_buff = kmalloc(sizeof(u16) *
-                       (last_word - first_word + 1), GFP_KERNEL);
-       if (!eeprom_buff)
-               return -ENOMEM;
-
-       for (i = 0; i < last_word - first_word + 1; i++)
-               eeprom_buff[i] = ks8851_eeprom_read(dev, first_word + 1);
-
-       /* Device's eeprom is little-endian, word addressable */
-       for (i = 0; i < last_word - first_word + 1; i++)
-               le16_to_cpus(&eeprom_buff[i]);
-
-       memcpy(bytes, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
-       kfree(eeprom_buff);
-
-       return ret_val;
-}
-
-static int ks8851_set_eeprom(struct net_device *dev,
-                           struct ethtool_eeprom *eeprom, u8 *bytes)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       u16 *eeprom_buff;
-       void *ptr;
-       int max_len;
-       int first_word;
-       int last_word;
-       int ret_val = 0;
-       u16 i;
-
-       if (eeprom->len == 0)
-               return -EOPNOTSUPP;
-
-       if (eeprom->len > ks->eeprom_size)
-               return -EINVAL;
-
-       if (eeprom->magic != ks8851_rdreg16(ks, KS_CIDER))
-               return -EFAULT;
-
-       first_word = eeprom->offset >> 1;
-       last_word = (eeprom->offset + eeprom->len - 1) >> 1;
-       max_len = (last_word - first_word + 1) * 2;
-       eeprom_buff = kmalloc(max_len, GFP_KERNEL);
-       if (!eeprom_buff)
-               return -ENOMEM;
-
-       ptr = (void *)eeprom_buff;
-
-       if (eeprom->offset & 1) {
-               /* need read/modify/write of first changed EEPROM word */
-               /* only the second byte of the word is being modified */
-               eeprom_buff[0] = ks8851_eeprom_read(dev, first_word);
-               ptr++;
-       }
-       if ((eeprom->offset + eeprom->len) & 1)
-               /* need read/modify/write of last changed EEPROM word */
-               /* only the first byte of the word is being modified */
-               eeprom_buff[last_word - first_word] =
-                                       ks8851_eeprom_read(dev, last_word);
-
-
-       /* Device's eeprom is little-endian, word addressable */
-       le16_to_cpus(&eeprom_buff[0]);
-       le16_to_cpus(&eeprom_buff[last_word - first_word]);
-
-       memcpy(ptr, bytes, eeprom->len);
-
-       for (i = 0; i < last_word - first_word + 1; i++)
-               eeprom_buff[i] = cpu_to_le16(eeprom_buff[i]);
-
-       ks8851_eeprom_write(dev, EEPROM_OP_EWEN, 0, 0);
-
-       for (i = 0; i < last_word - first_word + 1; i++) {
-               ks8851_eeprom_write(dev, EEPROM_OP_WRITE, first_word + i,
-                                                       eeprom_buff[i]);
-               mdelay(EEPROM_WRITE_TIME);
-       }
-
-       ks8851_eeprom_write(dev, EEPROM_OP_EWDS, 0, 0);
-
-       kfree(eeprom_buff);
-       return ret_val;
-}
-
-static const struct ethtool_ops ks8851_ethtool_ops = {
-       .get_drvinfo    = ks8851_get_drvinfo,
-       .get_msglevel   = ks8851_get_msglevel,
-       .set_msglevel   = ks8851_set_msglevel,
-       .get_settings   = ks8851_get_settings,
-       .set_settings   = ks8851_set_settings,
-       .get_link       = ks8851_get_link,
-       .nway_reset     = ks8851_nway_reset,
-       .get_eeprom_len = ks8851_get_eeprom_len,
-       .get_eeprom     = ks8851_get_eeprom,
-       .set_eeprom     = ks8851_set_eeprom,
-};
-
-/* MII interface controls */
-
-/**
- * ks8851_phy_reg - convert MII register into a KS8851 register
- * @reg: MII register number.
- *
- * Return the KS8851 register number for the corresponding MII PHY register
- * if possible. Return zero if the MII register has no direct mapping to the
- * KS8851 register set.
- */
-static int ks8851_phy_reg(int reg)
-{
-       switch (reg) {
-       case MII_BMCR:
-               return KS_P1MBCR;
-       case MII_BMSR:
-               return KS_P1MBSR;
-       case MII_PHYSID1:
-               return KS_PHY1ILR;
-       case MII_PHYSID2:
-               return KS_PHY1IHR;
-       case MII_ADVERTISE:
-               return KS_P1ANAR;
-       case MII_LPA:
-               return KS_P1ANLPR;
-       }
-
-       return 0x0;
-}
-
-/**
- * ks8851_phy_read - MII interface PHY register read.
- * @dev: The network device the PHY is on.
- * @phy_addr: Address of PHY (ignored as we only have one)
- * @reg: The register to read.
- *
- * This call reads data from the PHY register specified in @reg. Since the
- * device does not support all the MII registers, the non-existent values
- * are always returned as zero.
- *
- * We return zero for unsupported registers as the MII code does not check
- * the value returned for any error status, and simply returns it to the
- * caller. The mii-tool that the driver was tested with takes any -ve error
- * as real PHY capabilities, thus displaying incorrect data to the user.
- */
-static int ks8851_phy_read(struct net_device *dev, int phy_addr, int reg)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       int ksreg;
-       int result;
-
-       ksreg = ks8851_phy_reg(reg);
-       if (!ksreg)
-               return 0x0;     /* no error return allowed, so use zero */
-
-       mutex_lock(&ks->lock);
-       result = ks8851_rdreg16(ks, ksreg);
-       mutex_unlock(&ks->lock);
-
-       return result;
-}
-
-static void ks8851_phy_write(struct net_device *dev,
-                            int phy, int reg, int value)
-{
-       struct ks8851_net *ks = netdev_priv(dev);
-       int ksreg;
-
-       ksreg = ks8851_phy_reg(reg);
-       if (ksreg) {
-               mutex_lock(&ks->lock);
-               ks8851_wrreg16(ks, ksreg, value);
-               mutex_unlock(&ks->lock);
-       }
-}
-
-/**
- * ks8851_read_selftest - read the selftest memory info.
- * @ks: The device state
- *
- * Read and check the TX/RX memory selftest information.
- */
-static int ks8851_read_selftest(struct ks8851_net *ks)
-{
-       unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
-       int ret = 0;
-       unsigned rd;
-
-       rd = ks8851_rdreg16(ks, KS_MBIR);
-
-       if ((rd & both_done) != both_done) {
-               netdev_warn(ks->netdev, "Memory selftest not finished\n");
-               return 0;
-       }
-
-       if (rd & MBIR_TXMBFA) {
-               netdev_err(ks->netdev, "TX memory selftest fail\n");
-               ret |= 1;
-       }
-
-       if (rd & MBIR_RXMBFA) {
-               netdev_err(ks->netdev, "RX memory selftest fail\n");
-               ret |= 2;
-       }
-
-       return 0;
-}
-
-/* driver bus management functions */
-
-#ifdef CONFIG_PM
-static int ks8851_suspend(struct spi_device *spi, pm_message_t state)
-{
-       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
-       struct net_device *dev = ks->netdev;
-
-       if (netif_running(dev)) {
-               netif_device_detach(dev);
-               ks8851_net_stop(dev);
-       }
-
-       return 0;
-}
-
-static int ks8851_resume(struct spi_device *spi)
-{
-       struct ks8851_net *ks = dev_get_drvdata(&spi->dev);
-       struct net_device *dev = ks->netdev;
-
-       if (netif_running(dev)) {
-               ks8851_net_open(dev);
-               netif_device_attach(dev);
-       }
-
-       return 0;
-}
-#else
-#define ks8851_suspend NULL
-#define ks8851_resume NULL
-#endif
-
-static int __devinit ks8851_probe(struct spi_device *spi)
-{
-       struct net_device *ndev;
-       struct ks8851_net *ks;
-       int ret;
-
-       ndev = alloc_etherdev(sizeof(struct ks8851_net));
-       if (!ndev) {
-               dev_err(&spi->dev, "failed to alloc ethernet device\n");
-               return -ENOMEM;
-       }
-
-       spi->bits_per_word = 8;
-
-       ks = netdev_priv(ndev);
-
-       ks->netdev = ndev;
-       ks->spidev = spi;
-       ks->tx_space = 6144;
-
-       mutex_init(&ks->lock);
-       spin_lock_init(&ks->statelock);
-
-       INIT_WORK(&ks->tx_work, ks8851_tx_work);
-       INIT_WORK(&ks->irq_work, ks8851_irq_work);
-       INIT_WORK(&ks->rxctrl_work, ks8851_rxctrl_work);
-
-       /* initialise pre-made spi transfer messages */
-
-       spi_message_init(&ks->spi_msg1);
-       spi_message_add_tail(&ks->spi_xfer1, &ks->spi_msg1);
-
-       spi_message_init(&ks->spi_msg2);
-       spi_message_add_tail(&ks->spi_xfer2[0], &ks->spi_msg2);
-       spi_message_add_tail(&ks->spi_xfer2[1], &ks->spi_msg2);
-
-       /* setup mii state */
-       ks->mii.dev             = ndev;
-       ks->mii.phy_id          = 1,
-       ks->mii.phy_id_mask     = 1;
-       ks->mii.reg_num_mask    = 0xf;
-       ks->mii.mdio_read       = ks8851_phy_read;
-       ks->mii.mdio_write      = ks8851_phy_write;
-
-       dev_info(&spi->dev, "message enable is %d\n", msg_enable);
-
-       /* set the default message enable */
-       ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
-                                                    NETIF_MSG_PROBE |
-                                                    NETIF_MSG_LINK));
-
-       skb_queue_head_init(&ks->txq);
-
-       SET_ETHTOOL_OPS(ndev, &ks8851_ethtool_ops);
-       SET_NETDEV_DEV(ndev, &spi->dev);
-
-       dev_set_drvdata(&spi->dev, ks);
-
-       ndev->if_port = IF_PORT_100BASET;
-       ndev->netdev_ops = &ks8851_netdev_ops;
-       ndev->irq = spi->irq;
-
-       /* issue a global soft reset to reset the device. */
-       ks8851_soft_reset(ks, GRR_GSR);
-
-       /* simple check for a valid chip being connected to the bus */
-
-       if ((ks8851_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
-               dev_err(&spi->dev, "failed to read device ID\n");
-               ret = -ENODEV;
-               goto err_id;
-       }
-
-       /* cache the contents of the CCR register for EEPROM, etc. */
-       ks->rc_ccr = ks8851_rdreg16(ks, KS_CCR);
-
-       if (ks->rc_ccr & CCR_EEPROM)
-               ks->eeprom_size = 128;
-       else
-               ks->eeprom_size = 0;
-
-       ks8851_read_selftest(ks);
-       ks8851_init_mac(ks);
-
-       ret = request_irq(spi->irq, ks8851_irq, IRQF_TRIGGER_LOW,
-                         ndev->name, ks);
-       if (ret < 0) {
-               dev_err(&spi->dev, "failed to get irq\n");
-               goto err_irq;
-       }
-
-       ret = register_netdev(ndev);
-       if (ret) {
-               dev_err(&spi->dev, "failed to register network device\n");
-               goto err_netdev;
-       }
-
-       netdev_info(ndev, "revision %d, MAC %pM, IRQ %d\n",
-                   CIDER_REV_GET(ks8851_rdreg16(ks, KS_CIDER)),
-                   ndev->dev_addr, ndev->irq);
-
-       return 0;
-
-
-err_netdev:
-       free_irq(ndev->irq, ndev);
-
-err_id:
-err_irq:
-       free_netdev(ndev);
-       return ret;
-}
-
-static int __devexit ks8851_remove(struct spi_device *spi)
-{
-       struct ks8851_net *priv = dev_get_drvdata(&spi->dev);
-
-       if (netif_msg_drv(priv))
-               dev_info(&spi->dev, "remove\n");
-
-       unregister_netdev(priv->netdev);
-       free_irq(spi->irq, priv);
-       free_netdev(priv->netdev);
-
-       return 0;
-}
-
-static struct spi_driver ks8851_driver = {
-       .driver = {
-               .name = "ks8851",
-               .owner = THIS_MODULE,
-       },
-       .probe = ks8851_probe,
-       .remove = __devexit_p(ks8851_remove),
-       .suspend = ks8851_suspend,
-       .resume = ks8851_resume,
-};
-
-static int __init ks8851_init(void)
-{
-       return spi_register_driver(&ks8851_driver);
-}
-
-static void __exit ks8851_exit(void)
-{
-       spi_unregister_driver(&ks8851_driver);
-}
-
-module_init(ks8851_init);
-module_exit(ks8851_exit);
-
-MODULE_DESCRIPTION("KS8851 Network driver");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
-
-module_param_named(message, msg_enable, int, 0);
-MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
-MODULE_ALIAS("spi:ks8851");
diff --git a/drivers/net/ks8851.h b/drivers/net/ks8851.h
deleted file mode 100644 (file)
index 537fb06..0000000
+++ /dev/null
@@ -1,309 +0,0 @@
-/* drivers/net/ks8851.h
- *
- * Copyright 2009 Simtec Electronics
- *      Ben Dooks <ben@simtec.co.uk>
- *
- * KS8851 register definitions
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
-*/
-
-#define KS_CCR                                 0x08
-#define CCR_EEPROM                             (1 << 9)
-#define CCR_SPI                                        (1 << 8)
-#define CCR_32PIN                              (1 << 0)
-
-/* MAC address registers */
-#define KS_MAR(_m)                             0x15 - (_m)
-#define KS_MARL                                        0x10
-#define KS_MARM                                        0x12
-#define KS_MARH                                        0x14
-
-#define KS_OBCR                                        0x20
-#define OBCR_ODS_16mA                          (1 << 6)
-
-#define KS_EEPCR                               0x22
-#define EEPCR_EESRWA                           (1 << 5)
-#define EEPCR_EESA                             (1 << 4)
-#define EEPCR_EESB_OFFSET                      3
-#define EEPCR_EESB                             (1 << EEPCR_EESB_OFFSET)
-#define EEPCR_EEDO                             (1 << 2)
-#define EEPCR_EESCK                            (1 << 1)
-#define EEPCR_EECS                             (1 << 0)
-
-#define EEPROM_OP_LEN                          3       /* bits:*/
-#define EEPROM_OP_READ                         0x06
-#define EEPROM_OP_EWEN                         0x04
-#define EEPROM_OP_WRITE                                0x05
-#define EEPROM_OP_EWDS                         0x14
-
-#define EEPROM_DATA_LEN                                16      /* 16 bits EEPROM */
-#define EEPROM_WRITE_TIME                      4       /* wrt ack time in ms */
-#define EEPROM_SK_PERIOD                       400     /* in us */
-
-#define KS_MBIR                                        0x24
-#define MBIR_TXMBF                             (1 << 12)
-#define MBIR_TXMBFA                            (1 << 11)
-#define MBIR_RXMBF                             (1 << 4)
-#define MBIR_RXMBFA                            (1 << 3)
-
-#define KS_GRR                                 0x26
-#define GRR_QMU                                        (1 << 1)
-#define GRR_GSR                                        (1 << 0)
-
-#define KS_WFCR                                        0x2A
-#define WFCR_MPRXE                             (1 << 7)
-#define WFCR_WF3E                              (1 << 3)
-#define WFCR_WF2E                              (1 << 2)
-#define WFCR_WF1E                              (1 << 1)
-#define WFCR_WF0E                              (1 << 0)
-
-#define KS_WF0CRC0                             0x30
-#define KS_WF0CRC1                             0x32
-#define KS_WF0BM0                              0x34
-#define KS_WF0BM1                              0x36
-#define KS_WF0BM2                              0x38
-#define KS_WF0BM3                              0x3A
-
-#define KS_WF1CRC0                             0x40
-#define KS_WF1CRC1                             0x42
-#define KS_WF1BM0                              0x44
-#define KS_WF1BM1                              0x46
-#define KS_WF1BM2                              0x48
-#define KS_WF1BM3                              0x4A
-
-#define KS_WF2CRC0                             0x50
-#define KS_WF2CRC1                             0x52
-#define KS_WF2BM0                              0x54
-#define KS_WF2BM1                              0x56
-#define KS_WF2BM2                              0x58
-#define KS_WF2BM3                              0x5A
-
-#define KS_WF3CRC0                             0x60
-#define KS_WF3CRC1                             0x62
-#define KS_WF3BM0                              0x64
-#define KS_WF3BM1                              0x66
-#define KS_WF3BM2                              0x68
-#define KS_WF3BM3                              0x6A
-
-#define KS_TXCR                                        0x70
-#define TXCR_TCGICMP                           (1 << 8)
-#define TXCR_TCGUDP                            (1 << 7)
-#define TXCR_TCGTCP                            (1 << 6)
-#define TXCR_TCGIP                             (1 << 5)
-#define TXCR_FTXQ                              (1 << 4)
-#define TXCR_TXFCE                             (1 << 3)
-#define TXCR_TXPE                              (1 << 2)
-#define TXCR_TXCRC                             (1 << 1)
-#define TXCR_TXE                               (1 << 0)
-
-#define KS_TXSR                                        0x72
-#define TXSR_TXLC                              (1 << 13)
-#define TXSR_TXMC                              (1 << 12)
-#define TXSR_TXFID_MASK                                (0x3f << 0)
-#define TXSR_TXFID_SHIFT                       (0)
-#define TXSR_TXFID_GET(_v)                     (((_v) >> 0) & 0x3f)
-
-#define KS_RXCR1                               0x74
-#define RXCR1_FRXQ                             (1 << 15)
-#define RXCR1_RXUDPFCC                         (1 << 14)
-#define RXCR1_RXTCPFCC                         (1 << 13)
-#define RXCR1_RXIPFCC                          (1 << 12)
-#define RXCR1_RXPAFMA                          (1 << 11)
-#define RXCR1_RXFCE                            (1 << 10)
-#define RXCR1_RXEFE                            (1 << 9)
-#define RXCR1_RXMAFMA                          (1 << 8)
-#define RXCR1_RXBE                             (1 << 7)
-#define RXCR1_RXME                             (1 << 6)
-#define RXCR1_RXUE                             (1 << 5)
-#define RXCR1_RXAE                             (1 << 4)
-#define RXCR1_RXINVF                           (1 << 1)
-#define RXCR1_RXE                              (1 << 0)
-
-#define KS_RXCR2                               0x76
-#define RXCR2_SRDBL_MASK                       (0x7 << 5)
-#define RXCR2_SRDBL_SHIFT                      (5)
-#define RXCR2_SRDBL_4B                         (0x0 << 5)
-#define RXCR2_SRDBL_8B                         (0x1 << 5)
-#define RXCR2_SRDBL_16B                                (0x2 << 5)
-#define RXCR2_SRDBL_32B                                (0x3 << 5)
-#define RXCR2_SRDBL_FRAME                      (0x4 << 5)
-#define RXCR2_IUFFP                            (1 << 4)
-#define RXCR2_RXIUFCEZ                         (1 << 3)
-#define RXCR2_UDPLFE                           (1 << 2)
-#define RXCR2_RXICMPFCC                                (1 << 1)
-#define RXCR2_RXSAF                            (1 << 0)
-
-#define KS_TXMIR                               0x78
-
-#define KS_RXFHSR                              0x7C
-#define RXFSHR_RXFV                            (1 << 15)
-#define RXFSHR_RXICMPFCS                       (1 << 13)
-#define RXFSHR_RXIPFCS                         (1 << 12)
-#define RXFSHR_RXTCPFCS                                (1 << 11)
-#define RXFSHR_RXUDPFCS                                (1 << 10)
-#define RXFSHR_RXBF                            (1 << 7)
-#define RXFSHR_RXMF                            (1 << 6)
-#define RXFSHR_RXUF                            (1 << 5)
-#define RXFSHR_RXMR                            (1 << 4)
-#define RXFSHR_RXFT                            (1 << 3)
-#define RXFSHR_RXFTL                           (1 << 2)
-#define RXFSHR_RXRF                            (1 << 1)
-#define RXFSHR_RXCE                            (1 << 0)
-
-#define KS_RXFHBCR                             0x7E
-#define KS_TXQCR                               0x80
-#define TXQCR_AETFE                            (1 << 2)
-#define TXQCR_TXQMAM                           (1 << 1)
-#define TXQCR_METFE                            (1 << 0)
-
-#define KS_RXQCR                               0x82
-#define RXQCR_RXDTTS                           (1 << 12)
-#define RXQCR_RXDBCTS                          (1 << 11)
-#define RXQCR_RXFCTS                           (1 << 10)
-#define RXQCR_RXIPHTOE                         (1 << 9)
-#define RXQCR_RXDTTE                           (1 << 7)
-#define RXQCR_RXDBCTE                          (1 << 6)
-#define RXQCR_RXFCTE                           (1 << 5)
-#define RXQCR_ADRFE                            (1 << 4)
-#define RXQCR_SDA                              (1 << 3)
-#define RXQCR_RRXEF                            (1 << 0)
-
-#define KS_TXFDPR                              0x84
-#define TXFDPR_TXFPAI                          (1 << 14)
-#define TXFDPR_TXFP_MASK                       (0x7ff << 0)
-#define TXFDPR_TXFP_SHIFT                      (0)
-
-#define KS_RXFDPR                              0x86
-#define RXFDPR_RXFPAI                          (1 << 14)
-
-#define KS_RXDTTR                              0x8C
-#define KS_RXDBCTR                             0x8E
-
-#define KS_IER                                 0x90
-#define KS_ISR                                 0x92
-#define IRQ_LCI                                        (1 << 15)
-#define IRQ_TXI                                        (1 << 14)
-#define IRQ_RXI                                        (1 << 13)
-#define IRQ_RXOI                               (1 << 11)
-#define IRQ_TXPSI                              (1 << 9)
-#define IRQ_RXPSI                              (1 << 8)
-#define IRQ_TXSAI                              (1 << 6)
-#define IRQ_RXWFDI                             (1 << 5)
-#define IRQ_RXMPDI                             (1 << 4)
-#define IRQ_LDI                                        (1 << 3)
-#define IRQ_EDI                                        (1 << 2)
-#define IRQ_SPIBEI                             (1 << 1)
-#define IRQ_DEDI                               (1 << 0)
-
-#define KS_RXFCTR                              0x9C
-#define KS_RXFC                                        0x9D
-#define RXFCTR_RXFC_MASK                       (0xff << 8)
-#define RXFCTR_RXFC_SHIFT                      (8)
-#define RXFCTR_RXFC_GET(_v)                    (((_v) >> 8) & 0xff)
-#define RXFCTR_RXFCT_MASK                      (0xff << 0)
-#define RXFCTR_RXFCT_SHIFT                     (0)
-
-#define KS_TXNTFSR                             0x9E
-
-#define KS_MAHTR0                              0xA0
-#define KS_MAHTR1                              0xA2
-#define KS_MAHTR2                              0xA4
-#define KS_MAHTR3                              0xA6
-
-#define KS_FCLWR                               0xB0
-#define KS_FCHWR                               0xB2
-#define KS_FCOWR                               0xB4
-
-#define KS_CIDER                               0xC0
-#define CIDER_ID                               0x8870
-#define CIDER_REV_MASK                         (0x7 << 1)
-#define CIDER_REV_SHIFT                                (1)
-#define CIDER_REV_GET(_v)                      (((_v) >> 1) & 0x7)
-
-#define KS_CGCR                                        0xC6
-
-#define KS_IACR                                        0xC8
-#define IACR_RDEN                              (1 << 12)
-#define IACR_TSEL_MASK                         (0x3 << 10)
-#define IACR_TSEL_SHIFT                                (10)
-#define IACR_TSEL_MIB                          (0x3 << 10)
-#define IACR_ADDR_MASK                         (0x1f << 0)
-#define IACR_ADDR_SHIFT                                (0)
-
-#define KS_IADLR                               0xD0
-#define KS_IAHDR                               0xD2
-
-#define KS_PMECR                               0xD4
-#define PMECR_PME_DELAY                                (1 << 14)
-#define PMECR_PME_POL                          (1 << 12)
-#define PMECR_WOL_WAKEUP                       (1 << 11)
-#define PMECR_WOL_MAGICPKT                     (1 << 10)
-#define PMECR_WOL_LINKUP                       (1 << 9)
-#define PMECR_WOL_ENERGY                       (1 << 8)
-#define PMECR_AUTO_WAKE_EN                     (1 << 7)
-#define PMECR_WAKEUP_NORMAL                    (1 << 6)
-#define PMECR_WKEVT_MASK                       (0xf << 2)
-#define PMECR_WKEVT_SHIFT                      (2)
-#define PMECR_WKEVT_GET(_v)                    (((_v) >> 2) & 0xf)
-#define PMECR_WKEVT_ENERGY                     (0x1 << 2)
-#define PMECR_WKEVT_LINK                       (0x2 << 2)
-#define PMECR_WKEVT_MAGICPKT                   (0x4 << 2)
-#define PMECR_WKEVT_FRAME                      (0x8 << 2)
-#define PMECR_PM_MASK                          (0x3 << 0)
-#define PMECR_PM_SHIFT                         (0)
-#define PMECR_PM_NORMAL                                (0x0 << 0)
-#define PMECR_PM_ENERGY                                (0x1 << 0)
-#define PMECR_PM_SOFTDOWN                      (0x2 << 0)
-#define PMECR_PM_POWERSAVE                     (0x3 << 0)
-
-/* Standard MII PHY data */
-#define KS_P1MBCR                              0xE4
-#define KS_P1MBSR                              0xE6
-#define KS_PHY1ILR                             0xE8
-#define KS_PHY1IHR                             0xEA
-#define KS_P1ANAR                              0xEC
-#define KS_P1ANLPR                             0xEE
-
-#define KS_P1SCLMD                             0xF4
-#define P1SCLMD_LEDOFF                         (1 << 15)
-#define P1SCLMD_TXIDS                          (1 << 14)
-#define P1SCLMD_RESTARTAN                      (1 << 13)
-#define P1SCLMD_DISAUTOMDIX                    (1 << 10)
-#define P1SCLMD_FORCEMDIX                      (1 << 9)
-#define P1SCLMD_AUTONEGEN                      (1 << 7)
-#define P1SCLMD_FORCE100                       (1 << 6)
-#define P1SCLMD_FORCEFDX                       (1 << 5)
-#define P1SCLMD_ADV_FLOW                       (1 << 4)
-#define P1SCLMD_ADV_100BT_FDX                  (1 << 3)
-#define P1SCLMD_ADV_100BT_HDX                  (1 << 2)
-#define P1SCLMD_ADV_10BT_FDX                   (1 << 1)
-#define P1SCLMD_ADV_10BT_HDX                   (1 << 0)
-
-#define KS_P1CR                                        0xF6
-#define P1CR_HP_MDIX                           (1 << 15)
-#define P1CR_REV_POL                           (1 << 13)
-#define P1CR_OP_100M                           (1 << 10)
-#define P1CR_OP_FDX                            (1 << 9)
-#define P1CR_OP_MDI                            (1 << 7)
-#define P1CR_AN_DONE                           (1 << 6)
-#define P1CR_LINK_GOOD                         (1 << 5)
-#define P1CR_PNTR_FLOW                         (1 << 4)
-#define P1CR_PNTR_100BT_FDX                    (1 << 3)
-#define P1CR_PNTR_100BT_HDX                    (1 << 2)
-#define P1CR_PNTR_10BT_FDX                     (1 << 1)
-#define P1CR_PNTR_10BT_HDX                     (1 << 0)
-
-/* TX Frame control */
-
-#define TXFR_TXIC                              (1 << 15)
-#define TXFR_TXFID_MASK                                (0x3f << 0)
-#define TXFR_TXFID_SHIFT                       (0)
-
-/* SPI frame opcodes */
-#define KS_SPIOP_RD                            (0x00)
-#define KS_SPIOP_WR                            (0x40)
-#define KS_SPIOP_RXFIFO                                (0x80)
-#define KS_SPIOP_TXFIFO                                (0xC0)
diff --git a/drivers/net/ks8851_mll.c b/drivers/net/ks8851_mll.c
deleted file mode 100644 (file)
index d19c849..0000000
+++ /dev/null
@@ -1,1680 +0,0 @@
-/**
- * drivers/net/ks8851_mll.c
- * Copyright (c) 2009 Micrel Inc.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/**
- * Supports:
- * KS8851 16bit MLL chip from Micrel Inc.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/interrupt.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/ethtool.h>
-#include <linux/cache.h>
-#include <linux/crc32.h>
-#include <linux/mii.h>
-#include <linux/platform_device.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <asm/io.h>
-
-#define        DRV_NAME        "ks8851_mll"
-
-static u8 KS_DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x86, 0x95, 0x11 };
-#define MAX_RECV_FRAMES                        32
-#define MAX_BUF_SIZE                   2048
-#define TX_BUF_SIZE                    2000
-#define RX_BUF_SIZE                    2000
-
-#define KS_CCR                         0x08
-#define CCR_EEPROM                     (1 << 9)
-#define CCR_SPI                                (1 << 8)
-#define CCR_8BIT                       (1 << 7)
-#define CCR_16BIT                      (1 << 6)
-#define CCR_32BIT                      (1 << 5)
-#define CCR_SHARED                     (1 << 4)
-#define CCR_32PIN                      (1 << 0)
-
-/* MAC address registers */
-#define KS_MARL                                0x10
-#define KS_MARM                                0x12
-#define KS_MARH                                0x14
-
-#define KS_OBCR                                0x20
-#define OBCR_ODS_16MA                  (1 << 6)
-
-#define KS_EEPCR                       0x22
-#define EEPCR_EESA                     (1 << 4)
-#define EEPCR_EESB                     (1 << 3)
-#define EEPCR_EEDO                     (1 << 2)
-#define EEPCR_EESCK                    (1 << 1)
-#define EEPCR_EECS                     (1 << 0)
-
-#define KS_MBIR                                0x24
-#define MBIR_TXMBF                     (1 << 12)
-#define MBIR_TXMBFA                    (1 << 11)
-#define MBIR_RXMBF                     (1 << 4)
-#define MBIR_RXMBFA                    (1 << 3)
-
-#define KS_GRR                         0x26
-#define GRR_QMU                                (1 << 1)
-#define GRR_GSR                                (1 << 0)
-
-#define KS_WFCR                                0x2A
-#define WFCR_MPRXE                     (1 << 7)
-#define WFCR_WF3E                      (1 << 3)
-#define WFCR_WF2E                      (1 << 2)
-#define WFCR_WF1E                      (1 << 1)
-#define WFCR_WF0E                      (1 << 0)
-
-#define KS_WF0CRC0                     0x30
-#define KS_WF0CRC1                     0x32
-#define KS_WF0BM0                      0x34
-#define KS_WF0BM1                      0x36
-#define KS_WF0BM2                      0x38
-#define KS_WF0BM3                      0x3A
-
-#define KS_WF1CRC0                     0x40
-#define KS_WF1CRC1                     0x42
-#define KS_WF1BM0                      0x44
-#define KS_WF1BM1                      0x46
-#define KS_WF1BM2                      0x48
-#define KS_WF1BM3                      0x4A
-
-#define KS_WF2CRC0                     0x50
-#define KS_WF2CRC1                     0x52
-#define KS_WF2BM0                      0x54
-#define KS_WF2BM1                      0x56
-#define KS_WF2BM2                      0x58
-#define KS_WF2BM3                      0x5A
-
-#define KS_WF3CRC0                     0x60
-#define KS_WF3CRC1                     0x62
-#define KS_WF3BM0                      0x64
-#define KS_WF3BM1                      0x66
-#define KS_WF3BM2                      0x68
-#define KS_WF3BM3                      0x6A
-
-#define KS_TXCR                                0x70
-#define TXCR_TCGICMP                   (1 << 8)
-#define TXCR_TCGUDP                    (1 << 7)
-#define TXCR_TCGTCP                    (1 << 6)
-#define TXCR_TCGIP                     (1 << 5)
-#define TXCR_FTXQ                      (1 << 4)
-#define TXCR_TXFCE                     (1 << 3)
-#define TXCR_TXPE                      (1 << 2)
-#define TXCR_TXCRC                     (1 << 1)
-#define TXCR_TXE                       (1 << 0)
-
-#define KS_TXSR                                0x72
-#define TXSR_TXLC                      (1 << 13)
-#define TXSR_TXMC                      (1 << 12)
-#define TXSR_TXFID_MASK                        (0x3f << 0)
-#define TXSR_TXFID_SHIFT               (0)
-#define TXSR_TXFID_GET(_v)             (((_v) >> 0) & 0x3f)
-
-
-#define KS_RXCR1                       0x74
-#define RXCR1_FRXQ                     (1 << 15)
-#define RXCR1_RXUDPFCC                 (1 << 14)
-#define RXCR1_RXTCPFCC                 (1 << 13)
-#define RXCR1_RXIPFCC                  (1 << 12)
-#define RXCR1_RXPAFMA                  (1 << 11)
-#define RXCR1_RXFCE                    (1 << 10)
-#define RXCR1_RXEFE                    (1 << 9)
-#define RXCR1_RXMAFMA                  (1 << 8)
-#define RXCR1_RXBE                     (1 << 7)
-#define RXCR1_RXME                     (1 << 6)
-#define RXCR1_RXUE                     (1 << 5)
-#define RXCR1_RXAE                     (1 << 4)
-#define RXCR1_RXINVF                   (1 << 1)
-#define RXCR1_RXE                      (1 << 0)
-#define RXCR1_FILTER_MASK              (RXCR1_RXINVF | RXCR1_RXAE | \
-                                        RXCR1_RXMAFMA | RXCR1_RXPAFMA)
-
-#define KS_RXCR2                       0x76
-#define RXCR2_SRDBL_MASK               (0x7 << 5)
-#define RXCR2_SRDBL_SHIFT              (5)
-#define RXCR2_SRDBL_4B                 (0x0 << 5)
-#define RXCR2_SRDBL_8B                 (0x1 << 5)
-#define RXCR2_SRDBL_16B                        (0x2 << 5)
-#define RXCR2_SRDBL_32B                        (0x3 << 5)
-/* #define RXCR2_SRDBL_FRAME           (0x4 << 5) */
-#define RXCR2_IUFFP                    (1 << 4)
-#define RXCR2_RXIUFCEZ                 (1 << 3)
-#define RXCR2_UDPLFE                   (1 << 2)
-#define RXCR2_RXICMPFCC                        (1 << 1)
-#define RXCR2_RXSAF                    (1 << 0)
-
-#define KS_TXMIR                       0x78
-
-#define KS_RXFHSR                      0x7C
-#define RXFSHR_RXFV                    (1 << 15)
-#define RXFSHR_RXICMPFCS               (1 << 13)
-#define RXFSHR_RXIPFCS                 (1 << 12)
-#define RXFSHR_RXTCPFCS                        (1 << 11)
-#define RXFSHR_RXUDPFCS                        (1 << 10)
-#define RXFSHR_RXBF                    (1 << 7)
-#define RXFSHR_RXMF                    (1 << 6)
-#define RXFSHR_RXUF                    (1 << 5)
-#define RXFSHR_RXMR                    (1 << 4)
-#define RXFSHR_RXFT                    (1 << 3)
-#define RXFSHR_RXFTL                   (1 << 2)
-#define RXFSHR_RXRF                    (1 << 1)
-#define RXFSHR_RXCE                    (1 << 0)
-#define        RXFSHR_ERR                      (RXFSHR_RXCE | RXFSHR_RXRF |\
-                                       RXFSHR_RXFTL | RXFSHR_RXMR |\
-                                       RXFSHR_RXICMPFCS | RXFSHR_RXIPFCS |\
-                                       RXFSHR_RXTCPFCS)
-#define KS_RXFHBCR                     0x7E
-#define RXFHBCR_CNT_MASK               0x0FFF
-
-#define KS_TXQCR                       0x80
-#define TXQCR_AETFE                    (1 << 2)
-#define TXQCR_TXQMAM                   (1 << 1)
-#define TXQCR_METFE                    (1 << 0)
-
-#define KS_RXQCR                       0x82
-#define RXQCR_RXDTTS                   (1 << 12)
-#define RXQCR_RXDBCTS                  (1 << 11)
-#define RXQCR_RXFCTS                   (1 << 10)
-#define RXQCR_RXIPHTOE                 (1 << 9)
-#define RXQCR_RXDTTE                   (1 << 7)
-#define RXQCR_RXDBCTE                  (1 << 6)
-#define RXQCR_RXFCTE                   (1 << 5)
-#define RXQCR_ADRFE                    (1 << 4)
-#define RXQCR_SDA                      (1 << 3)
-#define RXQCR_RRXEF                    (1 << 0)
-#define RXQCR_CMD_CNTL                 (RXQCR_RXFCTE|RXQCR_ADRFE)
-
-#define KS_TXFDPR                      0x84
-#define TXFDPR_TXFPAI                  (1 << 14)
-#define TXFDPR_TXFP_MASK               (0x7ff << 0)
-#define TXFDPR_TXFP_SHIFT              (0)
-
-#define KS_RXFDPR                      0x86
-#define RXFDPR_RXFPAI                  (1 << 14)
-
-#define KS_RXDTTR                      0x8C
-#define KS_RXDBCTR                     0x8E
-
-#define KS_IER                         0x90
-#define KS_ISR                         0x92
-#define IRQ_LCI                                (1 << 15)
-#define IRQ_TXI                                (1 << 14)
-#define IRQ_RXI                                (1 << 13)
-#define IRQ_RXOI                       (1 << 11)
-#define IRQ_TXPSI                      (1 << 9)
-#define IRQ_RXPSI                      (1 << 8)
-#define IRQ_TXSAI                      (1 << 6)
-#define IRQ_RXWFDI                     (1 << 5)
-#define IRQ_RXMPDI                     (1 << 4)
-#define IRQ_LDI                                (1 << 3)
-#define IRQ_EDI                                (1 << 2)
-#define IRQ_SPIBEI                     (1 << 1)
-#define IRQ_DEDI                       (1 << 0)
-
-#define KS_RXFCTR                      0x9C
-#define RXFCTR_THRESHOLD_MASK          0x00FF
-
-#define KS_RXFC                                0x9D
-#define RXFCTR_RXFC_MASK               (0xff << 8)
-#define RXFCTR_RXFC_SHIFT              (8)
-#define RXFCTR_RXFC_GET(_v)            (((_v) >> 8) & 0xff)
-#define RXFCTR_RXFCT_MASK              (0xff << 0)
-#define RXFCTR_RXFCT_SHIFT             (0)
-
-#define KS_TXNTFSR                     0x9E
-
-#define KS_MAHTR0                      0xA0
-#define KS_MAHTR1                      0xA2
-#define KS_MAHTR2                      0xA4
-#define KS_MAHTR3                      0xA6
-
-#define KS_FCLWR                       0xB0
-#define KS_FCHWR                       0xB2
-#define KS_FCOWR                       0xB4
-
-#define KS_CIDER                       0xC0
-#define CIDER_ID                       0x8870
-#define CIDER_REV_MASK                 (0x7 << 1)
-#define CIDER_REV_SHIFT                        (1)
-#define CIDER_REV_GET(_v)              (((_v) >> 1) & 0x7)
-
-#define KS_CGCR                                0xC6
-#define KS_IACR                                0xC8
-#define IACR_RDEN                      (1 << 12)
-#define IACR_TSEL_MASK                 (0x3 << 10)
-#define IACR_TSEL_SHIFT                        (10)
-#define IACR_TSEL_MIB                  (0x3 << 10)
-#define IACR_ADDR_MASK                 (0x1f << 0)
-#define IACR_ADDR_SHIFT                        (0)
-
-#define KS_IADLR                       0xD0
-#define KS_IAHDR                       0xD2
-
-#define KS_PMECR                       0xD4
-#define PMECR_PME_DELAY                        (1 << 14)
-#define PMECR_PME_POL                  (1 << 12)
-#define PMECR_WOL_WAKEUP               (1 << 11)
-#define PMECR_WOL_MAGICPKT             (1 << 10)
-#define PMECR_WOL_LINKUP               (1 << 9)
-#define PMECR_WOL_ENERGY               (1 << 8)
-#define PMECR_AUTO_WAKE_EN             (1 << 7)
-#define PMECR_WAKEUP_NORMAL            (1 << 6)
-#define PMECR_WKEVT_MASK               (0xf << 2)
-#define PMECR_WKEVT_SHIFT              (2)
-#define PMECR_WKEVT_GET(_v)            (((_v) >> 2) & 0xf)
-#define PMECR_WKEVT_ENERGY             (0x1 << 2)
-#define PMECR_WKEVT_LINK               (0x2 << 2)
-#define PMECR_WKEVT_MAGICPKT           (0x4 << 2)
-#define PMECR_WKEVT_FRAME              (0x8 << 2)
-#define PMECR_PM_MASK                  (0x3 << 0)
-#define PMECR_PM_SHIFT                 (0)
-#define PMECR_PM_NORMAL                        (0x0 << 0)
-#define PMECR_PM_ENERGY                        (0x1 << 0)
-#define PMECR_PM_SOFTDOWN              (0x2 << 0)
-#define PMECR_PM_POWERSAVE             (0x3 << 0)
-
-/* Standard MII PHY data */
-#define KS_P1MBCR                      0xE4
-#define P1MBCR_FORCE_FDX               (1 << 8)
-
-#define KS_P1MBSR                      0xE6
-#define P1MBSR_AN_COMPLETE             (1 << 5)
-#define P1MBSR_AN_CAPABLE              (1 << 3)
-#define P1MBSR_LINK_UP                 (1 << 2)
-
-#define KS_PHY1ILR                     0xE8
-#define KS_PHY1IHR                     0xEA
-#define KS_P1ANAR                      0xEC
-#define KS_P1ANLPR                     0xEE
-
-#define KS_P1SCLMD                     0xF4
-#define P1SCLMD_LEDOFF                 (1 << 15)
-#define P1SCLMD_TXIDS                  (1 << 14)
-#define P1SCLMD_RESTARTAN              (1 << 13)
-#define P1SCLMD_DISAUTOMDIX            (1 << 10)
-#define P1SCLMD_FORCEMDIX              (1 << 9)
-#define P1SCLMD_AUTONEGEN              (1 << 7)
-#define P1SCLMD_FORCE100               (1 << 6)
-#define P1SCLMD_FORCEFDX               (1 << 5)
-#define P1SCLMD_ADV_FLOW               (1 << 4)
-#define P1SCLMD_ADV_100BT_FDX          (1 << 3)
-#define P1SCLMD_ADV_100BT_HDX          (1 << 2)
-#define P1SCLMD_ADV_10BT_FDX           (1 << 1)
-#define P1SCLMD_ADV_10BT_HDX           (1 << 0)
-
-#define KS_P1CR                                0xF6
-#define P1CR_HP_MDIX                   (1 << 15)
-#define P1CR_REV_POL                   (1 << 13)
-#define P1CR_OP_100M                   (1 << 10)
-#define P1CR_OP_FDX                    (1 << 9)
-#define P1CR_OP_MDI                    (1 << 7)
-#define P1CR_AN_DONE                   (1 << 6)
-#define P1CR_LINK_GOOD                 (1 << 5)
-#define P1CR_PNTR_FLOW                 (1 << 4)
-#define P1CR_PNTR_100BT_FDX            (1 << 3)
-#define P1CR_PNTR_100BT_HDX            (1 << 2)
-#define P1CR_PNTR_10BT_FDX             (1 << 1)
-#define P1CR_PNTR_10BT_HDX             (1 << 0)
-
-/* TX Frame control */
-
-#define TXFR_TXIC                      (1 << 15)
-#define TXFR_TXFID_MASK                        (0x3f << 0)
-#define TXFR_TXFID_SHIFT               (0)
-
-#define KS_P1SR                                0xF8
-#define P1SR_HP_MDIX                   (1 << 15)
-#define P1SR_REV_POL                   (1 << 13)
-#define P1SR_OP_100M                   (1 << 10)
-#define P1SR_OP_FDX                    (1 << 9)
-#define P1SR_OP_MDI                    (1 << 7)
-#define P1SR_AN_DONE                   (1 << 6)
-#define P1SR_LINK_GOOD                 (1 << 5)
-#define P1SR_PNTR_FLOW                 (1 << 4)
-#define P1SR_PNTR_100BT_FDX            (1 << 3)
-#define P1SR_PNTR_100BT_HDX            (1 << 2)
-#define P1SR_PNTR_10BT_FDX             (1 << 1)
-#define P1SR_PNTR_10BT_HDX             (1 << 0)
-
-#define        ENUM_BUS_NONE                   0
-#define        ENUM_BUS_8BIT                   1
-#define        ENUM_BUS_16BIT                  2
-#define        ENUM_BUS_32BIT                  3
-
-#define MAX_MCAST_LST                  32
-#define HW_MCAST_SIZE                  8
-
-/**
- * union ks_tx_hdr - tx header data
- * @txb: The header as bytes
- * @txw: The header as 16bit, little-endian words
- *
- * A dual representation of the tx header data to allow
- * access to individual bytes, and to allow 16bit accesses
- * with 16bit alignment.
- */
-union ks_tx_hdr {
-       u8      txb[4];
-       __le16  txw[2];
-};
-
-/**
- * struct ks_net - KS8851 driver private data
- * @net_device         : The network device we're bound to
- * @hw_addr    : start address of data register.
- * @hw_addr_cmd        : start address of command register.
- * @txh        : temporaly buffer to save status/length.
- * @lock       : Lock to ensure that the device is not accessed when busy.
- * @pdev       : Pointer to platform device.
- * @mii                : The MII state information for the mii calls.
- * @frame_head_info    : frame header information for multi-pkt rx.
- * @statelock  : Lock on this structure for tx list.
- * @msg_enable : The message flags controlling driver output (see ethtool).
- * @frame_cnt          : number of frames received.
- * @bus_width          : i/o bus width.
- * @irq        : irq number assigned to this device.
- * @rc_rxqcr   : Cached copy of KS_RXQCR.
- * @rc_txcr    : Cached copy of KS_TXCR.
- * @rc_ier     : Cached copy of KS_IER.
- * @sharedbus          : Multipex(addr and data bus) mode indicator.
- * @cmd_reg_cache      : command register cached.
- * @cmd_reg_cache_int  : command register cached. Used in the irq handler.
- * @promiscuous        : promiscuous mode indicator.
- * @all_mcast          : mutlicast indicator.
- * @mcast_lst_size     : size of multicast list.
- * @mcast_lst          : multicast list.
- * @mcast_bits         : multicast enabed.
- * @mac_addr                   : MAC address assigned to this device.
- * @fid                : frame id.
- * @extra_byte         : number of extra byte prepended rx pkt.
- * @enabled                    : indicator this device works.
- *
- * The @lock ensures that the chip is protected when certain operations are
- * in progress. When the read or write packet transfer is in progress, most
- * of the chip registers are not accessible until the transfer is finished and
- * the DMA has been de-asserted.
- *
- * The @statelock is used to protect information in the structure which may
- * need to be accessed via several sources, such as the network driver layer
- * or one of the work queues.
- *
- */
-
-/* Receive multiplex framer header info */
-struct type_frame_head {
-       u16     sts;         /* Frame status */
-       u16     len;         /* Byte count */
-};
-
-struct ks_net {
-       struct net_device       *netdev;
-       void __iomem            *hw_addr;
-       void __iomem            *hw_addr_cmd;
-       union ks_tx_hdr         txh ____cacheline_aligned;
-       struct mutex            lock; /* spinlock to be interrupt safe */
-       struct platform_device *pdev;
-       struct mii_if_info      mii;
-       struct type_frame_head  *frame_head_info;
-       spinlock_t              statelock;
-       u32                     msg_enable;
-       u32                     frame_cnt;
-       int                     bus_width;
-       int                     irq;
-
-       u16                     rc_rxqcr;
-       u16                     rc_txcr;
-       u16                     rc_ier;
-       u16                     sharedbus;
-       u16                     cmd_reg_cache;
-       u16                     cmd_reg_cache_int;
-       u16                     promiscuous;
-       u16                     all_mcast;
-       u16                     mcast_lst_size;
-       u8                      mcast_lst[MAX_MCAST_LST][ETH_ALEN];
-       u8                      mcast_bits[HW_MCAST_SIZE];
-       u8                      mac_addr[6];
-       u8                      fid;
-       u8                      extra_byte;
-       u8                      enabled;
-};
-
-static int msg_enable;
-
-#define BE3             0x8000      /* Byte Enable 3 */
-#define BE2             0x4000      /* Byte Enable 2 */
-#define BE1             0x2000      /* Byte Enable 1 */
-#define BE0             0x1000      /* Byte Enable 0 */
-
-/**
- * register read/write calls.
- *
- * All these calls issue transactions to access the chip's registers. They
- * all require that the necessary lock is held to prevent accesses when the
- * chip is busy transferring packet data (RX/TX FIFO accesses).
- */
-
-/**
- * ks_rdreg8 - read 8 bit register from device
- * @ks   : The chip information
- * @offset: The register address
- *
- * Read a 8bit register from the chip, returning the result
- */
-static u8 ks_rdreg8(struct ks_net *ks, int offset)
-{
-       u16 data;
-       u8 shift_bit = offset & 0x03;
-       u8 shift_data = (offset & 1) << 3;
-       ks->cmd_reg_cache = (u16) offset | (u16)(BE0 << shift_bit);
-       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
-       data  = ioread16(ks->hw_addr);
-       return (u8)(data >> shift_data);
-}
-
-/**
- * ks_rdreg16 - read 16 bit register from device
- * @ks   : The chip information
- * @offset: The register address
- *
- * Read a 16bit register from the chip, returning the result
- */
-
-static u16 ks_rdreg16(struct ks_net *ks, int offset)
-{
-       ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
-       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
-       return ioread16(ks->hw_addr);
-}
-
-/**
- * ks_wrreg8 - write 8bit register value to chip
- * @ks: The chip information
- * @offset: The register address
- * @value: The value to write
- *
- */
-static void ks_wrreg8(struct ks_net *ks, int offset, u8 value)
-{
-       u8  shift_bit = (offset & 0x03);
-       u16 value_write = (u16)(value << ((offset & 1) << 3));
-       ks->cmd_reg_cache = (u16)offset | (BE0 << shift_bit);
-       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
-       iowrite16(value_write, ks->hw_addr);
-}
-
-/**
- * ks_wrreg16 - write 16bit register value to chip
- * @ks: The chip information
- * @offset: The register address
- * @value: The value to write
- *
- */
-
-static void ks_wrreg16(struct ks_net *ks, int offset, u16 value)
-{
-       ks->cmd_reg_cache = (u16)offset | ((BE1 | BE0) << (offset & 0x02));
-       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
-       iowrite16(value, ks->hw_addr);
-}
-
-/**
- * ks_inblk - read a block of data from QMU. This is called after sudo DMA mode enabled.
- * @ks: The chip state
- * @wptr: buffer address to save data
- * @len: length in byte to read
- *
- */
-static inline void ks_inblk(struct ks_net *ks, u16 *wptr, u32 len)
-{
-       len >>= 1;
-       while (len--)
-               *wptr++ = (u16)ioread16(ks->hw_addr);
-}
-
-/**
- * ks_outblk - write data to QMU. This is called after sudo DMA mode enabled.
- * @ks: The chip information
- * @wptr: buffer address
- * @len: length in byte to write
- *
- */
-static inline void ks_outblk(struct ks_net *ks, u16 *wptr, u32 len)
-{
-       len >>= 1;
-       while (len--)
-               iowrite16(*wptr++, ks->hw_addr);
-}
-
-static void ks_disable_int(struct ks_net *ks)
-{
-       ks_wrreg16(ks, KS_IER, 0x0000);
-}  /* ks_disable_int */
-
-static void ks_enable_int(struct ks_net *ks)
-{
-       ks_wrreg16(ks, KS_IER, ks->rc_ier);
-}  /* ks_enable_int */
-
-/**
- * ks_tx_fifo_space - return the available hardware buffer size.
- * @ks: The chip information
- *
- */
-static inline u16 ks_tx_fifo_space(struct ks_net *ks)
-{
-       return ks_rdreg16(ks, KS_TXMIR) & 0x1fff;
-}
-
-/**
- * ks_save_cmd_reg - save the command register from the cache.
- * @ks: The chip information
- *
- */
-static inline void ks_save_cmd_reg(struct ks_net *ks)
-{
-       /*ks8851 MLL has a bug to read back the command register.
-       * So rely on software to save the content of command register.
-       */
-       ks->cmd_reg_cache_int = ks->cmd_reg_cache;
-}
-
-/**
- * ks_restore_cmd_reg - restore the command register from the cache and
- *     write to hardware register.
- * @ks: The chip information
- *
- */
-static inline void ks_restore_cmd_reg(struct ks_net *ks)
-{
-       ks->cmd_reg_cache = ks->cmd_reg_cache_int;
-       iowrite16(ks->cmd_reg_cache, ks->hw_addr_cmd);
-}
-
-/**
- * ks_set_powermode - set power mode of the device
- * @ks: The chip information
- * @pwrmode: The power mode value to write to KS_PMECR.
- *
- * Change the power mode of the chip.
- */
-static void ks_set_powermode(struct ks_net *ks, unsigned pwrmode)
-{
-       unsigned pmecr;
-
-       netif_dbg(ks, hw, ks->netdev, "setting power mode %d\n", pwrmode);
-
-       ks_rdreg16(ks, KS_GRR);
-       pmecr = ks_rdreg16(ks, KS_PMECR);
-       pmecr &= ~PMECR_PM_MASK;
-       pmecr |= pwrmode;
-
-       ks_wrreg16(ks, KS_PMECR, pmecr);
-}
-
-/**
- * ks_read_config - read chip configuration of bus width.
- * @ks: The chip information
- *
- */
-static void ks_read_config(struct ks_net *ks)
-{
-       u16 reg_data = 0;
-
-       /* Regardless of bus width, 8 bit read should always work.*/
-       reg_data = ks_rdreg8(ks, KS_CCR) & 0x00FF;
-       reg_data |= ks_rdreg8(ks, KS_CCR+1) << 8;
-
-       /* addr/data bus are multiplexed */
-       ks->sharedbus = (reg_data & CCR_SHARED) == CCR_SHARED;
-
-       /* There are garbage data when reading data from QMU,
-       depending on bus-width.
-       */
-
-       if (reg_data & CCR_8BIT) {
-               ks->bus_width = ENUM_BUS_8BIT;
-               ks->extra_byte = 1;
-       } else if (reg_data & CCR_16BIT) {
-               ks->bus_width = ENUM_BUS_16BIT;
-               ks->extra_byte = 2;
-       } else {
-               ks->bus_width = ENUM_BUS_32BIT;
-               ks->extra_byte = 4;
-       }
-}
-
-/**
- * ks_soft_reset - issue one of the soft reset to the device
- * @ks: The device state.
- * @op: The bit(s) to set in the GRR
- *
- * Issue the relevant soft-reset command to the device's GRR register
- * specified by @op.
- *
- * Note, the delays are in there as a caution to ensure that the reset
- * has time to take effect and then complete. Since the datasheet does
- * not currently specify the exact sequence, we have chosen something
- * that seems to work with our device.
- */
-static void ks_soft_reset(struct ks_net *ks, unsigned op)
-{
-       /* Disable interrupt first */
-       ks_wrreg16(ks, KS_IER, 0x0000);
-       ks_wrreg16(ks, KS_GRR, op);
-       mdelay(10);     /* wait a short time to effect reset */
-       ks_wrreg16(ks, KS_GRR, 0);
-       mdelay(1);      /* wait for condition to clear */
-}
-
-
-void ks_enable_qmu(struct ks_net *ks)
-{
-       u16 w;
-
-       w = ks_rdreg16(ks, KS_TXCR);
-       /* Enables QMU Transmit (TXCR). */
-       ks_wrreg16(ks, KS_TXCR, w | TXCR_TXE);
-
-       /*
-        * RX Frame Count Threshold Enable and Auto-Dequeue RXQ Frame
-        * Enable
-        */
-
-       w = ks_rdreg16(ks, KS_RXQCR);
-       ks_wrreg16(ks, KS_RXQCR, w | RXQCR_RXFCTE);
-
-       /* Enables QMU Receive (RXCR1). */
-       w = ks_rdreg16(ks, KS_RXCR1);
-       ks_wrreg16(ks, KS_RXCR1, w | RXCR1_RXE);
-       ks->enabled = true;
-}  /* ks_enable_qmu */
-
-static void ks_disable_qmu(struct ks_net *ks)
-{
-       u16     w;
-
-       w = ks_rdreg16(ks, KS_TXCR);
-
-       /* Disables QMU Transmit (TXCR). */
-       w  &= ~TXCR_TXE;
-       ks_wrreg16(ks, KS_TXCR, w);
-
-       /* Disables QMU Receive (RXCR1). */
-       w = ks_rdreg16(ks, KS_RXCR1);
-       w &= ~RXCR1_RXE ;
-       ks_wrreg16(ks, KS_RXCR1, w);
-
-       ks->enabled = false;
-
-}  /* ks_disable_qmu */
-
-/**
- * ks_read_qmu - read 1 pkt data from the QMU.
- * @ks: The chip information
- * @buf: buffer address to save 1 pkt
- * @len: Pkt length
- * Here is the sequence to read 1 pkt:
- *     1. set sudo DMA mode
- *     2. read prepend data
- *     3. read pkt data
- *     4. reset sudo DMA Mode
- */
-static inline void ks_read_qmu(struct ks_net *ks, u16 *buf, u32 len)
-{
-       u32 r =  ks->extra_byte & 0x1 ;
-       u32 w = ks->extra_byte - r;
-
-       /* 1. set sudo DMA mode */
-       ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
-       ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
-
-       /* 2. read prepend data */
-       /**
-        * read 4 + extra bytes and discard them.
-        * extra bytes for dummy, 2 for status, 2 for len
-        */
-
-       /* use likely(r) for 8 bit access for performance */
-       if (unlikely(r))
-               ioread8(ks->hw_addr);
-       ks_inblk(ks, buf, w + 2 + 2);
-
-       /* 3. read pkt data */
-       ks_inblk(ks, buf, ALIGN(len, 4));
-
-       /* 4. reset sudo DMA Mode */
-       ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr);
-}
-
-/**
- * ks_rcv - read multiple pkts data from the QMU.
- * @ks: The chip information
- * @netdev: The network device being opened.
- *
- * Read all of header information before reading pkt content.
- * It is not allowed only port of pkts in QMU after issuing
- * interrupt ack.
- */
-static void ks_rcv(struct ks_net *ks, struct net_device *netdev)
-{
-       u32     i;
-       struct type_frame_head *frame_hdr = ks->frame_head_info;
-       struct sk_buff *skb;
-
-       ks->frame_cnt = ks_rdreg16(ks, KS_RXFCTR) >> 8;
-
-       /* read all header information */
-       for (i = 0; i < ks->frame_cnt; i++) {
-               /* Checking Received packet status */
-               frame_hdr->sts = ks_rdreg16(ks, KS_RXFHSR);
-               /* Get packet len from hardware */
-               frame_hdr->len = ks_rdreg16(ks, KS_RXFHBCR);
-               frame_hdr++;
-       }
-
-       frame_hdr = ks->frame_head_info;
-       while (ks->frame_cnt--) {
-               skb = dev_alloc_skb(frame_hdr->len + 16);
-               if (likely(skb && (frame_hdr->sts & RXFSHR_RXFV) &&
-                       (frame_hdr->len < RX_BUF_SIZE) && frame_hdr->len)) {
-                       skb_reserve(skb, 2);
-                       /* read data block including CRC 4 bytes */
-                       ks_read_qmu(ks, (u16 *)skb->data, frame_hdr->len);
-                       skb_put(skb, frame_hdr->len);
-                       skb->protocol = eth_type_trans(skb, netdev);
-                       netif_rx(skb);
-               } else {
-                       pr_err("%s: err:skb alloc\n", __func__);
-                       ks_wrreg16(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_RRXEF));
-                       if (skb)
-                               dev_kfree_skb_irq(skb);
-               }
-               frame_hdr++;
-       }
-}
-
-/**
- * ks_update_link_status - link status update.
- * @netdev: The network device being opened.
- * @ks: The chip information
- *
- */
-
-static void ks_update_link_status(struct net_device *netdev, struct ks_net *ks)
-{
-       /* check the status of the link */
-       u32 link_up_status;
-       if (ks_rdreg16(ks, KS_P1SR) & P1SR_LINK_GOOD) {
-               netif_carrier_on(netdev);
-               link_up_status = true;
-       } else {
-               netif_carrier_off(netdev);
-               link_up_status = false;
-       }
-       netif_dbg(ks, link, ks->netdev,
-                 "%s: %s\n", __func__, link_up_status ? "UP" : "DOWN");
-}
-
-/**
- * ks_irq - device interrupt handler
- * @irq: Interrupt number passed from the IRQ hnalder.
- * @pw: The private word passed to register_irq(), our struct ks_net.
- *
- * This is the handler invoked to find out what happened
- *
- * Read the interrupt status, work out what needs to be done and then clear
- * any of the interrupts that are not needed.
- */
-
-static irqreturn_t ks_irq(int irq, void *pw)
-{
-       struct net_device *netdev = pw;
-       struct ks_net *ks = netdev_priv(netdev);
-       u16 status;
-
-       /*this should be the first in IRQ handler */
-       ks_save_cmd_reg(ks);
-
-       status = ks_rdreg16(ks, KS_ISR);
-       if (unlikely(!status)) {
-               ks_restore_cmd_reg(ks);
-               return IRQ_NONE;
-       }
-
-       ks_wrreg16(ks, KS_ISR, status);
-
-       if (likely(status & IRQ_RXI))
-               ks_rcv(ks, netdev);
-
-       if (unlikely(status & IRQ_LCI))
-               ks_update_link_status(netdev, ks);
-
-       if (unlikely(status & IRQ_TXI))
-               netif_wake_queue(netdev);
-
-       if (unlikely(status & IRQ_LDI)) {
-
-               u16 pmecr = ks_rdreg16(ks, KS_PMECR);
-               pmecr &= ~PMECR_WKEVT_MASK;
-               ks_wrreg16(ks, KS_PMECR, pmecr | PMECR_WKEVT_LINK);
-       }
-
-       /* this should be the last in IRQ handler*/
-       ks_restore_cmd_reg(ks);
-       return IRQ_HANDLED;
-}
-
-
-/**
- * ks_net_open - open network device
- * @netdev: The network device being opened.
- *
- * Called when the network device is marked active, such as a user executing
- * 'ifconfig up' on the device.
- */
-static int ks_net_open(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       int err;
-
-#define        KS_INT_FLAGS    (IRQF_DISABLED|IRQF_TRIGGER_LOW)
-       /* lock the card, even if we may not actually do anything
-        * else at the moment.
-        */
-
-       netif_dbg(ks, ifup, ks->netdev, "%s - entry\n", __func__);
-
-       /* reset the HW */
-       err = request_irq(ks->irq, ks_irq, KS_INT_FLAGS, DRV_NAME, netdev);
-
-       if (err) {
-               pr_err("Failed to request IRQ: %d: %d\n", ks->irq, err);
-               return err;
-       }
-
-       /* wake up powermode to normal mode */
-       ks_set_powermode(ks, PMECR_PM_NORMAL);
-       mdelay(1);      /* wait for normal mode to take effect */
-
-       ks_wrreg16(ks, KS_ISR, 0xffff);
-       ks_enable_int(ks);
-       ks_enable_qmu(ks);
-       netif_start_queue(ks->netdev);
-
-       netif_dbg(ks, ifup, ks->netdev, "network device up\n");
-
-       return 0;
-}
-
-/**
- * ks_net_stop - close network device
- * @netdev: The device being closed.
- *
- * Called to close down a network device which has been active. Cancell any
- * work, shutdown the RX and TX process and then place the chip into a low
- * power state whilst it is not being used.
- */
-static int ks_net_stop(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-
-       netif_info(ks, ifdown, netdev, "shutting down\n");
-
-       netif_stop_queue(netdev);
-
-       mutex_lock(&ks->lock);
-
-       /* turn off the IRQs and ack any outstanding */
-       ks_wrreg16(ks, KS_IER, 0x0000);
-       ks_wrreg16(ks, KS_ISR, 0xffff);
-
-       /* shutdown RX/TX QMU */
-       ks_disable_qmu(ks);
-
-       /* set powermode to soft power down to save power */
-       ks_set_powermode(ks, PMECR_PM_SOFTDOWN);
-       free_irq(ks->irq, netdev);
-       mutex_unlock(&ks->lock);
-       return 0;
-}
-
-
-/**
- * ks_write_qmu - write 1 pkt data to the QMU.
- * @ks: The chip information
- * @pdata: buffer address to save 1 pkt
- * @len: Pkt length in byte
- * Here is the sequence to write 1 pkt:
- *     1. set sudo DMA mode
- *     2. write status/length
- *     3. write pkt data
- *     4. reset sudo DMA Mode
- *     5. reset sudo DMA mode
- *     6. Wait until pkt is out
- */
-static void ks_write_qmu(struct ks_net *ks, u8 *pdata, u16 len)
-{
-       /* start header at txb[0] to align txw entries */
-       ks->txh.txw[0] = 0;
-       ks->txh.txw[1] = cpu_to_le16(len);
-
-       /* 1. set sudo-DMA mode */
-       ks_wrreg8(ks, KS_RXQCR, (ks->rc_rxqcr | RXQCR_SDA) & 0xff);
-       /* 2. write status/lenth info */
-       ks_outblk(ks, ks->txh.txw, 4);
-       /* 3. write pkt data */
-       ks_outblk(ks, (u16 *)pdata, ALIGN(len, 4));
-       /* 4. reset sudo-DMA mode */
-       ks_wrreg8(ks, KS_RXQCR, ks->rc_rxqcr);
-       /* 5. Enqueue Tx(move the pkt from TX buffer into TXQ) */
-       ks_wrreg16(ks, KS_TXQCR, TXQCR_METFE);
-       /* 6. wait until TXQCR_METFE is auto-cleared */
-       while (ks_rdreg16(ks, KS_TXQCR) & TXQCR_METFE)
-               ;
-}
-
-/**
- * ks_start_xmit - transmit packet
- * @skb                : The buffer to transmit
- * @netdev     : The device used to transmit the packet.
- *
- * Called by the network layer to transmit the @skb.
- * spin_lock_irqsave is required because tx and rx should be mutual exclusive.
- * So while tx is in-progress, prevent IRQ interrupt from happenning.
- */
-static int ks_start_xmit(struct sk_buff *skb, struct net_device *netdev)
-{
-       int retv = NETDEV_TX_OK;
-       struct ks_net *ks = netdev_priv(netdev);
-
-       disable_irq(netdev->irq);
-       ks_disable_int(ks);
-       spin_lock(&ks->statelock);
-
-       /* Extra space are required:
-       *  4 byte for alignment, 4 for status/length, 4 for CRC
-       */
-
-       if (likely(ks_tx_fifo_space(ks) >= skb->len + 12)) {
-               ks_write_qmu(ks, skb->data, skb->len);
-               dev_kfree_skb(skb);
-       } else
-               retv = NETDEV_TX_BUSY;
-       spin_unlock(&ks->statelock);
-       ks_enable_int(ks);
-       enable_irq(netdev->irq);
-       return retv;
-}
-
-/**
- * ks_start_rx - ready to serve pkts
- * @ks         : The chip information
- *
- */
-static void ks_start_rx(struct ks_net *ks)
-{
-       u16 cntl;
-
-       /* Enables QMU Receive (RXCR1). */
-       cntl = ks_rdreg16(ks, KS_RXCR1);
-       cntl |= RXCR1_RXE ;
-       ks_wrreg16(ks, KS_RXCR1, cntl);
-}  /* ks_start_rx */
-
-/**
- * ks_stop_rx - stop to serve pkts
- * @ks         : The chip information
- *
- */
-static void ks_stop_rx(struct ks_net *ks)
-{
-       u16 cntl;
-
-       /* Disables QMU Receive (RXCR1). */
-       cntl = ks_rdreg16(ks, KS_RXCR1);
-       cntl &= ~RXCR1_RXE ;
-       ks_wrreg16(ks, KS_RXCR1, cntl);
-
-}  /* ks_stop_rx */
-
-static unsigned long const ethernet_polynomial = 0x04c11db7U;
-
-static unsigned long ether_gen_crc(int length, u8 *data)
-{
-       long crc = -1;
-       while (--length >= 0) {
-               u8 current_octet = *data++;
-               int bit;
-
-               for (bit = 0; bit < 8; bit++, current_octet >>= 1) {
-                       crc = (crc << 1) ^
-                               ((crc < 0) ^ (current_octet & 1) ?
-                       ethernet_polynomial : 0);
-               }
-       }
-       return (unsigned long)crc;
-}  /* ether_gen_crc */
-
-/**
-* ks_set_grpaddr - set multicast information
-* @ks : The chip information
-*/
-
-static void ks_set_grpaddr(struct ks_net *ks)
-{
-       u8      i;
-       u32     index, position, value;
-
-       memset(ks->mcast_bits, 0, sizeof(u8) * HW_MCAST_SIZE);
-
-       for (i = 0; i < ks->mcast_lst_size; i++) {
-               position = (ether_gen_crc(6, ks->mcast_lst[i]) >> 26) & 0x3f;
-               index = position >> 3;
-               value = 1 << (position & 7);
-               ks->mcast_bits[index] |= (u8)value;
-       }
-
-       for (i  = 0; i < HW_MCAST_SIZE; i++) {
-               if (i & 1) {
-                       ks_wrreg16(ks, (u16)((KS_MAHTR0 + i) & ~1),
-                               (ks->mcast_bits[i] << 8) |
-                               ks->mcast_bits[i - 1]);
-               }
-       }
-}  /* ks_set_grpaddr */
-
-/*
-* ks_clear_mcast - clear multicast information
-*
-* @ks : The chip information
-* This routine removes all mcast addresses set in the hardware.
-*/
-
-static void ks_clear_mcast(struct ks_net *ks)
-{
-       u16     i, mcast_size;
-       for (i = 0; i < HW_MCAST_SIZE; i++)
-               ks->mcast_bits[i] = 0;
-
-       mcast_size = HW_MCAST_SIZE >> 2;
-       for (i = 0; i < mcast_size; i++)
-               ks_wrreg16(ks, KS_MAHTR0 + (2*i), 0);
-}
-
-static void ks_set_promis(struct ks_net *ks, u16 promiscuous_mode)
-{
-       u16             cntl;
-       ks->promiscuous = promiscuous_mode;
-       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
-       cntl = ks_rdreg16(ks, KS_RXCR1);
-
-       cntl &= ~RXCR1_FILTER_MASK;
-       if (promiscuous_mode)
-               /* Enable Promiscuous mode */
-               cntl |= RXCR1_RXAE | RXCR1_RXINVF;
-       else
-               /* Disable Promiscuous mode (default normal mode) */
-               cntl |= RXCR1_RXPAFMA;
-
-       ks_wrreg16(ks, KS_RXCR1, cntl);
-
-       if (ks->enabled)
-               ks_start_rx(ks);
-
-}  /* ks_set_promis */
-
-static void ks_set_mcast(struct ks_net *ks, u16 mcast)
-{
-       u16     cntl;
-
-       ks->all_mcast = mcast;
-       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
-       cntl = ks_rdreg16(ks, KS_RXCR1);
-       cntl &= ~RXCR1_FILTER_MASK;
-       if (mcast)
-               /* Enable "Perfect with Multicast address passed mode" */
-               cntl |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA);
-       else
-               /**
-                * Disable "Perfect with Multicast address passed
-                * mode" (normal mode).
-                */
-               cntl |= RXCR1_RXPAFMA;
-
-       ks_wrreg16(ks, KS_RXCR1, cntl);
-
-       if (ks->enabled)
-               ks_start_rx(ks);
-}  /* ks_set_mcast */
-
-static void ks_set_rx_mode(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       struct netdev_hw_addr *ha;
-
-       /* Turn on/off promiscuous mode. */
-       if ((netdev->flags & IFF_PROMISC) == IFF_PROMISC)
-               ks_set_promis(ks,
-                       (u16)((netdev->flags & IFF_PROMISC) == IFF_PROMISC));
-       /* Turn on/off all mcast mode. */
-       else if ((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI)
-               ks_set_mcast(ks,
-                       (u16)((netdev->flags & IFF_ALLMULTI) == IFF_ALLMULTI));
-       else
-               ks_set_promis(ks, false);
-
-       if ((netdev->flags & IFF_MULTICAST) && netdev_mc_count(netdev)) {
-               if (netdev_mc_count(netdev) <= MAX_MCAST_LST) {
-                       int i = 0;
-
-                       netdev_for_each_mc_addr(ha, netdev) {
-                               if (i >= MAX_MCAST_LST)
-                                       break;
-                               memcpy(ks->mcast_lst[i++], ha->addr, ETH_ALEN);
-                       }
-                       ks->mcast_lst_size = (u8)i;
-                       ks_set_grpaddr(ks);
-               } else {
-                       /**
-                        * List too big to support so
-                        * turn on all mcast mode.
-                        */
-                       ks->mcast_lst_size = MAX_MCAST_LST;
-                       ks_set_mcast(ks, true);
-               }
-       } else {
-               ks->mcast_lst_size = 0;
-               ks_clear_mcast(ks);
-       }
-} /* ks_set_rx_mode */
-
-static void ks_set_mac(struct ks_net *ks, u8 *data)
-{
-       u16 *pw = (u16 *)data;
-       u16 w, u;
-
-       ks_stop_rx(ks);  /* Stop receiving for reconfiguration */
-
-       u = *pw++;
-       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
-       ks_wrreg16(ks, KS_MARH, w);
-
-       u = *pw++;
-       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
-       ks_wrreg16(ks, KS_MARM, w);
-
-       u = *pw;
-       w = ((u & 0xFF) << 8) | ((u >> 8) & 0xFF);
-       ks_wrreg16(ks, KS_MARL, w);
-
-       memcpy(ks->mac_addr, data, 6);
-
-       if (ks->enabled)
-               ks_start_rx(ks);
-}
-
-static int ks_set_mac_address(struct net_device *netdev, void *paddr)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       struct sockaddr *addr = paddr;
-       u8 *da;
-
-       memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
-
-       da = (u8 *)netdev->dev_addr;
-
-       ks_set_mac(ks, da);
-       return 0;
-}
-
-static int ks_net_ioctl(struct net_device *netdev, struct ifreq *req, int cmd)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-
-       if (!netif_running(netdev))
-               return -EINVAL;
-
-       return generic_mii_ioctl(&ks->mii, if_mii(req), cmd, NULL);
-}
-
-static const struct net_device_ops ks_netdev_ops = {
-       .ndo_open               = ks_net_open,
-       .ndo_stop               = ks_net_stop,
-       .ndo_do_ioctl           = ks_net_ioctl,
-       .ndo_start_xmit         = ks_start_xmit,
-       .ndo_set_mac_address    = ks_set_mac_address,
-       .ndo_set_rx_mode        = ks_set_rx_mode,
-       .ndo_change_mtu         = eth_change_mtu,
-       .ndo_validate_addr      = eth_validate_addr,
-};
-
-/* ethtool support */
-
-static void ks_get_drvinfo(struct net_device *netdev,
-                              struct ethtool_drvinfo *di)
-{
-       strlcpy(di->driver, DRV_NAME, sizeof(di->driver));
-       strlcpy(di->version, "1.00", sizeof(di->version));
-       strlcpy(di->bus_info, dev_name(netdev->dev.parent),
-               sizeof(di->bus_info));
-}
-
-static u32 ks_get_msglevel(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       return ks->msg_enable;
-}
-
-static void ks_set_msglevel(struct net_device *netdev, u32 to)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       ks->msg_enable = to;
-}
-
-static int ks_get_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       return mii_ethtool_gset(&ks->mii, cmd);
-}
-
-static int ks_set_settings(struct net_device *netdev, struct ethtool_cmd *cmd)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       return mii_ethtool_sset(&ks->mii, cmd);
-}
-
-static u32 ks_get_link(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       return mii_link_ok(&ks->mii);
-}
-
-static int ks_nway_reset(struct net_device *netdev)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       return mii_nway_restart(&ks->mii);
-}
-
-static const struct ethtool_ops ks_ethtool_ops = {
-       .get_drvinfo    = ks_get_drvinfo,
-       .get_msglevel   = ks_get_msglevel,
-       .set_msglevel   = ks_set_msglevel,
-       .get_settings   = ks_get_settings,
-       .set_settings   = ks_set_settings,
-       .get_link       = ks_get_link,
-       .nway_reset     = ks_nway_reset,
-};
-
-/* MII interface controls */
-
-/**
- * ks_phy_reg - convert MII register into a KS8851 register
- * @reg: MII register number.
- *
- * Return the KS8851 register number for the corresponding MII PHY register
- * if possible. Return zero if the MII register has no direct mapping to the
- * KS8851 register set.
- */
-static int ks_phy_reg(int reg)
-{
-       switch (reg) {
-       case MII_BMCR:
-               return KS_P1MBCR;
-       case MII_BMSR:
-               return KS_P1MBSR;
-       case MII_PHYSID1:
-               return KS_PHY1ILR;
-       case MII_PHYSID2:
-               return KS_PHY1IHR;
-       case MII_ADVERTISE:
-               return KS_P1ANAR;
-       case MII_LPA:
-               return KS_P1ANLPR;
-       }
-
-       return 0x0;
-}
-
-/**
- * ks_phy_read - MII interface PHY register read.
- * @netdev: The network device the PHY is on.
- * @phy_addr: Address of PHY (ignored as we only have one)
- * @reg: The register to read.
- *
- * This call reads data from the PHY register specified in @reg. Since the
- * device does not support all the MII registers, the non-existent values
- * are always returned as zero.
- *
- * We return zero for unsupported registers as the MII code does not check
- * the value returned for any error status, and simply returns it to the
- * caller. The mii-tool that the driver was tested with takes any -ve error
- * as real PHY capabilities, thus displaying incorrect data to the user.
- */
-static int ks_phy_read(struct net_device *netdev, int phy_addr, int reg)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       int ksreg;
-       int result;
-
-       ksreg = ks_phy_reg(reg);
-       if (!ksreg)
-               return 0x0;     /* no error return allowed, so use zero */
-
-       mutex_lock(&ks->lock);
-       result = ks_rdreg16(ks, ksreg);
-       mutex_unlock(&ks->lock);
-
-       return result;
-}
-
-static void ks_phy_write(struct net_device *netdev,
-                            int phy, int reg, int value)
-{
-       struct ks_net *ks = netdev_priv(netdev);
-       int ksreg;
-
-       ksreg = ks_phy_reg(reg);
-       if (ksreg) {
-               mutex_lock(&ks->lock);
-               ks_wrreg16(ks, ksreg, value);
-               mutex_unlock(&ks->lock);
-       }
-}
-
-/**
- * ks_read_selftest - read the selftest memory info.
- * @ks: The device state
- *
- * Read and check the TX/RX memory selftest information.
- */
-static int ks_read_selftest(struct ks_net *ks)
-{
-       unsigned both_done = MBIR_TXMBF | MBIR_RXMBF;
-       int ret = 0;
-       unsigned rd;
-
-       rd = ks_rdreg16(ks, KS_MBIR);
-
-       if ((rd & both_done) != both_done) {
-               netdev_warn(ks->netdev, "Memory selftest not finished\n");
-               return 0;
-       }
-
-       if (rd & MBIR_TXMBFA) {
-               netdev_err(ks->netdev, "TX memory selftest fails\n");
-               ret |= 1;
-       }
-
-       if (rd & MBIR_RXMBFA) {
-               netdev_err(ks->netdev, "RX memory selftest fails\n");
-               ret |= 2;
-       }
-
-       netdev_info(ks->netdev, "the selftest passes\n");
-       return ret;
-}
-
-static void ks_setup(struct ks_net *ks)
-{
-       u16     w;
-
-       /**
-        * Configure QMU Transmit
-        */
-
-       /* Setup Transmit Frame Data Pointer Auto-Increment (TXFDPR) */
-       ks_wrreg16(ks, KS_TXFDPR, TXFDPR_TXFPAI);
-
-       /* Setup Receive Frame Data Pointer Auto-Increment */
-       ks_wrreg16(ks, KS_RXFDPR, RXFDPR_RXFPAI);
-
-       /* Setup Receive Frame Threshold - 1 frame (RXFCTFC) */
-       ks_wrreg16(ks, KS_RXFCTR, 1 & RXFCTR_THRESHOLD_MASK);
-
-       /* Setup RxQ Command Control (RXQCR) */
-       ks->rc_rxqcr = RXQCR_CMD_CNTL;
-       ks_wrreg16(ks, KS_RXQCR, ks->rc_rxqcr);
-
-       /**
-        * set the force mode to half duplex, default is full duplex
-        *  because if the auto-negotiation fails, most switch uses
-        *  half-duplex.
-        */
-
-       w = ks_rdreg16(ks, KS_P1MBCR);
-       w &= ~P1MBCR_FORCE_FDX;
-       ks_wrreg16(ks, KS_P1MBCR, w);
-
-       w = TXCR_TXFCE | TXCR_TXPE | TXCR_TXCRC | TXCR_TCGIP;
-       ks_wrreg16(ks, KS_TXCR, w);
-
-       w = RXCR1_RXFCE | RXCR1_RXBE | RXCR1_RXUE | RXCR1_RXME | RXCR1_RXIPFCC;
-
-       if (ks->promiscuous)         /* bPromiscuous */
-               w |= (RXCR1_RXAE | RXCR1_RXINVF);
-       else if (ks->all_mcast) /* Multicast address passed mode */
-               w |= (RXCR1_RXAE | RXCR1_RXMAFMA | RXCR1_RXPAFMA);
-       else                                   /* Normal mode */
-               w |= RXCR1_RXPAFMA;
-
-       ks_wrreg16(ks, KS_RXCR1, w);
-}  /*ks_setup */
-
-
-static void ks_setup_int(struct ks_net *ks)
-{
-       ks->rc_ier = 0x00;
-       /* Clear the interrupts status of the hardware. */
-       ks_wrreg16(ks, KS_ISR, 0xffff);
-
-       /* Enables the interrupts of the hardware. */
-       ks->rc_ier = (IRQ_LCI | IRQ_TXI | IRQ_RXI);
-}  /* ks_setup_int */
-
-static int ks_hw_init(struct ks_net *ks)
-{
-#define        MHEADER_SIZE    (sizeof(struct type_frame_head) * MAX_RECV_FRAMES)
-       ks->promiscuous = 0;
-       ks->all_mcast = 0;
-       ks->mcast_lst_size = 0;
-
-       ks->frame_head_info = (struct type_frame_head *) \
-               kmalloc(MHEADER_SIZE, GFP_KERNEL);
-       if (!ks->frame_head_info) {
-               pr_err("Error: Fail to allocate frame memory\n");
-               return false;
-       }
-
-       ks_set_mac(ks, KS_DEFAULT_MAC_ADDRESS);
-       return true;
-}
-
-
-static int __devinit ks8851_probe(struct platform_device *pdev)
-{
-       int err = -ENOMEM;
-       struct resource *io_d, *io_c;
-       struct net_device *netdev;
-       struct ks_net *ks;
-       u16 id, data;
-
-       io_d = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       io_c = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-
-       if (!request_mem_region(io_d->start, resource_size(io_d), DRV_NAME))
-               goto err_mem_region;
-
-       if (!request_mem_region(io_c->start, resource_size(io_c), DRV_NAME))
-               goto err_mem_region1;
-
-       netdev = alloc_etherdev(sizeof(struct ks_net));
-       if (!netdev)
-               goto err_alloc_etherdev;
-
-       SET_NETDEV_DEV(netdev, &pdev->dev);
-
-       ks = netdev_priv(netdev);
-       ks->netdev = netdev;
-       ks->hw_addr = ioremap(io_d->start, resource_size(io_d));
-
-       if (!ks->hw_addr)
-               goto err_ioremap;
-
-       ks->hw_addr_cmd = ioremap(io_c->start, resource_size(io_c));
-       if (!ks->hw_addr_cmd)
-               goto err_ioremap1;
-
-       ks->irq = platform_get_irq(pdev, 0);
-
-       if (ks->irq < 0) {
-               err = ks->irq;
-               goto err_get_irq;
-       }
-
-       ks->pdev = pdev;
-
-       mutex_init(&ks->lock);
-       spin_lock_init(&ks->statelock);
-
-       netdev->netdev_ops = &ks_netdev_ops;
-       netdev->ethtool_ops = &ks_ethtool_ops;
-
-       /* setup mii state */
-       ks->mii.dev             = netdev;
-       ks->mii.phy_id          = 1,
-       ks->mii.phy_id_mask     = 1;
-       ks->mii.reg_num_mask    = 0xf;
-       ks->mii.mdio_read       = ks_phy_read;
-       ks->mii.mdio_write      = ks_phy_write;
-
-       netdev_info(netdev, "message enable is %d\n", msg_enable);
-       /* set the default message enable */
-       ks->msg_enable = netif_msg_init(msg_enable, (NETIF_MSG_DRV |
-                                                    NETIF_MSG_PROBE |
-                                                    NETIF_MSG_LINK));
-       ks_read_config(ks);
-
-       /* simple check for a valid chip being connected to the bus */
-       if ((ks_rdreg16(ks, KS_CIDER) & ~CIDER_REV_MASK) != CIDER_ID) {
-               netdev_err(netdev, "failed to read device ID\n");
-               err = -ENODEV;
-               goto err_register;
-       }
-
-       if (ks_read_selftest(ks)) {
-               netdev_err(netdev, "failed to read device ID\n");
-               err = -ENODEV;
-               goto err_register;
-       }
-
-       err = register_netdev(netdev);
-       if (err)
-               goto err_register;
-
-       platform_set_drvdata(pdev, netdev);
-
-       ks_soft_reset(ks, GRR_GSR);
-       ks_hw_init(ks);
-       ks_disable_qmu(ks);
-       ks_setup(ks);
-       ks_setup_int(ks);
-       memcpy(netdev->dev_addr, ks->mac_addr, 6);
-
-       data = ks_rdreg16(ks, KS_OBCR);
-       ks_wrreg16(ks, KS_OBCR, data | OBCR_ODS_16MA);
-
-       /**
-        * If you want to use the default MAC addr,
-        * comment out the 2 functions below.
-        */
-
-       random_ether_addr(netdev->dev_addr);
-       ks_set_mac(ks, netdev->dev_addr);
-
-       id = ks_rdreg16(ks, KS_CIDER);
-
-       netdev_info(netdev, "Found chip, family: 0x%x, id: 0x%x, rev: 0x%x\n",
-                   (id >> 8) & 0xff, (id >> 4) & 0xf, (id >> 1) & 0x7);
-       return 0;
-
-err_register:
-err_get_irq:
-       iounmap(ks->hw_addr_cmd);
-err_ioremap1:
-       iounmap(ks->hw_addr);
-err_ioremap:
-       free_netdev(netdev);
-err_alloc_etherdev:
-       release_mem_region(io_c->start, resource_size(io_c));
-err_mem_region1:
-       release_mem_region(io_d->start, resource_size(io_d));
-err_mem_region:
-       return err;
-}
-
-static int __devexit ks8851_remove(struct platform_device *pdev)
-{
-       struct net_device *netdev = platform_get_drvdata(pdev);
-       struct ks_net *ks = netdev_priv(netdev);
-       struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
-       kfree(ks->frame_head_info);
-       unregister_netdev(netdev);
-       iounmap(ks->hw_addr);
-       free_netdev(netdev);
-       release_mem_region(iomem->start, resource_size(iomem));
-       platform_set_drvdata(pdev, NULL);
-       return 0;
-
-}
-
-static struct platform_driver ks8851_platform_driver = {
-       .driver = {
-               .name = DRV_NAME,
-               .owner = THIS_MODULE,
-       },
-       .probe = ks8851_probe,
-       .remove = __devexit_p(ks8851_remove),
-};
-
-static int __init ks8851_init(void)
-{
-       return platform_driver_register(&ks8851_platform_driver);
-}
-
-static void __exit ks8851_exit(void)
-{
-       platform_driver_unregister(&ks8851_platform_driver);
-}
-
-module_init(ks8851_init);
-module_exit(ks8851_exit);
-
-MODULE_DESCRIPTION("KS8851 MLL Network driver");
-MODULE_AUTHOR("David Choi <david.choi@micrel.com>");
-MODULE_LICENSE("GPL");
-module_param_named(message, msg_enable, int, 0);
-MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
-
diff --git a/drivers/net/ksz884x.c b/drivers/net/ksz884x.c
deleted file mode 100644 (file)
index 27418d3..0000000
+++ /dev/null
@@ -1,7289 +0,0 @@
-/**
- * drivers/net/ksx884x.c - Micrel KSZ8841/2 PCI Ethernet driver
- *
- * Copyright (c) 2009-2010 Micrel, Inc.
- *     Tristram Ha <Tristram.Ha@micrel.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * 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.
- */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/ioport.h>
-#include <linux/pci.h>
-#include <linux/proc_fs.h>
-#include <linux/mii.h>
-#include <linux/platform_device.h>
-#include <linux/ethtool.h>
-#include <linux/etherdevice.h>
-#include <linux/in.h>
-#include <linux/ip.h>
-#include <linux/if_vlan.h>
-#include <linux/crc32.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-
-
-/* DMA Registers */
-
-#define KS_DMA_TX_CTRL                 0x0000
-#define DMA_TX_ENABLE                  0x00000001
-#define DMA_TX_CRC_ENABLE              0x00000002
-#define DMA_TX_PAD_ENABLE              0x00000004
-#define DMA_TX_LOOPBACK                        0x00000100
-#define DMA_TX_FLOW_ENABLE             0x00000200
-#define DMA_TX_CSUM_IP                 0x00010000
-#define DMA_TX_CSUM_TCP                        0x00020000
-#define DMA_TX_CSUM_UDP                        0x00040000
-#define DMA_TX_BURST_SIZE              0x3F000000
-
-#define KS_DMA_RX_CTRL                 0x0004
-#define DMA_RX_ENABLE                  0x00000001
-#define KS884X_DMA_RX_MULTICAST                0x00000002
-#define DMA_RX_PROMISCUOUS             0x00000004
-#define DMA_RX_ERROR                   0x00000008
-#define DMA_RX_UNICAST                 0x00000010
-#define DMA_RX_ALL_MULTICAST           0x00000020
-#define DMA_RX_BROADCAST               0x00000040
-#define DMA_RX_FLOW_ENABLE             0x00000200
-#define DMA_RX_CSUM_IP                 0x00010000
-#define DMA_RX_CSUM_TCP                        0x00020000
-#define DMA_RX_CSUM_UDP                        0x00040000
-#define DMA_RX_BURST_SIZE              0x3F000000
-
-#define DMA_BURST_SHIFT                        24
-#define DMA_BURST_DEFAULT              8
-
-#define KS_DMA_TX_START                        0x0008
-#define KS_DMA_RX_START                        0x000C
-#define DMA_START                      0x00000001
-
-#define KS_DMA_TX_ADDR                 0x0010
-#define KS_DMA_RX_ADDR                 0x0014
-
-#define DMA_ADDR_LIST_MASK             0xFFFFFFFC
-#define DMA_ADDR_LIST_SHIFT            2
-
-/* MTR0 */
-#define KS884X_MULTICAST_0_OFFSET      0x0020
-#define KS884X_MULTICAST_1_OFFSET      0x0021
-#define KS884X_MULTICAST_2_OFFSET      0x0022
-#define KS884x_MULTICAST_3_OFFSET      0x0023
-/* MTR1 */
-#define KS884X_MULTICAST_4_OFFSET      0x0024
-#define KS884X_MULTICAST_5_OFFSET      0x0025
-#define KS884X_MULTICAST_6_OFFSET      0x0026
-#define KS884X_MULTICAST_7_OFFSET      0x0027
-
-/* Interrupt Registers */
-
-/* INTEN */
-#define KS884X_INTERRUPTS_ENABLE       0x0028
-/* INTST */
-#define KS884X_INTERRUPTS_STATUS       0x002C
-
-#define KS884X_INT_RX_STOPPED          0x02000000
-#define KS884X_INT_TX_STOPPED          0x04000000
-#define KS884X_INT_RX_OVERRUN          0x08000000
-#define KS884X_INT_TX_EMPTY            0x10000000
-#define KS884X_INT_RX                  0x20000000
-#define KS884X_INT_TX                  0x40000000
-#define KS884X_INT_PHY                 0x80000000
-
-#define KS884X_INT_RX_MASK             \
-       (KS884X_INT_RX | KS884X_INT_RX_OVERRUN)
-#define KS884X_INT_TX_MASK             \
-       (KS884X_INT_TX | KS884X_INT_TX_EMPTY)
-#define KS884X_INT_MASK        (KS884X_INT_RX | KS884X_INT_TX | KS884X_INT_PHY)
-
-/* MAC Additional Station Address */
-
-/* MAAL0 */
-#define KS_ADD_ADDR_0_LO               0x0080
-/* MAAH0 */
-#define KS_ADD_ADDR_0_HI               0x0084
-/* MAAL1 */
-#define KS_ADD_ADDR_1_LO               0x0088
-/* MAAH1 */
-#define KS_ADD_ADDR_1_HI               0x008C
-/* MAAL2 */
-#define KS_ADD_ADDR_2_LO               0x0090
-/* MAAH2 */
-#define KS_ADD_ADDR_2_HI               0x0094
-/* MAAL3 */
-#define KS_ADD_ADDR_3_LO               0x0098
-/* MAAH3 */
-#define KS_ADD_ADDR_3_HI               0x009C
-/* MAAL4 */
-#define KS_ADD_ADDR_4_LO               0x00A0
-/* MAAH4 */
-#define KS_ADD_ADDR_4_HI               0x00A4
-/* MAAL5 */
-#define KS_ADD_ADDR_5_LO               0x00A8
-/* MAAH5 */
-#define KS_ADD_ADDR_5_HI               0x00AC
-/* MAAL6 */
-#define KS_ADD_ADDR_6_LO               0x00B0
-/* MAAH6 */
-#define KS_ADD_ADDR_6_HI               0x00B4
-/* MAAL7 */
-#define KS_ADD_ADDR_7_LO               0x00B8
-/* MAAH7 */
-#define KS_ADD_ADDR_7_HI               0x00BC
-/* MAAL8 */
-#define KS_ADD_ADDR_8_LO               0x00C0
-/* MAAH8 */
-#define KS_ADD_ADDR_8_HI               0x00C4
-/* MAAL9 */
-#define KS_ADD_ADDR_9_LO               0x00C8
-/* MAAH9 */
-#define KS_ADD_ADDR_9_HI               0x00CC
-/* MAAL10 */
-#define KS_ADD_ADDR_A_LO               0x00D0
-/* MAAH10 */
-#define KS_ADD_ADDR_A_HI               0x00D4
-/* MAAL11 */
-#define KS_ADD_ADDR_B_LO               0x00D8
-/* MAAH11 */
-#define KS_ADD_ADDR_B_HI               0x00DC
-/* MAAL12 */
-#define KS_ADD_ADDR_C_LO               0x00E0
-/* MAAH12 */
-#define KS_ADD_ADDR_C_HI               0x00E4
-/* MAAL13 */
-#define KS_ADD_ADDR_D_LO               0x00E8
-/* MAAH13 */
-#define KS_ADD_ADDR_D_HI               0x00EC
-/* MAAL14 */
-#define KS_ADD_ADDR_E_LO               0x00F0
-/* MAAH14 */
-#define KS_ADD_ADDR_E_HI               0x00F4
-/* MAAL15 */
-#define KS_ADD_ADDR_F_LO               0x00F8
-/* MAAH15 */
-#define KS_ADD_ADDR_F_HI               0x00FC
-
-#define ADD_ADDR_HI_MASK               0x0000FFFF
-#define ADD_ADDR_ENABLE                        0x80000000
-#define ADD_ADDR_INCR                  8
-
-/* Miscellaneous Registers */
-
-/* MARL */
-#define KS884X_ADDR_0_OFFSET           0x0200
-#define KS884X_ADDR_1_OFFSET           0x0201
-/* MARM */
-#define KS884X_ADDR_2_OFFSET           0x0202
-#define KS884X_ADDR_3_OFFSET           0x0203
-/* MARH */
-#define KS884X_ADDR_4_OFFSET           0x0204
-#define KS884X_ADDR_5_OFFSET           0x0205
-
-/* OBCR */
-#define KS884X_BUS_CTRL_OFFSET         0x0210
-
-#define BUS_SPEED_125_MHZ              0x0000
-#define BUS_SPEED_62_5_MHZ             0x0001
-#define BUS_SPEED_41_66_MHZ            0x0002
-#define BUS_SPEED_25_MHZ               0x0003
-
-/* EEPCR */
-#define KS884X_EEPROM_CTRL_OFFSET      0x0212
-
-#define EEPROM_CHIP_SELECT             0x0001
-#define EEPROM_SERIAL_CLOCK            0x0002
-#define EEPROM_DATA_OUT                        0x0004
-#define EEPROM_DATA_IN                 0x0008
-#define EEPROM_ACCESS_ENABLE           0x0010
-
-/* MBIR */
-#define KS884X_MEM_INFO_OFFSET         0x0214
-
-#define RX_MEM_TEST_FAILED             0x0008
-#define RX_MEM_TEST_FINISHED           0x0010
-#define TX_MEM_TEST_FAILED             0x0800
-#define TX_MEM_TEST_FINISHED           0x1000
-
-/* GCR */
-#define KS884X_GLOBAL_CTRL_OFFSET      0x0216
-#define GLOBAL_SOFTWARE_RESET          0x0001
-
-#define KS8841_POWER_MANAGE_OFFSET     0x0218
-
-/* WFCR */
-#define KS8841_WOL_CTRL_OFFSET         0x021A
-#define KS8841_WOL_MAGIC_ENABLE                0x0080
-#define KS8841_WOL_FRAME3_ENABLE       0x0008
-#define KS8841_WOL_FRAME2_ENABLE       0x0004
-#define KS8841_WOL_FRAME1_ENABLE       0x0002
-#define KS8841_WOL_FRAME0_ENABLE       0x0001
-
-/* WF0 */
-#define KS8841_WOL_FRAME_CRC_OFFSET    0x0220
-#define KS8841_WOL_FRAME_BYTE0_OFFSET  0x0224
-#define KS8841_WOL_FRAME_BYTE2_OFFSET  0x0228
-
-/* IACR */
-#define KS884X_IACR_P                  0x04A0
-#define KS884X_IACR_OFFSET             KS884X_IACR_P
-
-/* IADR1 */
-#define KS884X_IADR1_P                 0x04A2
-#define KS884X_IADR2_P                 0x04A4
-#define KS884X_IADR3_P                 0x04A6
-#define KS884X_IADR4_P                 0x04A8
-#define KS884X_IADR5_P                 0x04AA
-
-#define KS884X_ACC_CTRL_SEL_OFFSET     KS884X_IACR_P
-#define KS884X_ACC_CTRL_INDEX_OFFSET   (KS884X_ACC_CTRL_SEL_OFFSET + 1)
-
-#define KS884X_ACC_DATA_0_OFFSET       KS884X_IADR4_P
-#define KS884X_ACC_DATA_1_OFFSET       (KS884X_ACC_DATA_0_OFFSET + 1)
-#define KS884X_ACC_DATA_2_OFFSET       KS884X_IADR5_P
-#define KS884X_ACC_DATA_3_OFFSET       (KS884X_ACC_DATA_2_OFFSET + 1)
-#define KS884X_ACC_DATA_4_OFFSET       KS884X_IADR2_P
-#define KS884X_ACC_DATA_5_OFFSET       (KS884X_ACC_DATA_4_OFFSET + 1)
-#define KS884X_ACC_DATA_6_OFFSET       KS884X_IADR3_P
-#define KS884X_ACC_DATA_7_OFFSET       (KS884X_ACC_DATA_6_OFFSET + 1)
-#define KS884X_ACC_DATA_8_OFFSET       KS884X_IADR1_P
-
-/* P1MBCR */
-#define KS884X_P1MBCR_P                        0x04D0
-#define KS884X_P1MBSR_P                        0x04D2
-#define KS884X_PHY1ILR_P               0x04D4
-#define KS884X_PHY1IHR_P               0x04D6
-#define KS884X_P1ANAR_P                        0x04D8
-#define KS884X_P1ANLPR_P               0x04DA
-
-/* P2MBCR */
-#define KS884X_P2MBCR_P                        0x04E0
-#define KS884X_P2MBSR_P                        0x04E2
-#define KS884X_PHY2ILR_P               0x04E4
-#define KS884X_PHY2IHR_P               0x04E6
-#define KS884X_P2ANAR_P                        0x04E8
-#define KS884X_P2ANLPR_P               0x04EA
-
-#define KS884X_PHY_1_CTRL_OFFSET       KS884X_P1MBCR_P
-#define PHY_CTRL_INTERVAL              (KS884X_P2MBCR_P - KS884X_P1MBCR_P)
-
-#define KS884X_PHY_CTRL_OFFSET         0x00
-
-/* Mode Control Register */
-#define PHY_REG_CTRL                   0
-
-#define PHY_RESET                      0x8000
-#define PHY_LOOPBACK                   0x4000
-#define PHY_SPEED_100MBIT              0x2000
-#define PHY_AUTO_NEG_ENABLE            0x1000
-#define PHY_POWER_DOWN                 0x0800
-#define PHY_MII_DISABLE                        0x0400
-#define PHY_AUTO_NEG_RESTART           0x0200
-#define PHY_FULL_DUPLEX                        0x0100
-#define PHY_COLLISION_TEST             0x0080
-#define PHY_HP_MDIX                    0x0020
-#define PHY_FORCE_MDIX                 0x0010
-#define PHY_AUTO_MDIX_DISABLE          0x0008
-#define PHY_REMOTE_FAULT_DISABLE       0x0004
-#define PHY_TRANSMIT_DISABLE           0x0002
-#define PHY_LED_DISABLE                        0x0001
-
-#define KS884X_PHY_STATUS_OFFSET       0x02
-
-/* Mode Status Register */
-#define PHY_REG_STATUS                 1
-
-#define PHY_100BT4_CAPABLE             0x8000
-#define PHY_100BTX_FD_CAPABLE          0x4000
-#define PHY_100BTX_CAPABLE             0x2000
-#define PHY_10BT_FD_CAPABLE            0x1000
-#define PHY_10BT_CAPABLE               0x0800
-#define PHY_MII_SUPPRESS_CAPABLE       0x0040
-#define PHY_AUTO_NEG_ACKNOWLEDGE       0x0020
-#define PHY_REMOTE_FAULT               0x0010
-#define PHY_AUTO_NEG_CAPABLE           0x0008
-#define PHY_LINK_STATUS                        0x0004
-#define PHY_JABBER_DETECT              0x0002
-#define PHY_EXTENDED_CAPABILITY                0x0001
-
-#define KS884X_PHY_ID_1_OFFSET         0x04
-#define KS884X_PHY_ID_2_OFFSET         0x06
-
-/* PHY Identifier Registers */
-#define PHY_REG_ID_1                   2
-#define PHY_REG_ID_2                   3
-
-#define KS884X_PHY_AUTO_NEG_OFFSET     0x08
-
-/* Auto-Negotiation Advertisement Register */
-#define PHY_REG_AUTO_NEGOTIATION       4
-
-#define PHY_AUTO_NEG_NEXT_PAGE         0x8000
-#define PHY_AUTO_NEG_REMOTE_FAULT      0x2000
-/* Not supported. */
-#define PHY_AUTO_NEG_ASYM_PAUSE                0x0800
-#define PHY_AUTO_NEG_SYM_PAUSE         0x0400
-#define PHY_AUTO_NEG_100BT4            0x0200
-#define PHY_AUTO_NEG_100BTX_FD         0x0100
-#define PHY_AUTO_NEG_100BTX            0x0080
-#define PHY_AUTO_NEG_10BT_FD           0x0040
-#define PHY_AUTO_NEG_10BT              0x0020
-#define PHY_AUTO_NEG_SELECTOR          0x001F
-#define PHY_AUTO_NEG_802_3             0x0001
-
-#define PHY_AUTO_NEG_PAUSE  (PHY_AUTO_NEG_SYM_PAUSE | PHY_AUTO_NEG_ASYM_PAUSE)
-
-#define KS884X_PHY_REMOTE_CAP_OFFSET   0x0A
-
-/* Auto-Negotiation Link Partner Ability Register */
-#define PHY_REG_REMOTE_CAPABILITY      5
-
-#define PHY_REMOTE_NEXT_PAGE           0x8000
-#define PHY_REMOTE_ACKNOWLEDGE         0x4000
-#define PHY_REMOTE_REMOTE_FAULT                0x2000
-#define PHY_REMOTE_SYM_PAUSE           0x0400
-#define PHY_REMOTE_100BTX_FD           0x0100
-#define PHY_REMOTE_100BTX              0x0080
-#define PHY_REMOTE_10BT_FD             0x0040
-#define PHY_REMOTE_10BT                        0x0020
-
-/* P1VCT */
-#define KS884X_P1VCT_P                 0x04F0
-#define KS884X_P1PHYCTRL_P             0x04F2
-
-/* P2VCT */
-#define KS884X_P2VCT_P                 0x04F4
-#define KS884X_P2PHYCTRL_P             0x04F6
-
-#define KS884X_PHY_SPECIAL_OFFSET      KS884X_P1VCT_P
-#define PHY_SPECIAL_INTERVAL           (KS884X_P2VCT_P - KS884X_P1VCT_P)
-
-#define KS884X_PHY_LINK_MD_OFFSET      0x00
-
-#define PHY_START_CABLE_DIAG           0x8000
-#define PHY_CABLE_DIAG_RESULT          0x6000
-#define PHY_CABLE_STAT_NORMAL          0x0000
-#define PHY_CABLE_STAT_OPEN            0x2000
-#define PHY_CABLE_STAT_SHORT           0x4000
-#define PHY_CABLE_STAT_FAILED          0x6000
-#define PHY_CABLE_10M_SHORT            0x1000
-#define PHY_CABLE_FAULT_COUNTER                0x01FF
-
-#define KS884X_PHY_PHY_CTRL_OFFSET     0x02
-
-#define PHY_STAT_REVERSED_POLARITY     0x0020
-#define PHY_STAT_MDIX                  0x0010
-#define PHY_FORCE_LINK                 0x0008
-#define PHY_POWER_SAVING_DISABLE       0x0004
-#define PHY_REMOTE_LOOPBACK            0x0002
-
-/* SIDER */
-#define KS884X_SIDER_P                 0x0400
-#define KS884X_CHIP_ID_OFFSET          KS884X_SIDER_P
-#define KS884X_FAMILY_ID_OFFSET                (KS884X_CHIP_ID_OFFSET + 1)
-
-#define REG_FAMILY_ID                  0x88
-
-#define REG_CHIP_ID_41                 0x8810
-#define REG_CHIP_ID_42                 0x8800
-
-#define KS884X_CHIP_ID_MASK_41         0xFF10
-#define KS884X_CHIP_ID_MASK            0xFFF0
-#define KS884X_CHIP_ID_SHIFT           4
-#define KS884X_REVISION_MASK           0x000E
-#define KS884X_REVISION_SHIFT          1
-#define KS8842_START                   0x0001
-
-#define CHIP_IP_41_M                   0x8810
-#define CHIP_IP_42_M                   0x8800
-#define CHIP_IP_61_M                   0x8890
-#define CHIP_IP_62_M                   0x8880
-
-#define CHIP_IP_41_P                   0x8850
-#define CHIP_IP_42_P                   0x8840
-#define CHIP_IP_61_P                   0x88D0
-#define CHIP_IP_62_P                   0x88C0
-
-/* SGCR1 */
-#define KS8842_SGCR1_P                 0x0402
-#define KS8842_SWITCH_CTRL_1_OFFSET    KS8842_SGCR1_P
-
-#define SWITCH_PASS_ALL                        0x8000
-#define SWITCH_TX_FLOW_CTRL            0x2000
-#define SWITCH_RX_FLOW_CTRL            0x1000
-#define SWITCH_CHECK_LENGTH            0x0800
-#define SWITCH_AGING_ENABLE            0x0400
-#define SWITCH_FAST_AGING              0x0200
-#define SWITCH_AGGR_BACKOFF            0x0100
-#define SWITCH_PASS_PAUSE              0x0008
-#define SWITCH_LINK_AUTO_AGING         0x0001
-
-/* SGCR2 */
-#define KS8842_SGCR2_P                 0x0404
-#define KS8842_SWITCH_CTRL_2_OFFSET    KS8842_SGCR2_P
-
-#define SWITCH_VLAN_ENABLE             0x8000
-#define SWITCH_IGMP_SNOOP              0x4000
-#define IPV6_MLD_SNOOP_ENABLE          0x2000
-#define IPV6_MLD_SNOOP_OPTION          0x1000
-#define PRIORITY_SCHEME_SELECT         0x0800
-#define SWITCH_MIRROR_RX_TX            0x0100
-#define UNICAST_VLAN_BOUNDARY          0x0080
-#define MULTICAST_STORM_DISABLE                0x0040
-#define SWITCH_BACK_PRESSURE           0x0020
-#define FAIR_FLOW_CTRL                 0x0010
-#define NO_EXC_COLLISION_DROP          0x0008
-#define SWITCH_HUGE_PACKET             0x0004
-#define SWITCH_LEGAL_PACKET            0x0002
-#define SWITCH_BUF_RESERVE             0x0001
-
-/* SGCR3 */
-#define KS8842_SGCR3_P                 0x0406
-#define KS8842_SWITCH_CTRL_3_OFFSET    KS8842_SGCR3_P
-
-#define BROADCAST_STORM_RATE_LO                0xFF00
-#define SWITCH_REPEATER                        0x0080
-#define SWITCH_HALF_DUPLEX             0x0040
-#define SWITCH_FLOW_CTRL               0x0020
-#define SWITCH_10_MBIT                 0x0010
-#define SWITCH_REPLACE_NULL_VID                0x0008
-#define BROADCAST_STORM_RATE_HI                0x0007
-
-#define BROADCAST_STORM_RATE           0x07FF
-
-/* SGCR4 */
-#define KS8842_SGCR4_P                 0x0408
-
-/* SGCR5 */
-#define KS8842_SGCR5_P                 0x040A
-#define KS8842_SWITCH_CTRL_5_OFFSET    KS8842_SGCR5_P
-
-#define LED_MODE                       0x8200
-#define LED_SPEED_DUPLEX_ACT           0x0000
-#define LED_SPEED_DUPLEX_LINK_ACT      0x8000
-#define LED_DUPLEX_10_100              0x0200
-
-/* SGCR6 */
-#define KS8842_SGCR6_P                 0x0410
-#define KS8842_SWITCH_CTRL_6_OFFSET    KS8842_SGCR6_P
-
-#define KS8842_PRIORITY_MASK           3
-#define KS8842_PRIORITY_SHIFT          2
-
-/* SGCR7 */
-#define KS8842_SGCR7_P                 0x0412
-#define KS8842_SWITCH_CTRL_7_OFFSET    KS8842_SGCR7_P
-
-#define SWITCH_UNK_DEF_PORT_ENABLE     0x0008
-#define SWITCH_UNK_DEF_PORT_3          0x0004
-#define SWITCH_UNK_DEF_PORT_2          0x0002
-#define SWITCH_UNK_DEF_PORT_1          0x0001
-
-/* MACAR1 */
-#define KS8842_MACAR1_P                        0x0470
-#define KS8842_MACAR2_P                        0x0472
-#define KS8842_MACAR3_P                        0x0474
-#define KS8842_MAC_ADDR_1_OFFSET       KS8842_MACAR1_P
-#define KS8842_MAC_ADDR_0_OFFSET       (KS8842_MAC_ADDR_1_OFFSET + 1)
-#define KS8842_MAC_ADDR_3_OFFSET       KS8842_MACAR2_P
-#define KS8842_MAC_ADDR_2_OFFSET       (KS8842_MAC_ADDR_3_OFFSET + 1)
-#define KS8842_MAC_ADDR_5_OFFSET       KS8842_MACAR3_P
-#define KS8842_MAC_ADDR_4_OFFSET       (KS8842_MAC_ADDR_5_OFFSET + 1)
-
-/* TOSR1 */
-#define KS8842_TOSR1_P                 0x0480
-#define KS8842_TOSR2_P                 0x0482
-#define KS8842_TOSR3_P                 0x0484
-#define KS8842_TOSR4_P                 0x0486
-#define KS8842_TOSR5_P                 0x0488
-#define KS8842_TOSR6_P                 0x048A
-#define KS8842_TOSR7_P                 0x0490
-#define KS8842_TOSR8_P                 0x0492
-#define KS8842_TOS_1_OFFSET            KS8842_TOSR1_P
-#define KS8842_TOS_2_OFFSET            KS8842_TOSR2_P
-#define KS8842_TOS_3_OFFSET            KS8842_TOSR3_P
-#define KS8842_TOS_4_OFFSET            KS8842_TOSR4_P
-#define KS8842_TOS_5_OFFSET            KS8842_TOSR5_P
-#define KS8842_TOS_6_OFFSET            KS8842_TOSR6_P
-
-#define KS8842_TOS_7_OFFSET            KS8842_TOSR7_P
-#define KS8842_TOS_8_OFFSET            KS8842_TOSR8_P
-
-/* P1CR1 */
-#define KS8842_P1CR1_P                 0x0500
-#define KS8842_P1CR2_P                 0x0502
-#define KS8842_P1VIDR_P                        0x0504
-#define KS8842_P1CR3_P                 0x0506
-#define KS8842_P1IRCR_P                        0x0508
-#define KS8842_P1ERCR_P                        0x050A
-#define KS884X_P1SCSLMD_P              0x0510
-#define KS884X_P1CR4_P                 0x0512
-#define KS884X_P1SR_P                  0x0514
-
-/* P2CR1 */
-#define KS8842_P2CR1_P                 0x0520
-#define KS8842_P2CR2_P                 0x0522
-#define KS8842_P2VIDR_P                        0x0524
-#define KS8842_P2CR3_P                 0x0526
-#define KS8842_P2IRCR_P                        0x0528
-#define KS8842_P2ERCR_P                        0x052A
-#define KS884X_P2SCSLMD_P              0x0530
-#define KS884X_P2CR4_P                 0x0532
-#define KS884X_P2SR_P                  0x0534
-
-/* P3CR1 */
-#define KS8842_P3CR1_P                 0x0540
-#define KS8842_P3CR2_P                 0x0542
-#define KS8842_P3VIDR_P                        0x0544
-#define KS8842_P3CR3_P                 0x0546
-#define KS8842_P3IRCR_P                        0x0548
-#define KS8842_P3ERCR_P                        0x054A
-
-#define KS8842_PORT_1_CTRL_1           KS8842_P1CR1_P
-#define KS8842_PORT_2_CTRL_1           KS8842_P2CR1_P
-#define KS8842_PORT_3_CTRL_1           KS8842_P3CR1_P
-
-#define PORT_CTRL_ADDR(port, addr)             \
-       (addr = KS8842_PORT_1_CTRL_1 + (port) * \
-               (KS8842_PORT_2_CTRL_1 - KS8842_PORT_1_CTRL_1))
-
-#define KS8842_PORT_CTRL_1_OFFSET      0x00
-
-#define PORT_BROADCAST_STORM           0x0080
-#define PORT_DIFFSERV_ENABLE           0x0040
-#define PORT_802_1P_ENABLE             0x0020
-#define PORT_BASED_PRIORITY_MASK       0x0018
-#define PORT_BASED_PRIORITY_BASE       0x0003
-#define PORT_BASED_PRIORITY_SHIFT      3
-#define PORT_BASED_PRIORITY_0          0x0000
-#define PORT_BASED_PRIORITY_1          0x0008
-#define PORT_BASED_PRIORITY_2          0x0010
-#define PORT_BASED_PRIORITY_3          0x0018
-#define PORT_INSERT_TAG                        0x0004
-#define PORT_REMOVE_TAG                        0x0002
-#define PORT_PRIO_QUEUE_ENABLE         0x0001
-
-#define KS8842_PORT_CTRL_2_OFFSET      0x02
-
-#define PORT_INGRESS_VLAN_FILTER       0x4000
-#define PORT_DISCARD_NON_VID           0x2000
-#define PORT_FORCE_FLOW_CTRL           0x1000
-#define PORT_BACK_PRESSURE             0x0800
-#define PORT_TX_ENABLE                 0x0400
-#define PORT_RX_ENABLE                 0x0200
-#define PORT_LEARN_DISABLE             0x0100
-#define PORT_MIRROR_SNIFFER            0x0080
-#define PORT_MIRROR_RX                 0x0040
-#define PORT_MIRROR_TX                 0x0020
-#define PORT_USER_PRIORITY_CEILING     0x0008
-#define PORT_VLAN_MEMBERSHIP           0x0007
-
-#define KS8842_PORT_CTRL_VID_OFFSET    0x04
-
-#define PORT_DEFAULT_VID               0x0001
-
-#define KS8842_PORT_CTRL_3_OFFSET      0x06
-
-#define PORT_INGRESS_LIMIT_MODE                0x000C
-#define PORT_INGRESS_ALL               0x0000
-#define PORT_INGRESS_UNICAST           0x0004
-#define PORT_INGRESS_MULTICAST         0x0008
-#define PORT_INGRESS_BROADCAST         0x000C
-#define PORT_COUNT_IFG                 0x0002
-#define PORT_COUNT_PREAMBLE            0x0001
-
-#define KS8842_PORT_IN_RATE_OFFSET     0x08
-#define KS8842_PORT_OUT_RATE_OFFSET    0x0A
-
-#define PORT_PRIORITY_RATE             0x0F
-#define PORT_PRIORITY_RATE_SHIFT       4
-
-#define KS884X_PORT_LINK_MD            0x10
-
-#define PORT_CABLE_10M_SHORT           0x8000
-#define PORT_CABLE_DIAG_RESULT         0x6000
-#define PORT_CABLE_STAT_NORMAL         0x0000
-#define PORT_CABLE_STAT_OPEN           0x2000
-#define PORT_CABLE_STAT_SHORT          0x4000
-#define PORT_CABLE_STAT_FAILED         0x6000
-#define PORT_START_CABLE_DIAG          0x1000
-#define PORT_FORCE_LINK                        0x0800
-#define PORT_POWER_SAVING_DISABLE      0x0400
-#define PORT_PHY_REMOTE_LOOPBACK       0x0200
-#define PORT_CABLE_FAULT_COUNTER       0x01FF
-
-#define KS884X_PORT_CTRL_4_OFFSET      0x12
-
-#define PORT_LED_OFF                   0x8000
-#define PORT_TX_DISABLE                        0x4000
-#define PORT_AUTO_NEG_RESTART          0x2000
-#define PORT_REMOTE_FAULT_DISABLE      0x1000
-#define PORT_POWER_DOWN                        0x0800
-#define PORT_AUTO_MDIX_DISABLE         0x0400
-#define PORT_FORCE_MDIX                        0x0200
-#define PORT_LOOPBACK                  0x0100
-#define PORT_AUTO_NEG_ENABLE           0x0080
-#define PORT_FORCE_100_MBIT            0x0040
-#define PORT_FORCE_FULL_DUPLEX         0x0020
-#define PORT_AUTO_NEG_SYM_PAUSE                0x0010
-#define PORT_AUTO_NEG_100BTX_FD                0x0008
-#define PORT_AUTO_NEG_100BTX           0x0004
-#define PORT_AUTO_NEG_10BT_FD          0x0002
-#define PORT_AUTO_NEG_10BT             0x0001
-
-#define KS884X_PORT_STATUS_OFFSET      0x14
-
-#define PORT_HP_MDIX                   0x8000
-#define PORT_REVERSED_POLARITY         0x2000
-#define PORT_RX_FLOW_CTRL              0x0800
-#define PORT_TX_FLOW_CTRL              0x1000
-#define PORT_STATUS_SPEED_100MBIT      0x0400
-#define PORT_STATUS_FULL_DUPLEX                0x0200
-#define PORT_REMOTE_FAULT              0x0100
-#define PORT_MDIX_STATUS               0x0080
-#define PORT_AUTO_NEG_COMPLETE         0x0040
-#define PORT_STATUS_LINK_GOOD          0x0020
-#define PORT_REMOTE_SYM_PAUSE          0x0010
-#define PORT_REMOTE_100BTX_FD          0x0008
-#define PORT_REMOTE_100BTX             0x0004
-#define PORT_REMOTE_10BT_FD            0x0002
-#define PORT_REMOTE_10BT               0x0001
-
-/*
-#define STATIC_MAC_TABLE_ADDR          00-0000FFFF-FFFFFFFF
-#define STATIC_MAC_TABLE_FWD_PORTS     00-00070000-00000000
-#define STATIC_MAC_TABLE_VALID         00-00080000-00000000
-#define STATIC_MAC_TABLE_OVERRIDE      00-00100000-00000000
-#define STATIC_MAC_TABLE_USE_FID       00-00200000-00000000
-#define STATIC_MAC_TABLE_FID           00-03C00000-00000000
-*/
-
-#define STATIC_MAC_TABLE_ADDR          0x0000FFFF
-#define STATIC_MAC_TABLE_FWD_PORTS     0x00070000
-#define STATIC_MAC_TABLE_VALID         0x00080000
-#define STATIC_MAC_TABLE_OVERRIDE      0x00100000
-#define STATIC_MAC_TABLE_USE_FID       0x00200000
-#define STATIC_MAC_TABLE_FID           0x03C00000
-
-#define STATIC_MAC_FWD_PORTS_SHIFT     16
-#define STATIC_MAC_FID_SHIFT           22
-
-/*
-#define VLAN_TABLE_VID                 00-00000000-00000FFF
-#define VLAN_TABLE_FID                 00-00000000-0000F000
-#define VLAN_TABLE_MEMBERSHIP          00-00000000-00070000
-#define VLAN_TABLE_VALID               00-00000000-00080000
-*/
-
-#define VLAN_TABLE_VID                 0x00000FFF
-#define VLAN_TABLE_FID                 0x0000F000
-#define VLAN_TABLE_MEMBERSHIP          0x00070000
-#define VLAN_TABLE_VALID               0x00080000
-
-#define VLAN_TABLE_FID_SHIFT           12
-#define VLAN_TABLE_MEMBERSHIP_SHIFT    16
-
-/*
-#define DYNAMIC_MAC_TABLE_ADDR         00-0000FFFF-FFFFFFFF
-#define DYNAMIC_MAC_TABLE_FID          00-000F0000-00000000
-#define DYNAMIC_MAC_TABLE_SRC_PORT     00-00300000-00000000
-#define DYNAMIC_MAC_TABLE_TIMESTAMP    00-00C00000-00000000
-#define DYNAMIC_MAC_TABLE_ENTRIES      03-FF000000-00000000
-#define DYNAMIC_MAC_TABLE_MAC_EMPTY    04-00000000-00000000
-#define DYNAMIC_MAC_TABLE_RESERVED     78-00000000-00000000
-#define DYNAMIC_MAC_TABLE_NOT_READY    80-00000000-00000000
-*/
-
-#define DYNAMIC_MAC_TABLE_ADDR         0x0000FFFF
-#define DYNAMIC_MAC_TABLE_FID          0x000F0000
-#define DYNAMIC_MAC_TABLE_SRC_PORT     0x00300000
-#define DYNAMIC_MAC_TABLE_TIMESTAMP    0x00C00000
-#define DYNAMIC_MAC_TABLE_ENTRIES      0xFF000000
-
-#define DYNAMIC_MAC_TABLE_ENTRIES_H    0x03
-#define DYNAMIC_MAC_TABLE_MAC_EMPTY    0x04
-#define DYNAMIC_MAC_TABLE_RESERVED     0x78
-#define DYNAMIC_MAC_TABLE_NOT_READY    0x80
-
-#define DYNAMIC_MAC_FID_SHIFT          16
-#define DYNAMIC_MAC_SRC_PORT_SHIFT     20
-#define DYNAMIC_MAC_TIMESTAMP_SHIFT    22
-#define DYNAMIC_MAC_ENTRIES_SHIFT      24
-#define DYNAMIC_MAC_ENTRIES_H_SHIFT    8
-
-/*
-#define MIB_COUNTER_VALUE              00-00000000-3FFFFFFF
-#define MIB_COUNTER_VALID              00-00000000-40000000
-#define MIB_COUNTER_OVERFLOW           00-00000000-80000000
-*/
-
-#define MIB_COUNTER_VALUE              0x3FFFFFFF
-#define MIB_COUNTER_VALID              0x40000000
-#define MIB_COUNTER_OVERFLOW           0x80000000
-
-#define MIB_PACKET_DROPPED             0x0000FFFF
-
-#define KS_MIB_PACKET_DROPPED_TX_0     0x100
-#define KS_MIB_PACKET_DROPPED_TX_1     0x101
-#define KS_MIB_PACKET_DROPPED_TX       0x102
-#define KS_MIB_PACKET_DROPPED_RX_0     0x103
-#define KS_MIB_PACKET_DROPPED_RX_1     0x104
-#define KS_MIB_PACKET_DROPPED_RX       0x105
-
-/* Change default LED mode. */
-#define SET_DEFAULT_LED                        LED_SPEED_DUPLEX_ACT
-
-#define MAC_ADDR_LEN                   6
-#define MAC_ADDR_ORDER(i)              (MAC_ADDR_LEN - 1 - (i))
-
-#define MAX_ETHERNET_BODY_SIZE         1500
-#define ETHERNET_HEADER_SIZE           14
-
-#define MAX_ETHERNET_PACKET_SIZE       \
-       (MAX_ETHERNET_BODY_SIZE + ETHERNET_HEADER_SIZE)
-
-#define REGULAR_RX_BUF_SIZE            (MAX_ETHERNET_PACKET_SIZE + 4)
-#define MAX_RX_BUF_SIZE                        (1912 + 4)
-
-#define ADDITIONAL_ENTRIES             16
-#define MAX_MULTICAST_LIST             32
-
-#define HW_MULTICAST_SIZE              8
-
-#define HW_TO_DEV_PORT(port)           (port - 1)
-
-enum {
-       media_connected,
-       media_disconnected
-};
-
-enum {
-       OID_COUNTER_UNKOWN,
-
-       OID_COUNTER_FIRST,
-
-       /* total transmit errors */
-       OID_COUNTER_XMIT_ERROR,
-
-       /* total receive errors */
-       OID_COUNTER_RCV_ERROR,
-
-       OID_COUNTER_LAST
-};
-
-/*
- * Hardware descriptor definitions
- */
-
-#define DESC_ALIGNMENT                 16
-#define BUFFER_ALIGNMENT               8
-
-#define NUM_OF_RX_DESC                 64
-#define NUM_OF_TX_DESC                 64
-
-#define KS_DESC_RX_FRAME_LEN           0x000007FF
-#define KS_DESC_RX_FRAME_TYPE          0x00008000
-#define KS_DESC_RX_ERROR_CRC           0x00010000
-#define KS_DESC_RX_ERROR_RUNT          0x00020000
-#define KS_DESC_RX_ERROR_TOO_LONG      0x00040000
-#define KS_DESC_RX_ERROR_PHY           0x00080000
-#define KS884X_DESC_RX_PORT_MASK       0x00300000
-#define KS_DESC_RX_MULTICAST           0x01000000
-#define KS_DESC_RX_ERROR               0x02000000
-#define KS_DESC_RX_ERROR_CSUM_UDP      0x04000000
-#define KS_DESC_RX_ERROR_CSUM_TCP      0x08000000
-#define KS_DESC_RX_ERROR_CSUM_IP       0x10000000
-#define KS_DESC_RX_LAST                        0x20000000
-#define KS_DESC_RX_FIRST               0x40000000
-#define KS_DESC_RX_ERROR_COND          \
-       (KS_DESC_RX_ERROR_CRC |         \
-       KS_DESC_RX_ERROR_RUNT |         \
-       KS_DESC_RX_ERROR_PHY |          \
-       KS_DESC_RX_ERROR_TOO_LONG)
-
-#define KS_DESC_HW_OWNED               0x80000000
-
-#define KS_DESC_BUF_SIZE               0x000007FF
-#define KS884X_DESC_TX_PORT_MASK       0x00300000
-#define KS_DESC_END_OF_RING            0x02000000
-#define KS_DESC_TX_CSUM_GEN_UDP                0x04000000
-#define KS_DESC_TX_CSUM_GEN_TCP                0x08000000
-#define KS_DESC_TX_CSUM_GEN_IP         0x10000000
-#define KS_DESC_TX_LAST                        0x20000000
-#define KS_DESC_TX_FIRST               0x40000000
-#define KS_DESC_TX_INTERRUPT           0x80000000
-
-#define KS_DESC_PORT_SHIFT             20
-
-#define KS_DESC_RX_MASK                        (KS_DESC_BUF_SIZE)
-
-#define KS_DESC_TX_MASK                        \
-       (KS_DESC_TX_INTERRUPT |         \
-       KS_DESC_TX_FIRST |              \
-       KS_DESC_TX_LAST |               \
-       KS_DESC_TX_CSUM_GEN_IP |        \
-       KS_DESC_TX_CSUM_GEN_TCP |       \
-       KS_DESC_TX_CSUM_GEN_UDP |       \
-       KS_DESC_BUF_SIZE)
-
-struct ksz_desc_rx_stat {
-#ifdef __BIG_ENDIAN_BITFIELD
-       u32 hw_owned:1;
-       u32 first_desc:1;
-       u32 last_desc:1;
-       u32 csum_err_ip:1;
-       u32 csum_err_tcp:1;
-       u32 csum_err_udp:1;
-       u32 error:1;
-       u32 multicast:1;
-       u32 src_port:4;
-       u32 err_phy:1;
-       u32 err_too_long:1;
-       u32 err_runt:1;
-       u32 err_crc:1;
-       u32 frame_type:1;
-       u32 reserved1:4;
-       u32 frame_len:11;
-#else
-       u32 frame_len:11;
-       u32 reserved1:4;
-       u32 frame_type:1;
-       u32 err_crc:1;
-       u32 err_runt:1;
-       u32 err_too_long:1;
-       u32 err_phy:1;
-       u32 src_port:4;
-       u32 multicast:1;
-       u32 error:1;
-       u32 csum_err_udp:1;
-       u32 csum_err_tcp:1;
-       u32 csum_err_ip:1;
-       u32 last_desc:1;
-       u32 first_desc:1;
-       u32 hw_owned:1;
-#endif
-};
-
-struct ksz_desc_tx_stat {
-#ifdef __BIG_ENDIAN_BITFIELD
-       u32 hw_owned:1;
-       u32 reserved1:31;
-#else
-       u32 reserved1:31;
-       u32 hw_owned:1;
-#endif
-};
-
-struct ksz_desc_rx_buf {
-#ifdef __BIG_ENDIAN_BITFIELD
-       u32 reserved4:6;
-       u32 end_of_ring:1;
-       u32 reserved3:14;
-       u32 buf_size:11;
-#else
-       u32 buf_size:11;
-       u32 reserved3:14;
-       u32 end_of_ring:1;
-       u32 reserved4:6;
-#endif
-};
-
-struct ksz_desc_tx_buf {
-#ifdef __BIG_ENDIAN_BITFIELD
-       u32 intr:1;
-       u32 first_seg:1;
-       u32 last_seg:1;
-       u32 csum_gen_ip:1;
-       u32 csum_gen_tcp:1;
-       u32 csum_gen_udp:1;
-       u32 end_of_ring:1;
-       u32 reserved4:1;
-       u32 dest_port:4;
-       u32 reserved3:9;
-       u32 buf_size:11;
-#else
-       u32 buf_size:11;
-       u32 reserved3:9;
-       u32 dest_port:4;
-       u32 reserved4:1;
-       u32 end_of_ring:1;
-       u32 csum_gen_udp:1;
-       u32 csum_gen_tcp:1;
-       u32 csum_gen_ip:1;
-       u32 last_seg:1;
-       u32 first_seg:1;
-       u32 intr:1;
-#endif
-};
-
-union desc_stat {
-       struct ksz_desc_rx_stat rx;
-       struct ksz_desc_tx_stat tx;
-       u32 data;
-};
-
-union desc_buf {
-       struct ksz_desc_rx_buf rx;
-       struct ksz_desc_tx_buf tx;
-       u32 data;
-};
-
-/**
- * struct ksz_hw_desc - Hardware descriptor data structure
- * @ctrl:      Descriptor control value.
- * @buf:       Descriptor buffer value.
- * @addr:      Physical address of memory buffer.
- * @next:      Pointer to next hardware descriptor.
- */
-struct ksz_hw_desc {
-       union desc_stat ctrl;
-       union desc_buf buf;
-       u32 addr;
-       u32 next;
-};
-
-/**
- * struct ksz_sw_desc - Software descriptor data structure
- * @ctrl:      Descriptor control value.
- * @buf:       Descriptor buffer value.
- * @buf_size:  Current buffers size value in hardware descriptor.
- */
-struct ksz_sw_desc {
-       union desc_stat ctrl;
-       union desc_buf buf;
-       u32 buf_size;
-};
-
-/**
- * struct ksz_dma_buf - OS dependent DMA buffer data structure
- * @skb:       Associated socket buffer.
- * @dma:       Associated physical DMA address.
- * len:                Actual len used.
- */
-struct ksz_dma_buf {
-       struct sk_buff *skb;
-       dma_addr_t dma;
-       int len;
-};
-
-/**
- * struct ksz_desc - Descriptor structure
- * @phw:       Hardware descriptor pointer to uncached physical memory.
- * @sw:                Cached memory to hold hardware descriptor values for
- *             manipulation.
- * @dma_buf:   Operating system dependent data structure to hold physical
- *             memory buffer allocation information.
- */
-struct ksz_desc {
-       struct ksz_hw_desc *phw;
-       struct ksz_sw_desc sw;
-       struct ksz_dma_buf dma_buf;
-};
-
-#define DMA_BUFFER(desc)  ((struct ksz_dma_buf *)(&(desc)->dma_buf))
-
-/**
- * struct ksz_desc_info - Descriptor information data structure
- * @ring:      First descriptor in the ring.
- * @cur:       Current descriptor being manipulated.
- * @ring_virt: First hardware descriptor in the ring.
- * @ring_phys: The physical address of the first descriptor of the ring.
- * @size:      Size of hardware descriptor.
- * @alloc:     Number of descriptors allocated.
- * @avail:     Number of descriptors available for use.
- * @last:      Index for last descriptor released to hardware.
- * @next:      Index for next descriptor available for use.
- * @mask:      Mask for index wrapping.
- */
-struct ksz_desc_info {
-       struct ksz_desc *ring;
-       struct ksz_desc *cur;
-       struct ksz_hw_desc *ring_virt;
-       u32 ring_phys;
-       int size;
-       int alloc;
-       int avail;
-       int last;
-       int next;
-       int mask;
-};
-
-/*
- * KSZ8842 switch definitions
- */
-
-enum {
-       TABLE_STATIC_MAC = 0,
-       TABLE_VLAN,
-       TABLE_DYNAMIC_MAC,
-       TABLE_MIB
-};
-
-#define LEARNED_MAC_TABLE_ENTRIES      1024
-#define STATIC_MAC_TABLE_ENTRIES       8
-
-/**
- * struct ksz_mac_table - Static MAC table data structure
- * @mac_addr:  MAC address to filter.
- * @vid:       VID value.
- * @fid:       FID value.
- * @ports:     Port membership.
- * @override:  Override setting.
- * @use_fid:   FID use setting.
- * @valid:     Valid setting indicating the entry is being used.
- */
-struct ksz_mac_table {
-       u8 mac_addr[MAC_ADDR_LEN];
-       u16 vid;
-       u8 fid;
-       u8 ports;
-       u8 override:1;
-       u8 use_fid:1;
-       u8 valid:1;
-};
-
-#define VLAN_TABLE_ENTRIES             16
-
-/**
- * struct ksz_vlan_table - VLAN table data structure
- * @vid:       VID value.
- * @fid:       FID value.
- * @member:    Port membership.
- */
-struct ksz_vlan_table {
-       u16 vid;
-       u8 fid;
-       u8 member;
-};
-
-#define DIFFSERV_ENTRIES               64
-#define PRIO_802_1P_ENTRIES            8
-#define PRIO_QUEUES                    4
-
-#define SWITCH_PORT_NUM                        2
-#define TOTAL_PORT_NUM                 (SWITCH_PORT_NUM + 1)
-#define HOST_MASK                      (1 << SWITCH_PORT_NUM)
-#define PORT_MASK                      7
-
-#define MAIN_PORT                      0
-#define OTHER_PORT                     1
-#define HOST_PORT                      SWITCH_PORT_NUM
-
-#define PORT_COUNTER_NUM               0x20
-#define TOTAL_PORT_COUNTER_NUM         (PORT_COUNTER_NUM + 2)
-
-#define MIB_COUNTER_RX_LO_PRIORITY     0x00
-#define MIB_COUNTER_RX_HI_PRIORITY     0x01
-#define MIB_COUNTER_RX_UNDERSIZE       0x02
-#define MIB_COUNTER_RX_FRAGMENT                0x03
-#define MIB_COUNTER_RX_OVERSIZE                0x04
-#define MIB_COUNTER_RX_JABBER          0x05
-#define MIB_COUNTER_RX_SYMBOL_ERR      0x06
-#define MIB_COUNTER_RX_CRC_ERR         0x07
-#define MIB_COUNTER_RX_ALIGNMENT_ERR   0x08
-#define MIB_COUNTER_RX_CTRL_8808       0x09
-#define MIB_COUNTER_RX_PAUSE           0x0A
-#define MIB_COUNTER_RX_BROADCAST       0x0B
-#define MIB_COUNTER_RX_MULTICAST       0x0C
-#define MIB_COUNTER_RX_UNICAST         0x0D
-#define MIB_COUNTER_RX_OCTET_64                0x0E
-#define MIB_COUNTER_RX_OCTET_65_127    0x0F
-#define MIB_COUNTER_RX_OCTET_128_255   0x10
-#define MIB_COUNTER_RX_OCTET_256_511   0x11
-#define MIB_COUNTER_RX_OCTET_512_1023  0x12
-#define MIB_COUNTER_RX_OCTET_1024_1522 0x13
-#define MIB_COUNTER_TX_LO_PRIORITY     0x14
-#define MIB_COUNTER_TX_HI_PRIORITY     0x15
-#define MIB_COUNTER_TX_LATE_COLLISION  0x16
-#define MIB_COUNTER_TX_PAUSE           0x17
-#define MIB_COUNTER_TX_BROADCAST       0x18
-#define MIB_COUNTER_TX_MULTICAST       0x19
-#define MIB_COUNTER_TX_UNICAST         0x1A
-#define MIB_COUNTER_TX_DEFERRED                0x1B
-#define MIB_COUNTER_TX_TOTAL_COLLISION 0x1C
-#define MIB_COUNTER_TX_EXCESS_COLLISION        0x1D
-#define MIB_COUNTER_TX_SINGLE_COLLISION        0x1E
-#define MIB_COUNTER_TX_MULTI_COLLISION 0x1F
-
-#define MIB_COUNTER_RX_DROPPED_PACKET  0x20
-#define MIB_COUNTER_TX_DROPPED_PACKET  0x21
-
-/**
- * struct ksz_port_mib - Port MIB data structure
- * @cnt_ptr:   Current pointer to MIB counter index.
- * @link_down: Indication the link has just gone down.
- * @state:     Connection status of the port.
- * @mib_start: The starting counter index.  Some ports do not start at 0.
- * @counter:   64-bit MIB counter value.
- * @dropped:   Temporary buffer to remember last read packet dropped values.
- *
- * MIB counters needs to be read periodically so that counters do not get
- * overflowed and give incorrect values.  A right balance is needed to
- * satisfy this condition and not waste too much CPU time.
- *
- * It is pointless to read MIB counters when the port is disconnected.  The
- * @state provides the connection status so that MIB counters are read only
- * when the port is connected.  The @link_down indicates the port is just
- * disconnected so that all MIB counters are read one last time to update the
- * information.
- */
-struct ksz_port_mib {
-       u8 cnt_ptr;
-       u8 link_down;
-       u8 state;
-       u8 mib_start;
-
-       u64 counter[TOTAL_PORT_COUNTER_NUM];
-       u32 dropped[2];
-};
-
-/**
- * struct ksz_port_cfg - Port configuration data structure
- * @vid:       VID value.
- * @member:    Port membership.
- * @port_prio: Port priority.
- * @rx_rate:   Receive priority rate.
- * @tx_rate:   Transmit priority rate.
- * @stp_state: Current Spanning Tree Protocol state.
- */
-struct ksz_port_cfg {
-       u16 vid;
-       u8 member;
-       u8 port_prio;
-       u32 rx_rate[PRIO_QUEUES];
-       u32 tx_rate[PRIO_QUEUES];
-       int stp_state;
-};
-
-/**
- * struct ksz_switch - KSZ8842 switch data structure
- * @mac_table: MAC table entries information.
- * @vlan_table:        VLAN table entries information.
- * @port_cfg:  Port configuration information.
- * @diffserv:  DiffServ priority settings.  Possible values from 6-bit of ToS
- *             (bit7 ~ bit2) field.
- * @p_802_1p:  802.1P priority settings.  Possible values from 3-bit of 802.1p
- *             Tag priority field.
- * @br_addr:   Bridge address.  Used for STP.
- * @other_addr:        Other MAC address.  Used for multiple network device mode.
- * @broad_per: Broadcast storm percentage.
- * @member:    Current port membership.  Used for STP.
- */
-struct ksz_switch {
-       struct ksz_mac_table mac_table[STATIC_MAC_TABLE_ENTRIES];
-       struct ksz_vlan_table vlan_table[VLAN_TABLE_ENTRIES];
-       struct ksz_port_cfg port_cfg[TOTAL_PORT_NUM];
-
-       u8 diffserv[DIFFSERV_ENTRIES];
-       u8 p_802_1p[PRIO_802_1P_ENTRIES];
-
-       u8 br_addr[MAC_ADDR_LEN];
-       u8 other_addr[MAC_ADDR_LEN];
-
-       u8 broad_per;
-       u8 member;
-};
-
-#define TX_RATE_UNIT                   10000
-
-/**
- * struct ksz_port_info - Port information data structure
- * @state:     Connection status of the port.
- * @tx_rate:   Transmit rate divided by 10000 to get Mbit.
- * @duplex:    Duplex mode.
- * @advertised:        Advertised auto-negotiation setting.  Used to determine link.
- * @partner:   Auto-negotiation partner setting.  Used to determine link.
- * @port_id:   Port index to access actual hardware register.
- * @pdev:      Pointer to OS dependent network device.
- */
-struct ksz_port_info {
-       uint state;
-       uint tx_rate;
-       u8 duplex;
-       u8 advertised;
-       u8 partner;
-       u8 port_id;
-       void *pdev;
-};
-
-#define MAX_TX_HELD_SIZE               52000
-
-/* Hardware features and bug fixes. */
-#define LINK_INT_WORKING               (1 << 0)
-#define SMALL_PACKET_TX_BUG            (1 << 1)
-#define HALF_DUPLEX_SIGNAL_BUG         (1 << 2)
-#define RX_HUGE_FRAME                  (1 << 4)
-#define STP_SUPPORT                    (1 << 8)
-
-/* Software overrides. */
-#define PAUSE_FLOW_CTRL                        (1 << 0)
-#define FAST_AGING                     (1 << 1)
-
-/**
- * struct ksz_hw - KSZ884X hardware data structure
- * @io:                        Virtual address assigned.
- * @ksz_switch:                Pointer to KSZ8842 switch.
- * @port_info:         Port information.
- * @port_mib:          Port MIB information.
- * @dev_count:         Number of network devices this hardware supports.
- * @dst_ports:         Destination ports in switch for transmission.
- * @id:                        Hardware ID.  Used for display only.
- * @mib_cnt:           Number of MIB counters this hardware has.
- * @mib_port_cnt:      Number of ports with MIB counters.
- * @tx_cfg:            Cached transmit control settings.
- * @rx_cfg:            Cached receive control settings.
- * @intr_mask:         Current interrupt mask.
- * @intr_set:          Current interrup set.
- * @intr_blocked:      Interrupt blocked.
- * @rx_desc_info:      Receive descriptor information.
- * @tx_desc_info:      Transmit descriptor information.
- * @tx_int_cnt:                Transmit interrupt count.  Used for TX optimization.
- * @tx_int_mask:       Transmit interrupt mask.  Used for TX optimization.
- * @tx_size:           Transmit data size.  Used for TX optimization.
- *                     The maximum is defined by MAX_TX_HELD_SIZE.
- * @perm_addr:         Permanent MAC address.
- * @override_addr:     Overrided MAC address.
- * @address:           Additional MAC address entries.
- * @addr_list_size:    Additional MAC address list size.
- * @mac_override:      Indication of MAC address overrided.
- * @promiscuous:       Counter to keep track of promiscuous mode set.
- * @all_multi:         Counter to keep track of all multicast mode set.
- * @multi_list:                Multicast address entries.
- * @multi_bits:                Cached multicast hash table settings.
- * @multi_list_size:   Multicast address list size.
- * @enabled:           Indication of hardware enabled.
- * @rx_stop:           Indication of receive process stop.
- * @features:          Hardware features to enable.
- * @overrides:         Hardware features to override.
- * @parent:            Pointer to parent, network device private structure.
- */
-struct ksz_hw {
-       void __iomem *io;
-
-       struct ksz_switch *ksz_switch;
-       struct ksz_port_info port_info[SWITCH_PORT_NUM];
-       struct ksz_port_mib port_mib[TOTAL_PORT_NUM];
-       int dev_count;
-       int dst_ports;
-       int id;
-       int mib_cnt;
-       int mib_port_cnt;
-
-       u32 tx_cfg;
-       u32 rx_cfg;
-       u32 intr_mask;
-       u32 intr_set;
-       uint intr_blocked;
-
-       struct ksz_desc_info rx_desc_info;
-       struct ksz_desc_info tx_desc_info;
-
-       int tx_int_cnt;
-       int tx_int_mask;
-       int tx_size;
-
-       u8 perm_addr[MAC_ADDR_LEN];
-       u8 override_addr[MAC_ADDR_LEN];
-       u8 address[ADDITIONAL_ENTRIES][MAC_ADDR_LEN];
-       u8 addr_list_size;
-       u8 mac_override;
-       u8 promiscuous;
-       u8 all_multi;
-       u8 multi_list[MAX_MULTICAST_LIST][MAC_ADDR_LEN];
-       u8 multi_bits[HW_MULTICAST_SIZE];
-       u8 multi_list_size;
-
-       u8 enabled;
-       u8 rx_stop;
-       u8 reserved2[1];
-
-       uint features;
-       uint overrides;
-
-       void *parent;
-};
-
-enum {
-       PHY_NO_FLOW_CTRL,
-       PHY_FLOW_CTRL,
-       PHY_TX_ONLY,
-       PHY_RX_ONLY
-};
-
-/**
- * struct ksz_port - Virtual port data structure
- * @duplex:            Duplex mode setting.  1 for half duplex, 2 for full
- *                     duplex, and 0 for auto, which normally results in full
- *                     duplex.
- * @speed:             Speed setting.  10 for 10 Mbit, 100 for 100 Mbit, and
- *                     0 for auto, which normally results in 100 Mbit.
- * @force_link:                Force link setting.  0 for auto-negotiation, and 1 for
- *                     force.
- * @flow_ctrl:         Flow control setting.  PHY_NO_FLOW_CTRL for no flow
- *                     control, and PHY_FLOW_CTRL for flow control.
- *                     PHY_TX_ONLY and PHY_RX_ONLY are not supported for 100
- *                     Mbit PHY.
- * @first_port:                Index of first port this port supports.
- * @mib_port_cnt:      Number of ports with MIB counters.
- * @port_cnt:          Number of ports this port supports.
- * @counter:           Port statistics counter.
- * @hw:                        Pointer to hardware structure.
- * @linked:            Pointer to port information linked to this port.
- */
-struct ksz_port {
-       u8 duplex;
-       u8 speed;
-       u8 force_link;
-       u8 flow_ctrl;
-
-       int first_port;
-       int mib_port_cnt;
-       int port_cnt;
-       u64 counter[OID_COUNTER_LAST];
-
-       struct ksz_hw *hw;
-       struct ksz_port_info *linked;
-};
-
-/**
- * struct ksz_timer_info - Timer information data structure
- * @timer:     Kernel timer.
- * @cnt:       Running timer counter.
- * @max:       Number of times to run timer; -1 for infinity.
- * @period:    Timer period in jiffies.
- */
-struct ksz_timer_info {
-       struct timer_list timer;
-       int cnt;
-       int max;
-       int period;
-};
-
-/**
- * struct ksz_shared_mem - OS dependent shared memory data structure
- * @dma_addr:  Physical DMA address allocated.
- * @alloc_size:        Allocation size.
- * @phys:      Actual physical address used.
- * @alloc_virt:        Virtual address allocated.
- * @virt:      Actual virtual address used.
- */
-struct ksz_shared_mem {
-       dma_addr_t dma_addr;
-       uint alloc_size;
-       uint phys;
-       u8 *alloc_virt;
-       u8 *virt;
-};
-
-/**
- * struct ksz_counter_info - OS dependent counter information data structure
- * @counter:   Wait queue to wakeup after counters are read.
- * @time:      Next time in jiffies to read counter.
- * @read:      Indication of counters read in full or not.
- */
-struct ksz_counter_info {
-       wait_queue_head_t counter;
-       unsigned long time;
-       int read;
-};
-
-/**
- * struct dev_info - Network device information data structure
- * @dev:               Pointer to network device.
- * @pdev:              Pointer to PCI device.
- * @hw:                        Hardware structure.
- * @desc_pool:         Physical memory used for descriptor pool.
- * @hwlock:            Spinlock to prevent hardware from accessing.
- * @lock:              Mutex lock to prevent device from accessing.
- * @dev_rcv:           Receive process function used.
- * @last_skb:          Socket buffer allocated for descriptor rx fragments.
- * @skb_index:         Buffer index for receiving fragments.
- * @skb_len:           Buffer length for receiving fragments.
- * @mib_read:          Workqueue to read MIB counters.
- * @mib_timer_info:    Timer to read MIB counters.
- * @counter:           Used for MIB reading.
- * @mtu:               Current MTU used.  The default is REGULAR_RX_BUF_SIZE;
- *                     the maximum is MAX_RX_BUF_SIZE.
- * @opened:            Counter to keep track of device open.
- * @rx_tasklet:                Receive processing tasklet.
- * @tx_tasklet:                Transmit processing tasklet.
- * @wol_enable:                Wake-on-LAN enable set by ethtool.
- * @wol_support:       Wake-on-LAN support used by ethtool.
- * @pme_wait:          Used for KSZ8841 power management.
- */
-struct dev_info {
-       struct net_device *dev;
-       struct pci_dev *pdev;
-
-       struct ksz_hw hw;
-       struct ksz_shared_mem desc_pool;
-
-       spinlock_t hwlock;
-       struct mutex lock;
-
-       int (*dev_rcv)(struct dev_info *);
-
-       struct sk_buff *last_skb;
-       int skb_index;
-       int skb_len;
-
-       struct work_struct mib_read;
-       struct ksz_timer_info mib_timer_info;
-       struct ksz_counter_info counter[TOTAL_PORT_NUM];
-
-       int mtu;
-       int opened;
-
-       struct tasklet_struct rx_tasklet;
-       struct tasklet_struct tx_tasklet;
-
-       int wol_enable;
-       int wol_support;
-       unsigned long pme_wait;
-};
-
-/**
- * struct dev_priv - Network device private data structure
- * @adapter:           Adapter device information.
- * @port:              Port information.
- * @monitor_time_info: Timer to monitor ports.
- * @proc_sem:          Semaphore for proc accessing.
- * @id:                        Device ID.
- * @mii_if:            MII interface information.
- * @advertising:       Temporary variable to store advertised settings.
- * @msg_enable:                The message flags controlling driver output.
- * @media_state:       The connection status of the device.
- * @multicast:         The all multicast state of the device.
- * @promiscuous:       The promiscuous state of the device.
- */
-struct dev_priv {
-       struct dev_info *adapter;
-       struct ksz_port port;
-       struct ksz_timer_info monitor_timer_info;
-
-       struct semaphore proc_sem;
-       int id;
-
-       struct mii_if_info mii_if;
-       u32 advertising;
-
-       u32 msg_enable;
-       int media_state;
-       int multicast;
-       int promiscuous;
-};
-
-#define DRV_NAME               "KSZ884X PCI"
-#define DEVICE_NAME            "KSZ884x PCI"
-#define DRV_VERSION            "1.0.0"
-#define DRV_RELDATE            "Feb 8, 2010"
-
-static char version[] __devinitdata =
-       "Micrel " DEVICE_NAME " " DRV_VERSION " (" DRV_RELDATE ")";
-
-static u8 DEFAULT_MAC_ADDRESS[] = { 0x00, 0x10, 0xA1, 0x88, 0x42, 0x01 };
-
-/*
- * Interrupt processing primary routines
- */
-
-static inline void hw_ack_intr(struct ksz_hw *hw, uint interrupt)
-{
-       writel(interrupt, hw->io + KS884X_INTERRUPTS_STATUS);
-}
-
-static inline void hw_dis_intr(struct ksz_hw *hw)
-{
-       hw->intr_blocked = hw->intr_mask;
-       writel(0, hw->io + KS884X_INTERRUPTS_ENABLE);
-       hw->intr_set = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
-}
-
-static inline void hw_set_intr(struct ksz_hw *hw, uint interrupt)
-{
-       hw->intr_set = interrupt;
-       writel(interrupt, hw->io + KS884X_INTERRUPTS_ENABLE);
-}
-
-static inline void hw_ena_intr(struct ksz_hw *hw)
-{
-       hw->intr_blocked = 0;
-       hw_set_intr(hw, hw->intr_mask);
-}
-
-static inline void hw_dis_intr_bit(struct ksz_hw *hw, uint bit)
-{
-       hw->intr_mask &= ~(bit);
-}
-
-static inline void hw_turn_off_intr(struct ksz_hw *hw, uint interrupt)
-{
-       u32 read_intr;
-
-       read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
-       hw->intr_set = read_intr & ~interrupt;
-       writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
-       hw_dis_intr_bit(hw, interrupt);
-}
-
-/**
- * hw_turn_on_intr - turn on specified interrupts
- * @hw:        The hardware instance.
- * @bit:       The interrupt bits to be on.
- *
- * This routine turns on the specified interrupts in the interrupt mask so that
- * those interrupts will be enabled.
- */
-static void hw_turn_on_intr(struct ksz_hw *hw, u32 bit)
-{
-       hw->intr_mask |= bit;
-
-       if (!hw->intr_blocked)
-               hw_set_intr(hw, hw->intr_mask);
-}
-
-static inline void hw_ena_intr_bit(struct ksz_hw *hw, uint interrupt)
-{
-       u32 read_intr;
-
-       read_intr = readl(hw->io + KS884X_INTERRUPTS_ENABLE);
-       hw->intr_set = read_intr | interrupt;
-       writel(hw->intr_set, hw->io + KS884X_INTERRUPTS_ENABLE);
-}
-
-static inline void hw_read_intr(struct ksz_hw *hw, uint *status)
-{
-       *status = readl(hw->io + KS884X_INTERRUPTS_STATUS);
-       *status = *status & hw->intr_set;
-}
-
-static inline void hw_restore_intr(struct ksz_hw *hw, uint interrupt)
-{
-       if (interrupt)
-               hw_ena_intr(hw);
-}
-
-/**
- * hw_block_intr - block hardware interrupts
- *
- * This function blocks all interrupts of the hardware and returns the current
- * interrupt enable mask so that interrupts can be restored later.
- *
- * Return the current interrupt enable mask.
- */
-static uint hw_block_intr(struct ksz_hw *hw)
-{
-       uint interrupt = 0;
-
-       if (!hw->intr_blocked) {
-               hw_dis_intr(hw);
-               interrupt = hw->intr_blocked;
-       }
-       return interrupt;
-}
-
-/*
- * Hardware descriptor routines
- */
-
-static inline void reset_desc(struct ksz_desc *desc, union desc_stat status)
-{
-       status.rx.hw_owned = 0;
-       desc->phw->ctrl.data = cpu_to_le32(status.data);
-}
-
-static inline void release_desc(struct ksz_desc *desc)
-{
-       desc->sw.ctrl.tx.hw_owned = 1;
-       if (desc->sw.buf_size != desc->sw.buf.data) {
-               desc->sw.buf_size = desc->sw.buf.data;
-               desc->phw->buf.data = cpu_to_le32(desc->sw.buf.data);
-       }
-       desc->phw->ctrl.data = cpu_to_le32(desc->sw.ctrl.data);
-}
-
-static void get_rx_pkt(struct ksz_desc_info *info, struct ksz_desc **desc)
-{
-       *desc = &info->ring[info->last];
-       info->last++;
-       info->last &= info->mask;
-       info->avail--;
-       (*desc)->sw.buf.data &= ~KS_DESC_RX_MASK;
-}
-
-static inline void set_rx_buf(struct ksz_desc *desc, u32 addr)
-{
-       desc->phw->addr = cpu_to_le32(addr);
-}
-
-static inline void set_rx_len(struct ksz_desc *desc, u32 len)
-{
-       desc->sw.buf.rx.buf_size = len;
-}
-
-static inline void get_tx_pkt(struct ksz_desc_info *info,
-       struct ksz_desc **desc)
-{
-       *desc = &info->ring[info->next];
-       info->next++;
-       info->next &= info->mask;
-       info->avail--;
-       (*desc)->sw.buf.data &= ~KS_DESC_TX_MASK;
-}
-
-static inline void set_tx_buf(struct ksz_desc *desc, u32 addr)
-{
-       desc->phw->addr = cpu_to_le32(addr);
-}
-
-static inline void set_tx_len(struct ksz_desc *desc, u32 len)
-{
-       desc->sw.buf.tx.buf_size = len;
-}
-
-/* Switch functions */
-
-#define TABLE_READ                     0x10
-#define TABLE_SEL_SHIFT                        2
-
-#define HW_DELAY(hw, reg)                      \
-       do {                                    \
-               u16 dummy;                      \
-               dummy = readw(hw->io + reg);    \
-       } while (0)
-
-/**
- * sw_r_table - read 4 bytes of data from switch table
- * @hw:                The hardware instance.
- * @table:     The table selector.
- * @addr:      The address of the table entry.
- * @data:      Buffer to store the read data.
- *
- * This routine reads 4 bytes of data from the table of the switch.
- * Hardware interrupts are disabled to minimize corruption of read data.
- */
-static void sw_r_table(struct ksz_hw *hw, int table, u16 addr, u32 *data)
-{
-       u16 ctrl_addr;
-       uint interrupt;
-
-       ctrl_addr = (((table << TABLE_SEL_SHIFT) | TABLE_READ) << 8) | addr;
-
-       interrupt = hw_block_intr(hw);
-
-       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
-       HW_DELAY(hw, KS884X_IACR_OFFSET);
-       *data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
-
-       hw_restore_intr(hw, interrupt);
-}
-
-/**
- * sw_w_table_64 - write 8 bytes of data to the switch table
- * @hw:                The hardware instance.
- * @table:     The table selector.
- * @addr:      The address of the table entry.
- * @data_hi:   The high part of data to be written (bit63 ~ bit32).
- * @data_lo:   The low part of data to be written (bit31 ~ bit0).
- *
- * This routine writes 8 bytes of data to the table of the switch.
- * Hardware interrupts are disabled to minimize corruption of written data.
- */
-static void sw_w_table_64(struct ksz_hw *hw, int table, u16 addr, u32 data_hi,
-       u32 data_lo)
-{
-       u16 ctrl_addr;
-       uint interrupt;
-
-       ctrl_addr = ((table << TABLE_SEL_SHIFT) << 8) | addr;
-
-       interrupt = hw_block_intr(hw);
-
-       writel(data_hi, hw->io + KS884X_ACC_DATA_4_OFFSET);
-       writel(data_lo, hw->io + KS884X_ACC_DATA_0_OFFSET);
-
-       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
-       HW_DELAY(hw, KS884X_IACR_OFFSET);
-
-       hw_restore_intr(hw, interrupt);
-}
-
-/**
- * sw_w_sta_mac_table - write to the static MAC table
- * @hw:        The hardware instance.
- * @addr:      The address of the table entry.
- * @mac_addr:  The MAC address.
- * @ports:     The port members.
- * @override:  The flag to override the port receive/transmit settings.
- * @valid:     The flag to indicate entry is valid.
- * @use_fid:   The flag to indicate the FID is valid.
- * @fid:       The FID value.
- *
- * This routine writes an entry of the static MAC table of the switch.  It
- * calls sw_w_table_64() to write the data.
- */
-static void sw_w_sta_mac_table(struct ksz_hw *hw, u16 addr, u8 *mac_addr,
-       u8 ports, int override, int valid, int use_fid, u8 fid)
-{
-       u32 data_hi;
-       u32 data_lo;
-
-       data_lo = ((u32) mac_addr[2] << 24) |
-               ((u32) mac_addr[3] << 16) |
-               ((u32) mac_addr[4] << 8) | mac_addr[5];
-       data_hi = ((u32) mac_addr[0] << 8) | mac_addr[1];
-       data_hi |= (u32) ports << STATIC_MAC_FWD_PORTS_SHIFT;
-
-       if (override)
-               data_hi |= STATIC_MAC_TABLE_OVERRIDE;
-       if (use_fid) {
-               data_hi |= STATIC_MAC_TABLE_USE_FID;
-               data_hi |= (u32) fid << STATIC_MAC_FID_SHIFT;
-       }
-       if (valid)
-               data_hi |= STATIC_MAC_TABLE_VALID;
-
-       sw_w_table_64(hw, TABLE_STATIC_MAC, addr, data_hi, data_lo);
-}
-
-/**
- * sw_r_vlan_table - read from the VLAN table
- * @hw:        The hardware instance.
- * @addr:      The address of the table entry.
- * @vid:       Buffer to store the VID.
- * @fid:       Buffer to store the VID.
- * @member:    Buffer to store the port membership.
- *
- * This function reads an entry of the VLAN table of the switch.  It calls
- * sw_r_table() to get the data.
- *
- * Return 0 if the entry is valid; otherwise -1.
- */
-static int sw_r_vlan_table(struct ksz_hw *hw, u16 addr, u16 *vid, u8 *fid,
-       u8 *member)
-{
-       u32 data;
-
-       sw_r_table(hw, TABLE_VLAN, addr, &data);
-       if (data & VLAN_TABLE_VALID) {
-               *vid = (u16)(data & VLAN_TABLE_VID);
-               *fid = (u8)((data & VLAN_TABLE_FID) >> VLAN_TABLE_FID_SHIFT);
-               *member = (u8)((data & VLAN_TABLE_MEMBERSHIP) >>
-                       VLAN_TABLE_MEMBERSHIP_SHIFT);
-               return 0;
-       }
-       return -1;
-}
-
-/**
- * port_r_mib_cnt - read MIB counter
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @addr:      The address of the counter.
- * @cnt:       Buffer to store the counter.
- *
- * This routine reads a MIB counter of the port.
- * Hardware interrupts are disabled to minimize corruption of read data.
- */
-static void port_r_mib_cnt(struct ksz_hw *hw, int port, u16 addr, u64 *cnt)
-{
-       u32 data;
-       u16 ctrl_addr;
-       uint interrupt;
-       int timeout;
-
-       ctrl_addr = addr + PORT_COUNTER_NUM * port;
-
-       interrupt = hw_block_intr(hw);
-
-       ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ) << 8);
-       writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
-       HW_DELAY(hw, KS884X_IACR_OFFSET);
-
-       for (timeout = 100; timeout > 0; timeout--) {
-               data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
-
-               if (data & MIB_COUNTER_VALID) {
-                       if (data & MIB_COUNTER_OVERFLOW)
-                               *cnt += MIB_COUNTER_VALUE + 1;
-                       *cnt += data & MIB_COUNTER_VALUE;
-                       break;
-               }
-       }
-
-       hw_restore_intr(hw, interrupt);
-}
-
-/**
- * port_r_mib_pkt - read dropped packet counts
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @cnt:       Buffer to store the receive and transmit dropped packet counts.
- *
- * This routine reads the dropped packet counts of the port.
- * Hardware interrupts are disabled to minimize corruption of read data.
- */
-static void port_r_mib_pkt(struct ksz_hw *hw, int port, u32 *last, u64 *cnt)
-{
-       u32 cur;
-       u32 data;
-       u16 ctrl_addr;
-       uint interrupt;
-       int index;
-
-       index = KS_MIB_PACKET_DROPPED_RX_0 + port;
-       do {
-               interrupt = hw_block_intr(hw);
-
-               ctrl_addr = (u16) index;
-               ctrl_addr |= (((TABLE_MIB << TABLE_SEL_SHIFT) | TABLE_READ)
-                       << 8);
-               writew(ctrl_addr, hw->io + KS884X_IACR_OFFSET);
-               HW_DELAY(hw, KS884X_IACR_OFFSET);
-               data = readl(hw->io + KS884X_ACC_DATA_0_OFFSET);
-
-               hw_restore_intr(hw, interrupt);
-
-               data &= MIB_PACKET_DROPPED;
-               cur = *last;
-               if (data != cur) {
-                       *last = data;
-                       if (data < cur)
-                               data += MIB_PACKET_DROPPED + 1;
-                       data -= cur;
-                       *cnt += data;
-               }
-               ++last;
-               ++cnt;
-               index -= KS_MIB_PACKET_DROPPED_TX -
-                       KS_MIB_PACKET_DROPPED_TX_0 + 1;
-       } while (index >= KS_MIB_PACKET_DROPPED_TX_0 + port);
-}
-
-/**
- * port_r_cnt - read MIB counters periodically
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine is used to read the counters of the port periodically to avoid
- * counter overflow.  The hardware should be acquired first before calling this
- * routine.
- *
- * Return non-zero when not all counters not read.
- */
-static int port_r_cnt(struct ksz_hw *hw, int port)
-{
-       struct ksz_port_mib *mib = &hw->port_mib[port];
-
-       if (mib->mib_start < PORT_COUNTER_NUM)
-               while (mib->cnt_ptr < PORT_COUNTER_NUM) {
-                       port_r_mib_cnt(hw, port, mib->cnt_ptr,
-                               &mib->counter[mib->cnt_ptr]);
-                       ++mib->cnt_ptr;
-               }
-       if (hw->mib_cnt > PORT_COUNTER_NUM)
-               port_r_mib_pkt(hw, port, mib->dropped,
-                       &mib->counter[PORT_COUNTER_NUM]);
-       mib->cnt_ptr = 0;
-       return 0;
-}
-
-/**
- * port_init_cnt - initialize MIB counter values
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine is used to initialize all counters to zero if the hardware
- * cannot do it after reset.
- */
-static void port_init_cnt(struct ksz_hw *hw, int port)
-{
-       struct ksz_port_mib *mib = &hw->port_mib[port];
-
-       mib->cnt_ptr = 0;
-       if (mib->mib_start < PORT_COUNTER_NUM)
-               do {
-                       port_r_mib_cnt(hw, port, mib->cnt_ptr,
-                               &mib->counter[mib->cnt_ptr]);
-                       ++mib->cnt_ptr;
-               } while (mib->cnt_ptr < PORT_COUNTER_NUM);
-       if (hw->mib_cnt > PORT_COUNTER_NUM)
-               port_r_mib_pkt(hw, port, mib->dropped,
-                       &mib->counter[PORT_COUNTER_NUM]);
-       memset((void *) mib->counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
-       mib->cnt_ptr = 0;
-}
-
-/*
- * Port functions
- */
-
-/**
- * port_chk - check port register bits
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the port register.
- * @bits:      The data bits to check.
- *
- * This function checks whether the specified bits of the port register are set
- * or not.
- *
- * Return 0 if the bits are not set.
- */
-static int port_chk(struct ksz_hw *hw, int port, int offset, u16 bits)
-{
-       u32 addr;
-       u16 data;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += offset;
-       data = readw(hw->io + addr);
-       return (data & bits) == bits;
-}
-
-/**
- * port_cfg - set port register bits
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the port register.
- * @bits:      The data bits to set.
- * @set:       The flag indicating whether the bits are to be set or not.
- *
- * This routine sets or resets the specified bits of the port register.
- */
-static void port_cfg(struct ksz_hw *hw, int port, int offset, u16 bits,
-       int set)
-{
-       u32 addr;
-       u16 data;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += offset;
-       data = readw(hw->io + addr);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       writew(data, hw->io + addr);
-}
-
-/**
- * port_chk_shift - check port bit
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the register.
- * @shift:     Number of bits to shift.
- *
- * This function checks whether the specified port is set in the register or
- * not.
- *
- * Return 0 if the port is not set.
- */
-static int port_chk_shift(struct ksz_hw *hw, int port, u32 addr, int shift)
-{
-       u16 data;
-       u16 bit = 1 << port;
-
-       data = readw(hw->io + addr);
-       data >>= shift;
-       return (data & bit) == bit;
-}
-
-/**
- * port_cfg_shift - set port bit
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the register.
- * @shift:     Number of bits to shift.
- * @set:       The flag indicating whether the port is to be set or not.
- *
- * This routine sets or resets the specified port in the register.
- */
-static void port_cfg_shift(struct ksz_hw *hw, int port, u32 addr, int shift,
-       int set)
-{
-       u16 data;
-       u16 bits = 1 << port;
-
-       data = readw(hw->io + addr);
-       bits <<= shift;
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       writew(data, hw->io + addr);
-}
-
-/**
- * port_r8 - read byte from port register
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the port register.
- * @data:      Buffer to store the data.
- *
- * This routine reads a byte from the port register.
- */
-static void port_r8(struct ksz_hw *hw, int port, int offset, u8 *data)
-{
-       u32 addr;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += offset;
-       *data = readb(hw->io + addr);
-}
-
-/**
- * port_r16 - read word from port register.
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the port register.
- * @data:      Buffer to store the data.
- *
- * This routine reads a word from the port register.
- */
-static void port_r16(struct ksz_hw *hw, int port, int offset, u16 *data)
-{
-       u32 addr;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += offset;
-       *data = readw(hw->io + addr);
-}
-
-/**
- * port_w16 - write word to port register.
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @offset:    The offset of the port register.
- * @data:      Data to write.
- *
- * This routine writes a word to the port register.
- */
-static void port_w16(struct ksz_hw *hw, int port, int offset, u16 data)
-{
-       u32 addr;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += offset;
-       writew(data, hw->io + addr);
-}
-
-/**
- * sw_chk - check switch register bits
- * @hw:        The hardware instance.
- * @addr:      The address of the switch register.
- * @bits:      The data bits to check.
- *
- * This function checks whether the specified bits of the switch register are
- * set or not.
- *
- * Return 0 if the bits are not set.
- */
-static int sw_chk(struct ksz_hw *hw, u32 addr, u16 bits)
-{
-       u16 data;
-
-       data = readw(hw->io + addr);
-       return (data & bits) == bits;
-}
-
-/**
- * sw_cfg - set switch register bits
- * @hw:        The hardware instance.
- * @addr:      The address of the switch register.
- * @bits:      The data bits to set.
- * @set:       The flag indicating whether the bits are to be set or not.
- *
- * This function sets or resets the specified bits of the switch register.
- */
-static void sw_cfg(struct ksz_hw *hw, u32 addr, u16 bits, int set)
-{
-       u16 data;
-
-       data = readw(hw->io + addr);
-       if (set)
-               data |= bits;
-       else
-               data &= ~bits;
-       writew(data, hw->io + addr);
-}
-
-/* Bandwidth */
-
-static inline void port_cfg_broad_storm(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM, set);
-}
-
-static inline int port_chk_broad_storm(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_BROADCAST_STORM);
-}
-
-/* Driver set switch broadcast storm protection at 10% rate. */
-#define BROADCAST_STORM_PROTECTION_RATE        10
-
-/* 148,800 frames * 67 ms / 100 */
-#define BROADCAST_STORM_VALUE          9969
-
-/**
- * sw_cfg_broad_storm - configure broadcast storm threshold
- * @hw:        The hardware instance.
- * @percent:   Broadcast storm threshold in percent of transmit rate.
- *
- * This routine configures the broadcast storm threshold of the switch.
- */
-static void sw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
-{
-       u16 data;
-       u32 value = ((u32) BROADCAST_STORM_VALUE * (u32) percent / 100);
-
-       if (value > BROADCAST_STORM_RATE)
-               value = BROADCAST_STORM_RATE;
-
-       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
-       data &= ~(BROADCAST_STORM_RATE_LO | BROADCAST_STORM_RATE_HI);
-       data |= ((value & 0x00FF) << 8) | ((value & 0xFF00) >> 8);
-       writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
-}
-
-/**
- * sw_get_board_storm - get broadcast storm threshold
- * @hw:        The hardware instance.
- * @percent:   Buffer to store the broadcast storm threshold percentage.
- *
- * This routine retrieves the broadcast storm threshold of the switch.
- */
-static void sw_get_broad_storm(struct ksz_hw *hw, u8 *percent)
-{
-       int num;
-       u16 data;
-
-       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
-       num = (data & BROADCAST_STORM_RATE_HI);
-       num <<= 8;
-       num |= (data & BROADCAST_STORM_RATE_LO) >> 8;
-       num = (num * 100 + BROADCAST_STORM_VALUE / 2) / BROADCAST_STORM_VALUE;
-       *percent = (u8) num;
-}
-
-/**
- * sw_dis_broad_storm - disable broadstorm
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine disables the broadcast storm limit function of the switch.
- */
-static void sw_dis_broad_storm(struct ksz_hw *hw, int port)
-{
-       port_cfg_broad_storm(hw, port, 0);
-}
-
-/**
- * sw_ena_broad_storm - enable broadcast storm
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine enables the broadcast storm limit function of the switch.
- */
-static void sw_ena_broad_storm(struct ksz_hw *hw, int port)
-{
-       sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
-       port_cfg_broad_storm(hw, port, 1);
-}
-
-/**
- * sw_init_broad_storm - initialize broadcast storm
- * @hw:        The hardware instance.
- *
- * This routine initializes the broadcast storm limit function of the switch.
- */
-static void sw_init_broad_storm(struct ksz_hw *hw)
-{
-       int port;
-
-       hw->ksz_switch->broad_per = 1;
-       sw_cfg_broad_storm(hw, hw->ksz_switch->broad_per);
-       for (port = 0; port < TOTAL_PORT_NUM; port++)
-               sw_dis_broad_storm(hw, port);
-       sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, MULTICAST_STORM_DISABLE, 1);
-}
-
-/**
- * hw_cfg_broad_storm - configure broadcast storm
- * @hw:        The hardware instance.
- * @percent:   Broadcast storm threshold in percent of transmit rate.
- *
- * This routine configures the broadcast storm threshold of the switch.
- * It is called by user functions.  The hardware should be acquired first.
- */
-static void hw_cfg_broad_storm(struct ksz_hw *hw, u8 percent)
-{
-       if (percent > 100)
-               percent = 100;
-
-       sw_cfg_broad_storm(hw, percent);
-       sw_get_broad_storm(hw, &percent);
-       hw->ksz_switch->broad_per = percent;
-}
-
-/**
- * sw_dis_prio_rate - disable switch priority rate
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine disables the priority rate function of the switch.
- */
-static void sw_dis_prio_rate(struct ksz_hw *hw, int port)
-{
-       u32 addr;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += KS8842_PORT_IN_RATE_OFFSET;
-       writel(0, hw->io + addr);
-}
-
-/**
- * sw_init_prio_rate - initialize switch prioirty rate
- * @hw:        The hardware instance.
- *
- * This routine initializes the priority rate function of the switch.
- */
-static void sw_init_prio_rate(struct ksz_hw *hw)
-{
-       int port;
-       int prio;
-       struct ksz_switch *sw = hw->ksz_switch;
-
-       for (port = 0; port < TOTAL_PORT_NUM; port++) {
-               for (prio = 0; prio < PRIO_QUEUES; prio++) {
-                       sw->port_cfg[port].rx_rate[prio] =
-                       sw->port_cfg[port].tx_rate[prio] = 0;
-               }
-               sw_dis_prio_rate(hw, port);
-       }
-}
-
-/* Communication */
-
-static inline void port_cfg_back_pressure(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE, set);
-}
-
-static inline void port_cfg_force_flow_ctrl(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL, set);
-}
-
-static inline int port_chk_back_pressure(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_BACK_PRESSURE);
-}
-
-static inline int port_chk_force_flow_ctrl(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_FORCE_FLOW_CTRL);
-}
-
-/* Spanning Tree */
-
-static inline void port_cfg_dis_learn(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_LEARN_DISABLE, set);
-}
-
-static inline void port_cfg_rx(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_RX_ENABLE, set);
-}
-
-static inline void port_cfg_tx(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_TX_ENABLE, set);
-}
-
-static inline void sw_cfg_fast_aging(struct ksz_hw *hw, int set)
-{
-       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET, SWITCH_FAST_AGING, set);
-}
-
-static inline void sw_flush_dyn_mac_table(struct ksz_hw *hw)
-{
-       if (!(hw->overrides & FAST_AGING)) {
-               sw_cfg_fast_aging(hw, 1);
-               mdelay(1);
-               sw_cfg_fast_aging(hw, 0);
-       }
-}
-
-/* VLAN */
-
-static inline void port_cfg_ins_tag(struct ksz_hw *hw, int p, int insert)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG, insert);
-}
-
-static inline void port_cfg_rmv_tag(struct ksz_hw *hw, int p, int remove)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG, remove);
-}
-
-static inline int port_chk_ins_tag(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_INSERT_TAG);
-}
-
-static inline int port_chk_rmv_tag(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_REMOVE_TAG);
-}
-
-static inline void port_cfg_dis_non_vid(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID, set);
-}
-
-static inline void port_cfg_in_filter(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER, set);
-}
-
-static inline int port_chk_dis_non_vid(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_DISCARD_NON_VID);
-}
-
-static inline int port_chk_in_filter(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_INGRESS_VLAN_FILTER);
-}
-
-/* Mirroring */
-
-static inline void port_cfg_mirror_sniffer(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_SNIFFER, set);
-}
-
-static inline void port_cfg_mirror_rx(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_RX, set);
-}
-
-static inline void port_cfg_mirror_tx(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_MIRROR_TX, set);
-}
-
-static inline void sw_cfg_mirror_rx_tx(struct ksz_hw *hw, int set)
-{
-       sw_cfg(hw, KS8842_SWITCH_CTRL_2_OFFSET, SWITCH_MIRROR_RX_TX, set);
-}
-
-static void sw_init_mirror(struct ksz_hw *hw)
-{
-       int port;
-
-       for (port = 0; port < TOTAL_PORT_NUM; port++) {
-               port_cfg_mirror_sniffer(hw, port, 0);
-               port_cfg_mirror_rx(hw, port, 0);
-               port_cfg_mirror_tx(hw, port, 0);
-       }
-       sw_cfg_mirror_rx_tx(hw, 0);
-}
-
-static inline void sw_cfg_unk_def_deliver(struct ksz_hw *hw, int set)
-{
-       sw_cfg(hw, KS8842_SWITCH_CTRL_7_OFFSET,
-               SWITCH_UNK_DEF_PORT_ENABLE, set);
-}
-
-static inline int sw_cfg_chk_unk_def_deliver(struct ksz_hw *hw)
-{
-       return sw_chk(hw, KS8842_SWITCH_CTRL_7_OFFSET,
-               SWITCH_UNK_DEF_PORT_ENABLE);
-}
-
-static inline void sw_cfg_unk_def_port(struct ksz_hw *hw, int port, int set)
-{
-       port_cfg_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0, set);
-}
-
-static inline int sw_chk_unk_def_port(struct ksz_hw *hw, int port)
-{
-       return port_chk_shift(hw, port, KS8842_SWITCH_CTRL_7_OFFSET, 0);
-}
-
-/* Priority */
-
-static inline void port_cfg_diffserv(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE, set);
-}
-
-static inline void port_cfg_802_1p(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE, set);
-}
-
-static inline void port_cfg_replace_vid(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING, set);
-}
-
-static inline void port_cfg_prio(struct ksz_hw *hw, int p, int set)
-{
-       port_cfg(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE, set);
-}
-
-static inline int port_chk_diffserv(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_DIFFSERV_ENABLE);
-}
-
-static inline int port_chk_802_1p(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_802_1P_ENABLE);
-}
-
-static inline int port_chk_replace_vid(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_2_OFFSET, PORT_USER_PRIORITY_CEILING);
-}
-
-static inline int port_chk_prio(struct ksz_hw *hw, int p)
-{
-       return port_chk(hw, p,
-               KS8842_PORT_CTRL_1_OFFSET, PORT_PRIO_QUEUE_ENABLE);
-}
-
-/**
- * sw_dis_diffserv - disable switch DiffServ priority
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine disables the DiffServ priority function of the switch.
- */
-static void sw_dis_diffserv(struct ksz_hw *hw, int port)
-{
-       port_cfg_diffserv(hw, port, 0);
-}
-
-/**
- * sw_dis_802_1p - disable switch 802.1p priority
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine disables the 802.1p priority function of the switch.
- */
-static void sw_dis_802_1p(struct ksz_hw *hw, int port)
-{
-       port_cfg_802_1p(hw, port, 0);
-}
-
-/**
- * sw_cfg_replace_null_vid -
- * @hw:        The hardware instance.
- * @set:       The flag to disable or enable.
- *
- */
-static void sw_cfg_replace_null_vid(struct ksz_hw *hw, int set)
-{
-       sw_cfg(hw, KS8842_SWITCH_CTRL_3_OFFSET, SWITCH_REPLACE_NULL_VID, set);
-}
-
-/**
- * sw_cfg_replace_vid - enable switch 802.10 priority re-mapping
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @set:       The flag to disable or enable.
- *
- * This routine enables the 802.1p priority re-mapping function of the switch.
- * That allows 802.1p priority field to be replaced with the port's default
- * tag's priority value if the ingress packet's 802.1p priority has a higher
- * priority than port's default tag's priority.
- */
-static void sw_cfg_replace_vid(struct ksz_hw *hw, int port, int set)
-{
-       port_cfg_replace_vid(hw, port, set);
-}
-
-/**
- * sw_cfg_port_based - configure switch port based priority
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @prio:      The priority to set.
- *
- * This routine configures the port based priority of the switch.
- */
-static void sw_cfg_port_based(struct ksz_hw *hw, int port, u8 prio)
-{
-       u16 data;
-
-       if (prio > PORT_BASED_PRIORITY_BASE)
-               prio = PORT_BASED_PRIORITY_BASE;
-
-       hw->ksz_switch->port_cfg[port].port_prio = prio;
-
-       port_r16(hw, port, KS8842_PORT_CTRL_1_OFFSET, &data);
-       data &= ~PORT_BASED_PRIORITY_MASK;
-       data |= prio << PORT_BASED_PRIORITY_SHIFT;
-       port_w16(hw, port, KS8842_PORT_CTRL_1_OFFSET, data);
-}
-
-/**
- * sw_dis_multi_queue - disable transmit multiple queues
- * @hw:        The hardware instance.
- * @port:      The port index.
- *
- * This routine disables the transmit multiple queues selection of the switch
- * port.  Only single transmit queue on the port.
- */
-static void sw_dis_multi_queue(struct ksz_hw *hw, int port)
-{
-       port_cfg_prio(hw, port, 0);
-}
-
-/**
- * sw_init_prio - initialize switch priority
- * @hw:        The hardware instance.
- *
- * This routine initializes the switch QoS priority functions.
- */
-static void sw_init_prio(struct ksz_hw *hw)
-{
-       int port;
-       int tos;
-       struct ksz_switch *sw = hw->ksz_switch;
-
-       /*
-        * Init all the 802.1p tag priority value to be assigned to different
-        * priority queue.
-        */
-       sw->p_802_1p[0] = 0;
-       sw->p_802_1p[1] = 0;
-       sw->p_802_1p[2] = 1;
-       sw->p_802_1p[3] = 1;
-       sw->p_802_1p[4] = 2;
-       sw->p_802_1p[5] = 2;
-       sw->p_802_1p[6] = 3;
-       sw->p_802_1p[7] = 3;
-
-       /*
-        * Init all the DiffServ priority value to be assigned to priority
-        * queue 0.
-        */
-       for (tos = 0; tos < DIFFSERV_ENTRIES; tos++)
-               sw->diffserv[tos] = 0;
-
-       /* All QoS functions disabled. */
-       for (port = 0; port < TOTAL_PORT_NUM; port++) {
-               sw_dis_multi_queue(hw, port);
-               sw_dis_diffserv(hw, port);
-               sw_dis_802_1p(hw, port);
-               sw_cfg_replace_vid(hw, port, 0);
-
-               sw->port_cfg[port].port_prio = 0;
-               sw_cfg_port_based(hw, port, sw->port_cfg[port].port_prio);
-       }
-       sw_cfg_replace_null_vid(hw, 0);
-}
-
-/**
- * port_get_def_vid - get port default VID.
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @vid:       Buffer to store the VID.
- *
- * This routine retrieves the default VID of the port.
- */
-static void port_get_def_vid(struct ksz_hw *hw, int port, u16 *vid)
-{
-       u32 addr;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += KS8842_PORT_CTRL_VID_OFFSET;
-       *vid = readw(hw->io + addr);
-}
-
-/**
- * sw_init_vlan - initialize switch VLAN
- * @hw:        The hardware instance.
- *
- * This routine initializes the VLAN function of the switch.
- */
-static void sw_init_vlan(struct ksz_hw *hw)
-{
-       int port;
-       int entry;
-       struct ksz_switch *sw = hw->ksz_switch;
-
-       /* Read 16 VLAN entries from device's VLAN table. */
-       for (entry = 0; entry < VLAN_TABLE_ENTRIES; entry++) {
-               sw_r_vlan_table(hw, entry,
-                       &sw->vlan_table[entry].vid,
-                       &sw->vlan_table[entry].fid,
-                       &sw->vlan_table[entry].member);
-       }
-
-       for (port = 0; port < TOTAL_PORT_NUM; port++) {
-               port_get_def_vid(hw, port, &sw->port_cfg[port].vid);
-               sw->port_cfg[port].member = PORT_MASK;
-       }
-}
-
-/**
- * sw_cfg_port_base_vlan - configure port-based VLAN membership
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @member:    The port-based VLAN membership.
- *
- * This routine configures the port-based VLAN membership of the port.
- */
-static void sw_cfg_port_base_vlan(struct ksz_hw *hw, int port, u8 member)
-{
-       u32 addr;
-       u8 data;
-
-       PORT_CTRL_ADDR(port, addr);
-       addr += KS8842_PORT_CTRL_2_OFFSET;
-
-       data = readb(hw->io + addr);
-       data &= ~PORT_VLAN_MEMBERSHIP;
-       data |= (member & PORT_MASK);
-       writeb(data, hw->io + addr);
-
-       hw->ksz_switch->port_cfg[port].member = member;
-}
-
-/**
- * sw_get_addr - get the switch MAC address.
- * @hw:        The hardware instance.
- * @mac_addr:  Buffer to store the MAC address.
- *
- * This function retrieves the MAC address of the switch.
- */
-static inline void sw_get_addr(struct ksz_hw *hw, u8 *mac_addr)
-{
-       int i;
-
-       for (i = 0; i < 6; i += 2) {
-               mac_addr[i] = readb(hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
-               mac_addr[1 + i] = readb(hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
-       }
-}
-
-/**
- * sw_set_addr - configure switch MAC address
- * @hw:        The hardware instance.
- * @mac_addr:  The MAC address.
- *
- * This function configures the MAC address of the switch.
- */
-static void sw_set_addr(struct ksz_hw *hw, u8 *mac_addr)
-{
-       int i;
-
-       for (i = 0; i < 6; i += 2) {
-               writeb(mac_addr[i], hw->io + KS8842_MAC_ADDR_0_OFFSET + i);
-               writeb(mac_addr[1 + i], hw->io + KS8842_MAC_ADDR_1_OFFSET + i);
-       }
-}
-
-/**
- * sw_set_global_ctrl - set switch global control
- * @hw:        The hardware instance.
- *
- * This routine sets the global control of the switch function.
- */
-static void sw_set_global_ctrl(struct ksz_hw *hw)
-{
-       u16 data;
-
-       /* Enable switch MII flow control. */
-       data = readw(hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
-       data |= SWITCH_FLOW_CTRL;
-       writew(data, hw->io + KS8842_SWITCH_CTRL_3_OFFSET);
-
-       data = readw(hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
-
-       /* Enable aggressive back off algorithm in half duplex mode. */
-       data |= SWITCH_AGGR_BACKOFF;
-
-       /* Enable automatic fast aging when link changed detected. */
-       data |= SWITCH_AGING_ENABLE;
-       data |= SWITCH_LINK_AUTO_AGING;
-
-       if (hw->overrides & FAST_AGING)
-               data |= SWITCH_FAST_AGING;
-       else
-               data &= ~SWITCH_FAST_AGING;
-       writew(data, hw->io + KS8842_SWITCH_CTRL_1_OFFSET);
-
-       data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
-
-       /* Enable no excessive collision drop. */
-       data |= NO_EXC_COLLISION_DROP;
-       writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
-}
-
-enum {
-       STP_STATE_DISABLED = 0,
-       STP_STATE_LISTENING,
-       STP_STATE_LEARNING,
-       STP_STATE_FORWARDING,
-       STP_STATE_BLOCKED,
-       STP_STATE_SIMPLE
-};
-
-/**
- * port_set_stp_state - configure port spanning tree state
- * @hw:        The hardware instance.
- * @port:      The port index.
- * @state:     The spanning tree state.
- *
- * This routine configures the spanning tree state of the port.
- */
-static void port_set_stp_state(struct ksz_hw *hw, int port, int state)
-{
-       u16 data;
-
-       port_r16(hw, port, KS8842_PORT_CTRL_2_OFFSET, &data);
-       switch (state) {
-       case STP_STATE_DISABLED:
-               data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
-               data |= PORT_LEARN_DISABLE;
-               break;
-       case STP_STATE_LISTENING:
-/*
- * No need to turn on transmit because of port direct mode.
- * Turning on receive is required if static MAC table is not setup.
- */
-               data &= ~PORT_TX_ENABLE;
-               data |= PORT_RX_ENABLE;
-               data |= PORT_LEARN_DISABLE;
-               break;
-       case STP_STATE_LEARNING:
-               data &= ~PORT_TX_ENABLE;
-               data |= PORT_RX_ENABLE;
-               data &= ~PORT_LEARN_DISABLE;
-               break;
-       case STP_STATE_FORWARDING:
-               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
-               data &= ~PORT_LEARN_DISABLE;
-               break;
-       case STP_STATE_BLOCKED:
-/*
- * Need to setup static MAC table with override to keep receiving BPDU
- * messages.  See sw_init_stp routine.
- */
-               data &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE);
-               data |= PORT_LEARN_DISABLE;
-               break;
-       case STP_STATE_SIMPLE:
-               data |= (PORT_TX_ENABLE | PORT_RX_ENABLE);
-               data |= PORT_LEARN_DISABLE;
-               break;
-       }
-       port_w16(hw, port, KS8842_PORT_CTRL_2_OFFSET, data);
-       hw->ksz_switch->port_cfg[port].stp_state = state;
-}
-
-#define STP_ENTRY                      0
-#define BROADCAST_ENTRY                        1
-#define BRIDGE_ADDR_ENTRY              2
-#define IPV6_ADDR_ENTRY                        3
-
-/**
- * sw_clr_sta_mac_table - clear static MAC table
- * @hw:        The hardware instance.
- *
- * This routine clears the static MAC table.
- */
-static void sw_clr_sta_mac_table(struct ksz_hw *hw)
-{
-       struct ksz_mac_table *entry;
-       int i;
-
-       for (i = 0; i < STATIC_MAC_TABLE_ENTRIES; i++) {
-               entry = &hw->ksz_switch->mac_table[i];
-               sw_w_sta_mac_table(hw, i,
-                       entry->mac_addr, entry->ports,
-                       entry->override, 0,
-                       entry->use_fid, entry->fid);
-       }
-}
-
-/**
- * sw_init_stp - initialize switch spanning tree support
- * @hw:        The hardware instance.
- *
- * This routine initializes the spanning tree support of the switch.
- */
-static void sw_init_stp(struct ksz_hw *hw)
-{
-       struct ksz_mac_table *entry;
-
-       entry = &hw->ksz_switch->mac_table[STP_ENTRY];
-       entry->mac_addr[0] = 0x01;
-       entry->mac_addr[1] = 0x80;
-       entry->mac_addr[2] = 0xC2;
-       entry->mac_addr[3] = 0x00;
-       entry->mac_addr[4] = 0x00;
-       entry->mac_addr[5] = 0x00;
-       entry->ports = HOST_MASK;
-       entry->override = 1;
-       entry->valid = 1;
-       sw_w_sta_mac_table(hw, STP_ENTRY,
-               entry->mac_addr, entry->ports,
-               entry->override, entry->valid,
-               entry->use_fid, entry->fid);
-}
-
-/**
- * sw_block_addr - block certain packets from the host port
- * @hw:        The hardware instance.
- *
- * This routine blocks certain packets from reaching to the host port.
- */
-static void sw_block_addr(struct ksz_hw *hw)
-{
-       struct ksz_mac_table *entry;
-       int i;
-
-       for (i = BROADCAST_ENTRY; i <= IPV6_ADDR_ENTRY; i++) {
-               entry = &hw->ksz_switch->mac_table[i];
-               entry->valid = 0;
-               sw_w_sta_mac_table(hw, i,
-                       entry->mac_addr, entry->ports,
-                       entry->override, entry->valid,
-                       entry->use_fid, entry->fid);
-       }
-}
-
-#define PHY_LINK_SUPPORT               \
-       (PHY_AUTO_NEG_ASYM_PAUSE |      \
-       PHY_AUTO_NEG_SYM_PAUSE |        \
-       PHY_AUTO_NEG_100BT4 |           \
-       PHY_AUTO_NEG_100BTX_FD |        \
-       PHY_AUTO_NEG_100BTX |           \
-       PHY_AUTO_NEG_10BT_FD |          \
-       PHY_AUTO_NEG_10BT)
-
-static inline void hw_r_phy_ctrl(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_w_phy_ctrl(struct ksz_hw *hw, int phy, u16 data)
-{
-       writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_r_phy_link_stat(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_STATUS_OFFSET);
-}
-
-static inline void hw_r_phy_auto_neg(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
-}
-
-static inline void hw_w_phy_auto_neg(struct ksz_hw *hw, int phy, u16 data)
-{
-       writew(data, hw->io + phy + KS884X_PHY_AUTO_NEG_OFFSET);
-}
-
-static inline void hw_r_phy_rem_cap(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_REMOTE_CAP_OFFSET);
-}
-
-static inline void hw_r_phy_crossover(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_w_phy_crossover(struct ksz_hw *hw, int phy, u16 data)
-{
-       writew(data, hw->io + phy + KS884X_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_r_phy_polarity(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_w_phy_polarity(struct ksz_hw *hw, int phy, u16 data)
-{
-       writew(data, hw->io + phy + KS884X_PHY_PHY_CTRL_OFFSET);
-}
-
-static inline void hw_r_phy_link_md(struct ksz_hw *hw, int phy, u16 *data)
-{
-       *data = readw(hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
-}
-
-static inline void hw_w_phy_link_md(struct ksz_hw *hw, int phy, u16 data)
-{
-       writew(data, hw->io + phy + KS884X_PHY_LINK_MD_OFFSET);
-}
-
-/**
- * hw_r_phy - read data from PHY register
- * @hw:        The hardware instance.
- * @port:      Port to read.
- * @reg:       PHY register to read.
- * @val:       Buffer to store the read data.
- *
- * This routine reads data from the PHY register.
- */
-static void hw_r_phy(struct ksz_hw *hw, int port, u16 reg, u16 *val)
-{
-       int phy;
-
-       phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
-       *val = readw(hw->io + phy);
-}
-
-/**
- * port_w_phy - write data to PHY register
- * @hw:        The hardware instance.
- * @port:      Port to write.
- * @reg:       PHY register to write.
- * @val:       Word data to write.
- *
- * This routine writes data to the PHY register.
- */
-static void hw_w_phy(struct ksz_hw *hw, int port, u16 reg, u16 val)
-{
-       int phy;
-
-       phy = KS884X_PHY_1_CTRL_OFFSET + port * PHY_CTRL_INTERVAL + reg;
-       writew(val, hw->io + phy);
-}
-
-/*
- * EEPROM access functions
- */
-
-#define AT93C_CODE                     0
-#define AT93C_WR_OFF                   0x00
-#define AT93C_WR_ALL                   0x10
-#define AT93C_ER_ALL                   0x20
-#define AT93C_WR_ON                    0x30
-
-#define AT93C_WRITE                    1
-#define AT93C_READ                     2
-#define AT93C_ERASE                    3
-
-#define EEPROM_DELAY                   4
-
-static inline void drop_gpio(struct ksz_hw *hw, u8 gpio)
-{
-       u16 data;
-
-       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
-       data &= ~gpio;
-       writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
-}
-
-static inline void raise_gpio(struct ksz_hw *hw, u8 gpio)
-{
-       u16 data;
-
-       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
-       data |= gpio;
-       writew(data, hw->io + KS884X_EEPROM_CTRL_OFFSET);
-}
-
-static inline u8 state_gpio(struct ksz_hw *hw, u8 gpio)
-{
-       u16 data;
-
-       data = readw(hw->io + KS884X_EEPROM_CTRL_OFFSET);
-       return (u8)(data & gpio);
-}
-
-static void eeprom_clk(struct ksz_hw *hw)
-{
-       raise_gpio(hw, EEPROM_SERIAL_CLOCK);
-       udelay(EEPROM_DELAY);
-       drop_gpio(hw, EEPROM_SERIAL_CLOCK);
-       udelay(EEPROM_DELAY);
-}
-
-static u16 spi_r(struct ksz_hw *hw)
-{
-       int i;
-       u16 temp = 0;
-
-       for (i = 15; i >= 0; i--) {
-               raise_gpio(hw, EEPROM_SERIAL_CLOCK);
-               udelay(EEPROM_DELAY);
-
-               temp |= (state_gpio(hw, EEPROM_DATA_IN)) ? 1 << i : 0;
-
-               drop_gpio(hw, EEPROM_SERIAL_CLOCK);
-               udelay(EEPROM_DELAY);
-       }
-       return temp;
-}
-
-static void spi_w(struct ksz_hw *hw, u16 data)
-{
-       int i;
-
-       for (i = 15; i >= 0; i--) {
-               (data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
-                       drop_gpio(hw, EEPROM_DATA_OUT);
-               eeprom_clk(hw);
-       }
-}
-
-static void spi_reg(struct ksz_hw *hw, u8 data, u8 reg)
-{
-       int i;
-
-       /* Initial start bit */
-       raise_gpio(hw, EEPROM_DATA_OUT);
-       eeprom_clk(hw);
-
-       /* AT93C operation */
-       for (i = 1; i >= 0; i--) {
-               (data & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
-                       drop_gpio(hw, EEPROM_DATA_OUT);
-               eeprom_clk(hw);
-       }
-
-       /* Address location */
-       for (i = 5; i >= 0; i--) {
-               (reg & (0x01 << i)) ? raise_gpio(hw, EEPROM_DATA_OUT) :
-                       drop_gpio(hw, EEPROM_DATA_OUT);
-               eeprom_clk(hw);
-       }
-}
-
-#define EEPROM_DATA_RESERVED           0
-#define EEPROM_DATA_MAC_ADDR_0         1
-#define EEPROM_DATA_MAC_ADDR_1         2
-#define EEPROM_DATA_MAC_ADDR_2         3
-#define EEPROM_DATA_SUBSYS_ID          4
-#define EEPROM_DATA_SUBSYS_VEN_ID      5
-#define EEPROM_DATA_PM_CAP             6
-
-/* User defined EEPROM data */
-#define EEPROM_DATA_OTHER_MAC_ADDR     9
-
-/**
- * eeprom_read - read from AT93C46 EEPROM
- * @hw:        The hardware instance.
- * @reg:       The register offset.
- *
- * This function reads a word from the AT93C46 EEPROM.
- *
- * Return the data value.
- */
-static u16 eeprom_read(struct ksz_hw *hw, u8 reg)
-{
-       u16 data;
-
-       raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
-
-       spi_reg(hw, AT93C_READ, reg);
-       data = spi_r(hw);
-
-       drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
-
-       return data;
-}
-
-/**
- * eeprom_write - write to AT93C46 EEPROM
- * @hw:        The hardware instance.
- * @reg:       The register offset.
- * @data:      The data value.
- *
- * This procedure writes a word to the AT93C46 EEPROM.
- */
-static void eeprom_write(struct ksz_hw *hw, u8 reg, u16 data)
-{
-       int timeout;
-
-       raise_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
-
-       /* Enable write. */
-       spi_reg(hw, AT93C_CODE, AT93C_WR_ON);
-       drop_gpio(hw, EEPROM_CHIP_SELECT);
-       udelay(1);
-
-       /* Erase the register. */
-       raise_gpio(hw, EEPROM_CHIP_SELECT);
-       spi_reg(hw, AT93C_ERASE, reg);
-       drop_gpio(hw, EEPROM_CHIP_SELECT);
-       udelay(1);
-
-       /* Check operation complete. */
-       raise_gpio(hw, EEPROM_CHIP_SELECT);
-       timeout = 8;
-       mdelay(2);
-       do {
-               mdelay(1);
-       } while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
-       drop_gpio(hw, EEPROM_CHIP_SELECT);
-       udelay(1);
-
-       /* Write the register. */
-       raise_gpio(hw, EEPROM_CHIP_SELECT);
-       spi_reg(hw, AT93C_WRITE, reg);
-       spi_w(hw, data);
-       drop_gpio(hw, EEPROM_CHIP_SELECT);
-       udelay(1);
-
-       /* Check operation complete. */
-       raise_gpio(hw, EEPROM_CHIP_SELECT);
-       timeout = 8;
-       mdelay(2);
-       do {
-               mdelay(1);
-       } while (!state_gpio(hw, EEPROM_DATA_IN) && --timeout);
-       drop_gpio(hw, EEPROM_CHIP_SELECT);
-       udelay(1);
-
-       /* Disable write. */
-       raise_gpio(hw, EEPROM_CHIP_SELECT);
-       spi_reg(hw, AT93C_CODE, AT93C_WR_OFF);
-
-       drop_gpio(hw, EEPROM_ACCESS_ENABLE | EEPROM_CHIP_SELECT);
-}
-
-/*
- * Link detection routines
- */
-
-static u16 advertised_flow_ctrl(struct ksz_port *port, u16 ctrl)
-{
-       ctrl &= ~PORT_AUTO_NEG_SYM_PAUSE;
-       switch (port->flow_ctrl) {
-       case PHY_FLOW_CTRL:
-               ctrl |= PORT_AUTO_NEG_SYM_PAUSE;
-               break;
-       /* Not supported. */
-       case PHY_TX_ONLY:
-       case PHY_RX_ONLY:
-       default:
-               break;
-       }
-       return ctrl;
-}
-
-static void set_flow_ctrl(struct ksz_hw *hw, int rx, int tx)
-{
-       u32 rx_cfg;
-       u32 tx_cfg;
-
-       rx_cfg = hw->rx_cfg;
-       tx_cfg = hw->tx_cfg;
-       if (rx)
-               hw->rx_cfg |= DMA_RX_FLOW_ENABLE;
-       else
-               hw->rx_cfg &= ~DMA_RX_FLOW_ENABLE;
-       if (tx)
-               hw->tx_cfg |= DMA_TX_FLOW_ENABLE;
-       else
-               hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
-       if (hw->enabled) {
-               if (rx_cfg != hw->rx_cfg)
-                       writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
-               if (tx_cfg != hw->tx_cfg)
-                       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
-       }
-}
-
-static void determine_flow_ctrl(struct ksz_hw *hw, struct ksz_port *port,
-       u16 local, u16 remote)
-{
-       int rx;
-       int tx;
-
-       if (hw->overrides & PAUSE_FLOW_CTRL)
-               return;
-
-       rx = tx = 0;
-       if (port->force_link)
-               rx = tx = 1;
-       if (remote & PHY_AUTO_NEG_SYM_PAUSE) {
-               if (local & PHY_AUTO_NEG_SYM_PAUSE) {
-                       rx = tx = 1;
-               } else if ((remote & PHY_AUTO_NEG_ASYM_PAUSE) &&
-                               (local & PHY_AUTO_NEG_PAUSE) ==
-                               PHY_AUTO_NEG_ASYM_PAUSE) {
-                       tx = 1;
-               }
-       } else if (remote & PHY_AUTO_NEG_ASYM_PAUSE) {
-               if ((local & PHY_AUTO_NEG_PAUSE) == PHY_AUTO_NEG_PAUSE)
-                       rx = 1;
-       }
-       if (!hw->ksz_switch)
-               set_flow_ctrl(hw, rx, tx);
-}
-
-static inline void port_cfg_change(struct ksz_hw *hw, struct ksz_port *port,
-       struct ksz_port_info *info, u16 link_status)
-{
-       if ((hw->features & HALF_DUPLEX_SIGNAL_BUG) &&
-                       !(hw->overrides & PAUSE_FLOW_CTRL)) {
-               u32 cfg = hw->tx_cfg;
-
-               /* Disable flow control in the half duplex mode. */
-               if (1 == info->duplex)
-                       hw->tx_cfg &= ~DMA_TX_FLOW_ENABLE;
-               if (hw->enabled && cfg != hw->tx_cfg)
-                       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
-       }
-}
-
-/**
- * port_get_link_speed - get current link status
- * @port:      The port instance.
- *
- * This routine reads PHY registers to determine the current link status of the
- * switch ports.
- */
-static void port_get_link_speed(struct ksz_port *port)
-{
-       uint interrupt;
-       struct ksz_port_info *info;
-       struct ksz_port_info *linked = NULL;
-       struct ksz_hw *hw = port->hw;
-       u16 data;
-       u16 status;
-       u8 local;
-       u8 remote;
-       int i;
-       int p;
-       int change = 0;
-
-       interrupt = hw_block_intr(hw);
-
-       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
-               info = &hw->port_info[p];
-               port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
-               port_r16(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
-
-               /*
-                * Link status is changing all the time even when there is no
-                * cable connection!
-                */
-               remote = status & (PORT_AUTO_NEG_COMPLETE |
-                       PORT_STATUS_LINK_GOOD);
-               local = (u8) data;
-
-               /* No change to status. */
-               if (local == info->advertised && remote == info->partner)
-                       continue;
-
-               info->advertised = local;
-               info->partner = remote;
-               if (status & PORT_STATUS_LINK_GOOD) {
-
-                       /* Remember the first linked port. */
-                       if (!linked)
-                               linked = info;
-
-                       info->tx_rate = 10 * TX_RATE_UNIT;
-                       if (status & PORT_STATUS_SPEED_100MBIT)
-                               info->tx_rate = 100 * TX_RATE_UNIT;
-
-                       info->duplex = 1;
-                       if (status & PORT_STATUS_FULL_DUPLEX)
-                               info->duplex = 2;
-
-                       if (media_connected != info->state) {
-                               hw_r_phy(hw, p, KS884X_PHY_AUTO_NEG_OFFSET,
-                                       &data);
-                               hw_r_phy(hw, p, KS884X_PHY_REMOTE_CAP_OFFSET,
-                                       &status);
-                               determine_flow_ctrl(hw, port, data, status);
-                               if (hw->ksz_switch) {
-                                       port_cfg_back_pressure(hw, p,
-                                               (1 == info->duplex));
-                               }
-                               change |= 1 << i;
-                               port_cfg_change(hw, port, info, status);
-                       }
-                       info->state = media_connected;
-               } else {
-                       if (media_disconnected != info->state) {
-                               change |= 1 << i;
-
-                               /* Indicate the link just goes down. */
-                               hw->port_mib[p].link_down = 1;
-                       }
-                       info->state = media_disconnected;
-               }
-               hw->port_mib[p].state = (u8) info->state;
-       }
-
-       if (linked && media_disconnected == port->linked->state)
-               port->linked = linked;
-
-       hw_restore_intr(hw, interrupt);
-}
-
-#define PHY_RESET_TIMEOUT              10
-
-/**
- * port_set_link_speed - set port speed
- * @port:      The port instance.
- *
- * This routine sets the link speed of the switch ports.
- */
-static void port_set_link_speed(struct ksz_port *port)
-{
-       struct ksz_port_info *info;
-       struct ksz_hw *hw = port->hw;
-       u16 data;
-       u16 cfg;
-       u8 status;
-       int i;
-       int p;
-
-       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
-               info = &hw->port_info[p];
-
-               port_r16(hw, p, KS884X_PORT_CTRL_4_OFFSET, &data);
-               port_r8(hw, p, KS884X_PORT_STATUS_OFFSET, &status);
-
-               cfg = 0;
-               if (status & PORT_STATUS_LINK_GOOD)
-                       cfg = data;
-
-               data |= PORT_AUTO_NEG_ENABLE;
-               data = advertised_flow_ctrl(port, data);
-
-               data |= PORT_AUTO_NEG_100BTX_FD | PORT_AUTO_NEG_100BTX |
-                       PORT_AUTO_NEG_10BT_FD | PORT_AUTO_NEG_10BT;
-
-               /* Check if manual configuration is specified by the user. */
-               if (port->speed || port->duplex) {
-                       if (10 == port->speed)
-                               data &= ~(PORT_AUTO_NEG_100BTX_FD |
-                                       PORT_AUTO_NEG_100BTX);
-                       else if (100 == port->speed)
-                               data &= ~(PORT_AUTO_NEG_10BT_FD |
-                                       PORT_AUTO_NEG_10BT);
-                       if (1 == port->duplex)
-                               data &= ~(PORT_AUTO_NEG_100BTX_FD |
-                                       PORT_AUTO_NEG_10BT_FD);
-                       else if (2 == port->duplex)
-                               data &= ~(PORT_AUTO_NEG_100BTX |
-                                       PORT_AUTO_NEG_10BT);
-               }
-               if (data != cfg) {
-                       data |= PORT_AUTO_NEG_RESTART;
-                       port_w16(hw, p, KS884X_PORT_CTRL_4_OFFSET, data);
-               }
-       }
-}
-
-/**
- * port_force_link_speed - force port speed
- * @port:      The port instance.
- *
- * This routine forces the link speed of the switch ports.
- */
-static void port_force_link_speed(struct ksz_port *port)
-{
-       struct ksz_hw *hw = port->hw;
-       u16 data;
-       int i;
-       int phy;
-       int p;
-
-       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
-               phy = KS884X_PHY_1_CTRL_OFFSET + p * PHY_CTRL_INTERVAL;
-               hw_r_phy_ctrl(hw, phy, &data);
-
-               data &= ~PHY_AUTO_NEG_ENABLE;
-
-               if (10 == port->speed)
-                       data &= ~PHY_SPEED_100MBIT;
-               else if (100 == port->speed)
-                       data |= PHY_SPEED_100MBIT;
-               if (1 == port->duplex)
-                       data &= ~PHY_FULL_DUPLEX;
-               else if (2 == port->duplex)
-                       data |= PHY_FULL_DUPLEX;
-               hw_w_phy_ctrl(hw, phy, data);
-       }
-}
-
-static void port_set_power_saving(struct ksz_port *port, int enable)
-{
-       struct ksz_hw *hw = port->hw;
-       int i;
-       int p;
-
-       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++)
-               port_cfg(hw, p,
-                       KS884X_PORT_CTRL_4_OFFSET, PORT_POWER_DOWN, enable);
-}
-
-/*
- * KSZ8841 power management functions
- */
-
-/**
- * hw_chk_wol_pme_status - check PMEN pin
- * @hw:        The hardware instance.
- *
- * This function is used to check PMEN pin is asserted.
- *
- * Return 1 if PMEN pin is asserted; otherwise, 0.
- */
-static int hw_chk_wol_pme_status(struct ksz_hw *hw)
-{
-       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
-       struct pci_dev *pdev = hw_priv->pdev;
-       u16 data;
-
-       if (!pdev->pm_cap)
-               return 0;
-       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
-       return (data & PCI_PM_CTRL_PME_STATUS) == PCI_PM_CTRL_PME_STATUS;
-}
-
-/**
- * hw_clr_wol_pme_status - clear PMEN pin
- * @hw:        The hardware instance.
- *
- * This routine is used to clear PME_Status to deassert PMEN pin.
- */
-static void hw_clr_wol_pme_status(struct ksz_hw *hw)
-{
-       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
-       struct pci_dev *pdev = hw_priv->pdev;
-       u16 data;
-
-       if (!pdev->pm_cap)
-               return;
-
-       /* Clear PME_Status to deassert PMEN pin. */
-       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
-       data |= PCI_PM_CTRL_PME_STATUS;
-       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
-}
-
-/**
- * hw_cfg_wol_pme - enable or disable Wake-on-LAN
- * @hw:        The hardware instance.
- * @set:       The flag indicating whether to enable or disable.
- *
- * This routine is used to enable or disable Wake-on-LAN.
- */
-static void hw_cfg_wol_pme(struct ksz_hw *hw, int set)
-{
-       struct dev_info *hw_priv = container_of(hw, struct dev_info, hw);
-       struct pci_dev *pdev = hw_priv->pdev;
-       u16 data;
-
-       if (!pdev->pm_cap)
-               return;
-       pci_read_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, &data);
-       data &= ~PCI_PM_CTRL_STATE_MASK;
-       if (set)
-               data |= PCI_PM_CTRL_PME_ENABLE | PCI_D3hot;
-       else
-               data &= ~PCI_PM_CTRL_PME_ENABLE;
-       pci_write_config_word(pdev, pdev->pm_cap + PCI_PM_CTRL, data);
-}
-
-/**
- * hw_cfg_wol - configure Wake-on-LAN features
- * @hw:        The hardware instance.
- * @frame:     The pattern frame bit.
- * @set:       The flag indicating whether to enable or disable.
- *
- * This routine is used to enable or disable certain Wake-on-LAN features.
- */
-static void hw_cfg_wol(struct ksz_hw *hw, u16 frame, int set)
-{
-       u16 data;
-
-       data = readw(hw->io + KS8841_WOL_CTRL_OFFSET);
-       if (set)
-               data |= frame;
-       else
-               data &= ~frame;
-       writew(data, hw->io + KS8841_WOL_CTRL_OFFSET);
-}
-
-/**
- * hw_set_wol_frame - program Wake-on-LAN pattern
- * @hw:        The hardware instance.
- * @i:         The frame index.
- * @mask_size: The size of the mask.
- * @mask:      Mask to ignore certain bytes in the pattern.
- * @frame_size:        The size of the frame.
- * @pattern:   The frame data.
- *
- * This routine is used to program Wake-on-LAN pattern.
- */
-static void hw_set_wol_frame(struct ksz_hw *hw, int i, uint mask_size,
-       const u8 *mask, uint frame_size, const u8 *pattern)
-{
-       int bits;
-       int from;
-       int len;
-       int to;
-       u32 crc;
-       u8 data[64];
-       u8 val = 0;
-
-       if (frame_size > mask_size * 8)
-               frame_size = mask_size * 8;
-       if (frame_size > 64)
-               frame_size = 64;
-
-       i *= 0x10;
-       writel(0, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i);
-       writel(0, hw->io + KS8841_WOL_FRAME_BYTE2_OFFSET + i);
-
-       bits = len = from = to = 0;
-       do {
-               if (bits) {
-                       if ((val & 1))
-                               data[to++] = pattern[from];
-                       val >>= 1;
-                       ++from;
-                       --bits;
-               } else {
-                       val = mask[len];
-                       writeb(val, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i
-                               + len);
-                       ++len;
-                       if (val)
-                               bits = 8;
-                       else
-                               from += 8;
-               }
-       } while (from < (int) frame_size);
-       if (val) {
-               bits = mask[len - 1];
-               val <<= (from % 8);
-               bits &= ~val;
-               writeb(bits, hw->io + KS8841_WOL_FRAME_BYTE0_OFFSET + i + len -
-                       1);
-       }
-       crc = ether_crc(to, data);
-       writel(crc, hw->io + KS8841_WOL_FRAME_CRC_OFFSET + i);
-}
-
-/**
- * hw_add_wol_arp - add ARP pattern
- * @hw:        The hardware instance.
- * @ip_addr:   The IPv4 address assigned to the device.
- *
- * This routine is used to add ARP pattern for waking up the host.
- */
-static void hw_add_wol_arp(struct ksz_hw *hw, const u8 *ip_addr)
-{
-       static const u8 mask[6] = { 0x3F, 0xF0, 0x3F, 0x00, 0xC0, 0x03 };
-       u8 pattern[42] = {
-               0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x08, 0x06,
-               0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00 };
-
-       memcpy(&pattern[38], ip_addr, 4);
-       hw_set_wol_frame(hw, 3, 6, mask, 42, pattern);
-}
-
-/**
- * hw_add_wol_bcast - add broadcast pattern
- * @hw:        The hardware instance.
- *
- * This routine is used to add broadcast pattern for waking up the host.
- */
-static void hw_add_wol_bcast(struct ksz_hw *hw)
-{
-       static const u8 mask[] = { 0x3F };
-       static const u8 pattern[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
-
-       hw_set_wol_frame(hw, 2, 1, mask, MAC_ADDR_LEN, pattern);
-}
-
-/**
- * hw_add_wol_mcast - add multicast pattern
- * @hw:        The hardware instance.
- *
- * This routine is used to add multicast pattern for waking up the host.
- *
- * It is assumed the multicast packet is the ICMPv6 neighbor solicitation used
- * by IPv6 ping command.  Note that multicast packets are filtred through the
- * multicast hash table, so not all multicast packets can wake up the host.
- */
-static void hw_add_wol_mcast(struct ksz_hw *hw)
-{
-       static const u8 mask[] = { 0x3F };
-       u8 pattern[] = { 0x33, 0x33, 0xFF, 0x00, 0x00, 0x00 };
-
-       memcpy(&pattern[3], &hw->override_addr[3], 3);
-       hw_set_wol_frame(hw, 1, 1, mask, 6, pattern);
-}
-
-/**
- * hw_add_wol_ucast - add unicast pattern
- * @hw:        The hardware instance.
- *
- * This routine is used to add unicast pattern to wakeup the host.
- *
- * It is assumed the unicast packet is directed to the device, as the hardware
- * can only receive them in normal case.
- */
-static void hw_add_wol_ucast(struct ksz_hw *hw)
-{
-       static const u8 mask[] = { 0x3F };
-
-       hw_set_wol_frame(hw, 0, 1, mask, MAC_ADDR_LEN, hw->override_addr);
-}
-
-/**
- * hw_enable_wol - enable Wake-on-LAN
- * @hw:        The hardware instance.
- * @wol_enable:        The Wake-on-LAN settings.
- * @net_addr:  The IPv4 address assigned to the device.
- *
- * This routine is used to enable Wake-on-LAN depending on driver settings.
- */
-static void hw_enable_wol(struct ksz_hw *hw, u32 wol_enable, const u8 *net_addr)
-{
-       hw_cfg_wol(hw, KS8841_WOL_MAGIC_ENABLE, (wol_enable & WAKE_MAGIC));
-       hw_cfg_wol(hw, KS8841_WOL_FRAME0_ENABLE, (wol_enable & WAKE_UCAST));
-       hw_add_wol_ucast(hw);
-       hw_cfg_wol(hw, KS8841_WOL_FRAME1_ENABLE, (wol_enable & WAKE_MCAST));
-       hw_add_wol_mcast(hw);
-       hw_cfg_wol(hw, KS8841_WOL_FRAME2_ENABLE, (wol_enable & WAKE_BCAST));
-       hw_cfg_wol(hw, KS8841_WOL_FRAME3_ENABLE, (wol_enable & WAKE_ARP));
-       hw_add_wol_arp(hw, net_addr);
-}
-
-/**
- * hw_init - check driver is correct for the hardware
- * @hw:        The hardware instance.
- *
- * This function checks the hardware is correct for this driver and sets the
- * hardware up for proper initialization.
- *
- * Return number of ports or 0 if not right.
- */
-static int hw_init(struct ksz_hw *hw)
-{
-       int rc = 0;
-       u16 data;
-       u16 revision;
-
-       /* Set bus speed to 125MHz. */
-       writew(BUS_SPEED_125_MHZ, hw->io + KS884X_BUS_CTRL_OFFSET);
-
-       /* Check KSZ884x chip ID. */
-       data = readw(hw->io + KS884X_CHIP_ID_OFFSET);
-
-       revision = (data & KS884X_REVISION_MASK) >> KS884X_REVISION_SHIFT;
-       data &= KS884X_CHIP_ID_MASK_41;
-       if (REG_CHIP_ID_41 == data)
-               rc = 1;
-       else if (REG_CHIP_ID_42 == data)
-               rc = 2;
-       else
-               return 0;
-
-       /* Setup hardware features or bug workarounds. */
-       if (revision <= 1) {
-               hw->features |= SMALL_PACKET_TX_BUG;
-               if (1 == rc)
-                       hw->features |= HALF_DUPLEX_SIGNAL_BUG;
-       }
-       return rc;
-}
-
-/**
- * hw_reset - reset the hardware
- * @hw:        The hardware instance.
- *
- * This routine resets the hardware.
- */
-static void hw_reset(struct ksz_hw *hw)
-{
-       writew(GLOBAL_SOFTWARE_RESET, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
-
-       /* Wait for device to reset. */
-       mdelay(10);
-
-       /* Write 0 to clear device reset. */
-       writew(0, hw->io + KS884X_GLOBAL_CTRL_OFFSET);
-}
-
-/**
- * hw_setup - setup the hardware
- * @hw:        The hardware instance.
- *
- * This routine setup the hardware for proper operation.
- */
-static void hw_setup(struct ksz_hw *hw)
-{
-#if SET_DEFAULT_LED
-       u16 data;
-
-       /* Change default LED mode. */
-       data = readw(hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
-       data &= ~LED_MODE;
-       data |= SET_DEFAULT_LED;
-       writew(data, hw->io + KS8842_SWITCH_CTRL_5_OFFSET);
-#endif
-
-       /* Setup transmit control. */
-       hw->tx_cfg = (DMA_TX_PAD_ENABLE | DMA_TX_CRC_ENABLE |
-               (DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_TX_ENABLE);
-
-       /* Setup receive control. */
-       hw->rx_cfg = (DMA_RX_BROADCAST | DMA_RX_UNICAST |
-               (DMA_BURST_DEFAULT << DMA_BURST_SHIFT) | DMA_RX_ENABLE);
-       hw->rx_cfg |= KS884X_DMA_RX_MULTICAST;
-
-       /* Hardware cannot handle UDP packet in IP fragments. */
-       hw->rx_cfg |= (DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
-
-       if (hw->all_multi)
-               hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
-       if (hw->promiscuous)
-               hw->rx_cfg |= DMA_RX_PROMISCUOUS;
-}
-
-/**
- * hw_setup_intr - setup interrupt mask
- * @hw:        The hardware instance.
- *
- * This routine setup the interrupt mask for proper operation.
- */
-static void hw_setup_intr(struct ksz_hw *hw)
-{
-       hw->intr_mask = KS884X_INT_MASK | KS884X_INT_RX_OVERRUN;
-}
-
-static void ksz_check_desc_num(struct ksz_desc_info *info)
-{
-#define MIN_DESC_SHIFT  2
-
-       int alloc = info->alloc;
-       int shift;
-
-       shift = 0;
-       while (!(alloc & 1)) {
-               shift++;
-               alloc >>= 1;
-       }
-       if (alloc != 1 || shift < MIN_DESC_SHIFT) {
-               pr_alert("Hardware descriptor numbers not right!\n");
-               while (alloc) {
-                       shift++;
-                       alloc >>= 1;
-               }
-               if (shift < MIN_DESC_SHIFT)
-                       shift = MIN_DESC_SHIFT;
-               alloc = 1 << shift;
-               info->alloc = alloc;
-       }
-       info->mask = info->alloc - 1;
-}
-
-static void hw_init_desc(struct ksz_desc_info *desc_info, int transmit)
-{
-       int i;
-       u32 phys = desc_info->ring_phys;
-       struct ksz_hw_desc *desc = desc_info->ring_virt;
-       struct ksz_desc *cur = desc_info->ring;
-       struct ksz_desc *previous = NULL;
-
-       for (i = 0; i < desc_info->alloc; i++) {
-               cur->phw = desc++;
-               phys += desc_info->size;
-               previous = cur++;
-               previous->phw->next = cpu_to_le32(phys);
-       }
-       previous->phw->next = cpu_to_le32(desc_info->ring_phys);
-       previous->sw.buf.rx.end_of_ring = 1;
-       previous->phw->buf.data = cpu_to_le32(previous->sw.buf.data);
-
-       desc_info->avail = desc_info->alloc;
-       desc_info->last = desc_info->next = 0;
-
-       desc_info->cur = desc_info->ring;
-}
-
-/**
- * hw_set_desc_base - set descriptor base addresses
- * @hw:        The hardware instance.
- * @tx_addr:   The transmit descriptor base.
- * @rx_addr:   The receive descriptor base.
- *
- * This routine programs the descriptor base addresses after reset.
- */
-static void hw_set_desc_base(struct ksz_hw *hw, u32 tx_addr, u32 rx_addr)
-{
-       /* Set base address of Tx/Rx descriptors. */
-       writel(tx_addr, hw->io + KS_DMA_TX_ADDR);
-       writel(rx_addr, hw->io + KS_DMA_RX_ADDR);
-}
-
-static void hw_reset_pkts(struct ksz_desc_info *info)
-{
-       info->cur = info->ring;
-       info->avail = info->alloc;
-       info->last = info->next = 0;
-}
-
-static inline void hw_resume_rx(struct ksz_hw *hw)
-{
-       writel(DMA_START, hw->io + KS_DMA_RX_START);
-}
-
-/**
- * hw_start_rx - start receiving
- * @hw:        The hardware instance.
- *
- * This routine starts the receive function of the hardware.
- */
-static void hw_start_rx(struct ksz_hw *hw)
-{
-       writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
-
-       /* Notify when the receive stops. */
-       hw->intr_mask |= KS884X_INT_RX_STOPPED;
-
-       writel(DMA_START, hw->io + KS_DMA_RX_START);
-       hw_ack_intr(hw, KS884X_INT_RX_STOPPED);
-       hw->rx_stop++;
-
-       /* Variable overflows. */
-       if (0 == hw->rx_stop)
-               hw->rx_stop = 2;
-}
-
-/*
- * hw_stop_rx - stop receiving
- * @hw:        The hardware instance.
- *
- * This routine stops the receive function of the hardware.
- */
-static void hw_stop_rx(struct ksz_hw *hw)
-{
-       hw->rx_stop = 0;
-       hw_turn_off_intr(hw, KS884X_INT_RX_STOPPED);
-       writel((hw->rx_cfg & ~DMA_RX_ENABLE), hw->io + KS_DMA_RX_CTRL);
-}
-
-/**
- * hw_start_tx - start transmitting
- * @hw:        The hardware instance.
- *
- * This routine starts the transmit function of the hardware.
- */
-static void hw_start_tx(struct ksz_hw *hw)
-{
-       writel(hw->tx_cfg, hw->io + KS_DMA_TX_CTRL);
-}
-
-/**
- * hw_stop_tx - stop transmitting
- * @hw:        The hardware instance.
- *
- * This routine stops the transmit function of the hardware.
- */
-static void hw_stop_tx(struct ksz_hw *hw)
-{
-       writel((hw->tx_cfg & ~DMA_TX_ENABLE), hw->io + KS_DMA_TX_CTRL);
-}
-
-/**
- * hw_disable - disable hardware
- * @hw:        The hardware instance.
- *
- * This routine disables the hardware.
- */
-static void hw_disable(struct ksz_hw *hw)
-{
-       hw_stop_rx(hw);
-       hw_stop_tx(hw);
-       hw->enabled = 0;
-}
-
-/**
- * hw_enable - enable hardware
- * @hw:        The hardware instance.
- *
- * This routine enables the hardware.
- */
-static void hw_enable(struct ksz_hw *hw)
-{
-       hw_start_tx(hw);
-       hw_start_rx(hw);
-       hw->enabled = 1;
-}
-
-/**
- * hw_alloc_pkt - allocate enough descriptors for transmission
- * @hw:        The hardware instance.
- * @length:    The length of the packet.
- * @physical:  Number of descriptors required.
- *
- * This function allocates descriptors for transmission.
- *
- * Return 0 if not successful; 1 for buffer copy; or number of descriptors.
- */
-static int hw_alloc_pkt(struct ksz_hw *hw, int length, int physical)
-{
-       /* Always leave one descriptor free. */
-       if (hw->tx_desc_info.avail <= 1)
-               return 0;
-
-       /* Allocate a descriptor for transmission and mark it current. */
-       get_tx_pkt(&hw->tx_desc_info, &hw->tx_desc_info.cur);
-       hw->tx_desc_info.cur->sw.buf.tx.first_seg = 1;
-
-       /* Keep track of number of transmit descriptors used so far. */
-       ++hw->tx_int_cnt;
-       hw->tx_size += length;
-
-       /* Cannot hold on too much data. */
-       if (hw->tx_size >= MAX_TX_HELD_SIZE)
-               hw->tx_int_cnt = hw->tx_int_mask + 1;
-
-       if (physical > hw->tx_desc_info.avail)
-               return 1;
-
-       return hw->tx_desc_info.avail;
-}
-
-/**
- * hw_send_pkt - mark packet for transmission
- * @hw:        The hardware instance.
- *
- * This routine marks the packet for transmission in PCI version.
- */
-static void hw_send_pkt(struct ksz_hw *hw)
-{
-       struct ksz_desc *cur = hw->tx_desc_info.cur;
-
-       cur->sw.buf.tx.last_seg = 1;
-
-       /* Interrupt only after specified number of descriptors used. */
-       if (hw->tx_int_cnt > hw->tx_int_mask) {
-               cur->sw.buf.tx.intr = 1;
-               hw->tx_int_cnt = 0;
-               hw->tx_size = 0;
-       }
-
-       /* KSZ8842 supports port directed transmission. */
-       cur->sw.buf.tx.dest_port = hw->dst_ports;
-
-       release_desc(cur);
-
-       writel(0, hw->io + KS_DMA_TX_START);
-}
-
-static int empty_addr(u8 *addr)
-{
-       u32 *addr1 = (u32 *) addr;
-       u16 *addr2 = (u16 *) &addr[4];
-
-       return 0 == *addr1 && 0 == *addr2;
-}
-
-/**
- * hw_set_addr - set MAC address
- * @hw:        The hardware instance.
- *
- * This routine programs the MAC address of the hardware when the address is
- * overrided.
- */
-static void hw_set_addr(struct ksz_hw *hw)
-{
-       int i;
-
-       for (i = 0; i < MAC_ADDR_LEN; i++)
-               writeb(hw->override_addr[MAC_ADDR_ORDER(i)],
-                       hw->io + KS884X_ADDR_0_OFFSET + i);
-
-       sw_set_addr(hw, hw->override_addr);
-}
-
-/**
- * hw_read_addr - read MAC address
- * @hw:        The hardware instance.
- *
- * This routine retrieves the MAC address of the hardware.
- */
-static void hw_read_addr(struct ksz_hw *hw)
-{
-       int i;
-
-       for (i = 0; i < MAC_ADDR_LEN; i++)
-               hw->perm_addr[MAC_ADDR_ORDER(i)] = readb(hw->io +
-                       KS884X_ADDR_0_OFFSET + i);
-
-       if (!hw->mac_override) {
-               memcpy(hw->override_addr, hw->perm_addr, MAC_ADDR_LEN);
-               if (empty_addr(hw->override_addr)) {
-                       memcpy(hw->perm_addr, DEFAULT_MAC_ADDRESS,
-                               MAC_ADDR_LEN);
-                       memcpy(hw->override_addr, DEFAULT_MAC_ADDRESS,
-                               MAC_ADDR_LEN);
-                       hw->override_addr[5] += hw->id;
-                       hw_set_addr(hw);
-               }
-       }
-}
-
-static void hw_ena_add_addr(struct ksz_hw *hw, int index, u8 *mac_addr)
-{
-       int i;
-       u32 mac_addr_lo;
-       u32 mac_addr_hi;
-
-       mac_addr_hi = 0;
-       for (i = 0; i < 2; i++) {
-               mac_addr_hi <<= 8;
-               mac_addr_hi |= mac_addr[i];
-       }
-       mac_addr_hi |= ADD_ADDR_ENABLE;
-       mac_addr_lo = 0;
-       for (i = 2; i < 6; i++) {
-               mac_addr_lo <<= 8;
-               mac_addr_lo |= mac_addr[i];
-       }
-       index *= ADD_ADDR_INCR;
-
-       writel(mac_addr_lo, hw->io + index + KS_ADD_ADDR_0_LO);
-       writel(mac_addr_hi, hw->io + index + KS_ADD_ADDR_0_HI);
-}
-
-static void hw_set_add_addr(struct ksz_hw *hw)
-{
-       int i;
-
-       for (i = 0; i < ADDITIONAL_ENTRIES; i++) {
-               if (empty_addr(hw->address[i]))
-                       writel(0, hw->io + ADD_ADDR_INCR * i +
-                               KS_ADD_ADDR_0_HI);
-               else
-                       hw_ena_add_addr(hw, i, hw->address[i]);
-       }
-}
-
-static int hw_add_addr(struct ksz_hw *hw, u8 *mac_addr)
-{
-       int i;
-       int j = ADDITIONAL_ENTRIES;
-
-       if (!memcmp(hw->override_addr, mac_addr, MAC_ADDR_LEN))
-               return 0;
-       for (i = 0; i < hw->addr_list_size; i++) {
-               if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN))
-                       return 0;
-               if (ADDITIONAL_ENTRIES == j && empty_addr(hw->address[i]))
-                       j = i;
-       }
-       if (j < ADDITIONAL_ENTRIES) {
-               memcpy(hw->address[j], mac_addr, MAC_ADDR_LEN);
-               hw_ena_add_addr(hw, j, hw->address[j]);
-               return 0;
-       }
-       return -1;
-}
-
-static int hw_del_addr(struct ksz_hw *hw, u8 *mac_addr)
-{
-       int i;
-
-       for (i = 0; i < hw->addr_list_size; i++) {
-               if (!memcmp(hw->address[i], mac_addr, MAC_ADDR_LEN)) {
-                       memset(hw->address[i], 0, MAC_ADDR_LEN);
-                       writel(0, hw->io + ADD_ADDR_INCR * i +
-                               KS_ADD_ADDR_0_HI);
-                       return 0;
-               }
-       }
-       return -1;
-}
-
-/**
- * hw_clr_multicast - clear multicast addresses
- * @hw:        The hardware instance.
- *
- * This routine removes all multicast addresses set in the hardware.
- */
-static void hw_clr_multicast(struct ksz_hw *hw)
-{
-       int i;
-
-       for (i = 0; i < HW_MULTICAST_SIZE; i++) {
-               hw->multi_bits[i] = 0;
-
-               writeb(0, hw->io + KS884X_MULTICAST_0_OFFSET + i);
-       }
-}
-
-/**
- * hw_set_grp_addr - set multicast addresses
- * @hw:        The hardware instance.
- *
- * This routine programs multicast addresses for the hardware to accept those
- * addresses.
- */
-static void hw_set_grp_addr(struct ksz_hw *hw)
-{
-       int i;
-       int index;
-       int position;
-       int value;
-
-       memset(hw->multi_bits, 0, sizeof(u8) * HW_MULTICAST_SIZE);
-
-       for (i = 0; i < hw->multi_list_size; i++) {
-               position = (ether_crc(6, hw->multi_list[i]) >> 26) & 0x3f;
-               index = position >> 3;
-               value = 1 << (position & 7);
-               hw->multi_bits[index] |= (u8) value;
-       }
-
-       for (i = 0; i < HW_MULTICAST_SIZE; i++)
-               writeb(hw->multi_bits[i], hw->io + KS884X_MULTICAST_0_OFFSET +
-                       i);
-}
-
-/**
- * hw_set_multicast - enable or disable all multicast receiving
- * @hw:        The hardware instance.
- * @multicast: To turn on or off the all multicast feature.
- *
- * This routine enables/disables the hardware to accept all multicast packets.
- */
-static void hw_set_multicast(struct ksz_hw *hw, u8 multicast)
-{
-       /* Stop receiving for reconfiguration. */
-       hw_stop_rx(hw);
-
-       if (multicast)
-               hw->rx_cfg |= DMA_RX_ALL_MULTICAST;
-       else
-               hw->rx_cfg &= ~DMA_RX_ALL_MULTICAST;
-
-       if (hw->enabled)
-               hw_start_rx(hw);
-}
-
-/**
- * hw_set_promiscuous - enable or disable promiscuous receiving
- * @hw:        The hardware instance.
- * @prom:      To turn on or off the promiscuous feature.
- *
- * This routine enables/disables the hardware to accept all packets.
- */
-static void hw_set_promiscuous(struct ksz_hw *hw, u8 prom)
-{
-       /* Stop receiving for reconfiguration. */
-       hw_stop_rx(hw);
-
-       if (prom)
-               hw->rx_cfg |= DMA_RX_PROMISCUOUS;
-       else
-               hw->rx_cfg &= ~DMA_RX_PROMISCUOUS;
-
-       if (hw->enabled)
-               hw_start_rx(hw);
-}
-
-/**
- * sw_enable - enable the switch
- * @hw:        The hardware instance.
- * @enable:    The flag to enable or disable the switch
- *
- * This routine is used to enable/disable the switch in KSZ8842.
- */
-static void sw_enable(struct ksz_hw *hw, int enable)
-{
-       int port;
-
-       for (port = 0; port < SWITCH_PORT_NUM; port++) {
-               if (hw->dev_count > 1) {
-                       /* Set port-base vlan membership with host port. */
-                       sw_cfg_port_base_vlan(hw, port,
-                               HOST_MASK | (1 << port));
-                       port_set_stp_state(hw, port, STP_STATE_DISABLED);
-               } else {
-                       sw_cfg_port_base_vlan(hw, port, PORT_MASK);
-                       port_set_stp_state(hw, port, STP_STATE_FORWARDING);
-               }
-       }
-       if (hw->dev_count > 1)
-               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
-       else
-               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_FORWARDING);
-
-       if (enable)
-               enable = KS8842_START;
-       writew(enable, hw->io + KS884X_CHIP_ID_OFFSET);
-}
-
-/**
- * sw_setup - setup the switch
- * @hw:        The hardware instance.
- *
- * This routine setup the hardware switch engine for default operation.
- */
-static void sw_setup(struct ksz_hw *hw)
-{
-       int port;
-
-       sw_set_global_ctrl(hw);
-
-       /* Enable switch broadcast storm protection at 10% percent rate. */
-       sw_init_broad_storm(hw);
-       hw_cfg_broad_storm(hw, BROADCAST_STORM_PROTECTION_RATE);
-       for (port = 0; port < SWITCH_PORT_NUM; port++)
-               sw_ena_broad_storm(hw, port);
-
-       sw_init_prio(hw);
-
-       sw_init_mirror(hw);
-
-       sw_init_prio_rate(hw);
-
-       sw_init_vlan(hw);
-
-       if (hw->features & STP_SUPPORT)
-               sw_init_stp(hw);
-       if (!sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                       SWITCH_TX_FLOW_CTRL | SWITCH_RX_FLOW_CTRL))
-               hw->overrides |= PAUSE_FLOW_CTRL;
-       sw_enable(hw, 1);
-}
-
-/**
- * ksz_start_timer - start kernel timer
- * @info:      Kernel timer information.
- * @time:      The time tick.
- *
- * This routine starts the kernel timer after the specified time tick.
- */
-static void ksz_start_timer(struct ksz_timer_info *info, int time)
-{
-       info->cnt = 0;
-       info->timer.expires = jiffies + time;
-       add_timer(&info->timer);
-
-       /* infinity */
-       info->max = -1;
-}
-
-/**
- * ksz_stop_timer - stop kernel timer
- * @info:      Kernel timer information.
- *
- * This routine stops the kernel timer.
- */
-static void ksz_stop_timer(struct ksz_timer_info *info)
-{
-       if (info->max) {
-               info->max = 0;
-               del_timer_sync(&info->timer);
-       }
-}
-
-static void ksz_init_timer(struct ksz_timer_info *info, int period,
-       void (*function)(unsigned long), void *data)
-{
-       info->max = 0;
-       info->period = period;
-       init_timer(&info->timer);
-       info->timer.function = function;
-       info->timer.data = (unsigned long) data;
-}
-
-static void ksz_update_timer(struct ksz_timer_info *info)
-{
-       ++info->cnt;
-       if (info->max > 0) {
-               if (info->cnt < info->max) {
-                       info->timer.expires = jiffies + info->period;
-                       add_timer(&info->timer);
-               } else
-                       info->max = 0;
-       } else if (info->max < 0) {
-               info->timer.expires = jiffies + info->period;
-               add_timer(&info->timer);
-       }
-}
-
-/**
- * ksz_alloc_soft_desc - allocate software descriptors
- * @desc_info: Descriptor information structure.
- * @transmit:  Indication that descriptors are for transmit.
- *
- * This local function allocates software descriptors for manipulation in
- * memory.
- *
- * Return 0 if successful.
- */
-static int ksz_alloc_soft_desc(struct ksz_desc_info *desc_info, int transmit)
-{
-       desc_info->ring = kmalloc(sizeof(struct ksz_desc) * desc_info->alloc,
-               GFP_KERNEL);
-       if (!desc_info->ring)
-               return 1;
-       memset((void *) desc_info->ring, 0,
-               sizeof(struct ksz_desc) * desc_info->alloc);
-       hw_init_desc(desc_info, transmit);
-       return 0;
-}
-
-/**
- * ksz_alloc_desc - allocate hardware descriptors
- * @adapter:   Adapter information structure.
- *
- * This local function allocates hardware descriptors for receiving and
- * transmitting.
- *
- * Return 0 if successful.
- */
-static int ksz_alloc_desc(struct dev_info *adapter)
-{
-       struct ksz_hw *hw = &adapter->hw;
-       int offset;
-
-       /* Allocate memory for RX & TX descriptors. */
-       adapter->desc_pool.alloc_size =
-               hw->rx_desc_info.size * hw->rx_desc_info.alloc +
-               hw->tx_desc_info.size * hw->tx_desc_info.alloc +
-               DESC_ALIGNMENT;
-
-       adapter->desc_pool.alloc_virt =
-               pci_alloc_consistent(
-                       adapter->pdev, adapter->desc_pool.alloc_size,
-                       &adapter->desc_pool.dma_addr);
-       if (adapter->desc_pool.alloc_virt == NULL) {
-               adapter->desc_pool.alloc_size = 0;
-               return 1;
-       }
-       memset(adapter->desc_pool.alloc_virt, 0, adapter->desc_pool.alloc_size);
-
-       /* Align to the next cache line boundary. */
-       offset = (((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT) ?
-               (DESC_ALIGNMENT -
-               ((ulong) adapter->desc_pool.alloc_virt % DESC_ALIGNMENT)) : 0);
-       adapter->desc_pool.virt = adapter->desc_pool.alloc_virt + offset;
-       adapter->desc_pool.phys = adapter->desc_pool.dma_addr + offset;
-
-       /* Allocate receive/transmit descriptors. */
-       hw->rx_desc_info.ring_virt = (struct ksz_hw_desc *)
-               adapter->desc_pool.virt;
-       hw->rx_desc_info.ring_phys = adapter->desc_pool.phys;
-       offset = hw->rx_desc_info.alloc * hw->rx_desc_info.size;
-       hw->tx_desc_info.ring_virt = (struct ksz_hw_desc *)
-               (adapter->desc_pool.virt + offset);
-       hw->tx_desc_info.ring_phys = adapter->desc_pool.phys + offset;
-
-       if (ksz_alloc_soft_desc(&hw->rx_desc_info, 0))
-               return 1;
-       if (ksz_alloc_soft_desc(&hw->tx_desc_info, 1))
-               return 1;
-
-       return 0;
-}
-
-/**
- * free_dma_buf - release DMA buffer resources
- * @adapter:   Adapter information structure.
- *
- * This routine is just a helper function to release the DMA buffer resources.
- */
-static void free_dma_buf(struct dev_info *adapter, struct ksz_dma_buf *dma_buf,
-       int direction)
-{
-       pci_unmap_single(adapter->pdev, dma_buf->dma, dma_buf->len, direction);
-       dev_kfree_skb(dma_buf->skb);
-       dma_buf->skb = NULL;
-       dma_buf->dma = 0;
-}
-
-/**
- * ksz_init_rx_buffers - initialize receive descriptors
- * @adapter:   Adapter information structure.
- *
- * This routine initializes DMA buffers for receiving.
- */
-static void ksz_init_rx_buffers(struct dev_info *adapter)
-{
-       int i;
-       struct ksz_desc *desc;
-       struct ksz_dma_buf *dma_buf;
-       struct ksz_hw *hw = &adapter->hw;
-       struct ksz_desc_info *info = &hw->rx_desc_info;
-
-       for (i = 0; i < hw->rx_desc_info.alloc; i++) {
-               get_rx_pkt(info, &desc);
-
-               dma_buf = DMA_BUFFER(desc);
-               if (dma_buf->skb && dma_buf->len != adapter->mtu)
-                       free_dma_buf(adapter, dma_buf, PCI_DMA_FROMDEVICE);
-               dma_buf->len = adapter->mtu;
-               if (!dma_buf->skb)
-                       dma_buf->skb = alloc_skb(dma_buf->len, GFP_ATOMIC);
-               if (dma_buf->skb && !dma_buf->dma) {
-                       dma_buf->skb->dev = adapter->dev;
-                       dma_buf->dma = pci_map_single(
-                               adapter->pdev,
-                               skb_tail_pointer(dma_buf->skb),
-                               dma_buf->len,
-                               PCI_DMA_FROMDEVICE);
-               }
-
-               /* Set descriptor. */
-               set_rx_buf(desc, dma_buf->dma);
-               set_rx_len(desc, dma_buf->len);
-               release_desc(desc);
-       }
-}
-
-/**
- * ksz_alloc_mem - allocate memory for hardware descriptors
- * @adapter:   Adapter information structure.
- *
- * This function allocates memory for use by hardware descriptors for receiving
- * and transmitting.
- *
- * Return 0 if successful.
- */
-static int ksz_alloc_mem(struct dev_info *adapter)
-{
-       struct ksz_hw *hw = &adapter->hw;
-
-       /* Determine the number of receive and transmit descriptors. */
-       hw->rx_desc_info.alloc = NUM_OF_RX_DESC;
-       hw->tx_desc_info.alloc = NUM_OF_TX_DESC;
-
-       /* Determine how many descriptors to skip transmit interrupt. */
-       hw->tx_int_cnt = 0;
-       hw->tx_int_mask = NUM_OF_TX_DESC / 4;
-       if (hw->tx_int_mask > 8)
-               hw->tx_int_mask = 8;
-       while (hw->tx_int_mask) {
-               hw->tx_int_cnt++;
-               hw->tx_int_mask >>= 1;
-       }
-       if (hw->tx_int_cnt) {
-               hw->tx_int_mask = (1 << (hw->tx_int_cnt - 1)) - 1;
-               hw->tx_int_cnt = 0;
-       }
-
-       /* Determine the descriptor size. */
-       hw->rx_desc_info.size =
-               (((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
-               DESC_ALIGNMENT) * DESC_ALIGNMENT);
-       hw->tx_desc_info.size =
-               (((sizeof(struct ksz_hw_desc) + DESC_ALIGNMENT - 1) /
-               DESC_ALIGNMENT) * DESC_ALIGNMENT);
-       if (hw->rx_desc_info.size != sizeof(struct ksz_hw_desc))
-               pr_alert("Hardware descriptor size not right!\n");
-       ksz_check_desc_num(&hw->rx_desc_info);
-       ksz_check_desc_num(&hw->tx_desc_info);
-
-       /* Allocate descriptors. */
-       if (ksz_alloc_desc(adapter))
-               return 1;
-
-       return 0;
-}
-
-/**
- * ksz_free_desc - free software and hardware descriptors
- * @adapter:   Adapter information structure.
- *
- * This local routine frees the software and hardware descriptors allocated by
- * ksz_alloc_desc().
- */
-static void ksz_free_desc(struct dev_info *adapter)
-{
-       struct ksz_hw *hw = &adapter->hw;
-
-       /* Reset descriptor. */
-       hw->rx_desc_info.ring_virt = NULL;
-       hw->tx_desc_info.ring_virt = NULL;
-       hw->rx_desc_info.ring_phys = 0;
-       hw->tx_desc_info.ring_phys = 0;
-
-       /* Free memory. */
-       if (adapter->desc_pool.alloc_virt)
-               pci_free_consistent(
-                       adapter->pdev,
-                       adapter->desc_pool.alloc_size,
-                       adapter->desc_pool.alloc_virt,
-                       adapter->desc_pool.dma_addr);
-
-       /* Reset resource pool. */
-       adapter->desc_pool.alloc_size = 0;
-       adapter->desc_pool.alloc_virt = NULL;
-
-       kfree(hw->rx_desc_info.ring);
-       hw->rx_desc_info.ring = NULL;
-       kfree(hw->tx_desc_info.ring);
-       hw->tx_desc_info.ring = NULL;
-}
-
-/**
- * ksz_free_buffers - free buffers used in the descriptors
- * @adapter:   Adapter information structure.
- * @desc_info: Descriptor information structure.
- *
- * This local routine frees buffers used in the DMA buffers.
- */
-static void ksz_free_buffers(struct dev_info *adapter,
-       struct ksz_desc_info *desc_info, int direction)
-{
-       int i;
-       struct ksz_dma_buf *dma_buf;
-       struct ksz_desc *desc = desc_info->ring;
-
-       for (i = 0; i < desc_info->alloc; i++) {
-               dma_buf = DMA_BUFFER(desc);
-               if (dma_buf->skb)
-                       free_dma_buf(adapter, dma_buf, direction);
-               desc++;
-       }
-}
-
-/**
- * ksz_free_mem - free all resources used by descriptors
- * @adapter:   Adapter information structure.
- *
- * This local routine frees all the resources allocated by ksz_alloc_mem().
- */
-static void ksz_free_mem(struct dev_info *adapter)
-{
-       /* Free transmit buffers. */
-       ksz_free_buffers(adapter, &adapter->hw.tx_desc_info,
-               PCI_DMA_TODEVICE);
-
-       /* Free receive buffers. */
-       ksz_free_buffers(adapter, &adapter->hw.rx_desc_info,
-               PCI_DMA_FROMDEVICE);
-
-       /* Free descriptors. */
-       ksz_free_desc(adapter);
-}
-
-static void get_mib_counters(struct ksz_hw *hw, int first, int cnt,
-       u64 *counter)
-{
-       int i;
-       int mib;
-       int port;
-       struct ksz_port_mib *port_mib;
-
-       memset(counter, 0, sizeof(u64) * TOTAL_PORT_COUNTER_NUM);
-       for (i = 0, port = first; i < cnt; i++, port++) {
-               port_mib = &hw->port_mib[port];
-               for (mib = port_mib->mib_start; mib < hw->mib_cnt; mib++)
-                       counter[mib] += port_mib->counter[mib];
-       }
-}
-
-/**
- * send_packet - send packet
- * @skb:       Socket buffer.
- * @dev:       Network device.
- *
- * This routine is used to send a packet out to the network.
- */
-static void send_packet(struct sk_buff *skb, struct net_device *dev)
-{
-       struct ksz_desc *desc;
-       struct ksz_desc *first;
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_desc_info *info = &hw->tx_desc_info;
-       struct ksz_dma_buf *dma_buf;
-       int len;
-       int last_frag = skb_shinfo(skb)->nr_frags;
-
-       /*
-        * KSZ8842 with multiple device interfaces needs to be told which port
-        * to send.
-        */
-       if (hw->dev_count > 1)
-               hw->dst_ports = 1 << priv->port.first_port;
-
-       /* Hardware will pad the length to 60. */
-       len = skb->len;
-
-       /* Remember the very first descriptor. */
-       first = info->cur;
-       desc = first;
-
-       dma_buf = DMA_BUFFER(desc);
-       if (last_frag) {
-               int frag;
-               skb_frag_t *this_frag;
-
-               dma_buf->len = skb_headlen(skb);
-
-               dma_buf->dma = pci_map_single(
-                       hw_priv->pdev, skb->data, dma_buf->len,
-                       PCI_DMA_TODEVICE);
-               set_tx_buf(desc, dma_buf->dma);
-               set_tx_len(desc, dma_buf->len);
-
-               frag = 0;
-               do {
-                       this_frag = &skb_shinfo(skb)->frags[frag];
-
-                       /* Get a new descriptor. */
-                       get_tx_pkt(info, &desc);
-
-                       /* Keep track of descriptors used so far. */
-                       ++hw->tx_int_cnt;
-
-                       dma_buf = DMA_BUFFER(desc);
-                       dma_buf->len = this_frag->size;
-
-                       dma_buf->dma = pci_map_single(
-                               hw_priv->pdev,
-                               page_address(this_frag->page) +
-                               this_frag->page_offset,
-                               dma_buf->len,
-                               PCI_DMA_TODEVICE);
-                       set_tx_buf(desc, dma_buf->dma);
-                       set_tx_len(desc, dma_buf->len);
-
-                       frag++;
-                       if (frag == last_frag)
-                               break;
-
-                       /* Do not release the last descriptor here. */
-                       release_desc(desc);
-               } while (1);
-
-               /* current points to the last descriptor. */
-               info->cur = desc;
-
-               /* Release the first descriptor. */
-               release_desc(first);
-       } else {
-               dma_buf->len = len;
-
-               dma_buf->dma = pci_map_single(
-                       hw_priv->pdev, skb->data, dma_buf->len,
-                       PCI_DMA_TODEVICE);
-               set_tx_buf(desc, dma_buf->dma);
-               set_tx_len(desc, dma_buf->len);
-       }
-
-       if (skb->ip_summed == CHECKSUM_PARTIAL) {
-               (desc)->sw.buf.tx.csum_gen_tcp = 1;
-               (desc)->sw.buf.tx.csum_gen_udp = 1;
-       }
-
-       /*
-        * The last descriptor holds the packet so that it can be returned to
-        * network subsystem after all descriptors are transmitted.
-        */
-       dma_buf->skb = skb;
-
-       hw_send_pkt(hw);
-
-       /* Update transmit statistics. */
-       dev->stats.tx_packets++;
-       dev->stats.tx_bytes += len;
-}
-
-/**
- * transmit_cleanup - clean up transmit descriptors
- * @dev:       Network device.
- *
- * This routine is called to clean up the transmitted buffers.
- */
-static void transmit_cleanup(struct dev_info *hw_priv, int normal)
-{
-       int last;
-       union desc_stat status;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_desc_info *info = &hw->tx_desc_info;
-       struct ksz_desc *desc;
-       struct ksz_dma_buf *dma_buf;
-       struct net_device *dev = NULL;
-
-       spin_lock(&hw_priv->hwlock);
-       last = info->last;
-
-       while (info->avail < info->alloc) {
-               /* Get next descriptor which is not hardware owned. */
-               desc = &info->ring[last];
-               status.data = le32_to_cpu(desc->phw->ctrl.data);
-               if (status.tx.hw_owned) {
-                       if (normal)
-                               break;
-                       else
-                               reset_desc(desc, status);
-               }
-
-               dma_buf = DMA_BUFFER(desc);
-               pci_unmap_single(
-                       hw_priv->pdev, dma_buf->dma, dma_buf->len,
-                       PCI_DMA_TODEVICE);
-
-               /* This descriptor contains the last buffer in the packet. */
-               if (dma_buf->skb) {
-                       dev = dma_buf->skb->dev;
-
-                       /* Release the packet back to network subsystem. */
-                       dev_kfree_skb_irq(dma_buf->skb);
-                       dma_buf->skb = NULL;
-               }
-
-               /* Free the transmitted descriptor. */
-               last++;
-               last &= info->mask;
-               info->avail++;
-       }
-       info->last = last;
-       spin_unlock(&hw_priv->hwlock);
-
-       /* Notify the network subsystem that the packet has been sent. */
-       if (dev)
-               dev->trans_start = jiffies;
-}
-
-/**
- * transmit_done - transmit done processing
- * @dev:       Network device.
- *
- * This routine is called when the transmit interrupt is triggered, indicating
- * either a packet is sent successfully or there are transmit errors.
- */
-static void tx_done(struct dev_info *hw_priv)
-{
-       struct ksz_hw *hw = &hw_priv->hw;
-       int port;
-
-       transmit_cleanup(hw_priv, 1);
-
-       for (port = 0; port < hw->dev_count; port++) {
-               struct net_device *dev = hw->port_info[port].pdev;
-
-               if (netif_running(dev) && netif_queue_stopped(dev))
-                       netif_wake_queue(dev);
-       }
-}
-
-static inline void copy_old_skb(struct sk_buff *old, struct sk_buff *skb)
-{
-       skb->dev = old->dev;
-       skb->protocol = old->protocol;
-       skb->ip_summed = old->ip_summed;
-       skb->csum = old->csum;
-       skb_set_network_header(skb, ETH_HLEN);
-
-       dev_kfree_skb(old);
-}
-
-/**
- * netdev_tx - send out packet
- * @skb:       Socket buffer.
- * @dev:       Network device.
- *
- * This function is used by the upper network layer to send out a packet.
- *
- * Return 0 if successful; otherwise an error code indicating failure.
- */
-static netdev_tx_t netdev_tx(struct sk_buff *skb, struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int left;
-       int num = 1;
-       int rc = 0;
-
-       if (hw->features & SMALL_PACKET_TX_BUG) {
-               struct sk_buff *org_skb = skb;
-
-               if (skb->len <= 48) {
-                       if (skb_end_pointer(skb) - skb->data >= 50) {
-                               memset(&skb->data[skb->len], 0, 50 - skb->len);
-                               skb->len = 50;
-                       } else {
-                               skb = dev_alloc_skb(50);
-                               if (!skb)
-                                       return NETDEV_TX_BUSY;
-                               memcpy(skb->data, org_skb->data, org_skb->len);
-                               memset(&skb->data[org_skb->len], 0,
-                                       50 - org_skb->len);
-                               skb->len = 50;
-                               copy_old_skb(org_skb, skb);
-                       }
-               }
-       }
-
-       spin_lock_irq(&hw_priv->hwlock);
-
-       num = skb_shinfo(skb)->nr_frags + 1;
-       left = hw_alloc_pkt(hw, skb->len, num);
-       if (left) {
-               if (left < num ||
-                               ((CHECKSUM_PARTIAL == skb->ip_summed) &&
-                               (ETH_P_IPV6 == htons(skb->protocol)))) {
-                       struct sk_buff *org_skb = skb;
-
-                       skb = dev_alloc_skb(org_skb->len);
-                       if (!skb) {
-                               rc = NETDEV_TX_BUSY;
-                               goto unlock;
-                       }
-                       skb_copy_and_csum_dev(org_skb, skb->data);
-                       org_skb->ip_summed = CHECKSUM_NONE;
-                       skb->len = org_skb->len;
-                       copy_old_skb(org_skb, skb);
-               }
-               send_packet(skb, dev);
-               if (left <= num)
-                       netif_stop_queue(dev);
-       } else {
-               /* Stop the transmit queue until packet is allocated. */
-               netif_stop_queue(dev);
-               rc = NETDEV_TX_BUSY;
-       }
-unlock:
-       spin_unlock_irq(&hw_priv->hwlock);
-
-       return rc;
-}
-
-/**
- * netdev_tx_timeout - transmit timeout processing
- * @dev:       Network device.
- *
- * This routine is called when the transmit timer expires.  That indicates the
- * hardware is not running correctly because transmit interrupts are not
- * triggered to free up resources so that the transmit routine can continue
- * sending out packets.  The hardware is reset to correct the problem.
- */
-static void netdev_tx_timeout(struct net_device *dev)
-{
-       static unsigned long last_reset;
-
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int port;
-
-       if (hw->dev_count > 1) {
-               /*
-                * Only reset the hardware if time between calls is long
-                * enough.
-                */
-               if (jiffies - last_reset <= dev->watchdog_timeo)
-                       hw_priv = NULL;
-       }
-
-       last_reset = jiffies;
-       if (hw_priv) {
-               hw_dis_intr(hw);
-               hw_disable(hw);
-
-               transmit_cleanup(hw_priv, 0);
-               hw_reset_pkts(&hw->rx_desc_info);
-               hw_reset_pkts(&hw->tx_desc_info);
-               ksz_init_rx_buffers(hw_priv);
-
-               hw_reset(hw);
-
-               hw_set_desc_base(hw,
-                       hw->tx_desc_info.ring_phys,
-                       hw->rx_desc_info.ring_phys);
-               hw_set_addr(hw);
-               if (hw->all_multi)
-                       hw_set_multicast(hw, hw->all_multi);
-               else if (hw->multi_list_size)
-                       hw_set_grp_addr(hw);
-
-               if (hw->dev_count > 1) {
-                       hw_set_add_addr(hw);
-                       for (port = 0; port < SWITCH_PORT_NUM; port++) {
-                               struct net_device *port_dev;
-
-                               port_set_stp_state(hw, port,
-                                       STP_STATE_DISABLED);
-
-                               port_dev = hw->port_info[port].pdev;
-                               if (netif_running(port_dev))
-                                       port_set_stp_state(hw, port,
-                                               STP_STATE_SIMPLE);
-                       }
-               }
-
-               hw_enable(hw);
-               hw_ena_intr(hw);
-       }
-
-       dev->trans_start = jiffies;
-       netif_wake_queue(dev);
-}
-
-static inline void csum_verified(struct sk_buff *skb)
-{
-       unsigned short protocol;
-       struct iphdr *iph;
-
-       protocol = skb->protocol;
-       skb_reset_network_header(skb);
-       iph = (struct iphdr *) skb_network_header(skb);
-       if (protocol == htons(ETH_P_8021Q)) {
-               protocol = iph->tot_len;
-               skb_set_network_header(skb, VLAN_HLEN);
-               iph = (struct iphdr *) skb_network_header(skb);
-       }
-       if (protocol == htons(ETH_P_IP)) {
-               if (iph->protocol == IPPROTO_TCP)
-                       skb->ip_summed = CHECKSUM_UNNECESSARY;
-       }
-}
-
-static inline int rx_proc(struct net_device *dev, struct ksz_hw* hw,
-       struct ksz_desc *desc, union desc_stat status)
-{
-       int packet_len;
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_dma_buf *dma_buf;
-       struct sk_buff *skb;
-       int rx_status;
-
-       /* Received length includes 4-byte CRC. */
-       packet_len = status.rx.frame_len - 4;
-
-       dma_buf = DMA_BUFFER(desc);
-       pci_dma_sync_single_for_cpu(
-               hw_priv->pdev, dma_buf->dma, packet_len + 4,
-               PCI_DMA_FROMDEVICE);
-
-       do {
-               /* skb->data != skb->head */
-               skb = dev_alloc_skb(packet_len + 2);
-               if (!skb) {
-                       dev->stats.rx_dropped++;
-                       return -ENOMEM;
-               }
-
-               /*
-                * Align socket buffer in 4-byte boundary for better
-                * performance.
-                */
-               skb_reserve(skb, 2);
-
-               memcpy(skb_put(skb, packet_len),
-                       dma_buf->skb->data, packet_len);
-       } while (0);
-
-       skb->protocol = eth_type_trans(skb, dev);
-
-       if (hw->rx_cfg & (DMA_RX_CSUM_UDP | DMA_RX_CSUM_TCP))
-               csum_verified(skb);
-
-       /* Update receive statistics. */
-       dev->stats.rx_packets++;
-       dev->stats.rx_bytes += packet_len;
-
-       /* Notify upper layer for received packet. */
-       rx_status = netif_rx(skb);
-
-       return 0;
-}
-
-static int dev_rcv_packets(struct dev_info *hw_priv)
-{
-       int next;
-       union desc_stat status;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct net_device *dev = hw->port_info[0].pdev;
-       struct ksz_desc_info *info = &hw->rx_desc_info;
-       int left = info->alloc;
-       struct ksz_desc *desc;
-       int received = 0;
-
-       next = info->next;
-       while (left--) {
-               /* Get next descriptor which is not hardware owned. */
-               desc = &info->ring[next];
-               status.data = le32_to_cpu(desc->phw->ctrl.data);
-               if (status.rx.hw_owned)
-                       break;
-
-               /* Status valid only when last descriptor bit is set. */
-               if (status.rx.last_desc && status.rx.first_desc) {
-                       if (rx_proc(dev, hw, desc, status))
-                               goto release_packet;
-                       received++;
-               }
-
-release_packet:
-               release_desc(desc);
-               next++;
-               next &= info->mask;
-       }
-       info->next = next;
-
-       return received;
-}
-
-static int port_rcv_packets(struct dev_info *hw_priv)
-{
-       int next;
-       union desc_stat status;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct net_device *dev = hw->port_info[0].pdev;
-       struct ksz_desc_info *info = &hw->rx_desc_info;
-       int left = info->alloc;
-       struct ksz_desc *desc;
-       int received = 0;
-
-       next = info->next;
-       while (left--) {
-               /* Get next descriptor which is not hardware owned. */
-               desc = &info->ring[next];
-               status.data = le32_to_cpu(desc->phw->ctrl.data);
-               if (status.rx.hw_owned)
-                       break;
-
-               if (hw->dev_count > 1) {
-                       /* Get received port number. */
-                       int p = HW_TO_DEV_PORT(status.rx.src_port);
-
-                       dev = hw->port_info[p].pdev;
-                       if (!netif_running(dev))
-                               goto release_packet;
-               }
-
-               /* Status valid only when last descriptor bit is set. */
-               if (status.rx.last_desc && status.rx.first_desc) {
-                       if (rx_proc(dev, hw, desc, status))
-                               goto release_packet;
-                       received++;
-               }
-
-release_packet:
-               release_desc(desc);
-               next++;
-               next &= info->mask;
-       }
-       info->next = next;
-
-       return received;
-}
-
-static int dev_rcv_special(struct dev_info *hw_priv)
-{
-       int next;
-       union desc_stat status;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct net_device *dev = hw->port_info[0].pdev;
-       struct ksz_desc_info *info = &hw->rx_desc_info;
-       int left = info->alloc;
-       struct ksz_desc *desc;
-       int received = 0;
-
-       next = info->next;
-       while (left--) {
-               /* Get next descriptor which is not hardware owned. */
-               desc = &info->ring[next];
-               status.data = le32_to_cpu(desc->phw->ctrl.data);
-               if (status.rx.hw_owned)
-                       break;
-
-               if (hw->dev_count > 1) {
-                       /* Get received port number. */
-                       int p = HW_TO_DEV_PORT(status.rx.src_port);
-
-                       dev = hw->port_info[p].pdev;
-                       if (!netif_running(dev))
-                               goto release_packet;
-               }
-
-               /* Status valid only when last descriptor bit is set. */
-               if (status.rx.last_desc && status.rx.first_desc) {
-                       /*
-                        * Receive without error.  With receive errors
-                        * disabled, packets with receive errors will be
-                        * dropped, so no need to check the error bit.
-                        */
-                       if (!status.rx.error || (status.data &
-                                       KS_DESC_RX_ERROR_COND) ==
-                                       KS_DESC_RX_ERROR_TOO_LONG) {
-                               if (rx_proc(dev, hw, desc, status))
-                                       goto release_packet;
-                               received++;
-                       } else {
-                               struct dev_priv *priv = netdev_priv(dev);
-
-                               /* Update receive error statistics. */
-                               priv->port.counter[OID_COUNTER_RCV_ERROR]++;
-                       }
-               }
-
-release_packet:
-               release_desc(desc);
-               next++;
-               next &= info->mask;
-       }
-       info->next = next;
-
-       return received;
-}
-
-static void rx_proc_task(unsigned long data)
-{
-       struct dev_info *hw_priv = (struct dev_info *) data;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       if (!hw->enabled)
-               return;
-       if (unlikely(!hw_priv->dev_rcv(hw_priv))) {
-
-               /* In case receive process is suspended because of overrun. */
-               hw_resume_rx(hw);
-
-               /* tasklets are interruptible. */
-               spin_lock_irq(&hw_priv->hwlock);
-               hw_turn_on_intr(hw, KS884X_INT_RX_MASK);
-               spin_unlock_irq(&hw_priv->hwlock);
-       } else {
-               hw_ack_intr(hw, KS884X_INT_RX);
-               tasklet_schedule(&hw_priv->rx_tasklet);
-       }
-}
-
-static void tx_proc_task(unsigned long data)
-{
-       struct dev_info *hw_priv = (struct dev_info *) data;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       hw_ack_intr(hw, KS884X_INT_TX_MASK);
-
-       tx_done(hw_priv);
-
-       /* tasklets are interruptible. */
-       spin_lock_irq(&hw_priv->hwlock);
-       hw_turn_on_intr(hw, KS884X_INT_TX);
-       spin_unlock_irq(&hw_priv->hwlock);
-}
-
-static inline void handle_rx_stop(struct ksz_hw *hw)
-{
-       /* Receive just has been stopped. */
-       if (0 == hw->rx_stop)
-               hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
-       else if (hw->rx_stop > 1) {
-               if (hw->enabled && (hw->rx_cfg & DMA_RX_ENABLE)) {
-                       hw_start_rx(hw);
-               } else {
-                       hw->intr_mask &= ~KS884X_INT_RX_STOPPED;
-                       hw->rx_stop = 0;
-               }
-       } else
-               /* Receive just has been started. */
-               hw->rx_stop++;
-}
-
-/**
- * netdev_intr - interrupt handling
- * @irq:       Interrupt number.
- * @dev_id:    Network device.
- *
- * This function is called by upper network layer to signal interrupt.
- *
- * Return IRQ_HANDLED if interrupt is handled.
- */
-static irqreturn_t netdev_intr(int irq, void *dev_id)
-{
-       uint int_enable = 0;
-       struct net_device *dev = (struct net_device *) dev_id;
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       hw_read_intr(hw, &int_enable);
-
-       /* Not our interrupt! */
-       if (!int_enable)
-               return IRQ_NONE;
-
-       do {
-               hw_ack_intr(hw, int_enable);
-               int_enable &= hw->intr_mask;
-
-               if (unlikely(int_enable & KS884X_INT_TX_MASK)) {
-                       hw_dis_intr_bit(hw, KS884X_INT_TX_MASK);
-                       tasklet_schedule(&hw_priv->tx_tasklet);
-               }
-
-               if (likely(int_enable & KS884X_INT_RX)) {
-                       hw_dis_intr_bit(hw, KS884X_INT_RX);
-                       tasklet_schedule(&hw_priv->rx_tasklet);
-               }
-
-               if (unlikely(int_enable & KS884X_INT_RX_OVERRUN)) {
-                       dev->stats.rx_fifo_errors++;
-                       hw_resume_rx(hw);
-               }
-
-               if (unlikely(int_enable & KS884X_INT_PHY)) {
-                       struct ksz_port *port = &priv->port;
-
-                       hw->features |= LINK_INT_WORKING;
-                       port_get_link_speed(port);
-               }
-
-               if (unlikely(int_enable & KS884X_INT_RX_STOPPED)) {
-                       handle_rx_stop(hw);
-                       break;
-               }
-
-               if (unlikely(int_enable & KS884X_INT_TX_STOPPED)) {
-                       u32 data;
-
-                       hw->intr_mask &= ~KS884X_INT_TX_STOPPED;
-                       pr_info("Tx stopped\n");
-                       data = readl(hw->io + KS_DMA_TX_CTRL);
-                       if (!(data & DMA_TX_ENABLE))
-                               pr_info("Tx disabled\n");
-                       break;
-               }
-       } while (0);
-
-       hw_ena_intr(hw);
-
-       return IRQ_HANDLED;
-}
-
-/*
- * Linux network device functions
- */
-
-static unsigned long next_jiffies;
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void netdev_netpoll(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-
-       hw_dis_intr(&hw_priv->hw);
-       netdev_intr(dev->irq, dev);
-}
-#endif
-
-static void bridge_change(struct ksz_hw *hw)
-{
-       int port;
-       u8  member;
-       struct ksz_switch *sw = hw->ksz_switch;
-
-       /* No ports in forwarding state. */
-       if (!sw->member) {
-               port_set_stp_state(hw, SWITCH_PORT_NUM, STP_STATE_SIMPLE);
-               sw_block_addr(hw);
-       }
-       for (port = 0; port < SWITCH_PORT_NUM; port++) {
-               if (STP_STATE_FORWARDING == sw->port_cfg[port].stp_state)
-                       member = HOST_MASK | sw->member;
-               else
-                       member = HOST_MASK | (1 << port);
-               if (member != sw->port_cfg[port].member)
-                       sw_cfg_port_base_vlan(hw, port, member);
-       }
-}
-
-/**
- * netdev_close - close network device
- * @dev:       Network device.
- *
- * This function process the close operation of network device.  This is caused
- * by the user command "ifconfig ethX down."
- *
- * Return 0 if successful; otherwise an error code indicating failure.
- */
-static int netdev_close(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_port *port = &priv->port;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int pi;
-
-       netif_stop_queue(dev);
-
-       ksz_stop_timer(&priv->monitor_timer_info);
-
-       /* Need to shut the port manually in multiple device interfaces mode. */
-       if (hw->dev_count > 1) {
-               port_set_stp_state(hw, port->first_port, STP_STATE_DISABLED);
-
-               /* Port is closed.  Need to change bridge setting. */
-               if (hw->features & STP_SUPPORT) {
-                       pi = 1 << port->first_port;
-                       if (hw->ksz_switch->member & pi) {
-                               hw->ksz_switch->member &= ~pi;
-                               bridge_change(hw);
-                       }
-               }
-       }
-       if (port->first_port > 0)
-               hw_del_addr(hw, dev->dev_addr);
-       if (!hw_priv->wol_enable)
-               port_set_power_saving(port, true);
-
-       if (priv->multicast)
-               --hw->all_multi;
-       if (priv->promiscuous)
-               --hw->promiscuous;
-
-       hw_priv->opened--;
-       if (!(hw_priv->opened)) {
-               ksz_stop_timer(&hw_priv->mib_timer_info);
-               flush_work(&hw_priv->mib_read);
-
-               hw_dis_intr(hw);
-               hw_disable(hw);
-               hw_clr_multicast(hw);
-
-               /* Delay for receive task to stop scheduling itself. */
-               msleep(2000 / HZ);
-
-               tasklet_disable(&hw_priv->rx_tasklet);
-               tasklet_disable(&hw_priv->tx_tasklet);
-               free_irq(dev->irq, hw_priv->dev);
-
-               transmit_cleanup(hw_priv, 0);
-               hw_reset_pkts(&hw->rx_desc_info);
-               hw_reset_pkts(&hw->tx_desc_info);
-
-               /* Clean out static MAC table when the switch is shutdown. */
-               if (hw->features & STP_SUPPORT)
-                       sw_clr_sta_mac_table(hw);
-       }
-
-       return 0;
-}
-
-static void hw_cfg_huge_frame(struct dev_info *hw_priv, struct ksz_hw *hw)
-{
-       if (hw->ksz_switch) {
-               u32 data;
-
-               data = readw(hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
-               if (hw->features & RX_HUGE_FRAME)
-                       data |= SWITCH_HUGE_PACKET;
-               else
-                       data &= ~SWITCH_HUGE_PACKET;
-               writew(data, hw->io + KS8842_SWITCH_CTRL_2_OFFSET);
-       }
-       if (hw->features & RX_HUGE_FRAME) {
-               hw->rx_cfg |= DMA_RX_ERROR;
-               hw_priv->dev_rcv = dev_rcv_special;
-       } else {
-               hw->rx_cfg &= ~DMA_RX_ERROR;
-               if (hw->dev_count > 1)
-                       hw_priv->dev_rcv = port_rcv_packets;
-               else
-                       hw_priv->dev_rcv = dev_rcv_packets;
-       }
-}
-
-static int prepare_hardware(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int rc = 0;
-
-       /* Remember the network device that requests interrupts. */
-       hw_priv->dev = dev;
-       rc = request_irq(dev->irq, netdev_intr, IRQF_SHARED, dev->name, dev);
-       if (rc)
-               return rc;
-       tasklet_enable(&hw_priv->rx_tasklet);
-       tasklet_enable(&hw_priv->tx_tasklet);
-
-       hw->promiscuous = 0;
-       hw->all_multi = 0;
-       hw->multi_list_size = 0;
-
-       hw_reset(hw);
-
-       hw_set_desc_base(hw,
-               hw->tx_desc_info.ring_phys, hw->rx_desc_info.ring_phys);
-       hw_set_addr(hw);
-       hw_cfg_huge_frame(hw_priv, hw);
-       ksz_init_rx_buffers(hw_priv);
-       return 0;
-}
-
-static void set_media_state(struct net_device *dev, int media_state)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-
-       if (media_state == priv->media_state)
-               netif_carrier_on(dev);
-       else
-               netif_carrier_off(dev);
-       netif_info(priv, link, dev, "link %s\n",
-                  media_state == priv->media_state ? "on" : "off");
-}
-
-/**
- * netdev_open - open network device
- * @dev:       Network device.
- *
- * This function process the open operation of network device.  This is caused
- * by the user command "ifconfig ethX up."
- *
- * Return 0 if successful; otherwise an error code indicating failure.
- */
-static int netdev_open(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port *port = &priv->port;
-       int i;
-       int p;
-       int rc = 0;
-
-       priv->multicast = 0;
-       priv->promiscuous = 0;
-
-       /* Reset device statistics. */
-       memset(&dev->stats, 0, sizeof(struct net_device_stats));
-       memset((void *) port->counter, 0,
-               (sizeof(u64) * OID_COUNTER_LAST));
-
-       if (!(hw_priv->opened)) {
-               rc = prepare_hardware(dev);
-               if (rc)
-                       return rc;
-               for (i = 0; i < hw->mib_port_cnt; i++) {
-                       if (next_jiffies < jiffies)
-                               next_jiffies = jiffies + HZ * 2;
-                       else
-                               next_jiffies += HZ * 1;
-                       hw_priv->counter[i].time = next_jiffies;
-                       hw->port_mib[i].state = media_disconnected;
-                       port_init_cnt(hw, i);
-               }
-               if (hw->ksz_switch)
-                       hw->port_mib[HOST_PORT].state = media_connected;
-               else {
-                       hw_add_wol_bcast(hw);
-                       hw_cfg_wol_pme(hw, 0);
-                       hw_clr_wol_pme_status(&hw_priv->hw);
-               }
-       }
-       port_set_power_saving(port, false);
-
-       for (i = 0, p = port->first_port; i < port->port_cnt; i++, p++) {
-               /*
-                * Initialize to invalid value so that link detection
-                * is done.
-                */
-               hw->port_info[p].partner = 0xFF;
-               hw->port_info[p].state = media_disconnected;
-       }
-
-       /* Need to open the port in multiple device interfaces mode. */
-       if (hw->dev_count > 1) {
-               port_set_stp_state(hw, port->first_port, STP_STATE_SIMPLE);
-               if (port->first_port > 0)
-                       hw_add_addr(hw, dev->dev_addr);
-       }
-
-       port_get_link_speed(port);
-       if (port->force_link)
-               port_force_link_speed(port);
-       else
-               port_set_link_speed(port);
-
-       if (!(hw_priv->opened)) {
-               hw_setup_intr(hw);
-               hw_enable(hw);
-               hw_ena_intr(hw);
-
-               if (hw->mib_port_cnt)
-                       ksz_start_timer(&hw_priv->mib_timer_info,
-                               hw_priv->mib_timer_info.period);
-       }
-
-       hw_priv->opened++;
-
-       ksz_start_timer(&priv->monitor_timer_info,
-               priv->monitor_timer_info.period);
-
-       priv->media_state = port->linked->state;
-
-       set_media_state(dev, media_connected);
-       netif_start_queue(dev);
-
-       return 0;
-}
-
-/* RX errors = rx_errors */
-/* RX dropped = rx_dropped */
-/* RX overruns = rx_fifo_errors */
-/* RX frame = rx_crc_errors + rx_frame_errors + rx_length_errors */
-/* TX errors = tx_errors */
-/* TX dropped = tx_dropped */
-/* TX overruns = tx_fifo_errors */
-/* TX carrier = tx_aborted_errors + tx_carrier_errors + tx_window_errors */
-/* collisions = collisions */
-
-/**
- * netdev_query_statistics - query network device statistics
- * @dev:       Network device.
- *
- * This function returns the statistics of the network device.  The device
- * needs not be opened.
- *
- * Return network device statistics.
- */
-static struct net_device_stats *netdev_query_statistics(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct ksz_port *port = &priv->port;
-       struct ksz_hw *hw = &priv->adapter->hw;
-       struct ksz_port_mib *mib;
-       int i;
-       int p;
-
-       dev->stats.rx_errors = port->counter[OID_COUNTER_RCV_ERROR];
-       dev->stats.tx_errors = port->counter[OID_COUNTER_XMIT_ERROR];
-
-       /* Reset to zero to add count later. */
-       dev->stats.multicast = 0;
-       dev->stats.collisions = 0;
-       dev->stats.rx_length_errors = 0;
-       dev->stats.rx_crc_errors = 0;
-       dev->stats.rx_frame_errors = 0;
-       dev->stats.tx_window_errors = 0;
-
-       for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
-               mib = &hw->port_mib[p];
-
-               dev->stats.multicast += (unsigned long)
-                       mib->counter[MIB_COUNTER_RX_MULTICAST];
-
-               dev->stats.collisions += (unsigned long)
-                       mib->counter[MIB_COUNTER_TX_TOTAL_COLLISION];
-
-               dev->stats.rx_length_errors += (unsigned long)(
-                       mib->counter[MIB_COUNTER_RX_UNDERSIZE] +
-                       mib->counter[MIB_COUNTER_RX_FRAGMENT] +
-                       mib->counter[MIB_COUNTER_RX_OVERSIZE] +
-                       mib->counter[MIB_COUNTER_RX_JABBER]);
-               dev->stats.rx_crc_errors += (unsigned long)
-                       mib->counter[MIB_COUNTER_RX_CRC_ERR];
-               dev->stats.rx_frame_errors += (unsigned long)(
-                       mib->counter[MIB_COUNTER_RX_ALIGNMENT_ERR] +
-                       mib->counter[MIB_COUNTER_RX_SYMBOL_ERR]);
-
-               dev->stats.tx_window_errors += (unsigned long)
-                       mib->counter[MIB_COUNTER_TX_LATE_COLLISION];
-       }
-
-       return &dev->stats;
-}
-
-/**
- * netdev_set_mac_address - set network device MAC address
- * @dev:       Network device.
- * @addr:      Buffer of MAC address.
- *
- * This function is used to set the MAC address of the network device.
- *
- * Return 0 to indicate success.
- */
-static int netdev_set_mac_address(struct net_device *dev, void *addr)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct sockaddr *mac = addr;
-       uint interrupt;
-
-       if (priv->port.first_port > 0)
-               hw_del_addr(hw, dev->dev_addr);
-       else {
-               hw->mac_override = 1;
-               memcpy(hw->override_addr, mac->sa_data, MAC_ADDR_LEN);
-       }
-
-       memcpy(dev->dev_addr, mac->sa_data, MAX_ADDR_LEN);
-
-       interrupt = hw_block_intr(hw);
-
-       if (priv->port.first_port > 0)
-               hw_add_addr(hw, dev->dev_addr);
-       else
-               hw_set_addr(hw);
-       hw_restore_intr(hw, interrupt);
-
-       return 0;
-}
-
-static void dev_set_promiscuous(struct net_device *dev, struct dev_priv *priv,
-       struct ksz_hw *hw, int promiscuous)
-{
-       if (promiscuous != priv->promiscuous) {
-               u8 prev_state = hw->promiscuous;
-
-               if (promiscuous)
-                       ++hw->promiscuous;
-               else
-                       --hw->promiscuous;
-               priv->promiscuous = promiscuous;
-
-               /* Turn on/off promiscuous mode. */
-               if (hw->promiscuous <= 1 && prev_state <= 1)
-                       hw_set_promiscuous(hw, hw->promiscuous);
-
-               /*
-                * Port is not in promiscuous mode, meaning it is released
-                * from the bridge.
-                */
-               if ((hw->features & STP_SUPPORT) && !promiscuous &&
-                   (dev->priv_flags & IFF_BRIDGE_PORT)) {
-                       struct ksz_switch *sw = hw->ksz_switch;
-                       int port = priv->port.first_port;
-
-                       port_set_stp_state(hw, port, STP_STATE_DISABLED);
-                       port = 1 << port;
-                       if (sw->member & port) {
-                               sw->member &= ~port;
-                               bridge_change(hw);
-                       }
-               }
-       }
-}
-
-static void dev_set_multicast(struct dev_priv *priv, struct ksz_hw *hw,
-       int multicast)
-{
-       if (multicast != priv->multicast) {
-               u8 all_multi = hw->all_multi;
-
-               if (multicast)
-                       ++hw->all_multi;
-               else
-                       --hw->all_multi;
-               priv->multicast = multicast;
-
-               /* Turn on/off all multicast mode. */
-               if (hw->all_multi <= 1 && all_multi <= 1)
-                       hw_set_multicast(hw, hw->all_multi);
-       }
-}
-
-/**
- * netdev_set_rx_mode
- * @dev:       Network device.
- *
- * This routine is used to set multicast addresses or put the network device
- * into promiscuous mode.
- */
-static void netdev_set_rx_mode(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct netdev_hw_addr *ha;
-       int multicast = (dev->flags & IFF_ALLMULTI);
-
-       dev_set_promiscuous(dev, priv, hw, (dev->flags & IFF_PROMISC));
-
-       if (hw_priv->hw.dev_count > 1)
-               multicast |= (dev->flags & IFF_MULTICAST);
-       dev_set_multicast(priv, hw, multicast);
-
-       /* Cannot use different hashes in multiple device interfaces mode. */
-       if (hw_priv->hw.dev_count > 1)
-               return;
-
-       if ((dev->flags & IFF_MULTICAST) && !netdev_mc_empty(dev)) {
-               int i = 0;
-
-               /* List too big to support so turn on all multicast mode. */
-               if (netdev_mc_count(dev) > MAX_MULTICAST_LIST) {
-                       if (MAX_MULTICAST_LIST != hw->multi_list_size) {
-                               hw->multi_list_size = MAX_MULTICAST_LIST;
-                               ++hw->all_multi;
-                               hw_set_multicast(hw, hw->all_multi);
-                       }
-                       return;
-               }
-
-               netdev_for_each_mc_addr(ha, dev) {
-                       if (i >= MAX_MULTICAST_LIST)
-                               break;
-                       memcpy(hw->multi_list[i++], ha->addr, MAC_ADDR_LEN);
-               }
-               hw->multi_list_size = (u8) i;
-               hw_set_grp_addr(hw);
-       } else {
-               if (MAX_MULTICAST_LIST == hw->multi_list_size) {
-                       --hw->all_multi;
-                       hw_set_multicast(hw, hw->all_multi);
-               }
-               hw->multi_list_size = 0;
-               hw_clr_multicast(hw);
-       }
-}
-
-static int netdev_change_mtu(struct net_device *dev, int new_mtu)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int hw_mtu;
-
-       if (netif_running(dev))
-               return -EBUSY;
-
-       /* Cannot use different MTU in multiple device interfaces mode. */
-       if (hw->dev_count > 1)
-               if (dev != hw_priv->dev)
-                       return 0;
-       if (new_mtu < 60)
-               return -EINVAL;
-
-       if (dev->mtu != new_mtu) {
-               hw_mtu = new_mtu + ETHERNET_HEADER_SIZE + 4;
-               if (hw_mtu > MAX_RX_BUF_SIZE)
-                       return -EINVAL;
-               if (hw_mtu > REGULAR_RX_BUF_SIZE) {
-                       hw->features |= RX_HUGE_FRAME;
-                       hw_mtu = MAX_RX_BUF_SIZE;
-               } else {
-                       hw->features &= ~RX_HUGE_FRAME;
-                       hw_mtu = REGULAR_RX_BUF_SIZE;
-               }
-               hw_mtu = (hw_mtu + 3) & ~3;
-               hw_priv->mtu = hw_mtu;
-               dev->mtu = new_mtu;
-       }
-       return 0;
-}
-
-/**
- * netdev_ioctl - I/O control processing
- * @dev:       Network device.
- * @ifr:       Interface request structure.
- * @cmd:       I/O control code.
- *
- * This function is used to process I/O control calls.
- *
- * Return 0 to indicate success.
- */
-static int netdev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port *port = &priv->port;
-       int rc;
-       int result = 0;
-       struct mii_ioctl_data *data = if_mii(ifr);
-
-       if (down_interruptible(&priv->proc_sem))
-               return -ERESTARTSYS;
-
-       /* assume success */
-       rc = 0;
-       switch (cmd) {
-       /* Get address of MII PHY in use. */
-       case SIOCGMIIPHY:
-               data->phy_id = priv->id;
-
-               /* Fallthrough... */
-
-       /* Read MII PHY register. */
-       case SIOCGMIIREG:
-               if (data->phy_id != priv->id || data->reg_num >= 6)
-                       result = -EIO;
-               else
-                       hw_r_phy(hw, port->linked->port_id, data->reg_num,
-                               &data->val_out);
-               break;
-
-       /* Write MII PHY register. */
-       case SIOCSMIIREG:
-               if (!capable(CAP_NET_ADMIN))
-                       result = -EPERM;
-               else if (data->phy_id != priv->id || data->reg_num >= 6)
-                       result = -EIO;
-               else
-                       hw_w_phy(hw, port->linked->port_id, data->reg_num,
-                               data->val_in);
-               break;
-
-       default:
-               result = -EOPNOTSUPP;
-       }
-
-       up(&priv->proc_sem);
-
-       return result;
-}
-
-/*
- * MII support
- */
-
-/**
- * mdio_read - read PHY register
- * @dev:       Network device.
- * @phy_id:    The PHY id.
- * @reg_num:   The register number.
- *
- * This function returns the PHY register value.
- *
- * Return the register value.
- */
-static int mdio_read(struct net_device *dev, int phy_id, int reg_num)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct ksz_port *port = &priv->port;
-       struct ksz_hw *hw = port->hw;
-       u16 val_out;
-
-       hw_r_phy(hw, port->linked->port_id, reg_num << 1, &val_out);
-       return val_out;
-}
-
-/**
- * mdio_write - set PHY register
- * @dev:       Network device.
- * @phy_id:    The PHY id.
- * @reg_num:   The register number.
- * @val:       The register value.
- *
- * This procedure sets the PHY register value.
- */
-static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct ksz_port *port = &priv->port;
-       struct ksz_hw *hw = port->hw;
-       int i;
-       int pi;
-
-       for (i = 0, pi = port->first_port; i < port->port_cnt; i++, pi++)
-               hw_w_phy(hw, pi, reg_num << 1, val);
-}
-
-/*
- * ethtool support
- */
-
-#define EEPROM_SIZE                    0x40
-
-static u16 eeprom_data[EEPROM_SIZE] = { 0 };
-
-#define ADVERTISED_ALL                 \
-       (ADVERTISED_10baseT_Half |      \
-       ADVERTISED_10baseT_Full |       \
-       ADVERTISED_100baseT_Half |      \
-       ADVERTISED_100baseT_Full)
-
-/* These functions use the MII functions in mii.c. */
-
-/**
- * netdev_get_settings - get network device settings
- * @dev:       Network device.
- * @cmd:       Ethtool command.
- *
- * This function queries the PHY and returns its state in the ethtool command.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-
-       mutex_lock(&hw_priv->lock);
-       mii_ethtool_gset(&priv->mii_if, cmd);
-       cmd->advertising |= SUPPORTED_TP;
-       mutex_unlock(&hw_priv->lock);
-
-       /* Save advertised settings for workaround in next function. */
-       priv->advertising = cmd->advertising;
-       return 0;
-}
-
-/**
- * netdev_set_settings - set network device settings
- * @dev:       Network device.
- * @cmd:       Ethtool command.
- *
- * This function sets the PHY according to the ethtool command.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_port *port = &priv->port;
-       u32 speed = ethtool_cmd_speed(cmd);
-       int rc;
-
-       /*
-        * ethtool utility does not change advertised setting if auto
-        * negotiation is not specified explicitly.
-        */
-       if (cmd->autoneg && priv->advertising == cmd->advertising) {
-               cmd->advertising |= ADVERTISED_ALL;
-               if (10 == speed)
-                       cmd->advertising &=
-                               ~(ADVERTISED_100baseT_Full |
-                               ADVERTISED_100baseT_Half);
-               else if (100 == speed)
-                       cmd->advertising &=
-                               ~(ADVERTISED_10baseT_Full |
-                               ADVERTISED_10baseT_Half);
-               if (0 == cmd->duplex)
-                       cmd->advertising &=
-                               ~(ADVERTISED_100baseT_Full |
-                               ADVERTISED_10baseT_Full);
-               else if (1 == cmd->duplex)
-                       cmd->advertising &=
-                               ~(ADVERTISED_100baseT_Half |
-                               ADVERTISED_10baseT_Half);
-       }
-       mutex_lock(&hw_priv->lock);
-       if (cmd->autoneg &&
-                       (cmd->advertising & ADVERTISED_ALL) ==
-                       ADVERTISED_ALL) {
-               port->duplex = 0;
-               port->speed = 0;
-               port->force_link = 0;
-       } else {
-               port->duplex = cmd->duplex + 1;
-               if (1000 != speed)
-                       port->speed = speed;
-               if (cmd->autoneg)
-                       port->force_link = 0;
-               else
-                       port->force_link = 1;
-       }
-       rc = mii_ethtool_sset(&priv->mii_if, cmd);
-       mutex_unlock(&hw_priv->lock);
-       return rc;
-}
-
-/**
- * netdev_nway_reset - restart auto-negotiation
- * @dev:       Network device.
- *
- * This function restarts the PHY for auto-negotiation.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_nway_reset(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       int rc;
-
-       mutex_lock(&hw_priv->lock);
-       rc = mii_nway_restart(&priv->mii_if);
-       mutex_unlock(&hw_priv->lock);
-       return rc;
-}
-
-/**
- * netdev_get_link - get network device link status
- * @dev:       Network device.
- *
- * This function gets the link status from the PHY.
- *
- * Return true if PHY is linked and false otherwise.
- */
-static u32 netdev_get_link(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       int rc;
-
-       rc = mii_link_ok(&priv->mii_if);
-       return rc;
-}
-
-/**
- * netdev_get_drvinfo - get network driver information
- * @dev:       Network device.
- * @info:      Ethtool driver info data structure.
- *
- * This procedure returns the driver information.
- */
-static void netdev_get_drvinfo(struct net_device *dev,
-       struct ethtool_drvinfo *info)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-
-       strcpy(info->driver, DRV_NAME);
-       strcpy(info->version, DRV_VERSION);
-       strcpy(info->bus_info, pci_name(hw_priv->pdev));
-}
-
-/**
- * netdev_get_regs_len - get length of register dump
- * @dev:       Network device.
- *
- * This function returns the length of the register dump.
- *
- * Return length of the register dump.
- */
-static struct hw_regs {
-       int start;
-       int end;
-} hw_regs_range[] = {
-       { KS_DMA_TX_CTRL,       KS884X_INTERRUPTS_STATUS },
-       { KS_ADD_ADDR_0_LO,     KS_ADD_ADDR_F_HI },
-       { KS884X_ADDR_0_OFFSET, KS8841_WOL_FRAME_BYTE2_OFFSET },
-       { KS884X_SIDER_P,       KS8842_SGCR7_P },
-       { KS8842_MACAR1_P,      KS8842_TOSR8_P },
-       { KS884X_P1MBCR_P,      KS8842_P3ERCR_P },
-       { 0, 0 }
-};
-
-static int netdev_get_regs_len(struct net_device *dev)
-{
-       struct hw_regs *range = hw_regs_range;
-       int regs_len = 0x10 * sizeof(u32);
-
-       while (range->end > range->start) {
-               regs_len += (range->end - range->start + 3) / 4 * 4;
-               range++;
-       }
-       return regs_len;
-}
-
-/**
- * netdev_get_regs - get register dump
- * @dev:       Network device.
- * @regs:      Ethtool registers data structure.
- * @ptr:       Buffer to store the register values.
- *
- * This procedure dumps the register values in the provided buffer.
- */
-static void netdev_get_regs(struct net_device *dev, struct ethtool_regs *regs,
-       void *ptr)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       int *buf = (int *) ptr;
-       struct hw_regs *range = hw_regs_range;
-       int len;
-
-       mutex_lock(&hw_priv->lock);
-       regs->version = 0;
-       for (len = 0; len < 0x40; len += 4) {
-               pci_read_config_dword(hw_priv->pdev, len, buf);
-               buf++;
-       }
-       while (range->end > range->start) {
-               for (len = range->start; len < range->end; len += 4) {
-                       *buf = readl(hw->io + len);
-                       buf++;
-               }
-               range++;
-       }
-       mutex_unlock(&hw_priv->lock);
-}
-
-#define WOL_SUPPORT                    \
-       (WAKE_PHY | WAKE_MAGIC |        \
-       WAKE_UCAST | WAKE_MCAST |       \
-       WAKE_BCAST | WAKE_ARP)
-
-/**
- * netdev_get_wol - get Wake-on-LAN support
- * @dev:       Network device.
- * @wol:       Ethtool Wake-on-LAN data structure.
- *
- * This procedure returns Wake-on-LAN support.
- */
-static void netdev_get_wol(struct net_device *dev,
-       struct ethtool_wolinfo *wol)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-
-       wol->supported = hw_priv->wol_support;
-       wol->wolopts = hw_priv->wol_enable;
-       memset(&wol->sopass, 0, sizeof(wol->sopass));
-}
-
-/**
- * netdev_set_wol - set Wake-on-LAN support
- * @dev:       Network device.
- * @wol:       Ethtool Wake-on-LAN data structure.
- *
- * This function sets Wake-on-LAN support.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_set_wol(struct net_device *dev,
-       struct ethtool_wolinfo *wol)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-
-       /* Need to find a way to retrieve the device IP address. */
-       static const u8 net_addr[] = { 192, 168, 1, 1 };
-
-       if (wol->wolopts & ~hw_priv->wol_support)
-               return -EINVAL;
-
-       hw_priv->wol_enable = wol->wolopts;
-
-       /* Link wakeup cannot really be disabled. */
-       if (wol->wolopts)
-               hw_priv->wol_enable |= WAKE_PHY;
-       hw_enable_wol(&hw_priv->hw, hw_priv->wol_enable, net_addr);
-       return 0;
-}
-
-/**
- * netdev_get_msglevel - get debug message level
- * @dev:       Network device.
- *
- * This function returns current debug message level.
- *
- * Return current debug message flags.
- */
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-
-       return priv->msg_enable;
-}
-
-/**
- * netdev_set_msglevel - set debug message level
- * @dev:       Network device.
- * @value:     Debug message flags.
- *
- * This procedure sets debug message level.
- */
-static void netdev_set_msglevel(struct net_device *dev, u32 value)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-
-       priv->msg_enable = value;
-}
-
-/**
- * netdev_get_eeprom_len - get EEPROM length
- * @dev:       Network device.
- *
- * This function returns the length of the EEPROM.
- *
- * Return length of the EEPROM.
- */
-static int netdev_get_eeprom_len(struct net_device *dev)
-{
-       return EEPROM_SIZE * 2;
-}
-
-/**
- * netdev_get_eeprom - get EEPROM data
- * @dev:       Network device.
- * @eeprom:    Ethtool EEPROM data structure.
- * @data:      Buffer to store the EEPROM data.
- *
- * This function dumps the EEPROM data in the provided buffer.
- *
- * Return 0 if successful; otherwise an error code.
- */
-#define EEPROM_MAGIC                   0x10A18842
-
-static int netdev_get_eeprom(struct net_device *dev,
-       struct ethtool_eeprom *eeprom, u8 *data)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       u8 *eeprom_byte = (u8 *) eeprom_data;
-       int i;
-       int len;
-
-       len = (eeprom->offset + eeprom->len + 1) / 2;
-       for (i = eeprom->offset / 2; i < len; i++)
-               eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
-       eeprom->magic = EEPROM_MAGIC;
-       memcpy(data, &eeprom_byte[eeprom->offset], eeprom->len);
-
-       return 0;
-}
-
-/**
- * netdev_set_eeprom - write EEPROM data
- * @dev:       Network device.
- * @eeprom:    Ethtool EEPROM data structure.
- * @data:      Data buffer.
- *
- * This function modifies the EEPROM data one byte at a time.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_set_eeprom(struct net_device *dev,
-       struct ethtool_eeprom *eeprom, u8 *data)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       u16 eeprom_word[EEPROM_SIZE];
-       u8 *eeprom_byte = (u8 *) eeprom_word;
-       int i;
-       int len;
-
-       if (eeprom->magic != EEPROM_MAGIC)
-               return -EINVAL;
-
-       len = (eeprom->offset + eeprom->len + 1) / 2;
-       for (i = eeprom->offset / 2; i < len; i++)
-               eeprom_data[i] = eeprom_read(&hw_priv->hw, i);
-       memcpy(eeprom_word, eeprom_data, EEPROM_SIZE * 2);
-       memcpy(&eeprom_byte[eeprom->offset], data, eeprom->len);
-       for (i = 0; i < EEPROM_SIZE; i++)
-               if (eeprom_word[i] != eeprom_data[i]) {
-                       eeprom_data[i] = eeprom_word[i];
-                       eeprom_write(&hw_priv->hw, i, eeprom_data[i]);
-       }
-
-       return 0;
-}
-
-/**
- * netdev_get_pauseparam - get flow control parameters
- * @dev:       Network device.
- * @pause:     Ethtool PAUSE settings data structure.
- *
- * This procedure returns the PAUSE control flow settings.
- */
-static void netdev_get_pauseparam(struct net_device *dev,
-       struct ethtool_pauseparam *pause)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       pause->autoneg = (hw->overrides & PAUSE_FLOW_CTRL) ? 0 : 1;
-       if (!hw->ksz_switch) {
-               pause->rx_pause =
-                       (hw->rx_cfg & DMA_RX_FLOW_ENABLE) ? 1 : 0;
-               pause->tx_pause =
-                       (hw->tx_cfg & DMA_TX_FLOW_ENABLE) ? 1 : 0;
-       } else {
-               pause->rx_pause =
-                       (sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_RX_FLOW_CTRL)) ? 1 : 0;
-               pause->tx_pause =
-                       (sw_chk(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_TX_FLOW_CTRL)) ? 1 : 0;
-       }
-}
-
-/**
- * netdev_set_pauseparam - set flow control parameters
- * @dev:       Network device.
- * @pause:     Ethtool PAUSE settings data structure.
- *
- * This function sets the PAUSE control flow settings.
- * Not implemented yet.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_set_pauseparam(struct net_device *dev,
-       struct ethtool_pauseparam *pause)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port *port = &priv->port;
-
-       mutex_lock(&hw_priv->lock);
-       if (pause->autoneg) {
-               if (!pause->rx_pause && !pause->tx_pause)
-                       port->flow_ctrl = PHY_NO_FLOW_CTRL;
-               else
-                       port->flow_ctrl = PHY_FLOW_CTRL;
-               hw->overrides &= ~PAUSE_FLOW_CTRL;
-               port->force_link = 0;
-               if (hw->ksz_switch) {
-                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_RX_FLOW_CTRL, 1);
-                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_TX_FLOW_CTRL, 1);
-               }
-               port_set_link_speed(port);
-       } else {
-               hw->overrides |= PAUSE_FLOW_CTRL;
-               if (hw->ksz_switch) {
-                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_RX_FLOW_CTRL, pause->rx_pause);
-                       sw_cfg(hw, KS8842_SWITCH_CTRL_1_OFFSET,
-                               SWITCH_TX_FLOW_CTRL, pause->tx_pause);
-               } else
-                       set_flow_ctrl(hw, pause->rx_pause, pause->tx_pause);
-       }
-       mutex_unlock(&hw_priv->lock);
-
-       return 0;
-}
-
-/**
- * netdev_get_ringparam - get tx/rx ring parameters
- * @dev:       Network device.
- * @pause:     Ethtool RING settings data structure.
- *
- * This procedure returns the TX/RX ring settings.
- */
-static void netdev_get_ringparam(struct net_device *dev,
-       struct ethtool_ringparam *ring)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       ring->tx_max_pending = (1 << 9);
-       ring->tx_pending = hw->tx_desc_info.alloc;
-       ring->rx_max_pending = (1 << 9);
-       ring->rx_pending = hw->rx_desc_info.alloc;
-}
-
-#define STATS_LEN                      (TOTAL_PORT_COUNTER_NUM)
-
-static struct {
-       char string[ETH_GSTRING_LEN];
-} ethtool_stats_keys[STATS_LEN] = {
-       { "rx_lo_priority_octets" },
-       { "rx_hi_priority_octets" },
-       { "rx_undersize_packets" },
-       { "rx_fragments" },
-       { "rx_oversize_packets" },
-       { "rx_jabbers" },
-       { "rx_symbol_errors" },
-       { "rx_crc_errors" },
-       { "rx_align_errors" },
-       { "rx_mac_ctrl_packets" },
-       { "rx_pause_packets" },
-       { "rx_bcast_packets" },
-       { "rx_mcast_packets" },
-       { "rx_ucast_packets" },
-       { "rx_64_or_less_octet_packets" },
-       { "rx_65_to_127_octet_packets" },
-       { "rx_128_to_255_octet_packets" },
-       { "rx_256_to_511_octet_packets" },
-       { "rx_512_to_1023_octet_packets" },
-       { "rx_1024_to_1522_octet_packets" },
-
-       { "tx_lo_priority_octets" },
-       { "tx_hi_priority_octets" },
-       { "tx_late_collisions" },
-       { "tx_pause_packets" },
-       { "tx_bcast_packets" },
-       { "tx_mcast_packets" },
-       { "tx_ucast_packets" },
-       { "tx_deferred" },
-       { "tx_total_collisions" },
-       { "tx_excessive_collisions" },
-       { "tx_single_collisions" },
-       { "tx_mult_collisions" },
-
-       { "rx_discards" },
-       { "tx_discards" },
-};
-
-/**
- * netdev_get_strings - get statistics identity strings
- * @dev:       Network device.
- * @stringset: String set identifier.
- * @buf:       Buffer to store the strings.
- *
- * This procedure returns the strings used to identify the statistics.
- */
-static void netdev_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       if (ETH_SS_STATS == stringset)
-               memcpy(buf, &ethtool_stats_keys,
-                       ETH_GSTRING_LEN * hw->mib_cnt);
-}
-
-/**
- * netdev_get_sset_count - get statistics size
- * @dev:       Network device.
- * @sset:      The statistics set number.
- *
- * This function returns the size of the statistics to be reported.
- *
- * Return size of the statistics to be reported.
- */
-static int netdev_get_sset_count(struct net_device *dev, int sset)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       switch (sset) {
-       case ETH_SS_STATS:
-               return hw->mib_cnt;
-       default:
-               return -EOPNOTSUPP;
-       }
-}
-
-/**
- * netdev_get_ethtool_stats - get network device statistics
- * @dev:       Network device.
- * @stats:     Ethtool statistics data structure.
- * @data:      Buffer to store the statistics.
- *
- * This procedure returns the statistics.
- */
-static void netdev_get_ethtool_stats(struct net_device *dev,
-       struct ethtool_stats *stats, u64 *data)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port *port = &priv->port;
-       int n_stats = stats->n_stats;
-       int i;
-       int n;
-       int p;
-       int rc;
-       u64 counter[TOTAL_PORT_COUNTER_NUM];
-
-       mutex_lock(&hw_priv->lock);
-       n = SWITCH_PORT_NUM;
-       for (i = 0, p = port->first_port; i < port->mib_port_cnt; i++, p++) {
-               if (media_connected == hw->port_mib[p].state) {
-                       hw_priv->counter[p].read = 1;
-
-                       /* Remember first port that requests read. */
-                       if (n == SWITCH_PORT_NUM)
-                               n = p;
-               }
-       }
-       mutex_unlock(&hw_priv->lock);
-
-       if (n < SWITCH_PORT_NUM)
-               schedule_work(&hw_priv->mib_read);
-
-       if (1 == port->mib_port_cnt && n < SWITCH_PORT_NUM) {
-               p = n;
-               rc = wait_event_interruptible_timeout(
-                       hw_priv->counter[p].counter,
-                       2 == hw_priv->counter[p].read,
-                       HZ * 1);
-       } else
-               for (i = 0, p = n; i < port->mib_port_cnt - n; i++, p++) {
-                       if (0 == i) {
-                               rc = wait_event_interruptible_timeout(
-                                       hw_priv->counter[p].counter,
-                                       2 == hw_priv->counter[p].read,
-                                       HZ * 2);
-                       } else if (hw->port_mib[p].cnt_ptr) {
-                               rc = wait_event_interruptible_timeout(
-                                       hw_priv->counter[p].counter,
-                                       2 == hw_priv->counter[p].read,
-                                       HZ * 1);
-                       }
-               }
-
-       get_mib_counters(hw, port->first_port, port->mib_port_cnt, counter);
-       n = hw->mib_cnt;
-       if (n > n_stats)
-               n = n_stats;
-       n_stats -= n;
-       for (i = 0; i < n; i++)
-               *data++ = counter[i];
-}
-
-/**
- * netdev_set_features - set receive checksum support
- * @dev:       Network device.
- * @features:  New device features (offloads).
- *
- * This function sets receive checksum support setting.
- *
- * Return 0 if successful; otherwise an error code.
- */
-static int netdev_set_features(struct net_device *dev, u32 features)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       mutex_lock(&hw_priv->lock);
-
-       /* see note in hw_setup() */
-       if (features & NETIF_F_RXCSUM)
-               hw->rx_cfg |= DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP;
-       else
-               hw->rx_cfg &= ~(DMA_RX_CSUM_TCP | DMA_RX_CSUM_IP);
-
-       if (hw->enabled)
-               writel(hw->rx_cfg, hw->io + KS_DMA_RX_CTRL);
-
-       mutex_unlock(&hw_priv->lock);
-
-       return 0;
-}
-
-static struct ethtool_ops netdev_ethtool_ops = {
-       .get_settings           = netdev_get_settings,
-       .set_settings           = netdev_set_settings,
-       .nway_reset             = netdev_nway_reset,
-       .get_link               = netdev_get_link,
-       .get_drvinfo            = netdev_get_drvinfo,
-       .get_regs_len           = netdev_get_regs_len,
-       .get_regs               = netdev_get_regs,
-       .get_wol                = netdev_get_wol,
-       .set_wol                = netdev_set_wol,
-       .get_msglevel           = netdev_get_msglevel,
-       .set_msglevel           = netdev_set_msglevel,
-       .get_eeprom_len         = netdev_get_eeprom_len,
-       .get_eeprom             = netdev_get_eeprom,
-       .set_eeprom             = netdev_set_eeprom,
-       .get_pauseparam         = netdev_get_pauseparam,
-       .set_pauseparam         = netdev_set_pauseparam,
-       .get_ringparam          = netdev_get_ringparam,
-       .get_strings            = netdev_get_strings,
-       .get_sset_count         = netdev_get_sset_count,
-       .get_ethtool_stats      = netdev_get_ethtool_stats,
-};
-
-/*
- * Hardware monitoring
- */
-
-static void update_link(struct net_device *dev, struct dev_priv *priv,
-       struct ksz_port *port)
-{
-       if (priv->media_state != port->linked->state) {
-               priv->media_state = port->linked->state;
-               if (netif_running(dev))
-                       set_media_state(dev, media_connected);
-       }
-}
-
-static void mib_read_work(struct work_struct *work)
-{
-       struct dev_info *hw_priv =
-               container_of(work, struct dev_info, mib_read);
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port_mib *mib;
-       int i;
-
-       next_jiffies = jiffies;
-       for (i = 0; i < hw->mib_port_cnt; i++) {
-               mib = &hw->port_mib[i];
-
-               /* Reading MIB counters or requested to read. */
-               if (mib->cnt_ptr || 1 == hw_priv->counter[i].read) {
-
-                       /* Need to process receive interrupt. */
-                       if (port_r_cnt(hw, i))
-                               break;
-                       hw_priv->counter[i].read = 0;
-
-                       /* Finish reading counters. */
-                       if (0 == mib->cnt_ptr) {
-                               hw_priv->counter[i].read = 2;
-                               wake_up_interruptible(
-                                       &hw_priv->counter[i].counter);
-                       }
-               } else if (jiffies >= hw_priv->counter[i].time) {
-                       /* Only read MIB counters when the port is connected. */
-                       if (media_connected == mib->state)
-                               hw_priv->counter[i].read = 1;
-                       next_jiffies += HZ * 1 * hw->mib_port_cnt;
-                       hw_priv->counter[i].time = next_jiffies;
-
-               /* Port is just disconnected. */
-               } else if (mib->link_down) {
-                       mib->link_down = 0;
-
-                       /* Read counters one last time after link is lost. */
-                       hw_priv->counter[i].read = 1;
-               }
-       }
-}
-
-static void mib_monitor(unsigned long ptr)
-{
-       struct dev_info *hw_priv = (struct dev_info *) ptr;
-
-       mib_read_work(&hw_priv->mib_read);
-
-       /* This is used to verify Wake-on-LAN is working. */
-       if (hw_priv->pme_wait) {
-               if (hw_priv->pme_wait <= jiffies) {
-                       hw_clr_wol_pme_status(&hw_priv->hw);
-                       hw_priv->pme_wait = 0;
-               }
-       } else if (hw_chk_wol_pme_status(&hw_priv->hw)) {
-
-               /* PME is asserted.  Wait 2 seconds to clear it. */
-               hw_priv->pme_wait = jiffies + HZ * 2;
-       }
-
-       ksz_update_timer(&hw_priv->mib_timer_info);
-}
-
-/**
- * dev_monitor - periodic monitoring
- * @ptr:       Network device pointer.
- *
- * This routine is run in a kernel timer to monitor the network device.
- */
-static void dev_monitor(unsigned long ptr)
-{
-       struct net_device *dev = (struct net_device *) ptr;
-       struct dev_priv *priv = netdev_priv(dev);
-       struct dev_info *hw_priv = priv->adapter;
-       struct ksz_hw *hw = &hw_priv->hw;
-       struct ksz_port *port = &priv->port;
-
-       if (!(hw->features & LINK_INT_WORKING))
-               port_get_link_speed(port);
-       update_link(dev, priv, port);
-
-       ksz_update_timer(&priv->monitor_timer_info);
-}
-
-/*
- * Linux network device interface functions
- */
-
-/* Driver exported variables */
-
-static int msg_enable;
-
-static char *macaddr = ":";
-static char *mac1addr = ":";
-
-/*
- * This enables multiple network device mode for KSZ8842, which contains a
- * switch with two physical ports.  Some users like to take control of the
- * ports for running Spanning Tree Protocol.  The driver will create an
- * additional eth? device for the other port.
- *
- * Some limitations are the network devices cannot have different MTU and
- * multicast hash tables.
- */
-static int multi_dev;
-
-/*
- * As most users select multiple network device mode to use Spanning Tree
- * Protocol, this enables a feature in which most unicast and multicast packets
- * are forwarded inside the switch and not passed to the host.  Only packets
- * that need the host's attention are passed to it.  This prevents the host
- * wasting CPU time to examine each and every incoming packets and do the
- * forwarding itself.
- *
- * As the hack requires the private bridge header, the driver cannot compile
- * with just the kernel headers.
- *
- * Enabling STP support also turns on multiple network device mode.
- */
-static int stp;
-
-/*
- * This enables fast aging in the KSZ8842 switch.  Not sure what situation
- * needs that.  However, fast aging is used to flush the dynamic MAC table when
- * STP suport is enabled.
- */
-static int fast_aging;
-
-/**
- * netdev_init - initialize network device.
- * @dev:       Network device.
- *
- * This function initializes the network device.
- *
- * Return 0 if successful; otherwise an error code indicating failure.
- */
-static int __init netdev_init(struct net_device *dev)
-{
-       struct dev_priv *priv = netdev_priv(dev);
-
-       /* 500 ms timeout */
-       ksz_init_timer(&priv->monitor_timer_info, 500 * HZ / 1000,
-               dev_monitor, dev);
-
-       /* 500 ms timeout */
-       dev->watchdog_timeo = HZ / 2;
-
-       dev->hw_features = NETIF_F_IP_CSUM | NETIF_F_SG | NETIF_F_RXCSUM;
-
-       /*
-        * Hardware does not really support IPv6 checksum generation, but
-        * driver actually runs faster with this on.
-        */
-       dev->hw_features |= NETIF_F_IPV6_CSUM;
-
-       dev->features |= dev->hw_features;
-
-       sema_init(&priv->proc_sem, 1);
-
-       priv->mii_if.phy_id_mask = 0x1;
-       priv->mii_if.reg_num_mask = 0x7;
-       priv->mii_if.dev = dev;
-       priv->mii_if.mdio_read = mdio_read;
-       priv->mii_if.mdio_write = mdio_write;
-       priv->mii_if.phy_id = priv->port.first_port + 1;
-
-       priv->msg_enable = netif_msg_init(msg_enable,
-               (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK));
-
-       return 0;
-}
-
-static const struct net_device_ops netdev_ops = {
-       .ndo_init               = netdev_init,
-       .ndo_open               = netdev_open,
-       .ndo_stop               = netdev_close,
-       .ndo_get_stats          = netdev_query_statistics,
-       .ndo_start_xmit         = netdev_tx,
-       .ndo_tx_timeout         = netdev_tx_timeout,
-       .ndo_change_mtu         = netdev_change_mtu,
-       .ndo_set_features       = netdev_set_features,
-       .ndo_set_mac_address    = netdev_set_mac_address,
-       .ndo_validate_addr      = eth_validate_addr,
-       .ndo_do_ioctl           = netdev_ioctl,
-       .ndo_set_rx_mode        = netdev_set_rx_mode,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-       .ndo_poll_controller    = netdev_netpoll,
-#endif
-};
-
-static void netdev_free(struct net_device *dev)
-{
-       if (dev->watchdog_timeo)
-               unregister_netdev(dev);
-
-       free_netdev(dev);
-}
-
-struct platform_info {
-       struct dev_info dev_info;
-       struct net_device *netdev[SWITCH_PORT_NUM];
-};
-
-static int net_device_present;
-
-static void get_mac_addr(struct dev_info *hw_priv, u8 *macaddr, int port)
-{
-       int i;
-       int j;
-       int got_num;
-       int num;
-
-       i = j = num = got_num = 0;
-       while (j < MAC_ADDR_LEN) {
-               if (macaddr[i]) {
-                       int digit;
-
-                       got_num = 1;
-                       digit = hex_to_bin(macaddr[i]);
-                       if (digit >= 0)
-                               num = num * 16 + digit;
-                       else if (':' == macaddr[i])
-                               got_num = 2;
-                       else
-                               break;
-               } else if (got_num)
-                       got_num = 2;
-               else
-                       break;
-               if (2 == got_num) {
-                       if (MAIN_PORT == port) {
-                               hw_priv->hw.override_addr[j++] = (u8) num;
-                               hw_priv->hw.override_addr[5] +=
-                                       hw_priv->hw.id;
-                       } else {
-                               hw_priv->hw.ksz_switch->other_addr[j++] =
-                                       (u8) num;
-                               hw_priv->hw.ksz_switch->other_addr[5] +=
-                                       hw_priv->hw.id;
-                       }
-                       num = got_num = 0;
-               }
-               i++;
-       }
-       if (MAC_ADDR_LEN == j) {
-               if (MAIN_PORT == port)
-                       hw_priv->hw.mac_override = 1;
-       }
-}
-
-#define KS884X_DMA_MASK                        (~0x0UL)
-
-static void read_other_addr(struct ksz_hw *hw)
-{
-       int i;
-       u16 data[3];
-       struct ksz_switch *sw = hw->ksz_switch;
-
-       for (i = 0; i < 3; i++)
-               data[i] = eeprom_read(hw, i + EEPROM_DATA_OTHER_MAC_ADDR);
-       if ((data[0] || data[1] || data[2]) && data[0] != 0xffff) {
-               sw->other_addr[5] = (u8) data[0];
-               sw->other_addr[4] = (u8)(data[0] >> 8);
-               sw->other_addr[3] = (u8) data[1];
-               sw->other_addr[2] = (u8)(data[1] >> 8);
-               sw->other_addr[1] = (u8) data[2];
-               sw->other_addr[0] = (u8)(data[2] >> 8);
-       }
-}
-
-#ifndef PCI_VENDOR_ID_MICREL_KS
-#define PCI_VENDOR_ID_MICREL_KS                0x16c6
-#endif
-
-static int __devinit pcidev_init(struct pci_dev *pdev,
-       const struct pci_device_id *id)
-{
-       struct net_device *dev;
-       struct dev_priv *priv;
-       struct dev_info *hw_priv;
-       struct ksz_hw *hw;
-       struct platform_info *info;
-       struct ksz_port *port;
-       unsigned long reg_base;
-       unsigned long reg_len;
-       int cnt;
-       int i;
-       int mib_port_count;
-       int pi;
-       int port_count;
-       int result;
-       char banner[sizeof(version)];
-       struct ksz_switch *sw = NULL;
-
-       result = pci_enable_device(pdev);
-       if (result)
-               return result;
-
-       result = -ENODEV;
-
-       if (pci_set_dma_mask(pdev, DMA_BIT_MASK(32)) ||
-                       pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
-               return result;
-
-       reg_base = pci_resource_start(pdev, 0);
-       reg_len = pci_resource_len(pdev, 0);
-       if ((pci_resource_flags(pdev, 0) & IORESOURCE_IO) != 0)
-               return result;
-
-       if (!request_mem_region(reg_base, reg_len, DRV_NAME))
-               return result;
-       pci_set_master(pdev);
-
-       result = -ENOMEM;
-
-       info = kzalloc(sizeof(struct platform_info), GFP_KERNEL);
-       if (!info)
-               goto pcidev_init_dev_err;
-
-       hw_priv = &info->dev_info;
-       hw_priv->pdev = pdev;
-
-       hw = &hw_priv->hw;
-
-       hw->io = ioremap(reg_base, reg_len);
-       if (!hw->io)
-               goto pcidev_init_io_err;
-
-       cnt = hw_init(hw);
-       if (!cnt) {
-               if (msg_enable & NETIF_MSG_PROBE)
-                       pr_alert("chip not detected\n");
-               result = -ENODEV;
-               goto pcidev_init_alloc_err;
-       }
-
-       snprintf(banner, sizeof(banner), "%s", version);
-       banner[13] = cnt + '0';         /* Replace x in "Micrel KSZ884x" */
-       dev_info(&hw_priv->pdev->dev, "%s\n", banner);
-       dev_dbg(&hw_priv->pdev->dev, "Mem = %p; IRQ = %d\n", hw->io, pdev->irq);
-
-       /* Assume device is KSZ8841. */
-       hw->dev_count = 1;
-       port_count = 1;
-       mib_port_count = 1;
-       hw->addr_list_size = 0;
-       hw->mib_cnt = PORT_COUNTER_NUM;
-       hw->mib_port_cnt = 1;
-
-       /* KSZ8842 has a switch with multiple ports. */
-       if (2 == cnt) {
-               if (fast_aging)
-                       hw->overrides |= FAST_AGING;
-
-               hw->mib_cnt = TOTAL_PORT_COUNTER_NUM;
-
-               /* Multiple network device interfaces are required. */
-               if (multi_dev) {
-                       hw->dev_count = SWITCH_PORT_NUM;
-                       hw->addr_list_size = SWITCH_PORT_NUM - 1;
-               }
-
-               /* Single network device has multiple ports. */
-               if (1 == hw->dev_count) {
-                       port_count = SWITCH_PORT_NUM;
-                       mib_port_count = SWITCH_PORT_NUM;
-               }
-               hw->mib_port_cnt = TOTAL_PORT_NUM;
-               hw->ksz_switch = kzalloc(sizeof(struct ksz_switch), GFP_KERNEL);
-               if (!hw->ksz_switch)
-                       goto pcidev_init_alloc_err;
-
-               sw = hw->ksz_switch;
-       }
-       for (i = 0; i < hw->mib_port_cnt; i++)
-               hw->port_mib[i].mib_start = 0;
-
-       hw->parent = hw_priv;
-
-       /* Default MTU is 1500. */
-       hw_priv->mtu = (REGULAR_RX_BUF_SIZE + 3) & ~3;
-
-       if (ksz_alloc_mem(hw_priv))
-               goto pcidev_init_mem_err;
-
-       hw_priv->hw.id = net_device_present;
-
-       spin_lock_init(&hw_priv->hwlock);
-       mutex_init(&hw_priv->lock);
-
-       /* tasklet is enabled. */
-       tasklet_init(&hw_priv->rx_tasklet, rx_proc_task,
-               (unsigned long) hw_priv);
-       tasklet_init(&hw_priv->tx_tasklet, tx_proc_task,
-               (unsigned long) hw_priv);
-
-       /* tasklet_enable will decrement the atomic counter. */
-       tasklet_disable(&hw_priv->rx_tasklet);
-       tasklet_disable(&hw_priv->tx_tasklet);
-
-       for (i = 0; i < TOTAL_PORT_NUM; i++)
-               init_waitqueue_head(&hw_priv->counter[i].counter);
-
-       if (macaddr[0] != ':')
-               get_mac_addr(hw_priv, macaddr, MAIN_PORT);
-
-       /* Read MAC address and initialize override address if not overrided. */
-       hw_read_addr(hw);
-
-       /* Multiple device interfaces mode requires a second MAC address. */
-       if (hw->dev_count > 1) {
-               memcpy(sw->other_addr, hw->override_addr, MAC_ADDR_LEN);
-               read_other_addr(hw);
-               if (mac1addr[0] != ':')
-                       get_mac_addr(hw_priv, mac1addr, OTHER_PORT);
-       }
-
-       hw_setup(hw);
-       if (hw->ksz_switch)
-               sw_setup(hw);
-       else {
-               hw_priv->wol_support = WOL_SUPPORT;
-               hw_priv->wol_enable = 0;
-       }
-
-       INIT_WORK(&hw_priv->mib_read, mib_read_work);
-
-       /* 500 ms timeout */
-       ksz_init_timer(&hw_priv->mib_timer_info, 500 * HZ / 1000,
-               mib_monitor, hw_priv);
-
-       for (i = 0; i < hw->dev_count; i++) {
-               dev = alloc_etherdev(sizeof(struct dev_priv));
-               if (!dev)
-                       goto pcidev_init_reg_err;
-               info->netdev[i] = dev;
-
-               priv = netdev_priv(dev);
-               priv->adapter = hw_priv;
-               priv->id = net_device_present++;
-
-               port = &priv->port;
-               port->port_cnt = port_count;
-               port->mib_port_cnt = mib_port_count;
-               port->first_port = i;
-               port->flow_ctrl = PHY_FLOW_CTRL;
-
-               port->hw = hw;
-               port->linked = &hw->port_info[port->first_port];
-
-               for (cnt = 0, pi = i; cnt < port_count; cnt++, pi++) {
-                       hw->port_info[pi].port_id = pi;
-                       hw->port_info[pi].pdev = dev;
-                       hw->port_info[pi].state = media_disconnected;
-               }
-
-               dev->mem_start = (unsigned long) hw->io;
-               dev->mem_end = dev->mem_start + reg_len - 1;
-               dev->irq = pdev->irq;
-               if (MAIN_PORT == i)
-                       memcpy(dev->dev_addr, hw_priv->hw.override_addr,
-                               MAC_ADDR_LEN);
-               else {
-                       memcpy(dev->dev_addr, sw->other_addr,
-                               MAC_ADDR_LEN);
-                       if (!memcmp(sw->other_addr, hw->override_addr,
-                                       MAC_ADDR_LEN))
-                               dev->dev_addr[5] += port->first_port;
-               }
-
-               dev->netdev_ops = &netdev_ops;
-               SET_ETHTOOL_OPS(dev, &netdev_ethtool_ops);
-               if (register_netdev(dev))
-                       goto pcidev_init_reg_err;
-               port_set_power_saving(port, true);
-       }
-
-       pci_dev_get(hw_priv->pdev);
-       pci_set_drvdata(pdev, info);
-       return 0;
-
-pcidev_init_reg_err:
-       for (i = 0; i < hw->dev_count; i++) {
-               if (info->netdev[i]) {
-                       netdev_free(info->netdev[i]);
-                       info->netdev[i] = NULL;
-               }
-       }
-
-pcidev_init_mem_err:
-       ksz_free_mem(hw_priv);
-       kfree(hw->ksz_switch);
-
-pcidev_init_alloc_err:
-       iounmap(hw->io);
-
-pcidev_init_io_err:
-       kfree(info);
-
-pcidev_init_dev_err:
-       release_mem_region(reg_base, reg_len);
-
-       return result;
-}
-
-static void pcidev_exit(struct pci_dev *pdev)
-{
-       int i;
-       struct platform_info *info = pci_get_drvdata(pdev);
-       struct dev_info *hw_priv = &info->dev_info;
-
-       pci_set_drvdata(pdev, NULL);
-
-       release_mem_region(pci_resource_start(pdev, 0),
-               pci_resource_len(pdev, 0));
-       for (i = 0; i < hw_priv->hw.dev_count; i++) {
-               if (info->netdev[i])
-                       netdev_free(info->netdev[i]);
-       }
-       if (hw_priv->hw.io)
-               iounmap(hw_priv->hw.io);
-       ksz_free_mem(hw_priv);
-       kfree(hw_priv->hw.ksz_switch);
-       pci_dev_put(hw_priv->pdev);
-       kfree(info);
-}
-
-#ifdef CONFIG_PM
-static int pcidev_resume(struct pci_dev *pdev)
-{
-       int i;
-       struct platform_info *info = pci_get_drvdata(pdev);
-       struct dev_info *hw_priv = &info->dev_info;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       pci_set_power_state(pdev, PCI_D0);
-       pci_restore_state(pdev);
-       pci_enable_wake(pdev, PCI_D0, 0);
-
-       if (hw_priv->wol_enable)
-               hw_cfg_wol_pme(hw, 0);
-       for (i = 0; i < hw->dev_count; i++) {
-               if (info->netdev[i]) {
-                       struct net_device *dev = info->netdev[i];
-
-                       if (netif_running(dev)) {
-                               netdev_open(dev);
-                               netif_device_attach(dev);
-                       }
-               }
-       }
-       return 0;
-}
-
-static int pcidev_suspend(struct pci_dev *pdev, pm_message_t state)
-{
-       int i;
-       struct platform_info *info = pci_get_drvdata(pdev);
-       struct dev_info *hw_priv = &info->dev_info;
-       struct ksz_hw *hw = &hw_priv->hw;
-
-       /* Need to find a way to retrieve the device IP address. */
-       static const u8 net_addr[] = { 192, 168, 1, 1 };
-
-       for (i = 0; i < hw->dev_count; i++) {
-               if (info->netdev[i]) {
-                       struct net_device *dev = info->netdev[i];
-
-                       if (netif_running(dev)) {
-                               netif_device_detach(dev);
-                               netdev_close(dev);
-                       }
-               }
-       }
-       if (hw_priv->wol_enable) {
-               hw_enable_wol(hw, hw_priv->wol_enable, net_addr);
-               hw_cfg_wol_pme(hw, 1);
-       }
-
-       pci_save_state(pdev);
-       pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
-       pci_set_power_state(pdev, pci_choose_state(pdev, state));
-       return 0;
-}
-#endif
-
-static char pcidev_name[] = "ksz884xp";
-
-static struct pci_device_id pcidev_table[] = {
-       { PCI_VENDOR_ID_MICREL_KS, 0x8841,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { PCI_VENDOR_ID_MICREL_KS, 0x8842,
-               PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
-       { 0 }
-};
-
-MODULE_DEVICE_TABLE(pci, pcidev_table);
-
-static struct pci_driver pci_device_driver = {
-#ifdef CONFIG_PM
-       .suspend        = pcidev_suspend,
-       .resume         = pcidev_resume,
-#endif
-       .name           = pcidev_name,
-       .id_table       = pcidev_table,
-       .probe          = pcidev_init,
-       .remove         = pcidev_exit
-};
-
-static int __init ksz884x_init_module(void)
-{
-       return pci_register_driver(&pci_device_driver);
-}
-
-static void __exit ksz884x_cleanup_module(void)
-{
-       pci_unregister_driver(&pci_device_driver);
-}
-
-module_init(ksz884x_init_module);
-module_exit(ksz884x_cleanup_module);
-
-MODULE_DESCRIPTION("KSZ8841/2 PCI network driver");
-MODULE_AUTHOR("Tristram Ha <Tristram.Ha@micrel.com>");
-MODULE_LICENSE("GPL");
-
-module_param_named(message, msg_enable, int, 0);
-MODULE_PARM_DESC(message, "Message verbosity level (0=none, 31=all)");
-
-module_param(macaddr, charp, 0);
-module_param(mac1addr, charp, 0);
-module_param(fast_aging, int, 0);
-module_param(multi_dev, int, 0);
-module_param(stp, int, 0);
-MODULE_PARM_DESC(macaddr, "MAC address");
-MODULE_PARM_DESC(mac1addr, "Second MAC address");
-MODULE_PARM_DESC(fast_aging, "Fast aging");
-MODULE_PARM_DESC(multi_dev, "Multiple device interfaces");
-MODULE_PARM_DESC(stp, "STP support");