fm/mEMAC: add mEMAC frame work

The multirate ethernet media access controller (mEMAC) interfaces to
10Gbps and below Ethernet/IEEE 802.3 networks via either RGMII/RMII
interfaces or XAUI/XFI/SGMII/QSGMII using the high-speed SerDes interface.

Signed-off-by: Sandeep Singh <Sandeep@freescale.com>
Signed-off-by: Poonam Aggrwal <poonam.aggrwal@freescale.com>
Signed-off-by: Roy Zang <tie-fei.zang@freescale.com>
Signed-off-by: Andy Fleming <afleming@freescale.com>
diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile
index 4642c54..7a1fcdd 100644
--- a/drivers/net/fm/Makefile
+++ b/drivers/net/fm/Makefile
@@ -32,6 +32,10 @@
 COBJS-y += tgec.o
 COBJS-y += tgec_phy.o
 
+# Soc have FMAN v3 with mEMAC
+COBJS-$(CONFIG_SYS_FMAN_V3) += memac_phy.o
+COBJS-$(CONFIG_SYS_FMAN_V3) += memac.o
+
 # SoC specific SERDES support
 COBJS-$(CONFIG_P1017)	+= p1023.o
 COBJS-$(CONFIG_P1023)	+= p1023.o
diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c
index 2b616ad..ed23fdd 100644
--- a/drivers/net/fm/eth.c
+++ b/drivers/net/fm/eth.c
@@ -1,5 +1,5 @@
 /*
- * Copyright 2009-2011 Freescale Semiconductor, Inc.
+ * Copyright 2009-2012 Freescale Semiconductor, Inc.
  *	Dave Liu <daveliu@freescale.com>
  *
  * This program is free software; you can redistribute it and/or
@@ -28,6 +28,7 @@
 #include <phy.h>
 #include <asm/fsl_dtsec.h>
 #include <asm/fsl_tgec.h>
+#include <asm/fsl_memac.h>
 
 #include "fm.h"
 
@@ -47,6 +48,28 @@
 /* Configure the TBI for SGMII operation */
 void dtsec_configure_serdes(struct fm_eth *priv)
 {
+#ifdef CONFIG_SYS_FMAN_V3
+	u32 value;
+	struct mii_dev bus;
+	bus.priv = priv->mac->phyregs;
+
+	/* SGMII IF mode + AN enable */
+	value = PHY_SGMII_IF_MODE_AN | PHY_SGMII_IF_MODE_SGMII;
+	memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x14, value);
+
+	/* Dev ability according to SGMII specification */
+	value = PHY_SGMII_DEV_ABILITY_SGMII;
+	memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x4, value);
+
+	/* Adjust link timer for SGMII  -
+	1.6 ms in units of 8 ns = 2 * 10^5 = 0x30d40 */
+	memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x13, 0x3);
+	memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0x12, 0xd40);
+
+	/* Restart AN */
+	value = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN;
+	memac_mdio_write(&bus, 0, MDIO_DEVAD_NONE, 0, value);
+#else
 	struct dtsec *regs = priv->mac->base;
 	struct tsec_mii_mng *phyregs = priv->mac->phyregs;
 
@@ -60,15 +83,18 @@
 			TBIANA_SGMII_ACK);
 	tsec_local_mdio_write(phyregs, in_be32(&regs->tbipa), 0,
 			TBI_CR, TBICR_SETTINGS);
+#endif
 }
 
 static void dtsec_init_phy(struct eth_device *dev)
 {
 	struct fm_eth *fm_eth = dev->priv;
-	struct dtsec *regs = (struct dtsec *)fm_eth->mac->base;
 
+#ifndef CONFIG_SYS_FMAN_V3
+	struct dtsec *regs = (struct dtsec *)fm_eth->mac->base;
 	/* Assign a Physical address to the TBI */
 	out_be32(&regs->tbipa, CONFIG_SYS_TBIPA_VALUE);
+#endif
 
 	if (fm_eth->enet_if == PHY_INTERFACE_MODE_SGMII)
 		dtsec_configure_serdes(fm_eth);
@@ -541,6 +567,10 @@
 
 	num = fm_eth->num;
 
+#ifdef CONFIG_SYS_FMAN_V3
+	base = &reg->memac[num].fm_memac;
+	phyregs = &reg->memac[num].fm_memac_mdio;
+#else
 	/* Get the mac registers base address */
 	if (fm_eth->type == FM_ETH_1G_E) {
 		base = &reg->mac_1g[num].fm_dtesc;
@@ -549,6 +579,7 @@
 		base = &reg->mac_10g[num].fm_10gec;
 		phyregs = &reg->mac_10g[num].fm_10gec_mdio;
 	}
+#endif
 
 	/* alloc mac controller */
 	mac = malloc(sizeof(struct fsl_enet_mac));
@@ -559,10 +590,14 @@
 	/* save the mac to fm_eth struct */
 	fm_eth->mac = mac;
 
+#ifdef CONFIG_SYS_FMAN_V3
+	init_memac(mac, base, phyregs, MAX_RXBUF_LEN);
+#else
 	if (fm_eth->type == FM_ETH_1G_E)
 		init_dtsec(mac, base, phyregs, MAX_RXBUF_LEN);
 	else
 		init_tgec(mac, base, phyregs, MAX_RXBUF_LEN);
+#endif
 
 	return 1;
 }
diff --git a/drivers/net/fm/memac.c b/drivers/net/fm/memac.c
new file mode 100644
index 0000000..32c7054
--- /dev/null
+++ b/drivers/net/fm/memac.c
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *	Roy Zang <tie-fei.zang@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+/* MAXFRM - maximum frame length */
+#define MAXFRM_MASK	0x0000ffff
+
+#include <common.h>
+#include <phy.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/fsl_enet.h>
+#include <asm/fsl_memac.h>
+
+#include "fm.h"
+
+static void memac_init_mac(struct fsl_enet_mac *mac)
+{
+	struct memac *regs = mac->base;
+
+	/* 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, mac->max_rx_len & MAXFRM_MASK);
+
+	/* multicast frame reception for the hash entry disable */
+	out_be32(&regs->hashtable_ctrl, 0);
+}
+
+static void memac_enable_mac(struct fsl_enet_mac *mac)
+{
+	struct memac *regs = mac->base;
+
+	setbits_be32(&regs->command_config, MEMAC_CMD_CFG_RXTX_EN);
+}
+
+static void memac_disable_mac(struct fsl_enet_mac *mac)
+{
+	struct memac *regs = mac->base;
+
+	clrbits_be32(&regs->command_config, MEMAC_CMD_CFG_RXTX_EN);
+}
+
+static void memac_set_mac_addr(struct fsl_enet_mac *mac, u8 *mac_addr)
+{
+	struct memac *regs = mac->base;
+	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 = (mac_addr[3] << 24) | (mac_addr[2] << 16) | \
+			(mac_addr[1] << 8)  | (mac_addr[0]);
+	out_be32(&regs->mac_addr_0, mac_addr0);
+
+	mac_addr1 = ((mac_addr[5] << 8) | mac_addr[4]) & 0x0000ffff;
+	out_be32(&regs->mac_addr_1, mac_addr1);
+}
+
+static void memac_set_interface_mode(struct fsl_enet_mac *mac,
+					phy_interface_t type, int speed)
+{
+	/* Roy need more work here */
+
+	struct memac *regs = mac->base;
+	u32 if_mode, if_status;
+
+	/* clear all bits relative with interface mode */
+	if_mode = in_be32(&regs->if_mode);
+	if_status = in_be32(&regs->if_status);
+
+	/* set interface mode */
+	switch (type) {
+	case PHY_INTERFACE_MODE_GMII:
+		if_mode &= ~IF_MODE_MASK;
+		if_mode |= IF_MODE_GMII;
+		break;
+	case PHY_INTERFACE_MODE_RGMII:
+		if_mode |= (IF_MODE_GMII | IF_MODE_RG);
+		break;
+	case PHY_INTERFACE_MODE_RMII:
+		if_mode |= (IF_MODE_GMII | IF_MODE_RM);
+		break;
+	case PHY_INTERFACE_MODE_SGMII:
+		if_mode &= ~IF_MODE_MASK;
+		if_mode |= (IF_MODE_GMII);
+		break;
+	default:
+		break;
+	}
+	/* Enable automatic speed selection */
+	if_mode |= IF_MODE_EN_AUTO;
+
+	debug(" %s, if_mode = %x\n", __func__,  if_mode);
+	debug(" %s, if_status = %x\n", __func__,  if_status);
+	out_be32(&regs->if_mode, if_mode);
+	return;
+}
+
+void init_memac(struct fsl_enet_mac *mac, void *base,
+		void *phyregs, int max_rx_len)
+{
+	mac->base = base;
+	mac->phyregs = phyregs;
+	mac->max_rx_len = max_rx_len;
+	mac->init_mac = memac_init_mac;
+	mac->enable_mac = memac_enable_mac;
+	mac->disable_mac = memac_disable_mac;
+	mac->set_mac_addr = memac_set_mac_addr;
+	mac->set_if_mode = memac_set_interface_mode;
+}
diff --git a/drivers/net/fm/memac_phy.c b/drivers/net/fm/memac_phy.c
new file mode 100644
index 0000000..ea6118b
--- /dev/null
+++ b/drivers/net/fm/memac_phy.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ *	Andy Fleming <afleming@freescale.com>
+ *	Roy Zang <tie-fei.zang@freescale.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ * Some part is taken from tsec.c
+ */
+#include <common.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <asm/io.h>
+#include <asm/fsl_memac.h>
+#include <fm_eth.h>
+
+/*
+ * Write value to the PHY for this device to the register at regnum, waiting
+ * until the write is done before it returns.  All PHY configuration has to be
+ * done through the TSEC1 MIIM regs
+ */
+int memac_mdio_write(struct mii_dev *bus, int port_addr, int dev_addr,
+			int regnum, u16 value)
+{
+	u32 mdio_ctl;
+	struct memac_mdio_controller *regs = bus->priv;
+	u32 c45 = 1; /* Default to 10G interface */
+
+	if (dev_addr == MDIO_DEVAD_NONE) {
+		c45 = 0; /* clause 22 */
+		dev_addr = regnum & 0x1f;
+		clrbits_be32(&regs->mdio_stat, MDIO_STAT_ENC);
+	} else {
+		setbits_be32(&regs->mdio_stat, MDIO_STAT_ENC);
+		setbits_be32(&regs->mdio_stat, MDIO_STAT_HOLD_15_CLK);
+	}
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+		;
+
+	/* Set the port and dev addr */
+	mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Set the register address */
+	if (c45)
+		out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+		;
+
+	/* Write the value to the register */
+	out_be32(&regs->mdio_data, MDIO_DATA(value));
+
+	/* Wait till the MDIO write is complete */
+	while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
+		;
+
+	return 0;
+}
+
+/*
+ * Reads from register regnum in the PHY for device dev, returning the value.
+ * Clears miimcom first.  All PHY configuration has to be done through the
+ * TSEC1 MIIM regs
+ */
+int memac_mdio_read(struct mii_dev *bus, int port_addr, int dev_addr,
+			int regnum)
+{
+	u32 mdio_ctl;
+	struct memac_mdio_controller *regs = bus->priv;
+	u32 c45 = 1;
+
+	if (dev_addr == MDIO_DEVAD_NONE) {
+		c45 = 0; /* clause 22 */
+		dev_addr = regnum & 0x1f;
+		clrbits_be32(&regs->mdio_stat, MDIO_STAT_ENC);
+	} else {
+		setbits_be32(&regs->mdio_stat, MDIO_STAT_ENC);
+		setbits_be32(&regs->mdio_stat, MDIO_STAT_HOLD_15_CLK);
+	}
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+		;
+
+	/* Set the Port and Device Addrs */
+	mdio_ctl = MDIO_CTL_PORT_ADDR(port_addr) | MDIO_CTL_DEV_ADDR(dev_addr);
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Set the register address */
+	if (c45)
+		out_be32(&regs->mdio_addr, regnum & 0xffff);
+
+	/* Wait till the bus is free */
+	while ((in_be32(&regs->mdio_stat)) & MDIO_STAT_BSY)
+		;
+
+	/* Initiate the read */
+	mdio_ctl |= MDIO_CTL_READ;
+	out_be32(&regs->mdio_ctl, mdio_ctl);
+
+	/* Wait till the MDIO write is complete */
+	while ((in_be32(&regs->mdio_data)) & MDIO_DATA_BSY)
+		;
+
+	/* Return all Fs if nothing was there */
+	if (in_be32(&regs->mdio_stat) & MDIO_STAT_RD_ER)
+		return 0xffff;
+
+	return in_be32(&regs->mdio_data) & 0xffff;
+}
+
+int memac_mdio_reset(struct mii_dev *bus)
+{
+	return 0;
+}
+
+int fm_memac_mdio_init(bd_t *bis, struct memac_mdio_info *info)
+{
+	struct mii_dev *bus = mdio_alloc();
+
+	if (!bus) {
+		printf("Failed to allocate FM TGEC MDIO bus\n");
+		return -1;
+	}
+
+	bus->read = memac_mdio_read;
+	bus->write = memac_mdio_write;
+	bus->reset = memac_mdio_reset;
+	sprintf(bus->name, info->name);
+
+	bus->priv = info->regs;
+
+	return mdio_register(bus);
+}