meta-nicole: u-boot: add bmc position support

This brings:
 - GPIO support in u-boot
 - BMC position is determined by the GPIOE1 and stored as a boot
   argument.

(From meta-yadro rev: 1b68cd934fe58ce3c33b272b45497d2a1b4e35e6)

Change-Id: Ieaf5bda2267b533327618330760431e66224092a
End-User-Impact: In the u-boot shell now available `gpio` command
Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
Signed-off-by: Andrew Geissler <geissonator@yahoo.com>
diff --git a/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0001-Add-system-reset-status-support.patch b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0001-Add-system-reset-status-support.patch
index 4daa354..d1cc9d9 100644
--- a/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0001-Add-system-reset-status-support.patch
+++ b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0001-Add-system-reset-status-support.patch
@@ -1,4 +1,4 @@
-From cf39fde2c1cfb55aa756bfb551c4087ba2fd4b6c Mon Sep 17 00:00:00 2001
+From 1c5b450a068583f2407767451ef636d0661071da Mon Sep 17 00:00:00 2001
 From: Alexander Filippov <a.filippov@yadro.com>
 Date: Tue, 7 Apr 2020 16:45:41 +0300
 Subject: [PATCH] Add system reset status support
@@ -11,27 +11,17 @@
 for applications to query.
 
 Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
-
 ---
- arch/arm/include/asm/arch-aspeed/ast_g5_platform.h |  1 +
- arch/arm/include/asm/arch-aspeed/ast_scu.h         |  2 +-
- arch/arm/mach-aspeed/ast-scu.c                     |  6 +-
- board/aspeed/ast-g5/ast-g5.c                       | 98 ++++++++++++++++++++++
- 4 files changed, 105 insertions(+), 2 deletions(-)
+ arch/arm/include/asm/arch-aspeed/ast_scu.h  |   2 +-
+ arch/arm/include/asm/arch-aspeed/platform.h |   2 +
+ arch/arm/mach-aspeed/Makefile               |   1 +
+ arch/arm/mach-aspeed/ast-late-init.c        | 114 ++++++++++++++++++++
+ arch/arm/mach-aspeed/ast-scu.c              |   6 +-
+ 5 files changed, 123 insertions(+), 2 deletions(-)
+ create mode 100644 arch/arm/mach-aspeed/ast-late-init.c
 
-diff --git a/arch/arm/include/asm/arch-aspeed/ast_g5_platform.h b/arch/arm/include/asm/arch-aspeed/ast_g5_platform.h
-index 4210873..d2a4268cd 100644
---- a/arch/arm/include/asm/arch-aspeed/ast_g5_platform.h
-+++ b/arch/arm/include/asm/arch-aspeed/ast_g5_platform.h
-@@ -188,5 +188,6 @@
- #define AST_FORMATTER_MEM_SIZE		0xC00000 /* 12MB */
- #define AST_FORMATTER_MEM_BASE 		(AST_H264_MEM_BASE + AST_H264_MEM_SIZE)
- 
-+#define CONFIG_BOARD_LATE_INIT 1 /* Call board_late_init */
- 
- #endif
 diff --git a/arch/arm/include/asm/arch-aspeed/ast_scu.h b/arch/arm/include/asm/arch-aspeed/ast_scu.h
-index dcbc673..b428f38 100644
+index dcbc6730d4..b428f386d6 100644
 --- a/arch/arm/include/asm/arch-aspeed/ast_scu.h
 +++ b/arch/arm/include/asm/arch-aspeed/ast_scu.h
 @@ -29,7 +29,7 @@
@@ -43,48 +33,43 @@
  extern void ast_scu_security_info(void);
  extern u32 ast_scu_revision_id(void);
  extern u32 ast_scu_get_vga_memsize(void);
-diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c
-index 12de9b8..5afd379 100644
---- a/arch/arm/mach-aspeed/ast-scu.c
-+++ b/arch/arm/mach-aspeed/ast-scu.c
-@@ -482,22 +482,26 @@ void ast_scu_security_info(void)
- 	}
- }
+diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h
+index 1c02914fcb..b9207c492f 100644
+--- a/arch/arm/include/asm/arch-aspeed/platform.h
++++ b/arch/arm/include/asm/arch-aspeed/platform.h
+@@ -31,4 +31,6 @@
+ #err "No define for platform.h"
+ #endif
  
--void ast_scu_sys_rest_info(void)
-+u32 ast_scu_sys_rest_info(void)
- {
- 	u32 rest = ast_scu_read(AST_SCU_SYS_CTRL);
- 
- 	if (rest & SCU_SYS_EXT_RESET_FLAG) {
- 		printf("RST : External\n");
- 		ast_scu_write(SCU_SYS_EXT_RESET_FLAG, AST_SCU_SYS_CTRL);
-+		rest = SCU_SYS_EXT_RESET_FLAG;
- 	} else if (rest & SCU_SYS_WDT_RESET_FLAG) {
- 		printf("RST : Watchdog\n");
- 		ast_scu_write(SCU_SYS_WDT_RESET_FLAG, AST_SCU_SYS_CTRL);
-+		rest = SCU_SYS_WDT_RESET_FLAG;
- 	} else if (rest & SCU_SYS_PWR_RESET_FLAG) {
- 		printf("RST : Power On\n");
- 		ast_scu_write(SCU_SYS_PWR_RESET_FLAG, AST_SCU_SYS_CTRL);
-+		rest = SCU_SYS_PWR_RESET_FLAG;
- 	} else {
- 		printf("RST : CLK en\n");
- 	}
-+	return rest;
- }
- 
- u32 ast_scu_get_vga_memsize(void)
-diff --git a/board/aspeed/ast-g5/ast-g5.c b/board/aspeed/ast-g5/ast-g5.c
-index 12496ce..42d7496 100644
---- a/board/aspeed/ast-g5/ast-g5.c
-+++ b/board/aspeed/ast-g5/ast-g5.c
-@@ -105,3 +105,104 @@ void hw_watchdog_reset(void)
- 	writel(0x4755, AST_WDT2_BASE + 0x08);
- }
- #endif /* CONFIG_WATCHDOG */
++#define CONFIG_BOARD_LATE_INIT 1 /* Call board_late_init */
 +
-+#ifdef CONFIG_BOARD_LATE_INIT
+ #endif
+diff --git a/arch/arm/mach-aspeed/Makefile b/arch/arm/mach-aspeed/Makefile
+index 7d8930beb9..4af2a7c96a 100644
+--- a/arch/arm/mach-aspeed/Makefile
++++ b/arch/arm/mach-aspeed/Makefile
+@@ -15,3 +15,4 @@ obj-y += timer.o reset.o cpuinfo.o ast-scu.o ast-ahbc.o ast-sdmc.o
+ obj-$(CONFIG_AST_SPI_NOR) += flash.o
+ obj-$(CONFIG_ARCH_AST2500) += platform_g5.o
+ obj-$(CONFIG_ARCH_AST2400) += platform_g4.o
++obj-$(CONFIG_BOARD_LATE_INIT) += ast-late-init.o
+diff --git a/arch/arm/mach-aspeed/ast-late-init.c b/arch/arm/mach-aspeed/ast-late-init.c
+new file mode 100644
+index 0000000000..5646c0e882
+--- /dev/null
++++ b/arch/arm/mach-aspeed/ast-late-init.c
+@@ -0,0 +1,114 @@
++/*
++ * SPDX-License-Identifier: Apache-2.0
++ * Copyright (C) 2020 YADRO.
++ */
++
++#include <common.h>
++
++#include <asm/arch/ast_scu.h>
++#include <asm/arch/regs-scu.h>
++#include <malloc.h>
++
 +static void update_bootargs_cmd(const char *key, const char *value)
 +{
 +    int buf_len;
@@ -158,7 +143,7 @@
 +    free(buf);
 +}
 +
-+int board_late_init(void)
++static void set_reset_reason(void)
 +{
 +    u32 reset_reason = ast_scu_sys_rest_info();
 +
@@ -180,7 +165,46 @@
 +        snprintf(value, sizeof(value) - 1, "0x%x", reset_reason);
 +        update_bootargs_cmd("resetreason", value);
 +    }
++}
++
++int board_late_init(void)
++{
++    set_reset_reason();
 +
 +    return 0;
 +}
-+#endif /* CONFIG_BOARD_LATE_INIT */
+diff --git a/arch/arm/mach-aspeed/ast-scu.c b/arch/arm/mach-aspeed/ast-scu.c
+index 12de9b8036..5afd3793e3 100644
+--- a/arch/arm/mach-aspeed/ast-scu.c
++++ b/arch/arm/mach-aspeed/ast-scu.c
+@@ -482,22 +482,26 @@ void ast_scu_security_info(void)
+ 	}
+ }
+ 
+-void ast_scu_sys_rest_info(void)
++u32 ast_scu_sys_rest_info(void)
+ {
+ 	u32 rest = ast_scu_read(AST_SCU_SYS_CTRL);
+ 
+ 	if (rest & SCU_SYS_EXT_RESET_FLAG) {
+ 		printf("RST : External\n");
+ 		ast_scu_write(SCU_SYS_EXT_RESET_FLAG, AST_SCU_SYS_CTRL);
++		rest = SCU_SYS_EXT_RESET_FLAG;
+ 	} else if (rest & SCU_SYS_WDT_RESET_FLAG) {
+ 		printf("RST : Watchdog\n");
+ 		ast_scu_write(SCU_SYS_WDT_RESET_FLAG, AST_SCU_SYS_CTRL);
++		rest = SCU_SYS_WDT_RESET_FLAG;
+ 	} else if (rest & SCU_SYS_PWR_RESET_FLAG) {
+ 		printf("RST : Power On\n");
+ 		ast_scu_write(SCU_SYS_PWR_RESET_FLAG, AST_SCU_SYS_CTRL);
++		rest = SCU_SYS_PWR_RESET_FLAG;
+ 	} else {
+ 		printf("RST : CLK en\n");
+ 	}
++	return rest;
+ }
+ 
+ u32 ast_scu_get_vga_memsize(void)
+-- 
+2.25.4
+
diff --git a/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0003-aspeed-add-gpio-support.patch b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0003-aspeed-add-gpio-support.patch
new file mode 100644
index 0000000..2e825c8
--- /dev/null
+++ b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0003-aspeed-add-gpio-support.patch
@@ -0,0 +1,512 @@
+From 9bb68d8820480519e8b331f7a8b866b8718ad7fd Mon Sep 17 00:00:00 2001
+From: Alexander Filippov <a.filippov@yadro.com>
+Date: Tue, 19 May 2020 18:55:41 +0300
+Subject: [PATCH] aspeed: add gpio support
+
+This is an initial support for the parallel GPIO pins directly connected
+to the AHB on the Aspeed 2400/2500.
+
+This brings the functions and a shell command to manipulate the GPIO
+state. The GPIO value reading and writing work in non interrupt mode
+only.
+
+Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
+---
+ arch/arm/include/asm/arch-aspeed/gpio.h     |  65 ++++
+ arch/arm/include/asm/arch-aspeed/platform.h |   1 +
+ drivers/gpio/Makefile                       |   2 +
+ drivers/gpio/aspeed_gpio.c                  | 386 ++++++++++++++++++++
+ 4 files changed, 454 insertions(+)
+ create mode 100644 arch/arm/include/asm/arch-aspeed/gpio.h
+ create mode 100644 drivers/gpio/aspeed_gpio.c
+
+diff --git a/arch/arm/include/asm/arch-aspeed/gpio.h b/arch/arm/include/asm/arch-aspeed/gpio.h
+new file mode 100644
+index 0000000000..c63987e917
+--- /dev/null
++++ b/arch/arm/include/asm/arch-aspeed/gpio.h
+@@ -0,0 +1,65 @@
++/*
++ * SPDX-License-Identifier: GPL-2.0+
++ * Copyright (C) 2020 YADRO.
++ */
++#ifndef _ASPEED_GPIO_H
++#define _ASPEED_GPIO_H
++
++#define ASPEED_GPIO_PORT_A 0
++#define ASPEED_GPIO_PORT_B 1
++#define ASPEED_GPIO_PORT_C 2
++#define ASPEED_GPIO_PORT_D 3
++#define ASPEED_GPIO_PORT_E 4
++#define ASPEED_GPIO_PORT_F 5
++#define ASPEED_GPIO_PORT_G 6
++#define ASPEED_GPIO_PORT_H 7
++#define ASPEED_GPIO_PORT_I 8
++#define ASPEED_GPIO_PORT_J 9
++#define ASPEED_GPIO_PORT_K 10
++#define ASPEED_GPIO_PORT_L 11
++#define ASPEED_GPIO_PORT_M 12
++#define ASPEED_GPIO_PORT_N 13
++#define ASPEED_GPIO_PORT_O 14
++#define ASPEED_GPIO_PORT_P 15
++#define ASPEED_GPIO_PORT_Q 16
++#define ASPEED_GPIO_PORT_R 17
++#define ASPEED_GPIO_PORT_S 18
++#define ASPEED_GPIO_PORT_T 19
++#define ASPEED_GPIO_PORT_U 20
++#define ASPEED_GPIO_PORT_V 21
++#define ASPEED_GPIO_PORT_W 22
++#define ASPEED_GPIO_PORT_X 23
++#define ASPEED_GPIO_PORT_Y 24
++#define ASPEED_GPIO_PORT_Z 25
++#define ASPEED_GPIO_PORT_AA 26
++#define ASPEED_GPIO_PORT_AB 27
++#define ASPEED_GPIO_PORT_AC 28
++
++#define ASPEED_GPIO_PORT_SHIFT 3
++#define ASPEED_GPIO_PIN_MASK 0x7
++#define ASPEED_GPIO(port, pin)                                                 \
++    ((ASPEED_GPIO_PORT_##port << ASPEED_GPIO_PORT_SHIFT) |                     \
++     (pin & ASPEED_GPIO_PIN_MASK))
++
++/* Direction values */
++#define ASPEED_GPIO_INPUT 0
++#define ASPEED_GPIO_OUTPUT 1
++
++/* Trigger values */
++#define ASPEED_GPIO_FALLING_EDGE 0
++#define ASPEED_GPIO_RISING_EDGE 1
++#define ASPEED_GPIO_LOW_LEVEL 2
++#define ASPEED_GPIO_HIGH_LEVEL 3
++#define ASPEED_GPIO_DUAL_EDGE 4
++
++/* Debounce values */
++#define ASPEED_GPIO_DEBOUNCE_NONE 0
++#define ASPEED_GPIO_DEBOUNCE_1 1
++#define ASPEED_GPIO_DEBOUNCE_2 2
++#define ASPEED_GPIO_DEBOUNCE_3 3
++
++#define gpio_status() gpio_info()
++
++extern void gpio_info(void);
++
++#endif /* #ifndef _ASPEED_GPIO_H */
+diff --git a/arch/arm/include/asm/arch-aspeed/platform.h b/arch/arm/include/asm/arch-aspeed/platform.h
+index b9207c492f..0a05a7a7a0 100644
+--- a/arch/arm/include/asm/arch-aspeed/platform.h
++++ b/arch/arm/include/asm/arch-aspeed/platform.h
+@@ -32,5 +32,6 @@
+ #endif
+ 
+ #define CONFIG_BOARD_LATE_INIT 1 /* Call board_late_init */
++#define CONFIG_CMD_GPIO 1        /* Enable gpio command in shell */
+ 
+ #endif
+diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
+index 792d19186a..5f043e07ce 100644
+--- a/drivers/gpio/Makefile
++++ b/drivers/gpio/Makefile
+@@ -14,6 +14,8 @@ obj-$(CONFIG_DM_GPIO)		+= gpio-uclass.o
+ obj-$(CONFIG_DM_PCA953X)	+= pca953x_gpio.o
+ obj-$(CONFIG_DM_74X164)		+= 74x164_gpio.o
+ 
++obj-$(CONFIG_ARCH_AST2400)	+= aspeed_gpio.o
++obj-$(CONFIG_ARCH_AST2500)	+= aspeed_gpio.o
+ obj-$(CONFIG_AT91_GPIO)	+= at91_gpio.o
+ obj-$(CONFIG_ATMEL_PIO4)	+= atmel_pio4.o
+ obj-$(CONFIG_INTEL_ICH6_GPIO)	+= intel_ich6_gpio.o
+diff --git a/drivers/gpio/aspeed_gpio.c b/drivers/gpio/aspeed_gpio.c
+new file mode 100644
+index 0000000000..dc07f5a520
+--- /dev/null
++++ b/drivers/gpio/aspeed_gpio.c
+@@ -0,0 +1,386 @@
++/*
++ * SPDX-License-Identifier: GPL-2.0+
++ * Copyright (C) 2020 YADRO.
++ */
++
++#include <common.h>
++
++#include <asm/arch/gpio.h>
++#include <asm/arch/platform.h>
++#include <asm/io.h>
++#include <linux/ctype.h>
++
++typedef struct _ast_gpio_regs
++{
++    uint32_t base;     /* data and direction registers */
++    uint32_t intcfg;   /* interrupt config */
++    uint32_t debounce; /* debounce config */
++    uint32_t cmdsrc;   /* command source config */
++    uint32_t data;     /* data read register */
++} ast_gpio_regs_t;
++
++static ast_gpio_regs_t ast_gpio_regs[] = {
++    /* A/B/C/D */
++    {AST_GPIO_BASE + 0x0000, AST_GPIO_BASE + 0x0008, AST_GPIO_BASE + 0x0040,
++     AST_GPIO_BASE + 0x0060, AST_GPIO_BASE + 0x00C0},
++    /* E/F/G/H */
++    {AST_GPIO_BASE + 0x0020, AST_GPIO_BASE + 0x0028, AST_GPIO_BASE + 0x0048,
++     AST_GPIO_BASE + 0x0068, AST_GPIO_BASE + 0x00C4},
++    /* I/J/K/L */
++    {AST_GPIO_BASE + 0x0070, AST_GPIO_BASE + 0x0098, AST_GPIO_BASE + 0x00B0,
++     AST_GPIO_BASE + 0x0090, AST_GPIO_BASE + 0x00C8},
++    /* M/N/O/P */
++    {AST_GPIO_BASE + 0x0078, AST_GPIO_BASE + 0x00E8, AST_GPIO_BASE + 0x0100,
++     AST_GPIO_BASE + 0x00E0, AST_GPIO_BASE + 0x00CC},
++    /* Q/R/S/T */
++    {AST_GPIO_BASE + 0x0080, AST_GPIO_BASE + 0x0118, AST_GPIO_BASE + 0x0130,
++     AST_GPIO_BASE + 0x0110, AST_GPIO_BASE + 0x00D0},
++    /* U/V/W/X */
++    {AST_GPIO_BASE + 0x0088, AST_GPIO_BASE + 0x0148, AST_GPIO_BASE + 0x0160,
++     AST_GPIO_BASE + 0x0140, AST_GPIO_BASE + 0x00D4},
++    /* Y/Z/AA/AB */
++    {AST_GPIO_BASE + 0x01E0, AST_GPIO_BASE + 0x0178, AST_GPIO_BASE + 0x0190,
++     AST_GPIO_BASE + 0x0170, AST_GPIO_BASE + 0x00D8},
++    /* AC */
++    {AST_GPIO_BASE + 0x01E8, AST_GPIO_BASE + 0x01A8, AST_GPIO_BASE + 0x01C0,
++     AST_GPIO_BASE + 0x01A0, AST_GPIO_BASE + 0x00DC},
++};
++
++#define AST_GPIO_PINS_PER_PORT 8
++#define AST_GPIO_PORTS_PER_REGISTER 4
++
++#define AST_GPIO_PORT(gpio) (gpio >> ASPEED_GPIO_PORT_SHIFT)
++#define AST_GPIO_PIN(gpio) (gpio & ASPEED_GPIO_PIN_MASK)
++#define AST_GPIO_SHIFT(gpio)                                                   \
++    ((AST_GPIO_PORT(gpio) % AST_GPIO_PORTS_PER_REGISTER) *                     \
++         AST_GPIO_PINS_PER_PORT +                                              \
++     AST_GPIO_PIN(gpio))
++
++#define AST_GPIO_REG_INDEX(gpio)                                               \
++    (AST_GPIO_PORT(gpio) / AST_GPIO_PORTS_PER_REGISTER)
++
++/**
++ * @return Pointer to corresponding item from ast_gpio_regs table.
++ */
++#define AST_GPIO_REGS(gpio)                                                    \
++    ((AST_GPIO_REG_INDEX(gpio) < ARRAY_SIZE(ast_gpio_regs))                    \
++         ? (ast_gpio_regs + AST_GPIO_REG_INDEX(gpio))                          \
++         : NULL)
++
++/**
++ * @brief Set a corresponding bit in specified register.
++ *
++ * @param val - Required bit value
++ * @param base - Register address
++ * @param shift - Bit index.
++ */
++#define AST_GPIO_WRITE(val, base, shift)                                       \
++    writel(((val) ? readl(base) | (1 << (shift))                               \
++                  : readl(base) & ~(1 << (shift))),                            \
++           base)
++
++/**
++ * @brief Get value of corresponging bit from specified register.
++ *
++ * @param base - Register address
++ * @param shift - Bit index
++ *
++ * @return Bit value
++ */
++#define AST_GPIO_READ(base, shift) ((readl(base) >> (shift)) & 1)
++
++#define IS_VALID_GPIO(gpio)                                                    \
++    ((gpio) >= ASPEED_GPIO(A, 0) && (gpio) <= ASPEED_GPIO(AC, 7))
++
++#define AST_GPIO_DIRECTION 0x04
++#define AST_GPIO_INT_SENS0 0x04
++#define AST_GPIO_INT_SENS1 0x08
++#define AST_GPIO_INT_SENS2 0x0C
++#define AST_GPIO_INT_STATUS 0x10
++#define AST_GPIO_DEBOUNCE0 0x00
++#define AST_GPIO_DEBOUNCE1 0x04
++#define AST_GPIO_CMD_SRC0 0x00
++#define AST_GPIO_CMD_SRC1 0x04
++
++/**
++ * @brief Set a GPIO direction
++ *
++ * @param gpio      GPIO line
++ * @param direction GPIO direction (0 for input or 1 for output)
++ *
++ * @return 0 if ok, -1 on error
++ */
++static int ast_gpio_set_direction(unsigned gpio, unsigned direction)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (!regs)
++    {
++        printf("%s: Invalid GPIO!\n", __func__);
++        return -1;
++    }
++
++    AST_GPIO_WRITE(direction, regs->base + AST_GPIO_DIRECTION,
++                   AST_GPIO_SHIFT(gpio));
++    return 0;
++}
++
++/**
++ * The 6 following functions are generic u-boot gpio implementation.
++ * They are declared in `include/asm-generic/gpio.h`
++ */
++
++int gpio_request(unsigned gpio, const char *label)
++{
++    return (IS_VALID_GPIO(gpio) ? 0 : -1);
++}
++
++int gpio_free(unsigned gpio)
++{
++    return (IS_VALID_GPIO(gpio) ? 0 : -1);
++}
++
++int gpio_get_value(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (!regs)
++    {
++        printf("%s: Invalid GPIO!\n", __func__);
++        return -1;
++    }
++
++    return AST_GPIO_READ(regs->base, AST_GPIO_SHIFT(gpio));
++}
++
++int gpio_set_value(unsigned gpio, int value)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (!regs)
++    {
++        printf("%s: Invalid GPIO!\n", __func__);
++        return -1;
++    }
++
++    AST_GPIO_WRITE(value, regs->base, AST_GPIO_SHIFT(gpio));
++    return 0;
++}
++
++int gpio_direction_input(unsigned gpio)
++{
++    return ast_gpio_set_direction(gpio, ASPEED_GPIO_INPUT);
++}
++
++int gpio_direction_output(unsigned gpio, int value)
++{
++    int rc = ast_gpio_set_direction(gpio, ASPEED_GPIO_OUTPUT);
++    return (rc == 0 ? gpio_set_value(gpio, value) : rc);
++}
++
++/**
++ * @brief Convert a string to GPIO line. Used by `do_gpio()` from `cmd/gpio.c`
++ *
++ * @param str a GPIO name or line number
++ *
++ * @return GPIO line if ok, -1 on error
++ */
++int name_to_gpio(const char *str)
++{
++    int gpio = -1;
++
++    if (str)
++    {
++        if (isalpha(*str))
++        {
++            gpio = (toupper(*str) - 'A') << ASPEED_GPIO_PORT_SHIFT;
++
++            if (toupper(*str) == 'A' && toupper(*(str + 1)) >= 'A' &&
++                toupper(*(str + 1)) <= 'C')
++            {
++                str++;
++                gpio = (ASPEED_GPIO_PORT_AA + toupper(*str) - 'A')
++                       << ASPEED_GPIO_PORT_SHIFT;
++            }
++
++            str++;
++            if (*str >= '0' && *str <= '7' && !*(str + 1))
++            {
++                gpio += *str - '0';
++            }
++            else
++            {
++                gpio = -1;
++            }
++        }
++        else if (isdigit(*str))
++        {
++            gpio = simple_strtoul(str, NULL, 0);
++        }
++    }
++
++    return gpio;
++}
++
++/**
++ * @return A GPIO direction in human readable format.
++ */
++static const char *ast_gpio_direction(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (regs)
++    {
++        int direction = AST_GPIO_READ(regs->base + AST_GPIO_DIRECTION,
++                                      AST_GPIO_SHIFT(gpio));
++        switch (direction)
++        {
++            case ASPEED_GPIO_INPUT:
++                return "input";
++            case ASPEED_GPIO_OUTPUT:
++                return "output";
++            default:
++                break;
++        }
++    }
++    return "error";
++}
++
++/**
++ * @return An interrupt trigger settings in human readable format.
++ */
++static const char *ast_gpio_trigger(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (regs)
++    {
++        unsigned shift = AST_GPIO_SHIFT(gpio);
++        unsigned trigger =
++            (AST_GPIO_READ(regs->intcfg + AST_GPIO_INT_SENS0, shift) << 0) |
++            (AST_GPIO_READ(regs->intcfg + AST_GPIO_INT_SENS1, shift) << 1) |
++            (AST_GPIO_READ(regs->intcfg + AST_GPIO_INT_SENS2, shift) << 2);
++
++        switch (trigger)
++        {
++            case ASPEED_GPIO_FALLING_EDGE:
++                return "fall";
++            case ASPEED_GPIO_RISING_EDGE:
++                return "rise";
++            case ASPEED_GPIO_LOW_LEVEL:
++                return "low ";
++            case ASPEED_GPIO_HIGH_LEVEL:
++                return "high";
++            default:
++                return "both";
++        }
++    }
++    return "error";
++}
++
++/**
++ * @return An interrupt status in human readable format.
++ */
++static const char *ast_gpio_int_status(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (regs)
++    {
++        unsigned shift = AST_GPIO_SHIFT(gpio);
++        if (AST_GPIO_READ(regs->intcfg, shift))
++        {
++            return AST_GPIO_READ(regs->intcfg + AST_GPIO_INT_STATUS, shift)
++                       ? "pending"
++                       : "cleaned";
++        }
++        return "disabled";
++    }
++
++    return "error";
++}
++
++/**
++ * @return A debounce value in human readable format.
++ */
++static const char *ast_gpio_debounce(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (regs)
++    {
++        unsigned shift = AST_GPIO_SHIFT(gpio);
++        unsigned debounce =
++            (AST_GPIO_READ(regs->debounce + AST_GPIO_DEBOUNCE0, shift) << 0) |
++            (AST_GPIO_READ(regs->debounce + AST_GPIO_DEBOUNCE1, shift) << 1);
++        switch (debounce)
++        {
++            case ASPEED_GPIO_DEBOUNCE_NONE:
++                return "none";
++            case ASPEED_GPIO_DEBOUNCE_1:
++                return "timer1";
++            case ASPEED_GPIO_DEBOUNCE_2:
++                return "timer2";
++            case ASPEED_GPIO_DEBOUNCE_3:
++                return "timer3";
++            default:
++                break;
++        }
++    }
++
++    return "error";
++}
++
++/**
++ * @return A command source value in human readable format.
++ */
++static const char *ast_gpio_command_source(unsigned gpio)
++{
++    ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++    if (regs)
++    {
++        /* Used one bit per gpio port */
++        unsigned shift = AST_GPIO_SHIFT(gpio) - AST_GPIO_PIN(gpio);
++        unsigned cmdsrc =
++            (AST_GPIO_READ(regs->cmdsrc + AST_GPIO_CMD_SRC0, shift) << 0) |
++            (AST_GPIO_READ(regs->cmdsrc + AST_GPIO_CMD_SRC1, shift) << 1);
++
++        switch (cmdsrc)
++        {
++            /* The single place where these values are used is here. */
++            case 0x0:
++                return "ARM";
++            case 0x1:
++                return "LPC";
++            case 0x2:
++                return "CoCPU";
++            default:
++                return "Unknown";
++        }
++    }
++
++    return "error";
++}
++
++/**
++ * @brief Show all GPIO pins statuses. Used by `do_gpio()` in `cmd/gpio.c`
++ */
++void gpio_info(void)
++{
++    unsigned first = ASPEED_GPIO(A, 0);
++    unsigned last = ASPEED_GPIO(AC, 7);
++    for (unsigned gpio = first; gpio <= last; gpio++)
++    {
++        unsigned port = AST_GPIO_PORT(gpio);
++        unsigned pin = AST_GPIO_PIN(gpio);
++        unsigned shift = AST_GPIO_SHIFT(gpio);
++        ast_gpio_regs_t *regs = AST_GPIO_REGS(gpio);
++        if (!regs)
++        {
++            printf("gpio %u is invalid!\n", gpio);
++            continue;
++        }
++
++        printf("gpio %c%c%c line %3d: %s, int: %s, %s, deb: %s, src: %s, "
++               "val: %d/%d\n",
++               (port >= ASPEED_GPIO_PORT_AA ? 'A' : ' '),
++               ('A' + port % ASPEED_GPIO_PORT_AA), ('0' + pin), gpio,
++               ast_gpio_direction(gpio), ast_gpio_trigger(gpio),
++               ast_gpio_int_status(gpio), ast_gpio_debounce(gpio),
++               ast_gpio_command_source(gpio), gpio_get_value(gpio),
++               AST_GPIO_READ(regs->data, shift));
++    }
++}
+-- 
+2.25.4
+
diff --git a/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0004-aspeed-add-bmc-position-support.patch b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0004-aspeed-add-bmc-position-support.patch
new file mode 100644
index 0000000..423092b
--- /dev/null
+++ b/meta-yadro/meta-nicole/recipes-bsp/u-boot/files/0004-aspeed-add-bmc-position-support.patch
@@ -0,0 +1,61 @@
+From 8098310be5887bd5e80830f105b63ed59dc10421 Mon Sep 17 00:00:00 2001
+From: Alexander Filippov <a.filippov@yadro.com>
+Date: Fri, 22 May 2020 11:45:31 +0300
+Subject: [PATCH] aspeed: add bmc position support
+
+There are two Nicoles in one chassis in the Tatlin hardware.
+The position is encoded by a pin, read by an MCU, and then translated
+to the BMC via GPIO pin GPIOE1.
+
+This reads the GPIO pin state and put is as a bootargs item.
+
+Signed-off-by: Alexander Filippov <a.filippov@yadro.com>
+---
+ arch/arm/mach-aspeed/ast-late-init.c | 21 +++++++++++++++++++++
+ 1 file changed, 21 insertions(+)
+
+diff --git a/arch/arm/mach-aspeed/ast-late-init.c b/arch/arm/mach-aspeed/ast-late-init.c
+index 5646c0e882..7d36dc0d8a 100644
+--- a/arch/arm/mach-aspeed/ast-late-init.c
++++ b/arch/arm/mach-aspeed/ast-late-init.c
+@@ -7,6 +7,7 @@
+ 
+ #include <asm/arch/ast_scu.h>
+ #include <asm/arch/regs-scu.h>
++#include <asm/gpio.h>
+ #include <malloc.h>
+ 
+ static void update_bootargs_cmd(const char *key, const char *value)
+@@ -106,9 +107,29 @@ static void set_reset_reason(void)
+     }
+ }
+ 
++static void set_bmc_position(void)
++{
++    unsigned gpio = ASPEED_GPIO(E, 1);
++
++    /* Init GPIO */
++    if (gpio_request(gpio, "bmcposition") == 0 &&
++        gpio_direction_input(gpio) == 0)
++    {
++        int position = gpio_get_value(gpio);
++        if (position >= 0)
++        {
++            printf("BMC Position: %d\n", position);
++            update_bootargs_cmd("bmcposition", position ? "1" : "0");
++        }
++    }
++
++    gpio_free(gpio);
++}
++
+ int board_late_init(void)
+ {
+     set_reset_reason();
++    set_bmc_position();
+ 
+     return 0;
+ }
+-- 
+2.25.4
+
diff --git a/meta-yadro/meta-nicole/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend b/meta-yadro/meta-nicole/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend
index bf7802a..1390922 100644
--- a/meta-yadro/meta-nicole/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend
+++ b/meta-yadro/meta-nicole/recipes-bsp/u-boot/u-boot-aspeed_%.bbappend
@@ -3,4 +3,6 @@
 SRC_URI_append = " \
     file://0001-Add-system-reset-status-support.patch \
     file://0002-config-ast-common-set-fieldmode-to-true.patch \
+    file://0003-aspeed-add-gpio-support.patch \
+    file://0004-aspeed-add-bmc-position-support.patch \
     "