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