This patch add support for the configuration of an external switch from
the 88E6xxx series from Marvell trough an MDIO link using indirect
adressing. This can be used if we do not want to use an EEPROM for the
configuration.

This driver is not generic and was not tested on a lot of switches,
that's why we keep it in our keymile specific code, where the
assumptions of the implementations remain true.

Signed-off-by: Valentin Longchamp <valentin.longch...@keymile.com>
---
changes for v2:
  - nothing
changes for v3:
  - more complete commit message

 board/keymile/common/common.h         |    7 --
 board/keymile/km_arm/managed_switch.c |  169 +++++++++++++++++++++++++++++++--
 board/keymile/km_arm/managed_switch.h |   99 +++++++++++++++++++
 3 files changed, 258 insertions(+), 17 deletions(-)
 create mode 100644 board/keymile/km_arm/managed_switch.h

diff --git a/board/keymile/common/common.h b/board/keymile/common/common.h
index c58e565..e9abfcd 100644
--- a/board/keymile/common/common.h
+++ b/board/keymile/common/common.h
@@ -125,13 +125,6 @@ struct bfticu_iomap {
 int ethernet_present(void);
 int ivm_read_eeprom(void);
 
-
-int ext_switch_reg_write(const char *devname, u8 phy_addr, u8 port,
-       u8 reg, u16 data);
-int ext_switch_reg_read(const char *devname, u8 phy_addr, u8 port,
-       u8 reg, u16 *data);
-
-
 int trigger_fpga_config(void);
 int wait_for_fpga_config(void);
 int fpga_reset(void);
diff --git a/board/keymile/km_arm/managed_switch.c 
b/board/keymile/km_arm/managed_switch.c
index 482c18d..3b022cd 100644
--- a/board/keymile/km_arm/managed_switch.c
+++ b/board/keymile/km_arm/managed_switch.c
@@ -25,15 +25,43 @@
 #include <miiphy.h>
 #include <asm/errno.h>
 
-#define SMI_HDR                ((0x8 | 0x1) << 12)
-#define SMI_BUSY_MASK  (0x8000)
-#define SMIRD_OP       (0x2 << 10)
-#define SMIWR_OP       (0x1 << 10)
-#define SMI_MASK       0x1f
-#define PORT_SHIFT     5
+#include "managed_switch.h"
 
-#define COMMAND_REG    0
-#define DATA_REG       1
+#if defined(CONFIG_KM_NUSA)
+struct switch_reg sw_conf[] = {
+       /* port 0, PIGY4, autoneg */
+       { PORT(0), PORT_PHY, NO_SPEED_FOR },
+       { PORT(0), PORT_CTRL, FORWARDING | EGRS_FLD_ALL },
+       { PHY(0), PHY_CTRL, PHY_100_MBPS | AUTONEG_EN | AUTONEG_RST |
+               FULL_DUPLEX },
+       { PHY(0), PHY_SPEC_CTRL, AUTO_MDIX_EN },
+       /* port 1, unused */
+       { PORT(1), PORT_CTRL, PORT_DIS },
+       { PHY(1), PHY_CTRL, PHY_PWR_DOWN },
+       { PHY(1), PHY_SPEC_CTRL, SPEC_PWR_DOWN },
+       /* port 2, unused */
+       { PORT(2), PORT_CTRL, PORT_DIS },
+       { PHY(2), PHY_CTRL, PHY_PWR_DOWN },
+       { PHY(2), PHY_SPEC_CTRL, SPEC_PWR_DOWN },
+       /* port 3, unused */
+       { PORT(3), PORT_CTRL, PORT_DIS },
+       { PHY(3), PHY_CTRL, PHY_PWR_DOWN },
+       { PHY(3), PHY_SPEC_CTRL, SPEC_PWR_DOWN },
+       /* port 4, ICNEV, SerDes, SGMII */
+       { PORT(4), PORT_STATUS, NO_PHY_DETECT },
+       { PORT(4), PORT_PHY, SPEED_1000_FOR },
+       { PORT(4), PORT_CTRL, FORWARDING | EGRS_FLD_ALL },
+       { PHY(4), PHY_CTRL, PHY_PWR_DOWN },
+       { PHY(4), PHY_SPEC_CTRL, SPEC_PWR_DOWN },
+       /* port 5, CPU_RGMII */
+       { PORT(5), PORT_PHY, RX_RGMII_TIM | TX_RGMII_TIM | FLOW_CTRL_EN |
+               FLOW_CTRL_FOR | LINK_VAL | LINK_FOR | FULL_DPX |
+               FULL_DPX_FOR | SPEED_1000_FOR },
+       { PORT(5), PORT_CTRL, FORWARDING | EGRS_FLD_ALL },
+       /* port 6, unused, this port has no phy */
+       { PORT(6), PORT_CTRL, PORT_DIS },
+};
+#endif
 
 static int ext_switch_wait_rdy(const char *devname, u8 phy_addr)
 {
@@ -59,7 +87,7 @@ static int ext_switch_wait_rdy(const char *devname, u8 
phy_addr)
        return 0;
 }
 
-int ext_switch_reg_read(const char *devname, u8 phy_addr, u8 port,
+static int ext_switch_reg_read(const char *devname, u8 phy_addr, u8 port,
        u8 reg, u16 *data)
 {
        int ret;
@@ -85,7 +113,7 @@ int ext_switch_reg_read(const char *devname, u8 phy_addr, u8 
port,
        return ret;
 }
 
-int ext_switch_reg_write(const char *devname, u8 phy_addr, u8 port,
+static int ext_switch_reg_write(const char *devname, u8 phy_addr, u8 port,
        u8 reg, u16 data)
 {
        int ret;
@@ -114,6 +142,127 @@ int ext_switch_reg_write(const char *devname, u8 
phy_addr, u8 port,
        return 0;
 }
 
+static int ppu_enable(const char *devname, u8 phy_addr)
+{
+       int i, ret = 0;
+       u16 reg;
+
+       ret = ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+       if (ret) {
+               printf("%s: Error reading global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       reg |= PPU_ENABLE;
+
+       ret = ext_switch_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+       if (ret) {
+               printf("%s: Error writing global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+                       &reg);
+               if ((reg & 0xc000) == 0xc000)
+                       return 0;
+               udelay(1000);
+       }
+
+       return -ETIMEDOUT;
+}
+
+static int ppu_disable(const char *devname, u8 phy_addr)
+{
+       int i, ret = 0;
+       u16 reg;
+
+       ret = ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+       if (ret) {
+               printf("%s: Error reading global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       reg &= ~PPU_ENABLE;
+
+       ret = ext_switch_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+       if (ret) {
+               printf("%s: Error writing global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+                       &reg);
+               if ((reg & 0xc000) != 0xc000)
+                       return 0;
+               udelay(1000);
+       }
+
+       return -ETIMEDOUT;
+}
+
+int ext_switch_program(const char *devname, u8 phy_addr)
+{
+       int i, ret = 0;
+
+       /* first we need to disable the PPU */
+       ret = ppu_disable(devname, phy_addr);
+       if (ret) {
+               printf("%s: Error disabling PPU\n", __func__);
+               return ret;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(sw_conf); i++) {
+               ret = ext_switch_reg_write(devname, phy_addr, sw_conf[i].port,
+                       sw_conf[i].reg, sw_conf[i].value);
+               if (ret) {
+                       printf("%s: Error configuring switch\n", __func__);
+                       ppu_enable(devname, phy_addr);
+                       return ret;
+               }
+       }
+
+       /* re-enable the PPU */
+       ret = ppu_enable(devname, phy_addr);
+       if (ret) {
+               printf("%s: Error enabling PPU\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+int ext_switch_reset(const char *devname, u8 phy_addr)
+{
+       int i, ret = 0;
+       u16 reg;
+
+       ret = ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_CTRL, &reg);
+       if (ret) {
+               printf("%s: Error reading global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       reg = SW_RESET | PPU_ENABLE | 0x0400;
+
+       ret = ext_switch_reg_write(devname, phy_addr, GLOBAL, GLOBAL_CTRL, reg);
+       if (ret) {
+               printf("%s: Error writing global ctrl reg\n", __func__);
+               return ret;
+       }
+
+       for (i = 0; i < 1000; i++) {
+               ext_switch_reg_read(devname, phy_addr, GLOBAL, GLOBAL_STATUS,
+                       &reg);
+               if ((reg & 0xc800) != 0xc800)
+                       return 0;
+               udelay(1000);
+       }
+
+       return -ETIMEDOUT;
+}
+
 int do_sw_reg_read(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 {
        char *name = "egiga0";
diff --git a/board/keymile/km_arm/managed_switch.h 
b/board/keymile/km_arm/managed_switch.h
new file mode 100644
index 0000000..c0dcf82
--- /dev/null
+++ b/board/keymile/km_arm/managed_switch.h
@@ -0,0 +1,99 @@
+/*
+ * (C) Copyright 2012
+ * Valentin Lontgchamp, Keymile AG, valentin.longch...@keymile.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 __MANAGED_SWITCH_H
+#define __MANAGED_SWITCH_H
+
+#include <common.h>
+
+#define SMI_HDR                ((0x8 | 0x1) << 12)
+#define SMI_BUSY_MASK  (0x8000)
+#define SMIRD_OP       (0x2 << 10)
+#define SMIWR_OP       (0x1 << 10)
+#define SMI_MASK       0x1f
+#define PORT_SHIFT     5
+
+#define COMMAND_REG    0
+#define DATA_REG       1
+
+/* global registers */
+#define GLOBAL         0x1b
+
+#define GLOBAL_STATUS  0x00
+#define PPU_STATE      0x8000
+
+#define GLOBAL_CTRL    0x04
+#define SW_RESET       0x8000
+#define PPU_ENABLE     0x4000
+
+/* PHY registers */
+#define PHY(itf)       (itf)
+
+#define PHY_CTRL       0x00
+#define PHY_100_MBPS   0x2000
+#define AUTONEG_EN     0x1000
+#define AUTONEG_RST    0x0200
+#define FULL_DUPLEX    0x0100
+#define PHY_PWR_DOWN   0x0800
+
+#define PHY_STATUS     0x01
+
+#define PHY_SPEC_CTRL  0x10
+#define SPEC_PWR_DOWN  0x0004
+#define AUTO_MDIX_EN   0x0060
+
+/* PORT or MAC registers */
+#define PORT(itf)      (itf+0x10)
+
+#define PORT_STATUS    0x00
+#define NO_PHY_DETECT  0x0000
+
+#define PORT_PHY       0x01
+#define RX_RGMII_TIM   0x8000
+#define TX_RGMII_TIM   0x4000
+#define FLOW_CTRL_EN   0x0080
+#define FLOW_CTRL_FOR  0x0040
+#define LINK_VAL       0x0020
+#define LINK_FOR       0x0010
+#define FULL_DPX       0x0008
+#define FULL_DPX_FOR   0x0004
+#define NO_SPEED_FOR   0x0003
+#define SPEED_1000_FOR 0x0002
+#define SPEED_100_FOR  0x0001
+#define SPEED_10_FOR   0x0000
+
+#define PORT_CTRL      0x04
+#define FORWARDING     0x0003
+#define EGRS_FLD_ALL   0x000c
+#define PORT_DIS       0x0000
+
+struct switch_reg {
+       u8 port;
+       u8 reg;
+       u16 value;
+};
+
+int ext_switch_reset(const char *devname, u8 phy_addr);
+int ext_switch_program(const char *devname, u8 phy_addr);
+
+#endif
-- 
1.7.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to