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