1 /* 2 * (C) Copyright 2017 Rockchip Electronics Co., Ltd 3 * 4 * SPDX-License-Identifier: GPL-2.0+ 5 */ 6 7 #include <asm/io.h> 8 #include <common.h> 9 #include <boot_rkimg.h> 10 #include <console.h> 11 #include <dm.h> 12 #include <errno.h> 13 #include <key.h> 14 #include <led.h> 15 #include <rtc.h> 16 #include <pwm.h> 17 #include <asm/arch/rockchip_smccc.h> 18 #include <asm/suspend.h> 19 #include <linux/input.h> 20 #include <power/charge_display.h> 21 #include <power/charge_animation.h> 22 #include <power/rockchip_pm.h> 23 #include <power/fuel_gauge.h> 24 #include <power/pmic.h> 25 #include <power/rk8xx_pmic.h> 26 #include <power/regulator.h> 27 #include <video_rockchip.h> 28 #ifdef CONFIG_IRQ 29 #include <irq-generic.h> 30 #include <rk_timer_irq.h> 31 #endif 32 #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 33 #include <rk_eink.h> 34 #endif 35 DECLARE_GLOBAL_DATA_PTR; 36 37 #define IMAGE_RESET_IDX -1 38 #define IMAGE_SOC_100_IDX(n) ((n) - 2) 39 #define IMAGE_LOWPOWER_IDX(n) ((n) - 1) 40 #define SYSTEM_SUSPEND_DELAY_MS 5000 41 #define FUEL_GAUGE_POLL_MS 1000 42 43 #define LED_CHARGING_NAME "battery_charging" 44 #define LED_CHARGING_FULL_NAME "battery_full" 45 46 struct charge_image { 47 const char *name; 48 int soc; 49 int period; /* ms */ 50 }; 51 52 struct charge_animation_priv { 53 struct udevice *pmic; 54 struct udevice *fg; 55 struct udevice *charger; 56 struct udevice *rtc; 57 #ifdef CONFIG_LED 58 struct udevice *led_charging; 59 struct udevice *led_full; 60 #endif 61 const struct charge_image *image; 62 int image_num; 63 64 int auto_wakeup_key_state; 65 ulong auto_screen_off_timeout; /* ms */ 66 ulong suspend_delay_timeout; /* ms */ 67 }; 68 69 /* 70 * IF you want to use your own charge images, please: 71 * 72 * 1. Update the following 'image[]' to point to your own images; 73 * 2. You must set the failed image as last one and soc = -1 !!! 74 */ 75 static const struct charge_image image[] = { 76 { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 77 { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 78 { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 79 { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 80 { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 81 { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 82 { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 83 }; 84 85 static int charge_animation_ofdata_to_platdata(struct udevice *dev) 86 { 87 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 88 89 /* charge mode */ 90 pdata->uboot_charge = 91 dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 92 pdata->android_charge = 93 dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 94 95 pdata->auto_exit_charge = 96 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-auto", 0); 97 pdata->exit_charge_level = 98 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 99 pdata->exit_charge_voltage = 100 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 101 102 pdata->low_power_voltage = 103 dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 104 105 pdata->screen_on_voltage = 106 dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 107 pdata->system_suspend = 108 dev_read_u32_default(dev, "rockchip,system-suspend", 0); 109 110 pdata->auto_wakeup_interval = 111 dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 112 pdata->auto_wakeup_screen_invert = 113 dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 114 115 pdata->auto_off_screen_interval = 116 dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 117 118 if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 119 pdata->screen_on_voltage = pdata->exit_charge_voltage; 120 121 if (pdata->auto_exit_charge && !pdata->auto_wakeup_interval) 122 pdata->auto_wakeup_interval = 10; 123 124 debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 125 "lp_voltage=%d%%, screen_on=%dmv\n", 126 pdata->uboot_charge, pdata->android_charge, 127 pdata->exit_charge_level, pdata->exit_charge_voltage, 128 pdata->low_power_voltage, pdata->screen_on_voltage); 129 130 return 0; 131 } 132 133 static int check_key_press(struct udevice *dev) 134 { 135 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 136 struct charge_animation_priv *priv = dev_get_priv(dev); 137 u32 state, rtc_state = 0; 138 139 #ifdef CONFIG_DM_RTC 140 if (priv->rtc) 141 rtc_state = rtc_alarm_trigger(priv->rtc); 142 #endif 143 if (rtc_state) { 144 printf("rtc alarm trigger...\n"); 145 return KEY_PRESS_LONG_DOWN; 146 } 147 148 state = key_read(KEY_POWER); 149 if (state < 0) 150 printf("read power key failed: %d\n", state); 151 else if (state == KEY_PRESS_DOWN) 152 printf("power key pressed...\n"); 153 else if (state == KEY_PRESS_LONG_DOWN) 154 printf("power key long pressed...\n"); 155 156 /* Fixup key state for following cases */ 157 if (pdata->auto_wakeup_interval) { 158 if (pdata->auto_wakeup_screen_invert) { 159 if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 160 /* Value is updated in timer interrupt */ 161 priv->auto_wakeup_key_state = KEY_PRESS_NONE; 162 state = KEY_PRESS_DOWN; 163 } 164 } 165 } 166 if (!pdata->auto_wakeup_screen_invert && 167 pdata->auto_off_screen_interval) { 168 if (priv->auto_screen_off_timeout && 169 get_timer(priv->auto_screen_off_timeout) > 170 pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 171 state = KEY_PRESS_DOWN; 172 printf("Auto screen off\n"); 173 } 174 } 175 176 return state; 177 } 178 179 /* 180 * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup 181 * period timer is useless. 182 */ 183 #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND) 184 static int system_suspend_enter(struct udevice *dev) 185 { 186 return 0; 187 } 188 189 static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {} 190 static void autowakeup_timer_uninit(void) {} 191 192 #else 193 static int system_suspend_enter(struct udevice *dev) 194 { 195 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 196 struct charge_animation_priv *priv = dev_get_priv(dev); 197 198 /* 199 * When cpu is in wfi and we try to give a long key press event without 200 * key release, cpu would wakeup and enter wfi again immediately. So 201 * here is the problem: cpu can only wakeup when long key released. 202 * 203 * Actually, we want cpu can detect long key event without key release, 204 * so we give a suspend delay timeout for cpu to detect this. 205 */ 206 if (priv->suspend_delay_timeout && 207 get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS) 208 return 0; 209 210 if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 211 printf("\nSystem suspend: "); 212 putc('0'); 213 local_irq_disable(); 214 putc('1'); 215 regulators_enable_state_mem(false); 216 putc('2'); 217 pmic_suspend(priv->pmic); 218 putc('3'); 219 irqs_suspend(); 220 putc('4'); 221 device_suspend(); 222 putc('5'); 223 putc('\n'); 224 225 /* Trap into ATF for low power mode */ 226 cpu_suspend(0, psci_system_suspend); 227 228 putc('\n'); 229 putc('4'); 230 device_resume(); 231 putc('3'); 232 irqs_resume(); 233 putc('2'); 234 pmic_resume(priv->pmic); 235 putc('1'); 236 local_irq_enable(); 237 putc('0'); 238 putc('\n'); 239 } else { 240 irqs_suspend(); 241 printf("\nWfi\n"); 242 wfi(); 243 putc('1'); 244 irqs_resume(); 245 } 246 247 priv->suspend_delay_timeout = get_timer(0); 248 249 /* 250 * We must wait for key release event finish, otherwise 251 * we may read key state too early. 252 */ 253 mdelay(300); 254 255 return 0; 256 } 257 258 static void timer_irq_handler(int irq, void *data) 259 { 260 struct udevice *dev = data; 261 struct charge_animation_priv *priv = dev_get_priv(dev); 262 static long long count; 263 264 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 265 266 priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 267 printf("auto wakeup count: %lld\n", ++count); 268 } 269 270 static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 271 { 272 uint64_t period = 24000000ULL * seconds; 273 274 /* Disable before conifg */ 275 writel(0, TIMER_BASE + TIMER_CTRL); 276 277 /* Config */ 278 writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 279 writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 280 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 281 writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 282 283 /* IRQ */ 284 irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 285 irq_handler_enable(TIMER_IRQ); 286 } 287 288 static void autowakeup_timer_uninit(void) 289 { 290 writel(0, TIMER_BASE + TIMER_CTRL); 291 292 irq_handler_disable(TIMER_IRQ); 293 irq_free_handler(TIMER_IRQ); 294 } 295 #endif 296 297 #ifdef CONFIG_DRM_ROCKCHIP 298 static void charge_show_bmp(const char *name) 299 { 300 rockchip_show_bmp(name); 301 } 302 303 static void charge_show_logo(void) 304 { 305 rockchip_show_logo(); 306 } 307 #else 308 static void charge_show_bmp(const char *name) {} 309 static void charge_show_logo(void) {} 310 #endif 311 312 #ifdef CONFIG_LED 313 static int leds_update(struct udevice *dev, int soc) 314 { 315 struct charge_animation_priv *priv = dev_get_priv(dev); 316 static int old_soc = -1; 317 int ret, ledst; 318 319 if (old_soc == soc) 320 return 0; 321 322 old_soc = soc; 323 if (priv->led_charging) { 324 ledst = (soc < 100) ? LEDST_ON : LEDST_OFF; 325 ret = led_set_state(priv->led_charging, ledst); 326 if (ret) { 327 printf("set charging led %s failed, ret=%d\n", 328 (ledst == LEDST_ON) ? "ON" : "OFF", ret); 329 return ret; 330 } 331 } 332 333 if (priv->led_full) { 334 ledst = (soc == 100) ? LEDST_ON : LEDST_OFF; 335 ret = led_set_state(priv->led_full, ledst); 336 if (ret) { 337 printf("set charging full led %s failed, ret=%d\n", 338 ledst == LEDST_ON ? "ON" : "OFF", ret); 339 return ret; 340 } 341 } 342 343 return 0; 344 } 345 #else 346 static int leds_update(struct udevice *dev, int soc) { return 0; } 347 #endif 348 349 static int fg_charger_get_chrg_online(struct udevice *dev) 350 { 351 struct charge_animation_priv *priv = dev_get_priv(dev); 352 struct udevice *charger; 353 354 charger = priv->charger ? : priv->fg; 355 356 return fuel_gauge_get_chrg_online(charger); 357 } 358 359 static int charge_extrem_low_power(struct udevice *dev) 360 { 361 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 362 struct charge_animation_priv *priv = dev_get_priv(dev); 363 struct udevice *pmic = priv->pmic; 364 struct udevice *fg = priv->fg; 365 int voltage, soc, charging = 1; 366 static int timer_initialized; 367 int ret; 368 369 voltage = fuel_gauge_get_voltage(fg); 370 if (voltage < 0) 371 return -EINVAL; 372 373 while (voltage < pdata->low_power_voltage + 50) { 374 /* Check charger online */ 375 charging = fg_charger_get_chrg_online(dev); 376 if (charging <= 0) { 377 printf("%s: Not charging, online=%d. Shutdown...\n", 378 __func__, charging); 379 /* wait uart flush before shutdown */ 380 mdelay(5); 381 /* PMIC shutdown */ 382 pmic_shutdown(pmic); 383 384 printf("Cpu should never reach here, shutdown failed !\n"); 385 continue; 386 } 387 388 /* Enable auto wakeup */ 389 if (!timer_initialized) { 390 timer_initialized = 1; 391 autowakeup_timer_init(dev, 5); 392 } 393 394 /* 395 * Just for fuel gauge to update something important, 396 * including charge current, coulometer or other. 397 */ 398 soc = fuel_gauge_update_get_soc(fg); 399 if (soc < 0 || soc > 100) { 400 printf("get soc failed: %d\n", soc); 401 continue; 402 } 403 404 /* Update led */ 405 ret = leds_update(dev, soc); 406 if (ret) 407 printf("update led failed: %d\n", ret); 408 409 printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 410 pdata->low_power_voltage, voltage); 411 412 /* System suspend */ 413 system_suspend_enter(dev); 414 415 /* Update voltage */ 416 voltage = fuel_gauge_get_voltage(fg); 417 if (voltage < 0) { 418 printf("get voltage failed: %d\n", voltage); 419 continue; 420 } 421 422 if (ctrlc()) { 423 printf("Extrem low charge: exit by ctrl+c\n"); 424 break; 425 } 426 } 427 428 autowakeup_timer_uninit(); 429 430 return 0; 431 } 432 433 static int charge_animation_show(struct udevice *dev) 434 { 435 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 436 struct charge_animation_priv *priv = dev_get_priv(dev); 437 const struct charge_image *image = priv->image; 438 struct udevice *pmic = priv->pmic; 439 struct udevice *fg = priv->fg; 440 const char *preboot = env_get("preboot"); 441 int image_num = priv->image_num; 442 bool ever_lowpower_screen_off = false; 443 bool screen_on = true; 444 ulong show_start = 0, charge_start = 0, debug_start = 0; 445 ulong delta; 446 ulong ms = 0, sec = 0; 447 int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RESET_IDX; 448 int soc, voltage, current, key_state; 449 int i, charging = 1, ret; 450 int boot_mode; 451 int first_poll_fg = 1; 452 bool exit_charge = false; 453 454 /* 455 * Check sequence: 456 * 457 * 1. Extrem low power charge? 458 * 2. Preboot cmd? 459 * 3. Valid boot mode? 460 * 4. U-Boot charge enabled by dts config? 461 * 5. Screen off before charge? 462 * 6. Enter charge ! 463 * 464 */ 465 if (!fuel_gauge_bat_is_exist(fg)) { 466 printf("Exit charge: battery is not exist\n"); 467 return 0; 468 } 469 470 /* Extrem low power charge */ 471 ret = charge_extrem_low_power(dev); 472 if (ret < 0) { 473 printf("extrem low power charge failed, ret=%d\n", ret); 474 return ret; 475 } 476 477 /* If there is preboot command, exit */ 478 if (preboot && !strstr(preboot, "dvfs")) { 479 printf("Exit charge: due to preboot cmd '%s'\n", preboot); 480 return 0; 481 } 482 483 /* Not valid charge mode, exit */ 484 #ifdef CONFIG_RKIMG_BOOTLOADER 485 boot_mode = rockchip_get_boot_mode(); 486 if ((boot_mode != BOOT_MODE_CHARGING) && 487 (boot_mode != BOOT_MODE_UNDEFINE)) { 488 printf("Exit charge: due to boot mode\n"); 489 return 0; 490 } 491 #endif 492 charging = fg_charger_get_chrg_online(dev); 493 /* Not charger online and low power, shutdown */ 494 if (charging <= 0 && pdata->auto_exit_charge) { 495 soc = fuel_gauge_update_get_soc(fg); 496 voltage = fuel_gauge_get_voltage(fg); 497 if (soc < pdata->exit_charge_level) { 498 printf("soc(%d%%) < exit_charge_level(%d%%)\n", 499 soc, pdata->exit_charge_level); 500 exit_charge = true; 501 } 502 if (voltage < pdata->exit_charge_voltage) { 503 printf("voltage(%d) < exit_charge_voltage(%d)\n", 504 voltage, pdata->exit_charge_voltage); 505 exit_charge = true; 506 } 507 if (exit_charge) { 508 printf("Not charging and low power, Shutdown...\n"); 509 show_idx = IMAGE_LOWPOWER_IDX(image_num); 510 charge_show_bmp(image[show_idx].name); 511 mdelay(1000); 512 pmic_shutdown(pmic); 513 } 514 } 515 516 /* Not charger online, exit */ 517 if (charging <= 0) { 518 printf("Exit charge: due to charger offline\n"); 519 return 0; 520 } 521 522 /* Enter android charge, set property for kernel */ 523 if (pdata->android_charge) { 524 env_update("bootargs", "androidboot.mode=charger"); 525 printf("Android charge mode\n"); 526 } 527 528 /* Not enable U-Boot charge, exit */ 529 if (!pdata->uboot_charge) { 530 printf("Exit charge: due to not enable uboot charge\n"); 531 return 0; 532 } 533 534 voltage = fuel_gauge_get_voltage(fg); 535 if (voltage < 0) { 536 printf("get voltage failed: %d\n", voltage); 537 return -EINVAL; 538 } 539 540 /* If low power, turn off screen */ 541 if (voltage <= pdata->screen_on_voltage + 50) { 542 screen_on = false; 543 ever_lowpower_screen_off = true; 544 charge_show_bmp(NULL); 545 } 546 547 /* Auto wakeup */ 548 if (pdata->auto_wakeup_interval) { 549 printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 550 autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 551 } 552 553 /* Give a message warning when CONFIG_IRQ is not enabled */ 554 #ifdef CONFIG_IRQ 555 printf("Enter U-Boot charging mode\n"); 556 #else 557 printf("Enter U-Boot charging mode(without IRQ)\n"); 558 #endif 559 560 charge_start = get_timer(0); 561 delta = get_timer(0); 562 563 /* Charging ! */ 564 while (1) { 565 /* 566 * At the most time, fuel gauge is usually a i2c device, we 567 * should avoid read/write all the time. We had better set 568 * poll seconds to update fuel gauge info. 569 */ 570 if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS) 571 goto show_images; 572 573 delta = get_timer(0); 574 575 debug("step1 (%d)... \n", screen_on); 576 577 /* 578 * Most fuel gauge is I2C interface, it shouldn't be interrupted 579 * during transfer. The power key event depends on interrupt, so 580 * we should disable local irq when update fuel gauge. 581 */ 582 local_irq_disable(); 583 584 /* Step1: Is charging now ? */ 585 charging = fg_charger_get_chrg_online(dev); 586 if (charging <= 0) { 587 printf("Not charging, online=%d. Shutdown...\n", 588 charging); 589 #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 590 /* 591 * If charger is plug out during charging, display poweroff 592 * image before device power off. 593 * Irq must be enable if CONFIG_IRQ is defined, because 594 * ebc need to wait irq to indicate frame is complete. 595 */ 596 #ifdef CONFIG_IRQ 597 local_irq_enable(); 598 #endif 599 ret = rockchip_eink_show_charge_logo(EINK_LOGO_POWEROFF); 600 if (ret != 0) 601 printf("Eink display reset logo failed\n"); 602 #ifdef CONFIG_IRQ 603 local_irq_disable(); 604 #endif 605 #endif 606 /* wait uart flush before shutdown */ 607 mdelay(5); 608 609 /* PMIC shutdown */ 610 pmic_shutdown(pmic); 611 612 printf("Cpu should never reach here, shutdown failed !\n"); 613 continue; 614 } 615 616 debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 617 618 /* Step2: get soc and voltage */ 619 soc = fuel_gauge_update_get_soc(fg); 620 if (soc < 0 || soc > 100) { 621 printf("get soc failed: %d\n", soc); 622 continue; 623 } 624 625 voltage = fuel_gauge_get_voltage(fg); 626 if (voltage < 0) { 627 printf("get voltage failed: %d\n", voltage); 628 continue; 629 } 630 631 current = fuel_gauge_get_current(fg); 632 if (current == -ENOSYS) { 633 printf("get current failed: %d\n", current); 634 continue; 635 } 636 637 first_poll_fg = 0; 638 local_irq_enable(); 639 640 if (pdata->auto_exit_charge) { 641 /* Is able to boot now ? */ 642 if (pdata->exit_charge_level && 643 soc >= pdata->exit_charge_level) { 644 printf("soc(%d%%) exit charge animation...\n", 645 soc); 646 break; 647 } 648 if (pdata->exit_charge_voltage && 649 voltage >= pdata->exit_charge_voltage) { 650 printf("vol(%d) exit charge animation...\n", 651 voltage); 652 break; 653 } 654 } 655 656 show_images: 657 /* 658 * Just for debug, otherwise there will be nothing output which 659 * is not good to know what happen. 660 */ 661 if (!debug_start) 662 debug_start = get_timer(0); 663 if (get_timer(debug_start) > 20000) { 664 debug_start = get_timer(0); 665 printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, " 666 "online=%d, screen_on=%d\n", 667 get_timer(0)/1000, soc, voltage, 668 current, charging, screen_on); 669 } 670 671 /* Update leds */ 672 ret = leds_update(dev, soc); 673 if (ret) 674 printf("update led failed: %d\n", ret); 675 676 /* 677 * If ever lowpower screen off, force screen_on=false, which 678 * means key event can't modify screen_on, only voltage higher 679 * then threshold can update screen_on=true; 680 */ 681 if (ever_lowpower_screen_off) 682 screen_on = false; 683 684 /* 685 * Auto turn on screen when voltage higher than Vol screen on. 686 * 'ever_lowpower_screen_off' means enter the while(1) loop with 687 * screen off. 688 */ 689 if ((ever_lowpower_screen_off) && 690 (voltage > pdata->screen_on_voltage)) { 691 ever_lowpower_screen_off = false; 692 screen_on = true; 693 show_idx = IMAGE_RESET_IDX; 694 } 695 696 /* 697 * IMAGE_RESET_IDX means show_idx show be update by start_idx. 698 * When short key pressed event trigged, we will set show_idx 699 * as IMAGE_RESET_IDX which updates images index from start_idx 700 * that calculate by current soc. 701 */ 702 if (show_idx == IMAGE_RESET_IDX) { 703 for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) { 704 /* Find out which image we start to show */ 705 if ((soc >= image[i].soc) && 706 (soc < image[i + 1].soc)) { 707 start_idx = i; 708 break; 709 } 710 711 if (soc >= 100) { 712 start_idx = IMAGE_SOC_100_IDX(image_num); 713 break; 714 } 715 } 716 717 debug("%s: show_idx=%d, screen_on=%d\n", 718 __func__, show_idx, screen_on); 719 720 /* Mark start index and start time */ 721 show_idx = start_idx; 722 show_start = get_timer(0); 723 } 724 725 debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 726 727 #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 728 /* 729 * Device is auto wakeup from suspend, if it's eink display, 730 * screen will display the last image after suspend, so 731 * we should update the image to show the approximate 732 * battery power if battery is charging to next level. 733 */ 734 if (pdata->auto_wakeup_interval && 735 priv->auto_wakeup_key_state == KEY_PRESS_DOWN && 736 !screen_on) { 737 if (soc >= image[old_show_idx + 1].soc && 738 soc < 100) { 739 int ret; 740 int logo_type = EINK_LOGO_CHARGING_0; 741 742 logo_type = logo_type << (old_show_idx + 1); 743 ret = rockchip_eink_show_charge_logo(logo_type); 744 /* 745 * only change the logic if eink is 746 * actually exist 747 */ 748 if (ret == 0) { 749 printf("Update image id[%d] for eink\n", 750 old_show_idx + 1); 751 old_show_idx++; 752 } 753 } 754 } 755 /* 756 * If battery capacity is charged to 100%, exit charging 757 * animation and boot android system. 758 */ 759 if (soc >= 100) { 760 int ret; 761 int logo_type = EINK_LOGO_CHARGING_5; 762 763 ret = rockchip_eink_show_charge_logo(logo_type); 764 /* Only change the logic if eink is acutally exist */ 765 if (ret == 0) { 766 printf("battery FULL,exit charge animation\n"); 767 mdelay(20); 768 break; 769 } 770 } 771 #endif 772 /* Step3: show images */ 773 if (screen_on) { 774 /* Don't call 'charge_show_bmp' unless image changed */ 775 if (old_show_idx != show_idx) { 776 #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 777 int logo_type = EINK_LOGO_CHARGING_0; 778 779 rockchip_eink_show_charge_logo(logo_type << 780 show_idx); 781 #endif 782 old_show_idx = show_idx; 783 debug("SHOW: %s\n", image[show_idx].name); 784 charge_show_bmp(image[show_idx].name); 785 } 786 /* Re-calculate timeout to off screen */ 787 if (priv->auto_screen_off_timeout == 0) 788 priv->auto_screen_off_timeout = get_timer(0); 789 } else { 790 priv->auto_screen_off_timeout = 0; 791 system_suspend_enter(dev); 792 } 793 794 mdelay(5); 795 796 /* Every image shows period */ 797 if (get_timer(show_start) > image[show_idx].period) { 798 show_start = get_timer(0); 799 /* Update to next image */ 800 show_idx++; 801 if (show_idx > IMAGE_SOC_100_IDX(image_num)) 802 show_idx = IMAGE_RESET_IDX; 803 } 804 805 debug("step4 (%d)... \n", screen_on); 806 807 /* 808 * Step4: check key event. 809 * 810 * Short key event: turn on/off screen; 811 * Long key event: show logo and boot system or still charging. 812 */ 813 key_state = check_key_press(dev); 814 if (key_state == KEY_PRESS_DOWN) { 815 /* 816 * Clear current image index, and show image 817 * from start_idx 818 */ 819 old_show_idx = IMAGE_RESET_IDX; 820 show_idx = IMAGE_RESET_IDX; 821 822 /* 823 * Reverse the screen state 824 * 825 * If screen_on=false, means this short key pressed 826 * event turn on the screen and we need show images. 827 * 828 * If screen_on=true, means this short key pressed 829 * event turn off the screen and we never show images. 830 */ 831 if (screen_on) { 832 #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 833 int type = EINK_LOGO_CHARGING_0 << start_idx; 834 /* 835 * Show current battery capacity before suspend 836 * if it's eink display, because eink screen 837 * will continue to display the last image 838 * after suspend, so user can get the 839 * approximate capacity by image displayed. 840 */ 841 ret = rockchip_eink_show_charge_logo(type); 842 /* only change the logic if eink display ok */ 843 if (ret == 0) 844 old_show_idx = start_idx; 845 #endif 846 charge_show_bmp(NULL); /* Turn off screen */ 847 screen_on = false; 848 priv->suspend_delay_timeout = get_timer(0); 849 } else { 850 screen_on = true; 851 } 852 853 printf("screen %s\n", screen_on ? "on" : "off"); 854 } else if (key_state == KEY_PRESS_LONG_DOWN) { 855 /* Set screen_on=true anyway when key long pressed */ 856 if (!screen_on) 857 screen_on = true; 858 859 printf("screen %s\n", screen_on ? "on" : "off"); 860 861 /* Is able to boot now ? */ 862 if (soc < pdata->exit_charge_level) { 863 printf("soc=%d%%, threshold soc=%d%%\n", 864 soc, pdata->exit_charge_level); 865 printf("Low power, unable to boot, charging...\n"); 866 show_idx = IMAGE_LOWPOWER_IDX(image_num); 867 continue; 868 } 869 870 if (voltage < pdata->exit_charge_voltage) { 871 printf("voltage=%dmv, threshold voltage=%dmv\n", 872 voltage, pdata->exit_charge_voltage); 873 printf("Low power, unable to boot, charging...\n"); 874 show_idx = IMAGE_LOWPOWER_IDX(image_num); 875 continue; 876 } 877 878 /* Success exit charging */ 879 printf("Exit charge animation...\n"); 880 charge_show_logo(); 881 break; 882 } else { 883 /* Do nothing */ 884 } 885 886 debug("step5 (%d)... \n", screen_on); 887 888 /* Step5: Exit by ctrl+c */ 889 if (ctrlc()) { 890 if (voltage >= pdata->screen_on_voltage) 891 charge_show_logo(); 892 printf("Exit charge, due to ctrl+c\n"); 893 break; 894 } 895 } 896 897 if (pdata->auto_wakeup_interval) 898 autowakeup_timer_uninit(); 899 900 ms = get_timer(charge_start); 901 if (ms >= 1000) { 902 sec = ms / 1000; 903 ms = ms % 1000; 904 } 905 906 printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 907 sec, ms, soc, voltage); 908 909 return 0; 910 } 911 912 static int fg_charger_get_device(struct udevice **fuel_gauge, 913 struct udevice **charger) 914 { 915 struct udevice *dev; 916 struct uclass *uc; 917 int ret, cap; 918 919 *fuel_gauge = NULL, 920 *charger = NULL; 921 922 ret = uclass_get(UCLASS_FG, &uc); 923 if (ret) 924 return ret; 925 926 for (uclass_first_device(UCLASS_FG, &dev); 927 dev; 928 uclass_next_device(&dev)) { 929 cap = fuel_gauge_capability(dev); 930 if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) { 931 *fuel_gauge = dev; 932 *charger = NULL; 933 } else if (cap == FG_CAP_FUEL_GAUGE) { 934 *fuel_gauge = dev; 935 } else if (cap == FG_CAP_CHARGER) { 936 *charger = dev; 937 } 938 } 939 940 return (*fuel_gauge) ? 0 : -ENODEV; 941 } 942 943 static const struct dm_charge_display_ops charge_animation_ops = { 944 .show = charge_animation_show, 945 }; 946 947 static int charge_animation_probe(struct udevice *dev) 948 { 949 struct charge_animation_priv *priv = dev_get_priv(dev); 950 int ret, soc; 951 952 /* Get PMIC: used for power off system */ 953 ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic); 954 if (ret) { 955 if (ret == -ENODEV) 956 printf("Can't find PMIC\n"); 957 else 958 printf("Get UCLASS PMIC failed: %d\n", ret); 959 return ret; 960 } 961 962 /* Get fuel gauge and charger(If need) */ 963 ret = fg_charger_get_device(&priv->fg, &priv->charger); 964 if (ret) { 965 if (ret == -ENODEV) 966 debug("Can't find FG\n"); 967 else 968 debug("Get UCLASS FG failed: %d\n", ret); 969 return ret; 970 } 971 972 /* Get rtc: used for power on */ 973 ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc); 974 if (ret) { 975 if (ret == -ENODEV) 976 debug("Can't find RTC\n"); 977 else 978 debug("Get UCLASS RTC failed: %d\n", ret); 979 } 980 981 /* Get PWRKEY: used for wakeup and turn off/on LCD */ 982 if (key_read(KEY_POWER) == KEY_NOT_EXIST) { 983 debug("Can't find power key\n"); 984 return -EINVAL; 985 } 986 987 /* Initialize charge current */ 988 soc = fuel_gauge_update_get_soc(priv->fg); 989 if (soc < 0 || soc > 100) { 990 debug("get soc failed: %d\n", soc); 991 return -EINVAL; 992 } 993 994 /* Get leds */ 995 #ifdef CONFIG_LED 996 ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging); 997 if (!ret) 998 printf("Found Charging LED\n"); 999 ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full); 1000 if (!ret) 1001 printf("Found Charging-Full LED\n"); 1002 #endif 1003 1004 /* Get charge images */ 1005 priv->image = image; 1006 priv->image_num = ARRAY_SIZE(image); 1007 1008 printf("Enable charge animation display\n"); 1009 1010 return 0; 1011 } 1012 1013 static const struct udevice_id charge_animation_ids[] = { 1014 { .compatible = "rockchip,uboot-charge" }, 1015 { }, 1016 }; 1017 1018 U_BOOT_DRIVER(charge_animation) = { 1019 .name = "charge-animation", 1020 .id = UCLASS_CHARGE_DISPLAY, 1021 .probe = charge_animation_probe, 1022 .of_match = charge_animation_ids, 1023 .ops = &charge_animation_ops, 1024 .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 1025 .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 1026 .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 1027 }; 1028