xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision df3e17bde5d906390f6d5e02dbf8b10007e161bb)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <asm/suspend.h>
8 #include <asm/arch/rockchip_smccc.h>
9 #include <asm/arch/bootrkp.h>
10 #include <common.h>
11 #include <console.h>
12 #include <dm.h>
13 #include <errno.h>
14 #include <key.h>
15 #include <irq-generic.h>
16 #include <linux/input.h>
17 #include <pwm.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_ROCKCHIP_PARTITION_BOOT
249 	boot_mode = rockchip_get_boot_mode();
250 	if (boot_mode != ANDROID_BOOT_MODE_NORMAL) {
251 		debug("exit charge, due to boot mode: %d\n", boot_mode);
252 		return 0;
253 	}
254 #endif
255 	/* Not charger online, exit */
256 	charging = fuel_gauge_get_chrg_online(fg);
257 	if (charging <= 0)
258 		return 0;
259 
260 	voltage = fuel_gauge_get_voltage(fg);
261 	if (voltage < 0) {
262 		printf("get voltage failed: %d\n", voltage);
263 		return -EINVAL;
264 	}
265 
266 	/* If low power, turn off screen */
267 	if (voltage <= pdata->screen_on_voltage_threshold + 50) {
268 		screen_on = false;
269 		ever_lowpower_screen_off = true;
270 		rockchip_show_bmp(NULL);
271 	}
272 
273 	charge_start = get_timer(0);
274 
275 	/* Charging ! */
276 	while (1) {
277 		debug("step1 (%d)... \n", screen_on);
278 
279 		/* Step1: Is charging now ? */
280 		charging = fuel_gauge_get_chrg_online(fg);
281 		if (charging <= 0) {
282 			printf("Not charging, online=%d. Shutdown...\n",
283 			       charging);
284 
285 			/* wait uart flush before shutdown */
286 			mdelay(500);
287 
288 			/* PMIC shutdown */
289 			pmic_shutdown(pmic);
290 
291 			printf("Cpu should never reach here, shutdown failed !\n");
292 			continue;
293 		}
294 
295 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
296 
297 		/* Step2: get soc and voltage */
298 		soc = fuel_gauge_get_soc(fg);
299 		if (soc < 0 || soc > 100) {
300 			printf("get soc failed: %d\n", soc);
301 			continue;
302 		}
303 
304 		voltage = fuel_gauge_get_voltage(fg);
305 		if (voltage < 0) {
306 			printf("get voltage failed: %d\n", voltage);
307 			continue;
308 		}
309 
310 		/*
311 		 * Just for debug, otherwise there will be nothing output which
312 		 * is not good to know what happen.
313 		 */
314 		if (!debug_start)
315 			debug_start = get_timer(0);
316 		if (get_timer(debug_start) > 20000) {
317 			debug_start = get_timer(0);
318 			printf("soc=%d, vol=%d, online=%d, screen_on=%d, show_idx=%d, ever_off=%d\n",
319 			       soc, voltage, charging, screen_on, show_idx,
320 			       ever_lowpower_screen_off);
321 		}
322 
323 		/*
324 		 * If ever lowpower screen off, force screen on false, which
325 		 * means key event can't modify screen_on, only voltage higher
326 		 * then threshold can update screen_on=true;
327 		 */
328 		if (ever_lowpower_screen_off)
329 			screen_on = false;
330 
331 		/*
332 		 * Auto turn on screen when voltage higher than Vol screen on.
333 		 * 'ever_lowpower_screen_off' means enter while loop with
334 		 * screen off.
335 		 */
336 		if ((ever_lowpower_screen_off) &&
337 		    (voltage > pdata->screen_on_voltage_threshold)) {
338 			ever_lowpower_screen_off = false;
339 			screen_on = true;
340 			show_idx = IMAGE_SHOW_RESET;
341 		}
342 
343 		/*
344 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
345 		 * When short key pressed event trigged, we will set show_idx
346 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
347 		 * that calculate by current soc.
348 		 */
349 		if (show_idx == IMAGE_SHOW_RESET) {
350 			for (i = 0; i < image_num - 2; i++) {
351 				/* Find out which image we start to show */
352 				if ((soc >= image[i].soc) &&
353 				    (soc < image[i + 1].soc)) {
354 					start_idx = i;
355 					break;
356 				}
357 
358 				if (soc >= 100) {
359 					start_idx = image_num - 2;
360 					break;
361 				}
362 			}
363 
364 			debug("%s: show_idx=%d, screen_on=%d\n",
365 			      __func__, show_idx, screen_on);
366 
367 			/* Mark start index and start time */
368 			show_idx = start_idx;
369 			show_start = get_timer(0);
370 		}
371 
372 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
373 
374 		/* Step3: show images */
375 		if (screen_on) {
376 			debug("SHOW: %s\n", image[show_idx].name);
377 			rockchip_show_bmp(image[show_idx].name);
378 		} else {
379 			system_suspend_enter();
380 		}
381 
382 		mdelay(5);
383 
384 		/* Every image shows period */
385 		if (get_timer(show_start) > image[show_idx].period) {
386 			show_start = get_timer(0);
387 			/* Update to next image */
388 			show_idx++;
389 			if (show_idx > (image_num - 2))
390 				show_idx = IMAGE_SHOW_RESET;
391 		}
392 
393 		debug("step4 (%d)... \n", screen_on);
394 
395 		/*
396 		 * Step4: check key event.
397 		 *
398 		 * Short key event: turn on/off screen;
399 		 * Long key event: show logo and boot system or still charging.
400 		 */
401 		key_state = check_key_press(pwrkey);
402 		if (key_state == KEY_PRESS_DOWN) {
403 			/* NULL means show nothing, ie. turn off screen */
404 			if (screen_on)
405 				rockchip_show_bmp(NULL);
406 
407 			/*
408 			 * Clear current image index, and show image
409 			 * from start_idx
410 			 */
411 			show_idx = IMAGE_SHOW_RESET;
412 
413 			/*
414 			 * We turn off screen by rockchip_show_bmp(NULL), so we
415 			 * should tell while loop to stop show images any more.
416 			 *
417 			 * If screen_on=false, means this short key pressed
418 			 * event turn on the screen and we need show images.
419 			 *
420 			 * If screen_on=true, means this short key pressed
421 			 * event turn off the screen and we never show images.
422 			 */
423 			if (screen_on)
424 				screen_on = false;
425 			else
426 				screen_on = true;
427 		} else if (key_state == KEY_PRESS_LONG_DOWN) {
428 			/* Only long pressed while screen off needs screen_on true */
429 			if (!screen_on)
430 				screen_on = true;
431 
432 			/* Is able to boot now ? */
433 			if (soc < pdata->power_on_soc_threshold) {
434 				printf("soc=%d%%, threshold soc=%d%%\n",
435 				       soc, pdata->power_on_soc_threshold);
436 				printf("Low power, unable to boot, charging...\n");
437 				show_idx = image_num - 1;
438 				continue;
439 			}
440 
441 			if (voltage < pdata->power_on_voltage_threshold) {
442 				printf("voltage=%dmv, threshold voltage=%dmv\n",
443 				       voltage, pdata->power_on_voltage_threshold);
444 				printf("Low power, unable to boot, charging...\n");
445 				show_idx = image_num - 1;
446 				continue;
447 			}
448 
449 			/* Success exit charging */
450 			printf("Exit charge animation...\n");
451 			rockchip_show_logo();
452 			break;
453 		} else {
454 			/* Do nothing */
455 		}
456 
457 		debug("step5 (%d)... \n", screen_on);
458 
459 		/*
460 		 * Step5: Check auto start kernel
461 		 */
462 		if (pdata->auto_start_kernel) {
463 			if ((voltage >= pdata->power_on_voltage_threshold) &&
464 			    (soc >= pdata->power_on_soc_threshold)) {
465 				printf("Auto start, exit charge animation..\n");
466 				rockchip_show_logo();
467 				break;
468 			}
469 		}
470 
471 		/* Step6: Exit by ctrl+c */
472 		if (ctrlc()) {
473 			if (voltage >= pdata->screen_on_voltage_threshold)
474 				rockchip_show_logo();
475 			printf("Exit charge, due to ctrl+c\n");
476 			break;
477 		}
478 	}
479 
480 	ms = get_timer(charge_start);
481 	if (ms >= 1000) {
482 		sec = ms / 1000;
483 		ms = ms % 1000;
484 	}
485 
486 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
487 	       sec, ms, soc, voltage);
488 
489 	return 0;
490 }
491 
492 static const struct dm_charge_display_ops charge_animation_ops = {
493 	.get_power_on_soc = charge_animation_get_power_on_soc,
494 	.get_power_on_voltage = charge_animation_get_power_on_voltage,
495 	.get_screen_on_voltage = charge_animation_get_screen_on_voltage,
496 	.set_power_on_soc = charge_animation_set_power_on_soc,
497 	.set_power_on_voltage = charge_animation_set_power_on_voltage,
498 	.set_screen_on_voltage = charge_animation_set_screen_on_voltage,
499 	.show = charge_animation_show,
500 };
501 
502 static int charge_animation_probe(struct udevice *dev)
503 {
504 	struct charge_animation_priv *priv = dev_get_priv(dev);
505 	struct udevice *pwrkey, *fg, *pmic;
506 	int ret;
507 
508 	/* Get PMIC */
509 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
510 	if (ret) {
511 		printf("Get UCLASS PMIC failed: %d\n", ret);
512 		return ret;
513 	}
514 	priv->pmic = pmic;
515 
516 	/* Get power key */
517 	for (uclass_first_device(UCLASS_KEY, &pwrkey);
518 	     pwrkey;
519 	     uclass_next_device(&pwrkey)) {
520 		if (key_type(pwrkey) == KEY_POWER) {
521 			priv->pwrkey = pwrkey;
522 			break;
523 		}
524 	}
525 	if (!priv->pwrkey) {
526 		printf("Can't find any power key\n");
527 		return -ENOSYS;
528 	}
529 
530 	/* Get fuel gauge */
531 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
532 	if (ret) {
533 		printf("Get UCLASS FG failed: %d\n", ret);
534 		return ret;
535 	}
536 	priv->fg = fg;
537 
538 	/* Get image */
539 	priv->image = image;
540 	priv->image_num = ARRAY_SIZE(image);
541 
542 	printf("Enable charge animation display\n");
543 
544 	return 0;
545 }
546 
547 static const struct udevice_id charge_animation_ids[] = {
548 	{ .compatible = "charge-animation" },
549 	{ },
550 };
551 
552 U_BOOT_DRIVER(charge_animation) = {
553 	.name = "charge-animation",
554 	.id = UCLASS_CHARGE_DISPLAY,
555 	.probe = charge_animation_probe,
556 	.of_match = charge_animation_ids,
557 	.ops = &charge_animation_ops,
558 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
559 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
560 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
561 };
562