xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision f1c8eccefa013f79019efacc657ed34b39d3f581)
1*f1c8ecceSJoseph Chen /*
2*f1c8ecceSJoseph Chen  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3*f1c8ecceSJoseph Chen  *
4*f1c8ecceSJoseph Chen  * SPDX-License-Identifier:     GPL-2.0+
5*f1c8ecceSJoseph Chen  */
6*f1c8ecceSJoseph Chen 
7*f1c8ecceSJoseph Chen #include <common.h>
8*f1c8ecceSJoseph Chen #include <dm.h>
9*f1c8ecceSJoseph Chen #include <errno.h>
10*f1c8ecceSJoseph Chen #include <key.h>
11*f1c8ecceSJoseph Chen #include <linux/input.h>
12*f1c8ecceSJoseph Chen #include <pwm.h>
13*f1c8ecceSJoseph Chen #include <power/charge_display.h>
14*f1c8ecceSJoseph Chen #include <power/fuel_gauge.h>
15*f1c8ecceSJoseph Chen #include <power/pmic.h>
16*f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h>
17*f1c8ecceSJoseph Chen #include <power/regulator.h>
18*f1c8ecceSJoseph Chen #include <video_rockchip.h>
19*f1c8ecceSJoseph Chen 
20*f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR;
21*f1c8ecceSJoseph Chen 
22*f1c8ecceSJoseph Chen #define LONG_PRESSED_TIME			2000 /* 2s */
23*f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET			-1
24*f1c8ecceSJoseph Chen 
25*f1c8ecceSJoseph Chen enum key_event {
26*f1c8ecceSJoseph Chen 	KEY_NOT_PRESSED = 0,
27*f1c8ecceSJoseph Chen 	KEY_SHORT_PRESSED,
28*f1c8ecceSJoseph Chen 	KEY_LONG_PRESSED,
29*f1c8ecceSJoseph Chen };
30*f1c8ecceSJoseph Chen 
31*f1c8ecceSJoseph Chen struct charge_image {
32*f1c8ecceSJoseph Chen 	const char *name;
33*f1c8ecceSJoseph Chen 	int soc;
34*f1c8ecceSJoseph Chen 	int period;	/* ms */
35*f1c8ecceSJoseph Chen };
36*f1c8ecceSJoseph Chen 
37*f1c8ecceSJoseph Chen struct charge_animation_priv {
38*f1c8ecceSJoseph Chen 	struct udevice *pmic;
39*f1c8ecceSJoseph Chen 	struct udevice *fg;
40*f1c8ecceSJoseph Chen 	struct udevice *pwrkey;
41*f1c8ecceSJoseph Chen 	const struct charge_image *image;
42*f1c8ecceSJoseph Chen 	int image_num;
43*f1c8ecceSJoseph Chen };
44*f1c8ecceSJoseph Chen 
45*f1c8ecceSJoseph Chen struct charge_animation_pdata {
46*f1c8ecceSJoseph Chen 	int screen_on_voltage_threshold;
47*f1c8ecceSJoseph Chen 	int power_on_voltage_threshold;
48*f1c8ecceSJoseph Chen 	int power_on_soc_threshold;
49*f1c8ecceSJoseph Chen 	bool suspend_to_sram;
50*f1c8ecceSJoseph Chen 	bool auto_start_kernel;
51*f1c8ecceSJoseph Chen };
52*f1c8ecceSJoseph Chen 
53*f1c8ecceSJoseph Chen static int charge_animation_threshold_soc(struct udevice *dev)
54*f1c8ecceSJoseph Chen {
55*f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
56*f1c8ecceSJoseph Chen 
57*f1c8ecceSJoseph Chen 	if (!pdata)
58*f1c8ecceSJoseph Chen 		return -ENOSYS;
59*f1c8ecceSJoseph Chen 
60*f1c8ecceSJoseph Chen 	return pdata->power_on_soc_threshold;
61*f1c8ecceSJoseph Chen }
62*f1c8ecceSJoseph Chen 
63*f1c8ecceSJoseph Chen static int charge_animation_threshold_voltage(struct udevice *dev)
64*f1c8ecceSJoseph Chen {
65*f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
66*f1c8ecceSJoseph Chen 
67*f1c8ecceSJoseph Chen 	if (!pdata)
68*f1c8ecceSJoseph Chen 		return -ENOSYS;
69*f1c8ecceSJoseph Chen 
70*f1c8ecceSJoseph Chen 	return pdata->power_on_voltage_threshold;
71*f1c8ecceSJoseph Chen }
72*f1c8ecceSJoseph Chen 
73*f1c8ecceSJoseph Chen /*
74*f1c8ecceSJoseph Chen  * IF you want to use your own charge images, please:
75*f1c8ecceSJoseph Chen  *
76*f1c8ecceSJoseph Chen  * 1. Update the following 'image[]' to point to your own images;
77*f1c8ecceSJoseph Chen  * 2. You must set the failed image as last one and soc = -1 !!!
78*f1c8ecceSJoseph Chen  */
79*f1c8ecceSJoseph Chen static const struct charge_image image[] = {
80*f1c8ecceSJoseph Chen 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
81*f1c8ecceSJoseph Chen 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
82*f1c8ecceSJoseph Chen 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
83*f1c8ecceSJoseph Chen 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
84*f1c8ecceSJoseph Chen 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
85*f1c8ecceSJoseph Chen 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
86*f1c8ecceSJoseph Chen 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
87*f1c8ecceSJoseph Chen };
88*f1c8ecceSJoseph Chen 
89*f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev)
90*f1c8ecceSJoseph Chen {
91*f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
92*f1c8ecceSJoseph Chen 
93*f1c8ecceSJoseph Chen 	if (dev_read_bool(dev, "charge,suspend-to-sram"))
94*f1c8ecceSJoseph Chen 		pdata->suspend_to_sram = true;
95*f1c8ecceSJoseph Chen 	else
96*f1c8ecceSJoseph Chen 		pdata->suspend_to_sram = false;
97*f1c8ecceSJoseph Chen 
98*f1c8ecceSJoseph Chen 	if (dev_read_bool(dev, "charge,auto-start-kernel"))
99*f1c8ecceSJoseph Chen 		pdata->auto_start_kernel = true;
100*f1c8ecceSJoseph Chen 	else
101*f1c8ecceSJoseph Chen 		pdata->auto_start_kernel = false;
102*f1c8ecceSJoseph Chen 
103*f1c8ecceSJoseph Chen 	pdata->power_on_soc_threshold =
104*f1c8ecceSJoseph Chen 		dev_read_u32_default(dev, "power-on-soc-threshold", 0);
105*f1c8ecceSJoseph Chen 	pdata->power_on_voltage_threshold =
106*f1c8ecceSJoseph Chen 		dev_read_u32_default(dev, "power-on-voltage-threshold", 0);
107*f1c8ecceSJoseph Chen 	pdata->screen_on_voltage_threshold =
108*f1c8ecceSJoseph Chen 		dev_read_u32_default(dev, "screen-on-voltage-threshold", 0);
109*f1c8ecceSJoseph Chen 
110*f1c8ecceSJoseph Chen 	if (pdata->screen_on_voltage_threshold >
111*f1c8ecceSJoseph Chen 	    pdata->power_on_voltage_threshold)
112*f1c8ecceSJoseph Chen 		pdata->screen_on_voltage_threshold =
113*f1c8ecceSJoseph Chen 					pdata->power_on_voltage_threshold;
114*f1c8ecceSJoseph Chen 
115*f1c8ecceSJoseph Chen 	debug("threshold soc=%d%%, voltage=%dmv, screen_on=%dmv, suspend=%d\n",
116*f1c8ecceSJoseph Chen 	      pdata->power_on_soc_threshold, pdata->power_on_voltage_threshold,
117*f1c8ecceSJoseph Chen 	      pdata->screen_on_voltage_threshold, pdata->suspend_to_sram);
118*f1c8ecceSJoseph Chen 
119*f1c8ecceSJoseph Chen 	return 0;
120*f1c8ecceSJoseph Chen }
121*f1c8ecceSJoseph Chen 
122*f1c8ecceSJoseph Chen static int check_key_press(struct udevice *dev, bool restar_time)
123*f1c8ecceSJoseph Chen {
124*f1c8ecceSJoseph Chen 	static unsigned long pressed_time;
125*f1c8ecceSJoseph Chen 	static int old_state;
126*f1c8ecceSJoseph Chen 	int report = KEY_NOT_PRESSED;
127*f1c8ecceSJoseph Chen 	int state;
128*f1c8ecceSJoseph Chen 
129*f1c8ecceSJoseph Chen 	/* just for restart time */
130*f1c8ecceSJoseph Chen 	if (restar_time) {
131*f1c8ecceSJoseph Chen 		pressed_time = get_timer(0);
132*f1c8ecceSJoseph Chen 		goto out;
133*f1c8ecceSJoseph Chen 	}
134*f1c8ecceSJoseph Chen 
135*f1c8ecceSJoseph Chen 	state = key_read(dev);
136*f1c8ecceSJoseph Chen 	if (state < 0) {
137*f1c8ecceSJoseph Chen 		printf("read power key failed: %d\n", state);
138*f1c8ecceSJoseph Chen 		goto out;
139*f1c8ecceSJoseph Chen 	}
140*f1c8ecceSJoseph Chen 
141*f1c8ecceSJoseph Chen 	/* Start time is not initialized, let's do it */
142*f1c8ecceSJoseph Chen 	if (!pressed_time && (state == KEY_PRESS_DOWN)) {
143*f1c8ecceSJoseph Chen 		pressed_time = get_timer(0);
144*f1c8ecceSJoseph Chen 		return KEY_NOT_PRESSED;
145*f1c8ecceSJoseph Chen 	} else {
146*f1c8ecceSJoseph Chen 		debug("key state = %d\n", state);
147*f1c8ecceSJoseph Chen 
148*f1c8ecceSJoseph Chen 		if (state == KEY_PRESS_DOWN) {
149*f1c8ecceSJoseph Chen 			if (get_timer(pressed_time) >= LONG_PRESSED_TIME) {
150*f1c8ecceSJoseph Chen 				report = KEY_LONG_PRESSED;
151*f1c8ecceSJoseph Chen 				pressed_time = 0;
152*f1c8ecceSJoseph Chen 				old_state = KEY_LONG_PRESSED;
153*f1c8ecceSJoseph Chen 				printf("power key long pressed...\n");
154*f1c8ecceSJoseph Chen 			}
155*f1c8ecceSJoseph Chen 		/*
156*f1c8ecceSJoseph Chen 		 * If you don't check 'old_state != KEY_LONG_PRESSED', it will
157*f1c8ecceSJoseph Chen 		 * treat 'key long pressed' release as a 'short key pressed'
158*f1c8ecceSJoseph Chen 		 * release.
159*f1c8ecceSJoseph Chen 		 */
160*f1c8ecceSJoseph Chen 		} else if ((state == KEY_PRESS_UP) &&
161*f1c8ecceSJoseph Chen 			   (old_state != KEY_LONG_PRESSED)) {
162*f1c8ecceSJoseph Chen 			report = KEY_SHORT_PRESSED;
163*f1c8ecceSJoseph Chen 			old_state = report;
164*f1c8ecceSJoseph Chen 			pressed_time = 0;
165*f1c8ecceSJoseph Chen 			printf("power key short pressed...\n");
166*f1c8ecceSJoseph Chen 		} else {
167*f1c8ecceSJoseph Chen 			report = KEY_NOT_PRESSED;
168*f1c8ecceSJoseph Chen 			old_state = report;
169*f1c8ecceSJoseph Chen 			pressed_time = 0;
170*f1c8ecceSJoseph Chen 			debug("power key Not pressed...\n");
171*f1c8ecceSJoseph Chen 		}
172*f1c8ecceSJoseph Chen 	}
173*f1c8ecceSJoseph Chen 
174*f1c8ecceSJoseph Chen out:
175*f1c8ecceSJoseph Chen 	return report;
176*f1c8ecceSJoseph Chen }
177*f1c8ecceSJoseph Chen 
178*f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev)
179*f1c8ecceSJoseph Chen {
180*f1c8ecceSJoseph Chen 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
181*f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
182*f1c8ecceSJoseph Chen 	const struct charge_image *image = priv->image;
183*f1c8ecceSJoseph Chen 	struct udevice *pwrkey = priv->pwrkey;
184*f1c8ecceSJoseph Chen 	struct udevice *pmic = priv->pmic;
185*f1c8ecceSJoseph Chen 	struct udevice *fg = priv->fg;
186*f1c8ecceSJoseph Chen 	int image_num = priv->image_num;
187*f1c8ecceSJoseph Chen 	bool ever_lowpower_screen_off = false;
188*f1c8ecceSJoseph Chen 	bool screen_on = true;
189*f1c8ecceSJoseph Chen 	ulong show_start = 0, charge_start = 0, debug_start = 0;
190*f1c8ecceSJoseph Chen 	ulong ms = 0, sec = 0;
191*f1c8ecceSJoseph Chen 	int start_idx = 0, show_idx = -1;
192*f1c8ecceSJoseph Chen 	int soc, voltage, key_state;
193*f1c8ecceSJoseph Chen 	int i, charging = 1;
194*f1c8ecceSJoseph Chen 
195*f1c8ecceSJoseph Chen 	/* Not charger online, exit */
196*f1c8ecceSJoseph Chen 	charging = fuel_gauge_get_chrg_online(fg);
197*f1c8ecceSJoseph Chen 	if (charging <= 0)
198*f1c8ecceSJoseph Chen 		return 0;
199*f1c8ecceSJoseph Chen 
200*f1c8ecceSJoseph Chen 	voltage = fuel_gauge_get_voltage(fg);
201*f1c8ecceSJoseph Chen 	if (voltage < 0) {
202*f1c8ecceSJoseph Chen 		printf("get voltage failed: %d\n", voltage);
203*f1c8ecceSJoseph Chen 		return -EINVAL;
204*f1c8ecceSJoseph Chen 	}
205*f1c8ecceSJoseph Chen 
206*f1c8ecceSJoseph Chen 	/* If low power, turn off screen */
207*f1c8ecceSJoseph Chen 	if (voltage <= pdata->screen_on_voltage_threshold + 50) {
208*f1c8ecceSJoseph Chen 		screen_on = false;
209*f1c8ecceSJoseph Chen 		ever_lowpower_screen_off = true;
210*f1c8ecceSJoseph Chen 		rockchip_show_bmp(NULL);
211*f1c8ecceSJoseph Chen 	}
212*f1c8ecceSJoseph Chen 
213*f1c8ecceSJoseph Chen 	charge_start = get_timer(0);
214*f1c8ecceSJoseph Chen 
215*f1c8ecceSJoseph Chen 	/* Charging ! */
216*f1c8ecceSJoseph Chen 	while (1) {
217*f1c8ecceSJoseph Chen 		debug("step1 (%d)... \n", screen_on);
218*f1c8ecceSJoseph Chen 
219*f1c8ecceSJoseph Chen 		/* Step1: Is charging now ? */
220*f1c8ecceSJoseph Chen 		charging = fuel_gauge_get_chrg_online(fg);
221*f1c8ecceSJoseph Chen 		if (charging <= 0) {
222*f1c8ecceSJoseph Chen 			printf("Not charging, online=%d. Shutdown...\n",
223*f1c8ecceSJoseph Chen 			       charging);
224*f1c8ecceSJoseph Chen 
225*f1c8ecceSJoseph Chen 			/* wait uart flush before shutdown */
226*f1c8ecceSJoseph Chen 			mdelay(500);
227*f1c8ecceSJoseph Chen 
228*f1c8ecceSJoseph Chen 			/* PMIC shutdown */
229*f1c8ecceSJoseph Chen 			pmic_shutdown(pmic);
230*f1c8ecceSJoseph Chen 
231*f1c8ecceSJoseph Chen 			printf("Cpu should never reach here, shutdown failed !\n");
232*f1c8ecceSJoseph Chen 			continue;
233*f1c8ecceSJoseph Chen 		}
234*f1c8ecceSJoseph Chen 
235*f1c8ecceSJoseph Chen 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
236*f1c8ecceSJoseph Chen 
237*f1c8ecceSJoseph Chen 		/* Step2: get soc and voltage */
238*f1c8ecceSJoseph Chen 		soc = fuel_gauge_get_soc(fg);
239*f1c8ecceSJoseph Chen 		if (soc < 0 || soc > 100) {
240*f1c8ecceSJoseph Chen 			printf("get soc failed: %d\n", soc);
241*f1c8ecceSJoseph Chen 			continue;
242*f1c8ecceSJoseph Chen 		}
243*f1c8ecceSJoseph Chen 
244*f1c8ecceSJoseph Chen 		voltage = fuel_gauge_get_voltage(fg);
245*f1c8ecceSJoseph Chen 		if (voltage < 0) {
246*f1c8ecceSJoseph Chen 			printf("get voltage failed: %d\n", voltage);
247*f1c8ecceSJoseph Chen 			continue;
248*f1c8ecceSJoseph Chen 		}
249*f1c8ecceSJoseph Chen 
250*f1c8ecceSJoseph Chen 		/*
251*f1c8ecceSJoseph Chen 		 * Just for debug, otherwise there will be nothing output which
252*f1c8ecceSJoseph Chen 		 * is not good to know what happen.
253*f1c8ecceSJoseph Chen 		 */
254*f1c8ecceSJoseph Chen 		if (!debug_start)
255*f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
256*f1c8ecceSJoseph Chen 		if (get_timer(debug_start) > 20000) {
257*f1c8ecceSJoseph Chen 			debug_start = get_timer(0);
258*f1c8ecceSJoseph Chen 			printf("soc=%d, vol=%d, online=%d, screen_on=%d, show_idx=%d, ever_off=%d\n",
259*f1c8ecceSJoseph Chen 			       soc, voltage, charging, screen_on, show_idx,
260*f1c8ecceSJoseph Chen 			       ever_lowpower_screen_off);
261*f1c8ecceSJoseph Chen 		}
262*f1c8ecceSJoseph Chen 
263*f1c8ecceSJoseph Chen 		/*
264*f1c8ecceSJoseph Chen 		 * If ever lowpower screen off, force screen on false, which
265*f1c8ecceSJoseph Chen 		 * means key event can't modify screen_on, only voltage higher
266*f1c8ecceSJoseph Chen 		 * then threshold can update screen_on=true;
267*f1c8ecceSJoseph Chen 		 */
268*f1c8ecceSJoseph Chen 		if (ever_lowpower_screen_off)
269*f1c8ecceSJoseph Chen 			screen_on = false;
270*f1c8ecceSJoseph Chen 
271*f1c8ecceSJoseph Chen 		/*
272*f1c8ecceSJoseph Chen 		 * Auto turn on screen when voltage higher than Vol screen on.
273*f1c8ecceSJoseph Chen 		 * 'ever_lowpower_screen_off' means enter while loop with
274*f1c8ecceSJoseph Chen 		 * screen off.
275*f1c8ecceSJoseph Chen 		 */
276*f1c8ecceSJoseph Chen 		if ((ever_lowpower_screen_off) &&
277*f1c8ecceSJoseph Chen 		    (voltage > pdata->screen_on_voltage_threshold)) {
278*f1c8ecceSJoseph Chen 			ever_lowpower_screen_off = false;
279*f1c8ecceSJoseph Chen 			screen_on = true;
280*f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
281*f1c8ecceSJoseph Chen 		}
282*f1c8ecceSJoseph Chen 
283*f1c8ecceSJoseph Chen 		/*
284*f1c8ecceSJoseph Chen 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
285*f1c8ecceSJoseph Chen 		 * When short key pressed event trigged, we will set show_idx
286*f1c8ecceSJoseph Chen 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
287*f1c8ecceSJoseph Chen 		 * that calculate by current soc.
288*f1c8ecceSJoseph Chen 		 */
289*f1c8ecceSJoseph Chen 		if (show_idx == IMAGE_SHOW_RESET) {
290*f1c8ecceSJoseph Chen 			for (i = 0; i < image_num - 2; i++) {
291*f1c8ecceSJoseph Chen 				/* Find out which image we start to show */
292*f1c8ecceSJoseph Chen 				if ((soc >= image[i].soc) &&
293*f1c8ecceSJoseph Chen 				    (soc < image[i + 1].soc)) {
294*f1c8ecceSJoseph Chen 					start_idx = i;
295*f1c8ecceSJoseph Chen 					break;
296*f1c8ecceSJoseph Chen 				}
297*f1c8ecceSJoseph Chen 
298*f1c8ecceSJoseph Chen 				if (soc >= 100) {
299*f1c8ecceSJoseph Chen 					start_idx = image_num - 2;
300*f1c8ecceSJoseph Chen 					break;
301*f1c8ecceSJoseph Chen 				}
302*f1c8ecceSJoseph Chen 			}
303*f1c8ecceSJoseph Chen 
304*f1c8ecceSJoseph Chen 			debug("%s: show_idx=%d, screen_on=%d\n",
305*f1c8ecceSJoseph Chen 			      __func__, show_idx, screen_on);
306*f1c8ecceSJoseph Chen 
307*f1c8ecceSJoseph Chen 			/* Mark start index and start time */
308*f1c8ecceSJoseph Chen 			show_idx = start_idx;
309*f1c8ecceSJoseph Chen 			show_start = get_timer(0);
310*f1c8ecceSJoseph Chen 		}
311*f1c8ecceSJoseph Chen 
312*f1c8ecceSJoseph Chen 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
313*f1c8ecceSJoseph Chen 
314*f1c8ecceSJoseph Chen 		/* Step3: show images */
315*f1c8ecceSJoseph Chen 		if (screen_on) {
316*f1c8ecceSJoseph Chen 			debug("SHOW: %s\n", image[show_idx].name);
317*f1c8ecceSJoseph Chen 			rockchip_show_bmp(image[show_idx].name);
318*f1c8ecceSJoseph Chen 		} else {
319*f1c8ecceSJoseph Chen 			/*
320*f1c8ecceSJoseph Chen 			 * TODO: enter low power mode:
321*f1c8ecceSJoseph Chen 			 *
322*f1c8ecceSJoseph Chen 			 * 1. cut off some regualtors;
323*f1c8ecceSJoseph Chen 			 * 2. set 24MHZ for some PLLs;
324*f1c8ecceSJoseph Chen 			 * 3. auto turn off screen when timout;
325*f1c8ecceSJoseph Chen 			 * 4. power key wakeup;
326*f1c8ecceSJoseph Chen 			 * 5. timer period wakeup for pmic fg ?
327*f1c8ecceSJoseph Chen 			 */
328*f1c8ecceSJoseph Chen 		}
329*f1c8ecceSJoseph Chen 
330*f1c8ecceSJoseph Chen 		mdelay(5);
331*f1c8ecceSJoseph Chen 
332*f1c8ecceSJoseph Chen 		/* Every image shows period */
333*f1c8ecceSJoseph Chen 		if (get_timer(show_start) > image[show_idx].period) {
334*f1c8ecceSJoseph Chen 			show_start = get_timer(0);
335*f1c8ecceSJoseph Chen 			/* Update to next image */
336*f1c8ecceSJoseph Chen 			show_idx++;
337*f1c8ecceSJoseph Chen 			if (show_idx > (image_num - 2))
338*f1c8ecceSJoseph Chen 				show_idx = IMAGE_SHOW_RESET;
339*f1c8ecceSJoseph Chen 		}
340*f1c8ecceSJoseph Chen 
341*f1c8ecceSJoseph Chen 		debug("step4 (%d)... \n", screen_on);
342*f1c8ecceSJoseph Chen 
343*f1c8ecceSJoseph Chen 		/*
344*f1c8ecceSJoseph Chen 		 * Step4: check key event.
345*f1c8ecceSJoseph Chen 		 *
346*f1c8ecceSJoseph Chen 		 * Short key event: turn on/off screen;
347*f1c8ecceSJoseph Chen 		 * Long key event: show logo and boot system or still charging.
348*f1c8ecceSJoseph Chen 		 */
349*f1c8ecceSJoseph Chen 		key_state = check_key_press(pwrkey, false);
350*f1c8ecceSJoseph Chen 		if (key_state == KEY_SHORT_PRESSED) {
351*f1c8ecceSJoseph Chen 			/* NULL means show nothing, ie. turn off screen */
352*f1c8ecceSJoseph Chen 			if (screen_on)
353*f1c8ecceSJoseph Chen 				rockchip_show_bmp(NULL);
354*f1c8ecceSJoseph Chen 
355*f1c8ecceSJoseph Chen 			/*
356*f1c8ecceSJoseph Chen 			 * Clear current image index, and show image
357*f1c8ecceSJoseph Chen 			 * from start_idx
358*f1c8ecceSJoseph Chen 			 */
359*f1c8ecceSJoseph Chen 			show_idx = IMAGE_SHOW_RESET;
360*f1c8ecceSJoseph Chen 
361*f1c8ecceSJoseph Chen 			/*
362*f1c8ecceSJoseph Chen 			 * We turn off screen by rockchip_show_bmp(NULL), so we
363*f1c8ecceSJoseph Chen 			 * should tell while loop to stop show images any more.
364*f1c8ecceSJoseph Chen 			 *
365*f1c8ecceSJoseph Chen 			 * If screen_on=false, means this short key pressed
366*f1c8ecceSJoseph Chen 			 * event turn on the screen and we need show images.
367*f1c8ecceSJoseph Chen 			 *
368*f1c8ecceSJoseph Chen 			 * If screen_on=true, means this short key pressed
369*f1c8ecceSJoseph Chen 			 * event turn off the screen and we never show images.
370*f1c8ecceSJoseph Chen 			 */
371*f1c8ecceSJoseph Chen 			if (screen_on)
372*f1c8ecceSJoseph Chen 				screen_on = false;
373*f1c8ecceSJoseph Chen 			else
374*f1c8ecceSJoseph Chen 				screen_on = true;
375*f1c8ecceSJoseph Chen 		} else if (key_state == KEY_LONG_PRESSED) {
376*f1c8ecceSJoseph Chen 			/* Only long pressed while screen off needs screen_on true */
377*f1c8ecceSJoseph Chen 			if (!screen_on)
378*f1c8ecceSJoseph Chen 				screen_on = true;
379*f1c8ecceSJoseph Chen 
380*f1c8ecceSJoseph Chen 			/* Is able to boot now ? */
381*f1c8ecceSJoseph Chen 			if (soc < pdata->power_on_soc_threshold) {
382*f1c8ecceSJoseph Chen 				printf("soc=%d%%, threshold soc=%d%%\n",
383*f1c8ecceSJoseph Chen 				       soc, pdata->power_on_soc_threshold);
384*f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
385*f1c8ecceSJoseph Chen 				/* 'true': just for clear time of check key */
386*f1c8ecceSJoseph Chen 				check_key_press(pwrkey, true);
387*f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
388*f1c8ecceSJoseph Chen 				continue;
389*f1c8ecceSJoseph Chen 			}
390*f1c8ecceSJoseph Chen 
391*f1c8ecceSJoseph Chen 			if (voltage < pdata->power_on_voltage_threshold) {
392*f1c8ecceSJoseph Chen 				printf("voltage=%dmv, threshold voltage=%dmv\n",
393*f1c8ecceSJoseph Chen 				       voltage, pdata->power_on_voltage_threshold);
394*f1c8ecceSJoseph Chen 				printf("Low power, unable to boot, charging...\n");
395*f1c8ecceSJoseph Chen 				/* 'true': just for clear time of check key */
396*f1c8ecceSJoseph Chen 				check_key_press(pwrkey, true);
397*f1c8ecceSJoseph Chen 				show_idx = image_num - 1;
398*f1c8ecceSJoseph Chen 				continue;
399*f1c8ecceSJoseph Chen 			}
400*f1c8ecceSJoseph Chen 
401*f1c8ecceSJoseph Chen 			/* Success exit charging */
402*f1c8ecceSJoseph Chen 			printf("Exit charge animation...\n");
403*f1c8ecceSJoseph Chen 			rockchip_show_logo();
404*f1c8ecceSJoseph Chen 			break;
405*f1c8ecceSJoseph Chen 		} else {
406*f1c8ecceSJoseph Chen 			/* Do nothing */
407*f1c8ecceSJoseph Chen 		}
408*f1c8ecceSJoseph Chen 
409*f1c8ecceSJoseph Chen 		debug("step5 (%d)... \n", screen_on);
410*f1c8ecceSJoseph Chen 
411*f1c8ecceSJoseph Chen 		/*
412*f1c8ecceSJoseph Chen 		 * Step5: Check auto start kernel
413*f1c8ecceSJoseph Chen 		 */
414*f1c8ecceSJoseph Chen 		if (pdata->auto_start_kernel) {
415*f1c8ecceSJoseph Chen 			if ((voltage >= pdata->power_on_voltage_threshold) &&
416*f1c8ecceSJoseph Chen 			    (soc >= pdata->power_on_soc_threshold)) {
417*f1c8ecceSJoseph Chen 				printf("Auto start, exit charge animation..\n");
418*f1c8ecceSJoseph Chen 				rockchip_show_logo();
419*f1c8ecceSJoseph Chen 				break;
420*f1c8ecceSJoseph Chen 			}
421*f1c8ecceSJoseph Chen 		}
422*f1c8ecceSJoseph Chen 	}
423*f1c8ecceSJoseph Chen 
424*f1c8ecceSJoseph Chen 	ms = get_timer(charge_start);
425*f1c8ecceSJoseph Chen 	if (ms >= 1000) {
426*f1c8ecceSJoseph Chen 		sec = ms / 1000;
427*f1c8ecceSJoseph Chen 		ms = ms % 1000;
428*f1c8ecceSJoseph Chen 	}
429*f1c8ecceSJoseph Chen 
430*f1c8ecceSJoseph Chen 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
431*f1c8ecceSJoseph Chen 	       sec, ms, soc, voltage);
432*f1c8ecceSJoseph Chen 
433*f1c8ecceSJoseph Chen 	return 0;
434*f1c8ecceSJoseph Chen }
435*f1c8ecceSJoseph Chen 
436*f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = {
437*f1c8ecceSJoseph Chen 	.get_power_on_soc = charge_animation_threshold_soc,
438*f1c8ecceSJoseph Chen 	.get_power_on_voltage = charge_animation_threshold_voltage,
439*f1c8ecceSJoseph Chen 	.show = charge_animation_show,
440*f1c8ecceSJoseph Chen };
441*f1c8ecceSJoseph Chen 
442*f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev)
443*f1c8ecceSJoseph Chen {
444*f1c8ecceSJoseph Chen 	struct charge_animation_priv *priv = dev_get_priv(dev);
445*f1c8ecceSJoseph Chen 	struct udevice *pwrkey, *fg, *pmic;
446*f1c8ecceSJoseph Chen 	int ret;
447*f1c8ecceSJoseph Chen 
448*f1c8ecceSJoseph Chen 	/* Get PMIC */
449*f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
450*f1c8ecceSJoseph Chen 	if (ret) {
451*f1c8ecceSJoseph Chen 		printf("Get UCLASS PMIC failed: %d\n", ret);
452*f1c8ecceSJoseph Chen 		return ret;
453*f1c8ecceSJoseph Chen 	}
454*f1c8ecceSJoseph Chen 	priv->pmic = pmic;
455*f1c8ecceSJoseph Chen 
456*f1c8ecceSJoseph Chen 	/* Get power key */
457*f1c8ecceSJoseph Chen 	for (uclass_first_device(UCLASS_KEY, &pwrkey);
458*f1c8ecceSJoseph Chen 	     pwrkey;
459*f1c8ecceSJoseph Chen 	     uclass_next_device(&pwrkey)) {
460*f1c8ecceSJoseph Chen 		if (key_type(pwrkey) == KEY_POWER) {
461*f1c8ecceSJoseph Chen 			priv->pwrkey = pwrkey;
462*f1c8ecceSJoseph Chen 			break;
463*f1c8ecceSJoseph Chen 		}
464*f1c8ecceSJoseph Chen 	}
465*f1c8ecceSJoseph Chen 	if (!priv->pwrkey) {
466*f1c8ecceSJoseph Chen 		printf("Can't find any power key\n");
467*f1c8ecceSJoseph Chen 		return -ENOSYS;
468*f1c8ecceSJoseph Chen 	}
469*f1c8ecceSJoseph Chen 
470*f1c8ecceSJoseph Chen 	/* Get fuel gauge */
471*f1c8ecceSJoseph Chen 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
472*f1c8ecceSJoseph Chen 	if (ret) {
473*f1c8ecceSJoseph Chen 		printf("Get UCLASS FG failed: %d\n", ret);
474*f1c8ecceSJoseph Chen 		return ret;
475*f1c8ecceSJoseph Chen 	}
476*f1c8ecceSJoseph Chen 	priv->fg = fg;
477*f1c8ecceSJoseph Chen 
478*f1c8ecceSJoseph Chen 	/* Get image */
479*f1c8ecceSJoseph Chen 	priv->image = image;
480*f1c8ecceSJoseph Chen 	priv->image_num = ARRAY_SIZE(image);
481*f1c8ecceSJoseph Chen 
482*f1c8ecceSJoseph Chen 	printf("Enable charge animation display\n");
483*f1c8ecceSJoseph Chen 
484*f1c8ecceSJoseph Chen 	return 0;
485*f1c8ecceSJoseph Chen }
486*f1c8ecceSJoseph Chen 
487*f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = {
488*f1c8ecceSJoseph Chen 	{ .compatible = "charge-animation" },
489*f1c8ecceSJoseph Chen 	{ },
490*f1c8ecceSJoseph Chen };
491*f1c8ecceSJoseph Chen 
492*f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = {
493*f1c8ecceSJoseph Chen 	.name = "charge-animation",
494*f1c8ecceSJoseph Chen 	.id = UCLASS_CHARGE_DISPLAY,
495*f1c8ecceSJoseph Chen 	.probe = charge_animation_probe,
496*f1c8ecceSJoseph Chen 	.of_match = charge_animation_ids,
497*f1c8ecceSJoseph Chen 	.ops = &charge_animation_ops,
498*f1c8ecceSJoseph Chen 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
499*f1c8ecceSJoseph Chen 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
500*f1c8ecceSJoseph Chen 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
501*f1c8ecceSJoseph Chen };
502