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