xref: /rk3399_rockchip-uboot/drivers/power/charge_animation.c (revision 21016d27c500da4326bdc59cd3505fcd85d236db)
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 <dm.h>
10 #include <errno.h>
11 #include <key.h>
12 #include <linux/input.h>
13 #include <pwm.h>
14 #include <power/charge_display.h>
15 #include <power/fuel_gauge.h>
16 #include <power/pmic.h>
17 #include <power/rk8xx_pmic.h>
18 #include <power/regulator.h>
19 #include <video_rockchip.h>
20 
21 DECLARE_GLOBAL_DATA_PTR;
22 
23 #define LONG_PRESSED_TIME			2000 /* 2s */
24 #define IMAGE_SHOW_RESET			-1
25 
26 enum key_event {
27 	KEY_NOT_PRESSED = 0,
28 	KEY_SHORT_PRESSED,
29 	KEY_LONG_PRESSED,
30 };
31 
32 struct charge_image {
33 	const char *name;
34 	int soc;
35 	int period;	/* ms */
36 };
37 
38 struct charge_animation_priv {
39 	struct udevice *pmic;
40 	struct udevice *fg;
41 	struct udevice *pwrkey;
42 	const struct charge_image *image;
43 	int image_num;
44 };
45 
46 struct charge_animation_pdata {
47 	int screen_on_voltage_threshold;
48 	int power_on_voltage_threshold;
49 	int power_on_soc_threshold;
50 	bool suspend_to_sram;
51 	bool auto_start_kernel;
52 };
53 
54 static int charge_animation_threshold_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->power_on_soc_threshold;
62 }
63 
64 static int charge_animation_threshold_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->power_on_voltage_threshold;
72 }
73 
74 /*
75  * IF you want to use your own charge images, please:
76  *
77  * 1. Update the following 'image[]' to point to your own images;
78  * 2. You must set the failed image as last one and soc = -1 !!!
79  */
80 static const struct charge_image image[] = {
81 	{ .name = "battery_0.bmp", .soc = 5, .period = 600 },
82 	{ .name = "battery_1.bmp", .soc = 20, .period = 600 },
83 	{ .name = "battery_2.bmp", .soc = 40, .period = 600 },
84 	{ .name = "battery_3.bmp", .soc = 60, .period = 600 },
85 	{ .name = "battery_4.bmp", .soc = 80, .period = 600 },
86 	{ .name = "battery_5.bmp", .soc = 100, .period = 600 },
87 	{ .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
88 };
89 
90 static int charge_animation_ofdata_to_platdata(struct udevice *dev)
91 {
92 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
93 
94 	if (dev_read_bool(dev, "charge,suspend-to-sram"))
95 		pdata->suspend_to_sram = true;
96 	else
97 		pdata->suspend_to_sram = false;
98 
99 	if (dev_read_bool(dev, "charge,auto-start-kernel"))
100 		pdata->auto_start_kernel = true;
101 	else
102 		pdata->auto_start_kernel = false;
103 
104 	pdata->power_on_soc_threshold =
105 		dev_read_u32_default(dev, "power-on-soc-threshold", 0);
106 	pdata->power_on_voltage_threshold =
107 		dev_read_u32_default(dev, "power-on-voltage-threshold", 0);
108 	pdata->screen_on_voltage_threshold =
109 		dev_read_u32_default(dev, "screen-on-voltage-threshold", 0);
110 
111 	if (pdata->screen_on_voltage_threshold >
112 	    pdata->power_on_voltage_threshold)
113 		pdata->screen_on_voltage_threshold =
114 					pdata->power_on_voltage_threshold;
115 
116 	debug("threshold soc=%d%%, voltage=%dmv, screen_on=%dmv, suspend=%d\n",
117 	      pdata->power_on_soc_threshold, pdata->power_on_voltage_threshold,
118 	      pdata->screen_on_voltage_threshold, pdata->suspend_to_sram);
119 
120 	return 0;
121 }
122 
123 static int check_key_press(struct udevice *dev, bool restar_time)
124 {
125 	static unsigned long pressed_time;
126 	static int old_state;
127 	int report = KEY_NOT_PRESSED;
128 	int state;
129 
130 	/* just for restart time */
131 	if (restar_time) {
132 		pressed_time = get_timer(0);
133 		goto out;
134 	}
135 
136 	state = key_read(dev);
137 	if (state < 0) {
138 		printf("read power key failed: %d\n", state);
139 		goto out;
140 	}
141 
142 	/* Start time is not initialized, let's do it */
143 	if (!pressed_time && (state == KEY_PRESS_DOWN)) {
144 		pressed_time = get_timer(0);
145 		return KEY_NOT_PRESSED;
146 	} else {
147 		debug("key state = %d\n", state);
148 
149 		if (state == KEY_PRESS_DOWN) {
150 			if (get_timer(pressed_time) >= LONG_PRESSED_TIME) {
151 				report = KEY_LONG_PRESSED;
152 				pressed_time = 0;
153 				old_state = KEY_LONG_PRESSED;
154 				printf("power key long pressed...\n");
155 			}
156 		/*
157 		 * If you don't check 'old_state != KEY_LONG_PRESSED', it will
158 		 * treat 'key long pressed' release as a 'short key pressed'
159 		 * release.
160 		 */
161 		} else if ((state == KEY_PRESS_UP) &&
162 			   (old_state != KEY_LONG_PRESSED)) {
163 			report = KEY_SHORT_PRESSED;
164 			old_state = report;
165 			pressed_time = 0;
166 			printf("power key short pressed...\n");
167 		} else {
168 			report = KEY_NOT_PRESSED;
169 			old_state = report;
170 			pressed_time = 0;
171 			debug("power key Not pressed...\n");
172 		}
173 	}
174 
175 out:
176 	return report;
177 }
178 
179 static int charge_animation_show(struct udevice *dev)
180 {
181 	struct charge_animation_pdata *pdata = dev_get_platdata(dev);
182 	struct charge_animation_priv *priv = dev_get_priv(dev);
183 	const struct charge_image *image = priv->image;
184 	struct udevice *pwrkey = priv->pwrkey;
185 	struct udevice *pmic = priv->pmic;
186 	struct udevice *fg = priv->fg;
187 	const char *preboot = env_get("preboot");
188 	int image_num = priv->image_num;
189 	bool ever_lowpower_screen_off = false;
190 	bool screen_on = true;
191 	ulong show_start = 0, charge_start = 0, debug_start = 0;
192 	ulong ms = 0, sec = 0;
193 	int start_idx = 0, show_idx = -1;
194 	int soc, voltage, key_state;
195 	int i, charging = 1;
196 	int boot_mode;
197 
198 	/* If there is preboot command, exit */
199 	if (preboot) {
200 		debug("preboot: %s\n", preboot);
201 		return 0;
202 	}
203 
204 #ifdef CONFIG_ROCKCHIP_PARTITION_BOOT
205 	boot_mode = rockchip_get_boot_mode();
206 	if (boot_mode != ANDROID_BOOT_MODE_NORMAL) {
207 		debug("exit charge, due to boot mode: %d\n", boot_mode);
208 		return 0;
209 	}
210 #endif
211 	/* Not charger online, exit */
212 	charging = fuel_gauge_get_chrg_online(fg);
213 	if (charging <= 0)
214 		return 0;
215 
216 	voltage = fuel_gauge_get_voltage(fg);
217 	if (voltage < 0) {
218 		printf("get voltage failed: %d\n", voltage);
219 		return -EINVAL;
220 	}
221 
222 	/* If low power, turn off screen */
223 	if (voltage <= pdata->screen_on_voltage_threshold + 50) {
224 		screen_on = false;
225 		ever_lowpower_screen_off = true;
226 		rockchip_show_bmp(NULL);
227 	}
228 
229 	charge_start = get_timer(0);
230 
231 	/* Charging ! */
232 	while (1) {
233 		debug("step1 (%d)... \n", screen_on);
234 
235 		/* Step1: Is charging now ? */
236 		charging = fuel_gauge_get_chrg_online(fg);
237 		if (charging <= 0) {
238 			printf("Not charging, online=%d. Shutdown...\n",
239 			       charging);
240 
241 			/* wait uart flush before shutdown */
242 			mdelay(500);
243 
244 			/* PMIC shutdown */
245 			pmic_shutdown(pmic);
246 
247 			printf("Cpu should never reach here, shutdown failed !\n");
248 			continue;
249 		}
250 
251 		debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
252 
253 		/* Step2: get soc and voltage */
254 		soc = fuel_gauge_get_soc(fg);
255 		if (soc < 0 || soc > 100) {
256 			printf("get soc failed: %d\n", soc);
257 			continue;
258 		}
259 
260 		voltage = fuel_gauge_get_voltage(fg);
261 		if (voltage < 0) {
262 			printf("get voltage failed: %d\n", voltage);
263 			continue;
264 		}
265 
266 		/*
267 		 * Just for debug, otherwise there will be nothing output which
268 		 * is not good to know what happen.
269 		 */
270 		if (!debug_start)
271 			debug_start = get_timer(0);
272 		if (get_timer(debug_start) > 20000) {
273 			debug_start = get_timer(0);
274 			printf("soc=%d, vol=%d, online=%d, screen_on=%d, show_idx=%d, ever_off=%d\n",
275 			       soc, voltage, charging, screen_on, show_idx,
276 			       ever_lowpower_screen_off);
277 		}
278 
279 		/*
280 		 * If ever lowpower screen off, force screen on false, which
281 		 * means key event can't modify screen_on, only voltage higher
282 		 * then threshold can update screen_on=true;
283 		 */
284 		if (ever_lowpower_screen_off)
285 			screen_on = false;
286 
287 		/*
288 		 * Auto turn on screen when voltage higher than Vol screen on.
289 		 * 'ever_lowpower_screen_off' means enter while loop with
290 		 * screen off.
291 		 */
292 		if ((ever_lowpower_screen_off) &&
293 		    (voltage > pdata->screen_on_voltage_threshold)) {
294 			ever_lowpower_screen_off = false;
295 			screen_on = true;
296 			show_idx = IMAGE_SHOW_RESET;
297 		}
298 
299 		/*
300 		 * IMAGE_SHOW_RESET means show_idx show be update by start_idx.
301 		 * When short key pressed event trigged, we will set show_idx
302 		 * as IMAGE_SHOW_RESET which updates images index from start_idx
303 		 * that calculate by current soc.
304 		 */
305 		if (show_idx == IMAGE_SHOW_RESET) {
306 			for (i = 0; i < image_num - 2; i++) {
307 				/* Find out which image we start to show */
308 				if ((soc >= image[i].soc) &&
309 				    (soc < image[i + 1].soc)) {
310 					start_idx = i;
311 					break;
312 				}
313 
314 				if (soc >= 100) {
315 					start_idx = image_num - 2;
316 					break;
317 				}
318 			}
319 
320 			debug("%s: show_idx=%d, screen_on=%d\n",
321 			      __func__, show_idx, screen_on);
322 
323 			/* Mark start index and start time */
324 			show_idx = start_idx;
325 			show_start = get_timer(0);
326 		}
327 
328 		debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
329 
330 		/* Step3: show images */
331 		if (screen_on) {
332 			debug("SHOW: %s\n", image[show_idx].name);
333 			rockchip_show_bmp(image[show_idx].name);
334 		} else {
335 			/*
336 			 * TODO: enter low power mode:
337 			 *
338 			 * 1. cut off some regualtors;
339 			 * 2. set 24MHZ for some PLLs;
340 			 * 3. auto turn off screen when timout;
341 			 * 4. power key wakeup;
342 			 * 5. timer period wakeup for pmic fg ?
343 			 */
344 		}
345 
346 		mdelay(5);
347 
348 		/* Every image shows period */
349 		if (get_timer(show_start) > image[show_idx].period) {
350 			show_start = get_timer(0);
351 			/* Update to next image */
352 			show_idx++;
353 			if (show_idx > (image_num - 2))
354 				show_idx = IMAGE_SHOW_RESET;
355 		}
356 
357 		debug("step4 (%d)... \n", screen_on);
358 
359 		/*
360 		 * Step4: check key event.
361 		 *
362 		 * Short key event: turn on/off screen;
363 		 * Long key event: show logo and boot system or still charging.
364 		 */
365 		key_state = check_key_press(pwrkey, false);
366 		if (key_state == KEY_SHORT_PRESSED) {
367 			/* NULL means show nothing, ie. turn off screen */
368 			if (screen_on)
369 				rockchip_show_bmp(NULL);
370 
371 			/*
372 			 * Clear current image index, and show image
373 			 * from start_idx
374 			 */
375 			show_idx = IMAGE_SHOW_RESET;
376 
377 			/*
378 			 * We turn off screen by rockchip_show_bmp(NULL), so we
379 			 * should tell while loop to stop show images any more.
380 			 *
381 			 * If screen_on=false, means this short key pressed
382 			 * event turn on the screen and we need show images.
383 			 *
384 			 * If screen_on=true, means this short key pressed
385 			 * event turn off the screen and we never show images.
386 			 */
387 			if (screen_on)
388 				screen_on = false;
389 			else
390 				screen_on = true;
391 		} else if (key_state == KEY_LONG_PRESSED) {
392 			/* Only long pressed while screen off needs screen_on true */
393 			if (!screen_on)
394 				screen_on = true;
395 
396 			/* Is able to boot now ? */
397 			if (soc < pdata->power_on_soc_threshold) {
398 				printf("soc=%d%%, threshold soc=%d%%\n",
399 				       soc, pdata->power_on_soc_threshold);
400 				printf("Low power, unable to boot, charging...\n");
401 				/* 'true': just for clear time of check key */
402 				check_key_press(pwrkey, true);
403 				show_idx = image_num - 1;
404 				continue;
405 			}
406 
407 			if (voltage < pdata->power_on_voltage_threshold) {
408 				printf("voltage=%dmv, threshold voltage=%dmv\n",
409 				       voltage, pdata->power_on_voltage_threshold);
410 				printf("Low power, unable to boot, charging...\n");
411 				/* 'true': just for clear time of check key */
412 				check_key_press(pwrkey, true);
413 				show_idx = image_num - 1;
414 				continue;
415 			}
416 
417 			/* Success exit charging */
418 			printf("Exit charge animation...\n");
419 			rockchip_show_logo();
420 			break;
421 		} else {
422 			/* Do nothing */
423 		}
424 
425 		debug("step5 (%d)... \n", screen_on);
426 
427 		/*
428 		 * Step5: Check auto start kernel
429 		 */
430 		if (pdata->auto_start_kernel) {
431 			if ((voltage >= pdata->power_on_voltage_threshold) &&
432 			    (soc >= pdata->power_on_soc_threshold)) {
433 				printf("Auto start, exit charge animation..\n");
434 				rockchip_show_logo();
435 				break;
436 			}
437 		}
438 	}
439 
440 	ms = get_timer(charge_start);
441 	if (ms >= 1000) {
442 		sec = ms / 1000;
443 		ms = ms % 1000;
444 	}
445 
446 	printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
447 	       sec, ms, soc, voltage);
448 
449 	return 0;
450 }
451 
452 static const struct dm_charge_display_ops charge_animation_ops = {
453 	.get_power_on_soc = charge_animation_threshold_soc,
454 	.get_power_on_voltage = charge_animation_threshold_voltage,
455 	.show = charge_animation_show,
456 };
457 
458 static int charge_animation_probe(struct udevice *dev)
459 {
460 	struct charge_animation_priv *priv = dev_get_priv(dev);
461 	struct udevice *pwrkey, *fg, *pmic;
462 	int ret;
463 
464 	/* Get PMIC */
465 	ret = uclass_get_device(UCLASS_PMIC, 0, &pmic);
466 	if (ret) {
467 		printf("Get UCLASS PMIC failed: %d\n", ret);
468 		return ret;
469 	}
470 	priv->pmic = pmic;
471 
472 	/* Get power key */
473 	for (uclass_first_device(UCLASS_KEY, &pwrkey);
474 	     pwrkey;
475 	     uclass_next_device(&pwrkey)) {
476 		if (key_type(pwrkey) == KEY_POWER) {
477 			priv->pwrkey = pwrkey;
478 			break;
479 		}
480 	}
481 	if (!priv->pwrkey) {
482 		printf("Can't find any power key\n");
483 		return -ENOSYS;
484 	}
485 
486 	/* Get fuel gauge */
487 	ret = uclass_get_device(UCLASS_FG, 0, &fg);
488 	if (ret) {
489 		printf("Get UCLASS FG failed: %d\n", ret);
490 		return ret;
491 	}
492 	priv->fg = fg;
493 
494 	/* Get image */
495 	priv->image = image;
496 	priv->image_num = ARRAY_SIZE(image);
497 
498 	printf("Enable charge animation display\n");
499 
500 	return 0;
501 }
502 
503 static const struct udevice_id charge_animation_ids[] = {
504 	{ .compatible = "charge-animation" },
505 	{ },
506 };
507 
508 U_BOOT_DRIVER(charge_animation) = {
509 	.name = "charge-animation",
510 	.id = UCLASS_CHARGE_DISPLAY,
511 	.probe = charge_animation_probe,
512 	.of_match = charge_animation_ids,
513 	.ops = &charge_animation_ops,
514 	.ofdata_to_platdata = charge_animation_ofdata_to_platdata,
515 	.platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
516 	.priv_auto_alloc_size = sizeof(struct charge_animation_priv),
517 };
518