[U-Boot] [PATCH 0/6] further updates for Keymile ARM boards

This patch seris is based on a already transmitted but currently not committed patch serie: http://lists.denx.de/pipermail/u-boot/2012-March/121009.html It adds some further support for our managed switch on kmnusa and kmcoge5un. And some other feature are added or fixed (dip switch detection for kmcoge5un, testpin detection for kmcoge5un).
This patch serie modifies only code for Keymile boards and does no changes in common u-boot code.
Thomas Herzmann (2): arm/km: add implementation for read_dip_switch arm/km: fix testpin detection for kmcoge5un
Valentin Longchamp (4): arm/km: add support for external switch configuration arm/km: enable external switch configuration for kmnusa arm/km: skip FPGA config when already configured arm/km: support the 2 PCIe fpga resets
board/keymile/common/common.h | 7 -- board/keymile/km_arm/fpga_config.c | 52 +++++++++- board/keymile/km_arm/km_arm.c | 31 +++++-- board/keymile/km_arm/managed_switch.c | 169 +++++++++++++++++++++++++++++++-- board/keymile/km_arm/managed_switch.h | 99 +++++++++++++++++++ include/configs/km_kirkwood.h | 3 - 6 files changed, 330 insertions(+), 31 deletions(-) create mode 100644 board/keymile/km_arm/managed_switch.h

From: Valentin Longchamp valentin.longchamp@keymile.com
This can be used if we do not want to use an EEPROM for the configuration.
Signed-off-by: Valentin Longchamp valentin.longchamp@keymile.com --- board/keymile/common/common.h | 7 -- board/keymile/km_arm/km_arm.c | 3 + board/keymile/km_arm/managed_switch.c | 169 +++++++++++++++++++++++++++++++-- board/keymile/km_arm/managed_switch.h | 99 +++++++++++++++++++ 4 files changed, 261 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/km_arm.c b/board/keymile/km_arm/km_arm.c index 3857c7d..5a06314 100644 --- a/board/keymile/km_arm/km_arm.c +++ b/board/keymile/km_arm/km_arm.c @@ -44,6 +44,9 @@ #include <asm/arch/mpp.h>
#include "../common/common.h" +#ifdef CONFIG_KM_MANAGED_SW_ADDR +#include "managed_switch.h" +#endif
DECLARE_GLOBAL_DATA_PTR;
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, ®); + 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, + ®); + 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, ®); + 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, + ®); + 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, ®); + 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, + ®); + 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.longchamp@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

From: Valentin Longchamp valentin.longchamp@keymile.com
The configuration EEPROM should be removed for P1B.
Signed-off-by: Valentin Longchamp valentin.longchamp@keymile.com --- board/keymile/km_arm/km_arm.c | 10 ++++------ 1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/board/keymile/km_arm/km_arm.c b/board/keymile/km_arm/km_arm.c index 5a06314..d2f88c2 100644 --- a/board/keymile/km_arm/km_arm.c +++ b/board/keymile/km_arm/km_arm.c @@ -393,13 +393,11 @@ void reset_phy(void) if (miiphy_set_current_dev(name)) return;
- /* enable autoneg on port 0 phy */ - ext_switch_reg_write(name, CONFIG_KM_MANAGED_SW_ADDR, 0, 0, 0x3300); +#if defined(CONFIG_KM_NUSA) + ext_switch_program(name, CONFIG_KM_MANAGED_SW_ADDR); +#endif
- /* egress broadcast franes on all macs, with forwarding */ - ext_switch_reg_write(name, CONFIG_KM_MANAGED_SW_ADDR, 16, 4, 0x000f); - ext_switch_reg_write(name, CONFIG_KM_MANAGED_SW_ADDR, 20, 4, 0x000f); - ext_switch_reg_write(name, CONFIG_KM_MANAGED_SW_ADDR, 21, 4, 0x000f); + ext_switch_reset(name, CONFIG_KM_MANAGED_SW_ADDR); } #endif

From: Valentin Longchamp valentin.longchamp@keymile.com
In order to be able to perform board resets without interrupting the traffic, the configuration of an already properly configured FPGA is skipped.
This is because some PCIe FPGAs embed some other function that must continue to work over reset.
It is then the responsibility of the application to trigger a reconfiguration when needed. This is done by lowering the FPGA_INIT_B pin for delaying the configuration to u-boot @ next reboot, and then lower the FPGA_PROGRAM_B signal.
Signed-off-by: Valentin Longchamp valentin.longchamp@keymile.com --- board/keymile/km_arm/fpga_config.c | 41 +++++++++++++++++++++++++++++++++++- 1 files changed, 40 insertions(+), 1 deletions(-)
diff --git a/board/keymile/km_arm/fpga_config.c b/board/keymile/km_arm/fpga_config.c index 4356b9a..fdc8727 100644 --- a/board/keymile/km_arm/fpga_config.c +++ b/board/keymile/km_arm/fpga_config.c @@ -96,12 +96,43 @@ static int boco_set_bits(u8 reg, u8 flags) #define SPI_REG 0x06 #define CFG_EEPROM 0x02 #define FPGA_PROG 0x04 +#define FPGA_INIT_B 0x10 #define FPGA_DONE 0x20
+static int fpga_done(void) +{ + int ret = 0; + u8 regval; + + /* this is only supported with the boco2 design */ + if (!check_boco2()) + return 0; + + ret = i2c_read(BOCO_ADDR, SPI_REG, 1, ®val, 1); + if (ret) { + printf("%s: error reading the BOCO @%#x !!\n", + __func__, SPI_REG); + return 0; + } + + return regval & FPGA_DONE ? 1 : 0; +} + +int skip; + int trigger_fpga_config(void) { int ret = 0;
+ /* if the FPGA is already configured, we do not want to + * reconfigure it */ + skip = 0; + if (fpga_done()) { + printf("PCIe FPGA config: skipped\n"); + skip = 1; + return 0; + } + if (check_boco2()) { /* we have a BOCO2, this has to be triggered here */
@@ -111,7 +142,7 @@ int trigger_fpga_config(void) return ret;
/* trigger the config start */ - ret = boco_clear_bits(SPI_REG, FPGA_PROG); + ret = boco_clear_bits(SPI_REG, FPGA_PROG | FPGA_INIT_B); if (ret) return ret;
@@ -123,6 +154,11 @@ int trigger_fpga_config(void) if (ret) return ret;
+ /* finally, raise INIT_B to remove the config delay */ + ret = boco_set_bits(SPI_REG, FPGA_INIT_B); + if (ret) + return ret; + } else { /* we do it the old way, with the gpio pin */ kw_gpio_set_valid(KM_XLX_PROGRAM_B_PIN, 1); @@ -141,6 +177,9 @@ int wait_for_fpga_config(void) u8 spictrl; u32 timeout = 20000;
+ if (skip) + return 0; + if (!check_boco2()) { /* we do not have BOCO2, this is not really used */ return 0;

From: Valentin Longchamp valentin.longchamp@keymile.com
The PCIe FPGAs now have to support 2 resets: one for the non traffic affecting part (PCIe) and one for the traffic affecting part.
When the FPGA is not reconfigured, we only reset the PCIe part.
Signed-off-by: Valentin Longchamp valentin.longchamp@keymile.com --- board/keymile/km_arm/fpga_config.c | 11 ++++++++--- 1 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/board/keymile/km_arm/fpga_config.c b/board/keymile/km_arm/fpga_config.c index fdc8727..fcc5fe6 100644 --- a/board/keymile/km_arm/fpga_config.c +++ b/board/keymile/km_arm/fpga_config.c @@ -206,25 +206,30 @@ int wait_for_fpga_config(void) }
#define PRST1 0x4 -#define BRIDGE_RST 0x4 +#define PCIE_RST 0x10 +#define TRAFFIC_RST 0x04
int fpga_reset(void) { int ret = 0; + u8 resets;
if (!check_boco2()) { /* we do not have BOCO2, this is not really used */ return 0; }
- ret = boco_clear_bits(PRST1, BRIDGE_RST); + /* if we have skipped, we only want to reset the PCIe part */ + resets = skip ? PCIE_RST : PCIE_RST | TRAFFIC_RST; + + ret = boco_clear_bits(PRST1, resets); if (ret) return ret;
/* small delay for the pulse */ udelay(10);
- ret = boco_set_bits(PRST1, BRIDGE_RST); + ret = boco_set_bits(PRST1, resets); if (ret) return ret;

From: Thomas Herzmann thomas.herzmann@keymile.com
Add a function to read the dip_switch on kmcoge5un. If the switch is set the actual_bank is set to 0 and this SW is booted.
Signed-off-by: Thomas Herzmann thomas.herzmann@keymile.com Signed-off-by: Holger Brunck holger.brunck@keymile.com --- board/keymile/km_arm/km_arm.c | 14 +++++++++++++- include/configs/km_kirkwood.h | 3 --- 2 files changed, 13 insertions(+), 4 deletions(-)
diff --git a/board/keymile/km_arm/km_arm.c b/board/keymile/km_arm/km_arm.c index d2f88c2..e4edbe4 100644 --- a/board/keymile/km_arm/km_arm.c +++ b/board/keymile/km_arm/km_arm.c @@ -287,12 +287,24 @@ int board_init(void)
int board_late_init(void) { +#if defined(CONFIG_KMCOGE5UN) +/* I/O pin to erase flash RGPP09 = MPP43 */ +#define KM_FLASH_ERASE_ENABLE 43 + u8 dip_switch = kw_gpio_get_value(KM_FLASH_ERASE_ENABLE); + + /* if pin 1 do full erase */ + if (dip_switch != 0) { + /* start bootloader */ + puts("DIP: Enabled\n"); + setenv("actual_bank", "0"); + } +#endif + #if defined(CONFIG_KM_FPGA_CONFIG) wait_for_fpga_config(); fpga_reset(); toggle_eeprom_spi_bus(); #endif - return 0; }
diff --git a/include/configs/km_kirkwood.h b/include/configs/km_kirkwood.h index 549122b..9ecfcf4 100644 --- a/include/configs/km_kirkwood.h +++ b/include/configs/km_kirkwood.h @@ -184,8 +184,5 @@ #undef CONFIG_KIRKWOOD_PCIE_INIT #endif
-#ifndef CONFIG_KM_FPGA_CONFIG -#undef BOARD_LATE_INIT -#endif
#endif /* _CONFIG_KM_KIRKWOOD */

From: Thomas Herzmann thomas.herzmann@keymile.com
On kmcoge5un unfortunately the HW wiring is differently to other km arm boards.
Signed-off-by: Thomas Herzmann thomas.herzmann@keymile.com Signed-off-by: Holger Brunck holger.brunck@keymile.com --- board/keymile/km_arm/km_arm.c | 4 ++++ 1 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/board/keymile/km_arm/km_arm.c b/board/keymile/km_arm/km_arm.c index e4edbe4..1825945 100644 --- a/board/keymile/km_arm/km_arm.c +++ b/board/keymile/km_arm/km_arm.c @@ -518,7 +518,11 @@ int get_scl(void)
int post_hotkeys_pressed(void) { +#if defined(CONFIG_KMCOGE5UN) + return kw_gpio_get_value(KM_POST_EN_L); +#else return !kw_gpio_get_value(KM_POST_EN_L); +#endif }
ulong post_word_load(void)
participants (1)
-
Holger Brunck