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