From: prafulla_wadaskar <prafu...@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 <yo...@marvell.com> Michael Blostein <michae...@marvell.com Signed-off-by: prafulla_wadaskar <prafu...@marvell.com> Reviewed by: Ronen Shitrit <rshit...@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 <prafu...@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 <prafu...@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 <prafu...@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 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot