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