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