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