xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision d4e1125bbcfb417adfb3dc61cfbb8f1dd72da9c4)
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 
7f1c8ecceSJoseph Chen #include <common.h>
8d3ff9cf9SKever Yang #include <boot_rkimg.h>
91367bfe3SJoseph Chen #include <console.h>
10f1c8ecceSJoseph Chen #include <dm.h>
11f1c8ecceSJoseph Chen #include <errno.h>
12f1c8ecceSJoseph Chen #include <key.h>
13f1c8ecceSJoseph Chen #include <pwm.h>
14d3ff9cf9SKever Yang #include <irq-generic.h>
15d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h>
16d3ff9cf9SKever Yang #include <asm/suspend.h>
17d3ff9cf9SKever Yang #include <linux/input.h>
18f1c8ecceSJoseph Chen #include <power/charge_display.h>
19f1c8ecceSJoseph Chen #include <power/fuel_gauge.h>
20f1c8ecceSJoseph Chen #include <power/pmic.h>
21f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h>
22f1c8ecceSJoseph Chen #include <power/regulator.h>
23f1c8ecceSJoseph Chen #include <video_rockchip.h>
24f1c8ecceSJoseph Chen 
25f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
26f1c8ecceSJoseph Chen 
27f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET			-1
28f1c8ecceSJoseph Chen 
29f1c8ecceSJoseph Chen struct charge_image {
30f1c8ecceSJoseph Chen 	const char *name;
31f1c8ecceSJoseph Chen 	int soc;
32f1c8ecceSJoseph Chen 	int period;	/* ms */
33f1c8ecceSJoseph Chen };
34f1c8ecceSJoseph Chen 
35f1c8ecceSJoseph Chen struct charge_animation_priv {
36f1c8ecceSJoseph Chen 	struct udevice *pmic;
37f1c8ecceSJoseph Chen 	struct udevice *fg;
38f1c8ecceSJoseph Chen 	const struct charge_image *image;
39f1c8ecceSJoseph Chen 	int image_num;
40f1c8ecceSJoseph Chen };
41f1c8ecceSJoseph Chen 
42f1c8ecceSJoseph Chen struct charge_animation_pdata {
43d6653c12SJoseph Chen 	int android_charge;
44d6653c12SJoseph Chen 	int uboot_charge;
45d6653c12SJoseph Chen 
46d6653c12SJoseph Chen 	int screen_on_voltage;
47d6653c12SJoseph Chen 	int exit_charge_voltage;
48d6653c12SJoseph Chen 
49d6653c12SJoseph Chen 	int exit_charge_level;
50d6653c12SJoseph Chen 	int low_power_level;
51d6653c12SJoseph Chen 
52f1c8ecceSJoseph Chen };
53f1c8ecceSJoseph Chen 
541367bfe3SJoseph Chen static int charge_animation_get_power_on_soc(struct udevice *dev)
55f1c8ecceSJoseph Chen {
56f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
57f1c8ecceSJoseph Chen 
58f1c8ecceSJoseph Chen 	if (!pdata)
59f1c8ecceSJoseph Chen 		return -ENOSYS;
60f1c8ecceSJoseph Chen 
61d6653c12SJoseph Chen 	return pdata->exit_charge_level;
62f1c8ecceSJoseph Chen }
63f1c8ecceSJoseph Chen 
641367bfe3SJoseph Chen static int charge_animation_get_power_on_voltage(struct udevice *dev)
65f1c8ecceSJoseph Chen {
66f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
67f1c8ecceSJoseph Chen 
68f1c8ecceSJoseph Chen 	if (!pdata)
69f1c8ecceSJoseph Chen 		return -ENOSYS;
70f1c8ecceSJoseph Chen 
71d6653c12SJoseph Chen 	return pdata->exit_charge_voltage;
72f1c8ecceSJoseph Chen }
73f1c8ecceSJoseph Chen 
741367bfe3SJoseph Chen static int charge_animation_get_screen_on_voltage(struct udevice *dev)
751367bfe3SJoseph Chen {
761367bfe3SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
771367bfe3SJoseph Chen 
781367bfe3SJoseph Chen 	if (!pdata)
791367bfe3SJoseph Chen 		return -ENOSYS;
801367bfe3SJoseph Chen 
81d6653c12SJoseph Chen 	return pdata->screen_on_voltage;
821367bfe3SJoseph Chen }
831367bfe3SJoseph Chen 
841367bfe3SJoseph Chen static int charge_animation_set_power_on_soc(struct udevice *dev, int val)
851367bfe3SJoseph Chen {
861367bfe3SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
871367bfe3SJoseph Chen 
881367bfe3SJoseph Chen 	if (!pdata)
891367bfe3SJoseph Chen 		return -ENOSYS;
901367bfe3SJoseph Chen 
91d6653c12SJoseph Chen 	pdata->exit_charge_level = val;
921367bfe3SJoseph Chen 
931367bfe3SJoseph Chen 	return 0;
941367bfe3SJoseph Chen }
951367bfe3SJoseph Chen 
961367bfe3SJoseph Chen static int charge_animation_set_power_on_voltage(struct udevice *dev, int val)
971367bfe3SJoseph Chen {
981367bfe3SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
991367bfe3SJoseph Chen 
1001367bfe3SJoseph Chen 	if (!pdata)
1011367bfe3SJoseph Chen 		return -ENOSYS;
1021367bfe3SJoseph Chen 
103d6653c12SJoseph Chen 	pdata->exit_charge_voltage = val;
1041367bfe3SJoseph Chen 
1051367bfe3SJoseph Chen 	return 0;
1061367bfe3SJoseph Chen }
1071367bfe3SJoseph Chen 
1081367bfe3SJoseph Chen static int charge_animation_set_screen_on_voltage(struct udevice *dev, int val)
1091367bfe3SJoseph Chen {
1101367bfe3SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
1111367bfe3SJoseph Chen 
1121367bfe3SJoseph Chen 	if (!pdata)
1131367bfe3SJoseph Chen 		return -ENOSYS;
1141367bfe3SJoseph Chen 
115d6653c12SJoseph Chen 	pdata->screen_on_voltage = val;
1161367bfe3SJoseph Chen 
1171367bfe3SJoseph Chen 	return 0;
1181367bfe3SJoseph Chen }
1191367bfe3SJoseph Chen 
120f1c8ecceSJoseph Chen /*
121f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
122f1c8ecceSJoseph Chen  *
123f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
124f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
125f1c8ecceSJoseph Chen  */
126f1c8ecceSJoseph Chen static const struct charge_image image[] = {
127f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
128f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
129f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
130f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
131f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
132f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
133f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
134f1c8ecceSJoseph Chen };
135f1c8ecceSJoseph Chen 
136f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
137f1c8ecceSJoseph Chen {
138f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
139f1c8ecceSJoseph Chen 
140d6653c12SJoseph Chen 	/* charge mode */
141d6653c12SJoseph Chen 	pdata->uboot_charge =
142d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
143d6653c12SJoseph Chen 	pdata->android_charge =
144d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
145f1c8ecceSJoseph Chen 
146d6653c12SJoseph Chen 	/* level */
147d6653c12SJoseph Chen 	pdata->exit_charge_level =
148d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
149d6653c12SJoseph Chen 	pdata->low_power_level =
150d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-low-power-level", 0);
151f1c8ecceSJoseph Chen 
152d6653c12SJoseph Chen 	/* voltage */
153d6653c12SJoseph Chen 	pdata->exit_charge_voltage =
154d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
155d6653c12SJoseph Chen 	pdata->screen_on_voltage =
156d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
157f1c8ecceSJoseph Chen 
158d6653c12SJoseph Chen 	if (pdata->screen_on_voltage >
159d6653c12SJoseph Chen 	    pdata->exit_charge_voltage)
160d6653c12SJoseph Chen 		pdata->screen_on_voltage =
161d6653c12SJoseph Chen 					pdata->exit_charge_voltage;
162f1c8ecceSJoseph Chen 
163d6653c12SJoseph Chen 	debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
164d6653c12SJoseph Chen 	      "lp_soc=%d%%, screen_on=%dmv\n",
165d6653c12SJoseph Chen 	      pdata->uboot_charge, pdata->android_charge,
166d6653c12SJoseph Chen 	      pdata->exit_charge_level, pdata->exit_charge_voltage,
167d6653c12SJoseph Chen 	      pdata->low_power_level, pdata->screen_on_voltage);
168f1c8ecceSJoseph Chen 
169f1c8ecceSJoseph Chen 	return 0;
170f1c8ecceSJoseph Chen }
171f1c8ecceSJoseph Chen 
172*d4e1125bSJoseph Chen static int check_key_press(void)
173f1c8ecceSJoseph Chen {
174b177a917SJoseph Chen 	u32 state;
175f1c8ecceSJoseph Chen 
176*d4e1125bSJoseph Chen 	state = platform_key_read(KEY_POWER);
177b177a917SJoseph Chen 	if (state < 0)
178f1c8ecceSJoseph Chen 		printf("read power key failed: %d\n", state);
179f1c8ecceSJoseph Chen 
180b177a917SJoseph Chen 	if (state == KEY_PRESS_LONG_DOWN)
181f1c8ecceSJoseph Chen 		printf("power key long pressed...\n");
182b177a917SJoseph Chen 	else if (state == KEY_PRESS_DOWN)
183f1c8ecceSJoseph Chen 		printf("power key short pressed...\n");
184b177a917SJoseph Chen 
185b177a917SJoseph Chen 	return state;
186f1c8ecceSJoseph Chen }
187f1c8ecceSJoseph Chen 
188b177a917SJoseph Chen static int system_suspend_enter(void)
189b177a917SJoseph Chen {
190b177a917SJoseph Chen 	/*
191b177a917SJoseph Chen 	 * TODO: enter low power mode:
192b177a917SJoseph Chen 	 * 3. auto turn off screen when timout;
193b177a917SJoseph Chen 	 * 4. power key wakeup;
194b177a917SJoseph Chen 	 * 5. timer period wakeup for pmic fg ?
195b177a917SJoseph Chen 	 */
196b177a917SJoseph Chen 	if (IS_ENABLED(CONFIG_ARM_SMCCC)) {
197b177a917SJoseph Chen 		printf("\nSystem suspend: ");
198b177a917SJoseph Chen 		putc('1');
199b177a917SJoseph Chen 		local_irq_disable();
200b177a917SJoseph Chen 		putc('2');
201b177a917SJoseph Chen 		irqs_suspend();
202b177a917SJoseph Chen 		putc('3');
203b177a917SJoseph Chen 		putc('\n');
204b177a917SJoseph Chen 
205b177a917SJoseph Chen 		/* Trap into ATF for low power mode */
206b177a917SJoseph Chen 		cpu_suspend(0, psci_system_suspend);
207b177a917SJoseph Chen 
208b177a917SJoseph Chen 		putc('\n');
209b177a917SJoseph Chen 		putc('3');
210b177a917SJoseph Chen 		irqs_resume();
211b177a917SJoseph Chen 		putc('2');
212b177a917SJoseph Chen 		local_irq_enable();
213b177a917SJoseph Chen 		putc('1');
214b177a917SJoseph Chen 		putc('\n');
215b177a917SJoseph Chen 
216b177a917SJoseph Chen 		/*
217b177a917SJoseph Chen 		 * We must wait for key release event finish, otherwise
218b177a917SJoseph Chen 		 * we may read key state too early.
219b177a917SJoseph Chen 		 */
220b177a917SJoseph Chen 		mdelay(300);
221b177a917SJoseph Chen 	} else {
222b177a917SJoseph Chen 		printf("\nWfi\n");
223b177a917SJoseph Chen 		wfi();
224b177a917SJoseph Chen 	}
225b177a917SJoseph Chen 
226b177a917SJoseph Chen 	return 0;
227f1c8ecceSJoseph Chen }
228f1c8ecceSJoseph Chen 
229f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
230f1c8ecceSJoseph Chen {
231f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
232f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
233f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
234f1c8ecceSJoseph Chen 	struct udevice *pmic = priv->pmic;
235f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
236a8b9d026SJoseph Chen 	const char *preboot = env_get("preboot");
237f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
238f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
239f1c8ecceSJoseph Chen 	bool screen_on = true;
240f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
241f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
242f1c8ecceSJoseph Chen 	int start_idx = 0, show_idx = -1;
243d6653c12SJoseph Chen 	int soc, voltage, current, key_state;
244f1c8ecceSJoseph Chen 	int i, charging = 1;
2458f9ff705SJoseph Chen 	int boot_mode;
246f1c8ecceSJoseph Chen 
247a8b9d026SJoseph Chen 	/* If there is preboot command, exit */
248a8b9d026SJoseph Chen 	if (preboot) {
249a8b9d026SJoseph Chen 		debug("preboot: %s\n", preboot);
250a8b9d026SJoseph Chen 		return 0;
251a8b9d026SJoseph Chen 	}
252a8b9d026SJoseph Chen 
253d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
2548f9ff705SJoseph Chen 	boot_mode = rockchip_get_boot_mode();
255221b5220SJoseph Chen 	if ((boot_mode != BOOT_MODE_CHARGING) &&
256221b5220SJoseph Chen 	    (boot_mode != BOOT_MODE_UNDEFINE)) {
2578f9ff705SJoseph Chen 		debug("exit charge, due to boot mode: %d\n", boot_mode);
2588f9ff705SJoseph Chen 		return 0;
2598f9ff705SJoseph Chen 	}
2608f9ff705SJoseph Chen #endif
261221b5220SJoseph Chen 
262d6653c12SJoseph Chen 	/* Enter android charge */
263d6653c12SJoseph Chen 	if (pdata->android_charge) {
264d6653c12SJoseph Chen 		env_update("bootargs", "androidboot.mode=charger");
265d6653c12SJoseph Chen 		printf("Android charge mode\n");
266d6653c12SJoseph Chen 		return 0;
267d6653c12SJoseph Chen 	}
268d6653c12SJoseph Chen 
269d6653c12SJoseph Chen 	if (!pdata->uboot_charge)
270d6653c12SJoseph Chen 		return 0;
271d6653c12SJoseph Chen 
272f1c8ecceSJoseph Chen 	/* Not charger online, exit */
273f1c8ecceSJoseph Chen 	charging = fuel_gauge_get_chrg_online(fg);
274f1c8ecceSJoseph Chen 	if (charging <= 0)
275f1c8ecceSJoseph Chen 		return 0;
276f1c8ecceSJoseph Chen 
277f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
278f1c8ecceSJoseph Chen 	if (voltage < 0) {
279f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
280f1c8ecceSJoseph Chen 		return -EINVAL;
281f1c8ecceSJoseph Chen 	}
282f1c8ecceSJoseph Chen 
283f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
284d6653c12SJoseph Chen 	if (voltage <= pdata->screen_on_voltage + 50) {
285f1c8ecceSJoseph Chen 		screen_on = false;
286f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
287f1c8ecceSJoseph Chen 		rockchip_show_bmp(NULL);
288f1c8ecceSJoseph Chen 	}
289f1c8ecceSJoseph Chen 
290d6653c12SJoseph Chen 	printf("Enter U-Boot charging mode\n");
291f1c8ecceSJoseph Chen 
292d6653c12SJoseph Chen 	charge_start = get_timer(0);
293f1c8ecceSJoseph Chen 	/* Charging ! */
294f1c8ecceSJoseph Chen 	while (1) {
295f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
296f1c8ecceSJoseph Chen 
297f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
298f1c8ecceSJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
299f1c8ecceSJoseph Chen 		if (charging <= 0) {
300f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
301f1c8ecceSJoseph Chen 			       charging);
302f1c8ecceSJoseph Chen 
303f1c8ecceSJoseph Chen 			/* wait uart flush before shutdown */
304f1c8ecceSJoseph Chen 			mdelay(500);
305f1c8ecceSJoseph Chen 
306f1c8ecceSJoseph Chen 			/* PMIC shutdown */
307f1c8ecceSJoseph Chen 			pmic_shutdown(pmic);
308f1c8ecceSJoseph Chen 
309f1c8ecceSJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
310f1c8ecceSJoseph Chen 			continue;
311f1c8ecceSJoseph Chen 		}
312f1c8ecceSJoseph Chen 
313f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
314f1c8ecceSJoseph Chen 
315f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
316f1c8ecceSJoseph Chen 		soc = fuel_gauge_get_soc(fg);
317f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
318f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
319f1c8ecceSJoseph Chen 			continue;
320f1c8ecceSJoseph Chen 		}
321f1c8ecceSJoseph Chen 
322f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
323f1c8ecceSJoseph Chen 		if (voltage < 0) {
324f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
325f1c8ecceSJoseph Chen 			continue;
326f1c8ecceSJoseph Chen 		}
327f1c8ecceSJoseph Chen 
328d6653c12SJoseph Chen 		current = fuel_gauge_get_current(fg);
329d6653c12SJoseph Chen 		if (current == -ENOSYS) {
330d6653c12SJoseph Chen 			printf("get current failed: %d\n", current);
331d6653c12SJoseph Chen 			continue;
332d6653c12SJoseph Chen 		}
333d6653c12SJoseph Chen 
334f1c8ecceSJoseph Chen 		/*
335f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
336f1c8ecceSJoseph Chen 		 * is not good to know what happen.
337f1c8ecceSJoseph Chen 		 */
338f1c8ecceSJoseph Chen 		if (!debug_start)
339f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
340f1c8ecceSJoseph Chen 		if (get_timer(debug_start) > 20000) {
341f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
342d6653c12SJoseph Chen 			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n",
343d6653c12SJoseph Chen 			       get_timer(0)/1000, soc, voltage,
344d6653c12SJoseph Chen 			       current, charging, screen_on);
345f1c8ecceSJoseph Chen 		}
346f1c8ecceSJoseph Chen 
347f1c8ecceSJoseph Chen 		/*
348f1c8ecceSJoseph Chen 		 * If ever lowpower screen off, force screen on false, which
349f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
350f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
351f1c8ecceSJoseph Chen 		 */
352f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
353f1c8ecceSJoseph Chen 			screen_on = false;
354f1c8ecceSJoseph Chen 
355f1c8ecceSJoseph Chen 		/*
356f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
357f1c8ecceSJoseph Chen 		 * 'ever_lowpower_screen_off' means enter while loop with
358f1c8ecceSJoseph Chen 		 * screen off.
359f1c8ecceSJoseph Chen 		 */
360f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
361d6653c12SJoseph Chen 		    (voltage > pdata->screen_on_voltage)) {
362f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
363f1c8ecceSJoseph Chen 			screen_on = true;
364f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
365f1c8ecceSJoseph Chen 		}
366f1c8ecceSJoseph Chen 
367f1c8ecceSJoseph Chen 		/*
368f1c8ecceSJoseph Chen 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
369f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
370f1c8ecceSJoseph Chen 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
371f1c8ecceSJoseph Chen 		 * that calculate by current soc.
372f1c8ecceSJoseph Chen 		 */
373f1c8ecceSJoseph Chen 		if (show_idx == IMAGE_SHOW_RESET) {
374f1c8ecceSJoseph Chen 			for (i = 0; i < image_num - 2; i++) {
375f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
376f1c8ecceSJoseph Chen 				if ((soc >= image[i].soc) &&
377f1c8ecceSJoseph Chen 				    (soc < image[i + 1].soc)) {
378f1c8ecceSJoseph Chen 					start_idx = i;
379f1c8ecceSJoseph Chen 					break;
380f1c8ecceSJoseph Chen 				}
381f1c8ecceSJoseph Chen 
382f1c8ecceSJoseph Chen 				if (soc >= 100) {
383f1c8ecceSJoseph Chen 					start_idx = image_num - 2;
384f1c8ecceSJoseph Chen 					break;
385f1c8ecceSJoseph Chen 				}
386f1c8ecceSJoseph Chen 			}
387f1c8ecceSJoseph Chen 
388f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
389f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
390f1c8ecceSJoseph Chen 
391f1c8ecceSJoseph Chen 			/* Mark start index and start time */
392f1c8ecceSJoseph Chen 			show_idx = start_idx;
393f1c8ecceSJoseph Chen 			show_start = get_timer(0);
394f1c8ecceSJoseph Chen 		}
395f1c8ecceSJoseph Chen 
396f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
397f1c8ecceSJoseph Chen 
398f1c8ecceSJoseph Chen 		/* Step3: show images */
399f1c8ecceSJoseph Chen 		if (screen_on) {
400f1c8ecceSJoseph Chen 			debug("SHOW: %s\n", image[show_idx].name);
401f1c8ecceSJoseph Chen 			rockchip_show_bmp(image[show_idx].name);
402f1c8ecceSJoseph Chen 		} else {
403b177a917SJoseph Chen 			system_suspend_enter();
404f1c8ecceSJoseph Chen 		}
405f1c8ecceSJoseph Chen 
406f1c8ecceSJoseph Chen 		mdelay(5);
407f1c8ecceSJoseph Chen 
408f1c8ecceSJoseph Chen 		/* Every image shows period */
409f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
410f1c8ecceSJoseph Chen 			show_start = get_timer(0);
411f1c8ecceSJoseph Chen 			/* Update to next image */
412f1c8ecceSJoseph Chen 			show_idx++;
413f1c8ecceSJoseph Chen 			if (show_idx > (image_num - 2))
414f1c8ecceSJoseph Chen 				show_idx = IMAGE_SHOW_RESET;
415f1c8ecceSJoseph Chen 		}
416f1c8ecceSJoseph Chen 
417f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
418f1c8ecceSJoseph Chen 
419f1c8ecceSJoseph Chen 		/*
420f1c8ecceSJoseph Chen 		 * Step4: check key event.
421f1c8ecceSJoseph Chen 		 *
422f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
423f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
424f1c8ecceSJoseph Chen 		 */
425*d4e1125bSJoseph Chen 		key_state = check_key_press();
426b177a917SJoseph Chen 		if (key_state == KEY_PRESS_DOWN) {
427f1c8ecceSJoseph Chen 			/* NULL means show nothing, ie. turn off screen */
428f1c8ecceSJoseph Chen 			if (screen_on)
429f1c8ecceSJoseph Chen 				rockchip_show_bmp(NULL);
430f1c8ecceSJoseph Chen 
431f1c8ecceSJoseph Chen 			/*
432f1c8ecceSJoseph Chen 			 * Clear current image index, and show image
433f1c8ecceSJoseph Chen 			 * from start_idx
434f1c8ecceSJoseph Chen 			 */
435f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
436f1c8ecceSJoseph Chen 
437f1c8ecceSJoseph Chen 			/*
438f1c8ecceSJoseph Chen 			 * We turn off screen by rockchip_show_bmp(NULL), so we
439f1c8ecceSJoseph Chen 			 * should tell while loop to stop show images any more.
440f1c8ecceSJoseph Chen 			 *
441f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
442f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
443f1c8ecceSJoseph Chen 			 *
444f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
445f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
446f1c8ecceSJoseph Chen 			 */
447f1c8ecceSJoseph Chen 			if (screen_on)
448f1c8ecceSJoseph Chen 				screen_on = false;
449f1c8ecceSJoseph Chen 			else
450f1c8ecceSJoseph Chen 				screen_on = true;
451b177a917SJoseph Chen 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
452f1c8ecceSJoseph Chen 			/* Only long pressed while screen off needs screen_on true */
453f1c8ecceSJoseph Chen 			if (!screen_on)
454f1c8ecceSJoseph Chen 				screen_on = true;
455f1c8ecceSJoseph Chen 
456f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
457d6653c12SJoseph Chen 			if (soc < pdata->exit_charge_level) {
458f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
459d6653c12SJoseph Chen 				       soc, pdata->exit_charge_level);
460f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
461f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
462f1c8ecceSJoseph Chen 				continue;
463f1c8ecceSJoseph Chen 			}
464f1c8ecceSJoseph Chen 
465d6653c12SJoseph Chen 			if (voltage < pdata->exit_charge_voltage) {
466f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
467d6653c12SJoseph Chen 				       voltage, pdata->exit_charge_voltage);
468f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
469f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
470f1c8ecceSJoseph Chen 				continue;
471f1c8ecceSJoseph Chen 			}
472f1c8ecceSJoseph Chen 
473f1c8ecceSJoseph Chen 			/* Success exit charging */
474f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
475f1c8ecceSJoseph Chen 			rockchip_show_logo();
476f1c8ecceSJoseph Chen 			break;
477f1c8ecceSJoseph Chen 		} else {
478f1c8ecceSJoseph Chen 			/* Do nothing */
479f1c8ecceSJoseph Chen 		}
480f1c8ecceSJoseph Chen 
481f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
482f1c8ecceSJoseph Chen 
483d6653c12SJoseph Chen 		/* Step5: Exit by ctrl+c */
4841367bfe3SJoseph Chen 		if (ctrlc()) {
485d6653c12SJoseph Chen 			if (voltage >= pdata->screen_on_voltage)
4861367bfe3SJoseph Chen 				rockchip_show_logo();
4871367bfe3SJoseph Chen 			printf("Exit charge, due to ctrl+c\n");
4881367bfe3SJoseph Chen 			break;
4891367bfe3SJoseph Chen 		}
490f1c8ecceSJoseph Chen 	}
491f1c8ecceSJoseph Chen 
492f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
493f1c8ecceSJoseph Chen 	if (ms >= 1000) {
494f1c8ecceSJoseph Chen 		sec = ms / 1000;
495f1c8ecceSJoseph Chen 		ms = ms % 1000;
496f1c8ecceSJoseph Chen 	}
497f1c8ecceSJoseph Chen 
498f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
499f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
500f1c8ecceSJoseph Chen 
501f1c8ecceSJoseph Chen 	return 0;
502f1c8ecceSJoseph Chen }
503f1c8ecceSJoseph Chen 
504f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
5051367bfe3SJoseph Chen 	.get_power_on_soc = charge_animation_get_power_on_soc,
5061367bfe3SJoseph Chen 	.get_power_on_voltage = charge_animation_get_power_on_voltage,
5071367bfe3SJoseph Chen 	.get_screen_on_voltage = charge_animation_get_screen_on_voltage,
5081367bfe3SJoseph Chen 	.set_power_on_soc = charge_animation_set_power_on_soc,
5091367bfe3SJoseph Chen 	.set_power_on_voltage = charge_animation_set_power_on_voltage,
5101367bfe3SJoseph Chen 	.set_screen_on_voltage = charge_animation_set_screen_on_voltage,
511f1c8ecceSJoseph Chen 	.show = charge_animation_show,
512f1c8ecceSJoseph Chen };
513f1c8ecceSJoseph Chen 
514f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
515f1c8ecceSJoseph Chen {
516f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
517*d4e1125bSJoseph Chen 	struct udevice *fg, *pmic;
518f1c8ecceSJoseph Chen 	int ret;
519f1c8ecceSJoseph Chen 
520f1c8ecceSJoseph Chen 	/* Get PMIC */
521f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
522f1c8ecceSJoseph Chen 	if (ret) {
523f1c8ecceSJoseph Chen 		printf("Get UCLASS PMIC failed: %d\n", ret);
524f1c8ecceSJoseph Chen 		return ret;
525f1c8ecceSJoseph Chen 	}
526f1c8ecceSJoseph Chen 	priv->pmic = pmic;
527f1c8ecceSJoseph Chen 
528f1c8ecceSJoseph Chen 	/* Get fuel gauge */
529f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
530f1c8ecceSJoseph Chen 	if (ret) {
531f1c8ecceSJoseph Chen 		printf("Get UCLASS FG failed: %d\n", ret);
532f1c8ecceSJoseph Chen 		return ret;
533f1c8ecceSJoseph Chen 	}
534f1c8ecceSJoseph Chen 	priv->fg = fg;
535f1c8ecceSJoseph Chen 
536f1c8ecceSJoseph Chen 	/* Get image */
537f1c8ecceSJoseph Chen 	priv->image = image;
538f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
539f1c8ecceSJoseph Chen 
540f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
541f1c8ecceSJoseph Chen 
542f1c8ecceSJoseph Chen 	return 0;
543f1c8ecceSJoseph Chen }
544f1c8ecceSJoseph Chen 
545f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
546d6653c12SJoseph Chen 	{ .compatible = "rockchip,uboot-charge" },
547f1c8ecceSJoseph Chen 	{ },
548f1c8ecceSJoseph Chen };
549f1c8ecceSJoseph Chen 
550f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
551f1c8ecceSJoseph Chen 	.name = "charge-animation",
552f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
553f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
554f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
555f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
556f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
557f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
558f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
559f1c8ecceSJoseph Chen };
560