[U-Boot] [PATCH] Marvell 88EXXXX Switch/PHY init support

Prafulla Wadaskar prafulla at marvell.com
Sat Apr 4 00:39:29 CEST 2009


From: prafulla_wadaskar <prafulla at marvell.com>

Chips supprted:-
1. 88E61XX 6 port gbe swtich with 5 integrated PHYs
2. 88E6061 6 port fe swtich with 5 integrated PHYs
3. 88E1116 gbe transceiver

Contributors:
Yotam Admon <yotam at marvell.com>
Michael Blostein <michaelbl at marvell.com

Signed-off-by: prafulla_wadaskar <prafulla at marvell.com>
Reviewed by: Ronen Shitrit <rshitrit at marvell.com>
---
 board/Marvell/common/mv88e1116.c |   72 +++++++
 board/Marvell/common/mv88e60xx.c |  409 +++++++++++++++++++++++++++++++++++++
 board/Marvell/common/mv88e61xx.c |  414 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 895 insertions(+), 0 deletions(-)
 create mode 100644 board/Marvell/common/mv88e1116.c
 create mode 100644 board/Marvell/common/mv88e60xx.c
 create mode 100644 board/Marvell/common/mv88e61xx.c

diff --git a/board/Marvell/common/mv88e1116.c b/board/Marvell/common/mv88e1116.c
new file mode 100644
index 0000000..87ec550
--- /dev/null
+++ b/board/Marvell/common/mv88e1116.c
@@ -0,0 +1,72 @@
+/*
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Prafulla Wadaskar <prafulla at marvell.com>
+ *
+ * Contributors
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef MV88E1116_DEBUG
+#define MV88E1116_DEBUG 0
+#endif
+#define DEBUG_PRINT	MV88E1116_DEBUG
+
+#include <common.h>
+#include <debug_prints.h>
+#include "../common/ppc_error_no.h"
+
+#if defined (CONFIG_PHY_88E1116)
+
+/*
+ * Marvell 88E1116 PHY initialization
+ */
+void mv_phy_88e1116_init(u32 eth_port_num)
+{
+	u16 reg;
+	u32 smi_dev_addr;
+
+	debug_print_ftrace();
+	smi_dev_addr = KW_REG_READ(KW_ETH_PHY_ADDR_REG(eth_port_num));
+
+	/* Leds link and activity */
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 22, 0x3);
+	eth_smi_reg_read(eth_port_num, smi_dev_addr, 16, &reg);
+	reg &= ~0xf;
+	reg |= 0x1;
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 16, reg);
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 22, 0x0);
+
+	/* Set RGMII delay */
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 22, 2);
+	eth_smi_reg_read(eth_port_num, smi_dev_addr, 21, &reg);
+	reg |= (BIT5 | BIT4);
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 21, reg);
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 22, 0);
+
+	/* reset the phy */
+	eth_smi_reg_read(eth_port_num, smi_dev_addr, 0, &reg);
+	reg |= BIT15;
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 0, reg);
+
+	info_print("88E1116 Initialized");
+}
+
+#endif /* CONFIG_PHY_88E61XX */
diff --git a/board/Marvell/common/mv88e60xx.c b/board/Marvell/common/mv88e60xx.c
new file mode 100644
index 0000000..6034f7b
--- /dev/null
+++ b/board/Marvell/common/mv88e60xx.c
@@ -0,0 +1,409 @@
+/*
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Prafulla Wadaskar <prafulla at marvell.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef MV88E60XX_DEBUG
+#define MV88E60XX_DEBUG 0
+#endif
+#define DEBUG_PRINT	MV88E60XX_DEBUG
+
+#include <common.h>
+#include <debug_prints.h>
+#include "../common/ppc_error_no.h"
+#include <command.h>
+#include <environment.h>
+#include <watchdog.h>
+#include <serial.h>
+#include <linux/stddef.h>
+#include <asm/byteorder.h>
+#if defined(CONFIG_CMD_NET)
+#include <net.h>
+#endif
+
+#if defined (CONFIG_SWITCH_88E60XX)
+
+/* CPU port can be configured in board header file */
+#if defined (CONFIG_SWITCH_88E60XX_CPU_PORT)
+#define MV88E60XX_CPU_PORT      CONFIG_SWITCH_88E60XX_CPU_PORT
+#else
+#define MV88E60XX_CPU_PORT      0x5
+#endif
+/* Enabled ports can be configured in board header file */
+#if defined (CONFIG_SWITCH_88E60XX_ENABLED_PORTS)
+#define MV88E60XX_ENABLED_PORTS CONFIG_SWITCH_88E60XX_ENABLED_PORTS
+#else
+#define MV88E60XX_ENABLED_PORTS (BIT0 | BIT1 | BIT2 | \
+                                 BIT3 | BIT4 | BIT5)
+#endif
+
+#ifdef CONFIG_SWITCH_MV88E6061
+#define MV88E60XX_NAME                                  "88E6061"
+#else
+#define MV88E60XX_NAME                                  "88E60xx"
+#endif
+#define MV88E60XX_PHY_TIMEOUT                           100000
+#define MV88E60XX_MAX_PORTS_NUM                         0x6
+
+#define MV88E60XX_PORT_STATUS_REG                       0x1
+#define MV88E60XX_PORT_CONTROL_REG                      0x4
+#define MV88E60XX_PORT_VMAP_REG                         0x6
+#define MV88E60XX_PORT_VID_REG                          0x7
+
+#define MV88E60XX_PHY_SPEC_CONTROL_REG                  0x10
+#define MV88E60XX_PHY_CONTROL_REG                       0x00
+
+#define MV88E60XX_PHY_OFFSET                            0x10
+#define MV88E60XX_PORTS_OFFSET                          0x18
+#define MV88E60XX_SMI_PHY_COMMAND                       0x18
+#define MV88E60XX_SMI_PHY_DATA                          0x19
+#define MV88E60XX_GLOBAL_2_REG_DEV_ADDR                 0x1C
+
+#define MV88E60XX_PORT_STATUS_BIT_LINKUP                BIT2
+
+#define mv_sw_eth_phy_reg_write eth_smi_reg_write
+#define mv_sw_eth_phy_reg_read eth_smi_reg_read
+
+static void mv_switch_88e60xx_vlan_init(u32 eth_port_num,
+					u32 switch_cpu_port,
+					u32 switch_max_ports_num,
+					u32 switch_ports_ofs,
+					u32 switch_enabled_ports_mask)
+{
+	u32 prt;
+	u16 reg;
+
+	debug_print_ftrace();
+	/* be sure all ports are disabled */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		mv_sw_eth_phy_reg_read(eth_port_num, (switch_ports_ofs + prt),
+				       MV88E60XX_PORT_CONTROL_REG, &reg);
+		reg &= ~0x3;
+		mv_sw_eth_phy_reg_write(eth_port_num, (switch_ports_ofs + prt),
+					MV88E60XX_PORT_CONTROL_REG, reg);
+	}
+	/* Set CPU port VID to 0x1 */
+	mv_sw_eth_phy_reg_read(eth_port_num,
+			       (switch_ports_ofs + switch_cpu_port),
+			       MV88E60XX_PORT_VID_REG, &reg);
+	reg &= ~0xfff;
+	reg |= 0x1;
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				(switch_ports_ofs + switch_cpu_port),
+				MV88E60XX_PORT_VID_REG, reg);
+	/* Setting  Port default priority for all ports to zero */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		mv_sw_eth_phy_reg_read(eth_port_num, (switch_ports_ofs + prt),
+				       MV88E60XX_PORT_VID_REG, &reg);
+		reg &= ~0xc000;
+		mv_sw_eth_phy_reg_write(eth_port_num, (switch_ports_ofs + prt),
+					MV88E60XX_PORT_VID_REG, reg);
+	}
+	/* Setting VID and VID map for all ports except CPU port */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		/* only for enabled ports */
+		if ((1 << prt) & switch_enabled_ports_mask) {
+			/* skip CPU port */
+			if (prt == switch_cpu_port)
+				continue;
+
+			/*
+			 *  set Ports VLAN Mapping.
+			 *      port prt <--> MV88E60XX_CPU_PORT VLAN #prt+1.
+			 */
+
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E60XX_PORT_VID_REG, &reg);
+			reg &= ~0x0fff;
+			reg |= (prt + 1);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E60XX_PORT_VID_REG, reg);
+
+			/* Set Vlan map table for all ports to send only to MV88E60XX_CPU_PORT */
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E60XX_PORT_VMAP_REG, &reg);
+			reg &= ~((1 << switch_max_ports_num) - 1);
+			reg |= (1 << switch_cpu_port);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E60XX_PORT_VMAP_REG, reg);
+		}
+	}
+	/* Set Vlan map table for MV88E60XX_CPU_PORT to see all ports */
+	mv_sw_eth_phy_reg_read(eth_port_num,
+			       (switch_ports_ofs + switch_cpu_port),
+			       MV88E60XX_PORT_VMAP_REG, &reg);
+	reg &= ~((1 << switch_max_ports_num) - 1);
+	reg |= switch_enabled_ports_mask & ~(1 << switch_cpu_port);
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				(switch_ports_ofs + switch_cpu_port),
+				MV88E60XX_PORT_VMAP_REG, reg);
+
+	/*enable only appropriate ports to forwarding mode - and disable the others */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+
+		if ((1 << prt) & switch_enabled_ports_mask) {
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E60XX_PORT_CONTROL_REG,
+					       &reg);
+			reg |= 0x3;
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E60XX_PORT_CONTROL_REG,
+						reg);
+		} else {
+			/* Disable port */
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E60XX_PORT_CONTROL_REG,
+					       &reg);
+			reg &= ~0x3;
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E60XX_PORT_CONTROL_REG,
+						reg);
+		}
+	}
+}
+
+/*
+ * Marvell 88E60XX Switch initialization
+ */
+int mv_switch_88e60xx_init(u32 eth_port_num)
+{
+	u32 prt;
+	u16 reg;
+	volatile u32 timeout;
+
+	debug_print_ftrace();
+
+	/* Init vlan */
+	mv_switch_88e60xx_vlan_init(eth_port_num, MV88E60XX_CPU_PORT,
+				    MV88E60XX_MAX_PORTS_NUM,
+				    MV88E60XX_PORTS_OFFSET,
+				    MV88E60XX_ENABLED_PORTS);
+	for (prt = 0; prt < MV88E60XX_MAX_PORTS_NUM; prt++) {
+		if (prt != MV88E60XX_CPU_PORT) {
+			/*Enable Phy power up */
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(MV88E60XX_PHY_OFFSET + prt),
+						MV88E60XX_PHY_CONTROL_REG,
+						0xA100);
+		}
+		/*Enable port */
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E60XX_PORTS_OFFSET + prt, 4, 0x17f);
+	}
+	/*Force CPU port to RGMII FDX 100Base */
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				MV88E60XX_PORTS_OFFSET + MV88E60XX_CPU_PORT, 1,
+				0x3d);
+
+	info_print(MV88E60XX_NAME " Initialized");
+	return 0;
+}
+
+#if defined(CONFIG_CMD_SMIRW)
+int smi_read_command(u32 smiaddr, u32 regaddr, u32 page)
+{
+	int prt = page;
+	u32 value;
+
+	mv_sw_eth_phy_reg_read(0, smiaddr, regaddr, &value);
+	printf("smiread(smiaddr %x, regaddr %x, page %d)=%04x\n", smiaddr,
+	       regaddr, page, (u16) value);
+	return 0;
+}
+
+int smi_write_command(u32 smiaddr, u32 regaddr, u32 page, u32 value)
+{
+	int prt = page;
+	u32 page_backup = 0;
+
+	mv_sw_eth_phy_reg_write(0, smiaddr, regaddr, value);
+	printf("smiwrite(smiaddr %x, regaddr %x, page %d) written =%04x\n",
+	       smiaddr, regaddr, page, value);
+	return 0;
+}
+
+/*
+ * "smi read [smiaddr] [regaddr] [page]\n"
+ * "    - read smi register command\n"
+ * "smi write [smiaddr] [regaddr] [value] [page]\n"
+ * "    - write <value> to <regaddr> register command\n"
+ */
+int do_smi(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+	u32 smiaddr = 0, regaddr = 0, value = 0, page = 0, size = 0;
+	char cmd = ' ';
+
+	if (argc > 1)
+		cmd = argv[1][0];
+
+	switch (cmd) {
+	case 'r':		/* read */
+		if (argc > 2)
+			smiaddr = simple_strtoul(argv[2], NULL, 16);
+		if (argc > 3)
+			regaddr = simple_strtoul(argv[3], NULL, 16);
+		if (argc > 4)
+			page = simple_strtoul(argv[4], NULL, 16);
+		break;
+	case 'w':		/* write */
+		if (argc < 4)
+			goto usage;
+		smiaddr = simple_strtoul(argv[2], NULL, 16);
+		regaddr = simple_strtoul(argv[3], NULL, 16);
+		value = simple_strtoul(argv[4], NULL, 16);
+		if (argc > 5)
+			page = simple_strtoul(argv[5], NULL, 16);
+		break;
+	default:
+	      usage:
+		printf("Usage:\n%s\n", cmdtp->usage);
+		return 1;
+	}
+
+	switch (argv[1][0]) {
+	case 'r':		/* read */
+		smi_read_command(smiaddr, regaddr, page);
+		return 0;
+	case 'w':		/* write */
+		smi_write_command(smiaddr, regaddr, page, value);
+		return 0;
+	}
+	return 1;
+}
+
+U_BOOT_CMD(smi, CONFIG_SYS_MAXARGS, 1, do_smi,
+	   "smi - isues read/write command on smi for switch registers\n",
+	   "smi read [smiaddr] [regaddr] [page]\n"
+	   "    - read smi register command\n"
+	   "smi write [smiaddr] [regaddr] [value] [page]\n"
+	   "    - write <value> to <regaddr> register command\n"
+	   "    - run the commands in the environment variable(s) 'var'\n");
+
+#endif /* CONFIG_CMD_SMIRW */
+
+#if defined(CONFIG_CMD_DUMP60XXPHY)
+static void phy_busywait(u32 eth_port_num)
+{
+	u32 timeout;
+	u16 reg;
+
+	timeout = MV88E60XX_PHY_TIMEOUT;
+	do {
+		mv_sw_eth_phy_reg_read(eth_port_num,
+				       MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+				       MV88E60XX_SMI_PHY_COMMAND, &reg);
+		if (timeout-- == 0) {
+			error_print("SMI busy timeout");
+			return -1;
+		}
+	} while (reg & BIT28);	/* busy mask */
+}
+
+int do_dump60xxphy(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+#define SMI_CMD_BUSY_OFFSET					15
+#define SMI_CMD_MODE_OFFSET					12
+#define SMI_CMD_OP_OFFSET					10
+#define SMI_CMD_ADDR_OFFSET					5
+
+	int page, i, prt;
+	int eth_port_num = 0;
+	u32 reg;
+
+	/* set page address to 3 */
+	prt = 0;
+	for (page = 0; page < 7; page++) {
+		if (page == 1)
+			continue;
+		if (page == 4)
+			continue;
+		phy_busywait(eth_port_num);
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+					MV88E60XX_SMI_PHY_DATA, page);
+
+		phy_busywait(eth_port_num);
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+					MV88E60XX_SMI_PHY_COMMAND,
+					(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+					 SMI_CMD_MODE_OFFSET | 1 <<
+					 SMI_CMD_OP_OFFSET | prt <<
+					 SMI_CMD_ADDR_OFFSET | 22));
+
+		/* read and display phy registers */
+		for (i = 0; i < 29; i++) {
+			if (page != 0) {
+				if (i < 16)
+					continue;
+				if (i == 22)
+					continue;
+			}
+			phy_busywait(eth_port_num);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E60XX_SMI_PHY_COMMAND,
+						(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+						 SMI_CMD_MODE_OFFSET | 2 <<
+						 SMI_CMD_OP_OFFSET | prt <<
+						 SMI_CMD_ADDR_OFFSET | i));
+
+			phy_busywait(eth_port_num);
+			reg = 0x0;
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+					       MV88E60XX_SMI_PHY_DATA, &reg);
+
+			printf("page %d reg %02d = %04x\n", page, i, (u16) reg);
+		}
+	}
+
+	/* restore page address to zero */
+	reg = 0;
+	phy_busywait(eth_port_num);
+	mv_sw_eth_phy_reg_write(eth_port_num, MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+				MV88E60XX_SMI_PHY_DATA, reg);
+	phy_busywait(eth_port_num);
+	mv_sw_eth_phy_reg_write(eth_port_num, MV88E60XX_GLOBAL_2_REG_DEV_ADDR,
+				MV88E60XX_SMI_PHY_COMMAND,
+				(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+				 SMI_CMD_MODE_OFFSET | 1 << SMI_CMD_OP_OFFSET |
+				 prt << SMI_CMD_ADDR_OFFSET | 22));
+
+	return 0;
+}
+
+U_BOOT_CMD(dump60xxphy, CONFIG_SYS_MAXARGS, 1, do_dump60xxphy,
+	   "dump60xxphy - dump 88360xx registers\n",
+	   "var [...]\n"
+	   "    - run the commands in the environment variable(s) 'var'\n");
+#endif /* CONFIG_CMD_DUMP60XXPHY */
+
+#endif /* CONFIG_SWITCH_88E60XX */
diff --git a/board/Marvell/common/mv88e61xx.c b/board/Marvell/common/mv88e61xx.c
new file mode 100644
index 0000000..72a58eb
--- /dev/null
+++ b/board/Marvell/common/mv88e61xx.c
@@ -0,0 +1,414 @@
+/*
+ * (C) Copyright 2009
+ * Marvell Semiconductor <www.marvell.com>
+ * Prafulla Wadaskar <prafulla at marvell.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * 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., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef MV88E61XX_DEBUG
+#define MV88E61XX_DEBUG 0
+#endif
+#define DEBUG_PRINT	MV88E61XX_DEBUG
+
+#include <common.h>
+#include <debug_prints.h>
+#include "../common/ppc_error_no.h"
+#include <command.h>
+#include <environment.h>
+#include <watchdog.h>
+#include <serial.h>
+#include <linux/stddef.h>
+#include <asm/byteorder.h>
+#if defined(CONFIG_CMD_NET)
+#include <net.h>
+#endif
+
+#if defined (CONFIG_SWITCH_88E61XX)
+
+/* CPU port can be configured in board header file */
+#if defined (CONFIG_SWITCH_88E61XX_CPU_PORT)
+#define MV88E61XX_CPU_PORT      CONFIG_SWITCH_88E61XX_CPU_PORT
+#else
+#define MV88E61XX_CPU_PORT      0x5
+#endif
+/* Enabled ports can be configured in board header file */
+#if defined (CONFIG_SWITCH_88E61XX_ENABLED_PORTS)
+#define MV88E61XX_ENABLED_PORTS CONFIG_SWITCH_88E61XX_ENABLED_PORTS
+#else
+#define MV88E61XX_ENABLED_PORTS (BIT0 | BIT1 | BIT2 | \
+                                 BIT3 | BIT4 | BIT5)
+#endif
+
+#define MV88E61XX_NAME                                  "88E6165"
+#define MV88E61XX_PHY_TIMEOUT                           100000
+#define MV88E61XX_MAX_PORTS_NUM                         0x6
+
+#define MV88E61XX_PORT_STATUS_REG                       0x1
+#define MV88E61XX_PORT_CONTROL_REG                      0x4
+#define MV88E61XX_PORT_VMAP_REG                         0x6
+#define MV88E61XX_PORT_VID_REG                          0x7
+
+#define MV88E61XX_PORTS_OFFSET                          0x10
+#define MV88E61XX_SMI_PHY_COMMAND                       0x18
+#define MV88E61XX_SMI_PHY_DATA                          0x19
+#define MV88E61XX_GLOBAL_2_REG_DEV_ADDR                 0x1C
+
+#define MV88E61XX_PORT_STATUS_BIT_LINKUP                BIT2
+
+/* Chip Address mode
+ * The Switch support two modes of operation
+ * 1. single chip mode and
+ * 2. Multi-chip mode
+ * Refer chip documentation for more details
+ *
+ * By default single chip mode is configured
+ * multichip mode operation can be configured in board header
+ */
+#ifndef CONFIG_SWITCH_88E61XX_MULTI_CHIP_ADDR_MODE
+#define mv_sw_eth_phy_reg_write eth_smi_reg_write
+#define mv_sw_eth_phy_reg_read eth_smi_reg_read
+#else
+void mv_sw_eth_phy_reg_write(u32 eth_port_num, u32 phy_adr, u32 reg_ofs,
+			     u16 data)
+{
+	u16 reg;
+	u32 smi_dev_addr;
+
+	smi_dev_addr = KW_REG_READ(KW_ETH_PHY_ADDR_REG(eth_port_num));
+	do {
+		eth_smi_reg_read(eth_port_num, smi_dev_addr, 0x0, &reg);
+	} while ((reg & BIT15));
+	/* Poll till SMIBusy bit is clear */
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 0x1, data);
+	/* Write data to Switch indirect data register */
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 0x0,
+			  reg_ofs | (phy_adr << 5) | BIT10 | BIT12 | BIT15);
+	/* Write command to Switch indirect command register (write) */
+}
+
+void mv_sw_eth_phy_reg_read(u32 eth_port_num, u32 phy_adr, u32 reg_ofs,
+			    u16 * data)
+{
+	u16 reg;
+	u32 smi_dev_addr;
+
+	smi_dev_addr = KW_REG_READ(KW_ETH_PHY_ADDR_REG(eth_port_num));
+	do {
+		eth_smi_reg_read(eth_port_num, smi_dev_addr, 0x0, &reg);
+	} while ((reg & BIT15));
+	/* Poll till SMIBusy bit is clear */
+	eth_smi_reg_write(eth_port_num, smi_dev_addr, 0x0,
+			  reg_ofs | (phy_adr << 5) | BIT10 | BIT12 | BIT15);
+	/* Write command to Switch indirect command register (read) */
+	do {
+		eth_smi_reg_read(eth_port_num, smi_dev_addr, 0x0, &reg);
+	} while ((reg & BIT15));
+	/* Poll till SMIBusy bit is clear */
+	eth_smi_reg_read(eth_port_num, smi_dev_addr, 0x1, (u16 *) & data);
+	/* Read data from Switch indirect data register */
+}
+#endif
+
+static void mv_switch_88e61xx_vlan_init(u32 eth_port_num,
+					u32 switch_cpu_port,
+					u32 switch_max_ports_num,
+					u32 switch_ports_ofs,
+					u32 switch_enabled_ports_mask)
+{
+	u32 prt;
+	u16 reg;
+
+	debug_print_ftrace();
+	/* be sure all ports are disabled */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		mv_sw_eth_phy_reg_read(eth_port_num, (switch_ports_ofs + prt),
+				       MV88E61XX_PORT_CONTROL_REG, &reg);
+		reg &= ~0x3;
+		mv_sw_eth_phy_reg_write(eth_port_num, (switch_ports_ofs + prt),
+					MV88E61XX_PORT_CONTROL_REG, reg);
+	}
+	/* Set CPU port VID to 0x1 */
+	mv_sw_eth_phy_reg_read(eth_port_num,
+			       (switch_ports_ofs + switch_cpu_port),
+			       MV88E61XX_PORT_VID_REG, &reg);
+	reg &= ~0xfff;
+	reg |= 0x1;
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				(switch_ports_ofs + switch_cpu_port),
+				MV88E61XX_PORT_VID_REG, reg);
+
+	/* Setting  Port default priority for all ports to zero */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		mv_sw_eth_phy_reg_read(eth_port_num, (switch_ports_ofs + prt),
+				       MV88E61XX_PORT_VID_REG, &reg);
+		reg &= ~0xc000;
+		mv_sw_eth_phy_reg_write(eth_port_num, (switch_ports_ofs + prt),
+					MV88E61XX_PORT_VID_REG, reg);
+	}
+	/* Setting VID and VID map for all ports except CPU port */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		/* only for enabled ports */
+		if ((1 << prt) & switch_enabled_ports_mask) {
+			/* skip CPU port */
+			if (prt == switch_cpu_port)
+				continue;
+
+			/*
+			 *  set Ports VLAN Mapping.
+			 *      port prt <--> MV88E61XX_CPU_PORT VLAN #prt+1.
+			 */
+
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E61XX_PORT_VID_REG, &reg);
+			reg &= ~0x0fff;
+			reg |= (prt + 1);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E61XX_PORT_VID_REG, reg);
+
+			/* Set Vlan map table for all ports to send only to MV88E61XX_CPU_PORT */
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E61XX_PORT_VMAP_REG, &reg);
+			reg &= ~((1 << switch_max_ports_num) - 1);
+			reg |= (1 << switch_cpu_port);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E61XX_PORT_VMAP_REG, reg);
+		}
+	}
+	/* Set Vlan map table for MV88E61XX_CPU_PORT to see all ports */
+	mv_sw_eth_phy_reg_read(eth_port_num,
+			       (switch_ports_ofs + switch_cpu_port),
+			       MV88E61XX_PORT_VMAP_REG, &reg);
+	reg &= ~((1 << switch_max_ports_num) - 1);
+	reg |= switch_enabled_ports_mask & ~(1 << switch_cpu_port);
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				(switch_ports_ofs + switch_cpu_port),
+				MV88E61XX_PORT_VMAP_REG, reg);
+
+	/*enable only appropriate ports to forwarding mode - and disable the others */
+	for (prt = 0; prt < switch_max_ports_num; prt++) {
+		if ((1 << prt) & switch_enabled_ports_mask) {
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E61XX_PORT_CONTROL_REG,
+					       &reg);
+			reg |= 0x3;
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E61XX_PORT_CONTROL_REG,
+						reg);
+		} else {
+			/* Disable port */
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       (switch_ports_ofs + prt),
+					       MV88E61XX_PORT_CONTROL_REG,
+					       &reg);
+			reg &= ~0x3;
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						(switch_ports_ofs + prt),
+						MV88E61XX_PORT_CONTROL_REG,
+						reg);
+		}
+	}
+}
+
+/*
+ * Marvell 88E61XX Switch initialization
+ */
+int mv_switch_88e61xx_init(u32 eth_port_num)
+{
+	u32 prt;
+	u16 reg;
+	volatile u32 timeout;
+
+	debug_print_ftrace();
+	/* Init vlan */
+	mv_switch_88e61xx_vlan_init(eth_port_num, MV88E61XX_CPU_PORT,
+				    MV88E61XX_MAX_PORTS_NUM,
+				    MV88E61XX_PORTS_OFFSET,
+				    MV88E61XX_ENABLED_PORTS);
+
+	/* Enable RGMII delay on Tx and Rx for CPU port */
+	mv_sw_eth_phy_reg_write(eth_port_num, 0x14, 0x1a, 0x81e7);
+	mv_sw_eth_phy_reg_read(eth_port_num, 0x15, 0x1a, &reg);
+	mv_sw_eth_phy_reg_write(eth_port_num, 0x15, 0x1a, 0x18);
+	mv_sw_eth_phy_reg_write(eth_port_num, 0x14, 0x1a, 0xc1e7);
+
+	for (prt = 0; prt < MV88E61XX_MAX_PORTS_NUM; prt++) {
+		if (prt != MV88E61XX_CPU_PORT) {
+			/*Enable Phy power up */
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E61XX_SMI_PHY_DATA, 0x3360);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E61XX_SMI_PHY_COMMAND,
+						(0x9410 | (prt << 5)));
+
+			/*Make sure SMIBusy bit cleared before another SMI operation can take place */
+			timeout = MV88E61XX_PHY_TIMEOUT;
+			do {
+				mv_sw_eth_phy_reg_read(eth_port_num,
+						       MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						       MV88E61XX_SMI_PHY_COMMAND,
+						       &reg);
+				if (timeout-- == 0) {
+					error_print("SMI busy timeout");
+					return -1;
+				}
+			} while (reg & BIT28);	/* busy mask */
+
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E61XX_SMI_PHY_DATA, 0x1140);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E61XX_SMI_PHY_COMMAND,
+						(0x9400 | (prt << 5)));
+
+			/*Make sure SMIBusy bit cleared before another SMI operation can take place */
+			timeout = MV88E61XX_PHY_TIMEOUT;
+			do {
+				mv_sw_eth_phy_reg_read(eth_port_num,
+						       MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						       MV88E61XX_SMI_PHY_COMMAND,
+						       &reg);
+				if (timeout-- == 0) {
+					error_print("SMI busy timeout");
+					return -1;
+				}
+			} while (reg & BIT28);	/* busy mask */
+		}
+
+		/*Enable port */
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E61XX_PORTS_OFFSET + prt, 4, 0x7f);
+	}
+	/*Force CPU port to RGMII FDX 1000Base */
+	mv_sw_eth_phy_reg_write(eth_port_num,
+				MV88E61XX_PORTS_OFFSET + MV88E61XX_CPU_PORT, 1,
+				0x3e);
+
+	info_print(MV88E61XX_NAME " Initialized");
+	return 0;
+}
+
+#if defined(CONFIG_CMD_DUMP61XXPHY)
+static void phy_busywait(u32 eth_port_num)
+{
+	u32 timeout;
+	u16 reg;
+
+	timeout = MV88E61XX_PHY_TIMEOUT;
+	do {
+		mv_sw_eth_phy_reg_read(eth_port_num,
+				       MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+				       MV88E61XX_SMI_PHY_COMMAND, &reg);
+		if (timeout-- == 0) {
+			error_print("SMI busy timeout");
+			return -1;
+		}
+	} while (reg & BIT28);	/* busy mask */
+}
+
+int do_dump61xxphy(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+#define SMI_CMD_BUSY_OFFSET					15
+#define SMI_CMD_MODE_OFFSET					12
+#define SMI_CMD_OP_OFFSET					10
+#define SMI_CMD_ADDR_OFFSET					5
+
+	int page, i, prt;
+	int eth_port_num = 0;
+	u32 reg;
+
+	/* set page address to 3 */
+	prt = 0;
+	for (page = 0; page < 7; page++) {
+		if (page == 1)
+			continue;
+		if (page == 4)
+			continue;
+		phy_busywait(eth_port_num);
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+					MV88E61XX_SMI_PHY_DATA, page);
+
+		phy_busywait(eth_port_num);
+		mv_sw_eth_phy_reg_write(eth_port_num,
+					MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+					MV88E61XX_SMI_PHY_COMMAND,
+					(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+					 SMI_CMD_MODE_OFFSET | 1 <<
+					 SMI_CMD_OP_OFFSET | prt <<
+					 SMI_CMD_ADDR_OFFSET | 22));
+
+		/* read and display phy registers */
+		for (i = 0; i < 29; i++) {
+			if (page != 0) {
+				if (i < 16)
+					continue;
+				if (i == 22)
+					continue;
+			}
+			phy_busywait(eth_port_num);
+			mv_sw_eth_phy_reg_write(eth_port_num,
+						MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+						MV88E61XX_SMI_PHY_COMMAND,
+						(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+						 SMI_CMD_MODE_OFFSET | 2 <<
+						 SMI_CMD_OP_OFFSET | prt <<
+						 SMI_CMD_ADDR_OFFSET | i));
+
+			phy_busywait(eth_port_num);
+			reg = 0x0;
+			mv_sw_eth_phy_reg_read(eth_port_num,
+					       MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+					       MV88E61XX_SMI_PHY_DATA, &reg);
+
+			printf("page %d reg %02d = %04x\n", page, i, (u16) reg);
+		}
+	}
+
+	/* restore page address to zero */
+	reg = 0;
+	phy_busywait(eth_port_num);
+	mv_sw_eth_phy_reg_write(eth_port_num, MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+				MV88E61XX_SMI_PHY_DATA, reg);
+	phy_busywait(eth_port_num);
+	mv_sw_eth_phy_reg_write(eth_port_num, MV88E61XX_GLOBAL_2_REG_DEV_ADDR,
+				MV88E61XX_SMI_PHY_COMMAND,
+				(1 << SMI_CMD_BUSY_OFFSET | 1 <<
+				 SMI_CMD_MODE_OFFSET | 1 << SMI_CMD_OP_OFFSET |
+				 prt << SMI_CMD_ADDR_OFFSET | 22));
+
+	return 0;
+}
+
+U_BOOT_CMD(dump61xxphy, CONFIG_SYS_MAXARGS, 1, do_dump61xxphy,
+	   "dump61xxphy - dump 88361xx registers\n",
+	   "var [...]\n"
+	   "    - run the commands in the environment variable(s) 'var'\n");
+#endif /* CONFIG_CMD_DUMP61XXPHY */
+
+#endif /* CONFIG_SWITCH_88E61XX */
-- 
1.5.3.3



More information about the U-Boot mailing list