This FEC differs slightly from the one used in the MPC5200. Maybe both can
share the most of the code, but I'm not sure if its worth the effort.
Note: this is tested on a MPC2125 only. It might work on an MPC5121/MPC5123 as
well, but it's untested.

Signed-off-by: Juergen Borleis <[email protected]>
---
 drivers/net/Kconfig       |   5 +
 drivers/net/Makefile      |   1 +
 drivers/net/fec_mpc5125.c | 690 ++++++++++++++++++++++++++++++++++++++++++++++
 drivers/net/fec_mpc5125.h |   0
 include/fec.h             |   2 +-
 5 files changed, 697 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/fec_mpc5125.c
 create mode 100644 drivers/net/fec_mpc5125.h

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index c99fcc8..f4714f7 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -137,6 +137,11 @@ config DRIVER_NET_MPC5200
        depends on ARCH_MPC5200
        select PHYLIB
 
+config DRIVER_NET_MPC5125
+       bool "MPC5125 Ethernet driver"
+       depends on SOC_MPC5125
+       select PHYLIB
+
 config DRIVER_NET_NETX
        bool "Hilscher Netx ethernet driver"
        depends on HAS_NETX_ETHER
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 1b85778..8912887 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_DRIVER_NET_KS8851_MLL)   += ks8851_mll.o
 obj-$(CONFIG_DRIVER_NET_MACB)          += macb.o
 obj-$(CONFIG_DRIVER_NET_MICREL)                += ksz8864rmn.o
 obj-$(CONFIG_DRIVER_NET_MPC5200)       += fec_mpc5200.o
+obj-$(CONFIG_DRIVER_NET_MPC5125)       += fec_mpc5125.o
 obj-$(CONFIG_DRIVER_NET_NETX)          += netx_eth.o
 obj-$(CONFIG_DRIVER_NET_ORION)         += orion-gbe.o
 obj-$(CONFIG_DRIVER_NET_RTL8139)       += rtl8139.o
diff --git a/drivers/net/fec_mpc5125.c b/drivers/net/fec_mpc5125.c
new file mode 100644
index 0000000..3d78db6
--- /dev/null
+++ b/drivers/net/fec_mpc5125.c
@@ -0,0 +1,690 @@
+/*
+ * Copyright (C) 2014 Juergen Borleis, Pengutronix
+ *
+ * Based partially on code of:
+ * (C) Copyright 2003-2010
+ * Wolfgang Denk, DENX Software Engineering, [email protected].
+ *
+ * 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.
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <malloc.h>
+#include <xfuncs.h>
+#include <net.h>
+#include <fec.h>
+#include <linux/phy.h>
+#include <asm/io.h>
+#include <mach/mpc5xxx.h>
+#include <mach/clocks.h>
+
+/*
+ * Please be aware:
+ * External phys needs a preamble to synchronize itself into the bit stream.
+ * This means a '1' bit at the MDIO line for 32 consecutive MDC clocks.
+ * It might be neccessary to enable the pull up at the MDIO line to force it
+ * to the '1' state for this purpose
+ */
+
+/* FEC's register description */
+struct fec5125 {
+       u32     fec_id;
+       u32     ievent;
+       u32     imask;
+       u32     reserved_01;
+       u32     r_des_active;
+       u32     x_des_active;
+       u32     reserved_02[3];
+       u32     ecntrl;
+       u32     reserved_03[6];
+       u32     mii_data;
+       u32     mii_speed;
+       u32     reserved_04[7];
+       u32     mib_control;
+       u32     reserved_05[7];
+       u32     r_cntrl;
+       u32     r_hash;
+       u32     reserved_06[14];
+       u32     x_cntrl;
+       u32     reserved_07[7];
+       u32     paddr1;
+       u32     paddr2;
+       u32     op_pause;
+       u32     reserved_08[10];
+       u32     iaddr1;
+       u32     iaddr2;
+       u32     gaddr1;
+       u32     gaddr2;
+       u32     reserved_09[7];
+       u32     x_wmrk;
+       u32     reserved_10;
+       u32     r_bound;
+       u32     r_fstart;
+       u32     reserved_11[11];
+       u32     r_des_start;
+       u32     x_des_start;
+       u32     r_buff_size;
+       u32     reserved_12[26];
+       u32     dma_control;
+       u32     reserved_13[2];
+       u32     mib1[30];
+       u32     reserved_14[2];
+       u32     mib2[25];
+};
+
+/* RBD bits definitions */
+#define FEC_RBD_EMPTY          0x8000  /* Buffer is empty */
+#define FEC_RBD_WRAP           0x2000  /* Last BD in ring */
+#define FEC_RBD_LAST           0x0800  /* Buffer is last in frame(useless) */
+#define FEC_RBD_MISS           0x0100  /* Miss bit for prom mode */
+#define FEC_RBD_BC             0x0080  /* The received frame is broadcast 
frame */
+#define FEC_RBD_MC             0x0040  /* The received frame is multicast 
frame */
+#define FEC_RBD_LG             0x0020  /* Frame length violation */
+#define FEC_RBD_NO             0x0010  /* Nonoctet align frame */
+#define FEC_RBD_SH             0x0008  /* Short frame */
+#define FEC_RBD_CR             0x0004  /* CRC error */
+#define FEC_RBD_OV             0x0002  /* Receive FIFO overrun */
+#define FEC_RBD_TR             0x0001  /* Frame is truncated */
+#define FEC_RBD_ERR            (FEC_RBD_LG | FEC_RBD_NO | FEC_RBD_CR | \
+                                       FEC_RBD_OV | FEC_RBD_TR)
+
+/* TBD bits definitions */
+#define FEC_TBD_READY          0x8000  /* Buffer is ready */
+#define FEC_TBD_WRAP           0x2000  /* Last BD in ring */
+#define FEC_TBD_LAST           0x0800  /* Buffer is last in frame */
+#define FEC_TBD_TC             0x0400  /* Transmit the CRC */
+#define FEC_TBD_ABC            0x0200  /* Append bad CRC */
+
+/* MII-related definitios */
+#define FEC_MII_DATA_ST                0x40000000      /* Start of frame 
delimiter */
+#define FEC_MII_DATA_OP_RD     0x20000000      /* Perform a read operation */
+#define FEC_MII_DATA_OP_WR     0x10000000      /* Perform a write operation */
+#define FEC_MII_DATA_PA_MSK    0x0f800000      /* PHY Address field mask */
+#define FEC_MII_DATA_PA(x)     (((x) << FEC_MII_DATA_PA_SHIFT) & 
FEC_MII_DATA_PA_MSK)
+#define FEC_MII_DATA_RA_MSK    0x007c0000      /* PHY Register field mask */
+#define FEC_MII_DATA_RA(x)     (((x) << FEC_MII_DATA_RA_SHIFT) & 
FEC_MII_DATA_RA_MSK)
+#define FEC_MII_DATA_TA                0x00020000      /* Turnaround */
+#define FEC_MII_DATA_DATAMSK   0x0000ffff      /* PHY data field */
+
+#define FEC_MII_DATA_RA_SHIFT  18      /* MII Register address bits */
+#define FEC_MII_DATA_PA_SHIFT  23      /* MII PHY address bits */
+
+#define FEC_IEVENT_HBERR       0x80000000
+#define FEC_IEVENT_GRA         0x10000000
+#define FEC_IEVENT_BABT                0x20000000
+#define FEC_IEVENT_MII         0x00800000
+#define FEC_IEVENT_XFIFO_ERROR 0x00040000
+#define FEC_IEVENT_RFIFO_ERROR 0x00020000
+
+/* Receive & Transmit Buffer Descriptor definitions */
+struct mpc5125_descriptor {
+       u16 status;
+       u16 dlength;
+       u32 dpointer;
+};
+
+/* BD Numer definitions */
+#define FEC_TBD_NUM            48      /* The user can adjust this value */
+#define FEC_RBD_NUM            32      /* The user can adjust this value */
+
+/* packet size limit */
+#define FEC_MAX_FRAME_LEN      1522    /* recommended default value */
+
+/* Buffer size must be evenly divisible by 16 */
+#define FEC_MAX_PKT_SIZE       ((FEC_MAX_FRAME_LEN + 0x10) & (~0xf))
+
+struct mpc5125_frame {
+       unsigned char frame[FEC_MAX_PKT_SIZE];
+};
+
+struct mpc5125_buff_descs {
+       struct mpc5125_descriptor rbd[FEC_RBD_NUM]; /* RBD ring */
+       struct mpc5125_descriptor tbd[FEC_TBD_NUM]; /* TBD ring */
+       struct mpc5125_frame recv_frames[FEC_RBD_NUM]; /* receive buff */
+} ;
+
+struct mpc5125_fec_priv {
+       struct fec5125 *eth;
+       struct mpc5125_buff_descs *bdBase; /* BD rings and recv buffer */
+       size_t rdb_idx; /* next receive BD to read */
+       size_t tdb_idx; /* next transmit BD to send */
+       size_t usedtdb_idx; /* next transmit BD to clean */
+       unsigned clean_tbd_cnt; /* the number of available transmit BDs */
+       unsigned char frame[FEC_MAX_PKT_SIZE];
+       size_t frame_idx;
+
+       phy_interface_t interface; /* transceiver type */
+       u32 phy_flags; /* nowhere used ?? */
+       unsigned phy_addr;
+       struct mii_bus miibus;
+};
+
+static void mpc5125_fec_collect_frame(struct mpc5125_fec_priv *fec,
+                                       const void *buf, size_t length)
+{
+       memcpy(&fec->frame[fec->frame_idx], buf, length - fec->frame_idx);
+       fec->frame_idx = length; /* FIXME not "+=" here???? */
+}
+
+static void mpc5125_fec_init_buffer_ring(struct mpc5125_fec_priv *fec)
+{
+       void *base;
+
+       base = xzalloc(sizeof(struct mpc5125_buff_descs) + 0x1f);
+       /* this buffer must be quad word aligned */
+       base = (void *)((unsigned)base & ~0xf);
+
+       fec->bdBase = base;
+}
+
+static void mpc5125_fec_activate_transmission(struct mpc5125_fec_priv *fec)
+{
+       /* Activate transmit Buffer Descriptor polling */
+       out_be32(&fec->eth->x_des_active, 0x01000000);
+}
+
+static void mpc5125_fec_disable_transmission(struct mpc5125_fec_priv *fec)
+{
+       /* nothing to be done here */
+}
+
+static void mpc5125_fec_activate_reception(struct mpc5125_fec_priv *fec)
+{
+       out_be32(&fec->eth->r_des_active, 0x01000000);
+}
+
+static void mpc5125_fec_disable_reception(struct mpc5125_fec_priv *fec)
+{
+       /* nothing to be done here */
+}
+
+static void mpc5125_fec_clear_reception_event(struct mpc5125_fec_priv *fec)
+{
+       /* nothing to be done here */
+}
+
+/* keep MII frequency below 2.5 MHz */
+static unsigned mpc5125_fec_limit_mii_clock(void)
+{
+       u32 reg;
+
+       /* MII clock is 1 / (MII_SPEED x 2) */
+       reg = get_ips_clock() + 2500000;
+       reg /= 5000000;
+       return reg + 1;
+}
+
+/* MII-interface related functions */
+static int fec5125_miibus_read(struct mii_bus *bus, int phy_addr, int reg_addr)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)bus->priv;
+       int rc;
+
+       /* clear mii transfer status first */
+       out_be32(&fec->eth->ievent, FEC_IEVENT_MII);
+       /*
+        * reading from any PHY's register is done by properly
+        * programming the FEC's MII data register.
+        */
+       out_be32(&fec->eth->mii_data, FEC_MII_DATA_ST | FEC_MII_DATA_OP_RD |
+                               FEC_MII_DATA_TA | FEC_MII_DATA_PA(phy_addr) |
+                               FEC_MII_DATA_RA(reg_addr));
+
+       rc = wait_on_timeout(500 * MSECOND,
+                               in_be32(&fec->eth->ievent) & FEC_IEVENT_MII);
+       if (rc < 0) {
+               dev_err(bus->parent, "MDIO read timed out\n");
+               return rc;
+       }
+
+       /* it's now safe to read the PHY's register */
+       return in_be32(&fec->eth->mii_data) & FEC_MII_DATA_DATAMSK;
+}
+
+static int fec5125_miibus_write(struct mii_bus *bus, int phy_addr,
+                                               int reg_addr, u16 data)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)bus->priv;
+       int rc;
+
+       /* clear mii transfer status first */
+       out_be32(&fec->eth->ievent, FEC_IEVENT_MII);
+
+       out_be32(&fec->eth->mii_data, FEC_MII_DATA_ST | FEC_MII_DATA_OP_WR |
+                               FEC_MII_DATA_TA | FEC_MII_DATA_PA(phy_addr) |
+                               FEC_MII_DATA_RA(reg_addr) | data);
+
+       rc = wait_on_timeout(500 * MSECOND,
+                               in_be32(&fec->eth->ievent) & FEC_IEVENT_MII);
+       if (rc < 0)
+               dev_err(bus->parent, "MDIO write timed out\n");
+
+       return rc;
+}
+
+/* initialize the receive buffer descriptors */
+static int mpc5125_fec_rbd_init(struct mpc5125_fec_priv *fec)
+{
+       size_t ix;
+
+       for (ix = 0; ix < FEC_RBD_NUM; ix++) {
+               fec->bdBase->rbd[ix].dpointer = 
(u32)&fec->bdBase->recv_frames[ix];
+               fec->bdBase->rbd[ix].status = FEC_RBD_EMPTY;
+               fec->bdBase->rbd[ix].dlength = 0;
+       }
+
+       /* let the last buffer descriptor close the ring */
+       fec->bdBase->rbd[ix - 1].status |= FEC_RBD_WRAP;
+       fec->rdb_idx = 0;
+
+       return 0;
+}
+
+/* initialize the transmitt buffer descriptors */
+static void mpc5125_fec_tbd_init(struct mpc5125_fec_priv *fec)
+{
+       int ix;
+
+       for (ix = 0; ix < FEC_TBD_NUM; ix++)
+               fec->bdBase->tbd[ix].status = 0;
+
+       /* let the last buffer descriptor close the ring */
+       fec->bdBase->tbd[ix - 1].status |= FEC_TBD_WRAP;
+
+       fec->tdb_idx = 0;
+       fec->usedtdb_idx = 0;
+       fec->clean_tbd_cnt = FEC_TBD_NUM;
+}
+
+static void mpc5125_fec_rbd_clean(struct mpc5125_fec_priv *fec,
+                                               struct mpc5125_descriptor *rbd)
+{
+       /* Reset buffer descriptor as empty */
+       if ((fec->rdb_idx) == (FEC_RBD_NUM - 1))
+               rbd->status = (FEC_RBD_WRAP | FEC_RBD_EMPTY);
+       else
+               rbd->status = FEC_RBD_EMPTY;
+
+       rbd->dlength = 0;
+
+       /* ensure all written data has hit the memory */
+       barrier();
+
+       /* Now, we have an empty RxBD, restart the engine */
+       mpc5125_fec_activate_reception(fec);
+
+       /* Increment BD count */
+       fec->rdb_idx = (fec->rdb_idx + 1) % FEC_RBD_NUM;
+}
+
+static void mpc5125_fec_tbd_scrub(struct mpc5125_fec_priv *fec)
+{
+       struct mpc5125_descriptor *used_tbd;
+
+       /* process all the consumed TBDs */
+       while (fec->clean_tbd_cnt < FEC_TBD_NUM) {
+               used_tbd = &fec->bdBase->tbd[fec->usedtdb_idx];
+               if (used_tbd->status & FEC_TBD_READY) {
+                       return;
+               }
+
+               /* clean this buffer descriptor */
+               if (fec->usedtdb_idx == (FEC_TBD_NUM - 1))
+                       used_tbd->status = FEC_TBD_WRAP;
+               else
+                       used_tbd->status = 0;
+
+               /* update some indeces for a correct handling of the TBD ring */
+               fec->clean_tbd_cnt++;
+               fec->usedtdb_idx = (fec->usedtdb_idx + 1) % FEC_TBD_NUM;
+       }
+
+       barrier();
+}
+
+static int mpc5125_fec_get_ethaddr(struct eth_device *dev, unsigned char *mac)
+{
+       /* no eeprom */
+       return -ENODEV;
+}
+
+static int mpc5125_fec_set_ethaddr(struct eth_device *dev, unsigned char *mac)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)dev->priv;
+       u8 currByte;
+       int byte, bit;
+       u32 crc = 0xffffffff;
+
+       /*
+        * The algorithm used is the following:
+        * we loop on each of the six bytes of the provided address,
+        * and we compute the CRC by left-shifting the previous
+        * value by one position, so that each bit in the current
+        * byte of the address may contribute the calculation. If
+        * the latter and the MSB in the CRC are different, then
+        * the CRC value so computed is also ex-ored with the
+        * "polynomium generator". The current byte of the address
+        * is also shifted right by one bit at each iteration.
+        * This is because the CRC generatore in hardware is implemented
+        * as a shift-register with as many ex-ores as the radixes
+        * in the polynomium. This suggests that we represent the
+        * polynomiumm itself as a 32-bit constant.
+        */
+       for (byte = 0; byte < 6; byte++) {
+               currByte = mac[byte];
+               for (bit = 0; bit < 8; bit++) {
+                       if ((currByte & 0x01) ^ (crc & 0x01)) {
+                               crc >>= 1;
+                               crc = crc ^ 0xedb88320;
+                       } else {
+                               crc >>= 1;
+                       }
+                       currByte >>= 1;
+               }
+       }
+
+       crc = crc >> 26;
+
+       /* Set individual hash table register */
+       if (crc >= 32) {
+               out_be32(&fec->eth->iaddr1, (1 << (crc - 32)));
+               out_be32(&fec->eth->iaddr2, 0);
+       } else {
+               out_be32(&fec->eth->iaddr1, 0);
+               out_be32(&fec->eth->iaddr2, (1 << crc));
+       }
+
+       /* Set physical address */
+       out_be32(&fec->eth->paddr1, (mac[0] << 24) + (mac[1] << 16) +
+                                       (mac[2] <<  8) + mac[3]);
+       out_be32(&fec->eth->paddr2, (mac[4] << 24) + (mac[5] << 16) + 0x8808);
+
+       return 0;
+}
+
+static int mpc5125_fec_init(struct eth_device *dev)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)dev->priv;
+
+       /* Initilize both BD rings */
+       mpc5125_fec_rbd_init(fec);
+       mpc5125_fec_tbd_init(fec);
+
+       /* Clear FEC-Lite interrupt event register(IEVENT) */
+       out_be32(&fec->eth->ievent, 0xffffffff);
+
+       /* Set interrupt mask register */
+       out_be32(&fec->eth->imask, 0x00000000);
+
+       /* Set transmit fifo watermark register(X_WMRK), default = 64 */
+       out_be32(&fec->eth->x_wmrk, 0x0);
+
+       /* Setup phy interface mode and max frame length */
+       switch (fec->interface) {
+       case PHY_INTERFACE_MODE_MII:
+               out_be32(&fec->eth->r_cntrl, (FEC_MAX_FRAME_LEN << 16) | 0x024);
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               out_be32(&fec->eth->r_cntrl, (FEC_MAX_FRAME_LEN << 16) | 0x124);
+               break;
+       default:
+               dev_err(&dev->dev, "Unsupported phy interface mode\n");
+               return -EINVAL;
+       }
+
+       /* Setup MII_SPEED and do not drop the Preamble. */
+       out_be32(&fec->eth->mii_speed, (mpc5125_fec_limit_mii_clock()) << 1);
+
+       /* Set Opcode/Pause Duration Register */
+       out_be32(&fec->eth->op_pause, 0x00010020);
+
+       /* Set multicast address filter */
+       out_be32(&fec->eth->gaddr1, 0x00000000);
+       out_be32(&fec->eth->gaddr2, 0x00000000);
+
+       /* Half-duplex, heartbeat disabled */
+       out_be32(&fec->eth->x_cntrl, 0x00000000);
+
+       /* Enable MIB counters */
+       out_be32(&fec->eth->mib_control, 0x0);
+
+       /* Setup recv fifo start and buff size */
+       out_be32(&fec->eth->r_fstart, 0x500);
+       out_be32(&fec->eth->r_buff_size, FEC_MAX_PKT_SIZE);
+
+       /* Setup BD base addresses */
+       out_be32(&fec->eth->r_des_start, (u32)fec->bdBase->rbd);
+       out_be32(&fec->eth->x_des_start, (u32)fec->bdBase->tbd);
+
+       /* DMA Control */
+       out_be32(&fec->eth->dma_control, 0xc0000000);
+
+       /* Enable FEC */
+       setbits_be32(&fec->eth->ecntrl, 0x00000002);
+
+       return 1;
+}
+
+static int mpc5125_fec_open(struct eth_device *edev)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)edev->priv;
+
+       mpc5125_fec_activate_reception(fec);
+
+       return phy_device_connect(edev, &fec->miibus, fec->phy_addr, NULL,
+                                       fec->phy_flags, fec->interface);
+}
+
+static void mpc5125_fec_halt(struct eth_device *dev)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)dev->priv;
+       int counter = 0xffff;
+
+       /* mask FEC chip interrupts */
+       out_be32(&fec->eth->imask, 0);
+
+       /* issue graceful stop command to the FEC transmitter if necessary */
+       setbits_be32(&fec->eth->x_cntrl, 0x00000001);
+
+       /* wait for graceful stop to register */
+       while ((counter--) && (!(in_be32(&fec->eth->ievent) & 0x10000000)))
+               ;
+
+       /* Disable the Ethernet Controller */
+       clrbits_be32(&fec->eth->ecntrl, 0x00000002);
+
+       /* Issue a reset command to the FEC chip */
+       setbits_be32(&fec->eth->ecntrl, 0x1);
+
+       /* wait at least 16 clock cycles */
+       udelay(10);
+}
+
+static int mpc5125_fec_send(struct eth_device *dev, void *eth_data, int 
data_length)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)dev->priv;
+       struct mpc5125_descriptor *tbd;
+       int rc;
+
+       /* Clear Tx BD ring at first */
+       mpc5125_fec_tbd_scrub(fec);
+
+       /* Check for valid length of data. */
+       if ((data_length > 1500) || (data_length <= 0))
+               return -EINVAL;
+
+       /* Check the number of vacant TxBDs. */
+       if (fec->clean_tbd_cnt < 1) {
+               dev_err(&dev->dev, "No available TxBDs ...\n");
+               return -ENOMEM;
+       }
+
+       /* Get the first free TxBD to send the mac header */
+       tbd = &fec->bdBase->tbd[fec->tdb_idx];
+       fec->clean_tbd_cnt -= 1;
+       tbd->dlength = data_length;
+       tbd->dpointer = (u32)eth_data;
+       tbd->status |= FEC_TBD_LAST | FEC_TBD_TC | FEC_TBD_READY;
+       fec->tdb_idx = (fec->tdb_idx + 1) % FEC_TBD_NUM;
+
+       /* ensure all written data has hit the memory */
+       barrier();
+
+       mpc5125_fec_activate_transmission(fec);
+
+       rc = wait_on_timeout(500 * MSECOND,
+                               !(in_be16(&tbd->status) & FEC_TBD_READY));
+       if (rc != 0) {
+               dev_err(&dev->dev, "Time out while waiting for 
transmission...\n");
+       } else {
+               dev_dbg(&dev->dev, "sucessfully sent (%hX)\n", 
in_be16(&tbd->status));
+       }
+
+       return rc;
+}
+
+static void mpc5125_fec_debug_rx_header(void *buffer, size_t length)
+{
+#ifdef DEBUG_RX_HEADER
+       int i;
+
+       printf ("recv data length 0x%08x data hdr: ", length);
+       for (i = 0; i < 14; i++)
+               printf ("%x ", *((u8*)buffer + i));
+       printf("\n");
+#endif
+}
+
+static int mpc5125_fec_recv(struct eth_device *dev)
+{
+       struct mpc5125_fec_priv *fec = (struct mpc5125_fec_priv *)dev->priv;
+       struct mpc5125_descriptor *pRbd = &fec->bdBase->rbd[fec->rdb_idx];
+       unsigned long ievent;
+       int frame_length = 0;
+
+       /* Check if any critical events have happened */
+       ievent = in_be32(&fec->eth->ievent);
+       out_be32(&fec->eth->ievent, ievent);
+       if (ievent & (FEC_IEVENT_BABT | FEC_IEVENT_XFIFO_ERROR |
+                               FEC_IEVENT_RFIFO_ERROR)) {
+               /* BABT, Rx/Tx FIFO errors */
+               mpc5125_fec_halt(dev);
+               mpc5125_fec_init(dev);
+               return 0;
+       }
+
+       if (ievent & FEC_IEVENT_HBERR) {
+               /* Heartbeat error */
+               setbits_be32(&fec->eth->x_cntrl, 0x00000001);
+       }
+
+       if (ievent & FEC_IEVENT_GRA) {
+               /* Graceful stop complete */
+               if (in_be32(&fec->eth->x_cntrl) & 0x00000001) {
+                       mpc5125_fec_halt(dev);
+                       clrbits_be32(&fec->eth->x_cntrl, 0x00000001);;
+                       mpc5125_fec_init(dev);
+               }
+       }
+
+       if (!(pRbd->status & FEC_RBD_EMPTY)) {
+               if (!(pRbd->status & FEC_RBD_ERR) &&
+                                               ((pRbd->dlength - 4) > 14)) {
+                       /* calculate payload size */
+                       frame_length = pRbd->dlength;
+                       if (pRbd->status & FEC_RBD_LAST)
+                               frame_length -= - 4;
+
+                       mpc5125_fec_debug_rx_header((void *)pRbd->dpointer,
+                                                               pRbd->dlength);
+                       mpc5125_fec_collect_frame(fec, (void *)pRbd->dpointer,
+                                                               frame_length);
+                       if (pRbd->status & FEC_RBD_LAST) {
+                               /* pass it to upper layers on demand */
+                               net_receive(dev, fec->frame, frame_length);
+                               fec->frame_idx = 0;
+                       }
+               }
+
+               /* Reset buffer descriptor as empty */
+               mpc5125_fec_rbd_clean(fec, pRbd);
+       }
+
+       /* Try to fill Buffer Descriptors */
+       out_be32(&fec->eth->r_des_active, 0x01000000);
+
+       return frame_length;
+}
+
+static int mpc5125_fec_probe(struct device_d *dev)
+{
+       struct fec_platform_data *pdata = dev->platform_data;
+       struct eth_device *edev;
+       struct mpc5125_fec_priv *fec;
+
+       edev = (struct eth_device *)xmalloc(sizeof(struct eth_device));
+       fec = (struct mpc5125_fec_priv *)xmalloc(sizeof(*fec));
+       dev->priv = edev;
+       edev->priv = fec;
+       edev->open = mpc5125_fec_open;
+       edev->init = mpc5125_fec_init;
+       edev->send = mpc5125_fec_send;
+       edev->recv = mpc5125_fec_recv;
+       edev->halt = mpc5125_fec_halt;
+       edev->get_ethaddr = mpc5125_fec_get_ethaddr;
+       edev->set_ethaddr = mpc5125_fec_set_ethaddr;
+       edev->parent = dev;
+
+       fec->eth = (struct fec5125 *)dev_request_mem_region(dev, 0);
+
+       mpc5125_fec_init_buffer_ring(fec);
+
+       fec->interface = pdata->xcv_type;
+       fec->phy_addr = pdata->phy_addr;
+
+       mpc5xxx_enable_fec_clock(dev->id);
+
+       fec->miibus.read = fec5125_miibus_read;
+       fec->miibus.write = fec5125_miibus_write;
+
+       fec->miibus.priv = fec;
+       fec->miibus.parent = dev;
+
+       /* do some FEC initialization once */
+
+       /* Clean up space FEC's MIB... */
+       memset(&fec->eth->mib1[0],  0x00, sizeof(fec->eth->mib1));
+       memset(&fec->eth->mib2[0],  0x00, sizeof(fec->eth->mib2));
+       /* disable all interrupts */
+       out_be32(&fec->eth->imask, 0x00000000);
+       /* init the status by clearing all bits */
+       out_be32(&fec->eth->ievent, 0xffffffff);
+
+       mdiobus_register(&fec->miibus);
+
+       eth_register(edev);
+       return 0;
+}
+
+static void mpc5125_fec_remove(struct device_d *dev)
+{
+       struct eth_device *edev = dev->priv;
+
+       mpc5125_fec_halt(edev);
+}
+
+static struct driver_d mpc5125_driver = {
+        .name  = "fec_mpc5125",
+        .probe = mpc5125_fec_probe,
+       .remove = mpc5125_fec_remove,
+};
+device_platform_driver(mpc5125_driver);
diff --git a/drivers/net/fec_mpc5125.h b/drivers/net/fec_mpc5125.h
new file mode 100644
index 0000000..e69de29
diff --git a/include/fec.h b/include/fec.h
index 699761a..8b6fb0e 100644
--- a/include/fec.h
+++ b/include/fec.h
@@ -25,7 +25,7 @@
 
 /*
  * Define the phy connected externally for FEC drivers
- * (like MPC52xx and i.MX27)
+ * (like MPC52xx, MPC512x and i.MX27)
  */
 struct fec_platform_data {
        phy_interface_t xcv_type;
-- 
2.1.0


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to