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