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