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