evb_rv1106.c
/*
* 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 <usb.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 GPIO1_BASE 0xff530000
#define GPIO1_B0_MASK BIT(8)
/* 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 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);
}
/* 拉高 LCD 背光(GPIO1_B0)*/
static void backlight_init(void)
{
u32 ddr = gpio_v2_read(GPIO1_BASE + GPIO0_DDR_L);
ddr |= GPIO1_B0_MASK;
gpio_v2_write(GPIO1_BASE + GPIO0_DDR_L, ddr);
u32 dr = gpio_v2_read(GPIO1_BASE + GPIO0_DR_L);
dr |= GPIO1_B0_MASK;
gpio_v2_write(GPIO1_BASE + GPIO0_DR_L, dr);
printf("[LCD] Backlight GPIO1_B0 on\n");
}
/* 拉低 LCD 背光(GPIO1_B0)*/
static void backlight_off(void)
{
u32 dr = gpio_v2_read(GPIO1_BASE + GPIO0_DR_L);
u32 ddr;
/* Latch low before switching to output, avoiding a visible high pulse. */
dr &= ~GPIO1_B0_MASK;
gpio_v2_write(GPIO1_BASE + GPIO0_DR_L, dr);
ddr = gpio_v2_read(GPIO1_BASE + GPIO0_DDR_L);
ddr |= GPIO1_B0_MASK;
gpio_v2_write(GPIO1_BASE + GPIO0_DDR_L, ddr);
}
/* 拉高 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;
}
static const char *charge_bmps[] = {
"battery_0.bmp",
"battery_1.bmp",
"battery_2.bmp",
"battery_3.bmp",
"battery_4.bmp",
"battery_5.bmp",
// "battery_fail.bmp",
};
#define CHARGE_BMP_COUNT ARRAY_SIZE(charge_bmps)
#define CHARGE_BMP_PERIOD 600 /* ms, 同 charge_animation.c */
#define CHARGE_FRAME_SETTLE_MS 30 /* > 2 frames at 70 Hz */
/*
* Load and decode every frame while the backlight is off. Subsequent calls
* only switch the VOP to an already cached buffer, so storage I/O and decode
* work cannot disturb scanout in the animation loop.
*/
static int prepare_charge_bmps(void)
{
int i, ret;
for (i = 0; i < CHARGE_BMP_COUNT; i++) {
ret = rockchip_show_bmp(charge_bmps[i]);
if (ret)
printf("[LCD] Failed to preload %s: %d\n",
charge_bmps[i], ret);
}
ret = rockchip_show_bmp(charge_bmps[0]);
if (ret)
return ret;
mdelay(CHARGE_FRAME_SETTLE_MS);
return 0;
}
/*
* 充电动画+休眠:
* USB 供电时进入, 轮播电池图, 5s 无操作灭屏休眠
* 按键唤醒 → 重新 5s 倒计时
* 按键 2 秒长按 → 拉高 A2 → 退出启动内核
* USB 拔出 → 直接掉电
*/
static void show_charge_logo(void)
{
int i, idx = 0, ret;
u32 key_start;
bool screen_on = false;
u32 last_activity;
printf("\n[CHARGE] USB Powered - Press power button 2s to boot\n");
power_gpio_init();
backlight_off();
ret = prepare_charge_bmps();
if (!ret) {
backlight_init();
screen_on = true;
} else {
printf("[LCD] Charge images unavailable: %d\n", ret);
}
last_activity = get_timer(0);
while (1) {
for (i = 0; i < CHARGE_BMP_PERIOD / 10; i++) {
mdelay(10);
if (!check_usb_powered())
goto exit;
if (screen_on && get_timer(last_activity) > 5000) {
backlight_off();
screen_on = false;
}
if (power_key_pressed()) {
last_activity = get_timer(0);
/* 休眠唤醒 */
if (!screen_on) {
ret = rockchip_show_bmp(charge_bmps[idx]);
if (!ret) {
mdelay(CHARGE_FRAME_SETTLE_MS);
backlight_init();
screen_on = true;
}
}
key_start = get_timer(0);
printf("[PWR] Power key pressed, hold 2s to boot...\n");
while (get_timer(key_start) < 2000) {
mdelay(10);
if (!check_usb_powered())
goto exit;
if (!power_key_pressed()) {
printf("[PWR] Key released, cancel\n");
last_activity = get_timer(0);
break;
}
}
if (power_key_pressed()) {
power_hold_on();
goto exit;
}
}
}
if (screen_on) {
idx = (idx + 1) % CHARGE_BMP_COUNT;
rockchip_show_bmp(charge_bmps[idx]);
}
}
exit:
// rockchip_show_logo();
printf("[CHARGE] Charge loop exit, booting...\n");
}
/* 在 board_late_init 画 logo 之前把背光拉灭, 避免 USB 供电开机时 logo 闪一帧 */
int rk_board_init(void)
{
backlight_off();
return 0;
}
/* USB 供电时禁止 uboot 绘制 logo, 避免闪屏 */
int rk_board_show_uboot_logo(void)
{
return !check_usb_powered();
}
int rk_board_late_init(void)
{
if (check_usb_powered()) {
printf("[CHARGE] USB Power detected!\n");
/* 充电循环会在首帧稳定后自行开背光 */
show_charge_logo();
} else {
/* 电池启动:立即拉高电源保持引脚以维持供电 */
power_gpio_init();
power_hold_on();
backlight_init();
}
// /* 初始化显示并显示 logo */
// rockchip_show_logo();
return 0;
}