xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision 8b436ce576efdf257ebd79c2b3ded29790c0aecc)
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>
14*8b436ce5SElaine Zhang #include <rtc.h>
15f1c8ecceSJoseph Chen #include <pwm.h>
16d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h>
17d3ff9cf9SKever Yang #include <asm/suspend.h>
18d3ff9cf9SKever Yang #include <linux/input.h>
19f1c8ecceSJoseph Chen #include <power/charge_display.h>
20ac1dc0c3SJoseph Chen #include <power/charge_animation.h>
2188949342SJoseph Chen #include <power/rockchip_pm.h>
22f1c8ecceSJoseph Chen #include <power/fuel_gauge.h>
23f1c8ecceSJoseph Chen #include <power/pmic.h>
24f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h>
25f1c8ecceSJoseph Chen #include <power/regulator.h>
26f1c8ecceSJoseph Chen #include <video_rockchip.h>
27175257e4SJoseph Chen #ifdef CONFIG_IRQ
28175257e4SJoseph Chen #include <irq-generic.h>
29175257e4SJoseph Chen #include <rk_timer_irq.h>
30175257e4SJoseph Chen #endif
31f1c8ecceSJoseph Chen 
32f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
33f1c8ecceSJoseph Chen 
34f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET			-1
355e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS			1000
36f1c8ecceSJoseph Chen 
37f1c8ecceSJoseph Chen struct charge_image {
38f1c8ecceSJoseph Chen 	const char *name;
39f1c8ecceSJoseph Chen 	int soc;
40f1c8ecceSJoseph Chen 	int period;	/* ms */
41f1c8ecceSJoseph Chen };
42f1c8ecceSJoseph Chen 
43f1c8ecceSJoseph Chen struct charge_animation_priv {
44f1c8ecceSJoseph Chen 	struct udevice *pmic;
45f1c8ecceSJoseph Chen 	struct udevice *fg;
46*8b436ce5SElaine Zhang 	struct udevice *rtc;
47f1c8ecceSJoseph Chen 	const struct charge_image *image;
48f1c8ecceSJoseph Chen 	int image_num;
49e7f9facbSJoseph Chen 
50e7f9facbSJoseph Chen 	int auto_wakeup_key_state;
51e7f9facbSJoseph Chen 	ulong auto_screen_off_timeout;
52f1c8ecceSJoseph Chen };
53f1c8ecceSJoseph Chen 
54f1c8ecceSJoseph Chen /*
55f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
56f1c8ecceSJoseph Chen  *
57f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
58f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
59f1c8ecceSJoseph Chen  */
60f1c8ecceSJoseph Chen static const struct charge_image image[] = {
61f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
62f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
63f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
64f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
65f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
66f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
67f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
68f1c8ecceSJoseph Chen };
69f1c8ecceSJoseph Chen 
70f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
71f1c8ecceSJoseph Chen {
72f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
73f1c8ecceSJoseph Chen 
74d6653c12SJoseph Chen 	/* charge mode */
75d6653c12SJoseph Chen 	pdata->uboot_charge =
76d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
77d6653c12SJoseph Chen 	pdata->android_charge =
78d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
79f1c8ecceSJoseph Chen 
80d6653c12SJoseph Chen 	pdata->exit_charge_level =
81d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
82d6653c12SJoseph Chen 	pdata->exit_charge_voltage =
83d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
84f23c35a8SJoseph Chen 
85f23c35a8SJoseph Chen 	pdata->low_power_voltage =
86f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0);
87f23c35a8SJoseph Chen 
88d6653c12SJoseph Chen 	pdata->screen_on_voltage =
89d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
90f23c35a8SJoseph Chen 	pdata->system_suspend =
91f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,system-suspend", 0);
92f1c8ecceSJoseph Chen 
93e7f9facbSJoseph Chen 	pdata->auto_wakeup_interval =
94e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0);
95e7f9facbSJoseph Chen 	pdata->auto_wakeup_screen_invert =
96e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0);
97e7f9facbSJoseph Chen 
98e7f9facbSJoseph Chen 	pdata->auto_off_screen_interval =
99e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15);
100e7f9facbSJoseph Chen 
101f23c35a8SJoseph Chen 	if (pdata->screen_on_voltage > pdata->exit_charge_voltage)
102f23c35a8SJoseph Chen 		pdata->screen_on_voltage = pdata->exit_charge_voltage;
103f1c8ecceSJoseph Chen 
104d6653c12SJoseph Chen 	debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
105f23c35a8SJoseph Chen 	      "lp_voltage=%d%%, screen_on=%dmv\n",
106d6653c12SJoseph Chen 	      pdata->uboot_charge, pdata->android_charge,
107d6653c12SJoseph Chen 	      pdata->exit_charge_level, pdata->exit_charge_voltage,
108f23c35a8SJoseph Chen 	      pdata->low_power_voltage, pdata->screen_on_voltage);
109f1c8ecceSJoseph Chen 
110f1c8ecceSJoseph Chen 	return 0;
111f1c8ecceSJoseph Chen }
112f1c8ecceSJoseph Chen 
113e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev)
114f1c8ecceSJoseph Chen {
115e7f9facbSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
116e7f9facbSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
117*8b436ce5SElaine Zhang 	u32 state, rtc_state = 0;
118*8b436ce5SElaine Zhang 
119*8b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC
120*8b436ce5SElaine Zhang 	if (priv->rtc)
121*8b436ce5SElaine Zhang 		rtc_state = rtc_alarm_trigger(priv->rtc);
122*8b436ce5SElaine Zhang #endif
123*8b436ce5SElaine Zhang 	if (rtc_state) {
124*8b436ce5SElaine Zhang 		printf("rtc alarm trigger...\n");
125*8b436ce5SElaine Zhang 		return KEY_PRESS_LONG_DOWN;
126*8b436ce5SElaine Zhang 	}
127f1c8ecceSJoseph Chen 
128787a62ebSJoseph Chen 	state = key_read(KEY_POWER);
129b177a917SJoseph Chen 	if (state < 0)
130f1c8ecceSJoseph Chen 		printf("read power key failed: %d\n", state);
13193aee2d9SJoseph Chen 	else if (state == KEY_PRESS_DOWN)
13293aee2d9SJoseph Chen 		printf("power key pressed...\n");
13393aee2d9SJoseph Chen 	else if (state == KEY_PRESS_LONG_DOWN)
13493aee2d9SJoseph Chen 		printf("power key long pressed...\n");
135f1c8ecceSJoseph Chen 
136e7f9facbSJoseph Chen 	/* Fixup key state for following cases */
137e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
138e7f9facbSJoseph Chen 		if  (pdata->auto_wakeup_screen_invert) {
139e7f9facbSJoseph Chen 			if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) {
140e7f9facbSJoseph Chen 				/* Value is updated in timer interrupt */
141e7f9facbSJoseph Chen 				priv->auto_wakeup_key_state = KEY_PRESS_NONE;
142e7f9facbSJoseph Chen 				state = KEY_PRESS_DOWN;
143e7f9facbSJoseph Chen 			}
144e7f9facbSJoseph Chen 		}
145e7f9facbSJoseph Chen 	} else if (pdata->auto_off_screen_interval) {
14693aee2d9SJoseph Chen 		if (priv->auto_screen_off_timeout &&
14793aee2d9SJoseph Chen 		    get_timer(priv->auto_screen_off_timeout) >
148e7f9facbSJoseph Chen 		    pdata->auto_off_screen_interval * 1000) {	/* 1000ms */
149e7f9facbSJoseph Chen 			state = KEY_PRESS_DOWN;
150e7f9facbSJoseph Chen 			printf("Auto screen off\n");
151e7f9facbSJoseph Chen 		}
152e7f9facbSJoseph Chen 	}
153e7f9facbSJoseph Chen 
154b177a917SJoseph Chen 	return state;
155f1c8ecceSJoseph Chen }
156f1c8ecceSJoseph Chen 
157175257e4SJoseph Chen /*
158175257e4SJoseph Chen  * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup
159175257e4SJoseph Chen  * period timer is useless.
160175257e4SJoseph Chen  */
161175257e4SJoseph Chen #ifndef CONFIG_IRQ
162175257e4SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata)
163175257e4SJoseph Chen {
164175257e4SJoseph Chen 	return 0;
165175257e4SJoseph Chen }
166175257e4SJoseph Chen 
167175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {}
168175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {}
169175257e4SJoseph Chen 
170175257e4SJoseph Chen #else
171f23c35a8SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata)
172b177a917SJoseph Chen {
173f23c35a8SJoseph Chen 	if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) {
174b177a917SJoseph Chen 		printf("\nSystem suspend: ");
175992f4e77SJoseph Chen 		putc('0');
176992f4e77SJoseph Chen 		regulators_enable_state_mem(false);
177b177a917SJoseph Chen 		putc('1');
178b177a917SJoseph Chen 		local_irq_disable();
179b177a917SJoseph Chen 		putc('2');
180b177a917SJoseph Chen 		irqs_suspend();
181b177a917SJoseph Chen 		putc('3');
18288949342SJoseph Chen 		device_suspend();
18388949342SJoseph Chen 		putc('4');
184b177a917SJoseph Chen 		putc('\n');
185b177a917SJoseph Chen 
186b177a917SJoseph Chen 		/* Trap into ATF for low power mode */
187b177a917SJoseph Chen 		cpu_suspend(0, psci_system_suspend);
188b177a917SJoseph Chen 
189b177a917SJoseph Chen 		putc('\n');
19088949342SJoseph Chen 		putc('4');
19188949342SJoseph Chen 		device_resume();
192b177a917SJoseph Chen 		putc('3');
193b177a917SJoseph Chen 		irqs_resume();
194b177a917SJoseph Chen 		putc('2');
195b177a917SJoseph Chen 		local_irq_enable();
196b177a917SJoseph Chen 		putc('1');
197b177a917SJoseph Chen 		putc('\n');
1988fc5ae06SJoseph Chen 	} else {
1998fc5ae06SJoseph Chen 		printf("\nWfi\n");
2008fc5ae06SJoseph Chen 		wfi();
2018fc5ae06SJoseph Chen 	}
202b177a917SJoseph Chen 
203b177a917SJoseph Chen 	/*
204b177a917SJoseph Chen 	 * We must wait for key release event finish, otherwise
205b177a917SJoseph Chen 	 * we may read key state too early.
206b177a917SJoseph Chen 	 */
207b177a917SJoseph Chen 	mdelay(300);
208b177a917SJoseph Chen 
209b177a917SJoseph Chen 	return 0;
210f1c8ecceSJoseph Chen }
21179244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data)
21279244e4cSJoseph Chen {
21379244e4cSJoseph Chen 	struct udevice *dev = data;
21479244e4cSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
21579244e4cSJoseph Chen 	static long long count;
21679244e4cSJoseph Chen 
21779244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
21879244e4cSJoseph Chen 
21979244e4cSJoseph Chen 	priv->auto_wakeup_key_state = KEY_PRESS_DOWN;
22079244e4cSJoseph Chen 	printf("auto wakeup count: %lld\n", ++count);
22179244e4cSJoseph Chen }
22279244e4cSJoseph Chen 
22379244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds)
22479244e4cSJoseph Chen {
22579244e4cSJoseph Chen 	uint64_t period = 24000000ULL * seconds;
22679244e4cSJoseph Chen 
22779244e4cSJoseph Chen 	/* Disable before conifg */
22879244e4cSJoseph Chen 	writel(0, TIMER_BASE + TIMER_CTRL);
22979244e4cSJoseph Chen 
23079244e4cSJoseph Chen 	/* Config */
23179244e4cSJoseph Chen 	writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
23279244e4cSJoseph Chen 	writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
23379244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
23479244e4cSJoseph Chen 	writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
23579244e4cSJoseph Chen 
23679244e4cSJoseph Chen 	/* IRQ */
23779244e4cSJoseph Chen 	irq_install_handler(TIMER_IRQ, timer_irq_handler, dev);
23879244e4cSJoseph Chen 	irq_handler_enable(TIMER_IRQ);
23979244e4cSJoseph Chen }
24079244e4cSJoseph Chen 
24179244e4cSJoseph Chen static void autowakeup_timer_uninit(void)
24279244e4cSJoseph Chen {
24379244e4cSJoseph Chen 	irq_free_handler(TIMER_IRQ);
24479244e4cSJoseph Chen }
245175257e4SJoseph Chen #endif
24679244e4cSJoseph Chen 
247f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP
248f23c35a8SJoseph Chen static void charge_show_bmp(const char *name)
249f23c35a8SJoseph Chen {
250f23c35a8SJoseph Chen 	rockchip_show_bmp(name);
251f23c35a8SJoseph Chen }
252f23c35a8SJoseph Chen 
253f23c35a8SJoseph Chen static void charge_show_logo(void)
254f23c35a8SJoseph Chen {
255f23c35a8SJoseph Chen 	rockchip_show_logo();
256f23c35a8SJoseph Chen }
257f23c35a8SJoseph Chen #else
258f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {}
259f23c35a8SJoseph Chen static void charge_show_logo(void) {}
260f23c35a8SJoseph Chen #endif
261f23c35a8SJoseph Chen 
262f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev)
263f23c35a8SJoseph Chen {
264f23c35a8SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
265f23c35a8SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
266f23c35a8SJoseph Chen 	struct udevice *pmic = priv->pmic;
267f23c35a8SJoseph Chen 	struct udevice *fg = priv->fg;
268f23c35a8SJoseph Chen 	int voltage, soc, charging = 1;
26979244e4cSJoseph Chen 	static int timer_initialized;
270f23c35a8SJoseph Chen 
271f23c35a8SJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
272f23c35a8SJoseph Chen 	if (voltage < 0)
273f23c35a8SJoseph Chen 		return -EINVAL;
274f23c35a8SJoseph Chen 
275f23c35a8SJoseph Chen 	while (voltage < pdata->low_power_voltage + 50) {
276f23c35a8SJoseph Chen 		/* Check charger online */
277f23c35a8SJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
278f23c35a8SJoseph Chen 		if (charging <= 0) {
27979244e4cSJoseph Chen 			printf("%s: Not charging, online=%d. Shutdown...\n",
28079244e4cSJoseph Chen 			       __func__, charging);
281f23c35a8SJoseph Chen 			/* wait uart flush before shutdown */
28279244e4cSJoseph Chen 			mdelay(5);
283f23c35a8SJoseph Chen 			/* PMIC shutdown */
284f23c35a8SJoseph Chen 			pmic_shutdown(pmic);
285f23c35a8SJoseph Chen 
286f23c35a8SJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
287f23c35a8SJoseph Chen 			continue;
288f23c35a8SJoseph Chen 		}
289f23c35a8SJoseph Chen 
29079244e4cSJoseph Chen 		/* Enable auto wakeup */
29179244e4cSJoseph Chen 		if (!timer_initialized) {
29279244e4cSJoseph Chen 			timer_initialized = 1;
29379244e4cSJoseph Chen 			autowakeup_timer_init(dev, 5);
29479244e4cSJoseph Chen 		}
29579244e4cSJoseph Chen 
296f23c35a8SJoseph Chen 		/*
297f23c35a8SJoseph Chen 		 * Just for fuel gauge to update something important,
298f23c35a8SJoseph Chen 		 * including charge current, coulometer or other.
299f23c35a8SJoseph Chen 		 */
300f23c35a8SJoseph Chen 		soc = fuel_gauge_get_soc(fg);
301f23c35a8SJoseph Chen 		if (soc < 0 || soc > 100) {
302f23c35a8SJoseph Chen 			printf("get soc failed: %d\n", soc);
303f23c35a8SJoseph Chen 			continue;
304f23c35a8SJoseph Chen 		}
305f23c35a8SJoseph Chen 
306f23c35a8SJoseph Chen 		printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
307f23c35a8SJoseph Chen 		       pdata->low_power_voltage, voltage);
308f23c35a8SJoseph Chen 
309f23c35a8SJoseph Chen 		/* System suspend */
310f23c35a8SJoseph Chen 		system_suspend_enter(pdata);
311f23c35a8SJoseph Chen 
312f23c35a8SJoseph Chen 		/* Update voltage */
313f23c35a8SJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
314f23c35a8SJoseph Chen 		if (voltage < 0) {
315f23c35a8SJoseph Chen 			printf("get voltage failed: %d\n", voltage);
316f23c35a8SJoseph Chen 			continue;
317f23c35a8SJoseph Chen 		}
318f23c35a8SJoseph Chen 	}
319f23c35a8SJoseph Chen 
32079244e4cSJoseph Chen 	autowakeup_timer_uninit();
32179244e4cSJoseph Chen 
322f23c35a8SJoseph Chen 	return 0;
323f23c35a8SJoseph Chen }
324f23c35a8SJoseph Chen 
325f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
326f1c8ecceSJoseph Chen {
327f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
328f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
329f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
330f1c8ecceSJoseph Chen 	struct udevice *pmic = priv->pmic;
331f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
332a8b9d026SJoseph Chen 	const char *preboot = env_get("preboot");
333f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
334f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
335f1c8ecceSJoseph Chen 	bool screen_on = true;
336f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
3375e804741SJoseph Chen 	ulong delta;
338f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
33993aee2d9SJoseph Chen 	int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_SHOW_RESET;
340d6653c12SJoseph Chen 	int soc, voltage, current, key_state;
341f23c35a8SJoseph Chen 	int i, charging = 1, ret;
3428f9ff705SJoseph Chen 	int boot_mode;
3435e804741SJoseph Chen 	int first_poll_fg = 1;
344f1c8ecceSJoseph Chen 
345f23c35a8SJoseph Chen /*
346f23c35a8SJoseph Chen  * Check sequence:
347f23c35a8SJoseph Chen  *
348f23c35a8SJoseph Chen  * 1. Extrem low power charge?
349f23c35a8SJoseph Chen  * 2. Preboot cmd?
350f23c35a8SJoseph Chen  * 3. Valid boot mode?
351f23c35a8SJoseph Chen  * 4. U-Boot charge enabled by dts config?
352f23c35a8SJoseph Chen  * 5. Screen off before charge?
353f23c35a8SJoseph Chen  * 6. Enter charge !
354f23c35a8SJoseph Chen  *
355f23c35a8SJoseph Chen  */
35661a7a6d6SJoseph Chen 	if (!fuel_gauge_bat_is_exist(fg)) {
35761a7a6d6SJoseph Chen 		printf("Exit charge: battery is not exist\n");
35861a7a6d6SJoseph Chen 		return 0;
35961a7a6d6SJoseph Chen 	}
36061a7a6d6SJoseph Chen 
361f23c35a8SJoseph Chen 	/* Extrem low power charge */
362f23c35a8SJoseph Chen 	ret = charge_extrem_low_power(dev);
363f23c35a8SJoseph Chen 	if (ret < 0) {
364f23c35a8SJoseph Chen 		printf("extrem low power charge failed, ret=%d\n", ret);
365f23c35a8SJoseph Chen 		return ret;
366f23c35a8SJoseph Chen 	}
367f23c35a8SJoseph Chen 
368a8b9d026SJoseph Chen 	/* If there is preboot command, exit */
36961a7a6d6SJoseph Chen 	if (preboot && !strstr(preboot, "dvfs")) {
3707ae45834SJoseph Chen 		printf("Exit charge: due to preboot cmd '%s'\n", preboot);
371a8b9d026SJoseph Chen 		return 0;
372a8b9d026SJoseph Chen 	}
373a8b9d026SJoseph Chen 
374f23c35a8SJoseph Chen 	/* Not valid charge mode, exit */
375d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
3768f9ff705SJoseph Chen 	boot_mode = rockchip_get_boot_mode();
377221b5220SJoseph Chen 	if ((boot_mode != BOOT_MODE_CHARGING) &&
378221b5220SJoseph Chen 	    (boot_mode != BOOT_MODE_UNDEFINE)) {
3797ae45834SJoseph Chen 		printf("Exit charge: due to boot mode\n");
3808f9ff705SJoseph Chen 		return 0;
3818f9ff705SJoseph Chen 	}
3828f9ff705SJoseph Chen #endif
383221b5220SJoseph Chen 
384616056c9Sshengfei Xu 	/* Not charger online, exit */
385616056c9Sshengfei Xu 	charging = fuel_gauge_get_chrg_online(fg);
386cf49f6adSJoseph Chen 	if (charging <= 0) {
3877ae45834SJoseph Chen 		printf("Exit charge: due to charger offline\n");
388616056c9Sshengfei Xu 		return 0;
389cf49f6adSJoseph Chen 	}
390616056c9Sshengfei Xu 
391f23c35a8SJoseph Chen 	/* Enter android charge, set property for kernel */
392d6653c12SJoseph Chen 	if (pdata->android_charge) {
393d6653c12SJoseph Chen 		env_update("bootargs", "androidboot.mode=charger");
394d6653c12SJoseph Chen 		printf("Android charge mode\n");
395d6653c12SJoseph Chen 	}
396d6653c12SJoseph Chen 
397f23c35a8SJoseph Chen 	/* Not enable U-Boot charge, exit */
398cf49f6adSJoseph Chen 	if (!pdata->uboot_charge) {
3997ae45834SJoseph Chen 		printf("Exit charge: due to not enable uboot charge\n");
400d6653c12SJoseph Chen 		return 0;
401cf49f6adSJoseph Chen 	}
402d6653c12SJoseph Chen 
403f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
404f1c8ecceSJoseph Chen 	if (voltage < 0) {
405f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
406f1c8ecceSJoseph Chen 		return -EINVAL;
407f1c8ecceSJoseph Chen 	}
408f1c8ecceSJoseph Chen 
409f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
410d6653c12SJoseph Chen 	if (voltage <= pdata->screen_on_voltage + 50) {
411f1c8ecceSJoseph Chen 		screen_on = false;
412f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
413f23c35a8SJoseph Chen 		charge_show_bmp(NULL);
414f1c8ecceSJoseph Chen 	}
415f1c8ecceSJoseph Chen 
416e7f9facbSJoseph Chen 	/* Auto wakeup */
417e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
418e7f9facbSJoseph Chen 		printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval);
41979244e4cSJoseph Chen 		autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
420e7f9facbSJoseph Chen 	}
421e7f9facbSJoseph Chen 
422175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */
423175257e4SJoseph Chen #ifdef CONFIG_IRQ
424d6653c12SJoseph Chen 	printf("Enter U-Boot charging mode\n");
425175257e4SJoseph Chen #else
426175257e4SJoseph Chen 	printf("Enter U-Boot charging mode(without IRQ)\n");
427175257e4SJoseph Chen #endif
428f1c8ecceSJoseph Chen 
429d6653c12SJoseph Chen 	charge_start = get_timer(0);
4305e804741SJoseph Chen 	delta = get_timer(0);
431e7f9facbSJoseph Chen 
432f1c8ecceSJoseph Chen 	/* Charging ! */
433f1c8ecceSJoseph Chen 	while (1) {
4345e804741SJoseph Chen 		/*
4355e804741SJoseph Chen 		 * At the most time, fuel gauge is usually a i2c device, we
4365e804741SJoseph Chen 		 * should avoid read/write all the time. We had better set
4375e804741SJoseph Chen 		 * poll seconds to update fuel gauge info.
4385e804741SJoseph Chen 		 */
4395e804741SJoseph Chen 		if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS)
4405e804741SJoseph Chen 			goto show_images;
4415e804741SJoseph Chen 
4425e804741SJoseph Chen 		delta = get_timer(0);
4435e804741SJoseph Chen 
444f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
445f1c8ecceSJoseph Chen 
446fd62311eSJoseph Chen 		/*
447fd62311eSJoseph Chen 		 * Most fuel gauge is I2C interface, it shouldn't be interrupted
448fd62311eSJoseph Chen 		 * during tansfer. The power key event depends on interrupt, so
449fd62311eSJoseph Chen 		 * so we should disable local irq when update fuel gauge.
450fd62311eSJoseph Chen 		 */
451fd62311eSJoseph Chen 		local_irq_disable();
452fd62311eSJoseph Chen 
453f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
454f1c8ecceSJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
455f1c8ecceSJoseph Chen 		if (charging <= 0) {
456f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
457f1c8ecceSJoseph Chen 			       charging);
458f1c8ecceSJoseph Chen 
459f1c8ecceSJoseph Chen 			/* wait uart flush before shutdown */
46079244e4cSJoseph Chen 			mdelay(5);
461f1c8ecceSJoseph Chen 
462f1c8ecceSJoseph Chen 			/* PMIC shutdown */
463f1c8ecceSJoseph Chen 			pmic_shutdown(pmic);
464f1c8ecceSJoseph Chen 
465f1c8ecceSJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
466f1c8ecceSJoseph Chen 			continue;
467f1c8ecceSJoseph Chen 		}
468f1c8ecceSJoseph Chen 
469f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
470f1c8ecceSJoseph Chen 
471f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
472f1c8ecceSJoseph Chen 		soc = fuel_gauge_get_soc(fg);
473f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
474f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
475f1c8ecceSJoseph Chen 			continue;
476f1c8ecceSJoseph Chen 		}
477f1c8ecceSJoseph Chen 
478f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
479f1c8ecceSJoseph Chen 		if (voltage < 0) {
480f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
481f1c8ecceSJoseph Chen 			continue;
482f1c8ecceSJoseph Chen 		}
483f1c8ecceSJoseph Chen 
484d6653c12SJoseph Chen 		current = fuel_gauge_get_current(fg);
485d6653c12SJoseph Chen 		if (current == -ENOSYS) {
486d6653c12SJoseph Chen 			printf("get current failed: %d\n", current);
487d6653c12SJoseph Chen 			continue;
488d6653c12SJoseph Chen 		}
4895e804741SJoseph Chen 		first_poll_fg = 0;
490fd62311eSJoseph Chen 
491fd62311eSJoseph Chen 		local_irq_enable();
492fd62311eSJoseph Chen 
4935e804741SJoseph Chen show_images:
494f1c8ecceSJoseph Chen 		/*
495f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
496f1c8ecceSJoseph Chen 		 * is not good to know what happen.
497f1c8ecceSJoseph Chen 		 */
498f1c8ecceSJoseph Chen 		if (!debug_start)
499f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
500f1c8ecceSJoseph Chen 		if (get_timer(debug_start) > 20000) {
501f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
502d6653c12SJoseph Chen 			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n",
503d6653c12SJoseph Chen 			       get_timer(0)/1000, soc, voltage,
504d6653c12SJoseph Chen 			       current, charging, screen_on);
505f1c8ecceSJoseph Chen 		}
506f1c8ecceSJoseph Chen 
507f1c8ecceSJoseph Chen 		/*
508f23c35a8SJoseph Chen 		 * If ever lowpower screen off, force screen_on=false, which
509f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
510f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
511f1c8ecceSJoseph Chen 		 */
512f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
513f1c8ecceSJoseph Chen 			screen_on = false;
514f1c8ecceSJoseph Chen 
515f1c8ecceSJoseph Chen 		/*
516f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
517f1c8ecceSJoseph Chen 		 * 'ever_lowpower_screen_off' means enter while loop with
518f1c8ecceSJoseph Chen 		 * screen off.
519f1c8ecceSJoseph Chen 		 */
520f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
521d6653c12SJoseph Chen 		    (voltage > pdata->screen_on_voltage)) {
522f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
523f1c8ecceSJoseph Chen 			screen_on = true;
524f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
525f1c8ecceSJoseph Chen 		}
526f1c8ecceSJoseph Chen 
527f1c8ecceSJoseph Chen 		/*
528f1c8ecceSJoseph Chen 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
529f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
530f1c8ecceSJoseph Chen 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
531f1c8ecceSJoseph Chen 		 * that calculate by current soc.
532f1c8ecceSJoseph Chen 		 */
533f1c8ecceSJoseph Chen 		if (show_idx == IMAGE_SHOW_RESET) {
534f1c8ecceSJoseph Chen 			for (i = 0; i < image_num - 2; i++) {
535f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
536f1c8ecceSJoseph Chen 				if ((soc >= image[i].soc) &&
537f1c8ecceSJoseph Chen 				    (soc < image[i + 1].soc)) {
538f1c8ecceSJoseph Chen 					start_idx = i;
539f1c8ecceSJoseph Chen 					break;
540f1c8ecceSJoseph Chen 				}
541f1c8ecceSJoseph Chen 
542f1c8ecceSJoseph Chen 				if (soc >= 100) {
543f1c8ecceSJoseph Chen 					start_idx = image_num - 2;
544f1c8ecceSJoseph Chen 					break;
545f1c8ecceSJoseph Chen 				}
546f1c8ecceSJoseph Chen 			}
547f1c8ecceSJoseph Chen 
548f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
549f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
550f1c8ecceSJoseph Chen 
551f1c8ecceSJoseph Chen 			/* Mark start index and start time */
552f1c8ecceSJoseph Chen 			show_idx = start_idx;
553f1c8ecceSJoseph Chen 			show_start = get_timer(0);
554f1c8ecceSJoseph Chen 		}
555f1c8ecceSJoseph Chen 
556f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
557f1c8ecceSJoseph Chen 
558f1c8ecceSJoseph Chen 		/* Step3: show images */
559f1c8ecceSJoseph Chen 		if (screen_on) {
56093aee2d9SJoseph Chen 			/* Don't call 'charge_show_bmp' unless image changed */
56193aee2d9SJoseph Chen 			if (old_show_idx != show_idx) {
56293aee2d9SJoseph Chen 				old_show_idx = show_idx;
563f1c8ecceSJoseph Chen 				debug("SHOW: %s\n", image[show_idx].name);
564f23c35a8SJoseph Chen 				charge_show_bmp(image[show_idx].name);
56593aee2d9SJoseph Chen 			}
566e7f9facbSJoseph Chen 			/* Re calculate timeout to off screen */
567e7f9facbSJoseph Chen 			if (priv->auto_screen_off_timeout == 0)
568e7f9facbSJoseph Chen 				priv->auto_screen_off_timeout = get_timer(0);
569f1c8ecceSJoseph Chen 		} else {
570e7f9facbSJoseph Chen 			priv->auto_screen_off_timeout = 0;
571e7f9facbSJoseph Chen 
572f23c35a8SJoseph Chen 			system_suspend_enter(pdata);
573f1c8ecceSJoseph Chen 		}
574f1c8ecceSJoseph Chen 
575f1c8ecceSJoseph Chen 		mdelay(5);
576f1c8ecceSJoseph Chen 
577f1c8ecceSJoseph Chen 		/* Every image shows period */
578f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
579f1c8ecceSJoseph Chen 			show_start = get_timer(0);
580f1c8ecceSJoseph Chen 			/* Update to next image */
581f1c8ecceSJoseph Chen 			show_idx++;
582f1c8ecceSJoseph Chen 			if (show_idx > (image_num - 2))
583f1c8ecceSJoseph Chen 				show_idx = IMAGE_SHOW_RESET;
584f1c8ecceSJoseph Chen 		}
585f1c8ecceSJoseph Chen 
586f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
587f1c8ecceSJoseph Chen 
588f1c8ecceSJoseph Chen 		/*
589f1c8ecceSJoseph Chen 		 * Step4: check key event.
590f1c8ecceSJoseph Chen 		 *
591f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
592f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
593f1c8ecceSJoseph Chen 		 */
594e7f9facbSJoseph Chen 		key_state = check_key_press(dev);
595b177a917SJoseph Chen 		if (key_state == KEY_PRESS_DOWN) {
59693aee2d9SJoseph Chen 			old_show_idx = IMAGE_SHOW_RESET;
59793aee2d9SJoseph Chen 
598f1c8ecceSJoseph Chen 			/* NULL means show nothing, ie. turn off screen */
599f1c8ecceSJoseph Chen 			if (screen_on)
600f23c35a8SJoseph Chen 				charge_show_bmp(NULL);
601f1c8ecceSJoseph Chen 
602f1c8ecceSJoseph Chen 			/*
603f1c8ecceSJoseph Chen 			 * Clear current image index, and show image
604f1c8ecceSJoseph Chen 			 * from start_idx
605f1c8ecceSJoseph Chen 			 */
606f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
607f1c8ecceSJoseph Chen 
608f1c8ecceSJoseph Chen 			/*
609f23c35a8SJoseph Chen 			 * We turn off screen by charge_show_bmp(NULL), so we
610f1c8ecceSJoseph Chen 			 * should tell while loop to stop show images any more.
611f1c8ecceSJoseph Chen 			 *
612f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
613f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
614f1c8ecceSJoseph Chen 			 *
615f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
616f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
617f1c8ecceSJoseph Chen 			 */
618f1c8ecceSJoseph Chen 			if (screen_on)
619f1c8ecceSJoseph Chen 				screen_on = false;
620f1c8ecceSJoseph Chen 			else
621f1c8ecceSJoseph Chen 				screen_on = true;
622b177a917SJoseph Chen 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
623f1c8ecceSJoseph Chen 			/* Only long pressed while screen off needs screen_on true */
624f1c8ecceSJoseph Chen 			if (!screen_on)
625f1c8ecceSJoseph Chen 				screen_on = true;
626f1c8ecceSJoseph Chen 
627f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
628d6653c12SJoseph Chen 			if (soc < pdata->exit_charge_level) {
629f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
630d6653c12SJoseph Chen 				       soc, pdata->exit_charge_level);
631f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
632f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
633f1c8ecceSJoseph Chen 				continue;
634f1c8ecceSJoseph Chen 			}
635f1c8ecceSJoseph Chen 
636d6653c12SJoseph Chen 			if (voltage < pdata->exit_charge_voltage) {
637f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
638d6653c12SJoseph Chen 				       voltage, pdata->exit_charge_voltage);
639f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
640f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
641f1c8ecceSJoseph Chen 				continue;
642f1c8ecceSJoseph Chen 			}
643f1c8ecceSJoseph Chen 
644f1c8ecceSJoseph Chen 			/* Success exit charging */
645f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
646f23c35a8SJoseph Chen 			charge_show_logo();
647f1c8ecceSJoseph Chen 			break;
648f1c8ecceSJoseph Chen 		} else {
649f1c8ecceSJoseph Chen 			/* Do nothing */
650f1c8ecceSJoseph Chen 		}
651f1c8ecceSJoseph Chen 
652f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
653f1c8ecceSJoseph Chen 
654d6653c12SJoseph Chen 		/* Step5: Exit by ctrl+c */
6551367bfe3SJoseph Chen 		if (ctrlc()) {
656d6653c12SJoseph Chen 			if (voltage >= pdata->screen_on_voltage)
657f23c35a8SJoseph Chen 				charge_show_logo();
6581367bfe3SJoseph Chen 			printf("Exit charge, due to ctrl+c\n");
6591367bfe3SJoseph Chen 			break;
6601367bfe3SJoseph Chen 		}
661f1c8ecceSJoseph Chen 	}
662f1c8ecceSJoseph Chen 
663e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval)
664e7f9facbSJoseph Chen 		autowakeup_timer_uninit();
665e7f9facbSJoseph Chen 
666f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
667f1c8ecceSJoseph Chen 	if (ms >= 1000) {
668f1c8ecceSJoseph Chen 		sec = ms / 1000;
669f1c8ecceSJoseph Chen 		ms = ms % 1000;
670f1c8ecceSJoseph Chen 	}
671f1c8ecceSJoseph Chen 
672f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
673f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
674f1c8ecceSJoseph Chen 
675f1c8ecceSJoseph Chen 	return 0;
676f1c8ecceSJoseph Chen }
677f1c8ecceSJoseph Chen 
678f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
679f1c8ecceSJoseph Chen 	.show = charge_animation_show,
680f1c8ecceSJoseph Chen };
681f1c8ecceSJoseph Chen 
682f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
683f1c8ecceSJoseph Chen {
684f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
685*8b436ce5SElaine Zhang 	struct udevice *fg, *pmic, *rtc;
686f23c35a8SJoseph Chen 	int ret, soc;
687f1c8ecceSJoseph Chen 
688f23c35a8SJoseph Chen 	/* Get PMIC: used for power off system  */
689f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
690f1c8ecceSJoseph Chen 	if (ret) {
691cf49f6adSJoseph Chen 		if (ret == -ENODEV)
692cf49f6adSJoseph Chen 			printf("Can't find PMIC\n");
693cf49f6adSJoseph Chen 		else
694f1c8ecceSJoseph Chen 			printf("Get UCLASS PMIC failed: %d\n", ret);
695f1c8ecceSJoseph Chen 		return ret;
696f1c8ecceSJoseph Chen 	}
697f1c8ecceSJoseph Chen 	priv->pmic = pmic;
698f1c8ecceSJoseph Chen 
699f23c35a8SJoseph Chen 	/* Get fuel gauge: used for charging */
700f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
701f1c8ecceSJoseph Chen 	if (ret) {
702cf49f6adSJoseph Chen 		if (ret == -ENODEV)
70331ab5b3bSJoseph Chen 			debug("Can't find FG\n");
704cf49f6adSJoseph Chen 		else
70531ab5b3bSJoseph Chen 			debug("Get UCLASS FG failed: %d\n", ret);
706f1c8ecceSJoseph Chen 		return ret;
707f1c8ecceSJoseph Chen 	}
708f1c8ecceSJoseph Chen 	priv->fg = fg;
709f1c8ecceSJoseph Chen 
710*8b436ce5SElaine Zhang 	/* Get rtc: used for power on */
711*8b436ce5SElaine Zhang 	ret = uclass_get_device(UCLASS_RTC, 0, &rtc);
712*8b436ce5SElaine Zhang 	if (ret) {
713*8b436ce5SElaine Zhang 		if (ret == -ENODEV)
714*8b436ce5SElaine Zhang 			debug("Can't find RTC\n");
715*8b436ce5SElaine Zhang 		else
716*8b436ce5SElaine Zhang 			debug("Get UCLASS RTC failed: %d\n", ret);
717*8b436ce5SElaine Zhang 	}
718*8b436ce5SElaine Zhang 	priv->rtc = rtc;
719*8b436ce5SElaine Zhang 
72079244e4cSJoseph Chen 	/* Get PWRKEY: used for wakeup and turn off/on LCD */
72131ab5b3bSJoseph Chen 	if (key_read(KEY_POWER) == KEY_NOT_EXIST) {
72231ab5b3bSJoseph Chen 		debug("Can't find power key\n");
723f23c35a8SJoseph Chen 		return -EINVAL;
724f23c35a8SJoseph Chen 	}
725f23c35a8SJoseph Chen 
726f23c35a8SJoseph Chen 	/* Initialize charge current */
727f23c35a8SJoseph Chen 	soc = fuel_gauge_get_soc(fg);
728f23c35a8SJoseph Chen 	if (soc < 0 || soc > 100) {
72931ab5b3bSJoseph Chen 		debug("get soc failed: %d\n", soc);
730f23c35a8SJoseph Chen 		return -EINVAL;
731f23c35a8SJoseph Chen 	}
732f23c35a8SJoseph Chen 
733f23c35a8SJoseph Chen 	/* Get charge images */
734f1c8ecceSJoseph Chen 	priv->image = image;
735f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
736f1c8ecceSJoseph Chen 
737f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
738f1c8ecceSJoseph Chen 
739f1c8ecceSJoseph Chen 	return 0;
740f1c8ecceSJoseph Chen }
741f1c8ecceSJoseph Chen 
742f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
743d6653c12SJoseph Chen 	{ .compatible = "rockchip,uboot-charge" },
744f1c8ecceSJoseph Chen 	{ },
745f1c8ecceSJoseph Chen };
746f1c8ecceSJoseph Chen 
747f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
748f1c8ecceSJoseph Chen 	.name = "charge-animation",
749f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
750f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
751f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
752f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
753f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
754f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
755f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
756f1c8ecceSJoseph Chen };
757