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