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