xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision 1d4a0494e9457700c32c7982c20901d7b5dddda2)
1f1c8ecceSJoseph Chen /*
2f1c8ecceSJoseph Chen  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3f1c8ecceSJoseph Chen  *
4f1c8ecceSJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
5f1c8ecceSJoseph Chen  */
6f1c8ecceSJoseph Chen 
7e7f9facbSJoseph Chen #include <asm/io.h>
8f1c8ecceSJoseph Chen #include <common.h>
9d3ff9cf9SKever Yang #include <boot_rkimg.h>
101367bfe3SJoseph Chen #include <console.h>
11f1c8ecceSJoseph Chen #include <dm.h>
12f1c8ecceSJoseph Chen #include <errno.h>
13f1c8ecceSJoseph Chen #include <key.h>
141b3009deSJoseph Chen #include <led.h>
158b436ce5SElaine Zhang #include <rtc.h>
16f1c8ecceSJoseph Chen #include <pwm.h>
17d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h>
18d3ff9cf9SKever Yang #include <asm/suspend.h>
19d3ff9cf9SKever Yang #include <linux/input.h>
20f1c8ecceSJoseph Chen #include <power/charge_display.h>
21ac1dc0c3SJoseph Chen #include <power/charge_animation.h>
2288949342SJoseph Chen #include <power/rockchip_pm.h>
23f1c8ecceSJoseph Chen #include <power/fuel_gauge.h>
24f1c8ecceSJoseph Chen #include <power/pmic.h>
25f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h>
26f1c8ecceSJoseph Chen #include <power/regulator.h>
27f1c8ecceSJoseph Chen #include <video_rockchip.h>
28175257e4SJoseph Chen #ifdef CONFIG_IRQ
29175257e4SJoseph Chen #include <irq-generic.h>
30175257e4SJoseph Chen #include <rk_timer_irq.h>
31175257e4SJoseph Chen #endif
32255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
33255e5751SWenping Zhang #include <rk_eink.h>
34255e5751SWenping Zhang #endif
35f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
36f1c8ecceSJoseph Chen 
379116e2f9SJoseph Chen #define IMAGE_RECALC_IDX				-1
38ebe3d004SJoseph Chen #define IMAGE_SOC_100_IDX(n)			((n) - 2)
39ebe3d004SJoseph Chen #define IMAGE_LOWPOWER_IDX(n)			((n) - 1)
402e68f6b5SJoseph Chen #define SYSTEM_SUSPEND_DELAY_MS			5000
415e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS			1000
42f1c8ecceSJoseph Chen 
431b3009deSJoseph Chen #define LED_CHARGING_NAME			"battery_charging"
441b3009deSJoseph Chen #define LED_CHARGING_FULL_NAME			"battery_full"
451b3009deSJoseph Chen 
46f1c8ecceSJoseph Chen struct charge_image {
47f1c8ecceSJoseph Chen 	const char *name;
48f1c8ecceSJoseph Chen 	int soc;
49f1c8ecceSJoseph Chen 	int period;	/* ms */
50f1c8ecceSJoseph Chen };
51f1c8ecceSJoseph Chen 
52f1c8ecceSJoseph Chen struct charge_animation_priv {
53f1c8ecceSJoseph Chen 	struct udevice *pmic;
54f1c8ecceSJoseph Chen 	struct udevice *fg;
554d083e3fSJoseph Chen 	struct udevice *charger;
568b436ce5SElaine Zhang 	struct udevice *rtc;
571b3009deSJoseph Chen #ifdef CONFIG_LED
581b3009deSJoseph Chen 	struct udevice *led_charging;
591b3009deSJoseph Chen 	struct udevice *led_full;
601b3009deSJoseph Chen #endif
61f1c8ecceSJoseph Chen 	const struct charge_image *image;
62f1c8ecceSJoseph Chen 	int image_num;
63e7f9facbSJoseph Chen 
64e7f9facbSJoseph Chen 	int auto_wakeup_key_state;
652e68f6b5SJoseph Chen 	ulong auto_screen_off_timeout;	/* ms */
662e68f6b5SJoseph Chen 	ulong suspend_delay_timeout;	/* ms */
67f1c8ecceSJoseph Chen };
68f1c8ecceSJoseph Chen 
69f1c8ecceSJoseph Chen /*
70f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
71f1c8ecceSJoseph Chen  *
72f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
73f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
74f1c8ecceSJoseph Chen  */
75f1c8ecceSJoseph Chen static const struct charge_image image[] = {
76f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
77f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
78f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
79f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
80f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
81f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
82f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
83f1c8ecceSJoseph Chen };
84f1c8ecceSJoseph Chen 
regulators_parse_assigned_mem_state(struct udevice * dev)85d07906f0SJoseph Chen static int regulators_parse_assigned_mem_state(struct udevice *dev)
86d07906f0SJoseph Chen {
87d07906f0SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
88d07906f0SJoseph Chen 	struct regulator_mem *mem;
89d07906f0SJoseph Chen 	const fdt32_t *list1;
90d07906f0SJoseph Chen 	const fdt32_t *list2;
91d07906f0SJoseph Chen 	int size1, size2;
92d07906f0SJoseph Chen 	int i, ret;
93d07906f0SJoseph Chen 	uint32_t phandle;
94d07906f0SJoseph Chen 
95d07906f0SJoseph Chen 	/* Must be both exist or not */
96d07906f0SJoseph Chen 	list1 = dev_read_prop(dev, "regulator-on-in-mem", &size1);
97d07906f0SJoseph Chen 	list2 = dev_read_prop(dev, "regulator-off-in-mem", &size2);
98d07906f0SJoseph Chen 	if (!list1 && !list2)
99d07906f0SJoseph Chen 		return 0;
100d07906f0SJoseph Chen 	if (list1 && !list2)
101d07906f0SJoseph Chen 		return -EINVAL;
102d07906f0SJoseph Chen 	else if (!list1 && list2)
103d07906f0SJoseph Chen 		return -EINVAL;
104d07906f0SJoseph Chen 
105d07906f0SJoseph Chen 	size1 = size1 / sizeof(*list1);
106d07906f0SJoseph Chen 	size2 = size2 / sizeof(*list2);
107d07906f0SJoseph Chen 
108d07906f0SJoseph Chen 	pdata->regulators_mem =
109d07906f0SJoseph Chen 		calloc(size1 + size2, sizeof(*pdata->regulators_mem));
110d07906f0SJoseph Chen 	if (!pdata->regulators_mem)
111d07906f0SJoseph Chen 		return -ENOMEM;
112d07906f0SJoseph Chen 
113d07906f0SJoseph Chen 	mem = pdata->regulators_mem;
114d07906f0SJoseph Chen 
115d07906f0SJoseph Chen 	for (i = 0; i < size1; i++, mem++) {
116d07906f0SJoseph Chen 		mem->enable = true;
117d07906f0SJoseph Chen 		phandle = fdt32_to_cpu(*list1++);
118d07906f0SJoseph Chen 		ret = uclass_get_device_by_phandle_id(UCLASS_REGULATOR,
119d07906f0SJoseph Chen 						phandle, &mem->dev);
120d07906f0SJoseph Chen 		if (ret)
121d07906f0SJoseph Chen 			return ret;
122d07906f0SJoseph Chen 	}
123d07906f0SJoseph Chen 	for (i = 0; i < size2; i++, mem++) {
124d07906f0SJoseph Chen 		mem->enable = false;
125d07906f0SJoseph Chen 		phandle = fdt32_to_cpu(*list2++);
126d07906f0SJoseph Chen 		ret = uclass_get_device_by_phandle_id(UCLASS_REGULATOR,
127d07906f0SJoseph Chen 						phandle, &mem->dev);
128d07906f0SJoseph Chen 		if (ret)
129d07906f0SJoseph Chen 			return ret;
130d07906f0SJoseph Chen 	}
131d07906f0SJoseph Chen 
132d07906f0SJoseph Chen #ifdef DEBUG
133d07906f0SJoseph Chen 	printf("assigned regulator mem:\n");
134d07906f0SJoseph Chen 	for (mem = pdata->regulators_mem; mem->dev; mem++)
135d07906f0SJoseph Chen 		printf("    %20s: suspend %s\n", mem->dev->name,
136d07906f0SJoseph Chen 		       mem->enable ? "enabling" : "disabled");
137d07906f0SJoseph Chen #endif
138d07906f0SJoseph Chen 	return 0;
139d07906f0SJoseph Chen }
140d07906f0SJoseph Chen 
141529b3b1eSJoseph Chen #ifdef CONFIG_IRQ
regulators_enable_assigned_state_mem(struct udevice * dev)142d07906f0SJoseph Chen static int regulators_enable_assigned_state_mem(struct udevice *dev)
143d07906f0SJoseph Chen {
144d07906f0SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
145d07906f0SJoseph Chen 	struct regulator_mem *mem;
146d07906f0SJoseph Chen 	int ret;
147d07906f0SJoseph Chen 
148d07906f0SJoseph Chen 	for (mem = pdata->regulators_mem; mem->dev; mem++) {
149d07906f0SJoseph Chen 		ret = regulator_set_suspend_enable(mem->dev, mem->enable);
150d07906f0SJoseph Chen 		if (ret)
151d07906f0SJoseph Chen 			printf("%s: suspend failed, ret=%d\n",
152d07906f0SJoseph Chen 			       mem->dev->name, ret);
153d07906f0SJoseph Chen 	}
154d07906f0SJoseph Chen 
155d07906f0SJoseph Chen 	return 0;
156d07906f0SJoseph Chen }
157d07906f0SJoseph Chen 
regulators_suspend(struct udevice * dev)158d07906f0SJoseph Chen static void regulators_suspend(struct udevice *dev)
159d07906f0SJoseph Chen {
160d07906f0SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
161d07906f0SJoseph Chen 
162d07906f0SJoseph Chen 	if (pdata->regulators_mem)
163d07906f0SJoseph Chen 		regulators_enable_assigned_state_mem(dev);
164d07906f0SJoseph Chen 	else
165d07906f0SJoseph Chen 		regulators_enable_state_mem(false);
166d07906f0SJoseph Chen }
167d07906f0SJoseph Chen 
pmics_ops(bool suspend)1689c7e8d73SJoseph Chen static void pmics_ops(bool suspend)
1699c7e8d73SJoseph Chen {
1709c7e8d73SJoseph Chen 	struct udevice *dev;
1719c7e8d73SJoseph Chen 
1729c7e8d73SJoseph Chen 	for (uclass_first_device(UCLASS_PMIC, &dev);
1739c7e8d73SJoseph Chen 	     dev;
1749c7e8d73SJoseph Chen 	     uclass_next_device(&dev)) {
1759c7e8d73SJoseph Chen 		if (suspend)
1769c7e8d73SJoseph Chen 			pmic_suspend(dev);
1779c7e8d73SJoseph Chen 		else
1789c7e8d73SJoseph Chen 			pmic_resume(dev);
1799c7e8d73SJoseph Chen 	}
1809c7e8d73SJoseph Chen }
1819c7e8d73SJoseph Chen 
pmics_suspend(void)1829c7e8d73SJoseph Chen static void pmics_suspend(void)
1839c7e8d73SJoseph Chen {
1849c7e8d73SJoseph Chen 	pmics_ops(true);
1859c7e8d73SJoseph Chen }
1869c7e8d73SJoseph Chen 
pmics_resume(void)1879c7e8d73SJoseph Chen static void pmics_resume(void)
1889c7e8d73SJoseph Chen {
1899c7e8d73SJoseph Chen 	pmics_ops(false);
1909c7e8d73SJoseph Chen }
191529b3b1eSJoseph Chen #endif
charge_animation_ofdata_to_platdata(struct udevice * dev)192f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
193f1c8ecceSJoseph Chen {
194f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
195f1c8ecceSJoseph Chen 
196d6653c12SJoseph Chen 	/* charge mode */
197d6653c12SJoseph Chen 	pdata->uboot_charge =
198d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
199d6653c12SJoseph Chen 	pdata->android_charge =
200d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
201f1c8ecceSJoseph Chen 
202038c1ecaSShunqing Chen 	pdata->auto_exit_charge =
203038c1ecaSShunqing Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-auto", 0);
204d6653c12SJoseph Chen 	pdata->exit_charge_level =
205d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
206d6653c12SJoseph Chen 	pdata->exit_charge_voltage =
207d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
208f23c35a8SJoseph Chen 
209f23c35a8SJoseph Chen 	pdata->low_power_voltage =
210f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0);
211f23c35a8SJoseph Chen 
212d6653c12SJoseph Chen 	pdata->screen_on_voltage =
213d6653c12SJoseph Chen 		dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
214f23c35a8SJoseph Chen 	pdata->system_suspend =
215f23c35a8SJoseph Chen 		dev_read_u32_default(dev, "rockchip,system-suspend", 0);
216f1c8ecceSJoseph Chen 
217e7f9facbSJoseph Chen 	pdata->auto_wakeup_interval =
218e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0);
219e7f9facbSJoseph Chen 	pdata->auto_wakeup_screen_invert =
220e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0);
221e7f9facbSJoseph Chen 
222e7f9facbSJoseph Chen 	pdata->auto_off_screen_interval =
223e7f9facbSJoseph Chen 		dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15);
224e7f9facbSJoseph Chen 
225f23c35a8SJoseph Chen 	if (pdata->screen_on_voltage > pdata->exit_charge_voltage)
226f23c35a8SJoseph Chen 		pdata->screen_on_voltage = pdata->exit_charge_voltage;
227f1c8ecceSJoseph Chen 
228038c1ecaSShunqing Chen 	if (pdata->auto_exit_charge && !pdata->auto_wakeup_interval)
229038c1ecaSShunqing Chen 		pdata->auto_wakeup_interval = 10;
230038c1ecaSShunqing Chen 
231d07906f0SJoseph Chen 	/* Not allow failure */
232d07906f0SJoseph Chen 	if (regulators_parse_assigned_mem_state(dev)) {
233d07906f0SJoseph Chen 		printf("Failed to parse assigned mem state\n");
234d07906f0SJoseph Chen 		return -EINVAL;
235d07906f0SJoseph Chen 	}
236d07906f0SJoseph Chen 
237d6653c12SJoseph Chen 	debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
238f23c35a8SJoseph Chen 	      "lp_voltage=%d%%, screen_on=%dmv\n",
239d6653c12SJoseph Chen 	      pdata->uboot_charge, pdata->android_charge,
240d6653c12SJoseph Chen 	      pdata->exit_charge_level, pdata->exit_charge_voltage,
241f23c35a8SJoseph Chen 	      pdata->low_power_voltage, pdata->screen_on_voltage);
242f1c8ecceSJoseph Chen 
243f1c8ecceSJoseph Chen 	return 0;
244f1c8ecceSJoseph Chen }
245f1c8ecceSJoseph Chen 
check_key_press(struct udevice * dev)246e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev)
247f1c8ecceSJoseph Chen {
248e7f9facbSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
249e7f9facbSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
2509116e2f9SJoseph Chen 	u32 event;
2518b436ce5SElaine Zhang 
2528b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC
2539116e2f9SJoseph Chen 	if (priv->rtc && rtc_alarm_trigger(priv->rtc)) {
2548b436ce5SElaine Zhang 		printf("rtc alarm trigger...\n");
2558b436ce5SElaine Zhang 		return KEY_PRESS_LONG_DOWN;
2568b436ce5SElaine Zhang 	}
2579116e2f9SJoseph Chen #endif
2589116e2f9SJoseph Chen 	event = key_read(KEY_POWER);
2599116e2f9SJoseph Chen 	if (event < 0)
2609116e2f9SJoseph Chen 		printf("read power key failed: %d\n", event);
2619116e2f9SJoseph Chen 	else if (event == KEY_PRESS_DOWN)
26293aee2d9SJoseph Chen 		printf("power key pressed...\n");
2639116e2f9SJoseph Chen 	else if (event == KEY_PRESS_LONG_DOWN)
26493aee2d9SJoseph Chen 		printf("power key long pressed...\n");
265f1c8ecceSJoseph Chen 
2669116e2f9SJoseph Chen 	/* auto screen invert ? */
2679116e2f9SJoseph Chen 	if (pdata->auto_wakeup_interval &&
2689116e2f9SJoseph Chen 	    pdata->auto_wakeup_screen_invert) {
269e7f9facbSJoseph Chen 		if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) {
270e7f9facbSJoseph Chen 			/* Value is updated in timer interrupt */
271e7f9facbSJoseph Chen 			priv->auto_wakeup_key_state = KEY_PRESS_NONE;
2729116e2f9SJoseph Chen 			event = KEY_PRESS_DOWN;
273e7f9facbSJoseph Chen 		}
274e7f9facbSJoseph Chen 	}
2759116e2f9SJoseph Chen 
2769116e2f9SJoseph Chen 	/* auto screen off (while not enable auto screen invert) ? */
27723858492SShunqing Chen 	if (!pdata->auto_wakeup_screen_invert &&
27823858492SShunqing Chen 	    pdata->auto_off_screen_interval) {
27993aee2d9SJoseph Chen 		if (priv->auto_screen_off_timeout &&
28093aee2d9SJoseph Chen 		    get_timer(priv->auto_screen_off_timeout) >
281e7f9facbSJoseph Chen 		    pdata->auto_off_screen_interval * 1000) {	/* 1000ms */
2829116e2f9SJoseph Chen 			event = KEY_PRESS_DOWN;
283e7f9facbSJoseph Chen 			printf("Auto screen off\n");
284e7f9facbSJoseph Chen 		}
285e7f9facbSJoseph Chen 	}
286e7f9facbSJoseph Chen 
2879116e2f9SJoseph Chen 	return event;
288f1c8ecceSJoseph Chen }
289f1c8ecceSJoseph Chen 
290175257e4SJoseph Chen /*
291175257e4SJoseph Chen  * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup
292175257e4SJoseph Chen  * period timer is useless.
293175257e4SJoseph Chen  */
2943415d4ebSJoseph Chen #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND)
system_suspend_enter(struct udevice * dev)2952e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev)
296175257e4SJoseph Chen {
297175257e4SJoseph Chen 	return 0;
298175257e4SJoseph Chen }
299175257e4SJoseph Chen 
autowakeup_timer_init(struct udevice * dev,uint32_t seconds)300175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {}
autowakeup_timer_uninit(void)301175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {}
302175257e4SJoseph Chen 
303175257e4SJoseph Chen #else
system_suspend_enter(struct udevice * dev)3042e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev)
305b177a917SJoseph Chen {
3062e68f6b5SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
3072e68f6b5SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
3082e68f6b5SJoseph Chen 
3092e68f6b5SJoseph Chen 	/*
3102e68f6b5SJoseph Chen 	 * When cpu is in wfi and we try to give a long key press event without
3112e68f6b5SJoseph Chen 	 * key release, cpu would wakeup and enter wfi again immediately. So
3122e68f6b5SJoseph Chen 	 * here is the problem: cpu can only wakeup when long key released.
3132e68f6b5SJoseph Chen 	 *
3142e68f6b5SJoseph Chen 	 * Actually, we want cpu can detect long key event without key release,
3152e68f6b5SJoseph Chen 	 * so we give a suspend delay timeout for cpu to detect this.
3162e68f6b5SJoseph Chen 	 */
3172e68f6b5SJoseph Chen 	if (priv->suspend_delay_timeout &&
3182e68f6b5SJoseph Chen 	    get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS)
3192e68f6b5SJoseph Chen 		return 0;
3202e68f6b5SJoseph Chen 
321f23c35a8SJoseph Chen 	if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) {
322b177a917SJoseph Chen 		printf("\nSystem suspend: ");
323992f4e77SJoseph Chen 		putc('0');
324b177a917SJoseph Chen 		local_irq_disable();
32506b61291SJoseph Chen 		putc('1');
326d07906f0SJoseph Chen 		regulators_suspend(dev);
327b177a917SJoseph Chen 		putc('2');
3289c7e8d73SJoseph Chen 		pmics_suspend();
329b177a917SJoseph Chen 		putc('3');
33006b61291SJoseph Chen 		irqs_suspend();
33188949342SJoseph Chen 		putc('4');
33206b61291SJoseph Chen 		device_suspend();
33306b61291SJoseph Chen 		putc('5');
334b177a917SJoseph Chen 		putc('\n');
335b177a917SJoseph Chen 
336b177a917SJoseph Chen 		/* Trap into ATF for low power mode */
337b177a917SJoseph Chen 		cpu_suspend(0, psci_system_suspend);
338b177a917SJoseph Chen 
339b177a917SJoseph Chen 		putc('\n');
34088949342SJoseph Chen 		putc('4');
34188949342SJoseph Chen 		device_resume();
342b177a917SJoseph Chen 		putc('3');
343b177a917SJoseph Chen 		irqs_resume();
344b177a917SJoseph Chen 		putc('2');
3459c7e8d73SJoseph Chen 		pmics_resume();
346b177a917SJoseph Chen 		putc('1');
34706b61291SJoseph Chen 		local_irq_enable();
34806b61291SJoseph Chen 		putc('0');
349b177a917SJoseph Chen 		putc('\n');
3508fc5ae06SJoseph Chen 	} else {
3515eac14dbSJoseph Chen 		irqs_suspend();
3528fc5ae06SJoseph Chen 		printf("\nWfi\n");
3538fc5ae06SJoseph Chen 		wfi();
3542e68f6b5SJoseph Chen 		putc('1');
3555eac14dbSJoseph Chen 		irqs_resume();
3568fc5ae06SJoseph Chen 	}
357b177a917SJoseph Chen 
3582e68f6b5SJoseph Chen 	priv->suspend_delay_timeout = get_timer(0);
3592e68f6b5SJoseph Chen 
360b177a917SJoseph Chen 	/*
361b177a917SJoseph Chen 	 * We must wait for key release event finish, otherwise
362b177a917SJoseph Chen 	 * we may read key state too early.
363b177a917SJoseph Chen 	 */
364b177a917SJoseph Chen 	mdelay(300);
365b177a917SJoseph Chen 
366b177a917SJoseph Chen 	return 0;
367f1c8ecceSJoseph Chen }
368ebe3d004SJoseph Chen 
autowake_timer_handler(int irq,void * data)3699116e2f9SJoseph Chen static void autowake_timer_handler(int irq, void *data)
37079244e4cSJoseph Chen {
37179244e4cSJoseph Chen 	struct udevice *dev = data;
37279244e4cSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
37379244e4cSJoseph Chen 	static long long count;
37479244e4cSJoseph Chen 
37579244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
37679244e4cSJoseph Chen 
37779244e4cSJoseph Chen 	priv->auto_wakeup_key_state = KEY_PRESS_DOWN;
37879244e4cSJoseph Chen 	printf("auto wakeup count: %lld\n", ++count);
37979244e4cSJoseph Chen }
38079244e4cSJoseph Chen 
autowakeup_timer_init(struct udevice * dev,uint32_t seconds)38179244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds)
38279244e4cSJoseph Chen {
38379244e4cSJoseph Chen 	uint64_t period = 24000000ULL * seconds;
38479244e4cSJoseph Chen 
38579244e4cSJoseph Chen 	/* Disable before conifg */
38679244e4cSJoseph Chen 	writel(0, TIMER_BASE + TIMER_CTRL);
38779244e4cSJoseph Chen 
38879244e4cSJoseph Chen 	/* Config */
38979244e4cSJoseph Chen 	writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
39079244e4cSJoseph Chen 	writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
39179244e4cSJoseph Chen 	writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
39279244e4cSJoseph Chen 	writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
39379244e4cSJoseph Chen 
39479244e4cSJoseph Chen 	/* IRQ */
3959116e2f9SJoseph Chen 	irq_install_handler(TIMER_IRQ, autowake_timer_handler, dev);
39679244e4cSJoseph Chen 	irq_handler_enable(TIMER_IRQ);
39779244e4cSJoseph Chen }
39879244e4cSJoseph Chen 
autowakeup_timer_uninit(void)39979244e4cSJoseph Chen static void autowakeup_timer_uninit(void)
40079244e4cSJoseph Chen {
401094465a9SJoseph Chen 	writel(0, TIMER_BASE + TIMER_CTRL);
402094465a9SJoseph Chen 
403094465a9SJoseph Chen 	irq_handler_disable(TIMER_IRQ);
40479244e4cSJoseph Chen 	irq_free_handler(TIMER_IRQ);
40579244e4cSJoseph Chen }
406175257e4SJoseph Chen #endif
40779244e4cSJoseph Chen 
408f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP
charge_show_bmp(const char * name)409f23c35a8SJoseph Chen static void charge_show_bmp(const char *name)
410f23c35a8SJoseph Chen {
411f23c35a8SJoseph Chen 	rockchip_show_bmp(name);
412f23c35a8SJoseph Chen }
413f23c35a8SJoseph Chen 
charge_show_logo(void)414f23c35a8SJoseph Chen static void charge_show_logo(void)
415f23c35a8SJoseph Chen {
416f23c35a8SJoseph Chen 	rockchip_show_logo();
417f23c35a8SJoseph Chen }
418f23c35a8SJoseph Chen #else
charge_show_bmp(const char * name)419f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {}
charge_show_logo(void)420f23c35a8SJoseph Chen static void charge_show_logo(void) {}
421f23c35a8SJoseph Chen #endif
422f23c35a8SJoseph Chen 
4231b3009deSJoseph Chen #ifdef CONFIG_LED
leds_update(struct udevice * dev,int soc)4241b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc)
4251b3009deSJoseph Chen {
4261b3009deSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
4271b3009deSJoseph Chen 	static int old_soc = -1;
4281b3009deSJoseph Chen 	int ret, ledst;
4291b3009deSJoseph Chen 
4301b3009deSJoseph Chen 	if (old_soc == soc)
4311b3009deSJoseph Chen 		return 0;
4321b3009deSJoseph Chen 
4331b3009deSJoseph Chen 	old_soc = soc;
4341b3009deSJoseph Chen 	if (priv->led_charging) {
4351b3009deSJoseph Chen 		ledst = (soc < 100) ? LEDST_ON : LEDST_OFF;
4361b3009deSJoseph Chen 		ret = led_set_state(priv->led_charging, ledst);
4371b3009deSJoseph Chen 		if (ret) {
4381b3009deSJoseph Chen 			printf("set charging led %s failed, ret=%d\n",
4391b3009deSJoseph Chen 			       (ledst == LEDST_ON) ? "ON" : "OFF", ret);
4401b3009deSJoseph Chen 			return ret;
4411b3009deSJoseph Chen 		}
4421b3009deSJoseph Chen 	}
4431b3009deSJoseph Chen 
4441b3009deSJoseph Chen 	if (priv->led_full) {
4451b3009deSJoseph Chen 		ledst = (soc == 100) ? LEDST_ON : LEDST_OFF;
4461b3009deSJoseph Chen 		ret = led_set_state(priv->led_full, ledst);
4471b3009deSJoseph Chen 		if (ret) {
4481b3009deSJoseph Chen 			printf("set charging full led %s failed, ret=%d\n",
4491b3009deSJoseph Chen 			       ledst == LEDST_ON ? "ON" : "OFF", ret);
4501b3009deSJoseph Chen 			return ret;
4511b3009deSJoseph Chen 		}
4521b3009deSJoseph Chen 	}
4531b3009deSJoseph Chen 
4541b3009deSJoseph Chen 	return 0;
4551b3009deSJoseph Chen }
4561b3009deSJoseph Chen #else
leds_update(struct udevice * dev,int soc)4571b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) { return 0; }
4581b3009deSJoseph Chen #endif
4591b3009deSJoseph Chen 
fg_charger_get_chrg_online(struct udevice * dev)4604d083e3fSJoseph Chen static int fg_charger_get_chrg_online(struct udevice *dev)
4614d083e3fSJoseph Chen {
4624d083e3fSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
4634d083e3fSJoseph Chen 	struct udevice *charger;
4644d083e3fSJoseph Chen 
4654d083e3fSJoseph Chen 	charger = priv->charger ? : priv->fg;
4664d083e3fSJoseph Chen 
4674d083e3fSJoseph Chen 	return fuel_gauge_get_chrg_online(charger);
4684d083e3fSJoseph Chen }
4694d083e3fSJoseph Chen 
sys_shutdown(struct udevice * dev)470ceb76aadSJoseph Chen static int sys_shutdown(struct udevice *dev)
471ceb76aadSJoseph Chen {
472ceb76aadSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
473ceb76aadSJoseph Chen 	struct udevice *pmic = priv->pmic;
474ceb76aadSJoseph Chen 	struct udevice *fg = priv->fg;
475ceb76aadSJoseph Chen 
476ceb76aadSJoseph Chen 	/*
477ceb76aadSJoseph Chen 	 * Call the fuel/charge again to update something specific
478ceb76aadSJoseph Chen 	 * before shutdown. This fix a scene:
479ceb76aadSJoseph Chen 	 *
480ceb76aadSJoseph Chen 	 * Plug out charger which auto wakeup cpu from a long time system suspend,
481ceb76aadSJoseph Chen 	 * fuel/charge need to update something before shutdown.
482ceb76aadSJoseph Chen 	 */
483ceb76aadSJoseph Chen 	fg_charger_get_chrg_online(dev);
484ceb76aadSJoseph Chen 	fuel_gauge_get_voltage(fg);
485ceb76aadSJoseph Chen 	fuel_gauge_update_get_soc(fg);
486ceb76aadSJoseph Chen 
487ceb76aadSJoseph Chen 	flushc();
488ceb76aadSJoseph Chen 	mdelay(50);
489ceb76aadSJoseph Chen 	pmic_shutdown(pmic);
490ceb76aadSJoseph Chen 
491ceb76aadSJoseph Chen 	mdelay(500);
492ceb76aadSJoseph Chen 	printf("Cpu should never reach here, shutdown failed !\n");
493ceb76aadSJoseph Chen 
494ceb76aadSJoseph Chen 	return 0;
495ceb76aadSJoseph Chen }
496ceb76aadSJoseph Chen 
charge_extrem_low_power(struct udevice * dev)497f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev)
498f23c35a8SJoseph Chen {
499f23c35a8SJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
500f23c35a8SJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
501f23c35a8SJoseph Chen 	struct udevice *fg = priv->fg;
502f23c35a8SJoseph Chen 	int voltage, soc, charging = 1;
5033a77b1c5SJoseph Chen 	int first_poll_fg = 1;
50479244e4cSJoseph Chen 	static int timer_initialized;
505f23c35a8SJoseph Chen 
506f23c35a8SJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
507f23c35a8SJoseph Chen 	if (voltage < 0)
508f23c35a8SJoseph Chen 		return -EINVAL;
509f23c35a8SJoseph Chen 
510f23c35a8SJoseph Chen 	while (voltage < pdata->low_power_voltage + 50) {
5113a77b1c5SJoseph Chen 		if (!first_poll_fg)
5123a77b1c5SJoseph Chen 			mdelay(FUEL_GAUGE_POLL_MS);
5133a77b1c5SJoseph Chen 
5143a77b1c5SJoseph Chen 		first_poll_fg = 0;
5153a77b1c5SJoseph Chen 
516f23c35a8SJoseph Chen 		/* Check charger online */
5174d083e3fSJoseph Chen 		charging = fg_charger_get_chrg_online(dev);
518f23c35a8SJoseph Chen 		if (charging <= 0) {
51979244e4cSJoseph Chen 			printf("%s: Not charging, online=%d. Shutdown...\n",
52079244e4cSJoseph Chen 			       __func__, charging);
521ceb76aadSJoseph Chen 			sys_shutdown(dev);
522f23c35a8SJoseph Chen 			continue;
523f23c35a8SJoseph Chen 		}
524f23c35a8SJoseph Chen 
52579244e4cSJoseph Chen 		/* Enable auto wakeup */
52679244e4cSJoseph Chen 		if (!timer_initialized) {
52779244e4cSJoseph Chen 			timer_initialized = 1;
52879244e4cSJoseph Chen 			autowakeup_timer_init(dev, 5);
52979244e4cSJoseph Chen 		}
53079244e4cSJoseph Chen 
531f23c35a8SJoseph Chen 		/*
532f23c35a8SJoseph Chen 		 * Just for fuel gauge to update something important,
533f23c35a8SJoseph Chen 		 * including charge current, coulometer or other.
534f23c35a8SJoseph Chen 		 */
5354f9cab27SJoseph Chen 		soc = fuel_gauge_update_get_soc(fg);
536f23c35a8SJoseph Chen 		if (soc < 0 || soc > 100) {
537f23c35a8SJoseph Chen 			printf("get soc failed: %d\n", soc);
538f23c35a8SJoseph Chen 			continue;
539f23c35a8SJoseph Chen 		}
540f23c35a8SJoseph Chen 
5411b3009deSJoseph Chen 		/* Update led */
5429116e2f9SJoseph Chen 		leds_update(dev, soc);
5431b3009deSJoseph Chen 
544f23c35a8SJoseph Chen 		printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
545f23c35a8SJoseph Chen 		       pdata->low_power_voltage, voltage);
546f23c35a8SJoseph Chen 
547f23c35a8SJoseph Chen 		/* System suspend */
5482e68f6b5SJoseph Chen 		system_suspend_enter(dev);
549f23c35a8SJoseph Chen 
550f23c35a8SJoseph Chen 		/* Update voltage */
551f23c35a8SJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
552f23c35a8SJoseph Chen 		if (voltage < 0) {
553f23c35a8SJoseph Chen 			printf("get voltage failed: %d\n", voltage);
554f23c35a8SJoseph Chen 			continue;
555f23c35a8SJoseph Chen 		}
5565fc2a70cSJoseph Chen 
5575fc2a70cSJoseph Chen 		if (ctrlc()) {
5585fc2a70cSJoseph Chen 			printf("Extrem low charge: exit by ctrl+c\n");
5595fc2a70cSJoseph Chen 			break;
5605fc2a70cSJoseph Chen 		}
561f23c35a8SJoseph Chen 	}
562f23c35a8SJoseph Chen 
56379244e4cSJoseph Chen 	autowakeup_timer_uninit();
56479244e4cSJoseph Chen 
565f23c35a8SJoseph Chen 	return 0;
566f23c35a8SJoseph Chen }
567f23c35a8SJoseph Chen 
charge_animation_show(struct udevice * dev)568f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
569f1c8ecceSJoseph Chen {
570f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
571f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
572f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
573f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
574a8b9d026SJoseph Chen 	const char *preboot = env_get("preboot");
575f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
576f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
577f1c8ecceSJoseph Chen 	bool screen_on = true;
578f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
5795e804741SJoseph Chen 	ulong delta;
580f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
5819116e2f9SJoseph Chen 	int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RECALC_IDX;
582d6653c12SJoseph Chen 	int soc, voltage, current, key_state;
583f23c35a8SJoseph Chen 	int i, charging = 1, ret;
5848f9ff705SJoseph Chen 	int boot_mode;
5855e804741SJoseph Chen 	int first_poll_fg = 1;
5869116e2f9SJoseph Chen 	bool lp_shutdown = false;
587f1c8ecceSJoseph Chen 
588f23c35a8SJoseph Chen /*
589f23c35a8SJoseph Chen  * Check sequence:
590f23c35a8SJoseph Chen  *
591f23c35a8SJoseph Chen  * 1. Extrem low power charge?
592f23c35a8SJoseph Chen  * 2. Preboot cmd?
593f23c35a8SJoseph Chen  * 3. Valid boot mode?
594f23c35a8SJoseph Chen  * 4. U-Boot charge enabled by dts config?
595f23c35a8SJoseph Chen  * 5. Screen off before charge?
596f23c35a8SJoseph Chen  * 6. Enter charge !
597f23c35a8SJoseph Chen  *
598f23c35a8SJoseph Chen  */
59961a7a6d6SJoseph Chen 	if (!fuel_gauge_bat_is_exist(fg)) {
60061a7a6d6SJoseph Chen 		printf("Exit charge: battery is not exist\n");
60161a7a6d6SJoseph Chen 		return 0;
60261a7a6d6SJoseph Chen 	}
60361a7a6d6SJoseph Chen 
604f23c35a8SJoseph Chen 	/* Extrem low power charge */
605f23c35a8SJoseph Chen 	ret = charge_extrem_low_power(dev);
606f23c35a8SJoseph Chen 	if (ret < 0) {
607f23c35a8SJoseph Chen 		printf("extrem low power charge failed, ret=%d\n", ret);
608f23c35a8SJoseph Chen 		return ret;
609f23c35a8SJoseph Chen 	}
610f23c35a8SJoseph Chen 
611a8b9d026SJoseph Chen 	/* If there is preboot command, exit */
61261a7a6d6SJoseph Chen 	if (preboot && !strstr(preboot, "dvfs")) {
6137ae45834SJoseph Chen 		printf("Exit charge: due to preboot cmd '%s'\n", preboot);
614a8b9d026SJoseph Chen 		return 0;
615a8b9d026SJoseph Chen 	}
616a8b9d026SJoseph Chen 
617f23c35a8SJoseph Chen 	/* Not valid charge mode, exit */
618d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER
6198f9ff705SJoseph Chen 	boot_mode = rockchip_get_boot_mode();
620221b5220SJoseph Chen 	if ((boot_mode != BOOT_MODE_CHARGING) &&
621221b5220SJoseph Chen 	    (boot_mode != BOOT_MODE_UNDEFINE)) {
6227ae45834SJoseph Chen 		printf("Exit charge: due to boot mode\n");
6238f9ff705SJoseph Chen 		return 0;
6248f9ff705SJoseph Chen 	}
6258f9ff705SJoseph Chen #endif
6269116e2f9SJoseph Chen 	/* No charger online + low power? shutdown */
627038c1ecaSShunqing Chen 	charging = fg_charger_get_chrg_online(dev);
628038c1ecaSShunqing Chen 	if (charging <= 0 && pdata->auto_exit_charge) {
629038c1ecaSShunqing Chen 		soc = fuel_gauge_update_get_soc(fg);
630038c1ecaSShunqing Chen 		voltage = fuel_gauge_get_voltage(fg);
631038c1ecaSShunqing Chen 		if (soc < pdata->exit_charge_level) {
632038c1ecaSShunqing Chen 			printf("soc(%d%%) < exit_charge_level(%d%%)\n",
633038c1ecaSShunqing Chen 			       soc, pdata->exit_charge_level);
6349116e2f9SJoseph Chen 			lp_shutdown = true;
635038c1ecaSShunqing Chen 		}
636038c1ecaSShunqing Chen 		if (voltage < pdata->exit_charge_voltage) {
637038c1ecaSShunqing Chen 			printf("voltage(%d) < exit_charge_voltage(%d)\n",
638038c1ecaSShunqing Chen 			       voltage, pdata->exit_charge_voltage);
6399116e2f9SJoseph Chen 			lp_shutdown = true;
640038c1ecaSShunqing Chen 		}
6419116e2f9SJoseph Chen 		if (lp_shutdown) {
642038c1ecaSShunqing Chen 			printf("Not charging and low power, Shutdown...\n");
643038c1ecaSShunqing Chen 			show_idx = IMAGE_LOWPOWER_IDX(image_num);
644038c1ecaSShunqing Chen 			charge_show_bmp(image[show_idx].name);
645ceb76aadSJoseph Chen 
646ceb76aadSJoseph Chen 			sys_shutdown(dev);
647038c1ecaSShunqing Chen 		}
648038c1ecaSShunqing Chen 	}
649221b5220SJoseph Chen 
6509116e2f9SJoseph Chen 	/* No charger online, exit */
651cf49f6adSJoseph Chen 	if (charging <= 0) {
6527ae45834SJoseph Chen 		printf("Exit charge: due to charger offline\n");
653616056c9Sshengfei Xu 		return 0;
654cf49f6adSJoseph Chen 	}
655616056c9Sshengfei Xu 
656f23c35a8SJoseph Chen 	/* Enter android charge, set property for kernel */
657d6653c12SJoseph Chen 	if (pdata->android_charge) {
658d6653c12SJoseph Chen 		env_update("bootargs", "androidboot.mode=charger");
659d6653c12SJoseph Chen 		printf("Android charge mode\n");
660d6653c12SJoseph Chen 	}
661d6653c12SJoseph Chen 
662f23c35a8SJoseph Chen 	/* Not enable U-Boot charge, exit */
663cf49f6adSJoseph Chen 	if (!pdata->uboot_charge) {
6647ae45834SJoseph Chen 		printf("Exit charge: due to not enable uboot charge\n");
665d6653c12SJoseph Chen 		return 0;
666cf49f6adSJoseph Chen 	}
667d6653c12SJoseph Chen 
668f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
669f1c8ecceSJoseph Chen 	if (voltage < 0) {
670f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
671f1c8ecceSJoseph Chen 		return -EINVAL;
672f1c8ecceSJoseph Chen 	}
673f1c8ecceSJoseph Chen 
674f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
675d6653c12SJoseph Chen 	if (voltage <= pdata->screen_on_voltage + 50) {
676f1c8ecceSJoseph Chen 		screen_on = false;
677f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
678f23c35a8SJoseph Chen 		charge_show_bmp(NULL);
679f1c8ecceSJoseph Chen 	}
680f1c8ecceSJoseph Chen 
681e7f9facbSJoseph Chen 	/* Auto wakeup */
682e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval) {
683e7f9facbSJoseph Chen 		printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval);
68479244e4cSJoseph Chen 		autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
685e7f9facbSJoseph Chen 	}
686e7f9facbSJoseph Chen 
687175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */
688175257e4SJoseph Chen #ifdef CONFIG_IRQ
689d6653c12SJoseph Chen 	printf("Enter U-Boot charging mode\n");
690175257e4SJoseph Chen #else
6919116e2f9SJoseph Chen 	printf("Enter U-Boot charging mode(IRQ)\n");
692175257e4SJoseph Chen #endif
693f1c8ecceSJoseph Chen 
694d6653c12SJoseph Chen 	charge_start = get_timer(0);
6955e804741SJoseph Chen 	delta = get_timer(0);
696e7f9facbSJoseph Chen 
697f1c8ecceSJoseph Chen 	/* Charging ! */
698f1c8ecceSJoseph Chen 	while (1) {
6995e804741SJoseph Chen 		/*
7005e804741SJoseph Chen 		 * At the most time, fuel gauge is usually a i2c device, we
7015e804741SJoseph Chen 		 * should avoid read/write all the time. We had better set
7025e804741SJoseph Chen 		 * poll seconds to update fuel gauge info.
7035e804741SJoseph Chen 		 */
7045e804741SJoseph Chen 		if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS)
7055e804741SJoseph Chen 			goto show_images;
7065e804741SJoseph Chen 
7075e804741SJoseph Chen 		delta = get_timer(0);
7085e804741SJoseph Chen 
709f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
710f1c8ecceSJoseph Chen 
711fd62311eSJoseph Chen 		/*
712fd62311eSJoseph Chen 		 * Most fuel gauge is I2C interface, it shouldn't be interrupted
713ebe3d004SJoseph Chen 		 * during transfer. The power key event depends on interrupt, so
714ebe3d004SJoseph Chen 		 * we should disable local irq when update fuel gauge.
715fd62311eSJoseph Chen 		 */
716fd62311eSJoseph Chen 		local_irq_disable();
717fd62311eSJoseph Chen 
718f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
7194d083e3fSJoseph Chen 		charging = fg_charger_get_chrg_online(dev);
720f1c8ecceSJoseph Chen 		if (charging <= 0) {
721f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
722f1c8ecceSJoseph Chen 			       charging);
723255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
724255e5751SWenping Zhang 			/*
72577bac292SZorro Liu 			 * If charger is plug out during charging, display poweroff
72677bac292SZorro Liu 			 * image before device power off.
727255e5751SWenping Zhang 			 * Irq must be enable if CONFIG_IRQ is defined, because
728255e5751SWenping Zhang 			 * ebc need to wait irq to indicate frame is complete.
729255e5751SWenping Zhang 			 */
730255e5751SWenping Zhang 			local_irq_enable();
7319116e2f9SJoseph Chen 
73277bac292SZorro Liu 			ret = rockchip_eink_show_charge_logo(EINK_LOGO_POWEROFF);
733255e5751SWenping Zhang 			if (ret != 0)
734255e5751SWenping Zhang 				printf("Eink display reset logo failed\n");
7359116e2f9SJoseph Chen 
736255e5751SWenping Zhang 			local_irq_disable();
737255e5751SWenping Zhang #endif
738ceb76aadSJoseph Chen 			sys_shutdown(dev);
739f1c8ecceSJoseph Chen 			continue;
740f1c8ecceSJoseph Chen 		}
741f1c8ecceSJoseph Chen 
742f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
743f1c8ecceSJoseph Chen 
744f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
7454f9cab27SJoseph Chen 		soc = fuel_gauge_update_get_soc(fg);
746f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
747f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
748f1c8ecceSJoseph Chen 			continue;
749f1c8ecceSJoseph Chen 		}
750f1c8ecceSJoseph Chen 
751f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
752f1c8ecceSJoseph Chen 		if (voltage < 0) {
753f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
754f1c8ecceSJoseph Chen 			continue;
755f1c8ecceSJoseph Chen 		}
756f1c8ecceSJoseph Chen 
757d6653c12SJoseph Chen 		current = fuel_gauge_get_current(fg);
758d6653c12SJoseph Chen 		if (current == -ENOSYS) {
759d6653c12SJoseph Chen 			printf("get current failed: %d\n", current);
760d6653c12SJoseph Chen 			continue;
761d6653c12SJoseph Chen 		}
762fd62311eSJoseph Chen 
763ebe3d004SJoseph Chen 		first_poll_fg = 0;
764fd62311eSJoseph Chen 		local_irq_enable();
765fd62311eSJoseph Chen 
766038c1ecaSShunqing Chen 		if (pdata->auto_exit_charge) {
767038c1ecaSShunqing Chen 			/* Is able to boot now ? */
768038c1ecaSShunqing Chen 			if (pdata->exit_charge_level &&
769038c1ecaSShunqing Chen 			    soc >= pdata->exit_charge_level) {
770038c1ecaSShunqing Chen 				printf("soc(%d%%) exit charge animation...\n",
771038c1ecaSShunqing Chen 				       soc);
772038c1ecaSShunqing Chen 				break;
773038c1ecaSShunqing Chen 			}
774038c1ecaSShunqing Chen 			if (pdata->exit_charge_voltage &&
775038c1ecaSShunqing Chen 			    voltage >= pdata->exit_charge_voltage) {
776038c1ecaSShunqing Chen 				printf("vol(%d) exit charge animation...\n",
777038c1ecaSShunqing Chen 				       voltage);
778038c1ecaSShunqing Chen 				break;
779038c1ecaSShunqing Chen 			}
780038c1ecaSShunqing Chen 		}
781038c1ecaSShunqing Chen 
7825e804741SJoseph Chen show_images:
783f1c8ecceSJoseph Chen 		/*
784f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
785f1c8ecceSJoseph Chen 		 * is not good to know what happen.
786f1c8ecceSJoseph Chen 		 */
787f1c8ecceSJoseph Chen 		if (!debug_start)
788f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
7899116e2f9SJoseph Chen 		if (get_timer(debug_start) > 30000) {
790f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
791ebe3d004SJoseph Chen 			printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, "
792ebe3d004SJoseph Chen 			       "online=%d, screen_on=%d\n",
793d6653c12SJoseph Chen 			       get_timer(0) / 1000, soc, voltage,
794d6653c12SJoseph Chen 			       current, charging, screen_on);
795f1c8ecceSJoseph Chen 		}
796f1c8ecceSJoseph Chen 
7971b3009deSJoseph Chen 		/* Update leds */
7989116e2f9SJoseph Chen 		leds_update(dev, soc);
7991b3009deSJoseph Chen 
800f1c8ecceSJoseph Chen 		/*
801f23c35a8SJoseph Chen 		 * If ever lowpower screen off, force screen_on=false, which
802f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
803f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
804f1c8ecceSJoseph Chen 		 */
805f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
806f1c8ecceSJoseph Chen 			screen_on = false;
807f1c8ecceSJoseph Chen 
808f1c8ecceSJoseph Chen 		/*
809f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
810ebe3d004SJoseph Chen 		 * 'ever_lowpower_screen_off' means enter the while(1) loop with
811f1c8ecceSJoseph Chen 		 * screen off.
812f1c8ecceSJoseph Chen 		 */
813f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
814d6653c12SJoseph Chen 		    (voltage > pdata->screen_on_voltage)) {
815f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
816f1c8ecceSJoseph Chen 			screen_on = true;
8179116e2f9SJoseph Chen 			show_idx = IMAGE_RECALC_IDX;
818f1c8ecceSJoseph Chen 		}
819f1c8ecceSJoseph Chen 
820f1c8ecceSJoseph Chen 		/*
8219116e2f9SJoseph Chen 		 * IMAGE_RECALC_IDX means show_idx show be update by start_idx.
822f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
8239116e2f9SJoseph Chen 		 * as IMAGE_RECALC_IDX which updates images index from start_idx
824f1c8ecceSJoseph Chen 		 * that calculate by current soc.
825f1c8ecceSJoseph Chen 		 */
8269116e2f9SJoseph Chen 		if (show_idx == IMAGE_RECALC_IDX) {
827ebe3d004SJoseph Chen 			for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) {
828f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
8299116e2f9SJoseph Chen 				if ((soc >= image[i].soc) && (soc < image[i + 1].soc)) {
830f1c8ecceSJoseph Chen 					start_idx = i;
831f1c8ecceSJoseph Chen 					break;
832f1c8ecceSJoseph Chen 				}
833f1c8ecceSJoseph Chen 
834f1c8ecceSJoseph Chen 				if (soc >= 100) {
835ebe3d004SJoseph Chen 					start_idx = IMAGE_SOC_100_IDX(image_num);
836f1c8ecceSJoseph Chen 					break;
837f1c8ecceSJoseph Chen 				}
838f1c8ecceSJoseph Chen 			}
839f1c8ecceSJoseph Chen 
840f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
841f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
842f1c8ecceSJoseph Chen 
843f1c8ecceSJoseph Chen 			/* Mark start index and start time */
844f1c8ecceSJoseph Chen 			show_idx = start_idx;
845f1c8ecceSJoseph Chen 			show_start = get_timer(0);
846f1c8ecceSJoseph Chen 		}
847f1c8ecceSJoseph Chen 
848f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
849f1c8ecceSJoseph Chen 
850255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
851255e5751SWenping Zhang 		/*
852255e5751SWenping Zhang 		 * Device is auto wakeup from suspend, if it's eink display,
853255e5751SWenping Zhang 		 * screen will display the last image after suspend, so
854255e5751SWenping Zhang 		 * we should update the image to show the approximate
855255e5751SWenping Zhang 		 * battery power if battery is charging to next level.
856255e5751SWenping Zhang 		 */
857255e5751SWenping Zhang 		if (pdata->auto_wakeup_interval &&
858255e5751SWenping Zhang 		    priv->auto_wakeup_key_state == KEY_PRESS_DOWN &&
859255e5751SWenping Zhang 		    !screen_on) {
860255e5751SWenping Zhang 			if (soc >= image[old_show_idx + 1].soc &&
861255e5751SWenping Zhang 			    soc < 100) {
862255e5751SWenping Zhang 				int ret;
863255e5751SWenping Zhang 				int logo_type = EINK_LOGO_CHARGING_0;
864255e5751SWenping Zhang 
865255e5751SWenping Zhang 				logo_type = logo_type << (old_show_idx + 1);
866255e5751SWenping Zhang 				ret = rockchip_eink_show_charge_logo(logo_type);
867255e5751SWenping Zhang 				/*
868255e5751SWenping Zhang 				 * only change the logic if eink is
869255e5751SWenping Zhang 				 * actually exist
870255e5751SWenping Zhang 				 */
871255e5751SWenping Zhang 				if (ret == 0) {
872255e5751SWenping Zhang 					printf("Update image id[%d] for eink\n",
873255e5751SWenping Zhang 					       old_show_idx + 1);
874255e5751SWenping Zhang 					old_show_idx++;
875255e5751SWenping Zhang 				}
876255e5751SWenping Zhang 			}
877255e5751SWenping Zhang 		}
878255e5751SWenping Zhang 		/*
879255e5751SWenping Zhang 		 * If battery capacity is charged to 100%, exit charging
880255e5751SWenping Zhang 		 * animation and boot android system.
881255e5751SWenping Zhang 		 */
882255e5751SWenping Zhang 		if (soc >= 100) {
883255e5751SWenping Zhang 			int ret;
884255e5751SWenping Zhang 			int logo_type = EINK_LOGO_CHARGING_5;
885255e5751SWenping Zhang 
886255e5751SWenping Zhang 			ret = rockchip_eink_show_charge_logo(logo_type);
887255e5751SWenping Zhang 			/* Only change the logic if eink is acutally exist */
888255e5751SWenping Zhang 			if (ret == 0) {
889255e5751SWenping Zhang 				printf("battery FULL,exit charge animation\n");
890255e5751SWenping Zhang 				mdelay(20);
891255e5751SWenping Zhang 				break;
892255e5751SWenping Zhang 			}
893255e5751SWenping Zhang 		}
894255e5751SWenping Zhang #endif
895f1c8ecceSJoseph Chen 		/* Step3: show images */
896f1c8ecceSJoseph Chen 		if (screen_on) {
89793aee2d9SJoseph Chen 			/* Don't call 'charge_show_bmp' unless image changed */
89893aee2d9SJoseph Chen 			if (old_show_idx != show_idx) {
899255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
900255e5751SWenping Zhang 				int logo_type = EINK_LOGO_CHARGING_0;
901255e5751SWenping Zhang 
902255e5751SWenping Zhang 				rockchip_eink_show_charge_logo(logo_type <<
903255e5751SWenping Zhang 							       show_idx);
904255e5751SWenping Zhang #endif
90593aee2d9SJoseph Chen 				old_show_idx = show_idx;
906f1c8ecceSJoseph Chen 				debug("SHOW: %s\n", image[show_idx].name);
907f23c35a8SJoseph Chen 				charge_show_bmp(image[show_idx].name);
90893aee2d9SJoseph Chen 			}
909ebe3d004SJoseph Chen 			/* Re-calculate timeout to off screen */
910e7f9facbSJoseph Chen 			if (priv->auto_screen_off_timeout == 0)
911e7f9facbSJoseph Chen 				priv->auto_screen_off_timeout = get_timer(0);
912f1c8ecceSJoseph Chen 		} else {
9139116e2f9SJoseph Chen 			/* Entering low power suspend mode !!! */
914e7f9facbSJoseph Chen 			priv->auto_screen_off_timeout = 0;
9152e68f6b5SJoseph Chen 			system_suspend_enter(dev);
916f1c8ecceSJoseph Chen 		}
917f1c8ecceSJoseph Chen 
918f1c8ecceSJoseph Chen 		mdelay(5);
919f1c8ecceSJoseph Chen 
9209116e2f9SJoseph Chen 		/* It's time to show next image ? */
921f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
922f1c8ecceSJoseph Chen 			show_start = get_timer(0);
923f1c8ecceSJoseph Chen 			show_idx++;
924ebe3d004SJoseph Chen 			if (show_idx > IMAGE_SOC_100_IDX(image_num))
9259116e2f9SJoseph Chen 				show_idx = IMAGE_RECALC_IDX;
926f1c8ecceSJoseph Chen 		}
927f1c8ecceSJoseph Chen 
928f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
929f1c8ecceSJoseph Chen 
930f1c8ecceSJoseph Chen 		/*
931f1c8ecceSJoseph Chen 		 * Step4: check key event.
932f1c8ecceSJoseph Chen 		 *
933f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
934f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
935f1c8ecceSJoseph Chen 		 */
936e7f9facbSJoseph Chen 		key_state = check_key_press(dev);
937b177a917SJoseph Chen 		if (key_state == KEY_PRESS_DOWN) {
9389116e2f9SJoseph Chen 			/* Clear current image index, recalc image index */
9399116e2f9SJoseph Chen 			old_show_idx = IMAGE_RECALC_IDX;
9409116e2f9SJoseph Chen 			show_idx = IMAGE_RECALC_IDX;
941f1c8ecceSJoseph Chen 
942f1c8ecceSJoseph Chen 			/*
943ebe3d004SJoseph Chen 			 *	Reverse the screen state
944f1c8ecceSJoseph Chen 			 *
945f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
946f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
947f1c8ecceSJoseph Chen 			 *
948f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
949f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
950f1c8ecceSJoseph Chen 			 */
951ebe3d004SJoseph Chen 			if (screen_on) {
952255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
953255e5751SWenping Zhang 				int type = EINK_LOGO_CHARGING_0 << start_idx;
954255e5751SWenping Zhang 				/*
955255e5751SWenping Zhang 				 * Show current battery capacity before suspend
956255e5751SWenping Zhang 				 * if it's eink display, because eink screen
957255e5751SWenping Zhang 				 * will continue to display the last image
958255e5751SWenping Zhang 				 * after suspend, so user can get the
959255e5751SWenping Zhang 				 * approximate capacity by image displayed.
960255e5751SWenping Zhang 				 */
961255e5751SWenping Zhang 				ret = rockchip_eink_show_charge_logo(type);
962255e5751SWenping Zhang 				/* only change the logic if eink display ok */
963255e5751SWenping Zhang 				if (ret == 0)
964255e5751SWenping Zhang 					old_show_idx = start_idx;
965255e5751SWenping Zhang #endif
966ebe3d004SJoseph Chen 				charge_show_bmp(NULL); /* Turn off screen */
967f1c8ecceSJoseph Chen 				screen_on = false;
9682e68f6b5SJoseph Chen 				priv->suspend_delay_timeout = get_timer(0);
969ebe3d004SJoseph Chen 			} else {
970f1c8ecceSJoseph Chen 				screen_on = true;
971ebe3d004SJoseph Chen 			}
9722e68f6b5SJoseph Chen 
9732e68f6b5SJoseph Chen 			printf("screen %s\n", screen_on ? "on" : "off");
974b177a917SJoseph Chen 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
975ebe3d004SJoseph Chen 			/* Set screen_on=true anyway when key long pressed */
976f1c8ecceSJoseph Chen 			if (!screen_on)
977f1c8ecceSJoseph Chen 				screen_on = true;
978f1c8ecceSJoseph Chen 
9792e68f6b5SJoseph Chen 			printf("screen %s\n", screen_on ? "on" : "off");
9802e68f6b5SJoseph Chen 
981f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
982d6653c12SJoseph Chen 			if (soc < pdata->exit_charge_level) {
983f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
984d6653c12SJoseph Chen 				       soc, pdata->exit_charge_level);
985f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
986ebe3d004SJoseph Chen 				show_idx = IMAGE_LOWPOWER_IDX(image_num);
987f1c8ecceSJoseph Chen 				continue;
988f1c8ecceSJoseph Chen 			}
989f1c8ecceSJoseph Chen 
990d6653c12SJoseph Chen 			if (voltage < pdata->exit_charge_voltage) {
991f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
992d6653c12SJoseph Chen 				       voltage, pdata->exit_charge_voltage);
993f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
994ebe3d004SJoseph Chen 				show_idx = IMAGE_LOWPOWER_IDX(image_num);
995f1c8ecceSJoseph Chen 				continue;
996f1c8ecceSJoseph Chen 			}
997f1c8ecceSJoseph Chen 
998f1c8ecceSJoseph Chen 			/* Success exit charging */
999f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
1000f23c35a8SJoseph Chen 			charge_show_logo();
1001f1c8ecceSJoseph Chen 			break;
1002f1c8ecceSJoseph Chen 		} else {
1003f1c8ecceSJoseph Chen 			/* Do nothing */
1004f1c8ecceSJoseph Chen 		}
1005f1c8ecceSJoseph Chen 
1006f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
1007f1c8ecceSJoseph Chen 
1008d6653c12SJoseph Chen 		/* Step5: Exit by ctrl+c */
10091367bfe3SJoseph Chen 		if (ctrlc()) {
1010d6653c12SJoseph Chen 			if (voltage >= pdata->screen_on_voltage)
1011f23c35a8SJoseph Chen 				charge_show_logo();
10121367bfe3SJoseph Chen 			printf("Exit charge, due to ctrl+c\n");
10131367bfe3SJoseph Chen 			break;
10141367bfe3SJoseph Chen 		}
1015f1c8ecceSJoseph Chen 	}
1016f1c8ecceSJoseph Chen 
1017e7f9facbSJoseph Chen 	if (pdata->auto_wakeup_interval)
1018e7f9facbSJoseph Chen 		autowakeup_timer_uninit();
1019e7f9facbSJoseph Chen 
1020f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
1021f1c8ecceSJoseph Chen 	if (ms >= 1000) {
1022f1c8ecceSJoseph Chen 		sec = ms / 1000;
1023f1c8ecceSJoseph Chen 		ms = ms % 1000;
1024f1c8ecceSJoseph Chen 	}
1025f1c8ecceSJoseph Chen 
1026f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
1027f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
1028f1c8ecceSJoseph Chen 
1029f1c8ecceSJoseph Chen 	return 0;
1030f1c8ecceSJoseph Chen }
1031f1c8ecceSJoseph Chen 
fg_charger_get_device(struct udevice ** fuel_gauge,struct udevice ** charger)10324d083e3fSJoseph Chen static int fg_charger_get_device(struct udevice **fuel_gauge,
10334d083e3fSJoseph Chen 				 struct udevice **charger)
10344d083e3fSJoseph Chen {
10354d083e3fSJoseph Chen 	struct udevice *dev;
10364d083e3fSJoseph Chen 	struct uclass *uc;
10374d083e3fSJoseph Chen 	int ret, cap;
10384d083e3fSJoseph Chen 
10394d083e3fSJoseph Chen 	*fuel_gauge = NULL,
10404d083e3fSJoseph Chen 	*charger = NULL;
10414d083e3fSJoseph Chen 
10424d083e3fSJoseph Chen 	ret = uclass_get(UCLASS_FG, &uc);
10434d083e3fSJoseph Chen 	if (ret)
10444d083e3fSJoseph Chen 		return ret;
10454d083e3fSJoseph Chen 
10464d083e3fSJoseph Chen 	for (uclass_first_device(UCLASS_FG, &dev);
10474d083e3fSJoseph Chen 	     dev;
10484d083e3fSJoseph Chen 	     uclass_next_device(&dev)) {
10494d083e3fSJoseph Chen 		cap = fuel_gauge_capability(dev);
10504d083e3fSJoseph Chen 		if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) {
10514d083e3fSJoseph Chen 			*fuel_gauge = dev;
10524d083e3fSJoseph Chen 			*charger = NULL;
10534d083e3fSJoseph Chen 		} else if (cap == FG_CAP_FUEL_GAUGE) {
10544d083e3fSJoseph Chen 			*fuel_gauge = dev;
10554d083e3fSJoseph Chen 		} else if (cap == FG_CAP_CHARGER) {
10564d083e3fSJoseph Chen 			*charger = dev;
10574d083e3fSJoseph Chen 		}
10584d083e3fSJoseph Chen 	}
10594d083e3fSJoseph Chen 
10604d083e3fSJoseph Chen 	return (*fuel_gauge) ? 0 : -ENODEV;
10614d083e3fSJoseph Chen }
10624d083e3fSJoseph Chen 
1063f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
1064f1c8ecceSJoseph Chen 	.show = charge_animation_show,
1065f1c8ecceSJoseph Chen };
1066f1c8ecceSJoseph Chen 
charge_animation_probe(struct udevice * dev)1067f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
1068f1c8ecceSJoseph Chen {
1069f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
1070*1d4a0494SXiaoDong Huang 	__maybe_unused struct udevice *rk_pm_cfg;
1071f23c35a8SJoseph Chen 	int ret, soc;
1072f1c8ecceSJoseph Chen 
1073*1d4a0494SXiaoDong Huang #ifdef CONFIG_ROCKCHIP_PM_CONFIG
1074*1d4a0494SXiaoDong Huang 	ret = uclass_get_device_by_driver(UCLASS_MISC,
1075*1d4a0494SXiaoDong Huang 					  DM_GET_DRIVER(rockchip_pm_config),
1076*1d4a0494SXiaoDong Huang 					  &rk_pm_cfg);
1077*1d4a0494SXiaoDong Huang 	if (ret) {
1078*1d4a0494SXiaoDong Huang 		if (ret == -ENODEV)
1079*1d4a0494SXiaoDong Huang 			printf("Can't find rockchip_pm_config\n");
1080*1d4a0494SXiaoDong Huang 		else
1081*1d4a0494SXiaoDong Huang 			printf("Get rockchip_pm_config failed: %d\n", ret);
1082*1d4a0494SXiaoDong Huang 	}
1083*1d4a0494SXiaoDong Huang #endif
1084*1d4a0494SXiaoDong Huang 
1085f23c35a8SJoseph Chen 	/* Get PMIC: used for power off system  */
1086ebe3d004SJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic);
1087f1c8ecceSJoseph Chen 	if (ret) {
1088cf49f6adSJoseph Chen 		if (ret == -ENODEV)
1089cf49f6adSJoseph Chen 			printf("Can't find PMIC\n");
1090cf49f6adSJoseph Chen 		else
1091f1c8ecceSJoseph Chen 			printf("Get UCLASS PMIC failed: %d\n", ret);
1092f1c8ecceSJoseph Chen 		return ret;
1093f1c8ecceSJoseph Chen 	}
1094f1c8ecceSJoseph Chen 
10954d083e3fSJoseph Chen 	/* Get fuel gauge and charger(If need) */
10964d083e3fSJoseph Chen 	ret = fg_charger_get_device(&priv->fg, &priv->charger);
1097f1c8ecceSJoseph Chen 	if (ret) {
1098cf49f6adSJoseph Chen 		if (ret == -ENODEV)
109931ab5b3bSJoseph Chen 			debug("Can't find FG\n");
1100cf49f6adSJoseph Chen 		else
110131ab5b3bSJoseph Chen 			debug("Get UCLASS FG failed: %d\n", ret);
1102f1c8ecceSJoseph Chen 		return ret;
1103f1c8ecceSJoseph Chen 	}
1104f1c8ecceSJoseph Chen 
11058b436ce5SElaine Zhang 	/* Get rtc: used for power on */
1106ebe3d004SJoseph Chen 	ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc);
11078b436ce5SElaine Zhang 	if (ret) {
11088b436ce5SElaine Zhang 		if (ret == -ENODEV)
11098b436ce5SElaine Zhang 			debug("Can't find RTC\n");
11108b436ce5SElaine Zhang 		else
11118b436ce5SElaine Zhang 			debug("Get UCLASS RTC failed: %d\n", ret);
11128b436ce5SElaine Zhang 	}
11138b436ce5SElaine Zhang 
111479244e4cSJoseph Chen 	/* Get PWRKEY: used for wakeup and turn off/on LCD */
1115d0e4711bSJoseph Chen 	if (!key_exist(KEY_POWER)) {
111631ab5b3bSJoseph Chen 		debug("Can't find power key\n");
1117f23c35a8SJoseph Chen 		return -EINVAL;
1118f23c35a8SJoseph Chen 	}
1119f23c35a8SJoseph Chen 
1120f23c35a8SJoseph Chen 	/* Initialize charge current */
11214f9cab27SJoseph Chen 	soc = fuel_gauge_update_get_soc(priv->fg);
1122f23c35a8SJoseph Chen 	if (soc < 0 || soc > 100) {
112331ab5b3bSJoseph Chen 		debug("get soc failed: %d\n", soc);
1124f23c35a8SJoseph Chen 		return -EINVAL;
1125f23c35a8SJoseph Chen 	}
1126f23c35a8SJoseph Chen 
11271b3009deSJoseph Chen 	/* Get leds */
11281b3009deSJoseph Chen #ifdef CONFIG_LED
11291b3009deSJoseph Chen 	ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging);
11301b3009deSJoseph Chen 	if (!ret)
11311b3009deSJoseph Chen 		printf("Found Charging LED\n");
11321b3009deSJoseph Chen 	ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full);
11331b3009deSJoseph Chen 	if (!ret)
11341b3009deSJoseph Chen 		printf("Found Charging-Full LED\n");
11351b3009deSJoseph Chen #endif
11361b3009deSJoseph Chen 
1137f23c35a8SJoseph Chen 	/* Get charge images */
1138f1c8ecceSJoseph Chen 	priv->image = image;
1139f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
1140f1c8ecceSJoseph Chen 
1141f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
1142f1c8ecceSJoseph Chen 
1143f1c8ecceSJoseph Chen 	return 0;
1144f1c8ecceSJoseph Chen }
1145f1c8ecceSJoseph Chen 
1146f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
1147d6653c12SJoseph Chen 	{ .compatible = "rockchip,uboot-charge" },
1148f1c8ecceSJoseph Chen 	{ },
1149f1c8ecceSJoseph Chen };
1150f1c8ecceSJoseph Chen 
1151f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
1152f1c8ecceSJoseph Chen 	.name = "charge-animation",
1153f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
1154f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
1155f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
1156f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
1157f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
1158f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
1159f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
1160f1c8ecceSJoseph Chen };
1161