[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 &= ~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 |= (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 |= 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 &= ~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 &= ~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 &= ~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 &= ~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 &= ~((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 &= ~((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 |= 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 &= ~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, ®);
+ 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, ®);
+
+ 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, ®);
+ } 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, ®);
+ } 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, ®);
+ } 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 &= ~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 &= ~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 &= ~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 &= ~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 &= ~((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 &= ~((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 |= 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 &= ~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, ®);
+ 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,
+ ®);
+ 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,
+ ®);
+ 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, ®);
+ 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, ®);
+
+ 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