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;
}