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