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 <pwm.h> 15 #include <irq-generic.h> 16 #include <asm/arch/rockchip_smccc.h> 17 #include <asm/suspend.h> 18 #include <linux/input.h> 19 #include <power/charge_display.h> 20 #include <power/charge_animation.h> 21 #include <power/rockchip_pm.h> 22 #include <power/fuel_gauge.h> 23 #include <power/pmic.h> 24 #include <power/rk8xx_pmic.h> 25 #include <power/regulator.h> 26 #include <rk_timer_irq.h> 27 #include <video_rockchip.h> 28 29 DECLARE_GLOBAL_DATA_PTR; 30 31 #define IMAGE_SHOW_RESET -1 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 const struct charge_image *image; 43 int image_num; 44 45 int auto_wakeup_key_state; 46 ulong auto_screen_off_timeout; 47 }; 48 49 /* 50 * IF you want to use your own charge images, please: 51 * 52 * 1. Update the following 'image[]' to point to your own images; 53 * 2. You must set the failed image as last one and soc = -1 !!! 54 */ 55 static const struct charge_image image[] = { 56 { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 57 { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 58 { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 59 { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 60 { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 61 { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 62 { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 63 }; 64 65 static int charge_animation_ofdata_to_platdata(struct udevice *dev) 66 { 67 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 68 69 /* charge mode */ 70 pdata->uboot_charge = 71 dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 72 pdata->android_charge = 73 dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 74 75 pdata->exit_charge_level = 76 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 77 pdata->exit_charge_voltage = 78 dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 79 80 pdata->low_power_voltage = 81 dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 82 83 pdata->screen_on_voltage = 84 dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 85 pdata->system_suspend = 86 dev_read_u32_default(dev, "rockchip,system-suspend", 0); 87 88 pdata->auto_wakeup_interval = 89 dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 90 pdata->auto_wakeup_screen_invert = 91 dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 92 93 pdata->auto_off_screen_interval = 94 dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 95 96 if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 97 pdata->screen_on_voltage = pdata->exit_charge_voltage; 98 99 debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 100 "lp_voltage=%d%%, screen_on=%dmv\n", 101 pdata->uboot_charge, pdata->android_charge, 102 pdata->exit_charge_level, pdata->exit_charge_voltage, 103 pdata->low_power_voltage, pdata->screen_on_voltage); 104 105 return 0; 106 } 107 108 static int check_key_press(struct udevice *dev) 109 { 110 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 111 struct charge_animation_priv *priv = dev_get_priv(dev); 112 u32 state; 113 114 state = platform_key_read(KEY_POWER); 115 if (state < 0) 116 printf("read power key failed: %d\n", state); 117 118 /* Fixup key state for following cases */ 119 if (pdata->auto_wakeup_interval) { 120 if (pdata->auto_wakeup_screen_invert) { 121 if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 122 /* Value is updated in timer interrupt */ 123 priv->auto_wakeup_key_state = KEY_PRESS_NONE; 124 state = KEY_PRESS_DOWN; 125 } 126 } 127 } else if (pdata->auto_off_screen_interval) { 128 if (get_timer(priv->auto_screen_off_timeout) > 129 pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 130 state = KEY_PRESS_DOWN; 131 printf("Auto screen off\n"); 132 } 133 } 134 135 return state; 136 } 137 138 static int system_suspend_enter(struct charge_animation_pdata *pdata) 139 { 140 /* 141 * TODO: enter low power mode: 142 * 3. auto turn off screen when timout; 143 * 4. power key wakeup; 144 * 5. timer period wakeup for pmic fg 145 */ 146 if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 147 printf("\nSystem suspend: "); 148 putc('1'); 149 local_irq_disable(); 150 putc('2'); 151 irqs_suspend(); 152 putc('3'); 153 device_suspend(); 154 putc('4'); 155 putc('\n'); 156 157 /* Trap into ATF for low power mode */ 158 cpu_suspend(0, psci_system_suspend); 159 160 putc('\n'); 161 putc('4'); 162 device_resume(); 163 putc('3'); 164 irqs_resume(); 165 putc('2'); 166 local_irq_enable(); 167 putc('1'); 168 putc('\n'); 169 } else { 170 printf("\nWfi\n"); 171 wfi(); 172 } 173 174 /* 175 * We must wait for key release event finish, otherwise 176 * we may read key state too early. 177 */ 178 mdelay(300); 179 180 return 0; 181 } 182 183 static void timer_irq_handler(int irq, void *data) 184 { 185 struct udevice *dev = data; 186 struct charge_animation_priv *priv = dev_get_priv(dev); 187 static long long count; 188 189 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 190 191 priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 192 printf("auto wakeup count: %lld\n", ++count); 193 } 194 195 static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 196 { 197 uint64_t period = 24000000ULL * seconds; 198 199 /* Disable before conifg */ 200 writel(0, TIMER_BASE + TIMER_CTRL); 201 202 /* Config */ 203 writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 204 writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 205 writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 206 writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 207 208 /* IRQ */ 209 irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 210 irq_handler_enable(TIMER_IRQ); 211 } 212 213 static void autowakeup_timer_uninit(void) 214 { 215 irq_free_handler(TIMER_IRQ); 216 } 217 218 #ifdef CONFIG_DRM_ROCKCHIP 219 static void charge_show_bmp(const char *name) 220 { 221 rockchip_show_bmp(name); 222 } 223 224 static void charge_show_logo(void) 225 { 226 rockchip_show_logo(); 227 } 228 #else 229 static void charge_show_bmp(const char *name) {} 230 static void charge_show_logo(void) {} 231 #endif 232 233 static int charge_extrem_low_power(struct udevice *dev) 234 { 235 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 236 struct charge_animation_priv *priv = dev_get_priv(dev); 237 struct udevice *pmic = priv->pmic; 238 struct udevice *fg = priv->fg; 239 int voltage, soc, charging = 1; 240 static int timer_initialized; 241 242 voltage = fuel_gauge_get_voltage(fg); 243 if (voltage < 0) 244 return -EINVAL; 245 246 while (voltage < pdata->low_power_voltage + 50) { 247 /* Check charger online */ 248 charging = fuel_gauge_get_chrg_online(fg); 249 if (charging <= 0) { 250 printf("%s: Not charging, online=%d. Shutdown...\n", 251 __func__, charging); 252 /* wait uart flush before shutdown */ 253 mdelay(5); 254 /* PMIC shutdown */ 255 pmic_shutdown(pmic); 256 257 printf("Cpu should never reach here, shutdown failed !\n"); 258 continue; 259 } 260 261 /* Enable auto wakeup */ 262 if (!timer_initialized) { 263 timer_initialized = 1; 264 autowakeup_timer_init(dev, 5); 265 } 266 267 /* 268 * Just for fuel gauge to update something important, 269 * including charge current, coulometer or other. 270 */ 271 soc = fuel_gauge_get_soc(fg); 272 if (soc < 0 || soc > 100) { 273 printf("get soc failed: %d\n", soc); 274 continue; 275 } 276 277 printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 278 pdata->low_power_voltage, voltage); 279 280 /* System suspend */ 281 system_suspend_enter(pdata); 282 283 /* Update voltage */ 284 voltage = fuel_gauge_get_voltage(fg); 285 if (voltage < 0) { 286 printf("get voltage failed: %d\n", voltage); 287 continue; 288 } 289 } 290 291 autowakeup_timer_uninit(); 292 293 return 0; 294 } 295 296 static int charge_animation_show(struct udevice *dev) 297 { 298 struct charge_animation_pdata *pdata = dev_get_platdata(dev); 299 struct charge_animation_priv *priv = dev_get_priv(dev); 300 const struct charge_image *image = priv->image; 301 struct udevice *pmic = priv->pmic; 302 struct udevice *fg = priv->fg; 303 const char *preboot = env_get("preboot"); 304 int image_num = priv->image_num; 305 bool ever_lowpower_screen_off = false; 306 bool screen_on = true; 307 ulong show_start = 0, charge_start = 0, debug_start = 0; 308 ulong ms = 0, sec = 0; 309 int start_idx = 0, show_idx = -1; 310 int soc, voltage, current, key_state; 311 int i, charging = 1, ret; 312 int boot_mode; 313 314 /* 315 * Check sequence: 316 * 317 * 1. Extrem low power charge? 318 * 2. Preboot cmd? 319 * 3. Valid boot mode? 320 * 4. U-Boot charge enabled by dts config? 321 * 5. Screen off before charge? 322 * 6. Enter charge ! 323 * 324 */ 325 /* Extrem low power charge */ 326 ret = charge_extrem_low_power(dev); 327 if (ret < 0) { 328 printf("extrem low power charge failed, ret=%d\n", ret); 329 return ret; 330 } 331 332 /* If there is preboot command, exit */ 333 if (preboot) { 334 debug("exit charge, due to preboot: %s\n", preboot); 335 return 0; 336 } 337 338 /* Not valid charge mode, exit */ 339 #ifdef CONFIG_RKIMG_BOOTLOADER 340 boot_mode = rockchip_get_boot_mode(); 341 if ((boot_mode != BOOT_MODE_CHARGING) && 342 (boot_mode != BOOT_MODE_UNDEFINE)) { 343 debug("exit charge, due to boot mode: %d\n", boot_mode); 344 return 0; 345 } 346 #endif 347 348 /* Not charger online, exit */ 349 charging = fuel_gauge_get_chrg_online(fg); 350 if (charging <= 0) { 351 debug("exit charge, due to charger offline\n"); 352 return 0; 353 } 354 355 /* Enter android charge, set property for kernel */ 356 if (pdata->android_charge) { 357 env_update("bootargs", "androidboot.mode=charger"); 358 printf("Android charge mode\n"); 359 } 360 361 /* Not enable U-Boot charge, exit */ 362 if (!pdata->uboot_charge) { 363 debug("exit charge, due to not enable uboot charge\n"); 364 return 0; 365 } 366 367 voltage = fuel_gauge_get_voltage(fg); 368 if (voltage < 0) { 369 printf("get voltage failed: %d\n", voltage); 370 return -EINVAL; 371 } 372 373 /* If low power, turn off screen */ 374 if (voltage <= pdata->screen_on_voltage + 50) { 375 screen_on = false; 376 ever_lowpower_screen_off = true; 377 charge_show_bmp(NULL); 378 } 379 380 /* Auto wakeup */ 381 if (pdata->auto_wakeup_interval) { 382 printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 383 autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 384 } 385 386 printf("Enter U-Boot charging mode\n"); 387 388 charge_start = get_timer(0); 389 390 /* Charging ! */ 391 while (1) { 392 debug("step1 (%d)... \n", screen_on); 393 394 /* Step1: Is charging now ? */ 395 charging = fuel_gauge_get_chrg_online(fg); 396 if (charging <= 0) { 397 printf("Not charging, online=%d. Shutdown...\n", 398 charging); 399 400 /* wait uart flush before shutdown */ 401 mdelay(5); 402 403 /* PMIC shutdown */ 404 pmic_shutdown(pmic); 405 406 printf("Cpu should never reach here, shutdown failed !\n"); 407 continue; 408 } 409 410 debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 411 412 /* Step2: get soc and voltage */ 413 soc = fuel_gauge_get_soc(fg); 414 if (soc < 0 || soc > 100) { 415 printf("get soc failed: %d\n", soc); 416 continue; 417 } 418 419 voltage = fuel_gauge_get_voltage(fg); 420 if (voltage < 0) { 421 printf("get voltage failed: %d\n", voltage); 422 continue; 423 } 424 425 current = fuel_gauge_get_current(fg); 426 if (current == -ENOSYS) { 427 printf("get current failed: %d\n", current); 428 continue; 429 } 430 431 /* 432 * Just for debug, otherwise there will be nothing output which 433 * is not good to know what happen. 434 */ 435 if (!debug_start) 436 debug_start = get_timer(0); 437 if (get_timer(debug_start) > 20000) { 438 debug_start = get_timer(0); 439 printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n", 440 get_timer(0)/1000, soc, voltage, 441 current, charging, screen_on); 442 } 443 444 /* 445 * If ever lowpower screen off, force screen_on=false, which 446 * means key event can't modify screen_on, only voltage higher 447 * then threshold can update screen_on=true; 448 */ 449 if (ever_lowpower_screen_off) 450 screen_on = false; 451 452 /* 453 * Auto turn on screen when voltage higher than Vol screen on. 454 * 'ever_lowpower_screen_off' means enter while loop with 455 * screen off. 456 */ 457 if ((ever_lowpower_screen_off) && 458 (voltage > pdata->screen_on_voltage)) { 459 ever_lowpower_screen_off = false; 460 screen_on = true; 461 show_idx = IMAGE_SHOW_RESET; 462 } 463 464 /* 465 * IMAGE_SHOW_RESET means show_idx show be update by start_idx. 466 * When short key pressed event trigged, we will set show_idx 467 * as IMAGE_SHOW_RESET which updates images index from start_idx 468 * that calculate by current soc. 469 */ 470 if (show_idx == IMAGE_SHOW_RESET) { 471 for (i = 0; i < image_num - 2; i++) { 472 /* Find out which image we start to show */ 473 if ((soc >= image[i].soc) && 474 (soc < image[i + 1].soc)) { 475 start_idx = i; 476 break; 477 } 478 479 if (soc >= 100) { 480 start_idx = image_num - 2; 481 break; 482 } 483 } 484 485 debug("%s: show_idx=%d, screen_on=%d\n", 486 __func__, show_idx, screen_on); 487 488 /* Mark start index and start time */ 489 show_idx = start_idx; 490 show_start = get_timer(0); 491 } 492 493 debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 494 495 /* Step3: show images */ 496 if (screen_on) { 497 debug("SHOW: %s\n", image[show_idx].name); 498 charge_show_bmp(image[show_idx].name); 499 500 /* Re calculate timeout to off screen */ 501 if (priv->auto_screen_off_timeout == 0) 502 priv->auto_screen_off_timeout = get_timer(0); 503 } else { 504 priv->auto_screen_off_timeout = 0; 505 506 system_suspend_enter(pdata); 507 } 508 509 mdelay(5); 510 511 /* Every image shows period */ 512 if (get_timer(show_start) > image[show_idx].period) { 513 show_start = get_timer(0); 514 /* Update to next image */ 515 show_idx++; 516 if (show_idx > (image_num - 2)) 517 show_idx = IMAGE_SHOW_RESET; 518 } 519 520 debug("step4 (%d)... \n", screen_on); 521 522 /* 523 * Step4: check key event. 524 * 525 * Short key event: turn on/off screen; 526 * Long key event: show logo and boot system or still charging. 527 */ 528 key_state = check_key_press(dev); 529 if (key_state == KEY_PRESS_DOWN) { 530 /* NULL means show nothing, ie. turn off screen */ 531 if (screen_on) 532 charge_show_bmp(NULL); 533 534 /* 535 * Clear current image index, and show image 536 * from start_idx 537 */ 538 show_idx = IMAGE_SHOW_RESET; 539 540 /* 541 * We turn off screen by charge_show_bmp(NULL), so we 542 * should tell while loop to stop show images any more. 543 * 544 * If screen_on=false, means this short key pressed 545 * event turn on the screen and we need show images. 546 * 547 * If screen_on=true, means this short key pressed 548 * event turn off the screen and we never show images. 549 */ 550 if (screen_on) 551 screen_on = false; 552 else 553 screen_on = true; 554 } else if (key_state == KEY_PRESS_LONG_DOWN) { 555 /* Only long pressed while screen off needs screen_on true */ 556 if (!screen_on) 557 screen_on = true; 558 559 /* Is able to boot now ? */ 560 if (soc < pdata->exit_charge_level) { 561 printf("soc=%d%%, threshold soc=%d%%\n", 562 soc, pdata->exit_charge_level); 563 printf("Low power, unable to boot, charging...\n"); 564 show_idx = image_num - 1; 565 continue; 566 } 567 568 if (voltage < pdata->exit_charge_voltage) { 569 printf("voltage=%dmv, threshold voltage=%dmv\n", 570 voltage, pdata->exit_charge_voltage); 571 printf("Low power, unable to boot, charging...\n"); 572 show_idx = image_num - 1; 573 continue; 574 } 575 576 /* Success exit charging */ 577 printf("Exit charge animation...\n"); 578 charge_show_logo(); 579 break; 580 } else { 581 /* Do nothing */ 582 } 583 584 debug("step5 (%d)... \n", screen_on); 585 586 /* Step5: Exit by ctrl+c */ 587 if (ctrlc()) { 588 if (voltage >= pdata->screen_on_voltage) 589 charge_show_logo(); 590 printf("Exit charge, due to ctrl+c\n"); 591 break; 592 } 593 } 594 595 if (pdata->auto_wakeup_interval) 596 autowakeup_timer_uninit(); 597 598 ms = get_timer(charge_start); 599 if (ms >= 1000) { 600 sec = ms / 1000; 601 ms = ms % 1000; 602 } 603 604 printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 605 sec, ms, soc, voltage); 606 607 return 0; 608 } 609 610 static const struct dm_charge_display_ops charge_animation_ops = { 611 .show = charge_animation_show, 612 }; 613 614 static int charge_animation_probe(struct udevice *dev) 615 { 616 struct charge_animation_priv *priv = dev_get_priv(dev); 617 struct udevice *fg, *pmic; 618 int ret, soc; 619 620 /* Get PMIC: used for power off system */ 621 ret = uclass_get_device(UCLASS_PMIC, 0, &pmic); 622 if (ret) { 623 if (ret == -ENODEV) 624 printf("Can't find PMIC\n"); 625 else 626 printf("Get UCLASS PMIC failed: %d\n", ret); 627 return ret; 628 } 629 priv->pmic = pmic; 630 631 /* Get fuel gauge: used for charging */ 632 ret = uclass_get_device(UCLASS_FG, 0, &fg); 633 if (ret) { 634 if (ret == -ENODEV) 635 printf("Can't find FG\n"); 636 else 637 printf("Get UCLASS FG failed: %d\n", ret); 638 return ret; 639 } 640 priv->fg = fg; 641 642 /* Get PWRKEY: used for wakeup and turn off/on LCD */ 643 ret = platform_key_read(KEY_POWER); 644 if (ret == KEY_NOT_EXIST) { 645 printf("Can't find power key\n"); 646 return -EINVAL; 647 } 648 649 /* Initialize charge current */ 650 soc = fuel_gauge_get_soc(fg); 651 if (soc < 0 || soc > 100) { 652 printf("get soc failed: %d\n", soc); 653 return -EINVAL; 654 } 655 656 /* Get charge images */ 657 priv->image = image; 658 priv->image_num = ARRAY_SIZE(image); 659 660 printf("Enable charge animation display\n"); 661 662 return 0; 663 } 664 665 static const struct udevice_id charge_animation_ids[] = { 666 { .compatible = "rockchip,uboot-charge" }, 667 { }, 668 }; 669 670 U_BOOT_DRIVER(charge_animation) = { 671 .name = "charge-animation", 672 .id = UCLASS_CHARGE_DISPLAY, 673 .probe = charge_animation_probe, 674 .of_match = charge_animation_ids, 675 .ops = &charge_animation_ops, 676 .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 677 .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 678 .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 679 }; 680