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