Files
BPI-CM9-Linux/build/patch/u-boot/v2025.10/board_khadas-edge2/0001-khadas-edge2-add-support-for-KBI-and-apply-some-patc.patch

909 lines
22 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Muhammed Efe Cetin <[email protected]>
Date: Wed, 22 Oct 2025 12:44:49 +0200
Subject: khadas-edge2: add support for KBI and apply some patches from BSP
uboot
---
arch/arm/dts/rk3588-khadas-edge2-u-boot.dtsi | 3 +
board/khadas/khadas-edge2-rk3588s/Makefile | 3 +
board/khadas/khadas-edge2-rk3588s/khadas-edge2-rk3588s.c | 95 ++
cmd/Kconfig | 6 +
cmd/Makefile | 1 +
cmd/kbi.c | 710 ++++++++++
configs/khadas-edge2-rk3588s_defconfig | 6 +
7 files changed, 824 insertions(+)
diff --git a/arch/arm/dts/rk3588-khadas-edge2-u-boot.dtsi b/arch/arm/dts/rk3588-khadas-edge2-u-boot.dtsi
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/arch/arm/dts/rk3588-khadas-edge2-u-boot.dtsi
@@ -0,0 +1,3 @@
+// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
+
+#include "rk3588s-u-boot.dtsi"
\ No newline at end of file
diff --git a/board/khadas/khadas-edge2-rk3588s/Makefile b/board/khadas/khadas-edge2-rk3588s/Makefile
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/board/khadas/khadas-edge2-rk3588s/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-y += khadas-edge2-rk3588s.o
diff --git a/board/khadas/khadas-edge2-rk3588s/khadas-edge2-rk3588s.c b/board/khadas/khadas-edge2-rk3588s/khadas-edge2-rk3588s.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/board/khadas/khadas-edge2-rk3588s/khadas-edge2-rk3588s.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022 Wesion Technology Co., Ltd
+ */
+
+#include <dm.h>
+#include <adc.h>
+#include <env.h>
+#include <i2c.h>
+#include <dm/uclass.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <log.h>
+#include <stdlib.h>
+#include <power/regulator.h>
+#include <command.h>
+
+#define HW_VERSION_ADC_VALUE_TOLERANCE 0x28
+#define HW_VERSION_ADC_VAL_EDGE2_V11 0x2b3
+#define HW_VERSION_ADC_VAL_EDGE2_V12 0x56d
+#define HW_VERSION_ADC_VAL_EDGE2_V13 0x807
+
+static int set_hw_version(void)
+{
+ unsigned int val = 0;
+ int ret;
+ struct udevice *dev;
+ int current_channel = 5;
+
+ ret = uclass_get_device_by_name(UCLASS_ADC, "adc@fec10000", &dev);
+ if (ret) {
+ printf("%s uclass_get_device_by_name fail! ret=%d\n", __func__, ret);
+ return 0;
+ }
+
+ udelay(100);
+
+ ret = adc_start_channel(dev, current_channel);
+ if (ret) {
+ printf("%s adc_start_channel fail! ret=%d\n", __func__, ret);
+ return 0;
+ }
+
+ ret = adc_channel_data(dev, current_channel, &val);
+ if (ret) {
+ printf("%s adc_channel_data fail! ret=%d\n", __func__, ret);
+ return 0;
+ }
+
+ if ((val >= HW_VERSION_ADC_VAL_EDGE2_V11 - HW_VERSION_ADC_VALUE_TOLERANCE) &&
+ (val <= HW_VERSION_ADC_VAL_EDGE2_V11 + HW_VERSION_ADC_VALUE_TOLERANCE)) {
+ env_set("hwver", "EDGE2.V11");
+ } else if ((val >= HW_VERSION_ADC_VAL_EDGE2_V12 - HW_VERSION_ADC_VALUE_TOLERANCE) &&
+ (val <= HW_VERSION_ADC_VAL_EDGE2_V12 + HW_VERSION_ADC_VALUE_TOLERANCE)) {
+ env_set("hwver", "EDGE2.V12");
+ } else if ((val >= HW_VERSION_ADC_VAL_EDGE2_V13 - HW_VERSION_ADC_VALUE_TOLERANCE) &&
+ (val <= HW_VERSION_ADC_VAL_EDGE2_V13 + HW_VERSION_ADC_VALUE_TOLERANCE)) {
+ env_set("hwver", "EDGE2.V13");
+ } else {
+ env_set("hwver", "Unknown");
+ }
+
+ return 0;
+}
+
+int rk_board_late_init(void)
+{
+ unsigned int val;
+ u8 linebuf[1];
+
+ /* Set FAN test */
+ run_command("i2c dev 2; i2c mw 18 96 1", 0);
+
+ /* Set Green LED on */
+ run_command("gpio set 138; gpio clear 139; gpio clear 140", 0);
+
+ /* GPIO4_A2 vcc 5v */
+ run_command("gpio set 130", 0);
+
+ /* Enable Pogo power */
+ int ret = adc_channel_single_shot("adc@fec10000", 2, &val);
+ if (ret) {
+ printf("%s adc_channel_single_shot fail! ret=%d\n", __func__, ret);
+ return 0;
+ }
+
+ if (!ret && val < 50) {
+ run_command("gpio set 105", 1); /* pogo_power_enable */
+ }
+
+ set_hw_version();
+
+ return 0;
+}
\ No newline at end of file
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 111111111111..222222222222 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -190,6 +190,12 @@ config CMD_FWU_METADATA
help
Command to read the metadata and dump it's contents
+config CMD_KHADAS_KBI
+ bool "khadas_kbi"
+ default y
+ help
+ Command to show KBI instructions.
+
config CMD_HELP
bool "help"
default y
diff --git a/cmd/Makefile b/cmd/Makefile
index 111111111111..222222222222 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -12,6 +12,7 @@ obj-y += panic.o
obj-y += version.o
# command
+obj-$(CONFIG_CMD_KHADAS_KBI) += kbi.o
obj-$(CONFIG_CMD_ARMFFA) += armffa.o
obj-$(CONFIG_CMD_2048) += 2048.o
obj-$(CONFIG_CMD_ACPI) += acpi.o
diff --git a/cmd/kbi.c b/cmd/kbi.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/cmd/kbi.c
@@ -0,0 +1,710 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * KBI (Khadas Bootloader Instructions) driver
+ *
+ * Copyright (C) 2025 Khadas
+ */
+
+#include <dm.h>
+#include <dm/uclass.h>
+#include <adc.h>
+#include <env.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <log.h>
+#include <command.h>
+
+/* MCU I2C Configuration */
+#define KBI_MCU_ADDR 0x18
+#define KBI_I2C_BUS_NUM 2
+
+/* Register Map */
+#define REG_PASSWD_VENDOR 0x00
+#define REG_USID 0x06
+#define REG_VERSION 0x12
+
+#define REG_BOOT_MODE 0x20
+#define REG_BOOT_EN_DCIN 0x21
+#define REG_BOOT_EN_RTC 0x22
+#define REG_LED_ON 0x23
+#define REG_LED_OFF 0x24
+#define REG_LED_RGB_ON_R 0x25
+#define REG_LED_RGB_ON_G 0x26
+#define REG_LED_RGB_ON_B 0x27
+#define REG_LED_RGB_OFF_R 0x28
+#define REG_LED_RGB_OFF_G 0x29
+#define REG_LED_RGB_OFF_B 0x2A
+#define REG_REST_CONF 0x2C
+#define REG_SLEEP_EN 0x2E
+
+#define REG_PWD_WRITE 0x81
+#define REG_PWD_DATA 0x82
+#define REG_LED_ON_RAM 0x89
+#define REG_FAN_CTRL 0x8A
+#define REG_WDT_EN 0x8B
+
+#define REG_SYS_RST 0x91
+#define REG_BOOT_FLAG 0x92
+#define REG_FAN_TEST 0x96
+
+/* Configuration Constants */
+#define VERSION_LENGTH 2
+#define USID_LENGTH 7
+
+#define HW_VERSION_ADC_CHANNEL 5
+#define HW_VERSION_ADC_TOLERANCE 0x28
+#define HW_VERSION_ADC_VAL_V11 0x2b3
+#define HW_VERSION_ADC_VAL_V12 0x56d
+#define HW_VERSION_ADC_VAL_V13 0x807
+
+/* LED Modes */
+enum led_mode {
+ LED_MODE_OFF = 0,
+ LED_MODE_ON,
+ LED_MODE_R_BREATHE,
+ LED_MODE_G_BREATHE,
+ LED_MODE_B_BREATHE,
+ LED_MODE_RG_BREATHE,
+ LED_MODE_RB_BREATHE,
+ LED_MODE_GB_BREATHE,
+ LED_MODE_RGB_BREATHE,
+ LED_MODE_R_HEARTBEAT,
+ LED_MODE_G_HEARTBEAT,
+ LED_MODE_B_HEARTBEAT,
+ LED_MODE_RG_HEARTBEAT,
+ LED_MODE_RB_HEARTBEAT,
+ LED_MODE_GB_HEARTBEAT,
+ LED_MODE_RGB_HEARTBEAT,
+ LED_MODE_MAX
+};
+
+enum led_system_state {
+ LED_SYSTEM_OFF = 0,
+ LED_SYSTEM_ON
+};
+
+enum boot_trigger {
+ BOOT_TRIGGER_RTC = 1,
+ BOOT_TRIGGER_DCIN
+};
+
+/* LED mode string mapping */
+static const char * const led_mode_str[] = {
+ [LED_MODE_OFF] = "off",
+ [LED_MODE_ON] = "on",
+ [LED_MODE_R_BREATHE] = "r_breathe",
+ [LED_MODE_G_BREATHE] = "g_breathe",
+ [LED_MODE_B_BREATHE] = "b_breathe",
+ [LED_MODE_RG_BREATHE] = "rg_breathe",
+ [LED_MODE_RB_BREATHE] = "rb_breathe",
+ [LED_MODE_GB_BREATHE] = "gb_breathe",
+ [LED_MODE_RGB_BREATHE] = "rgb_breathe",
+ [LED_MODE_R_HEARTBEAT] = "r_heartbeat",
+ [LED_MODE_G_HEARTBEAT] = "g_heartbeat",
+ [LED_MODE_B_HEARTBEAT] = "b_heartbeat",
+ [LED_MODE_RG_HEARTBEAT] = "rg_heartbeat",
+ [LED_MODE_RB_HEARTBEAT] = "rb_heartbeat",
+ [LED_MODE_GB_HEARTBEAT] = "gb_heartbeat",
+ [LED_MODE_RGB_HEARTBEAT] = "rgb_heartbeat",
+};
+
+/**
+ * struct kbi_mcu - KBI MCU instance data
+ * @i2c_dev: I2C device for MCU communication
+ */
+struct kbi_mcu {
+ struct udevice *i2c_dev;
+};
+
+static struct kbi_mcu g_kbi;
+
+/**
+ * kbi_i2c_init() - Initialize I2C communication with MCU
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_i2c_init(void)
+{
+ struct udevice *bus;
+ int ret;
+
+ if (g_kbi.i2c_dev)
+ return 0;
+
+ ret = uclass_get_device_by_seq(UCLASS_I2C, KBI_I2C_BUS_NUM, &bus);
+ if (ret) {
+ printf("KBI: Failed to get I2C bus %d: %d\n",
+ KBI_I2C_BUS_NUM, ret);
+ return ret;
+ }
+
+ ret = i2c_get_chip(bus, KBI_MCU_ADDR, 1, &g_kbi.i2c_dev);
+ if (ret) {
+ printf("KBI: Failed to get I2C chip 0x%x: %d\n",
+ KBI_MCU_ADDR, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * kbi_i2c_read_reg() - Read a single register from MCU
+ * @reg: Register address
+ * @val: Pointer to store read value
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_i2c_read_reg(u8 reg, u8 *val)
+{
+ int ret;
+
+ ret = kbi_i2c_init();
+ if (ret)
+ return ret;
+
+ ret = dm_i2c_read(g_kbi.i2c_dev, reg, val, 1);
+ if (ret) {
+ printf("KBI: Failed to read reg 0x%02x: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * kbi_i2c_write_reg() - Write a single register to MCU
+ * @reg: Register address
+ * @val: Value to write
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_i2c_write_reg(u8 reg, u8 val)
+{
+ int ret;
+
+ ret = kbi_i2c_init();
+ if (ret)
+ return ret;
+
+ ret = dm_i2c_write(g_kbi.i2c_dev, reg, &val, 1);
+ if (ret) {
+ printf("KBI: Failed to write reg 0x%02x: %d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * kbi_i2c_read_block() - Read multiple consecutive registers
+ * @start_reg: Starting register address
+ * @buf: Buffer to store read data
+ * @len: Number of bytes to read
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_i2c_read_block(u8 start_reg, u8 *buf, size_t len)
+{
+ int ret;
+
+ if (!buf || len == 0)
+ return -EINVAL;
+
+ ret = kbi_i2c_init();
+ if (ret)
+ return ret;
+
+ ret = dm_i2c_read(g_kbi.i2c_dev, start_reg, buf, len);
+ if (ret) {
+ printf("KBI: Failed to read block at 0x%02x: %d\n",
+ start_reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * kbi_get_version() - Read and display MCU firmware version
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_get_version(void)
+{
+ u8 version[VERSION_LENGTH];
+ int ret;
+ int i;
+
+ ret = kbi_i2c_read_block(REG_VERSION, version, VERSION_LENGTH);
+ if (ret)
+ return ret;
+
+ printf("MCU firmware version: ");
+ for (i = 0; i < VERSION_LENGTH; i++)
+ printf("%02x ", version[i]);
+ printf("\n");
+
+ return 0;
+}
+
+/**
+ * kbi_get_usid() - Read USID and store in environment
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_get_usid(void)
+{
+ u8 usid[USID_LENGTH];
+ char serial[USID_LENGTH * 2 + 1];
+ int ret;
+ int i;
+
+ ret = kbi_i2c_read_block(REG_USID, usid, USID_LENGTH);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < USID_LENGTH; i++)
+ sprintf(serial + i * 2, "%02X", usid[i]);
+
+ printf("USID: %s\n", serial);
+ env_set("usid", serial);
+
+ return 0;
+}
+
+/**
+ * kbi_get_hw_version() - Detect hardware version via ADC
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_get_hw_version(void)
+{
+ struct udevice *dev;
+ unsigned int adc_val;
+ const char *hw_ver_str;
+ int ret;
+
+ ret = uclass_get_device_by_driver(UCLASS_ADC,
+ DM_DRIVER_GET(rockchip_saradc),
+ &dev);
+ if (ret) {
+ printf("KBI: Failed to get ADC device: %d\n", ret);
+ return ret;
+ }
+
+ ret = adc_channel_single_shot(dev->name, HW_VERSION_ADC_CHANNEL, &adc_val);
+ if (ret) {
+ printf("KBI: Failed to read ADC: %d\n", ret);
+ return ret;
+ }
+
+ printf("ADC channel %d value: 0x%x (%u)\n",
+ HW_VERSION_ADC_CHANNEL, adc_val, adc_val);
+
+ /* Determine hardware version based on ADC value */
+ if (adc_val >= HW_VERSION_ADC_VAL_V11 - HW_VERSION_ADC_TOLERANCE &&
+ adc_val <= HW_VERSION_ADC_VAL_V11 + HW_VERSION_ADC_TOLERANCE) {
+ hw_ver_str = "EDGE2.V11";
+ } else if (adc_val >= HW_VERSION_ADC_VAL_V12 - HW_VERSION_ADC_TOLERANCE &&
+ adc_val <= HW_VERSION_ADC_VAL_V12 + HW_VERSION_ADC_TOLERANCE) {
+ hw_ver_str = "EDGE2.V12";
+ } else if (adc_val >= HW_VERSION_ADC_VAL_V13 - HW_VERSION_ADC_TOLERANCE &&
+ adc_val <= HW_VERSION_ADC_VAL_V13 + HW_VERSION_ADC_TOLERANCE) {
+ hw_ver_str = "EDGE2.V13";
+ } else {
+ hw_ver_str = "Unknown";
+ }
+
+ printf("Hardware version: %s\n", hw_ver_str);
+ env_set("hwver", hw_ver_str);
+
+ return 0;
+}
+
+/**
+ * led_str_to_mode() - Convert LED mode string to enum value
+ * @str: LED mode string
+ *
+ * Return: LED mode enum value, or -EINVAL if invalid
+ */
+static int led_str_to_mode(const char *str)
+{
+ int i;
+
+ if (!str)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(led_mode_str); i++) {
+ if (strcmp(str, led_mode_str[i]) == 0)
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * kbi_get_led_mode() - Read and display current LED mode
+ * @state: LED_SYSTEM_OFF or LED_SYSTEM_ON
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_get_led_mode(enum led_system_state state)
+{
+ u8 mode, r, g, b;
+ u8 mode_reg, r_reg, g_reg, b_reg;
+ int ret;
+ const char *state_str;
+
+ if (state == LED_SYSTEM_OFF) {
+ mode_reg = REG_LED_OFF;
+ r_reg = REG_LED_RGB_OFF_R;
+ g_reg = REG_LED_RGB_OFF_G;
+ b_reg = REG_LED_RGB_OFF_B;
+ state_str = "systemoff";
+ } else {
+ mode_reg = REG_LED_ON;
+ r_reg = REG_LED_RGB_ON_R;
+ g_reg = REG_LED_RGB_ON_G;
+ b_reg = REG_LED_RGB_ON_B;
+ state_str = "systemon";
+ }
+
+ ret = kbi_i2c_read_reg(mode_reg, &mode);
+ if (ret)
+ return ret;
+
+ if (mode >= LED_MODE_MAX) {
+ printf("Invalid LED mode: 0x%02x\n", mode);
+ return -EINVAL;
+ }
+
+ printf("[%s] LED mode: %s", state_str, led_mode_str[mode]);
+
+ /* For static on/off modes, also display RGB values */
+ if (mode == LED_MODE_ON || mode == LED_MODE_OFF) {
+ ret = kbi_i2c_read_reg(r_reg, &r);
+ if (ret)
+ return ret;
+ ret = kbi_i2c_read_reg(g_reg, &g);
+ if (ret)
+ return ret;
+ ret = kbi_i2c_read_reg(b_reg, &b);
+ if (ret)
+ return ret;
+
+ printf(" [R:0x%02x G:0x%02x B:0x%02x]", r, g, b);
+ }
+
+ printf("\n");
+ return 0;
+}
+
+/**
+ * kbi_set_led_rgb() - Set RGB LED color values
+ * @state: LED_SYSTEM_OFF or LED_SYSTEM_ON
+ * @r: Red value (0-255)
+ * @g: Green value (0-255)
+ * @b: Blue value (0-255)
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_set_led_rgb(enum led_system_state state, u8 r, u8 g, u8 b)
+{
+ u8 r_reg, g_reg, b_reg;
+ int ret;
+
+ if (state == LED_SYSTEM_OFF) {
+ r_reg = REG_LED_RGB_OFF_R;
+ g_reg = REG_LED_RGB_OFF_G;
+ b_reg = REG_LED_RGB_OFF_B;
+ } else {
+ r_reg = REG_LED_RGB_ON_R;
+ g_reg = REG_LED_RGB_ON_G;
+ b_reg = REG_LED_RGB_ON_B;
+ }
+
+ ret = kbi_i2c_write_reg(r_reg, r);
+ if (ret)
+ return ret;
+
+ ret = kbi_i2c_write_reg(g_reg, g);
+ if (ret)
+ return ret;
+
+ ret = kbi_i2c_write_reg(b_reg, b);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+/**
+ * kbi_set_led_mode() - Set LED mode
+ * @state: LED_SYSTEM_OFF or LED_SYSTEM_ON
+ * @color: Color string ("r", "g", "b", "rg", "rb", "gb", "rgb")
+ * @mode_str: Mode string (see led_mode_str array)
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_set_led_mode(enum led_system_state state,
+ const char *color, const char *mode_str)
+{
+ char full_mode_str[32];
+ int mode;
+ u8 mode_reg;
+ u8 r = 0, g = 0, b = 0;
+ int ret;
+
+ if (!color || !mode_str)
+ return -EINVAL;
+
+ /* Handle on/off modes with color specification */
+ if (strcmp(mode_str, "on") == 0 || strcmp(mode_str, "off") == 0) {
+ /* Validate color string */
+ if (strcmp(color, "r") != 0 && strcmp(color, "g") != 0 &&
+ strcmp(color, "b") != 0 && strcmp(color, "rg") != 0 &&
+ strcmp(color, "rb") != 0 && strcmp(color, "gb") != 0 &&
+ strcmp(color, "rgb") != 0) {
+ printf("Invalid color: %s\n", color);
+ return -EINVAL;
+ }
+
+ mode = (strcmp(mode_str, "on") == 0) ? LED_MODE_ON : LED_MODE_OFF;
+
+ /* Set RGB values based on color and mode */
+ if (mode == LED_MODE_ON) {
+ if (strchr(color, 'r'))
+ r = 0xff;
+ if (strchr(color, 'g'))
+ g = 0xff;
+ if (strchr(color, 'b'))
+ b = 0xff;
+ }
+ } else {
+ /* Combine color and mode for other modes (e.g., "r_breathe") */
+ snprintf(full_mode_str, sizeof(full_mode_str), "%s_%s",
+ color, mode_str);
+ mode = led_str_to_mode(full_mode_str);
+ if (mode < 0) {
+ printf("Invalid LED mode: %s\n", full_mode_str);
+ return -EINVAL;
+ }
+ }
+
+ mode_reg = (state == LED_SYSTEM_OFF) ? REG_LED_OFF : REG_LED_ON;
+
+ /* Set LED mode first */
+ ret = kbi_i2c_write_reg(mode_reg, mode);
+ if (ret)
+ return ret;
+
+ /* For on/off modes, set RGB values */
+ if (mode == LED_MODE_ON || mode == LED_MODE_OFF) {
+ ret = kbi_set_led_rgb(state, r, g, b);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * kbi_get_boot_trigger() - Read boot trigger enable state
+ * @trigger: BOOT_TRIGGER_RTC or BOOT_TRIGGER_DCIN
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_get_boot_trigger(enum boot_trigger trigger)
+{
+ u8 enable;
+ u8 reg;
+ const char *name;
+ int ret;
+
+ if (trigger == BOOT_TRIGGER_RTC) {
+ reg = REG_BOOT_EN_RTC;
+ name = "RTC";
+ } else if (trigger == BOOT_TRIGGER_DCIN) {
+ reg = REG_BOOT_EN_DCIN;
+ name = "DCIN";
+ } else {
+ return -EINVAL;
+ }
+
+ ret = kbi_i2c_read_reg(reg, &enable);
+ if (ret)
+ return ret;
+
+ printf("Boot trigger %s: %s\n", name,
+ enable ? "enabled" : "disabled");
+
+ return 0;
+}
+
+/**
+ * kbi_set_boot_trigger() - Set boot trigger enable state
+ * @trigger: BOOT_TRIGGER_RTC or BOOT_TRIGGER_DCIN
+ * @enable: 1 to enable, 0 to disable
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+static int kbi_set_boot_trigger(enum boot_trigger trigger, bool enable)
+{
+ u8 reg;
+
+ if (trigger == BOOT_TRIGGER_RTC) {
+ reg = REG_BOOT_EN_RTC;
+ } else if (trigger == BOOT_TRIGGER_DCIN) {
+ reg = REG_BOOT_EN_DCIN;
+ } else {
+ return -EINVAL;
+ }
+
+ return kbi_i2c_write_reg(reg, enable ? 1 : 0);
+}
+
+/* Command implementations */
+
+static int do_kbi_version(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return kbi_get_version();
+}
+
+static int do_kbi_usid(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return kbi_get_usid();
+}
+
+static int do_kbi_hwver(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ return kbi_get_hw_version();
+}
+
+static int do_kbi_led(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum led_system_state state;
+ const char *op;
+ const char *color;
+ const char *mode;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /* Parse system state */
+ if (strcmp(argv[1], "systemoff") == 0) {
+ state = LED_SYSTEM_OFF;
+ } else if (strcmp(argv[1], "systemon") == 0) {
+ state = LED_SYSTEM_ON;
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ op = argv[2];
+
+ if (strcmp(op, "r") == 0) {
+ /* Read LED mode */
+ return kbi_get_led_mode(state);
+ } else if (strcmp(op, "w") == 0) {
+ /* Write LED mode */
+ if (argc < 5)
+ return CMD_RET_USAGE;
+
+ color = argv[3];
+ mode = argv[4];
+
+ return kbi_set_led_mode(state, color, mode);
+ }
+
+ return CMD_RET_USAGE;
+}
+
+static int do_kbi_trigger(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ enum boot_trigger trigger;
+ const char *op;
+ int enable;
+
+ if (argc < 3)
+ return CMD_RET_USAGE;
+
+ /* Parse trigger type */
+ if (strcmp(argv[1], "rtc") == 0) {
+ trigger = BOOT_TRIGGER_RTC;
+ } else if (strcmp(argv[1], "dcin") == 0) {
+ trigger = BOOT_TRIGGER_DCIN;
+ } else {
+ return CMD_RET_USAGE;
+ }
+
+ op = argv[2];
+
+ if (strcmp(op, "r") == 0) {
+ /* Read trigger state */
+ return kbi_get_boot_trigger(trigger);
+ } else if (strcmp(op, "w") == 0) {
+ /* Write trigger state */
+ if (argc < 4)
+ return CMD_RET_USAGE;
+
+ enable = simple_strtoul(argv[3], NULL, 10);
+ if (enable != 0 && enable != 1) {
+ printf("Invalid value: %s (must be 0 or 1)\n", argv[3]);
+ return CMD_RET_USAGE;
+ }
+
+ return kbi_set_boot_trigger(trigger, enable);
+ }
+
+ return CMD_RET_USAGE;
+}
+
+static struct cmd_tbl cmd_kbi_sub[] = {
+ U_BOOT_CMD_MKENT(version, 1, 1, do_kbi_version, "", ""),
+ U_BOOT_CMD_MKENT(usid, 1, 1, do_kbi_usid, "", ""),
+ U_BOOT_CMD_MKENT(hwver, 1, 1, do_kbi_hwver, "", ""),
+ U_BOOT_CMD_MKENT(led, 5, 1, do_kbi_led, "", ""),
+ U_BOOT_CMD_MKENT(trigger, 4, 1, do_kbi_trigger, "", ""),
+};
+
+static int do_kbi(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct cmd_tbl *c;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* Strip off leading 'kbi' command argument */
+ argc--;
+ argv++;
+
+ c = find_cmd_tbl(argv[0], cmd_kbi_sub, ARRAY_SIZE(cmd_kbi_sub));
+
+ if (c)
+ return c->cmd(cmdtp, flag, argc, argv);
+ else
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_CMD(
+ kbi, 6, 1, do_kbi,
+ "KBI (Khadas Bootloader Instructions)",
+ "version - Read MCU firmware version\n"
+ "kbi usid - Read unique serial ID\n"
+ "kbi hwver - Read hardware version\n"
+ "kbi led [systemoff|systemon] r - Read LED mode\n"
+ "kbi led [systemoff|systemon] w <color> <mode> - Set LED mode\n"
+ " color: r, g, b, rg, rb, gb, rgb\n"
+ " mode: off, on, breathe, heartbeat\n"
+ "kbi trigger [rtc|dcin] r - Read boot trigger state\n"
+ "kbi trigger [rtc|dcin] w <0|1> - Set boot trigger (0=disable, 1=enable)"
+);
\ No newline at end of file
diff --git a/configs/khadas-edge2-rk3588s_defconfig b/configs/khadas-edge2-rk3588s_defconfig
index 111111111111..222222222222 100644
--- a/configs/khadas-edge2-rk3588s_defconfig
+++ b/configs/khadas-edge2-rk3588s_defconfig
@@ -38,6 +38,7 @@ CONFIG_CMD_I2C=y
CONFIG_CMD_MMC=y
CONFIG_CMD_SPI=y
CONFIG_CMD_USB=y
+CONFIG_CMD_KHADAS_KBI=y
CONFIG_CMD_USB_MASS_STORAGE=y
# CONFIG_CMD_SETEXPR is not set
CONFIG_CMD_TFTPPUT=y
@@ -104,3 +105,8 @@ CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_PRODUCT_NUM=0x350a
CONFIG_SPL_TINY_MEMSET=y
CONFIG_ERRNO_STR=y
+CONFIG_SPL_ADC=y
+CONFIG_CMD_ADC=y
+CONFIG_BUTTON_ADC=y
+CONFIG_PMIC_RK8XX=y
+CONFIG_REGULATOR_RK8XX=y
\ No newline at end of file
--
Armbian