/*
* SPDX-License-Identifier: GPL-2.0+
*
* (C) Copyright 2022 Rockchip Electronics Co., Ltd
*/
#include <common.h>
#include <asm/io.h>
#include <dwc3-uboot.h>
#include <video_rockchip.h>
DECLARE_GLOBAL_DATA_PTR;
/*
* ==================== USB VBUS 检测 ====================
* USB_VBUSDET (SoC引脚25) → USB 2.0 PHY → utmi_bvalid
* 寄存器: PERI_GRF (0xff000000) + 0x60 bit 9
* 来源: drivers/phy/phy-rockchip-inno-usb2.c
*
* ==================== 电源按键 ====================
* GPIO0_A3 (SoC引脚65) = 电源按键, 按下为低电平
* GPIO0_A2 (SoC引脚64) = 电源保持, 拉高后维持供电
* 对应 DT uboot-charge 节点:
* power-key-gpios = <&gpio0 RK_PA3 GPIO_ACTIVE_LOW>
* power-hold-gpios = <&gpio0 RK_PA2 GPIO_ACTIVE_HIGH>
*
* GPIO3_B7 (SoC引脚124) = 充电芯片 NSTDBY, 低电平=充满
*/
#define PERI_GRF_BASE 0xff000000
#define USB_UTMI_BVALID_OFF 0x0060
#define USB_UTMI_BVALID_BIT BIT(9)
#define GPIO0_BASE 0xff380000
#define GPIO0_DR_L 0x0000
#define GPIO0_DDR_L 0x0008
#define GPIO0_EXT_PORT 0x0070
#define GPIO0_A2 2
#define GPIO0_A3 3
#define GPIO0_A2_MASK (1 << GPIO0_A2)
#define GPIO0_A3_MASK (1 << GPIO0_A3)
#define PMU_IOC_BASE 0xff388000
#define GPIO0A_IOMUX_SEL_L 0x00
#define GPIO0_A2_IOMUX_BIT 8 /* bits[11:8] */
#define GPIO0_A3_IOMUX_BIT 12 /* bits[15:12] */
#define GPIO3_BASE 0xff550000
#define GPIO3_B7_MASK (1 << 15)
/* V2 GPIO 读写宏(与 drivers/gpio/rk_gpio.c 一致) */
#define gpio_v2_read(addr) (readl(addr) & 0xFFFF)
#define gpio_v2_write(addr, val) writel((val) | 0xFFFF0000, addr)
static int check_usb_powered(void)
{
u32 val = readl(PERI_GRF_BASE + USB_UTMI_BVALID_OFF);
return (val & USB_UTMI_BVALID_BIT) ? 1 : 0;
}
static int read_nstdby(void)
{
u32 val = readl(GPIO3_BASE + GPIO0_EXT_PORT);
return (val & GPIO3_B7_MASK) ? 1 : 0;
}
/* 配置电源按键和电源保持引脚 */
static void power_gpio_init(void)
{
/* IOMUX: GPIO0_A2/A3 设为 GPIO 功能(function 0)*/
writel((0xFF << (GPIO0_A2_IOMUX_BIT + 16)) |
(0 << GPIO0_A2_IOMUX_BIT) |
(0 << GPIO0_A3_IOMUX_BIT),
PMU_IOC_BASE + GPIO0A_IOMUX_SEL_L);
/* DDR: A3=输入(按键), A2=输出(电源保持), 初始低 */
u32 ddr = gpio_v2_read(GPIO0_BASE + GPIO0_DDR_L);
ddr |= GPIO0_A2_MASK; /* A2 = output */
ddr &= ~GPIO0_A3_MASK; /* A3 = input */
gpio_v2_write(GPIO0_BASE + GPIO0_DDR_L, ddr);
/* DR: A2 = low(不供电)*/
u32 dr = gpio_v2_read(GPIO0_BASE + GPIO0_DR_L);
dr &= ~GPIO0_A2_MASK;
gpio_v2_write(GPIO0_BASE + GPIO0_DR_L, dr);
}
/* 拉高 A2 保持供电 */
static void power_hold_on(void)
{
u32 dr = gpio_v2_read(GPIO0_BASE + GPIO0_DR_L);
dr |= GPIO0_A2_MASK;
gpio_v2_write(GPIO0_BASE + GPIO0_DR_L, dr);
printf("[PWR] Power hold enabled\n");
}
/* 读按键状态: 按下=低电平, 返回 1=按下 */
static int power_key_pressed(void)
{
u32 ext = readl(GPIO0_BASE + GPIO0_EXT_PORT);
return (ext & GPIO0_A3_MASK) ? 0 : 1;
}
/*
* 充电+电源按键逻辑:
* 有 USB 供电时进入, 轮询按键和 USB 拔出
* 按键按下 2 秒 → 拉高 A2 保持供电 → 退出启动内核
* USB 拔出 → 直接退出(若无 A2 保持则硬件掉电)
*/
static void show_charge_logo(void)
{
int charging = read_nstdby();
int last_charging = charging;
int battery_level = 0; /* 0, 1, 2, 3, 4 → 0%, 25%, 50%, 75%, 100% */
static const char *bmp_names[] = {
"battery_0.bmp", "battery_25.bmp", "battery_50.bmp",
"battery_75.bmp", "battery_100.bmp"
};
printf("\n[%s] USB Powered\n",
charging ? "CHARGING" : "FULL");
power_gpio_init();
/*
* USB 检测只需要上电时判断这一次。
* 进入循环后不再反复读 USB 寄存器 —— USB 拔出后硬件无 power_hold 自然掉电,
* 无需软件检测。
*/
/* === 按键开机 === */
if (power_key_pressed()) {
/*
* 上电到进入此函数时按键仍按着——用户从上电一直按到现在,
* 已超过 2 秒,直接开机,无需再等。
*/
printf("[PWR] Key held since power-on, boot now\n");
power_hold_on();
goto exit;
}
printf("[PWR] No key press, charge mode\n");
/* 显示当前电量图片(图片找不到也不影响后续流程)*/
rockchip_show_bmp(bmp_names[battery_level]);
while (1) {
/* 充电状态变化时重置电量指示 */
if (read_nstdby() != last_charging) {
last_charging = !last_charging;
battery_level = last_charging ? 4 : 0;
printf("[%s] state changed\n",
last_charging ? "CHARGING" : "FULL");
rockchip_show_bmp(bmp_names[battery_level]);
}
/* 每 3 秒递增电量指示(充满则停在 100%) */
if (last_charging) {
/* charging: 0% → 25% → 50% → 75% → repeat */
battery_level = (battery_level + 1) % 4;
} /* else full: 停在 100%,不循环 */
rockchip_show_bmp(bmp_names[battery_level]);
/* 等待 3 秒,期间检测按键长按 2s 开机 */
{
u32 start = get_timer(0);
u32 key_start = 0;
bool key_was_down = false;
while (get_timer(start) < 3000) {
mdelay(10);
if (power_key_pressed()) {
if (!key_was_down) {
key_was_down = true;
key_start = get_timer(0);
} else if (get_timer(key_start) >= 2000) {
printf("[PWR] Long press, booting...\n");
power_hold_on();
goto exit;
}
} else {
key_was_down = false;
}
}
}
}
exit:
printf("[CHARGE] Boot...\n");
}
int rk_board_late_init(void)
{
if (check_usb_powered()) {
printf("[CHARGE] USB Power detected!\n");
show_charge_logo();
}
return 0;
}