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