xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision 5eac14db172e97ceec514b9d7eeabbabd047aee3)
1f1c8ecceSJoseph Chen /*
2f1c8ecceSJoseph Chen  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3f1c8ecceSJoseph Chen  *
4f1c8ecceSJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
5f1c8ecceSJoseph Chen  */
6f1c8ecceSJoseph Chen 
7e7f9facbSJoseph Chen #include <asm/io.h>
8f1c8ecceSJoseph Chen #include <common.h>
9d3ff9cf9SKever Yang #include <boot_rkimg.h>
101367bfe3SJoseph Chen #include <console.h>
11f1c8ecceSJoseph Chen #include <dm.h>
12f1c8ecceSJoseph Chen #include <errno.h>
13f1c8ecceSJoseph Chen #include <key.h>
141b3009deSJoseph Chen #include <led.h>
158b436ce5SElaine Zhang #include <rtc.h>
16f1c8ecceSJoseph Chen #include <pwm.h>
17d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h>
18d3ff9cf9SKever Yang #include <asm/suspend.h>
19d3ff9cf9SKever Yang #include <linux/input.h>
20f1c8ecceSJoseph Chen #include <power/charge_display.h>
21ac1dc0c3SJoseph Chen #include <power/charge_animation.h>
2288949342SJoseph Chen #include <power/rockchip_pm.h>
23f1c8ecceSJoseph Chen #include <power/fuel_gauge.h>
24f1c8ecceSJoseph Chen #include <power/pmic.h>
25f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h>
26f1c8ecceSJoseph Chen #include <power/regulator.h>
27f1c8ecceSJoseph Chen #include <video_rockchip.h>
28175257e4SJoseph Chen #ifdef CONFIG_IRQ
29175257e4SJoseph Chen #include <irq-generic.h>
30175257e4SJoseph Chen #include <rk_timer_irq.h>
31175257e4SJoseph Chen #endif
32f1c8ecceSJoseph Chen 
33f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
34f1c8ecceSJoseph Chen 
35ebe3d004SJoseph Chen #define IMAGE_RESET_IDX				-1
36ebe3d004SJoseph Chen #define IMAGE_SOC_100_IDX(n)			((n) - 2)
37ebe3d004SJoseph Chen #define IMAGE_LOWPOWER_IDX(n)			((n) - 1)
382e68f6b5SJoseph Chen #define SYSTEM_SUSPEND_DELAY_MS			5000
395e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS			1000
40f1c8ecceSJoseph Chen 
411b3009deSJoseph Chen #define LED_CHARGING_NAME			"battery_charging"
421b3009deSJoseph Chen #define LED_CHARGING_FULL_NAME			"battery_full"
431b3009deSJoseph Chen 
44f1c8ecceSJoseph Chen struct charge_image {
45f1c8ecceSJoseph Chen 	const char *name;
46f1c8ecceSJoseph Chen 	int soc;
47f1c8ecceSJoseph Chen 	int period;	/* ms */
48f1c8ecceSJoseph Chen };
49f1c8ecceSJoseph Chen 
50f1c8ecceSJoseph Chen struct charge_animation_priv {
51f1c8ecceSJoseph Chen 	struct udevice *pmic;
52f1c8ecceSJoseph Chen 	struct udevice *fg;
534d083e3fSJoseph Chen 	struct udevice *charger;
548b436ce5SElaine Zhang 	struct udevice *rtc;
551b3009deSJoseph Chen #ifdef CONFIG_LED
561b3009deSJoseph Chen 	struct udevice *led_charging;
571b3009deSJoseph Chen 	struct udevice *led_full;
581b3009deSJoseph Chen #endif
59f1c8ecceSJoseph Chen 	const struct charge_image *image;
60f1c8ecceSJoseph Chen 	int image_num;
61e7f9facbSJoseph Chen 
62e7f9facbSJoseph Chen 	int auto_wakeup_key_state;
632e68f6b5SJoseph Chen 	ulong auto_screen_off_timeout;	/* ms */
642e68f6b5SJoseph Chen 	ulong suspend_delay_timeout;	/* ms */
65f1c8ecceSJoseph Chen };
66f1c8ecceSJoseph Chen 
67f1c8ecceSJoseph Chen /*
68f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
69f1c8ecceSJoseph Chen  *
70f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
71f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
72f1c8ecceSJoseph Chen  */
73f1c8ecceSJoseph Chen static const struct charge_image image[] = {
74f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
75f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
76f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
77f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
78f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
79f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
80f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
81f1c8ecceSJoseph Chen };
82f1c8ecceSJoseph Chen 
83f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
84f1c8ecceSJoseph Chen {
85f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
86f1c8ecceSJoseph Chen 
87d6653c12SJoseph Chen 	/* charge mode */
88d6653c12SJoseph Chen 	pdata->uboot_charge =
89d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
90d6653c12SJoseph Chen 	pdata->android_charge =
91d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
92f1c8ecceSJoseph Chen 
93d6653c12SJoseph Chen 	pdata->exit_charge_level =
94d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
95d6653c12SJoseph Chen 	pdata->exit_charge_voltage =
96d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
97f23c35a8SJoseph Chen 
98f23c35a8SJoseph Chen 	pdata->low_power_voltage =
99f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0);
100f23c35a8SJoseph Chen 
101d6653c12SJoseph Chen 	pdata->screen_on_voltage =
102d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
103f23c35a8SJoseph Chen 	pdata->system_suspend =
104f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,system-suspend", 0);
105f1c8ecceSJoseph Chen 
106e7f9facbSJoseph Chen 	pdata->auto_wakeup_interval =
107e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0);
108e7f9facbSJoseph Chen 	pdata->auto_wakeup_screen_invert =
109e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0);
110e7f9facbSJoseph Chen 
111e7f9facbSJoseph Chen 	pdata->auto_off_screen_interval =
112e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15);
113e7f9facbSJoseph Chen 
114f23c35a8SJoseph Chen 	if (pdata->screen_on_voltage > pdata->exit_charge_voltage)
115f23c35a8SJoseph Chen 		pdata->screen_on_voltage = pdata->exit_charge_voltage;
116f1c8ecceSJoseph Chen 
117d6653c12SJoseph Chen 	debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
118f23c35a8SJoseph Chen 	      "lp_voltage=%d%%, screen_on=%dmv\n",
119d6653c12SJoseph Chen 	      pdata->uboot_charge, pdata->android_charge,
120d6653c12SJoseph Chen 	      pdata->exit_charge_level, pdata->exit_charge_voltage,
121f23c35a8SJoseph Chen 	      pdata->low_power_voltage, pdata->screen_on_voltage);
122f1c8ecceSJoseph Chen 
123f1c8ecceSJoseph Chen 	return 0;
124f1c8ecceSJoseph Chen }
125f1c8ecceSJoseph Chen 
126e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev)
127f1c8ecceSJoseph Chen {
128e7f9facbSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
129e7f9facbSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
1308b436ce5SElaine Zhang 	u32 state, rtc_state = 0;
1318b436ce5SElaine Zhang 
1328b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC
1338b436ce5SElaine Zhang 	if (priv->rtc)
1348b436ce5SElaine Zhang 		rtc_state = rtc_alarm_trigger(priv->rtc);
1358b436ce5SElaine Zhang #endif
1368b436ce5SElaine Zhang 	if (rtc_state) {
1378b436ce5SElaine Zhang 		printf("rtc alarm trigger...\n");
1388b436ce5SElaine Zhang 		return KEY_PRESS_LONG_DOWN;
1398b436ce5SElaine Zhang 	}
140f1c8ecceSJoseph Chen 
141787a62ebSJoseph Chen 	state = key_read(KEY_POWER);
142b177a917SJoseph Chen 	if (state < 0)
143f1c8ecceSJoseph Chen 		printf("read power key failed: %d\n", state);
14493aee2d9SJoseph Chen 	else if (state == KEY_PRESS_DOWN)
14593aee2d9SJoseph Chen 		printf("power key pressed...\n");
14693aee2d9SJoseph Chen 	else if (state == KEY_PRESS_LONG_DOWN)
14793aee2d9SJoseph Chen 		printf("power key long pressed...\n");
148f1c8ecceSJoseph Chen 
149e7f9facbSJoseph Chen 	/* Fixup key state for following cases */
150e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
151e7f9facbSJoseph Chen 		if (pdata->auto_wakeup_screen_invert) {
152e7f9facbSJoseph Chen 			if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) {
153e7f9facbSJoseph Chen 				/* Value is updated in timer interrupt */
154e7f9facbSJoseph Chen 				priv->auto_wakeup_key_state = KEY_PRESS_NONE;
155e7f9facbSJoseph Chen 				state = KEY_PRESS_DOWN;
156e7f9facbSJoseph Chen 			}
157e7f9facbSJoseph Chen 		}
158e7f9facbSJoseph Chen 	} else if (pdata->auto_off_screen_interval) {
15993aee2d9SJoseph Chen 		if (priv->auto_screen_off_timeout &&
16093aee2d9SJoseph Chen 		    get_timer(priv->auto_screen_off_timeout) >
161e7f9facbSJoseph Chen 		    pdata->auto_off_screen_interval * 1000) {	/* 1000ms */
162e7f9facbSJoseph Chen 			state = KEY_PRESS_DOWN;
163e7f9facbSJoseph Chen 			printf("Auto screen off\n");
164e7f9facbSJoseph Chen 		}
165e7f9facbSJoseph Chen 	}
166e7f9facbSJoseph Chen 
167b177a917SJoseph Chen 	return state;
168f1c8ecceSJoseph Chen }
169f1c8ecceSJoseph Chen 
170175257e4SJoseph Chen /*
171175257e4SJoseph Chen  * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup
172175257e4SJoseph Chen  * period timer is useless.
173175257e4SJoseph Chen  */
1743415d4ebSJoseph Chen #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND)
1752e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev)
176175257e4SJoseph Chen {
177175257e4SJoseph Chen 	return 0;
178175257e4SJoseph Chen }
179175257e4SJoseph Chen 
180175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {}
181175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {}
182175257e4SJoseph Chen 
183175257e4SJoseph Chen #else
1842e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev)
185b177a917SJoseph Chen {
1862e68f6b5SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
1872e68f6b5SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
1882e68f6b5SJoseph Chen 
1892e68f6b5SJoseph Chen 	/*
1902e68f6b5SJoseph Chen 	 * When cpu is in wfi and we try to give a long key press event without
1912e68f6b5SJoseph Chen 	 * key release, cpu would wakeup and enter wfi again immediately. So
1922e68f6b5SJoseph Chen 	 * here is the problem: cpu can only wakeup when long key released.
1932e68f6b5SJoseph Chen 	 *
1942e68f6b5SJoseph Chen 	 * Actually, we want cpu can detect long key event without key release,
1952e68f6b5SJoseph Chen 	 * so we give a suspend delay timeout for cpu to detect this.
1962e68f6b5SJoseph Chen 	 */
1972e68f6b5SJoseph Chen 	if (priv->suspend_delay_timeout &&
1982e68f6b5SJoseph Chen 	    get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS)
1992e68f6b5SJoseph Chen 		return 0;
2002e68f6b5SJoseph Chen 
201f23c35a8SJoseph Chen 	if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) {
202b177a917SJoseph Chen 		printf("\nSystem suspend: ");
203992f4e77SJoseph Chen 		putc('0');
204992f4e77SJoseph Chen 		regulators_enable_state_mem(false);
205b177a917SJoseph Chen 		putc('1');
206b177a917SJoseph Chen 		local_irq_disable();
207b177a917SJoseph Chen 		putc('2');
208b177a917SJoseph Chen 		irqs_suspend();
209b177a917SJoseph Chen 		putc('3');
21088949342SJoseph Chen 		device_suspend();
21188949342SJoseph Chen 		putc('4');
212b177a917SJoseph Chen 		putc('\n');
213b177a917SJoseph Chen 
214b177a917SJoseph Chen 		/* Trap into ATF for low power mode */
215b177a917SJoseph Chen 		cpu_suspend(0, psci_system_suspend);
216b177a917SJoseph Chen 
217b177a917SJoseph Chen 		putc('\n');
21888949342SJoseph Chen 		putc('4');
21988949342SJoseph Chen 		device_resume();
220b177a917SJoseph Chen 		putc('3');
221b177a917SJoseph Chen 		irqs_resume();
222b177a917SJoseph Chen 		putc('2');
223b177a917SJoseph Chen 		local_irq_enable();
224b177a917SJoseph Chen 		putc('1');
225b177a917SJoseph Chen 		putc('\n');
2268fc5ae06SJoseph Chen 	} else {
227*5eac14dbSJoseph Chen 		irqs_suspend();
2288fc5ae06SJoseph Chen 		printf("\nWfi\n");
2298fc5ae06SJoseph Chen 		wfi();
2302e68f6b5SJoseph Chen 		putc('1');
231*5eac14dbSJoseph Chen 		irqs_resume();
2328fc5ae06SJoseph Chen 	}
233b177a917SJoseph Chen 
2342e68f6b5SJoseph Chen 	priv->suspend_delay_timeout = get_timer(0);
2352e68f6b5SJoseph Chen 
236b177a917SJoseph Chen 	/*
237b177a917SJoseph Chen 	 * We must wait for key release event finish, otherwise
238b177a917SJoseph Chen 	 * we may read key state too early.
239b177a917SJoseph Chen 	 */
240b177a917SJoseph Chen 	mdelay(300);
241b177a917SJoseph Chen 
242b177a917SJoseph Chen 	return 0;
243f1c8ecceSJoseph Chen }
244ebe3d004SJoseph Chen 
24579244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data)
24679244e4cSJoseph Chen {
24779244e4cSJoseph Chen 	struct udevice *dev = data;
24879244e4cSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
24979244e4cSJoseph Chen 	static long long count;
25079244e4cSJoseph Chen 
25179244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
25279244e4cSJoseph Chen 
25379244e4cSJoseph Chen 	priv->auto_wakeup_key_state = KEY_PRESS_DOWN;
25479244e4cSJoseph Chen 	printf("auto wakeup count: %lld\n", ++count);
25579244e4cSJoseph Chen }
25679244e4cSJoseph Chen 
25779244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds)
25879244e4cSJoseph Chen {
25979244e4cSJoseph Chen 	uint64_t period = 24000000ULL * seconds;
26079244e4cSJoseph Chen 
26179244e4cSJoseph Chen 	/* Disable before conifg */
26279244e4cSJoseph Chen 	writel(0, TIMER_BASE + TIMER_CTRL);
26379244e4cSJoseph Chen 
26479244e4cSJoseph Chen 	/* Config */
26579244e4cSJoseph Chen 	writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
26679244e4cSJoseph Chen 	writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
26779244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
26879244e4cSJoseph Chen 	writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
26979244e4cSJoseph Chen 
27079244e4cSJoseph Chen 	/* IRQ */
27179244e4cSJoseph Chen 	irq_install_handler(TIMER_IRQ, timer_irq_handler, dev);
27279244e4cSJoseph Chen 	irq_handler_enable(TIMER_IRQ);
27379244e4cSJoseph Chen }
27479244e4cSJoseph Chen 
27579244e4cSJoseph Chen static void autowakeup_timer_uninit(void)
27679244e4cSJoseph Chen {
27779244e4cSJoseph Chen 	irq_free_handler(TIMER_IRQ);
27879244e4cSJoseph Chen }
279175257e4SJoseph Chen #endif
28079244e4cSJoseph Chen 
281f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP
282f23c35a8SJoseph Chen static void charge_show_bmp(const char *name)
283f23c35a8SJoseph Chen {
284f23c35a8SJoseph Chen 	rockchip_show_bmp(name);
285f23c35a8SJoseph Chen }
286f23c35a8SJoseph Chen 
287f23c35a8SJoseph Chen static void charge_show_logo(void)
288f23c35a8SJoseph Chen {
289f23c35a8SJoseph Chen 	rockchip_show_logo();
290f23c35a8SJoseph Chen }
291f23c35a8SJoseph Chen #else
292f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {}
293f23c35a8SJoseph Chen static void charge_show_logo(void) {}
294f23c35a8SJoseph Chen #endif
295f23c35a8SJoseph Chen 
2961b3009deSJoseph Chen #ifdef CONFIG_LED
2971b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc)
2981b3009deSJoseph Chen {
2991b3009deSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
3001b3009deSJoseph Chen 	static int old_soc = -1;
3011b3009deSJoseph Chen 	int ret, ledst;
3021b3009deSJoseph Chen 
3031b3009deSJoseph Chen 	if (old_soc == soc)
3041b3009deSJoseph Chen 		return 0;
3051b3009deSJoseph Chen 
3061b3009deSJoseph Chen 	old_soc = soc;
3071b3009deSJoseph Chen 	if (priv->led_charging) {
3081b3009deSJoseph Chen 		ledst = (soc < 100) ? LEDST_ON : LEDST_OFF;
3091b3009deSJoseph Chen 		ret = led_set_state(priv->led_charging, ledst);
3101b3009deSJoseph Chen 		if (ret) {
3111b3009deSJoseph Chen 			printf("set charging led %s failed, ret=%d\n",
3121b3009deSJoseph Chen 			       (ledst == LEDST_ON) ? "ON" : "OFF", ret);
3131b3009deSJoseph Chen 			return ret;
3141b3009deSJoseph Chen 		}
3151b3009deSJoseph Chen 	}
3161b3009deSJoseph Chen 
3171b3009deSJoseph Chen 	if (priv->led_full) {
3181b3009deSJoseph Chen 		ledst = (soc == 100) ? LEDST_ON : LEDST_OFF;
3191b3009deSJoseph Chen 		ret = led_set_state(priv->led_full, ledst);
3201b3009deSJoseph Chen 		if (ret) {
3211b3009deSJoseph Chen 			printf("set charging full led %s failed, ret=%d\n",
3221b3009deSJoseph Chen 			       ledst == LEDST_ON ? "ON" : "OFF", ret);
3231b3009deSJoseph Chen 			return ret;
3241b3009deSJoseph Chen 		}
3251b3009deSJoseph Chen 	}
3261b3009deSJoseph Chen 
3271b3009deSJoseph Chen 	return 0;
3281b3009deSJoseph Chen }
3291b3009deSJoseph Chen #else
3301b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) { return 0; }
3311b3009deSJoseph Chen #endif
3321b3009deSJoseph Chen 
3334d083e3fSJoseph Chen static int fg_charger_get_chrg_online(struct udevice *dev)
3344d083e3fSJoseph Chen {
3354d083e3fSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
3364d083e3fSJoseph Chen 	struct udevice *charger;
3374d083e3fSJoseph Chen 
3384d083e3fSJoseph Chen 	charger = priv->charger ? : priv->fg;
3394d083e3fSJoseph Chen 
3404d083e3fSJoseph Chen 	return fuel_gauge_get_chrg_online(charger);
3414d083e3fSJoseph Chen }
3424d083e3fSJoseph Chen 
343f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev)
344f23c35a8SJoseph Chen {
345f23c35a8SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
346f23c35a8SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
347f23c35a8SJoseph Chen 	struct udevice *pmic = priv->pmic;
348f23c35a8SJoseph Chen 	struct udevice *fg = priv->fg;
349f23c35a8SJoseph Chen 	int voltage, soc, charging = 1;
35079244e4cSJoseph Chen 	static int timer_initialized;
3511b3009deSJoseph Chen 	int ret;
352f23c35a8SJoseph Chen 
353f23c35a8SJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
354f23c35a8SJoseph Chen 	if (voltage < 0)
355f23c35a8SJoseph Chen 		return -EINVAL;
356f23c35a8SJoseph Chen 
357f23c35a8SJoseph Chen 	while (voltage < pdata->low_power_voltage + 50) {
358f23c35a8SJoseph Chen 		/* Check charger online */
3594d083e3fSJoseph Chen 		charging = fg_charger_get_chrg_online(dev);
360f23c35a8SJoseph Chen 		if (charging <= 0) {
36179244e4cSJoseph Chen 			printf("%s: Not charging, online=%d. Shutdown...\n",
36279244e4cSJoseph Chen 			       __func__, charging);
363f23c35a8SJoseph Chen 			/* wait uart flush before shutdown */
36479244e4cSJoseph Chen 			mdelay(5);
365f23c35a8SJoseph Chen 			/* PMIC shutdown */
366f23c35a8SJoseph Chen 			pmic_shutdown(pmic);
367f23c35a8SJoseph Chen 
368f23c35a8SJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
369f23c35a8SJoseph Chen 			continue;
370f23c35a8SJoseph Chen 		}
371f23c35a8SJoseph Chen 
37279244e4cSJoseph Chen 		/* Enable auto wakeup */
37379244e4cSJoseph Chen 		if (!timer_initialized) {
37479244e4cSJoseph Chen 			timer_initialized = 1;
37579244e4cSJoseph Chen 			autowakeup_timer_init(dev, 5);
37679244e4cSJoseph Chen 		}
37779244e4cSJoseph Chen 
378f23c35a8SJoseph Chen 		/*
379f23c35a8SJoseph Chen 		 * Just for fuel gauge to update something important,
380f23c35a8SJoseph Chen 		 * including charge current, coulometer or other.
381f23c35a8SJoseph Chen 		 */
3824f9cab27SJoseph Chen 		soc = fuel_gauge_update_get_soc(fg);
383f23c35a8SJoseph Chen 		if (soc < 0 || soc > 100) {
384f23c35a8SJoseph Chen 			printf("get soc failed: %d\n", soc);
385f23c35a8SJoseph Chen 			continue;
386f23c35a8SJoseph Chen 		}
387f23c35a8SJoseph Chen 
3881b3009deSJoseph Chen 		/* Update led */
3891b3009deSJoseph Chen 		ret = leds_update(dev, soc);
3901b3009deSJoseph Chen 		if (ret)
3911b3009deSJoseph Chen 			printf("update led failed: %d\n", ret);
3921b3009deSJoseph Chen 
393f23c35a8SJoseph Chen 		printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
394f23c35a8SJoseph Chen 		       pdata->low_power_voltage, voltage);
395f23c35a8SJoseph Chen 
396f23c35a8SJoseph Chen 		/* System suspend */
3972e68f6b5SJoseph Chen 		system_suspend_enter(dev);
398f23c35a8SJoseph Chen 
399f23c35a8SJoseph Chen 		/* Update voltage */
400f23c35a8SJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
401f23c35a8SJoseph Chen 		if (voltage < 0) {
402f23c35a8SJoseph Chen 			printf("get voltage failed: %d\n", voltage);
403f23c35a8SJoseph Chen 			continue;
404f23c35a8SJoseph Chen 		}
4055fc2a70cSJoseph Chen 
4065fc2a70cSJoseph Chen 		if (ctrlc()) {
4075fc2a70cSJoseph Chen 			printf("Extrem low charge: exit by ctrl+c\n");
4085fc2a70cSJoseph Chen 			break;
4095fc2a70cSJoseph Chen 		}
410f23c35a8SJoseph Chen 	}
411f23c35a8SJoseph Chen 
41279244e4cSJoseph Chen 	autowakeup_timer_uninit();
41379244e4cSJoseph Chen 
414f23c35a8SJoseph Chen 	return 0;
415f23c35a8SJoseph Chen }
416f23c35a8SJoseph Chen 
417f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
418f1c8ecceSJoseph Chen {
419f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
420f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
421f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
422f1c8ecceSJoseph Chen 	struct udevice *pmic = priv->pmic;
423f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
424a8b9d026SJoseph Chen 	const char *preboot = env_get("preboot");
425f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
426f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
427f1c8ecceSJoseph Chen 	bool screen_on = true;
428f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
4295e804741SJoseph Chen 	ulong delta;
430f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
431ebe3d004SJoseph Chen 	int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RESET_IDX;
432d6653c12SJoseph Chen 	int soc, voltage, current, key_state;
433f23c35a8SJoseph Chen 	int i, charging = 1, ret;
4348f9ff705SJoseph Chen 	int boot_mode;
4355e804741SJoseph Chen 	int first_poll_fg = 1;
436f1c8ecceSJoseph Chen 
437f23c35a8SJoseph Chen /*
438f23c35a8SJoseph Chen  * Check sequence:
439f23c35a8SJoseph Chen  *
440f23c35a8SJoseph Chen  * 1. Extrem low power charge?
441f23c35a8SJoseph Chen  * 2. Preboot cmd?
442f23c35a8SJoseph Chen  * 3. Valid boot mode?
443f23c35a8SJoseph Chen  * 4. U-Boot charge enabled by dts config?
444f23c35a8SJoseph Chen  * 5. Screen off before charge?
445f23c35a8SJoseph Chen  * 6. Enter charge !
446f23c35a8SJoseph Chen  *
447f23c35a8SJoseph Chen  */
44861a7a6d6SJoseph Chen 	if (!fuel_gauge_bat_is_exist(fg)) {
44961a7a6d6SJoseph Chen 		printf("Exit charge: battery is not exist\n");
45061a7a6d6SJoseph Chen 		return 0;
45161a7a6d6SJoseph Chen 	}
45261a7a6d6SJoseph Chen 
453f23c35a8SJoseph Chen 	/* Extrem low power charge */
454f23c35a8SJoseph Chen 	ret = charge_extrem_low_power(dev);
455f23c35a8SJoseph Chen 	if (ret < 0) {
456f23c35a8SJoseph Chen 		printf("extrem low power charge failed, ret=%d\n", ret);
457f23c35a8SJoseph Chen 		return ret;
458f23c35a8SJoseph Chen 	}
459f23c35a8SJoseph Chen 
460a8b9d026SJoseph Chen 	/* If there is preboot command, exit */
46161a7a6d6SJoseph Chen 	if (preboot && !strstr(preboot, "dvfs")) {
4627ae45834SJoseph Chen 		printf("Exit charge: due to preboot cmd '%s'\n", preboot);
463a8b9d026SJoseph Chen 		return 0;
464a8b9d026SJoseph Chen 	}
465a8b9d026SJoseph Chen 
466f23c35a8SJoseph Chen 	/* Not valid charge mode, exit */
467d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
4688f9ff705SJoseph Chen 	boot_mode = rockchip_get_boot_mode();
469221b5220SJoseph Chen 	if ((boot_mode != BOOT_MODE_CHARGING) &&
470221b5220SJoseph Chen 	    (boot_mode != BOOT_MODE_UNDEFINE)) {
4717ae45834SJoseph Chen 		printf("Exit charge: due to boot mode\n");
4728f9ff705SJoseph Chen 		return 0;
4738f9ff705SJoseph Chen 	}
4748f9ff705SJoseph Chen #endif
475221b5220SJoseph Chen 
476616056c9Sshengfei Xu 	/* Not charger online, exit */
4774d083e3fSJoseph Chen 	charging = fg_charger_get_chrg_online(dev);
478cf49f6adSJoseph Chen 	if (charging <= 0) {
4797ae45834SJoseph Chen 		printf("Exit charge: due to charger offline\n");
480616056c9Sshengfei Xu 		return 0;
481cf49f6adSJoseph Chen 	}
482616056c9Sshengfei Xu 
483f23c35a8SJoseph Chen 	/* Enter android charge, set property for kernel */
484d6653c12SJoseph Chen 	if (pdata->android_charge) {
485d6653c12SJoseph Chen 		env_update("bootargs", "androidboot.mode=charger");
486d6653c12SJoseph Chen 		printf("Android charge mode\n");
487d6653c12SJoseph Chen 	}
488d6653c12SJoseph Chen 
489f23c35a8SJoseph Chen 	/* Not enable U-Boot charge, exit */
490cf49f6adSJoseph Chen 	if (!pdata->uboot_charge) {
4917ae45834SJoseph Chen 		printf("Exit charge: due to not enable uboot charge\n");
492d6653c12SJoseph Chen 		return 0;
493cf49f6adSJoseph Chen 	}
494d6653c12SJoseph Chen 
495f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
496f1c8ecceSJoseph Chen 	if (voltage < 0) {
497f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
498f1c8ecceSJoseph Chen 		return -EINVAL;
499f1c8ecceSJoseph Chen 	}
500f1c8ecceSJoseph Chen 
501f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
502d6653c12SJoseph Chen 	if (voltage <= pdata->screen_on_voltage + 50) {
503f1c8ecceSJoseph Chen 		screen_on = false;
504f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
505f23c35a8SJoseph Chen 		charge_show_bmp(NULL);
506f1c8ecceSJoseph Chen 	}
507f1c8ecceSJoseph Chen 
508e7f9facbSJoseph Chen 	/* Auto wakeup */
509e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
510e7f9facbSJoseph Chen 		printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval);
51179244e4cSJoseph Chen 		autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
512e7f9facbSJoseph Chen 	}
513e7f9facbSJoseph Chen 
514175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */
515175257e4SJoseph Chen #ifdef CONFIG_IRQ
516d6653c12SJoseph Chen 	printf("Enter U-Boot charging mode\n");
517175257e4SJoseph Chen #else
518175257e4SJoseph Chen 	printf("Enter U-Boot charging mode(without IRQ)\n");
519175257e4SJoseph Chen #endif
520f1c8ecceSJoseph Chen 
521d6653c12SJoseph Chen 	charge_start = get_timer(0);
5225e804741SJoseph Chen 	delta = get_timer(0);
523e7f9facbSJoseph Chen 
524f1c8ecceSJoseph Chen 	/* Charging ! */
525f1c8ecceSJoseph Chen 	while (1) {
5265e804741SJoseph Chen 		/*
5275e804741SJoseph Chen 		 * At the most time, fuel gauge is usually a i2c device, we
5285e804741SJoseph Chen 		 * should avoid read/write all the time. We had better set
5295e804741SJoseph Chen 		 * poll seconds to update fuel gauge info.
5305e804741SJoseph Chen 		 */
5315e804741SJoseph Chen 		if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS)
5325e804741SJoseph Chen 			goto show_images;
5335e804741SJoseph Chen 
5345e804741SJoseph Chen 		delta = get_timer(0);
5355e804741SJoseph Chen 
536f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
537f1c8ecceSJoseph Chen 
538fd62311eSJoseph Chen 		/*
539fd62311eSJoseph Chen 		 * Most fuel gauge is I2C interface, it shouldn't be interrupted
540ebe3d004SJoseph Chen 		 * during transfer. The power key event depends on interrupt, so
541ebe3d004SJoseph Chen 		 * we should disable local irq when update fuel gauge.
542fd62311eSJoseph Chen 		 */
543fd62311eSJoseph Chen 		local_irq_disable();
544fd62311eSJoseph Chen 
545f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
5464d083e3fSJoseph Chen 		charging = fg_charger_get_chrg_online(dev);
547f1c8ecceSJoseph Chen 		if (charging <= 0) {
548f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
549f1c8ecceSJoseph Chen 			       charging);
550f1c8ecceSJoseph Chen 
551f1c8ecceSJoseph Chen 			/* wait uart flush before shutdown */
55279244e4cSJoseph Chen 			mdelay(5);
553f1c8ecceSJoseph Chen 
554f1c8ecceSJoseph Chen 			/* PMIC shutdown */
555f1c8ecceSJoseph Chen 			pmic_shutdown(pmic);
556f1c8ecceSJoseph Chen 
557f1c8ecceSJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
558f1c8ecceSJoseph Chen 			continue;
559f1c8ecceSJoseph Chen 		}
560f1c8ecceSJoseph Chen 
561f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
562f1c8ecceSJoseph Chen 
563f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
5644f9cab27SJoseph Chen 		soc = fuel_gauge_update_get_soc(fg);
565f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
566f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
567f1c8ecceSJoseph Chen 			continue;
568f1c8ecceSJoseph Chen 		}
569f1c8ecceSJoseph Chen 
570f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
571f1c8ecceSJoseph Chen 		if (voltage < 0) {
572f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
573f1c8ecceSJoseph Chen 			continue;
574f1c8ecceSJoseph Chen 		}
575f1c8ecceSJoseph Chen 
576d6653c12SJoseph Chen 		current = fuel_gauge_get_current(fg);
577d6653c12SJoseph Chen 		if (current == -ENOSYS) {
578d6653c12SJoseph Chen 			printf("get current failed: %d\n", current);
579d6653c12SJoseph Chen 			continue;
580d6653c12SJoseph Chen 		}
581fd62311eSJoseph Chen 
582ebe3d004SJoseph Chen 		first_poll_fg = 0;
583fd62311eSJoseph Chen 		local_irq_enable();
584fd62311eSJoseph Chen 
5855e804741SJoseph Chen show_images:
586f1c8ecceSJoseph Chen 		/*
587f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
588f1c8ecceSJoseph Chen 		 * is not good to know what happen.
589f1c8ecceSJoseph Chen 		 */
590f1c8ecceSJoseph Chen 		if (!debug_start)
591f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
592f1c8ecceSJoseph Chen 		if (get_timer(debug_start) > 20000) {
593f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
594ebe3d004SJoseph Chen 			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, "
595ebe3d004SJoseph Chen 			       "online=%d, screen_on=%d\n",
596d6653c12SJoseph Chen 			       get_timer(0)/1000, soc, voltage,
597d6653c12SJoseph Chen 			       current, charging, screen_on);
598f1c8ecceSJoseph Chen 		}
599f1c8ecceSJoseph Chen 
6001b3009deSJoseph Chen 		/* Update leds */
6011b3009deSJoseph Chen 		ret = leds_update(dev, soc);
6021b3009deSJoseph Chen 		if (ret)
6031b3009deSJoseph Chen 			printf("update led failed: %d\n", ret);
6041b3009deSJoseph Chen 
605f1c8ecceSJoseph Chen 		/*
606f23c35a8SJoseph Chen 		 * If ever lowpower screen off, force screen_on=false, which
607f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
608f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
609f1c8ecceSJoseph Chen 		 */
610f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
611f1c8ecceSJoseph Chen 			screen_on = false;
612f1c8ecceSJoseph Chen 
613f1c8ecceSJoseph Chen 		/*
614f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
615ebe3d004SJoseph Chen 		 * 'ever_lowpower_screen_off' means enter the while(1) loop with
616f1c8ecceSJoseph Chen 		 * screen off.
617f1c8ecceSJoseph Chen 		 */
618f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
619d6653c12SJoseph Chen 		    (voltage > pdata->screen_on_voltage)) {
620f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
621f1c8ecceSJoseph Chen 			screen_on = true;
622ebe3d004SJoseph Chen 			show_idx = IMAGE_RESET_IDX;
623f1c8ecceSJoseph Chen 		}
624f1c8ecceSJoseph Chen 
625f1c8ecceSJoseph Chen 		/*
626ebe3d004SJoseph Chen 		 * IMAGE_RESET_IDX means show_idx show be update by start_idx.
627f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
628ebe3d004SJoseph Chen 		 * as IMAGE_RESET_IDX which updates images index from start_idx
629f1c8ecceSJoseph Chen 		 * that calculate by current soc.
630f1c8ecceSJoseph Chen 		 */
631ebe3d004SJoseph Chen 		if (show_idx == IMAGE_RESET_IDX) {
632ebe3d004SJoseph Chen 			for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) {
633f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
634f1c8ecceSJoseph Chen 				if ((soc >= image[i].soc) &&
635f1c8ecceSJoseph Chen 				    (soc < image[i + 1].soc)) {
636f1c8ecceSJoseph Chen 					start_idx = i;
637f1c8ecceSJoseph Chen 					break;
638f1c8ecceSJoseph Chen 				}
639f1c8ecceSJoseph Chen 
640f1c8ecceSJoseph Chen 				if (soc >= 100) {
641ebe3d004SJoseph Chen 					start_idx = IMAGE_SOC_100_IDX(image_num);
642f1c8ecceSJoseph Chen 					break;
643f1c8ecceSJoseph Chen 				}
644f1c8ecceSJoseph Chen 			}
645f1c8ecceSJoseph Chen 
646f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
647f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
648f1c8ecceSJoseph Chen 
649f1c8ecceSJoseph Chen 			/* Mark start index and start time */
650f1c8ecceSJoseph Chen 			show_idx = start_idx;
651f1c8ecceSJoseph Chen 			show_start = get_timer(0);
652f1c8ecceSJoseph Chen 		}
653f1c8ecceSJoseph Chen 
654f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
655f1c8ecceSJoseph Chen 
656f1c8ecceSJoseph Chen 		/* Step3: show images */
657f1c8ecceSJoseph Chen 		if (screen_on) {
65893aee2d9SJoseph Chen 			/* Don't call 'charge_show_bmp' unless image changed */
65993aee2d9SJoseph Chen 			if (old_show_idx != show_idx) {
66093aee2d9SJoseph Chen 				old_show_idx = show_idx;
661f1c8ecceSJoseph Chen 				debug("SHOW: %s\n", image[show_idx].name);
662f23c35a8SJoseph Chen 				charge_show_bmp(image[show_idx].name);
66393aee2d9SJoseph Chen 			}
664ebe3d004SJoseph Chen 			/* Re-calculate timeout to off screen */
665e7f9facbSJoseph Chen 			if (priv->auto_screen_off_timeout == 0)
666e7f9facbSJoseph Chen 				priv->auto_screen_off_timeout = get_timer(0);
667f1c8ecceSJoseph Chen 		} else {
668e7f9facbSJoseph Chen 			priv->auto_screen_off_timeout = 0;
6692e68f6b5SJoseph Chen 			system_suspend_enter(dev);
670f1c8ecceSJoseph Chen 		}
671f1c8ecceSJoseph Chen 
672f1c8ecceSJoseph Chen 		mdelay(5);
673f1c8ecceSJoseph Chen 
674f1c8ecceSJoseph Chen 		/* Every image shows period */
675f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
676f1c8ecceSJoseph Chen 			show_start = get_timer(0);
677f1c8ecceSJoseph Chen 			/* Update to next image */
678f1c8ecceSJoseph Chen 			show_idx++;
679ebe3d004SJoseph Chen 			if (show_idx > IMAGE_SOC_100_IDX(image_num))
680ebe3d004SJoseph Chen 				show_idx = IMAGE_RESET_IDX;
681f1c8ecceSJoseph Chen 		}
682f1c8ecceSJoseph Chen 
683f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
684f1c8ecceSJoseph Chen 
685f1c8ecceSJoseph Chen 		/*
686f1c8ecceSJoseph Chen 		 * Step4: check key event.
687f1c8ecceSJoseph Chen 		 *
688f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
689f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
690f1c8ecceSJoseph Chen 		 */
691e7f9facbSJoseph Chen 		key_state = check_key_press(dev);
692b177a917SJoseph Chen 		if (key_state == KEY_PRESS_DOWN) {
693f1c8ecceSJoseph Chen 			/*
694f1c8ecceSJoseph Chen 			 * Clear current image index, and show image
695f1c8ecceSJoseph Chen 			 * from start_idx
696f1c8ecceSJoseph Chen 			 */
697ebe3d004SJoseph Chen 			old_show_idx = IMAGE_RESET_IDX;
698ebe3d004SJoseph Chen 			show_idx = IMAGE_RESET_IDX;
699f1c8ecceSJoseph Chen 
700f1c8ecceSJoseph Chen 			/*
701ebe3d004SJoseph Chen 			 *	Reverse the screen state
702f1c8ecceSJoseph Chen 			 *
703f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
704f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
705f1c8ecceSJoseph Chen 			 *
706f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
707f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
708f1c8ecceSJoseph Chen 			 */
709ebe3d004SJoseph Chen 			if (screen_on) {
710ebe3d004SJoseph Chen 				charge_show_bmp(NULL); /* Turn off screen */
711f1c8ecceSJoseph Chen 				screen_on = false;
7122e68f6b5SJoseph Chen 				priv->suspend_delay_timeout = get_timer(0);
713ebe3d004SJoseph Chen 			} else {
714f1c8ecceSJoseph Chen 				screen_on = true;
715ebe3d004SJoseph Chen 			}
7162e68f6b5SJoseph Chen 
7172e68f6b5SJoseph Chen 			printf("screen %s\n", screen_on ? "on" : "off");
718b177a917SJoseph Chen 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
719ebe3d004SJoseph Chen 			/* Set screen_on=true anyway when key long pressed */
720f1c8ecceSJoseph Chen 			if (!screen_on)
721f1c8ecceSJoseph Chen 				screen_on = true;
722f1c8ecceSJoseph Chen 
7232e68f6b5SJoseph Chen 			printf("screen %s\n", screen_on ? "on" : "off");
7242e68f6b5SJoseph Chen 
725f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
726d6653c12SJoseph Chen 			if (soc < pdata->exit_charge_level) {
727f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
728d6653c12SJoseph Chen 				       soc, pdata->exit_charge_level);
729f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
730ebe3d004SJoseph Chen 				show_idx = IMAGE_LOWPOWER_IDX(image_num);
731f1c8ecceSJoseph Chen 				continue;
732f1c8ecceSJoseph Chen 			}
733f1c8ecceSJoseph Chen 
734d6653c12SJoseph Chen 			if (voltage < pdata->exit_charge_voltage) {
735f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
736d6653c12SJoseph Chen 				       voltage, pdata->exit_charge_voltage);
737f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
738ebe3d004SJoseph Chen 				show_idx = IMAGE_LOWPOWER_IDX(image_num);
739f1c8ecceSJoseph Chen 				continue;
740f1c8ecceSJoseph Chen 			}
741f1c8ecceSJoseph Chen 
742f1c8ecceSJoseph Chen 			/* Success exit charging */
743f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
744f23c35a8SJoseph Chen 			charge_show_logo();
745f1c8ecceSJoseph Chen 			break;
746f1c8ecceSJoseph Chen 		} else {
747f1c8ecceSJoseph Chen 			/* Do nothing */
748f1c8ecceSJoseph Chen 		}
749f1c8ecceSJoseph Chen 
750f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
751f1c8ecceSJoseph Chen 
752d6653c12SJoseph Chen 		/* Step5: Exit by ctrl+c */
7531367bfe3SJoseph Chen 		if (ctrlc()) {
754d6653c12SJoseph Chen 			if (voltage >= pdata->screen_on_voltage)
755f23c35a8SJoseph Chen 				charge_show_logo();
7561367bfe3SJoseph Chen 			printf("Exit charge, due to ctrl+c\n");
7571367bfe3SJoseph Chen 			break;
7581367bfe3SJoseph Chen 		}
759f1c8ecceSJoseph Chen 	}
760f1c8ecceSJoseph Chen 
761e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval)
762e7f9facbSJoseph Chen 		autowakeup_timer_uninit();
763e7f9facbSJoseph Chen 
764f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
765f1c8ecceSJoseph Chen 	if (ms >= 1000) {
766f1c8ecceSJoseph Chen 		sec = ms / 1000;
767f1c8ecceSJoseph Chen 		ms = ms % 1000;
768f1c8ecceSJoseph Chen 	}
769f1c8ecceSJoseph Chen 
770f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
771f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
772f1c8ecceSJoseph Chen 
773f1c8ecceSJoseph Chen 	return 0;
774f1c8ecceSJoseph Chen }
775f1c8ecceSJoseph Chen 
7764d083e3fSJoseph Chen static int fg_charger_get_device(struct udevice **fuel_gauge,
7774d083e3fSJoseph Chen 				 struct udevice **charger)
7784d083e3fSJoseph Chen {
7794d083e3fSJoseph Chen 	struct udevice *dev;
7804d083e3fSJoseph Chen 	struct uclass *uc;
7814d083e3fSJoseph Chen 	int ret, cap;
7824d083e3fSJoseph Chen 
7834d083e3fSJoseph Chen 	*fuel_gauge = NULL,
7844d083e3fSJoseph Chen 	*charger = NULL;
7854d083e3fSJoseph Chen 
7864d083e3fSJoseph Chen 	ret = uclass_get(UCLASS_FG, &uc);
7874d083e3fSJoseph Chen 	if (ret)
7884d083e3fSJoseph Chen 		return ret;
7894d083e3fSJoseph Chen 
7904d083e3fSJoseph Chen 	for (uclass_first_device(UCLASS_FG, &dev);
7914d083e3fSJoseph Chen 	     dev;
7924d083e3fSJoseph Chen 	     uclass_next_device(&dev)) {
7934d083e3fSJoseph Chen 		cap = fuel_gauge_capability(dev);
7944d083e3fSJoseph Chen 		if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) {
7954d083e3fSJoseph Chen 			*fuel_gauge = dev;
7964d083e3fSJoseph Chen 			*charger = NULL;
7974d083e3fSJoseph Chen 		} else if (cap == FG_CAP_FUEL_GAUGE) {
7984d083e3fSJoseph Chen 			*fuel_gauge = dev;
7994d083e3fSJoseph Chen 		} else if (cap == FG_CAP_CHARGER) {
8004d083e3fSJoseph Chen 			*charger = dev;
8014d083e3fSJoseph Chen 		}
8024d083e3fSJoseph Chen 	}
8034d083e3fSJoseph Chen 
8044d083e3fSJoseph Chen 	return (*fuel_gauge) ? 0 : -ENODEV;
8054d083e3fSJoseph Chen }
8064d083e3fSJoseph Chen 
807f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
808f1c8ecceSJoseph Chen 	.show = charge_animation_show,
809f1c8ecceSJoseph Chen };
810f1c8ecceSJoseph Chen 
811f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
812f1c8ecceSJoseph Chen {
813f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
814f23c35a8SJoseph Chen 	int ret, soc;
815f1c8ecceSJoseph Chen 
816f23c35a8SJoseph Chen 	/* Get PMIC: used for power off system  */
817ebe3d004SJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic);
818f1c8ecceSJoseph Chen 	if (ret) {
819cf49f6adSJoseph Chen 		if (ret == -ENODEV)
820cf49f6adSJoseph Chen 			printf("Can't find PMIC\n");
821cf49f6adSJoseph Chen 		else
822f1c8ecceSJoseph Chen 			printf("Get UCLASS PMIC failed: %d\n", ret);
823f1c8ecceSJoseph Chen 		return ret;
824f1c8ecceSJoseph Chen 	}
825f1c8ecceSJoseph Chen 
8264d083e3fSJoseph Chen 	/* Get fuel gauge and charger(If need) */
8274d083e3fSJoseph Chen 	ret = fg_charger_get_device(&priv->fg, &priv->charger);
828f1c8ecceSJoseph Chen 	if (ret) {
829cf49f6adSJoseph Chen 		if (ret == -ENODEV)
83031ab5b3bSJoseph Chen 			debug("Can't find FG\n");
831cf49f6adSJoseph Chen 		else
83231ab5b3bSJoseph Chen 			debug("Get UCLASS FG failed: %d\n", ret);
833f1c8ecceSJoseph Chen 		return ret;
834f1c8ecceSJoseph Chen 	}
835f1c8ecceSJoseph Chen 
8368b436ce5SElaine Zhang 	/* Get rtc: used for power on */
837ebe3d004SJoseph Chen 	ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc);
8388b436ce5SElaine Zhang 	if (ret) {
8398b436ce5SElaine Zhang 		if (ret == -ENODEV)
8408b436ce5SElaine Zhang 			debug("Can't find RTC\n");
8418b436ce5SElaine Zhang 		else
8428b436ce5SElaine Zhang 			debug("Get UCLASS RTC failed: %d\n", ret);
8438b436ce5SElaine Zhang 	}
8448b436ce5SElaine Zhang 
84579244e4cSJoseph Chen 	/* Get PWRKEY: used for wakeup and turn off/on LCD */
84631ab5b3bSJoseph Chen 	if (key_read(KEY_POWER) == KEY_NOT_EXIST) {
84731ab5b3bSJoseph Chen 		debug("Can't find power key\n");
848f23c35a8SJoseph Chen 		return -EINVAL;
849f23c35a8SJoseph Chen 	}
850f23c35a8SJoseph Chen 
851f23c35a8SJoseph Chen 	/* Initialize charge current */
8524f9cab27SJoseph Chen 	soc = fuel_gauge_update_get_soc(priv->fg);
853f23c35a8SJoseph Chen 	if (soc < 0 || soc > 100) {
85431ab5b3bSJoseph Chen 		debug("get soc failed: %d\n", soc);
855f23c35a8SJoseph Chen 		return -EINVAL;
856f23c35a8SJoseph Chen 	}
857f23c35a8SJoseph Chen 
8581b3009deSJoseph Chen 	/* Get leds */
8591b3009deSJoseph Chen #ifdef CONFIG_LED
8601b3009deSJoseph Chen 	ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging);
8611b3009deSJoseph Chen 	if (!ret)
8621b3009deSJoseph Chen 		printf("Found Charging LED\n");
8631b3009deSJoseph Chen 	ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full);
8641b3009deSJoseph Chen 	if (!ret)
8651b3009deSJoseph Chen 		printf("Found Charging-Full LED\n");
8661b3009deSJoseph Chen #endif
8671b3009deSJoseph Chen 
868f23c35a8SJoseph Chen 	/* Get charge images */
869f1c8ecceSJoseph Chen 	priv->image = image;
870f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
871f1c8ecceSJoseph Chen 
872f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
873f1c8ecceSJoseph Chen 
874f1c8ecceSJoseph Chen 	return 0;
875f1c8ecceSJoseph Chen }
876f1c8ecceSJoseph Chen 
877f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
878d6653c12SJoseph Chen 	{ .compatible = "rockchip,uboot-charge" },
879f1c8ecceSJoseph Chen 	{ },
880f1c8ecceSJoseph Chen };
881f1c8ecceSJoseph Chen 
882f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
883f1c8ecceSJoseph Chen 	.name = "charge-animation",
884f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
885f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
886f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
887f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
888f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
889f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
890f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
891f1c8ecceSJoseph Chen };
892