xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision cf49f6adf1fb0dfee3536a201ec79e371ac594b8)
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>
14f1c8ecceSJoseph Chen #include <pwm.h>
15d3ff9cf9SKever Yang #include <irq-generic.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>
26e7f9facbSJoseph Chen #include <rk_timer_irq.h>
27f1c8ecceSJoseph Chen #include <video_rockchip.h>
28f1c8ecceSJoseph Chen 
29f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
30f1c8ecceSJoseph Chen 
31f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET			-1
32f1c8ecceSJoseph Chen 
33f1c8ecceSJoseph Chen struct charge_image {
34f1c8ecceSJoseph Chen 	const char *name;
35f1c8ecceSJoseph Chen 	int soc;
36f1c8ecceSJoseph Chen 	int period;	/* ms */
37f1c8ecceSJoseph Chen };
38f1c8ecceSJoseph Chen 
39f1c8ecceSJoseph Chen struct charge_animation_priv {
40f1c8ecceSJoseph Chen 	struct udevice *pmic;
41f1c8ecceSJoseph Chen 	struct udevice *fg;
42f1c8ecceSJoseph Chen 	const struct charge_image *image;
43f1c8ecceSJoseph Chen 	int image_num;
44e7f9facbSJoseph Chen 
45e7f9facbSJoseph Chen 	int auto_wakeup_key_state;
46e7f9facbSJoseph Chen 	ulong auto_screen_off_timeout;
47f1c8ecceSJoseph Chen };
48f1c8ecceSJoseph Chen 
49f1c8ecceSJoseph Chen /*
50f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
51f1c8ecceSJoseph Chen  *
52f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
53f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
54f1c8ecceSJoseph Chen  */
55f1c8ecceSJoseph Chen static const struct charge_image image[] = {
56f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
57f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
58f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
59f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
60f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
61f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
62f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
63f1c8ecceSJoseph Chen };
64f1c8ecceSJoseph Chen 
65f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
66f1c8ecceSJoseph Chen {
67f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
68f1c8ecceSJoseph Chen 
69d6653c12SJoseph Chen 	/* charge mode */
70d6653c12SJoseph Chen 	pdata->uboot_charge =
71d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
72d6653c12SJoseph Chen 	pdata->android_charge =
73d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
74f1c8ecceSJoseph Chen 
75d6653c12SJoseph Chen 	pdata->exit_charge_level =
76d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
77d6653c12SJoseph Chen 	pdata->exit_charge_voltage =
78d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
79f23c35a8SJoseph Chen 
80f23c35a8SJoseph Chen 	pdata->low_power_voltage =
81f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0);
82f23c35a8SJoseph Chen 
83d6653c12SJoseph Chen 	pdata->screen_on_voltage =
84d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
85f23c35a8SJoseph Chen 	pdata->system_suspend =
86f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,system-suspend", 0);
87f1c8ecceSJoseph Chen 
88e7f9facbSJoseph Chen 	pdata->auto_wakeup_interval =
89e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0);
90e7f9facbSJoseph Chen 	pdata->auto_wakeup_screen_invert =
91e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0);
92e7f9facbSJoseph Chen 
93e7f9facbSJoseph Chen 	pdata->auto_off_screen_interval =
94e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15);
95e7f9facbSJoseph Chen 
96f23c35a8SJoseph Chen 	if (pdata->screen_on_voltage > pdata->exit_charge_voltage)
97f23c35a8SJoseph Chen 		pdata->screen_on_voltage = pdata->exit_charge_voltage;
98f1c8ecceSJoseph Chen 
99d6653c12SJoseph Chen 	debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
100f23c35a8SJoseph Chen 	      "lp_voltage=%d%%, screen_on=%dmv\n",
101d6653c12SJoseph Chen 	      pdata->uboot_charge, pdata->android_charge,
102d6653c12SJoseph Chen 	      pdata->exit_charge_level, pdata->exit_charge_voltage,
103f23c35a8SJoseph Chen 	      pdata->low_power_voltage, pdata->screen_on_voltage);
104f1c8ecceSJoseph Chen 
105f1c8ecceSJoseph Chen 	return 0;
106f1c8ecceSJoseph Chen }
107f1c8ecceSJoseph Chen 
108e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev)
109f1c8ecceSJoseph Chen {
110e7f9facbSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
111e7f9facbSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
112b177a917SJoseph Chen 	u32 state;
113f1c8ecceSJoseph Chen 
114d4e1125bSJoseph Chen 	state = platform_key_read(KEY_POWER);
115b177a917SJoseph Chen 	if (state < 0)
116f1c8ecceSJoseph Chen 		printf("read power key failed: %d\n", state);
117f1c8ecceSJoseph Chen 
118e7f9facbSJoseph Chen 	/* Fixup key state for following cases */
119e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
120e7f9facbSJoseph Chen 		if  (pdata->auto_wakeup_screen_invert) {
121e7f9facbSJoseph Chen 			if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) {
122e7f9facbSJoseph Chen 				/* Value is updated in timer interrupt */
123e7f9facbSJoseph Chen 				priv->auto_wakeup_key_state = KEY_PRESS_NONE;
124e7f9facbSJoseph Chen 				state = KEY_PRESS_DOWN;
125e7f9facbSJoseph Chen 			}
126e7f9facbSJoseph Chen 		}
127e7f9facbSJoseph Chen 	} else if (pdata->auto_off_screen_interval) {
128e7f9facbSJoseph Chen 		if (get_timer(priv->auto_screen_off_timeout) >
129e7f9facbSJoseph Chen 		    pdata->auto_off_screen_interval * 1000) {	/* 1000ms */
130e7f9facbSJoseph Chen 			state = KEY_PRESS_DOWN;
131e7f9facbSJoseph Chen 			printf("Auto screen off\n");
132e7f9facbSJoseph Chen 		}
133e7f9facbSJoseph Chen 	}
134e7f9facbSJoseph Chen 
135b177a917SJoseph Chen 	return state;
136f1c8ecceSJoseph Chen }
137f1c8ecceSJoseph Chen 
138f23c35a8SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata)
139b177a917SJoseph Chen {
140b177a917SJoseph Chen 	/*
141b177a917SJoseph Chen 	 * TODO: enter low power mode:
142b177a917SJoseph Chen 	 * 3. auto turn off screen when timout;
143b177a917SJoseph Chen 	 * 4. power key wakeup;
144f23c35a8SJoseph Chen 	 * 5. timer period wakeup for pmic fg
145b177a917SJoseph Chen 	 */
146f23c35a8SJoseph Chen 	if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) {
147b177a917SJoseph Chen 		printf("\nSystem suspend: ");
148b177a917SJoseph Chen 		putc('1');
149b177a917SJoseph Chen 		local_irq_disable();
150b177a917SJoseph Chen 		putc('2');
151b177a917SJoseph Chen 		irqs_suspend();
152b177a917SJoseph Chen 		putc('3');
15388949342SJoseph Chen 		device_suspend();
15488949342SJoseph Chen 		putc('4');
155b177a917SJoseph Chen 		putc('\n');
156b177a917SJoseph Chen 
157b177a917SJoseph Chen 		/* Trap into ATF for low power mode */
158b177a917SJoseph Chen 		cpu_suspend(0, psci_system_suspend);
159b177a917SJoseph Chen 
160b177a917SJoseph Chen 		putc('\n');
16188949342SJoseph Chen 		putc('4');
16288949342SJoseph Chen 		device_resume();
163b177a917SJoseph Chen 		putc('3');
164b177a917SJoseph Chen 		irqs_resume();
165b177a917SJoseph Chen 		putc('2');
166b177a917SJoseph Chen 		local_irq_enable();
167b177a917SJoseph Chen 		putc('1');
168b177a917SJoseph Chen 		putc('\n');
1698fc5ae06SJoseph Chen 	} else {
1708fc5ae06SJoseph Chen 		printf("\nWfi\n");
1718fc5ae06SJoseph Chen 		wfi();
1728fc5ae06SJoseph Chen 	}
173b177a917SJoseph Chen 
174b177a917SJoseph Chen 	/*
175b177a917SJoseph Chen 	 * We must wait for key release event finish, otherwise
176b177a917SJoseph Chen 	 * we may read key state too early.
177b177a917SJoseph Chen 	 */
178b177a917SJoseph Chen 	mdelay(300);
179b177a917SJoseph Chen 
180b177a917SJoseph Chen 	return 0;
181f1c8ecceSJoseph Chen }
182f1c8ecceSJoseph Chen 
18379244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data)
18479244e4cSJoseph Chen {
18579244e4cSJoseph Chen 	struct udevice *dev = data;
18679244e4cSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
18779244e4cSJoseph Chen 	static long long count;
18879244e4cSJoseph Chen 
18979244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
19079244e4cSJoseph Chen 
19179244e4cSJoseph Chen 	priv->auto_wakeup_key_state = KEY_PRESS_DOWN;
19279244e4cSJoseph Chen 	printf("auto wakeup count: %lld\n", ++count);
19379244e4cSJoseph Chen }
19479244e4cSJoseph Chen 
19579244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds)
19679244e4cSJoseph Chen {
19779244e4cSJoseph Chen 	uint64_t period = 24000000ULL * seconds;
19879244e4cSJoseph Chen 
19979244e4cSJoseph Chen 	/* Disable before conifg */
20079244e4cSJoseph Chen 	writel(0, TIMER_BASE + TIMER_CTRL);
20179244e4cSJoseph Chen 
20279244e4cSJoseph Chen 	/* Config */
20379244e4cSJoseph Chen 	writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
20479244e4cSJoseph Chen 	writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
20579244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
20679244e4cSJoseph Chen 	writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
20779244e4cSJoseph Chen 
20879244e4cSJoseph Chen 	/* IRQ */
20979244e4cSJoseph Chen 	irq_install_handler(TIMER_IRQ, timer_irq_handler, dev);
21079244e4cSJoseph Chen 	irq_handler_enable(TIMER_IRQ);
21179244e4cSJoseph Chen }
21279244e4cSJoseph Chen 
21379244e4cSJoseph Chen static void autowakeup_timer_uninit(void)
21479244e4cSJoseph Chen {
21579244e4cSJoseph Chen 	irq_free_handler(TIMER_IRQ);
21679244e4cSJoseph Chen }
21779244e4cSJoseph Chen 
218f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP
219f23c35a8SJoseph Chen static void charge_show_bmp(const char *name)
220f23c35a8SJoseph Chen {
221f23c35a8SJoseph Chen 	rockchip_show_bmp(name);
222f23c35a8SJoseph Chen }
223f23c35a8SJoseph Chen 
224f23c35a8SJoseph Chen static void charge_show_logo(void)
225f23c35a8SJoseph Chen {
226f23c35a8SJoseph Chen 	rockchip_show_logo();
227f23c35a8SJoseph Chen }
228f23c35a8SJoseph Chen #else
229f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {}
230f23c35a8SJoseph Chen static void charge_show_logo(void) {}
231f23c35a8SJoseph Chen #endif
232f23c35a8SJoseph Chen 
233f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev)
234f23c35a8SJoseph Chen {
235f23c35a8SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
236f23c35a8SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
237f23c35a8SJoseph Chen 	struct udevice *pmic = priv->pmic;
238f23c35a8SJoseph Chen 	struct udevice *fg = priv->fg;
239f23c35a8SJoseph Chen 	int voltage, soc, charging = 1;
24079244e4cSJoseph Chen 	static int timer_initialized;
241f23c35a8SJoseph Chen 
242f23c35a8SJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
243f23c35a8SJoseph Chen 	if (voltage < 0)
244f23c35a8SJoseph Chen 		return -EINVAL;
245f23c35a8SJoseph Chen 
246f23c35a8SJoseph Chen 	while (voltage < pdata->low_power_voltage + 50) {
247f23c35a8SJoseph Chen 		/* Check charger online */
248f23c35a8SJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
249f23c35a8SJoseph Chen 		if (charging <= 0) {
25079244e4cSJoseph Chen 			printf("%s: Not charging, online=%d. Shutdown...\n",
25179244e4cSJoseph Chen 			       __func__, charging);
252f23c35a8SJoseph Chen 			/* wait uart flush before shutdown */
25379244e4cSJoseph Chen 			mdelay(5);
254f23c35a8SJoseph Chen 			/* PMIC shutdown */
255f23c35a8SJoseph Chen 			pmic_shutdown(pmic);
256f23c35a8SJoseph Chen 
257f23c35a8SJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
258f23c35a8SJoseph Chen 			continue;
259f23c35a8SJoseph Chen 		}
260f23c35a8SJoseph Chen 
26179244e4cSJoseph Chen 		/* Enable auto wakeup */
26279244e4cSJoseph Chen 		if (!timer_initialized) {
26379244e4cSJoseph Chen 			timer_initialized = 1;
26479244e4cSJoseph Chen 			autowakeup_timer_init(dev, 5);
26579244e4cSJoseph Chen 		}
26679244e4cSJoseph Chen 
267f23c35a8SJoseph Chen 		/*
268f23c35a8SJoseph Chen 		 * Just for fuel gauge to update something important,
269f23c35a8SJoseph Chen 		 * including charge current, coulometer or other.
270f23c35a8SJoseph Chen 		 */
271f23c35a8SJoseph Chen 		soc = fuel_gauge_get_soc(fg);
272f23c35a8SJoseph Chen 		if (soc < 0 || soc > 100) {
273f23c35a8SJoseph Chen 			printf("get soc failed: %d\n", soc);
274f23c35a8SJoseph Chen 			continue;
275f23c35a8SJoseph Chen 		}
276f23c35a8SJoseph Chen 
277f23c35a8SJoseph Chen 		printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
278f23c35a8SJoseph Chen 		       pdata->low_power_voltage, voltage);
279f23c35a8SJoseph Chen 
280f23c35a8SJoseph Chen 		/* System suspend */
281f23c35a8SJoseph Chen 		system_suspend_enter(pdata);
282f23c35a8SJoseph Chen 
283f23c35a8SJoseph Chen 		/* Update voltage */
284f23c35a8SJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
285f23c35a8SJoseph Chen 		if (voltage < 0) {
286f23c35a8SJoseph Chen 			printf("get voltage failed: %d\n", voltage);
287f23c35a8SJoseph Chen 			continue;
288f23c35a8SJoseph Chen 		}
289f23c35a8SJoseph Chen 	}
290f23c35a8SJoseph Chen 
29179244e4cSJoseph Chen 	autowakeup_timer_uninit();
29279244e4cSJoseph Chen 
293f23c35a8SJoseph Chen 	return 0;
294f23c35a8SJoseph Chen }
295f23c35a8SJoseph Chen 
296f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
297f1c8ecceSJoseph Chen {
298f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
299f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
300f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
301f1c8ecceSJoseph Chen 	struct udevice *pmic = priv->pmic;
302f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
303a8b9d026SJoseph Chen 	const char *preboot = env_get("preboot");
304f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
305f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
306f1c8ecceSJoseph Chen 	bool screen_on = true;
307f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
308f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
309f1c8ecceSJoseph Chen 	int start_idx = 0, show_idx = -1;
310d6653c12SJoseph Chen 	int soc, voltage, current, key_state;
311f23c35a8SJoseph Chen 	int i, charging = 1, ret;
3128f9ff705SJoseph Chen 	int boot_mode;
313f1c8ecceSJoseph Chen 
314f23c35a8SJoseph Chen /*
315f23c35a8SJoseph Chen  * Check sequence:
316f23c35a8SJoseph Chen  *
317f23c35a8SJoseph Chen  * 1. Extrem low power charge?
318f23c35a8SJoseph Chen  * 2. Preboot cmd?
319f23c35a8SJoseph Chen  * 3. Valid boot mode?
320f23c35a8SJoseph Chen  * 4. U-Boot charge enabled by dts config?
321f23c35a8SJoseph Chen  * 5. Screen off before charge?
322f23c35a8SJoseph Chen  * 6. Enter charge !
323f23c35a8SJoseph Chen  *
324f23c35a8SJoseph Chen  */
325f23c35a8SJoseph Chen 	/* Extrem low power charge */
326f23c35a8SJoseph Chen 	ret = charge_extrem_low_power(dev);
327f23c35a8SJoseph Chen 	if (ret < 0) {
328f23c35a8SJoseph Chen 		printf("extrem low power charge failed, ret=%d\n", ret);
329f23c35a8SJoseph Chen 		return ret;
330f23c35a8SJoseph Chen 	}
331f23c35a8SJoseph Chen 
332a8b9d026SJoseph Chen 	/* If there is preboot command, exit */
333a8b9d026SJoseph Chen 	if (preboot) {
334*cf49f6adSJoseph Chen 		debug("exit charge, due to preboot: %s\n", preboot);
335a8b9d026SJoseph Chen 		return 0;
336a8b9d026SJoseph Chen 	}
337a8b9d026SJoseph Chen 
338f23c35a8SJoseph Chen 	/* Not valid charge mode, exit */
339d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
3408f9ff705SJoseph Chen 	boot_mode = rockchip_get_boot_mode();
341221b5220SJoseph Chen 	if ((boot_mode != BOOT_MODE_CHARGING) &&
342221b5220SJoseph Chen 	    (boot_mode != BOOT_MODE_UNDEFINE)) {
3438f9ff705SJoseph Chen 		debug("exit charge, due to boot mode: %d\n", boot_mode);
3448f9ff705SJoseph Chen 		return 0;
3458f9ff705SJoseph Chen 	}
3468f9ff705SJoseph Chen #endif
347221b5220SJoseph Chen 
348616056c9Sshengfei Xu 	/* Not charger online, exit */
349616056c9Sshengfei Xu 	charging = fuel_gauge_get_chrg_online(fg);
350*cf49f6adSJoseph Chen 	if (charging <= 0) {
351*cf49f6adSJoseph Chen 		debug("exit charge, due to charger offline\n");
352616056c9Sshengfei Xu 		return 0;
353*cf49f6adSJoseph Chen 	}
354616056c9Sshengfei Xu 
355f23c35a8SJoseph Chen 	/* Enter android charge, set property for kernel */
356d6653c12SJoseph Chen 	if (pdata->android_charge) {
357d6653c12SJoseph Chen 		env_update("bootargs", "androidboot.mode=charger");
358d6653c12SJoseph Chen 		printf("Android charge mode\n");
359d6653c12SJoseph Chen 	}
360d6653c12SJoseph Chen 
361f23c35a8SJoseph Chen 	/* Not enable U-Boot charge, exit */
362*cf49f6adSJoseph Chen 	if (!pdata->uboot_charge) {
363*cf49f6adSJoseph Chen 		debug("exit charge, due to not enable uboot charge\n");
364d6653c12SJoseph Chen 		return 0;
365*cf49f6adSJoseph Chen 	}
366d6653c12SJoseph Chen 
367f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
368f1c8ecceSJoseph Chen 	if (voltage < 0) {
369f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
370f1c8ecceSJoseph Chen 		return -EINVAL;
371f1c8ecceSJoseph Chen 	}
372f1c8ecceSJoseph Chen 
373f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
374d6653c12SJoseph Chen 	if (voltage <= pdata->screen_on_voltage + 50) {
375f1c8ecceSJoseph Chen 		screen_on = false;
376f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
377f23c35a8SJoseph Chen 		charge_show_bmp(NULL);
378f1c8ecceSJoseph Chen 	}
379f1c8ecceSJoseph Chen 
380e7f9facbSJoseph Chen 	/* Auto wakeup */
381e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
382e7f9facbSJoseph Chen 		printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval);
38379244e4cSJoseph Chen 		autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
384e7f9facbSJoseph Chen 	}
385e7f9facbSJoseph Chen 
386d6653c12SJoseph Chen 	printf("Enter U-Boot charging mode\n");
387f1c8ecceSJoseph Chen 
388d6653c12SJoseph Chen 	charge_start = get_timer(0);
389e7f9facbSJoseph Chen 
390f1c8ecceSJoseph Chen 	/* Charging ! */
391f1c8ecceSJoseph Chen 	while (1) {
392f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
393f1c8ecceSJoseph Chen 
394f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
395f1c8ecceSJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
396f1c8ecceSJoseph Chen 		if (charging <= 0) {
397f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
398f1c8ecceSJoseph Chen 			       charging);
399f1c8ecceSJoseph Chen 
400f1c8ecceSJoseph Chen 			/* wait uart flush before shutdown */
40179244e4cSJoseph Chen 			mdelay(5);
402f1c8ecceSJoseph Chen 
403f1c8ecceSJoseph Chen 			/* PMIC shutdown */
404f1c8ecceSJoseph Chen 			pmic_shutdown(pmic);
405f1c8ecceSJoseph Chen 
406f1c8ecceSJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
407f1c8ecceSJoseph Chen 			continue;
408f1c8ecceSJoseph Chen 		}
409f1c8ecceSJoseph Chen 
410f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
411f1c8ecceSJoseph Chen 
412f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
413f1c8ecceSJoseph Chen 		soc = fuel_gauge_get_soc(fg);
414f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
415f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
416f1c8ecceSJoseph Chen 			continue;
417f1c8ecceSJoseph Chen 		}
418f1c8ecceSJoseph Chen 
419f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
420f1c8ecceSJoseph Chen 		if (voltage < 0) {
421f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
422f1c8ecceSJoseph Chen 			continue;
423f1c8ecceSJoseph Chen 		}
424f1c8ecceSJoseph Chen 
425d6653c12SJoseph Chen 		current = fuel_gauge_get_current(fg);
426d6653c12SJoseph Chen 		if (current == -ENOSYS) {
427d6653c12SJoseph Chen 			printf("get current failed: %d\n", current);
428d6653c12SJoseph Chen 			continue;
429d6653c12SJoseph Chen 		}
430d6653c12SJoseph Chen 
431f1c8ecceSJoseph Chen 		/*
432f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
433f1c8ecceSJoseph Chen 		 * is not good to know what happen.
434f1c8ecceSJoseph Chen 		 */
435f1c8ecceSJoseph Chen 		if (!debug_start)
436f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
437f1c8ecceSJoseph Chen 		if (get_timer(debug_start) > 20000) {
438f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
439d6653c12SJoseph Chen 			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n",
440d6653c12SJoseph Chen 			       get_timer(0)/1000, soc, voltage,
441d6653c12SJoseph Chen 			       current, charging, screen_on);
442f1c8ecceSJoseph Chen 		}
443f1c8ecceSJoseph Chen 
444f1c8ecceSJoseph Chen 		/*
445f23c35a8SJoseph Chen 		 * If ever lowpower screen off, force screen_on=false, which
446f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
447f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
448f1c8ecceSJoseph Chen 		 */
449f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
450f1c8ecceSJoseph Chen 			screen_on = false;
451f1c8ecceSJoseph Chen 
452f1c8ecceSJoseph Chen 		/*
453f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
454f1c8ecceSJoseph Chen 		 * 'ever_lowpower_screen_off' means enter while loop with
455f1c8ecceSJoseph Chen 		 * screen off.
456f1c8ecceSJoseph Chen 		 */
457f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
458d6653c12SJoseph Chen 		    (voltage > pdata->screen_on_voltage)) {
459f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
460f1c8ecceSJoseph Chen 			screen_on = true;
461f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
462f1c8ecceSJoseph Chen 		}
463f1c8ecceSJoseph Chen 
464f1c8ecceSJoseph Chen 		/*
465f1c8ecceSJoseph Chen 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
466f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
467f1c8ecceSJoseph Chen 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
468f1c8ecceSJoseph Chen 		 * that calculate by current soc.
469f1c8ecceSJoseph Chen 		 */
470f1c8ecceSJoseph Chen 		if (show_idx == IMAGE_SHOW_RESET) {
471f1c8ecceSJoseph Chen 			for (i = 0; i < image_num - 2; i++) {
472f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
473f1c8ecceSJoseph Chen 				if ((soc >= image[i].soc) &&
474f1c8ecceSJoseph Chen 				    (soc < image[i + 1].soc)) {
475f1c8ecceSJoseph Chen 					start_idx = i;
476f1c8ecceSJoseph Chen 					break;
477f1c8ecceSJoseph Chen 				}
478f1c8ecceSJoseph Chen 
479f1c8ecceSJoseph Chen 				if (soc >= 100) {
480f1c8ecceSJoseph Chen 					start_idx = image_num - 2;
481f1c8ecceSJoseph Chen 					break;
482f1c8ecceSJoseph Chen 				}
483f1c8ecceSJoseph Chen 			}
484f1c8ecceSJoseph Chen 
485f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
486f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
487f1c8ecceSJoseph Chen 
488f1c8ecceSJoseph Chen 			/* Mark start index and start time */
489f1c8ecceSJoseph Chen 			show_idx = start_idx;
490f1c8ecceSJoseph Chen 			show_start = get_timer(0);
491f1c8ecceSJoseph Chen 		}
492f1c8ecceSJoseph Chen 
493f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
494f1c8ecceSJoseph Chen 
495f1c8ecceSJoseph Chen 		/* Step3: show images */
496f1c8ecceSJoseph Chen 		if (screen_on) {
497f1c8ecceSJoseph Chen 			debug("SHOW: %s\n", image[show_idx].name);
498f23c35a8SJoseph Chen 			charge_show_bmp(image[show_idx].name);
499e7f9facbSJoseph Chen 
500e7f9facbSJoseph Chen 			/* Re calculate timeout to off screen */
501e7f9facbSJoseph Chen 			if (priv->auto_screen_off_timeout == 0)
502e7f9facbSJoseph Chen 				priv->auto_screen_off_timeout = get_timer(0);
503f1c8ecceSJoseph Chen 		} else {
504e7f9facbSJoseph Chen 			priv->auto_screen_off_timeout = 0;
505e7f9facbSJoseph Chen 
506f23c35a8SJoseph Chen 			system_suspend_enter(pdata);
507f1c8ecceSJoseph Chen 		}
508f1c8ecceSJoseph Chen 
509f1c8ecceSJoseph Chen 		mdelay(5);
510f1c8ecceSJoseph Chen 
511f1c8ecceSJoseph Chen 		/* Every image shows period */
512f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
513f1c8ecceSJoseph Chen 			show_start = get_timer(0);
514f1c8ecceSJoseph Chen 			/* Update to next image */
515f1c8ecceSJoseph Chen 			show_idx++;
516f1c8ecceSJoseph Chen 			if (show_idx > (image_num - 2))
517f1c8ecceSJoseph Chen 				show_idx = IMAGE_SHOW_RESET;
518f1c8ecceSJoseph Chen 		}
519f1c8ecceSJoseph Chen 
520f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
521f1c8ecceSJoseph Chen 
522f1c8ecceSJoseph Chen 		/*
523f1c8ecceSJoseph Chen 		 * Step4: check key event.
524f1c8ecceSJoseph Chen 		 *
525f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
526f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
527f1c8ecceSJoseph Chen 		 */
528e7f9facbSJoseph Chen 		key_state = check_key_press(dev);
529b177a917SJoseph Chen 		if (key_state == KEY_PRESS_DOWN) {
530f1c8ecceSJoseph Chen 			/* NULL means show nothing, ie. turn off screen */
531f1c8ecceSJoseph Chen 			if (screen_on)
532f23c35a8SJoseph Chen 				charge_show_bmp(NULL);
533f1c8ecceSJoseph Chen 
534f1c8ecceSJoseph Chen 			/*
535f1c8ecceSJoseph Chen 			 * Clear current image index, and show image
536f1c8ecceSJoseph Chen 			 * from start_idx
537f1c8ecceSJoseph Chen 			 */
538f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
539f1c8ecceSJoseph Chen 
540f1c8ecceSJoseph Chen 			/*
541f23c35a8SJoseph Chen 			 * We turn off screen by charge_show_bmp(NULL), so we
542f1c8ecceSJoseph Chen 			 * should tell while loop to stop show images any more.
543f1c8ecceSJoseph Chen 			 *
544f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
545f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
546f1c8ecceSJoseph Chen 			 *
547f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
548f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
549f1c8ecceSJoseph Chen 			 */
550f1c8ecceSJoseph Chen 			if (screen_on)
551f1c8ecceSJoseph Chen 				screen_on = false;
552f1c8ecceSJoseph Chen 			else
553f1c8ecceSJoseph Chen 				screen_on = true;
554b177a917SJoseph Chen 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
555f1c8ecceSJoseph Chen 			/* Only long pressed while screen off needs screen_on true */
556f1c8ecceSJoseph Chen 			if (!screen_on)
557f1c8ecceSJoseph Chen 				screen_on = true;
558f1c8ecceSJoseph Chen 
559f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
560d6653c12SJoseph Chen 			if (soc < pdata->exit_charge_level) {
561f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
562d6653c12SJoseph Chen 				       soc, pdata->exit_charge_level);
563f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
564f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
565f1c8ecceSJoseph Chen 				continue;
566f1c8ecceSJoseph Chen 			}
567f1c8ecceSJoseph Chen 
568d6653c12SJoseph Chen 			if (voltage < pdata->exit_charge_voltage) {
569f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
570d6653c12SJoseph Chen 				       voltage, pdata->exit_charge_voltage);
571f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
572f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
573f1c8ecceSJoseph Chen 				continue;
574f1c8ecceSJoseph Chen 			}
575f1c8ecceSJoseph Chen 
576f1c8ecceSJoseph Chen 			/* Success exit charging */
577f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
578f23c35a8SJoseph Chen 			charge_show_logo();
579f1c8ecceSJoseph Chen 			break;
580f1c8ecceSJoseph Chen 		} else {
581f1c8ecceSJoseph Chen 			/* Do nothing */
582f1c8ecceSJoseph Chen 		}
583f1c8ecceSJoseph Chen 
584f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
585f1c8ecceSJoseph Chen 
586d6653c12SJoseph Chen 		/* Step5: Exit by ctrl+c */
5871367bfe3SJoseph Chen 		if (ctrlc()) {
588d6653c12SJoseph Chen 			if (voltage >= pdata->screen_on_voltage)
589f23c35a8SJoseph Chen 				charge_show_logo();
5901367bfe3SJoseph Chen 			printf("Exit charge, due to ctrl+c\n");
5911367bfe3SJoseph Chen 			break;
5921367bfe3SJoseph Chen 		}
593f1c8ecceSJoseph Chen 	}
594f1c8ecceSJoseph Chen 
595e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval)
596e7f9facbSJoseph Chen 		autowakeup_timer_uninit();
597e7f9facbSJoseph Chen 
598f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
599f1c8ecceSJoseph Chen 	if (ms >= 1000) {
600f1c8ecceSJoseph Chen 		sec = ms / 1000;
601f1c8ecceSJoseph Chen 		ms = ms % 1000;
602f1c8ecceSJoseph Chen 	}
603f1c8ecceSJoseph Chen 
604f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
605f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
606f1c8ecceSJoseph Chen 
607f1c8ecceSJoseph Chen 	return 0;
608f1c8ecceSJoseph Chen }
609f1c8ecceSJoseph Chen 
610f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
611f1c8ecceSJoseph Chen 	.show = charge_animation_show,
612f1c8ecceSJoseph Chen };
613f1c8ecceSJoseph Chen 
614f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
615f1c8ecceSJoseph Chen {
616f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
617d4e1125bSJoseph Chen 	struct udevice *fg, *pmic;
618f23c35a8SJoseph Chen 	int ret, soc;
619f1c8ecceSJoseph Chen 
620f23c35a8SJoseph Chen 	/* Get PMIC: used for power off system  */
621f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
622f1c8ecceSJoseph Chen 	if (ret) {
623*cf49f6adSJoseph Chen 		if (ret == -ENODEV)
624*cf49f6adSJoseph Chen 			printf("Can't find PMIC\n");
625*cf49f6adSJoseph Chen 		else
626f1c8ecceSJoseph Chen 			printf("Get UCLASS PMIC failed: %d\n", ret);
627f1c8ecceSJoseph Chen 		return ret;
628f1c8ecceSJoseph Chen 	}
629f1c8ecceSJoseph Chen 	priv->pmic = pmic;
630f1c8ecceSJoseph Chen 
631f23c35a8SJoseph Chen 	/* Get fuel gauge: used for charging */
632f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
633f1c8ecceSJoseph Chen 	if (ret) {
634*cf49f6adSJoseph Chen 		if (ret == -ENODEV)
635*cf49f6adSJoseph Chen 			printf("Can't find FG\n");
636*cf49f6adSJoseph Chen 		else
637f1c8ecceSJoseph Chen 			printf("Get UCLASS FG failed: %d\n", ret);
638f1c8ecceSJoseph Chen 		return ret;
639f1c8ecceSJoseph Chen 	}
640f1c8ecceSJoseph Chen 	priv->fg = fg;
641f1c8ecceSJoseph Chen 
64279244e4cSJoseph Chen 	/* Get PWRKEY: used for wakeup and turn off/on LCD */
643f23c35a8SJoseph Chen 	ret = platform_key_read(KEY_POWER);
644f23c35a8SJoseph Chen 	if (ret == KEY_NOT_EXIST) {
645f23c35a8SJoseph Chen 		printf("Can't find power key\n");
646f23c35a8SJoseph Chen 		return -EINVAL;
647f23c35a8SJoseph Chen 	}
648f23c35a8SJoseph Chen 
649f23c35a8SJoseph Chen 	/* Initialize charge current */
650f23c35a8SJoseph Chen 	soc = fuel_gauge_get_soc(fg);
651f23c35a8SJoseph Chen 	if (soc < 0 || soc > 100) {
652f23c35a8SJoseph Chen 		printf("get soc failed: %d\n", soc);
653f23c35a8SJoseph Chen 		return -EINVAL;
654f23c35a8SJoseph Chen 	}
655f23c35a8SJoseph Chen 
656f23c35a8SJoseph Chen 	/* Get charge images */
657f1c8ecceSJoseph Chen 	priv->image = image;
658f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
659f1c8ecceSJoseph Chen 
660f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
661f1c8ecceSJoseph Chen 
662f1c8ecceSJoseph Chen 	return 0;
663f1c8ecceSJoseph Chen }
664f1c8ecceSJoseph Chen 
665f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
666d6653c12SJoseph Chen 	{ .compatible = "rockchip,uboot-charge" },
667f1c8ecceSJoseph Chen 	{ },
668f1c8ecceSJoseph Chen };
669f1c8ecceSJoseph Chen 
670f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
671f1c8ecceSJoseph Chen 	.name = "charge-animation",
672f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
673f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
674f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
675f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
676f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
677f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
678f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
679f1c8ecceSJoseph Chen };
680