xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision f270a3f8f1778b63ebcf6fb34fac70a2ccfdae9c)
1 /*
2  * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6 
7 #include <asm/arch/bootrkp.h>
8 #include <common.h>
9 #include <console.h>
10 #include <dm.h>
11 #include <errno.h>
12 #include <key.h>
13 #include <linux/input.h>
14 #include <pwm.h>
15 #include <power/charge_display.h>
16 #include <power/fuel_gauge.h>
17 #include <power/pmic.h>
18 #include <power/rk8xx_pmic.h>
19 #include <power/regulator.h>
20 #include <video_rockchip.h>
21 
22 DECLARE_GLOBAL_DATA_PTR;
23 
24 #define LONG_PRESSED_TIME			2000 /* 2s */
25 #define IMAGE_SHOW_RESET			-1
26 
27 enum key_event {
28 	KEY_NOT_PRESSED = 0,
29 	KEY_SHORT_PRESSED,
30 	KEY_LONG_PRESSED,
31 };
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 	struct udevice *pwrkey;
43 	const struct charge_image *image;
44 	int image_num;
45 };
46 
47 struct charge_animation_pdata {
48 	int screen_on_voltage_threshold;
49 	int power_on_voltage_threshold;
50 	int power_on_soc_threshold;
51 	bool suspend_to_sram;
52 	bool auto_start_kernel;
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->power_on_soc_threshold;
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->power_on_voltage_threshold;
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_threshold;
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->power_on_soc_threshold = 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->power_on_voltage_threshold = 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_threshold = 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 	if (dev_read_bool(dev, "charge,suspend-to-sram"))
142 		pdata->suspend_to_sram = true;
143 	else
144 		pdata->suspend_to_sram = false;
145 
146 	if (dev_read_bool(dev, "charge,auto-start-kernel"))
147 		pdata->auto_start_kernel = true;
148 	else
149 		pdata->auto_start_kernel = false;
150 
151 	pdata->power_on_soc_threshold =
152 		dev_read_u32_default(dev, "power-on-soc-threshold", 0);
153 	pdata->power_on_voltage_threshold =
154 		dev_read_u32_default(dev, "power-on-voltage-threshold", 0);
155 	pdata->screen_on_voltage_threshold =
156 		dev_read_u32_default(dev, "screen-on-voltage-threshold", 0);
157 
158 	if (pdata->screen_on_voltage_threshold >
159 	    pdata->power_on_voltage_threshold)
160 		pdata->screen_on_voltage_threshold =
161 					pdata->power_on_voltage_threshold;
162 
163 	debug("threshold soc=%d%%, voltage=%dmv, screen_on=%dmv, suspend=%d\n",
164 	      pdata->power_on_soc_threshold, pdata->power_on_voltage_threshold,
165 	      pdata->screen_on_voltage_threshold, pdata->suspend_to_sram);
166 
167 	return 0;
168 }
169 
170 static int check_key_press(struct udevice *dev, bool restar_time)
171 {
172 	static unsigned long pressed_time;
173 	static int old_state;
174 	int report = KEY_NOT_PRESSED;
175 	int state;
176 
177 	/* just for restart time */
178 	if (restar_time) {
179 		pressed_time = get_timer(0);
180 		goto out;
181 	}
182 
183 	state = key_read(dev);
184 	if (state < 0) {
185 		printf("read power key failed: %d\n", state);
186 		goto out;
187 	}
188 
189 	/* Start time is not initialized, let's do it */
190 	if (!pressed_time && (state == KEY_PRESS_DOWN)) {
191 		pressed_time = get_timer(0);
192 		return KEY_NOT_PRESSED;
193 	} else {
194 		debug("key state = %d\n", state);
195 
196 		if (state == KEY_PRESS_DOWN) {
197 			if (get_timer(pressed_time) >= LONG_PRESSED_TIME) {
198 				report = KEY_LONG_PRESSED;
199 				pressed_time = 0;
200 				old_state = KEY_LONG_PRESSED;
201 				printf("power key long pressed...\n");
202 			}
203 		/*
204 		 * If you don't check 'old_state != KEY_LONG_PRESSED', it will
205 		 * treat 'key long pressed' release as a 'short key pressed'
206 		 * release.
207 		 */
208 		} else if ((state == KEY_PRESS_UP) &&
209 			   (old_state != KEY_LONG_PRESSED)) {
210 			report = KEY_SHORT_PRESSED;
211 			old_state = report;
212 			pressed_time = 0;
213 			printf("power key short pressed...\n");
214 		} else {
215 			report = KEY_NOT_PRESSED;
216 			old_state = report;
217 			pressed_time = 0;
218 			debug("power key Not pressed...\n");
219 		}
220 	}
221 
222 out:
223 	return report;
224 }
225 
226 static int charge_animation_show(struct udevice *dev)
227 {
228 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
229 	struct charge_animation_priv *priv = dev_get_priv(dev);
230 	const struct charge_image *image = priv->image;
231 	struct udevice *pwrkey = priv->pwrkey;
232 	struct udevice *pmic = priv->pmic;
233 	struct udevice *fg = priv->fg;
234 	const char *preboot = env_get("preboot");
235 	int image_num = priv->image_num;
236 	bool ever_lowpower_screen_off = false;
237 	bool screen_on = true;
238 	ulong show_start = 0, charge_start = 0, debug_start = 0;
239 	ulong ms = 0, sec = 0;
240 	int start_idx = 0, show_idx = -1;
241 	int soc, voltage, key_state;
242 	int i, charging = 1;
243 	int boot_mode;
244 
245 	/* If there is preboot command, exit */
246 	if (preboot) {
247 		debug("preboot: %s\n", preboot);
248 		return 0;
249 	}
250 
251 #ifdef CONFIG_ROCKCHIP_PARTITION_BOOT
252 	boot_mode = rockchip_get_boot_mode();
253 	if (boot_mode != ANDROID_BOOT_MODE_NORMAL) {
254 		debug("exit charge, due to boot mode: %d\n", boot_mode);
255 		return 0;
256 	}
257 #endif
258 	/* Not charger online, exit */
259 	charging = fuel_gauge_get_chrg_online(fg);
260 	if (charging <= 0)
261 		return 0;
262 
263 	voltage = fuel_gauge_get_voltage(fg);
264 	if (voltage < 0) {
265 		printf("get voltage failed: %d\n", voltage);
266 		return -EINVAL;
267 	}
268 
269 	/* If low power, turn off screen */
270 	if (voltage <= pdata->screen_on_voltage_threshold + 50) {
271 		screen_on = false;
272 		ever_lowpower_screen_off = true;
273 		rockchip_show_bmp(NULL);
274 	}
275 
276 	charge_start = get_timer(0);
277 
278 	/* Charging ! */
279 	while (1) {
280 		debug("step1 (%d)... \n", screen_on);
281 
282 		/* Step1: Is charging now ? */
283 		charging = fuel_gauge_get_chrg_online(fg);
284 		if (charging <= 0) {
285 			printf("Not charging, online=%d. Shutdown...\n",
286 			       charging);
287 
288 			/* wait uart flush before shutdown */
289 			mdelay(500);
290 
291 			/* PMIC shutdown */
292 			pmic_shutdown(pmic);
293 
294 			printf("Cpu should never reach here, shutdown failed !\n");
295 			continue;
296 		}
297 
298 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
299 
300 		/* Step2: get soc and voltage */
301 		soc = fuel_gauge_get_soc(fg);
302 		if (soc < 0 || soc > 100) {
303 			printf("get soc failed: %d\n", soc);
304 			continue;
305 		}
306 
307 		voltage = fuel_gauge_get_voltage(fg);
308 		if (voltage < 0) {
309 			printf("get voltage failed: %d\n", voltage);
310 			continue;
311 		}
312 
313 		/*
314 		 * Just for debug, otherwise there will be nothing output which
315 		 * is not good to know what happen.
316 		 */
317 		if (!debug_start)
318 			debug_start = get_timer(0);
319 		if (get_timer(debug_start) > 20000) {
320 			debug_start = get_timer(0);
321 			printf("soc=%d, vol=%d, online=%d, screen_on=%d, show_idx=%d, ever_off=%d\n",
322 			       soc, voltage, charging, screen_on, show_idx,
323 			       ever_lowpower_screen_off);
324 		}
325 
326 		/*
327 		 * If ever lowpower screen off, force screen on false, which
328 		 * means key event can't modify screen_on, only voltage higher
329 		 * then threshold can update screen_on=true;
330 		 */
331 		if (ever_lowpower_screen_off)
332 			screen_on = false;
333 
334 		/*
335 		 * Auto turn on screen when voltage higher than Vol screen on.
336 		 * 'ever_lowpower_screen_off' means enter while loop with
337 		 * screen off.
338 		 */
339 		if ((ever_lowpower_screen_off) &&
340 		    (voltage > pdata->screen_on_voltage_threshold)) {
341 			ever_lowpower_screen_off = false;
342 			screen_on = true;
343 			show_idx = IMAGE_SHOW_RESET;
344 		}
345 
346 		/*
347 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
348 		 * When short key pressed event trigged, we will set show_idx
349 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
350 		 * that calculate by current soc.
351 		 */
352 		if (show_idx == IMAGE_SHOW_RESET) {
353 			for (i = 0; i < image_num - 2; i++) {
354 				/* Find out which image we start to show */
355 				if ((soc >= image[i].soc) &&
356 				    (soc < image[i + 1].soc)) {
357 					start_idx = i;
358 					break;
359 				}
360 
361 				if (soc >= 100) {
362 					start_idx = image_num - 2;
363 					break;
364 				}
365 			}
366 
367 			debug("%s: show_idx=%d, screen_on=%d\n",
368 			      __func__, show_idx, screen_on);
369 
370 			/* Mark start index and start time */
371 			show_idx = start_idx;
372 			show_start = get_timer(0);
373 		}
374 
375 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
376 
377 		/* Step3: show images */
378 		if (screen_on) {
379 			debug("SHOW: %s\n", image[show_idx].name);
380 			rockchip_show_bmp(image[show_idx].name);
381 		} else {
382 			/*
383 			 * TODO: enter low power mode:
384 			 *
385 			 * 1. cut off some regualtors;
386 			 * 2. set 24MHZ for some PLLs;
387 			 * 3. auto turn off screen when timout;
388 			 * 4. power key wakeup;
389 			 * 5. timer period wakeup for pmic fg ?
390 			 */
391 		}
392 
393 		mdelay(5);
394 
395 		/* Every image shows period */
396 		if (get_timer(show_start) > image[show_idx].period) {
397 			show_start = get_timer(0);
398 			/* Update to next image */
399 			show_idx++;
400 			if (show_idx > (image_num - 2))
401 				show_idx = IMAGE_SHOW_RESET;
402 		}
403 
404 		debug("step4 (%d)... \n", screen_on);
405 
406 		/*
407 		 * Step4: check key event.
408 		 *
409 		 * Short key event: turn on/off screen;
410 		 * Long key event: show logo and boot system or still charging.
411 		 */
412 		key_state = check_key_press(pwrkey, false);
413 		if (key_state == KEY_SHORT_PRESSED) {
414 			/* NULL means show nothing, ie. turn off screen */
415 			if (screen_on)
416 				rockchip_show_bmp(NULL);
417 
418 			/*
419 			 * Clear current image index, and show image
420 			 * from start_idx
421 			 */
422 			show_idx = IMAGE_SHOW_RESET;
423 
424 			/*
425 			 * We turn off screen by rockchip_show_bmp(NULL), so we
426 			 * should tell while loop to stop show images any more.
427 			 *
428 			 * If screen_on=false, means this short key pressed
429 			 * event turn on the screen and we need show images.
430 			 *
431 			 * If screen_on=true, means this short key pressed
432 			 * event turn off the screen and we never show images.
433 			 */
434 			if (screen_on)
435 				screen_on = false;
436 			else
437 				screen_on = true;
438 		} else if (key_state == KEY_LONG_PRESSED) {
439 			/* Only long pressed while screen off needs screen_on true */
440 			if (!screen_on)
441 				screen_on = true;
442 
443 			/* Is able to boot now ? */
444 			if (soc < pdata->power_on_soc_threshold) {
445 				printf("soc=%d%%, threshold soc=%d%%\n",
446 				       soc, pdata->power_on_soc_threshold);
447 				printf("Low power, unable to boot, charging...\n");
448 				/* 'true': just for clear time of check key */
449 				check_key_press(pwrkey, true);
450 				show_idx = image_num - 1;
451 				continue;
452 			}
453 
454 			if (voltage < pdata->power_on_voltage_threshold) {
455 				printf("voltage=%dmv, threshold voltage=%dmv\n",
456 				       voltage, pdata->power_on_voltage_threshold);
457 				printf("Low power, unable to boot, charging...\n");
458 				/* 'true': just for clear time of check key */
459 				check_key_press(pwrkey, true);
460 				show_idx = image_num - 1;
461 				continue;
462 			}
463 
464 			/* Success exit charging */
465 			printf("Exit charge animation...\n");
466 			rockchip_show_logo();
467 			break;
468 		} else {
469 			/* Do nothing */
470 		}
471 
472 		debug("step5 (%d)... \n", screen_on);
473 
474 		/*
475 		 * Step5: Check auto start kernel
476 		 */
477 		if (pdata->auto_start_kernel) {
478 			if ((voltage >= pdata->power_on_voltage_threshold) &&
479 			    (soc >= pdata->power_on_soc_threshold)) {
480 				printf("Auto start, exit charge animation..\n");
481 				rockchip_show_logo();
482 				break;
483 			}
484 		}
485 
486 		/* Step6: Exit by ctrl+c */
487 		if (ctrlc()) {
488 			if (voltage >= pdata->screen_on_voltage_threshold)
489 				rockchip_show_logo();
490 			printf("Exit charge, due to ctrl+c\n");
491 			break;
492 		}
493 	}
494 
495 	ms = get_timer(charge_start);
496 	if (ms >= 1000) {
497 		sec = ms / 1000;
498 		ms = ms % 1000;
499 	}
500 
501 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
502 	       sec, ms, soc, voltage);
503 
504 	return 0;
505 }
506 
507 static const struct dm_charge_display_ops charge_animation_ops = {
508 	.get_power_on_soc = charge_animation_get_power_on_soc,
509 	.get_power_on_voltage = charge_animation_get_power_on_voltage,
510 	.get_screen_on_voltage = charge_animation_get_screen_on_voltage,
511 	.set_power_on_soc = charge_animation_set_power_on_soc,
512 	.set_power_on_voltage = charge_animation_set_power_on_voltage,
513 	.set_screen_on_voltage = charge_animation_set_screen_on_voltage,
514 	.show = charge_animation_show,
515 };
516 
517 static int charge_animation_probe(struct udevice *dev)
518 {
519 	struct charge_animation_priv *priv = dev_get_priv(dev);
520 	struct udevice *pwrkey, *fg, *pmic;
521 	int ret;
522 
523 	/* Get PMIC */
524 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
525 	if (ret) {
526 		printf("Get UCLASS PMIC failed: %d\n", ret);
527 		return ret;
528 	}
529 	priv->pmic = pmic;
530 
531 	/* Get power key */
532 	for (uclass_first_device(UCLASS_KEY, &pwrkey);
533 	     pwrkey;
534 	     uclass_next_device(&pwrkey)) {
535 		if (key_type(pwrkey) == KEY_POWER) {
536 			priv->pwrkey = pwrkey;
537 			break;
538 		}
539 	}
540 	if (!priv->pwrkey) {
541 		printf("Can't find any power key\n");
542 		return -ENOSYS;
543 	}
544 
545 	/* Get fuel gauge */
546 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
547 	if (ret) {
548 		printf("Get UCLASS FG failed: %d\n", ret);
549 		return ret;
550 	}
551 	priv->fg = fg;
552 
553 	/* Get image */
554 	priv->image = image;
555 	priv->image_num = ARRAY_SIZE(image);
556 
557 	printf("Enable charge animation display\n");
558 
559 	return 0;
560 }
561 
562 static const struct udevice_id charge_animation_ids[] = {
563 	{ .compatible = "charge-animation" },
564 	{ },
565 };
566 
567 U_BOOT_DRIVER(charge_animation) = {
568 	.name = "charge-animation",
569 	.id = UCLASS_CHARGE_DISPLAY,
570 	.probe = charge_animation_probe,
571 	.of_match = charge_animation_ids,
572 	.ops = &charge_animation_ops,
573 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
574 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
575 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
576 };
577