]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
unicore32: add pkunity-v3 mac/net driver (umal)
authorGuan Xuetao <gxt@mprc.pku.edu.cn>
Mon, 22 Aug 2011 07:13:06 +0000 (15:13 +0800)
committerGuan Xuetao <gxt@mprc.pku.edu.cn>
Mon, 22 Aug 2011 07:37:41 +0000 (15:37 +0800)
Thanks Arnd and Ben.
We have changed our code by the review, and submit the patch again.
    1. Use variable type integer to describe umal_speed instead of enum.
    2. Changed the name ETHER_ADDR_LEN to ETH_ALEN.
    3. Renamed those members in struct umaldmadscr to fit the naming convention.
    4. Modified our code skb_reserve(sb_new, 2) to skb_reserve(sb_new, SMP_CACHE_BYTES + NET_IP_ALIGN)
           in function umaldma_add_rcvbuffer.
    5. Put judgment statement if (!netif_device_present(dev)) before lock the sc->umal_lock
           in function umaldma_tx_process.
    6. Reorder those functions in driver and delete all prototypes.
    7. Modified our code comment to fit the demands of kernel-doc.

For the mac address, we use the fixed, single mac address just for board test, and this
mac address is just assigned to our company, so we didn't choose random_ether_addr().

And NAPI work is just on the way.

Tested-and-Signed-off-by: Zhang Shu <davidshuzhang@gmail.com>
Signed-off-by: Chu Xiaowei <chuxiaowei@mprc.pku.edu.cn>
Signed-off-by: Su Yonggang <tonyhook.su@gmail.com>
Signed-off-by: Guan Xuetao <gxt@mprc.pku.edu.cn>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Ben Hutchings <bhutchings@solarflare.com>
Cc: netdev@vger.kernel.org
MAINTAINERS
arch/unicore32/configs/unicore32_defconfig
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/mac-puv3.c [new file with mode: 0644]

index 1d445f572987793c1919d3458af11a4d29cf72a4..a7e63cc621f1f100357a0a5c6dd9cba5d1b2f12b 100644 (file)
@@ -5016,6 +5016,7 @@ F:        drivers/input/serio/i8042-unicore32io.h
 F:     drivers/i2c/busses/i2c-puv3.c
 F:     drivers/video/fb-puv3.c
 F:     drivers/rtc/rtc-puv3.c
+F:     drivers/net/mac-puv3.c
 
 PMBUS HARDWARE MONITORING DRIVERS
 M:     Guenter Roeck <guenter.roeck@ericsson.com>
index c9dd3198b6f701d3e07b50c81528fb240da7b9ca..6265e7360dcd77bfe5e0209847f2ce9c4ff7aa0a 100644 (file)
@@ -64,7 +64,6 @@ CONFIG_I2C_BATTERY_BQ27200=n
 CONFIG_I2C_EEPROM_AT24=n
 CONFIG_LCD_BACKLIGHT=n
 
-CONFIG_PUV3_UMAL=y
 CONFIG_PUV3_MUSB=n
 CONFIG_PUV3_AC97=n
 CONFIG_PUV3_NAND=n
@@ -101,6 +100,7 @@ CONFIG_SATA_VIA=y
 #      Network device support
 CONFIG_NETDEVICES=y
 CONFIG_NET_ETHERNET=y
+CONFIG_MAC_PUV3=y
 CONFIG_NETDEV_1000=y
 #      Wireless LAN
 CONFIG_WLAN_80211=n
index 8d0314dbd946c99141d0870542376c367bbf35cb..e8d055cc1bf9949daf585ef91521c223b10bcf9d 100644 (file)
@@ -2016,6 +2016,11 @@ config LANTIQ_ETOP
        help
          Support for the MII0 inside the Lantiq SoC
 
+config MAC_PUV3
+       tristate "PKUnity v3 UMAL Gigabit Network Adapter support"
+       depends on UNICORE32 && ARCH_PUV3
+       select MII
+       select PHYLIB
 
 source "drivers/net/fs_enet/Kconfig"
 
index e1eca2ab505ea248648346e21269ae380c0b7634..aef59c2c11e1da483013d00f240bf71280b41344 100644 (file)
@@ -148,6 +148,7 @@ obj-$(CONFIG_AX88796) += ax88796.o
 obj-$(CONFIG_BCM63XX_ENET) += bcm63xx_enet.o
 obj-$(CONFIG_FTGMAC100) += ftgmac100.o
 obj-$(CONFIG_FTMAC100) += ftmac100.o
+obj-$(CONFIG_MAC_PUV3) += mac-puv3.o
 
 obj-$(CONFIG_TSI108_ETH) += tsi108_eth.o
 obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
diff --git a/drivers/net/mac-puv3.c b/drivers/net/mac-puv3.c
new file mode 100644 (file)
index 0000000..f55d6c2
--- /dev/null
@@ -0,0 +1,1807 @@
+/*
+ * Code specific to PKUnity SoC and UniCore ISA
+ *
+ *     Maintained by GUAN Xue-tao <gxt@mprc.pku.edu.cn>
+ *     Copyright (C) 2001-2010 Guan Xuetao
+ *
+ * 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.
+ */
+#include <linux/bug.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/phy.h>
+#include <linux/platform_device.h>
+#include <linux/cache.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+
+#include <mach/hardware.h>
+
+MODULE_DESCRIPTION("PKUNITY-3 SoC Ethernet driver");
+MODULE_LICENSE("GPL v2");
+
+/*
+ * umal_string: PKUnity-v3-UMAL
+ * umal_mdio_string: umal-mdio
+ */
+static const char umal_string[] = "PKUnity-v3-UMAL";
+static const char umal_mdio_string[] = "umal-mdio";
+
+/*
+ * enum umal-fc -  Simple types
+ */
+enum umal_fc {
+       umal_fc_none,
+       umal_fc_disabled,
+       umal_fc_frame,
+       umal_fc_collision,
+       umal_fc_carrier,
+};
+
+/*
+ * enum umal-state -  Simple types
+ */
+enum umal_state {
+       umal_state_uninit,
+       umal_state_off,
+       umal_state_on,
+       umal_state_broken,
+};
+
+/*
+ *  Macros
+ */
+#define UMALDMA_NEXTBUF(d, f) ((((d)->f+1) == (d)->umaldma_dscrtable_end) ? \
+                         (d)->umaldma_dscrtable : (d)->f+1)
+
+#define DMA_RX                 0
+#define DMA_TX                 1
+
+#define UMAL_MAX_TXDESCR       256
+#define UMAL_MAX_RXDESCR       256
+
+#define ENET_PACKET_SIZE       1518
+
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT             (2*HZ)
+
+/*
+ * struct umaldmadscr - DMA Descriptor structure
+ */
+struct umaldmadscr {
+       dma_addr_t   packet_start_addr;
+       int          packet_size;
+       dma_addr_t   next_descriptor;
+       struct umaldmadscr *next_descriptor_Virt;
+};
+
+/**
+ * struct umadlma - DMA Controller structure
+ * @umaldma_eth: back pointer to associated MAC
+ * @umaldma_txdir: direction(1 = transmit)
+ * @umaldma_maxdescr: total number of descriptors in ring
+ * @umaldma_dscrtable: base of the descriptor table
+ * @umaldma_dscrtable_unaligned: base of the descriptor table unaligned
+ * @umaldma_dscrtable_phys: phys addr
+ * @umaldma_dscrtable_phys_unaligned: another phys addr
+ * @umaldma_dscrtable_end: the end of the descriptor table
+ * @umaldma_ctxtable: context table, one per descr
+ * @umaldma_addptr: next dscr for sw to add
+ * @umaldma_remptr: next dscr for sw to remove
+ *
+ * DMA Controller.
+ * umaldma_eth, umaldma_txdir, umaldma_maxdescr is used to identify
+ * the channel and the registers associated with it.
+ * And the following other members is used for maintenance of the ring.
+ */
+struct umaldma {
+       /*
+        * This stuff is used to identify the channel and the registers
+        * associated with it.
+        */
+       /* back pointer to associated MAC */
+       struct umal_softc       *umaldma_eth;
+       /* direction (1=transmit) */
+       int                     umaldma_txdir;
+       /* total # of descriptors in ring */
+       int                     umaldma_maxdescr;
+       /*
+        * This stuff is for maintenance of the ring
+        */
+       /* base of descriptor table */
+       struct umaldmadscr      *umaldma_dscrtable;
+       void                    *umaldma_dscrtable_unaligned;
+       /* and also the phys addr */
+       dma_addr_t              umaldma_dscrtable_phys;
+       /* and also the phys addr */
+       dma_addr_t              umaldma_dscrtable_phys_unaligned;
+       /* end of descriptor table */
+       struct umaldmadscr      *umaldma_dscrtable_end;
+       /* context table, one per descr */
+       struct sk_buff          **umaldma_ctxtable;
+       /* next dscr for sw to add */
+       struct umaldmadscr      *umaldma_addptr;
+       /* next dscr for sw to remove */
+       struct umaldmadscr      *umaldma_remptr;
+};
+
+/*
+ * struct my_struct -  Ethernet softc structure
+ */
+struct umal_softc {
+       /* Linux-specific things */
+       struct net_device       *umal_dev;      /* pointer to linux device */
+       int                     umal_idx;
+       struct phy_device       *phy_dev;       /* the associated PHY device */
+       struct mii_bus          *mii_bus;       /* the MII bus */
+       int                     phy_irq[PHY_MAX_ADDR];
+       spinlock_t              umal_lock;      /* spin lock */
+       int                     umal_devflags;  /* current device flags */
+
+       /* Controller-specific things */
+       enum umal_state         umal_state;     /* current state */
+       unsigned char           umal_hwaddr[ETH_ALEN];
+
+       int                     umal_speed;     /* current speed */
+       int                     umal_duplex;    /* current duplex */
+       enum umal_fc            umal_fc;        /* cur. flow control setting */
+       int                     umal_pause;     /* current pause setting */
+       int                     umal_link;      /* current link state */
+
+       struct umaldma          umal_rxdma;     /* rx dma channel */
+       struct umaldma          umal_txdma;     /* tx dma channel */
+};
+
+/*
+ * MII Bus functions  - for PAL (phy abstraction layer)
+ */
+
+/**
+ * umal_mii_reset() - Reset MII bus.
+ * @bus: MDIO bus handle.
+ *
+ * MII Bus functions for PAL (phy abstraction layer)
+ * Reset MII bus.
+ * Return value, 0 if ok.
+ */
+static int umal_mii_reset(struct mii_bus *bus)
+{
+       /* reset the MII management */
+       writel(UMAL_MIICFG_RESET, UMAL_MIICFG);
+       /* enable the MII management */
+       writel(0, UMAL_MIICFG);
+       /* source clock division = 28 */
+       writel(readl(UMAL_MIICFG) | 0x7, UMAL_MIICFG);
+
+       return 0;
+}
+
+/**
+ * umal_mii_read() - Read a PHY register.
+ * @bus: MDIO bus handle
+ * @phyaddr: PHY's address
+ * @regidx: index of register to read
+ *
+ * MII Bus functions for PAL (phy abstraction layer)
+ * Read a PHY register.
+ * Return valume, value read, or 0xffff if an error occurred.
+ */
+static int umal_mii_read(struct mii_bus *bus, int phyaddr, int regidx)
+{
+       int tmp = 0;
+
+       writel((phyaddr<<8) | regidx, UMAL_MIIADDR);
+       writel(UMAL_MIICMD_READ, UMAL_MIICMD);
+
+       tmp = readl(UMAL_MIIIDCT);
+       while (tmp & UMAL_MIIIDCT_BUSY)
+               tmp = readl(UMAL_MIIIDCT);
+
+       if (tmp & UMAL_MIIIDCT_NOTVALID)
+               return 0xffff;
+
+       writel(0, UMAL_MIICMD);
+
+       tmp = readl(UMAL_MIISTATUS);
+       return tmp;
+}
+
+/**
+ * umal_mii_write() - Write a value to a PHY register.
+ * @bus: MDIO bus handle
+ * @phyaddr: PHY to use
+ * @regidx: register within the PHY
+ * @val: data to write to register
+ *
+ * MII Bus functions for PAL (phy abstraction layer)
+ * Write a value to a PHY register
+ * Return value, 0 if ok
+ */
+static int umal_mii_write(struct mii_bus *bus, int phyaddr, int regidx, u16 val)
+{
+       int tmp = 0;
+
+       writel((phyaddr<<8) | regidx, UMAL_MIIADDR);
+       writel(val, UMAL_MIICTRL);
+
+       tmp = readl(UMAL_MIIIDCT);
+       while (tmp & UMAL_MIIIDCT_BUSY)
+               tmp = readl(UMAL_MIIIDCT);
+
+       return 0;
+}
+
+/**
+ * umal_clr_intr() - Clear all interrupt of umal
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Clear all interrupt of umal
+ * Return value, nothing
+ */
+static void umal_clr_intr(struct net_device *dev)
+{
+       unsigned int int_status;
+
+       if (!netif_device_present(dev))
+               return;
+       int_status = readl(UMAL_DMAInterrupt);
+       if (int_status & INT_RX_PKT)
+               writel(CLR_RX_PKT, UMAL_DMARxStatus);
+
+       if (int_status & INT_RX_BUS_ERR)
+               writel(CLR_RX_BUS_ERR, UMAL_DMARxStatus);
+
+       if (int_status & INT_RX_OVERFLOW)
+               writel(CLR_RX_OVERFLOW, UMAL_DMARxStatus);
+
+       if (int_status & INT_TX_PKT)
+               writel(CLR_TX_PKT, UMAL_DMATxStatus);
+
+       if (int_status & INT_TX_BUS_ERR)
+               writel(CLR_TX_BUS_ERR, UMAL_DMATxStatus);
+
+       if (int_status & INT_TX_UNDERRUN)
+               writel(CLR_TX_UNDERRUN, UMAL_DMATxStatus);
+}
+
+/**
+ * umal_promiscuous_mode() - Turn on or off promiscuous mode
+ * @sc: softc
+ * @onoff: 1 to turn on, 0 to turn off
+ *
+ * UMAL functions
+ * Turn on or off promiscuous mode
+ * Return value, nothing
+ */
+static void umal_promiscuous_mode(struct umal_softc *sc, int onoff)
+{
+       if (onoff) {
+               writel(readl(UMAL_FIFOCFG4) & ~0x40000, UMAL_FIFOCFG4);
+               writel(readl(UMAL_FIFOCFG5) | 0x40000, UMAL_FIFOCFG5);
+       } else {
+               writel(readl(UMAL_FIFOCFG4) | 0x40000, UMAL_FIFOCFG4);
+               writel(readl(UMAL_FIFOCFG5) & ~0x40000, UMAL_FIFOCFG5);
+       }
+}
+
+/**
+ * umal_setmulti() - Reprogram the multicasr table into the hardware.
+ * @sc: softc
+ *
+ * UMAL functions
+ * Reprogram the multicast table into the hardware, given
+ * the list of multicasts associated with the interface
+ * structure.
+ * Return value, nothing
+ */
+static void umal_setmulti(struct umal_softc *sc)
+{
+}
+
+/**
+ * umal_set_speed() - Configure LAN speed for the specified MAC.
+ * @s: sbmac structure
+ * @speed: speed to set MAC to (see enum sbmac_speed)
+ *
+ * UMAL functions
+ * Configure LAN speed for the specified MAC.
+ * Warning: must be called when MAC is off!
+ * Return value,
+ *     1 if successful
+ *     0 indicates invalid parameters
+ */
+static int umal_set_speed(struct umal_softc *s, int speed)
+{
+       unsigned int cfg;
+
+       /* Save new current values */
+       s->umal_speed = speed;
+
+       if (s->umal_state == umal_state_on)
+               return 0;       /* save for next restart */
+
+       /* Read current register values */
+       cfg = readl(UMAL_CFG2);
+
+       /* Mask out the stuff we want to change */
+       cfg &= ~(UMAL_CFG2_MODEMASK);
+
+       /* Now add in the new bits */
+       switch (speed) {
+       case SPEED_10:
+       case SPEED_100:
+               cfg |= UMAL_CFG2_NIBBLEMODE;
+               break;
+
+       default:
+               return 0;
+       }
+
+       /* Send the bits back to the hardware */
+       writel(cfg, UMAL_CFG2);
+
+       return 1;
+}
+
+/**
+ * umal_set_duplex() - Set Ethernet duplex and flow control options for this MAC
+ * @s: umal structure
+ * @duplex: duplex setting (see enum sbmac_duplex)
+ * @fc: flow control setting (see enum sbmac_fc)
+ *
+ * UMAL functions
+ * Set Ethernet duplex and flow control options for this MAC
+ * Warning: must be called when MAC is off!
+ * Return value,
+ *      1 if ok
+ *      0 if an invalid parameter combination was specified
+ */
+static int umal_set_duplex(struct umal_softc *s, int duplex,
+               enum umal_fc fc)
+{
+       unsigned int cfg1, cfg2;
+       int err = 0;
+
+       /* Save new current values */
+       s->umal_duplex = duplex;
+       s->umal_fc = fc;
+
+       if (s->umal_state == umal_state_on)
+               return 0;       /* save for next restart */
+
+       /* Read current register values */
+       cfg1 = readl(UMAL_CFG1);
+       cfg2 = readl(UMAL_CFG2);
+
+       /* Mask off the stuff we're about to change */
+       cfg1 &= ~(UMAL_CFG1_TXFLOWCTL | UMAL_CFG1_RXFLOWCTL);
+       cfg2 &= ~(UMAL_CFG2_FULLDUPLEX);
+
+       err = 0;
+       switch (duplex) {
+       case DUPLEX_HALF:
+               break;
+
+       case DUPLEX_FULL:
+               cfg2 |= UMAL_CFG2_FULLDUPLEX;
+               break;
+
+       default:
+               err = 1;
+       }
+       if (!err)
+               writel(cfg2, UMAL_CFG2);
+
+       err = 0;
+       switch (fc) {
+       case umal_fc_disabled:
+               break;
+
+       case umal_fc_collision:
+               break;
+
+       case umal_fc_carrier:
+               break;
+
+       case umal_fc_frame:
+               cfg1 |= UMAL_CFG1_TXFLOWCTL | UMAL_CFG1_RXFLOWCTL;
+               break;
+
+       default:
+               err = 1;
+       }
+
+       if (!err)
+               writel(cfg1, UMAL_CFG1);
+
+       /* Send the bits back to the hardware */
+       return 1;
+}
+
+/**
+ * umaldma_channel_start() - Open a DMA channel.
+ * @d: DMA channel to init (context must be previously init'd)
+ * @rxtx: DMA_RX or DMA_TX depending on what type of channel
+ *
+ * UMAL DMA functions
+ * Open a DMA channel
+ * Return value, nothing
+ */
+static void umaldma_channel_start(struct umaldma *d, int rxtx)
+{
+       /*
+        * Turn on the DMA channel
+        */
+       if (rxtx == DMA_TX) {
+               writel(d->umaldma_dscrtable_phys, UMAL_DMATxDescriptor);
+               writel(UMAL_DMA_Enable, UMAL_DMATxCtrl);
+       } else {
+               writel(d->umaldma_dscrtable_phys, UMAL_DMARxDescriptor);
+               writel(UMAL_DMA_Enable, UMAL_DMARxCtrl);
+       }
+}
+
+/**
+ * umaldma_channel_stop() - Close DMA channel.
+ * @d: DMA channel to init (context must be previously init'd
+ *
+ * UMAL DMA functions
+ * Close DMA channel.
+ * Return value, nothing
+ */
+static void umaldma_channel_stop(struct umaldma *d)
+{
+       /*
+        * Turn off the DMA channel
+        */
+       if (d->umaldma_txdir == DMA_TX) {
+               writel(0, UMAL_DMATxCtrl);
+               writel(0, UMAL_DMATxDescriptor);
+       } else {
+               writel(0, UMAL_DMARxCtrl);
+               writel(0, UMAL_DMARxDescriptor);
+       }
+
+       /*
+        * Zero ring pointers
+        */
+       d->umaldma_addptr = d->umaldma_dscrtable;
+       d->umaldma_remptr = d->umaldma_dscrtable;
+}
+
+/**
+ * umaldma_add_rcvbuffer() - Add a buffer to the specified DMA channel.
+ * @sc: softc structure
+ * @d: DMA channel descriptor
+ * @sb: sk_buff to add, or NULL if we shoule allocate one
+ *
+ * UMAL DMA functions
+ * Add a buffer to the specified DMA channel.
+ * For receive channels, this queues a buffer for inbound packets.
+ * Return calue,
+ *     0 if buffer coule could not be added(ring is full)
+ *     1 if buffer added successfully
+ */
+static int umaldma_add_rcvbuffer(struct umal_softc *sc, struct umaldma *d,
+               struct sk_buff *sb)
+{
+       struct net_device *dev = sc->umal_dev;
+       struct umaldmadscr *dsc;
+       struct umaldmadscr *nextdsc;
+       struct sk_buff *sb_new = NULL;
+
+       /* get pointer to our current place in the ring */
+       dsc = d->umaldma_addptr;
+       nextdsc = UMALDMA_NEXTBUF(d, umaldma_addptr);
+
+       /*
+        * figure out if the ring is full - if the next descriptor
+        * is the same as the one that we're going to remove from
+        * the ring, the ring is full
+        */
+       if (nextdsc == d->umaldma_remptr)
+               return -ENOSPC;
+
+       /*
+        * Allocate a sk_buff if we don't already have one.
+        * If we do have an sk_buff, reset it so that it's empty.
+        *
+        * Note: sk_buffs don't seem to be guaranteed to have any sort
+        * of alignment when they are allocated.  Therefore, allocate enough
+        * extra space to make sure that:
+        *
+        *    1. the data does not start in the middle of a cache line.
+        *    2. The data does not end in the middle of a cache line
+        *    3. The buffer can be aligned such that the IP addresses are
+        *       naturally aligned.
+        *
+        *  Remember, the SOCs MAC writes whole cache lines at a time,
+        *  without reading the old contents first.  So, if the sk_buff's
+        *  data portion starts in the middle of a cache line, the SOC
+        *  DMA will trash the beginning (and ending) portions.
+        */
+       if (sb == NULL) {
+               sb_new = netdev_alloc_skb(dev, ENET_PACKET_SIZE +
+                       SMP_CACHE_BYTES * 2 + NET_IP_ALIGN);
+               if (sb_new == NULL) {
+                       pr_info("%s: sk_buff allocation failed\n",
+                               d->umaldma_eth->umal_dev->name);
+                       return -ENOMEM;
+               }
+               skb_reserve(sb_new, SMP_CACHE_BYTES + NET_IP_ALIGN);
+       } else {
+               sb_new = sb;
+               /*
+                * nothing special to reinit buffer, it's already aligned
+                * and sb->data already points to a good place.
+                */
+       }
+
+       /* fill in the descriptor */
+       dsc->packet_start_addr = virt_to_phys(sb_new->data);
+       dsc->packet_size      = UMAL_DESC_PACKETSIZE_EMPTY;
+
+       /* fill in the context */
+       d->umaldma_ctxtable[dsc-d->umaldma_dscrtable] = sb_new;
+
+       /* point at next packet */
+       d->umaldma_addptr = nextdsc;
+
+       return 0;
+}
+
+/**
+ * umaldma_fillring() - Fill the specified DMA channel with sk_buffs
+ * @sc: softc structure
+ * @d: DMA channel
+ *
+ * UMAL DMA functions
+ * Fill the specified DMA channel (must be receive channel) with sk_buffs
+ * Return value, nothing
+ */
+static void umaldma_fillring(struct umal_softc *sc, struct umaldma *d)
+{
+       int idx;
+       for (idx = 0; idx < UMAL_MAX_RXDESCR - 1; idx++) {
+               if (umaldma_add_rcvbuffer(sc, d, NULL) != 0)
+                       break;
+       }
+}
+
+/**
+ * umal_channel_start() - Start packet processing on this MAC.
+ * @s: umal context structure
+ *
+ * UMAL Channel functions
+ * Start packet processing on this MAC.
+ * Return value, nothing
+ */
+static void umal_channel_start(struct umal_softc *s)
+{
+       /*
+        * Don't do this if running
+        */
+       if (s->umal_state == umal_state_on)
+               return;
+
+       /* don't accept any packets, disable all interrupts */
+       umaldma_channel_stop(&(s->umal_rxdma));
+       umaldma_channel_stop(&(s->umal_txdma));
+
+       writel(0, UMAL_DMAIntrMask);
+       umal_clr_intr(s->umal_dev);
+
+       /*
+        * Program the hardware address.  It goes into the hardware-address
+        * register as well as the first filter register.
+        */
+       writel(s->umal_hwaddr[0]<<24 | s->umal_hwaddr[1]<<16 |
+                       s->umal_hwaddr[2]<<8 | s->umal_hwaddr[3], UMAL_STADDR1);
+       writel(s->umal_hwaddr[4]<<24 |  s->umal_hwaddr[5]<<16, UMAL_STADDR2);
+
+       /* Configure the speed, duplex, and flow control */
+       umal_set_speed(s, s->umal_speed);
+       umal_set_duplex(s, s->umal_duplex, s->umal_fc);
+
+       /* Program multicast addresses */
+       umal_setmulti(s);
+
+       /* If channel was in promiscuous mode before, turn that on */
+       if (s->umal_devflags & IFF_PROMISC)
+               umal_promiscuous_mode(s, 1);
+
+       /* Fill the receive ring */
+       umaldma_fillring(s, &(s->umal_rxdma));
+
+       umaldma_channel_start(&(s->umal_rxdma), DMA_RX);
+       umaldma_channel_start(&(s->umal_txdma), DMA_TX);
+
+       s->umal_state = umal_state_on;
+
+       /* Initialize DMA channels (rings should be ok now) */
+       writel(INT_RX_BUS_ERR | INT_RX_OVERFLOW |
+                       INT_RX_PKT        | INT_TX_BUS_ERR |
+                       INT_TX_UNDERRUN   | INT_TX_PKT |
+                       UMAL_DMAIntrMask_ENABLEHALFWORD, UMAL_DMAIntrMask);
+
+       /* we're running now.  */
+       writel(readl(UMAL_CFG1) | UMAL_CFG1_RXENABLE | UMAL_CFG1_TXENABLE,
+                       UMAL_CFG1);
+}
+
+/**
+ * umaldma_emptyring() - Free all allocated sk_buffs on specified DMA channel.
+ * @d: DMA channel
+ *
+ * UMAL DMA functions
+ * Free all allocated sk_buffs on the specified DMA channel
+ * Return value, nothing
+ */
+static void umaldma_emptyring(struct umaldma *d)
+{
+       int idx;
+       struct sk_buff *sb;
+
+       for (idx = 0; idx < d->umaldma_maxdescr; idx++) {
+               sb = d->umaldma_ctxtable[idx];
+               if (sb) {
+                       dev_kfree_skb(sb);
+                       d->umaldma_ctxtable[idx] = NULL;
+               }
+       }
+}
+
+/**
+ * umal_channel_stop() - Stop packet processing on this MAC.
+ * @s: umal context structure
+ *
+ * UMAL Channel functions
+ * Stop packet processing on this MAC.
+ * Return value, nothing
+ */
+static void umal_channel_stop(struct umal_softc *s)
+{
+       /* don't do this if already stopped */
+       if (s->umal_state == umal_state_off)
+               return;
+
+       /* don't accept any packets, disable all interrupts */
+       writel(0, UMAL_DMAIntrMask);
+       umal_clr_intr(s->umal_dev);
+
+       /* turn off receiver and transmitter */
+       writel(UMAL_CFG1_RESET, UMAL_CFG1);     /* reset MAC */
+       writel(0, UMAL_CFG1);
+
+       /* We're stopped now. */
+       s->umal_state = umal_state_off;
+
+       /* Stop DMA channels (rings should be ok now) */
+       umaldma_channel_stop(&(s->umal_rxdma));
+       umaldma_channel_stop(&(s->umal_txdma));
+
+       /* Empty the receive and transmit rings */
+       umaldma_emptyring(&(s->umal_rxdma));
+       umaldma_emptyring(&(s->umal_txdma));
+}
+
+/**
+ * umal_miipoll() - Phy statemachine call back routine for link change
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Phy statemachine call back routine for link change
+ * Return value, nothing
+ */
+static void umal_miipoll(struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+       struct phy_device *phy_dev = sc->phy_dev;
+       unsigned long flags;
+       enum umal_fc fc;
+       int link_chg, speed_chg, duplex_chg, pause_chg, fc_chg;
+
+       link_chg = (sc->umal_link != phy_dev->link);
+       speed_chg = (sc->umal_speed != phy_dev->speed);
+       duplex_chg = (sc->umal_duplex != phy_dev->duplex);
+       pause_chg = (sc->umal_pause != phy_dev->pause);
+
+       if (!link_chg && !speed_chg && !duplex_chg && !pause_chg)
+               return;
+
+       if (!phy_dev->link) {
+               if (link_chg) {
+                       sc->umal_link = phy_dev->link;
+                       sc->umal_duplex = -1;
+                       sc->umal_fc = umal_fc_disabled;
+                       sc->umal_pause = -1;
+                       pr_info("%s: link unavailable\n", dev->name);
+               }
+               return;
+       }
+
+       if (phy_dev->duplex == DUPLEX_FULL) {
+               if (phy_dev->pause)
+                       fc = umal_fc_frame;
+               else
+                       fc = umal_fc_disabled;
+       } else
+               fc = umal_fc_collision;
+       fc_chg = (sc->umal_fc != fc);
+
+       pr_info("%s: link available: %dbase-%cD\n", dev->name, phy_dev->speed,
+               phy_dev->duplex == DUPLEX_FULL ? 'F' : 'H');
+
+       spin_lock_irqsave(&sc->umal_lock, flags);
+
+       sc->umal_speed = phy_dev->speed;
+       sc->umal_duplex = phy_dev->duplex;
+       sc->umal_fc = fc;
+       sc->umal_pause = phy_dev->pause;
+       sc->umal_link = phy_dev->link;
+
+       if ((speed_chg || duplex_chg || fc_chg) &&
+               sc->umal_state != umal_state_off) {
+               /* something changed, restart the channel */
+               umal_channel_stop(sc);
+               umal_channel_start(sc);
+       }
+
+       spin_unlock_irqrestore(&sc->umal_lock, flags);
+}
+
+/**
+ * umal_mii_probe() - Write a value to a PHY register.
+ * @dev: net_device structure
+ *
+ * MII Bus functions for PAL (phy abstraction layer)
+ * Write a value to a PHY register.
+ * Return value, 0 if ok.
+ */
+static int umal_mii_probe(struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+       struct phy_device *phy_dev;
+       int i;
+
+       for (i = 0; i < PHY_MAX_ADDR; i++) {
+               phy_dev = sc->mii_bus->phy_map[i];
+               if (phy_dev)
+                       break;
+       }
+       if (!phy_dev) {
+               printk(KERN_ERR "%s: no PHY found\n", dev->name);
+               return -ENXIO;
+       }
+
+       phy_dev = phy_connect(dev, dev_name(&phy_dev->dev), &umal_miipoll, 0,
+                             PHY_INTERFACE_MODE_MII);
+       if (IS_ERR(phy_dev)) {
+               printk(KERN_ERR "%s: could not attach to PHY\n", dev->name);
+               return PTR_ERR(phy_dev);
+       }
+
+       /* Remove any features not supported by the controller */
+       phy_dev->supported &= SUPPORTED_10baseT_Half |
+                             SUPPORTED_10baseT_Full |
+                             SUPPORTED_100baseT_Half |
+                             SUPPORTED_100baseT_Full |
+                             SUPPORTED_Autoneg |
+                             SUPPORTED_MII;
+       phy_dev->advertising = phy_dev->supported;
+
+       pr_info("%s: attached PHY driver [%s] (mii_bus:phy_addr=%s, irq=%d)\n",
+               dev->name, phy_dev->drv->name,
+               dev_name(&phy_dev->dev), phy_dev->irq);
+
+       sc->phy_dev = phy_dev;
+
+       return 0;
+}
+
+/*
+ * UMAL DMA functions
+ */
+
+/**
+ * umaldma_initctx() - Initialize a DMA channel context.
+ * @d: struct umaldma (DMA channel context)
+ * @s: struct umal_softc (pointer to a MAC)
+ * @rxtx: Identifies DMA_TX or DMA_RX for channel direction
+ * @maxdescr: number of descriptors
+ *
+ * UMAL DMA functions
+ * Initialize a DMA channel context.  Since there are potentially
+ * eight DMA channels per MAC, it's nice to do this in a standard
+ * way.
+ * Return value, nothing
+ */
+static void umaldma_initctx(struct umaldma *d, struct umal_softc *s, int rxtx,
+               int maxdescr)
+{
+       struct umaldmadscr *dscr_item;
+       int idx;
+
+       /*
+        * Save away interesting stuff in the structure
+        */
+       d->umaldma_eth   = s;
+       d->umaldma_txdir = rxtx;
+       d->umaldma_maxdescr = maxdescr;
+
+       /*
+        * Allocate memory for the ring
+        */
+       d->umaldma_dscrtable_unaligned = dma_alloc_coherent(NULL,
+                                       sizeof(*d->umaldma_dscrtable),
+                                       &d->umaldma_dscrtable_phys_unaligned,
+                                       GFP_KERNEL | GFP_DMA);
+       dma_cache_sync(NULL, d->umaldma_dscrtable_unaligned,
+                       sizeof(*d->umaldma_dscrtable), DMA_BIDIRECTIONAL);
+
+       /*
+        * The descriptor table must be aligned to at least 16 bytes or the
+        * MAC will corrupt it.
+        */
+       d->umaldma_dscrtable = (struct umaldmadscr *)
+                       ALIGN((unsigned long)d->umaldma_dscrtable_unaligned,
+                       sizeof(*d->umaldma_dscrtable));
+       d->umaldma_dscrtable_end = d->umaldma_dscrtable + d->umaldma_maxdescr;
+       d->umaldma_dscrtable_phys = ALIGN((unsigned long)
+                               d->umaldma_dscrtable_phys_unaligned,
+                               sizeof(*d->umaldma_dscrtable));
+
+       for (idx = 0; idx < d->umaldma_maxdescr; idx++) {
+               dscr_item                      = d->umaldma_dscrtable + idx;
+               dscr_item->packet_start_addr     = 0;
+               dscr_item->packet_size          = UMAL_DESC_PACKETSIZE_EMPTY;
+               dscr_item->next_descriptor      = (dma_addr_t)(
+                               (struct umaldmadscr *)d->umaldma_dscrtable_phys
+                               + (idx+1));
+               dscr_item->next_descriptor_Virt
+                       = d->umaldma_dscrtable + (idx+1);
+       }
+       dscr_item                      = d->umaldma_dscrtable +
+                                       (d->umaldma_maxdescr - 1);
+       dscr_item->next_descriptor      = d->umaldma_dscrtable_phys;
+       dscr_item->next_descriptor_Virt = d->umaldma_dscrtable;
+
+       d->umaldma_addptr = d->umaldma_dscrtable;
+       d->umaldma_remptr = d->umaldma_dscrtable;
+
+       /*
+        * And context table
+        */
+       d->umaldma_ctxtable = kcalloc(d->umaldma_maxdescr,
+                                   sizeof(*d->umaldma_ctxtable), GFP_KERNEL);
+}
+
+/**
+ * umaldma_uninitctx() - Uninitialize a DMA channel context.
+ * @d: struct umaldma (DMA channel context)
+ *
+ * UMAL DMA functions
+ * Uninitialize a DMA channel context
+ * Return value, nothing
+ */
+static void umaldma_uninitctx(struct umaldma *d)
+{
+       if (d->umaldma_dscrtable_unaligned) {
+               dma_free_coherent(NULL, sizeof(*d->umaldma_dscrtable),
+                               d->umaldma_dscrtable_unaligned,
+                               d->umaldma_dscrtable_phys_unaligned);
+               d->umaldma_dscrtable_unaligned = d->umaldma_dscrtable = NULL;
+               d->umaldma_dscrtable_phys_unaligned = 0;
+               d->umaldma_dscrtable_phys = 0;
+       }
+
+       kfree(d->umaldma_ctxtable);
+       d->umaldma_ctxtable = NULL;
+}
+
+/**
+ * umaldma_add_txbuffer() - Add a transmit buffer to the specified DMA channel.
+ * @d: DMA channel descriptor
+ * @sb: sk_buff to add
+ *
+ * UMAL DMA functions
+ * Add a transmit buffer to the specified DMA channel, causing a
+ * transmit to start.
+ * Return value,
+ *     0 transmit queued successfully
+ *     otherwise error code
+ */
+static int umaldma_add_txbuffer(struct umaldma *d, struct sk_buff *sb)
+{
+       struct umaldmadscr *dsc;
+       struct umaldmadscr *nextdsc;
+
+       /* get pointer to our current place in the ring */
+       dsc = d->umaldma_addptr;
+       nextdsc = UMALDMA_NEXTBUF(d, umaldma_addptr);
+
+       /*
+        * figure out if the ring is full - if the next descriptor
+        * is the same as the one that we're going to remove from
+        * the ring, the ring is full
+        */
+       if (nextdsc == d->umaldma_remptr)
+               return -ENOSPC;
+
+       /*
+        * fill in the descriptor.  Note that the number of cache
+        * blocks in the descriptor is the number of blocks
+        * *spanned*, so we need to add in the offset (if any)
+        * while doing the calculation.
+        */
+       dsc->packet_start_addr = virt_to_phys(sb->data);
+       dsc->packet_size      = sb->len | UMAL_DESC_PACKETSIZE_NONEMPTY;
+
+       dma_map_single(NULL, sb->data, sb->len, DMA_BIDIRECTIONAL);
+       dma_cache_sync(NULL, sb->data, sb->len, DMA_BIDIRECTIONAL);
+
+       /* fill in the context */
+       d->umaldma_ctxtable[dsc-d->umaldma_dscrtable] = sb;
+
+       /* point at next packet */
+       d->umaldma_addptr = nextdsc;
+
+       return 0;
+}
+
+/**
+ * umaldma_rx_process() - Process "completed" receive buffers.
+ * @sc: softc structure
+ * @d: DMA channel context
+ * @work_to_do: no. of packets to process before enabling interrupt
+ *  again (for NAPI)
+ * @poll: 1. using polling (for NAPI)
+ *
+ * UMAL DMA functions
+ * Process "completed" receive buffers on the specified DMA channel.
+ * Return value, nothing
+ */
+static int umaldma_rx_process(struct umal_softc *sc, struct umaldma *d,
+               int work_to_do, int poll)
+{
+       struct net_device *dev = sc->umal_dev;
+       int curidx;
+       int hwidx;
+       struct umaldmadscr *dsc;
+       struct sk_buff *sb;
+       int len;
+       int work_done = 0;
+       int dropped = 0;
+       unsigned int int_status;
+
+       if (!netif_device_present(dev))
+               return 0;
+
+       int_status = readl(UMAL_DMAInterrupt);
+
+       if (int_status & INT_RX_BUS_ERR) {
+               writel(CLR_RX_BUS_ERR, UMAL_DMARxStatus);
+               writel(readl(UMAL_DMARxCtrl) | UMAL_DMA_Enable, UMAL_DMARxCtrl);
+       }
+
+       if (int_status & INT_RX_OVERFLOW) {
+               writel(CLR_RX_OVERFLOW, UMAL_DMARxStatus);
+               writel(readl(UMAL_DMARxCtrl) | UMAL_DMA_Enable, UMAL_DMARxCtrl);
+       }
+
+       if (!(int_status & INT_RX_PKT))
+               return 0;
+
+       while (work_to_do-- > 0) {
+               /*
+                * figure out where we are (as an index) and where
+                * the hardware is (also as an index)
+                *
+                * This could be done faster if (for example) the
+                * descriptor table was page-aligned and contiguous in
+                * both virtual and physical memory -- you could then
+                * just compare the low-order bits of the virtual
+                * address (sbdma_remptr) and the physical address
+                * (sbdma_curdscr CSR)
+                */
+               dsc = d->umaldma_remptr;
+               curidx = dsc - d->umaldma_dscrtable;
+
+               hwidx = (struct umaldmadscr *) readl(UMAL_DMARxDescriptor) -
+                       (struct umaldmadscr *)d->umaldma_dscrtable_phys;
+
+               writel(CLR_RX_PKT, UMAL_DMARxStatus);
+
+               /*
+                * If they're the same, that means we've processed all
+                * of the descriptors up to (but not including) the one
+                * that the hardware is working on right now.
+                */
+               if (curidx == hwidx)
+                       goto done;
+
+               /*
+                * Otherwise, get the packet's sk_buff ptr back
+                */
+               sb = d->umaldma_ctxtable[curidx];
+               len = dsc->packet_size & 0xfff;
+               d->umaldma_ctxtable[curidx] = NULL;
+
+               /* .. and advance to the next buffer.  */
+               d->umaldma_remptr = UMALDMA_NEXTBUF(d, umaldma_remptr);
+
+               /*
+                * Check packet status.  If good, process it.
+                * If not, silently drop it and put it back on the
+                * receive ring.
+                */
+               if (likely(!(dsc->packet_size & UMAL_DESC_PACKETSIZE_EMPTY))) {
+                       /*
+                        * Add a new buffer to replace the old one.
+                        * If we fail to allocate a buffer, we're going
+                        * to drop this packet and put it right back on
+                        * the receive ring.
+                        */
+                       if (unlikely(umaldma_add_rcvbuffer(sc, d, NULL)
+                                       == -ENOMEM)) {
+                               dev->stats.rx_dropped++;
+                               /* Re-add old buffer */
+                               umaldma_add_rcvbuffer(sc, d, sb);
+                               /* No point in continuing */
+                               printk(KERN_ERR "dropped packet (1)\n");
+                               d->umaldma_remptr = UMALDMA_NEXTBUF(d,
+                                               umaldma_remptr);
+                               goto done;
+                       } else {
+                               /* Set length into the packet */
+                               skb_put(sb, len + 4);
+
+                               /*
+                                * Buffer has been replaced on the
+                                * receive ring.  Pass the buffer to
+                                * the kernel
+                                */
+                               sb->protocol = eth_type_trans(sb,
+                                       d->umaldma_eth->umal_dev);
+                               /*
+                                * Check hw IPv4/TCP checksum
+                                * if supported
+                                */
+                               skb_checksum_none_assert(sb);
+
+                               if (poll)
+                                       dropped = netif_receive_skb(sb);
+                               else
+                                       dropped = netif_rx(sb);
+
+                               if (dropped == NET_RX_DROP) {
+                                       dev->stats.rx_dropped++;
+                                       d->umaldma_remptr = UMALDMA_NEXTBUF(d,
+                                                       umaldma_remptr);
+                                       goto done;
+                               } else {
+                                       dev->stats.rx_bytes += len;
+                                       dev->stats.rx_packets++;
+                               }
+                       }
+               } else {
+                       /*
+                        * Packet was mangled somehow.  Just drop it and
+                        * put it back on the receive ring.
+                        */
+                       dev->stats.rx_errors++;
+                       umaldma_add_rcvbuffer(sc, d, sb);
+               }
+               work_done++;
+       }
+done:
+       return work_done;
+}
+
+/**
+ * umaldma_tx_process() - Process "completed" transmit buffers.
+ * @sc: softc structure
+ * @d: DMA channel context
+ * @poll: - 1, using polling (for NAPI)
+ *
+ * UMAL DMA functions
+ * Process "completed" transmit buffers on the specified DMA channel.
+ * This is normally called within the interrupt service routine.
+ * Note that this isn't really ideal for priority channels, since
+ * it processes all of the packets on a given channel before
+ * returning.
+ * Return value, nothing
+ */
+static void umaldma_tx_process(struct umal_softc *sc, struct umaldma *d,
+               int poll)
+{
+       struct net_device *dev = sc->umal_dev;
+       int curidx;
+       int hwidx;
+       struct umaldmadscr *dsc;
+       struct sk_buff *sb;
+       unsigned long flags;
+       int packets_handled = 0;
+       unsigned int int_status;
+
+       if (!netif_device_present(dev))
+               return;
+
+       spin_lock_irqsave(&(sc->umal_lock), flags);
+
+       int_status = readl(UMAL_DMAInterrupt);
+
+       if (int_status & INT_TX_BUS_ERR)
+               writel(CLR_TX_BUS_ERR, UMAL_DMATxStatus);
+
+       if (int_status & INT_TX_UNDERRUN)
+               writel(CLR_TX_UNDERRUN, UMAL_DMATxStatus);
+
+       if (int_status & INT_TX_PKT) {
+               hwidx = (struct umaldmadscr *)readl(UMAL_DMATxDescriptor) -
+                       (struct umaldmadscr *)d->umaldma_dscrtable_phys;
+
+               if (d->umaldma_remptr == d->umaldma_addptr)
+                       goto end_unlock;
+
+               for (;;) {
+                       /*
+                        * figure out where we are (as an index) and where
+                        * the hardware is (also as an index)
+                        *
+                        * This could be done faster if (for example) the
+                        * descriptor table was page-aligned and contiguous in
+                        * both virtual and physical memory -- you could then
+                        * just compare the low-order bits of the virtual
+                        * address (sbdma_remptr) and the physical address
+                        * (sbdma_curdscr CSR)
+                        */
+                       curidx = d->umaldma_remptr - d->umaldma_dscrtable;
+
+                       /*
+                        * If they're the same, that means we've processed all
+                        * of the descriptors up to (but not including) the one
+                        * that the hardware is working on right now.
+                        */
+                       if (curidx == hwidx)
+                               break;
+
+                       /* Otherwise, get the packet's sk_buff ptr back */
+                       dsc = &(d->umaldma_dscrtable[curidx]);
+                       sb = d->umaldma_ctxtable[curidx];
+
+                       /* Stats */
+                       dev->stats.tx_bytes += sb->len;
+                       dev->stats.tx_packets++;
+
+                       /* for transmits, we just free buffers.  */
+                       dev_kfree_skb_irq(sb);
+                       d->umaldma_ctxtable[curidx] = NULL;
+                       writel(CLR_TX_PKT, UMAL_DMATxStatus);
+
+                       /* .. and advance to the next buffer.  */
+                       d->umaldma_remptr = UMALDMA_NEXTBUF(d, umaldma_remptr);
+                       packets_handled++;
+               }
+
+               /*
+                * Decide if we should wake up the protocol or not.
+                * Other drivers seem to do this when we reach a low
+                * watermark on the transmit queue.
+                */
+               if (packets_handled)
+                       netif_wake_queue(d->umaldma_eth->umal_dev);
+       }
+
+end_unlock:
+       spin_unlock_irqrestore(&(sc->umal_lock), flags);
+}
+
+/*
+ * UMAL Channel functions
+ */
+
+/**
+ * umal_initctx() - Initialize an Ethernet context structure
+ * @s: umal context structure
+ *
+ * UMAL Channel functions
+ * Initialize an Ethernet context structure - this is called
+ * once per MAC. Memory is allocated here, so don't
+ * call it again from inside the ioctl routines that bring the
+ * interface up/down
+ * Return value, 0
+ */
+static int umal_initctx(struct umal_softc *s)
+{
+       /*
+        * Initialize the DMA channels.
+        * Note: Only do this _once_, as it allocates memory from the kernel!
+        */
+       umaldma_initctx(&(s->umal_txdma), s, DMA_TX, UMAL_MAX_TXDESCR);
+       umaldma_initctx(&(s->umal_rxdma), s, DMA_RX, UMAL_MAX_RXDESCR);
+
+       /* initial state is OFF */
+       s->umal_state = umal_state_off;
+
+       return 0;
+}
+
+/**
+ * umal_uninitctx() - Initialize an Ethernet context structure.
+ * @s: umal context structure
+ *
+ * UMAL Channel functions
+ * Initialize an Ethernet context structure.
+ * Memory is allocated here, so don't call it again from inside the
+ * ioctl routines that bring the interface up/down
+ * Return value, Nothing
+ */
+static void umal_uninitctx(struct umal_softc *s)
+{
+       umaldma_uninitctx(&(s->umal_txdma));
+       umaldma_uninitctx(&(s->umal_rxdma));
+}
+
+/**
+ * umal_set_channel_state() - Set the channel's state ON or OFF
+ * @s: umal context structure
+ * @state: new state
+ *
+ * UMAL Channel functions
+ * Set the channel's state ON or OFF
+ * Return value, old state
+ */
+static enum umal_state umal_set_channel_state(struct umal_softc *s,
+               enum umal_state state)
+{
+       enum umal_state oldstate = s->umal_state;
+
+       /* If same as previous state, return */
+       if (state == oldstate)
+               return oldstate;
+
+       /* If new state is ON, turn channel on */
+       if (state == umal_state_on)
+               umal_channel_start(s);
+       else
+               umal_channel_stop(s);
+
+       /* Return previous state */
+       return oldstate;
+}
+
+/**
+ * umal_set_rx_mode() - Set promiscuous mode and multicast list
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Set promiscuous mode and multicast list
+ * Return value, nothing
+ */
+static void umal_set_rx_mode(struct net_device *dev)
+{
+       unsigned long flags;
+       struct umal_softc *sc = netdev_priv(dev);
+
+       spin_lock_irqsave(&sc->umal_lock, flags);
+       if ((dev->flags ^ sc->umal_devflags) & IFF_PROMISC) {
+               /* Promiscuous changed.  */
+               if (dev->flags & IFF_PROMISC)
+                       umal_promiscuous_mode(sc, 1);
+               else
+                       umal_promiscuous_mode(sc, 0);
+       }
+       spin_unlock_irqrestore(&sc->umal_lock, flags);
+
+       /* Program the multicasts.  Do this every time.  */
+       umal_setmulti(sc);
+}
+
+/**
+ * umal_intr() - Interrupt handler for MAC interrupts
+ * @irq: irq number
+ * @dev_instance: net_device structure
+ *
+ * UMAL functions
+ * Interrupt handler for MAC interrupts
+ * Return value, irq handling result
+ */
+static irqreturn_t umal_intr(int irq, void *dev_instance)
+{
+       struct net_device *dev = (struct net_device *) dev_instance;
+       struct umal_softc *sc = netdev_priv(dev);
+       uint64_t isr;
+       int handled = 0;
+
+       /*
+        * Read the ISR (this clears the bits in the real
+        * register, except for counter addr)
+        */
+       isr = readl(UMAL_DMAInterrupt);
+       if (isr == 0)
+               return IRQ_RETVAL(0);
+
+       if (sc->umal_state != umal_state_on) {
+               umal_clr_intr(dev);
+               return IRQ_RETVAL(0);
+       }
+       handled = 1;
+
+       /* Transmits on channel 0 */
+       if (isr & INT_TX_MASK)
+               umaldma_tx_process(sc, &(sc->umal_txdma), 0);
+       if (isr & INT_RX_MASK)
+               umaldma_rx_process(sc, &(sc->umal_rxdma),
+                                UMAL_MAX_RXDESCR * 2, 0);
+
+       return IRQ_RETVAL(handled);
+}
+
+/**
+ * umal_open() - OPen umal device
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Open umal device
+ * Return value,
+ *     0 if ok
+ *     otherwise error
+ */
+static int umal_open(struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+       int err;
+
+       sc->umal_speed = 0;
+       sc->umal_duplex = -1;
+       sc->umal_fc = umal_fc_none;
+       sc->umal_pause = -1;
+       sc->umal_link = 0;
+
+       /* reset mac and interface */
+       writel(UMAL_CFG1_RESET, UMAL_CFG1);     /* reset MAC */
+       writel(0, UMAL_CFG1);                   /* clear the reset bit of MAC */
+       writel(UMAL_IFCTRL_RESET, UMAL_IFCTRL); /* reset the MAC Interface */
+       writel(0, UMAL_DMAIntrMask);
+
+       /* Attach to the PHY */
+       err = umal_mii_probe(dev);
+       if (err)
+               goto out_unregister;
+
+       /* Turn on the channel */
+       phy_start(sc->phy_dev);
+
+       /* config fifo */
+       writel(0x000000ff, UMAL_FIFOCFG0);      /* reset FIFO */
+       writel(0x0fff0fff, UMAL_FIFOCFG1);
+       writel(0x0aaa0555, UMAL_FIFOCFG2);
+       writel(0x02800fff, UMAL_FIFOCFG3);
+       writel(0x00000070, UMAL_FIFOCFG4);
+       writel(0x0007ff8f, UMAL_FIFOCFG5);
+       writel(0x0000ff00, UMAL_FIFOCFG0);
+
+       writel(0x7016, UMAL_CFG2);
+
+       /*
+        * map/route interrupt (clear status first, in case something
+        * weird is pending; we haven't initialized the mac registers
+        * yet)
+        */
+       umal_clr_intr(dev);
+
+       err = request_irq(dev->irq, umal_intr, IRQF_SHARED, dev->name, dev);
+       if (err) {
+               printk(KERN_ERR "%s: unable to get IRQ %d\n", dev->name,
+                      dev->irq);
+               goto out_err;
+       }
+
+       umal_set_channel_state(sc, umal_state_on);
+
+       netif_start_queue(dev);
+
+       umal_set_rx_mode(dev);
+
+       return 0;
+
+out_unregister:
+       free_irq(dev->irq, dev);
+out_err:
+       return err;
+}
+
+/**
+ * umal_close() - Close umal device
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Close umal device
+ * Return value, 0 if ok
+ */
+static int umal_close(struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+
+       phy_stop(sc->phy_dev);
+
+       umal_set_channel_state(sc, umal_state_off);
+
+       netif_stop_queue(dev);
+
+       phy_disconnect(sc->phy_dev);
+       sc->phy_dev = NULL;
+
+       free_irq(dev->irq, dev);
+
+       umaldma_emptyring(&(sc->umal_rxdma));
+       umaldma_emptyring(&(sc->umal_txdma));
+
+       return 0;
+}
+
+/**
+ * umal_mii_ioctl() - Umal device ioctrl routine
+ * @dev: net_device structure
+ * @rq: interface request structure
+ * @cmd: ioctrl command
+ *
+ * UMAL functions
+ * Umal device ioctrl routine
+ * Return value, ioctrl command result
+ */
+static int umal_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+
+       if (!netif_running(dev) || !sc->phy_dev)
+               return -EINVAL;
+
+       return phy_mii_ioctl(sc->phy_dev, rq, cmd);
+}
+
+/**
+ * umal_start_tx() - Start output on the specified interface.
+ * @skb: sk_buff structure
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Start output on the specified interface.  Basically, we
+ * queue as many buffers as we can until the ring fills up, or
+ * we run off the end of the queue, whichever comes first.
+ * Return value,
+ *     0 if ok
+ *     otherwise error
+ */
+static int umal_start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+       unsigned long flags;
+
+       /* lock eth irq */
+       spin_lock_irqsave(&sc->umal_lock, flags);
+
+       /*
+        * Put the buffer on the transmit ring.  If we
+        * don't have room, stop the queue.
+        */
+       if (umaldma_add_txbuffer(&(sc->umal_txdma), skb)) {
+               /* XXX: save skb that we could not send */
+               netif_stop_queue(dev);
+               spin_unlock_irqrestore(&sc->umal_lock, flags);
+
+               return NETDEV_TX_BUSY;
+       }
+
+       writel(readl(UMAL_CFG1) | UMAL_CFG1_TXENABLE, UMAL_CFG1);
+       writel(readl(UMAL_DMATxCtrl) | UMAL_DMA_Enable, UMAL_DMATxCtrl);
+
+       spin_unlock_irqrestore(&sc->umal_lock, flags);
+
+       return 0;
+}
+
+/**
+ * umal_tx_timeout() - Update statistic structure
+ * @dev: net_device structure
+ *
+ * UMAL functions
+ * Tx timeout, update statistic structure
+ * Return value, nothing
+ */
+static void umal_tx_timeout(struct net_device *dev)
+{
+       struct umal_softc *sc = netdev_priv(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&sc->umal_lock, flags);
+
+       dev->trans_start = jiffies; /* prevent tx timeout */
+       dev->stats.tx_errors++;
+
+       spin_unlock_irqrestore(&sc->umal_lock, flags);
+
+       printk(KERN_WARNING "%s: Transmit timed out\n", dev->name);
+}
+
+/**
+ * umal_change_mtu() - Change MTU value
+ * @dev: net_device structure
+ * @new_mtu: new mtu value
+ *
+ * UMAL functions
+ * Change MTU value
+ * Return value, 1 if ok
+ */
+static int umal_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if (new_mtu > ENET_PACKET_SIZE)
+               return -EINVAL;
+
+       dev->mtu = new_mtu;
+
+       pr_info("changing the mtu to %d\n", new_mtu);
+
+       return 0;
+}
+
+/*
+ * UMAL functions
+ */
+static const struct net_device_ops umal_netdev_ops = {
+       .ndo_open               = umal_open,
+       .ndo_stop               = umal_close,
+       .ndo_start_xmit         = umal_start_tx,
+       .ndo_set_multicast_list = umal_set_rx_mode,
+       .ndo_tx_timeout         = umal_tx_timeout,
+       .ndo_do_ioctl           = umal_mii_ioctl,
+       .ndo_change_mtu         = umal_change_mtu,
+       .ndo_validate_addr      = eth_validate_addr,
+       .ndo_set_mac_address    = eth_mac_addr,
+};
+
+/**
+ * umal_init() - init hardware and hook ourselves into linux.
+ * @pldev: net_device structure
+ * @base: unknown
+ *
+ * UMAL functions
+ * Attach routine - init hardware and hook ourselves into linux
+ * Return value, 0 if ok
+ */
+static int umal_init(struct platform_device *pldev, long long base)
+{
+       struct net_device *dev = dev_get_drvdata(&pldev->dev);
+       int idx = pldev->id;
+       struct umal_softc *sc = netdev_priv(dev);
+       unsigned char *eaddr;
+       int i;
+       int err;
+
+       sc->umal_dev = dev;
+       sc->umal_idx = idx;
+
+       eaddr = sc->umal_hwaddr;
+
+#ifdef CONFIG_CMDLINE_FORCE
+       eaddr[0] = 0x00;
+       eaddr[1] = 0x25;
+       eaddr[2] = 0x9b;
+       eaddr[3] = 0xff;
+       eaddr[4] = 0x00;
+       eaddr[5] = 0x00;
+#endif
+
+       for (i = 0; i < 6; i++)
+               dev->dev_addr[i] = eaddr[i];
+
+       /* Set up Linux device callins */
+       spin_lock_init(&(sc->umal_lock));
+
+       dev->netdev_ops = &umal_netdev_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+       dev->irq = IRQ_UMAL;
+
+       sc->mii_bus = mdiobus_alloc();
+       if (sc->mii_bus == NULL) {
+               err = -ENOMEM;
+               goto uninit_ctx;
+       }
+
+       sc->mii_bus->name = umal_mdio_string;
+       snprintf(sc->mii_bus->id, MII_BUS_ID_SIZE, "%x", idx);
+       sc->mii_bus->priv = sc;
+       sc->mii_bus->read = umal_mii_read;
+       sc->mii_bus->write = umal_mii_write;
+       sc->mii_bus->reset = umal_mii_reset;
+       sc->mii_bus->irq = sc->phy_irq;
+       for (i = 0; i < PHY_MAX_ADDR; ++i)
+               sc->mii_bus->irq[i] = PHY_POLL;
+
+       sc->mii_bus->parent = &pldev->dev;
+
+       /* Probe PHY address */
+       err = mdiobus_register(sc->mii_bus);
+       if (err) {
+               printk(KERN_ERR "%s: unable to register MDIO bus\n",
+                      dev->name);
+               goto free_mdio;
+       }
+       dev_set_drvdata(&pldev->dev, sc->mii_bus);
+
+       err = register_netdev(dev);
+       if (err) {
+               printk(KERN_ERR "%s.%d: unable to register netdev\n",
+                      umal_string, idx);
+               goto unreg_mdio;
+       }
+
+       pr_info("%s.%d: registered as %s\n", umal_string, idx, dev->name);
+
+       /*
+        * Initialize context (get pointers to registers and stuff), then
+        * allocate the memory for the descriptor tables.
+        */
+       dev->dev.coherent_dma_mask = 0xFFFFFFFF;
+       umal_initctx(sc);
+
+       /*
+        * Display Ethernet address (this is called during the config
+        * process so we need to finish off the config message that
+        * was being displayed)
+        */
+       pr_info("%s: UMAL Ethernet at 0x%08Lx, address: %pM\n",
+              dev->name, base, eaddr);
+
+       return 0;
+unreg_mdio:
+       mdiobus_unregister(sc->mii_bus);
+       dev_set_drvdata(&pldev->dev, NULL);
+free_mdio:
+       mdiobus_free(sc->mii_bus);
+uninit_ctx:
+       umal_uninitctx(sc);
+       return err;
+}
+
+static int umal_probe(struct platform_device *pldev)
+{
+       struct net_device *dev;
+       struct umal_softc *sc;
+       struct resource *res;
+       int err;
+
+       res = platform_get_resource(pldev, IORESOURCE_MEM, 0);
+       BUG_ON(!res);
+
+       /* Okay.  Initialize this MAC.  */
+       dev = alloc_etherdev(sizeof(struct umal_softc));
+       if (!dev) {
+               printk(KERN_ERR "%s: unable to allocate etherdev\n",
+                      dev_name(&pldev->dev));
+               err = -ENOMEM;
+               goto out_out;
+       }
+
+       dev_set_drvdata(&pldev->dev, dev);
+       SET_NETDEV_DEV(dev, &pldev->dev);
+
+       sc = netdev_priv(dev);
+
+       err = umal_init(pldev, res->start);
+       if (err)
+               goto out_kfree;
+
+       return 0;
+
+out_kfree:
+       free_netdev(dev);
+
+out_out:
+       return err;
+}
+
+static int __exit umal_remove(struct platform_device *pldev)
+{
+       struct net_device *dev = dev_get_drvdata(&pldev->dev);
+       struct umal_softc *sc = netdev_priv(dev);
+
+       unregister_netdev(dev);
+       umal_uninitctx(sc);
+       mdiobus_unregister(sc->mii_bus);
+       free_netdev(dev);
+
+       return 0;
+}
+
+#if CONFIG_PM
+static void umal_reset(struct net_device *ndev)
+{
+       writel(UMAL_CFG1_RESET, UMAL_CFG1);     /* reset MAC */
+       writel(0, UMAL_CFG1);                   /* clear the reset bit of MAC */
+       writel(UMAL_IFCTRL_RESET, UMAL_IFCTRL); /* reset the MAC Interface */
+       writel(1, UMAL_DMAIntrMask);
+
+       writel(0x000000ff, UMAL_FIFOCFG0);      /* reset FIFO */
+       writel(0x0fff0fff, UMAL_FIFOCFG1);
+       writel(0x0aaa0555, UMAL_FIFOCFG2);
+       writel(0x02800fff, UMAL_FIFOCFG3);
+       writel(0x00000070, UMAL_FIFOCFG4);
+       writel(0x0007ff8f, UMAL_FIFOCFG5);
+       writel(0x0000ff00, UMAL_FIFOCFG0);
+
+       writel(0x7016, UMAL_CFG2);
+}
+
+static void umal_shutdown(struct net_device *ndev)
+{
+       /* XXX: something should be cleared */
+       return;
+}
+
+static int umal_suspend(struct platform_device *pldev, pm_message_t state)
+{
+       struct net_device *ndev = platform_get_drvdata(pldev);
+
+       if (netif_running(ndev)) {
+               netif_device_detach(ndev);
+               umal_shutdown(ndev);
+       }
+       return 0;
+}
+
+static int umal_resume(struct platform_device *pldev)
+{
+       struct net_device *dev = platform_get_drvdata(pldev);
+
+       if (netif_running(dev)) {
+               umal_reset(dev);
+               netif_device_attach(dev);
+       }
+       return 0;
+}
+#else
+static int umal_suspend(struct platform_device *pldev, pm_message_t state) { }
+static int umal_resume(struct platform_device *pldev) { }
+#endif
+static struct platform_driver umal_driver = {
+       .probe = umal_probe,
+       .remove = __exit_p(umal_remove),
+       .driver = {
+               .name = umal_string,
+               .owner  = THIS_MODULE,
+       },
+       .suspend = umal_suspend,
+       .resume  = umal_resume,
+};
+
+static int __init umal_init_module(void)
+{
+       return platform_driver_register(&umal_driver);
+}
+
+static void __exit umal_cleanup_module(void)
+{
+       platform_driver_unregister(&umal_driver);
+}
+
+module_init(umal_init_module);
+module_exit(umal_cleanup_module);