summaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2019-01-31 09:00:01 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2019-03-13 10:27:42 +0100
commitec09152b909d88a1bd26ba125d87d74a5ca424d8 (patch)
tree82d66f02f99520b48063755b69c197b76345d16a /drivers/net
parent3b21c8a8b4bf9592fbb99a298c2df0e98b8ff3e9 (diff)
downloadbarebox-ec09152b909d88a1bd26ba125d87d74a5ca424d8.tar.gz
barebox-ec09152b909d88a1bd26ba125d87d74a5ca424d8.tar.xz
net: Add Freescale FMan ethernet support
This adds ethernet support for the Freecale Layerscape SoCs. The architecture in these SoCs is called "Data Path Acceleration Architecture" (DPAA). It is comprised of: - The Queue Manager (QMan) - Buffer Manager (BMan) - Frame Manager (FMan) - Multirate Ethernet Media Access Controller (mEMAC) The code is based on the corresponding U-Boot driver enriched with device tree parsing and proper device driver support. Tested on LS1046a, should work on other SoCs aswell with some minor quirks. SerDes support has been removed for now. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/net')
-rw-r--r--drivers/net/Kconfig8
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/fsl-fman.c1333
3 files changed, 1342 insertions, 0 deletions
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 1aa096f005..3e3de5a975 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -137,6 +137,14 @@ config DRIVER_NET_FEC_IMX
depends on ARCH_HAS_FEC_IMX
select PHYLIB
+config DRIVER_NET_FSL_FMAN
+ bool "Freescale fman ethernet driver"
+ select PHYLIB
+ select FSL_QE_FIRMWARE
+ help
+ This option enabled support for the Freescale fman core found
+ on Layerscape SoCs.
+
config DRIVER_NET_GIANFAR
bool "Gianfar Ethernet"
depends on ARCH_MPC85XX
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 304bbba02d..6ccd22cc10 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_DRIVER_NET_ENC28J60) += enc28j60.o
obj-$(CONFIG_DRIVER_NET_EP93XX) += ep93xx.o
obj-$(CONFIG_DRIVER_NET_ETHOC) += ethoc.o
obj-$(CONFIG_DRIVER_NET_FEC_IMX) += fec_imx.o
+obj-$(CONFIG_DRIVER_NET_FSL_FMAN) += fsl-fman.o
obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o
obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o
obj-$(CONFIG_DRIVER_NET_MACB) += macb.o
diff --git a/drivers/net/fsl-fman.c b/drivers/net/fsl-fman.c
new file mode 100644
index 0000000000..1a11ca4926
--- /dev/null
+++ b/drivers/net/fsl-fman.c
@@ -0,0 +1,1333 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2009-2012 Freescale Semiconductor, Inc.
+ * Dave Liu <daveliu@freescale.com>
+ */
+
+#include <common.h>
+#include <io.h>
+#include <malloc.h>
+#include <net.h>
+#include <init.h>
+#include <of_net.h>
+#include <dma.h>
+#include <firmware.h>
+#include <soc/fsl/qe.h>
+#include <soc/fsl/fsl_fman.h>
+#include <soc/fsl/fsl_memac.h>
+
+/* Port ID */
+#define OH_PORT_ID_BASE 0x01
+#define MAX_NUM_OH_PORT 7
+#define RX_PORT_1G_BASE 0x08
+#define MAX_NUM_RX_PORT_1G 8
+#define RX_PORT_10G_BASE 0x10
+#define RX_PORT_10G_BASE2 0x08
+#define TX_PORT_1G_BASE 0x28
+#define MAX_NUM_TX_PORT_1G 8
+#define TX_PORT_10G_BASE 0x30
+#define TX_PORT_10G_BASE2 0x28
+#define MIIM_TIMEOUT 0xFFFF
+
+struct fm_muram {
+ void *base;
+ void *top;
+ size_t size;
+ void *alloc;
+};
+#define FM_MURAM_RES_SIZE 0x01000
+
+/* Rx/Tx buffer descriptor */
+struct fm_port_bd {
+ u16 status;
+ u16 len;
+ u32 res0;
+ u16 res1;
+ u16 buf_ptr_hi;
+ u32 buf_ptr_lo;
+};
+
+/* Common BD flags */
+#define BD_LAST 0x0800
+
+/* Rx BD status flags */
+#define RxBD_EMPTY 0x8000
+#define RxBD_LAST BD_LAST
+#define RxBD_FIRST 0x0400
+#define RxBD_PHYS_ERR 0x0008
+#define RxBD_SIZE_ERR 0x0004
+#define RxBD_ERROR (RxBD_PHYS_ERR | RxBD_SIZE_ERR)
+
+/* Tx BD status flags */
+#define TxBD_READY 0x8000
+#define TxBD_LAST BD_LAST
+
+/* Rx/Tx queue descriptor */
+struct fm_port_qd {
+ u16 gen;
+ u16 bd_ring_base_hi;
+ u32 bd_ring_base_lo;
+ u16 bd_ring_size;
+ u16 offset_in;
+ u16 offset_out;
+ u16 res0;
+ u32 res1[0x4];
+};
+
+/* IM global parameter RAM */
+struct fm_port_global_pram {
+ u32 mode; /* independent mode register */
+ u32 rxqd_ptr; /* Rx queue descriptor pointer */
+ u32 txqd_ptr; /* Tx queue descriptor pointer */
+ u16 mrblr; /* max Rx buffer length */
+ u16 rxqd_bsy_cnt; /* RxQD busy counter, should be cleared */
+ u32 res0[0x4];
+ struct fm_port_qd rxqd; /* Rx queue descriptor */
+ struct fm_port_qd txqd; /* Tx queue descriptor */
+ u32 res1[0x28];
+};
+
+#define FM_PRAM_SIZE sizeof(struct fm_port_global_pram)
+#define FM_PRAM_ALIGN 256
+#define PRAM_MODE_GLOBAL 0x20000000
+#define PRAM_MODE_GRACEFUL_STOP 0x00800000
+
+#define FM_FREE_POOL_SIZE 0x20000 /* 128K bytes */
+#define FM_FREE_POOL_ALIGN 256
+
+/* Fman ethernet private struct */
+struct fm_eth {
+ struct fm_bmi_tx_port *tx_port;
+ struct fm_bmi_rx_port *rx_port;
+ phy_interface_t enet_if;
+ struct eth_device edev;
+ struct device_d *dev;
+ struct fm_port_global_pram *rx_pram; /* Rx parameter table */
+ struct fm_port_global_pram *tx_pram; /* Tx parameter table */
+ void *rx_bd_ring; /* Rx BD ring base */
+ void *cur_rxbd; /* current Rx BD */
+ void *rx_buf; /* Rx buffer base */
+ void *tx_bd_ring; /* Tx BD ring base */
+ void *cur_txbd; /* current Tx BD */
+ struct memac *regs;
+};
+
+#define RX_BD_RING_SIZE 8
+#define TX_BD_RING_SIZE 8
+#define MAX_RXBUF_LOG2 11
+#define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2)
+
+struct fsl_fman_mdio {
+ struct mii_bus bus;
+ struct memac_mdio_controller *regs;
+};
+
+enum fman_port_type {
+ FMAN_PORT_TYPE_RX = 0, /* RX Port */
+ FMAN_PORT_TYPE_TX, /* TX Port */
+};
+
+struct fsl_fman_port {
+ void *regs;
+ enum fman_port_type type;
+ struct fm_bmi_rx_port *rxport;
+ struct fm_bmi_tx_port *txport;
+};
+
+static struct fm_eth *to_fm_eth(struct eth_device *edev)
+{
+ return container_of(edev, struct fm_eth, edev);
+}
+
+static struct fm_muram muram;
+
+static void *fm_muram_base(void)
+{
+ return muram.base;
+}
+
+static void *fm_muram_alloc(size_t size, unsigned long align)
+{
+ void *ret;
+ unsigned long align_mask;
+ size_t off;
+ void *save;
+
+ align_mask = align - 1;
+ save = muram.alloc;
+
+ off = (unsigned long)save & align_mask;
+ if (off != 0)
+ muram.alloc += (align - off);
+ off = size & align_mask;
+ if (off != 0)
+ size += (align - off);
+ if ((muram.alloc + size) >= muram.top) {
+ muram.alloc = save;
+ printf("%s: run out of ram.\n", __func__);
+ return NULL;
+ }
+
+ ret = muram.alloc;
+ muram.alloc += size;
+
+ return ret;
+}
+
+/*
+ * fm_upload_ucode - Fman microcode upload worker function
+ *
+ * This function does the actual uploading of an Fman microcode
+ * to an Fman.
+ */
+static int fm_upload_ucode(struct fm_imem *imem,
+ u32 *ucode, unsigned int size)
+{
+ unsigned int i;
+ unsigned int timeout = 1000000;
+
+ /* enable address auto increase */
+ out_be32(&imem->iadd, IRAM_IADD_AIE);
+ /* write microcode to IRAM */
+ for (i = 0; i < size / 4; i++)
+ out_be32(&imem->idata, (be32_to_cpu(ucode[i])));
+
+ /* verify if the writing is over */
+ out_be32(&imem->iadd, 0);
+ while ((in_be32(&imem->idata) != be32_to_cpu(ucode[0])) && --timeout)
+ ;
+ if (!timeout) {
+ printf("microcode upload timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ /* enable microcode from IRAM */
+ out_be32(&imem->iready, IRAM_READY);
+
+ return 0;
+}
+
+static int fman_upload_firmware(struct device_d *dev, struct fm_imem *fm_imem)
+{
+ int i, size, ret;
+ const struct qe_firmware *firmware;
+
+ get_builtin_firmware(fsl_fman_ucode_ls1046_r1_0_106_4_18_bin, &firmware, &size);
+
+ ret = qe_validate_firmware(firmware, size);
+ if (ret)
+ return ret;
+
+ if (firmware->count != 1) {
+ dev_err(dev, "Invalid data in firmware header\n");
+ return -EINVAL;
+ }
+
+ /* Loop through each microcode. */
+ for (i = 0; i < firmware->count; i++) {
+ const struct qe_microcode *ucode = &firmware->microcode[i];
+
+ /* Upload a microcode if it's present */
+ if (be32_to_cpu(ucode->code_offset)) {
+ u32 ucode_size;
+ u32 *code;
+ dev_info(dev, "Uploading microcode version %u.%u.%u\n",
+ ucode->major, ucode->minor,
+ ucode->revision);
+ code = (void *)firmware +
+ be32_to_cpu(ucode->code_offset);
+ ucode_size = sizeof(u32) * be32_to_cpu(ucode->count);
+ ret = fm_upload_ucode(fm_imem, code, ucode_size);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static u32 fm_assign_risc(int port_id)
+{
+ u32 risc_sel, val;
+ risc_sel = (port_id & 0x1) ? FMFPPRC_RISC2 : FMFPPRC_RISC1;
+ val = (port_id << FMFPPRC_PORTID_SHIFT) & FMFPPRC_PORTID_MASK;
+ val |= ((risc_sel << FMFPPRC_ORA_SHIFT) | risc_sel);
+
+ return val;
+}
+
+static void fm_init_fpm(struct fm_fpm *fpm)
+{
+ int i, port_id;
+ u32 val;
+
+ setbits_be32(&fpm->fmfpee, FMFPEE_EHM | FMFPEE_UEC |
+ FMFPEE_CER | FMFPEE_DER);
+
+ /* IM mode, each even port ID to RISC#1, each odd port ID to RISC#2 */
+
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Tx 1G port */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE;
+ val = fm_assign_risc(port_id);
+ out_be32(&fpm->fpmprc, val);
+
+ /* disable the dispatch limit in IM case */
+ out_be32(&fpm->fpmflc, FMFP_FLC_DISP_LIM_NONE);
+ /* clear events */
+ out_be32(&fpm->fmfpee, FMFPEE_CLEAR_EVENT);
+
+ /* clear risc events */
+ for (i = 0; i < 4; i++)
+ out_be32(&fpm->fpmcev[i], 0xffffffff);
+
+ /* clear error */
+ out_be32(&fpm->fpmrcr, FMFP_RCR_MDEC | FMFP_RCR_IDEC);
+}
+
+static int fm_init_bmi(struct fm_bmi_common *bmi)
+{
+ int blk, i, port_id;
+ u32 val;
+ size_t offset;
+ void *base;
+
+ /* alloc free buffer pool in MURAM */
+ base = fm_muram_alloc(FM_FREE_POOL_SIZE, FM_FREE_POOL_ALIGN);
+ if (!base) {
+ printf("%s: no muram for free buffer pool\n", __func__);
+ return -ENOMEM;
+ }
+ offset = base - fm_muram_base();
+
+ /* Need 128KB total free buffer pool size */
+ val = offset / 256;
+ blk = FM_FREE_POOL_SIZE / 256;
+ /* in IM, we must not begin from offset 0 in MURAM */
+ val |= ((blk - 1) << FMBM_CFG1_FBPS_SHIFT);
+ out_be32(&bmi->fmbm_cfg1, val);
+
+ /* disable all BMI interrupt */
+ out_be32(&bmi->fmbm_ier, FMBM_IER_DISABLE_ALL);
+
+ /* clear all events */
+ out_be32(&bmi->fmbm_ievr, FMBM_IEVR_CLEAR_ALL);
+
+ /*
+ * set port parameters - FMBM_PP_x
+ * max tasks 10G Rx/Tx=12, 1G Rx/Tx 4, others is 1
+ * max dma 10G Rx/Tx=3, others is 1
+ * set port FIFO size - FMBM_PFS_x
+ * 4KB for all Rx and Tx ports
+ */
+ /* offline/parser port */
+ for (i = 0; i < MAX_NUM_OH_PORT; i++) {
+ port_id = OH_PORT_ID_BASE + i - 1;
+ /* max tasks=1, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], 0);
+ /* port FIFO size - 256 bytes, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], 0);
+ }
+ /* Rx 1G port */
+ for (i = 0; i < MAX_NUM_RX_PORT_1G; i++) {
+ port_id = RX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Tx 1G port FIFO size - 4KB, no extra */
+ for (i = 0; i < MAX_NUM_TX_PORT_1G; i++) {
+ port_id = TX_PORT_1G_BASE + i - 1;
+ /* max tasks=4, max dma=1, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(4));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+ }
+ /* Rx 10G port */
+ port_id = RX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* Tx 10G port */
+ port_id = TX_PORT_10G_BASE - 1;
+ /* max tasks=12, max dma=3, no extra */
+ out_be32(&bmi->fmbm_pp[port_id], FMBM_PP_MXT(12) | FMBM_PP_MXD(3));
+ /* FIFO size - 4KB, no extra */
+ out_be32(&bmi->fmbm_pfs[port_id], FMBM_PFS_IFSZ(0xf));
+
+ /* initialize internal buffers data base (linked list) */
+ out_be32(&bmi->fmbm_init, FMBM_INIT_START);
+
+ return 0;
+}
+
+static void fm_init_qmi(struct fm_qmi_common *qmi)
+{
+ /* disable all error interrupts */
+ out_be32(&qmi->fmqm_eien, FMQM_EIEN_DISABLE_ALL);
+ /* clear all error events */
+ out_be32(&qmi->fmqm_eie, FMQM_EIE_CLEAR_ALL);
+
+ /* disable all interrupts */
+ out_be32(&qmi->fmqm_ien, FMQM_IEN_DISABLE_ALL);
+ /* clear all interrupts */
+ out_be32(&qmi->fmqm_ie, FMQM_IE_CLEAR_ALL);
+}
+
+static int fm_init_common(struct device_d *dev, struct ccsr_fman *reg)
+{
+ int ret;
+
+ /* Upload the Fman microcode if it's present */
+ ret = fman_upload_firmware(dev, &reg->fm_imem);
+ if (ret)
+ return ret;
+
+ fm_init_qmi(&reg->fm_qmi_common);
+ fm_init_fpm(&reg->fm_fpm);
+
+ /* clear DMA status */
+ setbits_be32(&reg->fm_dma.fmdmsr, FMDMSR_CLEAR_ALL);
+
+ /* set DMA mode */
+ setbits_be32(&reg->fm_dma.fmdmmr, FMDMMR_SBER);
+
+ return fm_init_bmi(&reg->fm_bmi_common);
+}
+
+#define memac_out_32(a, v) out_be32(a, v)
+#define memac_in_32(a) in_be32(a)
+#define memac_clrbits_32(a, v) clrbits_be32(a, v)
+#define memac_setbits_32(a, v) setbits_be32(a, v)
+
+static int memac_mdio_write(struct mii_bus *bus, int port_addr, int regnum, u16 value)
+{
+ struct fsl_fman_mdio *priv = container_of(bus, struct fsl_fman_mdio, bus);
+ struct memac_mdio_controller *regs = priv->regs;
+ u32 mdio_ctl;
+
+ memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the port and dev addr */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(regnum);
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Write the value to the register */
+ memac_out_32(&regs->mdio_data, MDIO_DATA(value));
+
+ /* Wait till the MDIO write is complete */
+ while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ return 0;
+}
+
+static int memac_mdio_read(struct mii_bus *bus, int port_addr, int regnum)
+{
+ struct fsl_fman_mdio *priv = container_of(bus, struct fsl_fman_mdio, bus);
+ struct memac_mdio_controller *regs = priv->regs;
+ u32 mdio_ctl;
+
+ memac_clrbits_32(&regs->mdio_stat, MDIO_STAT_ENC);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Set the Port and Device Addrs */
+ mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(regnum);
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the bus is free */
+ while ((memac_in_32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+ ;
+
+ /* Initiate the read */
+ mdio_ctl |= MDIO_CTL_READ;
+ memac_out_32(&regs->mdio_ctl, mdio_ctl);
+
+ /* Wait till the MDIO write is complete */
+ while ((memac_in_32(&regs->mdio_data)) & MDIO_DATA_BSY)
+ ;
+
+ /* Return all Fs if nothing was there */
+ if (memac_in_32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
+ return 0xffff;
+
+ return memac_in_32(&regs->mdio_data) & 0xffff;
+}
+
+static u16 muram_readw(u16 *addr)
+{
+ unsigned long base = (unsigned long)addr & ~0x3UL;
+ u32 val32 = in_be32((void *)base);
+ int byte_pos;
+ u16 ret;
+
+ byte_pos = (unsigned long)addr & 0x3UL;
+ if (byte_pos)
+ ret = (u16)(val32 & 0x0000ffff);
+ else
+ ret = (u16)((val32 & 0xffff0000) >> 16);
+
+ return ret;
+}
+
+static void muram_writew(u16 *addr, u16 val)
+{
+ unsigned long base = (unsigned long)addr & ~0x3UL;
+ u32 org32 = in_be32((void *)base);
+ u32 val32;
+ int byte_pos;
+
+ byte_pos = (unsigned long)addr & 0x3UL;
+ if (byte_pos)
+ val32 = (org32 & 0xffff0000) | val;
+ else
+ val32 = (org32 & 0x0000ffff) | ((u32)val << 16);
+
+ out_be32((void *)base, val32);
+}
+
+static void bmi_rx_port_disable(struct fm_bmi_rx_port *rx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+
+ /* wait until the rx port is not busy */
+ while ((in_be32(&rx_port->fmbm_rst) & FMBM_RST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_rx_port_init(struct fm_bmi_rx_port *rx_port)
+{
+ /* set BMI to independent mode, Rx port disable */
+ out_be32(&rx_port->fmbm_rcfg, FMBM_RCFG_IM);
+ /* clear FOF in IM case */
+ out_be32(&rx_port->fmbm_rim, 0);
+ /* Rx frame next engine -RISC */
+ out_be32(&rx_port->fmbm_rfne, NIA_ENG_RISC | NIA_RISC_AC_IM_RX);
+ /* Rx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_ORDER | FMBM_RFCA_MR_MASK);
+ setbits_be32(&rx_port->fmbm_rfca, FMBM_RFCA_MR(4));
+ /* enable Rx statistic counters */
+ out_be32(&rx_port->fmbm_rstc, FMBM_RSTC_EN);
+ /* disable Rx performance counters */
+ out_be32(&rx_port->fmbm_rpc, 0);
+}
+
+static void bmi_tx_port_disable(struct fm_bmi_tx_port *tx_port)
+{
+ int timeout = 1000000;
+
+ clrbits_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+
+ /* wait until the tx port is not busy */
+ while ((in_be32(&tx_port->fmbm_tst) & FMBM_TST_BSY) && timeout--)
+ ;
+}
+
+static void bmi_tx_port_init(struct fm_bmi_tx_port *tx_port)
+{
+ /* set BMI to independent mode, Tx port disable */
+ out_be32(&tx_port->fmbm_tcfg, FMBM_TCFG_IM);
+ /* Tx frame next engine -RISC */
+ out_be32(&tx_port->fmbm_tfne, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ out_be32(&tx_port->fmbm_tfene, NIA_ENG_RISC | NIA_RISC_AC_IM_TX);
+ /* Tx command attribute - no order, MR[3] = 1 */
+ clrbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_ORDER | FMBM_TFCA_MR_MASK);
+ setbits_be32(&tx_port->fmbm_tfca, FMBM_TFCA_MR(4));
+ /* enable Tx statistic counters */
+ out_be32(&tx_port->fmbm_tstc, FMBM_TSTC_EN);
+ /* disable Tx performance counters */
+ out_be32(&tx_port->fmbm_tpc, 0);
+}
+
+static int fm_eth_rx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *rx_bd_ring_base;
+ void *rx_buf_pool;
+ u32 bd_ring_base_lo, bd_ring_base_hi;
+ u32 buf_lo, buf_hi;
+ struct fm_port_bd *rxbd;
+ struct fm_port_qd *rxqd;
+ struct fm_bmi_rx_port *bmi_rx_port = fm_eth->rx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = fm_muram_alloc(FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ if (!pram) {
+ printf("%s: No muram for Rx global parameter\n", __func__);
+ return -ENOMEM;
+ }
+
+ fm_eth->rx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (void *)pram - fm_muram_base();
+
+ /* enable global mode- snooping data buffers and BDs */
+ out_be32(&pram->mode, PRAM_MODE_GLOBAL);
+
+ /* init the Rx queue descriptor pointer */
+ out_be32(&pram->rxqd_ptr, pram_page_offset + 0x20);
+
+ /* set the max receive buffer length, power of 2 */
+ muram_writew(&pram->mrblr, MAX_RXBUF_LOG2);
+
+ /* alloc Rx buffer descriptors from main memory */
+ rx_bd_ring_base = dma_alloc_coherent(sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE, DMA_ADDRESS_BROKEN);
+ if (!rx_bd_ring_base)
+ return -ENOMEM;
+
+ memset(rx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+
+ /* alloc Rx buffer from main memory */
+ rx_buf_pool = malloc(MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+ if (!rx_buf_pool)
+ return -ENOMEM;
+
+ memset(rx_buf_pool, 0, MAX_RXBUF_LEN * RX_BD_RING_SIZE);
+
+ /* save them to fm_eth */
+ fm_eth->rx_bd_ring = rx_bd_ring_base;
+ fm_eth->cur_rxbd = rx_bd_ring_base;
+ fm_eth->rx_buf = rx_buf_pool;
+
+ /* init Rx BDs ring */
+ rxbd = rx_bd_ring_base;
+ for (i = 0; i < RX_BD_RING_SIZE; i++) {
+ muram_writew(&rxbd->status, RxBD_EMPTY);
+ muram_writew(&rxbd->len, 0);
+ buf_hi = upper_32_bits(virt_to_phys(rx_buf_pool +
+ i * MAX_RXBUF_LEN));
+ buf_lo = lower_32_bits(virt_to_phys(rx_buf_pool +
+ i * MAX_RXBUF_LEN));
+ muram_writew(&rxbd->buf_ptr_hi, (u16)buf_hi);
+ out_be32(&rxbd->buf_ptr_lo, buf_lo);
+ rxbd++;
+ }
+
+ /* set the Rx queue descriptor */
+ rxqd = &pram->rxqd;
+ muram_writew(&rxqd->gen, 0);
+ bd_ring_base_hi = upper_32_bits(virt_to_phys(rx_bd_ring_base));
+ bd_ring_base_lo = lower_32_bits(virt_to_phys(rx_bd_ring_base));
+ muram_writew(&rxqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
+ out_be32(&rxqd->bd_ring_base_lo, bd_ring_base_lo);
+ muram_writew(&rxqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * RX_BD_RING_SIZE);
+ muram_writew(&rxqd->offset_in, 0);
+ muram_writew(&rxqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Rx Frame Queue ID */
+ out_be32(&bmi_rx_port->fmbm_rfqid, pram_page_offset);
+
+ return 0;
+}
+
+static int fm_eth_tx_port_parameter_init(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+ u32 pram_page_offset;
+ void *tx_bd_ring_base;
+ u32 bd_ring_base_lo, bd_ring_base_hi;
+ struct fm_port_bd *txbd;
+ struct fm_port_qd *txqd;
+ struct fm_bmi_tx_port *bmi_tx_port = fm_eth->tx_port;
+ int i;
+
+ /* alloc global parameter ram at MURAM */
+ pram = fm_muram_alloc(FM_PRAM_SIZE, FM_PRAM_ALIGN);
+ if (!pram)
+ return -ENOMEM;
+
+ fm_eth->tx_pram = pram;
+
+ /* parameter page offset to MURAM */
+ pram_page_offset = (void *)pram - fm_muram_base();
+
+ /* enable global mode- snooping data buffers and BDs */
+ out_be32(&pram->mode, PRAM_MODE_GLOBAL);
+
+ /* init the Tx queue descriptor pionter */
+ out_be32(&pram->txqd_ptr, pram_page_offset + 0x40);
+
+ /* alloc Tx buffer descriptors from main memory */
+ tx_bd_ring_base = dma_alloc_coherent(sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE, DMA_ADDRESS_BROKEN);
+ if (!tx_bd_ring_base)
+ return -ENOMEM;
+
+ memset(tx_bd_ring_base, 0, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ /* save it to fm_eth */
+ fm_eth->tx_bd_ring = tx_bd_ring_base;
+ fm_eth->cur_txbd = tx_bd_ring_base;
+
+ /* init Tx BDs ring */
+ txbd = tx_bd_ring_base;
+ for (i = 0; i < TX_BD_RING_SIZE; i++) {
+ muram_writew(&txbd->status, TxBD_LAST);
+ muram_writew(&txbd->len, 0);
+ muram_writew(&txbd->buf_ptr_hi, 0);
+ out_be32(&txbd->buf_ptr_lo, 0);
+ txbd++;
+ }
+
+ /* set the Tx queue decriptor */
+ txqd = &pram->txqd;
+ bd_ring_base_hi = upper_32_bits(virt_to_phys(tx_bd_ring_base));
+ bd_ring_base_lo = lower_32_bits(virt_to_phys(tx_bd_ring_base));
+ muram_writew(&txqd->bd_ring_base_hi, (u16)bd_ring_base_hi);
+ out_be32(&txqd->bd_ring_base_lo, bd_ring_base_lo);
+ muram_writew(&txqd->bd_ring_size, sizeof(struct fm_port_bd)
+ * TX_BD_RING_SIZE);
+ muram_writew(&txqd->offset_in, 0);
+ muram_writew(&txqd->offset_out, 0);
+
+ /* set IM parameter ram pointer to Tx Confirmation Frame Queue ID */
+ out_be32(&bmi_tx_port->fmbm_tcfqid, pram_page_offset);
+
+ return 0;
+}
+
+static void fmc_tx_port_graceful_stop_enable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* graceful stop transmission of frames */
+ setbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
+}
+
+static void fmc_tx_port_graceful_stop_disable(struct fm_eth *fm_eth)
+{
+ struct fm_port_global_pram *pram;
+
+ pram = fm_eth->tx_pram;
+ /* re-enable transmission of frames */
+ clrbits_be32(&pram->mode, PRAM_MODE_GRACEFUL_STOP);
+}
+
+static void memac_adjust_link_speed(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ int speed = edev->phydev->speed;
+ u32 if_mode;
+ phy_interface_t type = fm_eth->enet_if;
+
+ if_mode = in_be32(&regs->if_mode);
+
+ if (type == PHY_INTERFACE_MODE_RGMII ||
+ type == PHY_INTERFACE_MODE_RGMII_ID ||
+ type == PHY_INTERFACE_MODE_RGMII_TXID) {
+ if_mode &= ~IF_MODE_EN_AUTO;
+ if_mode &= ~IF_MODE_SETSP_MASK;
+
+ switch (speed) {
+ case SPEED_1000:
+ if_mode |= IF_MODE_SETSP_1000M;
+ break;
+ case SPEED_100:
+ if_mode |= IF_MODE_SETSP_100M;
+ break;
+ case SPEED_10:
+ if_mode |= IF_MODE_SETSP_10M;
+ default:
+ break;
+ }
+ }
+
+ out_be32(&regs->if_mode, if_mode);
+
+ return;
+}
+
+static int fm_eth_open(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ int ret;
+
+ ret = phy_device_connect(edev, NULL, -1, memac_adjust_link_speed, 0,
+ fm_eth->enet_if);
+ if (ret)
+ return ret;
+
+ /* enable bmi Rx port */
+ setbits_be32(&fm_eth->rx_port->fmbm_rcfg, FMBM_RCFG_EN);
+ /* enable MAC rx/tx port */
+ setbits_be32(&regs->command_config,
+ MEMAC_CMD_CFG_RXTX_EN | MEMAC_CMD_CFG_NO_LEN_CHK);
+
+ /* enable bmi Tx port */
+ setbits_be32(&fm_eth->tx_port->fmbm_tcfg, FMBM_TCFG_EN);
+ /* re-enable transmission of frame */
+ fmc_tx_port_graceful_stop_disable(fm_eth);
+
+ return 0;
+}
+
+static void memac_disable_mac(struct fm_eth *fm_eth)
+{
+ struct memac *regs = fm_eth->regs;
+
+ clrbits_be32(&regs->command_config, MEMAC_CMD_CFG_RXTX_EN);
+}
+
+static void fm_eth_halt(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+
+ /* graceful stop the transmission of frames */
+ fmc_tx_port_graceful_stop_enable(fm_eth);
+ /* disable bmi Tx port */
+ bmi_tx_port_disable(fm_eth->tx_port);
+ /* disable MAC rx/tx port */
+ memac_disable_mac(fm_eth);
+ /* disable bmi Rx port */
+ bmi_rx_port_disable(fm_eth->rx_port);
+}
+
+static int fm_eth_send(struct eth_device *edev, void *buf, int len)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *txbd, *txbd_base;
+ u16 offset_in;
+ int i;
+ dma_addr_t dma;
+
+ pram = fm_eth->tx_pram;
+ txbd = fm_eth->cur_txbd;
+
+ /* find one empty TxBD */
+ for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
+ udelay(100);
+ if (i > 0x1000) {
+ dev_err(&edev->dev, "Tx buffer not ready, txbd->status = 0x%x\n",
+ muram_readw(&txbd->status));
+ return -EIO;
+ }
+ }
+
+ dma = dma_map_single(fm_eth->dev, buf, len, DMA_TO_DEVICE);
+ if (dma_mapping_error(fm_eth->dev, dma))
+ return -EFAULT;
+
+ /* setup TxBD */
+ muram_writew(&txbd->buf_ptr_hi, (u16)upper_32_bits(dma));
+ out_be32(&txbd->buf_ptr_lo, lower_32_bits(dma));
+ muram_writew(&txbd->len, len);
+ muram_writew(&txbd->status, TxBD_READY | TxBD_LAST);
+
+ /* update TxQD, let RISC to send the packet */
+ offset_in = muram_readw(&pram->txqd.offset_in);
+ offset_in += sizeof(struct fm_port_bd);
+ if (offset_in >= muram_readw(&pram->txqd.bd_ring_size))
+ offset_in = 0;
+ muram_writew(&pram->txqd.offset_in, offset_in);
+
+ /* wait for buffer to be transmitted */
+ for (i = 0; muram_readw(&txbd->status) & TxBD_READY; i++) {
+ udelay(10);
+ if (i > 0x10000) {
+ dev_err(&edev->dev, "Tx error, txbd->status = 0x%x\n",
+ muram_readw(&txbd->status));
+ return -EIO;
+ }
+ }
+
+ dma_unmap_single(fm_eth->dev, dma, len, DMA_TO_DEVICE);
+
+ /* advance the TxBD */
+ txbd++;
+ txbd_base = fm_eth->tx_bd_ring;
+ if (txbd >= (txbd_base + TX_BD_RING_SIZE))
+ txbd = txbd_base;
+ /* update current txbd */
+ fm_eth->cur_txbd = (void *)txbd;
+
+ return 0;
+}
+
+static int fm_eth_recv(struct eth_device *edev)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct fm_port_global_pram *pram;
+ struct fm_port_bd *rxbd, *rxbd_base;
+ u16 status, len;
+ u32 buf_lo, buf_hi;
+ u8 *data;
+ u16 offset_out;
+ int ret = 1;
+
+ pram = fm_eth->rx_pram;
+ rxbd = fm_eth->cur_rxbd;
+ status = muram_readw(&rxbd->status);
+
+ while (!(status & RxBD_EMPTY)) {
+ if (!(status & RxBD_ERROR)) {
+ buf_hi = muram_readw(&rxbd->buf_ptr_hi);
+ buf_lo = in_be32(&rxbd->buf_ptr_lo);
+ data = (u8 *)((unsigned long)(buf_hi << 16) << 16 | buf_lo);
+ len = muram_readw(&rxbd->len);
+
+ dma_sync_single_for_cpu((unsigned long)data,
+ len,
+ DMA_FROM_DEVICE);
+
+ net_receive(edev, data, len);
+
+ dma_sync_single_for_device((unsigned long)data,
+ len,
+ DMA_FROM_DEVICE);
+ } else {
+ dev_err(&edev->dev, "Rx error\n");
+ ret = 0;
+ }
+
+ /* clear the RxBDs */
+ muram_writew(&rxbd->status, RxBD_EMPTY);
+ muram_writew(&rxbd->len, 0);
+
+ /* advance RxBD */
+ rxbd++;
+ rxbd_base = fm_eth->rx_bd_ring;
+ if (rxbd >= (rxbd_base + RX_BD_RING_SIZE))
+ rxbd = rxbd_base;
+ /* read next status */
+ status = muram_readw(&rxbd->status);
+
+ /* update RxQD */
+ offset_out = muram_readw(&pram->rxqd.offset_out);
+ offset_out += sizeof(struct fm_port_bd);
+ if (offset_out >= muram_readw(&pram->rxqd.bd_ring_size))
+ offset_out = 0;
+ muram_writew(&pram->rxqd.offset_out, offset_out);
+ }
+ fm_eth->cur_rxbd = rxbd;
+
+ return ret;
+}
+
+static void memac_init_mac(struct fm_eth *fm_eth)
+{
+ struct memac *regs = fm_eth->regs;
+
+ /* mask all interrupt */
+ out_be32(&regs->imask, IMASK_MASK_ALL);
+
+ /* clear all events */
+ out_be32(&regs->ievent, IEVENT_CLEAR_ALL);
+
+ /* set the max receive length */
+ out_be32(&regs->maxfrm, MAX_RXBUF_LEN);
+
+ /* multicast frame reception for the hash entry disable */
+ out_be32(&regs->hashtable_ctrl, 0);
+}
+
+static int memac_set_ethaddr(struct eth_device *edev, const unsigned char *adr)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ u32 mac_addr0, mac_addr1;
+
+ /*
+ * if a station address of 0x12345678ABCD, perform a write to
+ * MAC_ADDR0 of 0x78563412, MAC_ADDR1 of 0x0000CDAB
+ */
+ mac_addr0 = (adr[3] << 24) | (adr[2] << 16) | \
+ (adr[1] << 8) | (adr[0]);
+ out_be32(&regs->mac_addr_0, mac_addr0);
+
+ mac_addr1 = ((adr[5] << 8) | adr[4]) & 0x0000ffff;
+ out_be32(&regs->mac_addr_1, mac_addr1);
+
+ return 0;
+}
+
+static int memac_get_ethaddr(struct eth_device *edev, unsigned char *adr)
+{
+ struct fm_eth *fm_eth = to_fm_eth(edev);
+ struct memac *regs = fm_eth->regs;
+ u32 mac_addr0, mac_addr1;
+
+ mac_addr0 = in_be32(&regs->mac_addr_0);
+ mac_addr1 = in_be32(&regs->mac_addr_1);
+
+ adr[0] = mac_addr0 & 0xff;
+ adr[1] = (mac_addr0 >> 8) & 0xff;
+ adr[2] = (mac_addr0 >> 16) & 0xff;
+ adr[3] = (mac_addr0 >> 24) & 0xff;
+ adr[4] = mac_addr1 & 0xff;
+ adr[5] = (mac_addr1 >> 8) & 0xff;
+
+ return 0;
+}
+
+static void memac_set_interface_mode(struct fm_eth *fm_eth,
+ phy_interface_t type)
+{
+ struct memac *regs = fm_eth->regs;
+ u32 if_mode;
+
+ /* clear all bits relative with interface mode */
+ if_mode = in_be32(&regs->if_mode) & ~IF_MODE_MASK;
+
+ /* set interface mode */
+ switch (type) {
+ case PHY_INTERFACE_MODE_GMII:
+ if_mode |= IF_MODE_GMII | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ if_mode |= IF_MODE_GMII | IF_MODE_RG | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ if_mode |= IF_MODE_GMII | IF_MODE_EN_AUTO;
+ break;
+ case PHY_INTERFACE_MODE_XGMII:
+ if_mode |= IF_MODE_XGMII;
+ break;
+ default:
+ break;
+ }
+
+ out_be32(&regs->if_mode, if_mode);
+}
+
+static int fm_eth_startup(struct fm_eth *fm_eth)
+{
+ int ret;
+
+ ret = fm_eth_rx_port_parameter_init(fm_eth);
+ if (ret)
+ return ret;
+
+ ret = fm_eth_tx_port_parameter_init(fm_eth);
+ if (ret)
+ return ret;
+
+ /* setup the MAC controller */
+ memac_init_mac(fm_eth);
+
+ /* init bmi rx port, IM mode and disable */
+ bmi_rx_port_init(fm_eth->rx_port);
+
+ /* init bmi tx port, IM mode and disable */
+ bmi_tx_port_init(fm_eth->tx_port);
+
+ memac_set_interface_mode(fm_eth, fm_eth->enet_if);
+
+ return 0;
+}
+
+static int fsl_fman_mdio_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ int ret;
+ struct fsl_fman_mdio *priv;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ priv = xzalloc(sizeof(*priv));
+
+ priv->bus.read = memac_mdio_read;
+ priv->bus.write = memac_mdio_write;
+ priv->bus.parent = dev;
+ priv->regs = IOMEM(iores->start);
+
+ memac_setbits_32(&priv->regs->mdio_stat,
+ MDIO_STAT_CLKDIV(258) | MDIO_STAT_NEG);
+
+ ret = mdiobus_register(&priv->bus);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_fman_port_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ int ret;
+ struct fsl_fman_port *port;
+ unsigned long type;
+
+ dev_dbg(dev, "probe\n");
+
+ ret = dev_get_drvdata(dev, (const void **)&type);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ port = xzalloc(sizeof(*port));
+
+ port->regs = IOMEM(iores->start);
+ port->type = type;
+
+ if (type == FMAN_PORT_TYPE_RX)
+ port->rxport = port->regs;
+ else
+ port->txport = port->regs;
+
+ dev->priv = port;
+
+ return 0;
+}
+
+static int fsl_fman_memac_port_bind(struct fm_eth *fm_eth, enum fman_port_type type)
+{
+ struct device_node *macnp = fm_eth->dev->device_node;
+ struct device_node *portnp;
+ struct device_d *portdev;
+ struct fsl_fman_port *port;
+
+ portnp = of_parse_phandle(macnp, "fsl,fman-ports", type);
+ if (!portnp) {
+ dev_err(fm_eth->dev, "of_parse_phandle(%s, fsl,fman-ports) failed\n",
+ macnp->full_name);
+ return -EINVAL;
+ }
+
+ portdev = of_find_device_by_node(portnp);
+ if (!portdev)
+ return -ENOENT;
+
+ port = portdev->priv;
+ if (!port)
+ return -EINVAL;
+
+ if (type == FMAN_PORT_TYPE_TX)
+ fm_eth->tx_port = port->txport;
+ else
+ fm_eth->rx_port = port->rxport;
+
+ return 0;
+}
+
+static int fsl_fman_memac_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct fm_eth *fm_eth;
+ struct eth_device *edev;
+ int ret;
+ int phy_mode;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ /* alloc the FMan ethernet private struct */
+ fm_eth = xzalloc(sizeof(*fm_eth));
+
+ fm_eth->dev = dev;
+
+ ret = fsl_fman_memac_port_bind(fm_eth, FMAN_PORT_TYPE_TX);
+ if (ret)
+ return ret;
+
+ ret = fsl_fman_memac_port_bind(fm_eth, FMAN_PORT_TYPE_RX);
+ if (ret)
+ return ret;
+
+ phy_mode = of_get_phy_mode(dev->device_node);
+ if (phy_mode < 0)
+ return phy_mode;
+
+ fm_eth->enet_if = phy_mode;
+
+ fm_eth->regs = IOMEM(iores->start);
+
+ dev->priv = fm_eth;
+
+ edev = &fm_eth->edev;
+ edev->open = fm_eth_open;
+ edev->halt = fm_eth_halt;
+ edev->send = fm_eth_send;
+ edev->recv = fm_eth_recv;
+ edev->get_ethaddr = memac_get_ethaddr;
+ edev->set_ethaddr = memac_set_ethaddr;
+ edev->parent = dev;
+
+ /* startup the FM im */
+ ret = fm_eth_startup(fm_eth);
+ if (ret)
+ return ret;
+
+ ret = eth_register(edev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int fsl_fman_muram_probe(struct device_d *dev)
+{
+ struct resource *iores;
+
+ dev_dbg(dev, "probe\n");
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ muram.base = IOMEM(iores->start);
+ muram.size = resource_size(iores);
+ muram.alloc = muram.base + FM_MURAM_RES_SIZE;
+ muram.top = muram.base + muram.size;
+
+ return 0;
+}
+
+static struct of_device_id fsl_fman_mdio_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-memac-mdio",
+ }, {
+ }
+};
+
+static struct driver_d fman_mdio_driver = {
+ .name = "fsl-fman-mdio",
+ .probe = fsl_fman_mdio_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_mdio_dt_ids),
+};
+
+static struct of_device_id fsl_fman_port_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-v3-port-rx",
+ .data = (void *)FMAN_PORT_TYPE_RX,
+ }, {
+ .compatible = "fsl,fman-v3-port-tx",
+ .data = (void *)FMAN_PORT_TYPE_TX,
+ }, {
+ }
+};
+
+static struct driver_d fman_port_driver = {
+ .name = "fsl-fman-port",
+ .probe = fsl_fman_port_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_port_dt_ids),
+};
+
+static struct of_device_id fsl_fman_memac_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-memac",
+ }, {
+ }
+};
+
+static struct driver_d fman_memac_driver = {
+ .name = "fsl-fman-memac",
+ .probe = fsl_fman_memac_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_memac_dt_ids),
+};
+
+static struct of_device_id fsl_fman_muram_dt_ids[] = {
+ {
+ .compatible = "fsl,fman-muram",
+ }, {
+ }
+};
+
+static struct driver_d fman_muram_driver = {
+ .name = "fsl-fman-muram",
+ .probe = fsl_fman_muram_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_muram_dt_ids),
+};
+
+static int fsl_fman_probe(struct device_d *dev)
+{
+ struct resource *iores;
+ struct ccsr_fman *reg;
+ int ret;
+
+ dev_dbg(dev, "----------------> probe\n");
+
+ iores = dev_get_resource(dev, IORESOURCE_MEM, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ reg = IOMEM(iores->start);
+
+ ret = of_platform_populate(dev->device_node, NULL, dev);
+ if (ret)
+ return ret;
+
+ platform_driver_register(&fman_muram_driver);
+ platform_driver_register(&fman_mdio_driver);
+ platform_driver_register(&fman_port_driver);
+ platform_driver_register(&fman_memac_driver);
+
+ ret = fm_init_common(dev, reg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct of_device_id fsl_fman_dt_ids[] = {
+ {
+ .compatible = "fsl,fman",
+ }, {
+ }
+};
+
+static struct driver_d fman_driver = {
+ .name = "fsl-fman",
+ .probe = fsl_fman_probe,
+ .of_compatible = DRV_OF_COMPAT(fsl_fman_dt_ids),
+};
+device_platform_driver(fman_driver);