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