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 7e7f9facbSJoseph Chen #include <asm/io.h> 8f1c8ecceSJoseph Chen #include <common.h> 9d3ff9cf9SKever Yang #include <boot_rkimg.h> 101367bfe3SJoseph Chen #include <console.h> 11f1c8ecceSJoseph Chen #include <dm.h> 12f1c8ecceSJoseph Chen #include <errno.h> 13f1c8ecceSJoseph Chen #include <key.h> 14*8b436ce5SElaine Zhang #include <rtc.h> 15f1c8ecceSJoseph Chen #include <pwm.h> 16d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h> 17d3ff9cf9SKever Yang #include <asm/suspend.h> 18d3ff9cf9SKever Yang #include <linux/input.h> 19f1c8ecceSJoseph Chen #include <power/charge_display.h> 20ac1dc0c3SJoseph Chen #include <power/charge_animation.h> 2188949342SJoseph Chen #include <power/rockchip_pm.h> 22f1c8ecceSJoseph Chen #include <power/fuel_gauge.h> 23f1c8ecceSJoseph Chen #include <power/pmic.h> 24f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h> 25f1c8ecceSJoseph Chen #include <power/regulator.h> 26f1c8ecceSJoseph Chen #include <video_rockchip.h> 27175257e4SJoseph Chen #ifdef CONFIG_IRQ 28175257e4SJoseph Chen #include <irq-generic.h> 29175257e4SJoseph Chen #include <rk_timer_irq.h> 30175257e4SJoseph Chen #endif 31f1c8ecceSJoseph Chen 32f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR; 33f1c8ecceSJoseph Chen 34f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET -1 355e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS 1000 36f1c8ecceSJoseph Chen 37f1c8ecceSJoseph Chen struct charge_image { 38f1c8ecceSJoseph Chen const char *name; 39f1c8ecceSJoseph Chen int soc; 40f1c8ecceSJoseph Chen int period; /* ms */ 41f1c8ecceSJoseph Chen }; 42f1c8ecceSJoseph Chen 43f1c8ecceSJoseph Chen struct charge_animation_priv { 44f1c8ecceSJoseph Chen struct udevice *pmic; 45f1c8ecceSJoseph Chen struct udevice *fg; 46*8b436ce5SElaine Zhang struct udevice *rtc; 47f1c8ecceSJoseph Chen const struct charge_image *image; 48f1c8ecceSJoseph Chen int image_num; 49e7f9facbSJoseph Chen 50e7f9facbSJoseph Chen int auto_wakeup_key_state; 51e7f9facbSJoseph Chen ulong auto_screen_off_timeout; 52f1c8ecceSJoseph Chen }; 53f1c8ecceSJoseph Chen 54f1c8ecceSJoseph Chen /* 55f1c8ecceSJoseph Chen * IF you want to use your own charge images, please: 56f1c8ecceSJoseph Chen * 57f1c8ecceSJoseph Chen * 1. Update the following 'image[]' to point to your own images; 58f1c8ecceSJoseph Chen * 2. You must set the failed image as last one and soc = -1 !!! 59f1c8ecceSJoseph Chen */ 60f1c8ecceSJoseph Chen static const struct charge_image image[] = { 61f1c8ecceSJoseph Chen { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 62f1c8ecceSJoseph Chen { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 63f1c8ecceSJoseph Chen { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 64f1c8ecceSJoseph Chen { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 65f1c8ecceSJoseph Chen { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 66f1c8ecceSJoseph Chen { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 67f1c8ecceSJoseph Chen { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 68f1c8ecceSJoseph Chen }; 69f1c8ecceSJoseph Chen 70f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev) 71f1c8ecceSJoseph Chen { 72f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 73f1c8ecceSJoseph Chen 74d6653c12SJoseph Chen /* charge mode */ 75d6653c12SJoseph Chen pdata->uboot_charge = 76d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 77d6653c12SJoseph Chen pdata->android_charge = 78d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 79f1c8ecceSJoseph Chen 80d6653c12SJoseph Chen pdata->exit_charge_level = 81d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 82d6653c12SJoseph Chen pdata->exit_charge_voltage = 83d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 84f23c35a8SJoseph Chen 85f23c35a8SJoseph Chen pdata->low_power_voltage = 86f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 87f23c35a8SJoseph Chen 88d6653c12SJoseph Chen pdata->screen_on_voltage = 89d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 90f23c35a8SJoseph Chen pdata->system_suspend = 91f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,system-suspend", 0); 92f1c8ecceSJoseph Chen 93e7f9facbSJoseph Chen pdata->auto_wakeup_interval = 94e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 95e7f9facbSJoseph Chen pdata->auto_wakeup_screen_invert = 96e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 97e7f9facbSJoseph Chen 98e7f9facbSJoseph Chen pdata->auto_off_screen_interval = 99e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 100e7f9facbSJoseph Chen 101f23c35a8SJoseph Chen if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 102f23c35a8SJoseph Chen pdata->screen_on_voltage = pdata->exit_charge_voltage; 103f1c8ecceSJoseph Chen 104d6653c12SJoseph Chen debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 105f23c35a8SJoseph Chen "lp_voltage=%d%%, screen_on=%dmv\n", 106d6653c12SJoseph Chen pdata->uboot_charge, pdata->android_charge, 107d6653c12SJoseph Chen pdata->exit_charge_level, pdata->exit_charge_voltage, 108f23c35a8SJoseph Chen pdata->low_power_voltage, pdata->screen_on_voltage); 109f1c8ecceSJoseph Chen 110f1c8ecceSJoseph Chen return 0; 111f1c8ecceSJoseph Chen } 112f1c8ecceSJoseph Chen 113e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev) 114f1c8ecceSJoseph Chen { 115e7f9facbSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 116e7f9facbSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 117*8b436ce5SElaine Zhang u32 state, rtc_state = 0; 118*8b436ce5SElaine Zhang 119*8b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC 120*8b436ce5SElaine Zhang if (priv->rtc) 121*8b436ce5SElaine Zhang rtc_state = rtc_alarm_trigger(priv->rtc); 122*8b436ce5SElaine Zhang #endif 123*8b436ce5SElaine Zhang if (rtc_state) { 124*8b436ce5SElaine Zhang printf("rtc alarm trigger...\n"); 125*8b436ce5SElaine Zhang return KEY_PRESS_LONG_DOWN; 126*8b436ce5SElaine Zhang } 127f1c8ecceSJoseph Chen 128787a62ebSJoseph Chen state = key_read(KEY_POWER); 129b177a917SJoseph Chen if (state < 0) 130f1c8ecceSJoseph Chen printf("read power key failed: %d\n", state); 13193aee2d9SJoseph Chen else if (state == KEY_PRESS_DOWN) 13293aee2d9SJoseph Chen printf("power key pressed...\n"); 13393aee2d9SJoseph Chen else if (state == KEY_PRESS_LONG_DOWN) 13493aee2d9SJoseph Chen printf("power key long pressed...\n"); 135f1c8ecceSJoseph Chen 136e7f9facbSJoseph Chen /* Fixup key state for following cases */ 137e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 138e7f9facbSJoseph Chen if (pdata->auto_wakeup_screen_invert) { 139e7f9facbSJoseph Chen if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 140e7f9facbSJoseph Chen /* Value is updated in timer interrupt */ 141e7f9facbSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_NONE; 142e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 143e7f9facbSJoseph Chen } 144e7f9facbSJoseph Chen } 145e7f9facbSJoseph Chen } else if (pdata->auto_off_screen_interval) { 14693aee2d9SJoseph Chen if (priv->auto_screen_off_timeout && 14793aee2d9SJoseph Chen get_timer(priv->auto_screen_off_timeout) > 148e7f9facbSJoseph Chen pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 149e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 150e7f9facbSJoseph Chen printf("Auto screen off\n"); 151e7f9facbSJoseph Chen } 152e7f9facbSJoseph Chen } 153e7f9facbSJoseph Chen 154b177a917SJoseph Chen return state; 155f1c8ecceSJoseph Chen } 156f1c8ecceSJoseph Chen 157175257e4SJoseph Chen /* 158175257e4SJoseph Chen * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup 159175257e4SJoseph Chen * period timer is useless. 160175257e4SJoseph Chen */ 161175257e4SJoseph Chen #ifndef CONFIG_IRQ 162175257e4SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata) 163175257e4SJoseph Chen { 164175257e4SJoseph Chen return 0; 165175257e4SJoseph Chen } 166175257e4SJoseph Chen 167175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {} 168175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {} 169175257e4SJoseph Chen 170175257e4SJoseph Chen #else 171f23c35a8SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata) 172b177a917SJoseph Chen { 173f23c35a8SJoseph Chen if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 174b177a917SJoseph Chen printf("\nSystem suspend: "); 175992f4e77SJoseph Chen putc('0'); 176992f4e77SJoseph Chen regulators_enable_state_mem(false); 177b177a917SJoseph Chen putc('1'); 178b177a917SJoseph Chen local_irq_disable(); 179b177a917SJoseph Chen putc('2'); 180b177a917SJoseph Chen irqs_suspend(); 181b177a917SJoseph Chen putc('3'); 18288949342SJoseph Chen device_suspend(); 18388949342SJoseph Chen putc('4'); 184b177a917SJoseph Chen putc('\n'); 185b177a917SJoseph Chen 186b177a917SJoseph Chen /* Trap into ATF for low power mode */ 187b177a917SJoseph Chen cpu_suspend(0, psci_system_suspend); 188b177a917SJoseph Chen 189b177a917SJoseph Chen putc('\n'); 19088949342SJoseph Chen putc('4'); 19188949342SJoseph Chen device_resume(); 192b177a917SJoseph Chen putc('3'); 193b177a917SJoseph Chen irqs_resume(); 194b177a917SJoseph Chen putc('2'); 195b177a917SJoseph Chen local_irq_enable(); 196b177a917SJoseph Chen putc('1'); 197b177a917SJoseph Chen putc('\n'); 1988fc5ae06SJoseph Chen } else { 1998fc5ae06SJoseph Chen printf("\nWfi\n"); 2008fc5ae06SJoseph Chen wfi(); 2018fc5ae06SJoseph Chen } 202b177a917SJoseph Chen 203b177a917SJoseph Chen /* 204b177a917SJoseph Chen * We must wait for key release event finish, otherwise 205b177a917SJoseph Chen * we may read key state too early. 206b177a917SJoseph Chen */ 207b177a917SJoseph Chen mdelay(300); 208b177a917SJoseph Chen 209b177a917SJoseph Chen return 0; 210f1c8ecceSJoseph Chen } 21179244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data) 21279244e4cSJoseph Chen { 21379244e4cSJoseph Chen struct udevice *dev = data; 21479244e4cSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 21579244e4cSJoseph Chen static long long count; 21679244e4cSJoseph Chen 21779244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 21879244e4cSJoseph Chen 21979244e4cSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 22079244e4cSJoseph Chen printf("auto wakeup count: %lld\n", ++count); 22179244e4cSJoseph Chen } 22279244e4cSJoseph Chen 22379244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 22479244e4cSJoseph Chen { 22579244e4cSJoseph Chen uint64_t period = 24000000ULL * seconds; 22679244e4cSJoseph Chen 22779244e4cSJoseph Chen /* Disable before conifg */ 22879244e4cSJoseph Chen writel(0, TIMER_BASE + TIMER_CTRL); 22979244e4cSJoseph Chen 23079244e4cSJoseph Chen /* Config */ 23179244e4cSJoseph Chen writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 23279244e4cSJoseph Chen writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 23379244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 23479244e4cSJoseph Chen writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 23579244e4cSJoseph Chen 23679244e4cSJoseph Chen /* IRQ */ 23779244e4cSJoseph Chen irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 23879244e4cSJoseph Chen irq_handler_enable(TIMER_IRQ); 23979244e4cSJoseph Chen } 24079244e4cSJoseph Chen 24179244e4cSJoseph Chen static void autowakeup_timer_uninit(void) 24279244e4cSJoseph Chen { 24379244e4cSJoseph Chen irq_free_handler(TIMER_IRQ); 24479244e4cSJoseph Chen } 245175257e4SJoseph Chen #endif 24679244e4cSJoseph Chen 247f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP 248f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) 249f23c35a8SJoseph Chen { 250f23c35a8SJoseph Chen rockchip_show_bmp(name); 251f23c35a8SJoseph Chen } 252f23c35a8SJoseph Chen 253f23c35a8SJoseph Chen static void charge_show_logo(void) 254f23c35a8SJoseph Chen { 255f23c35a8SJoseph Chen rockchip_show_logo(); 256f23c35a8SJoseph Chen } 257f23c35a8SJoseph Chen #else 258f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {} 259f23c35a8SJoseph Chen static void charge_show_logo(void) {} 260f23c35a8SJoseph Chen #endif 261f23c35a8SJoseph Chen 262f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev) 263f23c35a8SJoseph Chen { 264f23c35a8SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 265f23c35a8SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 266f23c35a8SJoseph Chen struct udevice *pmic = priv->pmic; 267f23c35a8SJoseph Chen struct udevice *fg = priv->fg; 268f23c35a8SJoseph Chen int voltage, soc, charging = 1; 26979244e4cSJoseph Chen static int timer_initialized; 270f23c35a8SJoseph Chen 271f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 272f23c35a8SJoseph Chen if (voltage < 0) 273f23c35a8SJoseph Chen return -EINVAL; 274f23c35a8SJoseph Chen 275f23c35a8SJoseph Chen while (voltage < pdata->low_power_voltage + 50) { 276f23c35a8SJoseph Chen /* Check charger online */ 277f23c35a8SJoseph Chen charging = fuel_gauge_get_chrg_online(fg); 278f23c35a8SJoseph Chen if (charging <= 0) { 27979244e4cSJoseph Chen printf("%s: Not charging, online=%d. Shutdown...\n", 28079244e4cSJoseph Chen __func__, charging); 281f23c35a8SJoseph Chen /* wait uart flush before shutdown */ 28279244e4cSJoseph Chen mdelay(5); 283f23c35a8SJoseph Chen /* PMIC shutdown */ 284f23c35a8SJoseph Chen pmic_shutdown(pmic); 285f23c35a8SJoseph Chen 286f23c35a8SJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 287f23c35a8SJoseph Chen continue; 288f23c35a8SJoseph Chen } 289f23c35a8SJoseph Chen 29079244e4cSJoseph Chen /* Enable auto wakeup */ 29179244e4cSJoseph Chen if (!timer_initialized) { 29279244e4cSJoseph Chen timer_initialized = 1; 29379244e4cSJoseph Chen autowakeup_timer_init(dev, 5); 29479244e4cSJoseph Chen } 29579244e4cSJoseph Chen 296f23c35a8SJoseph Chen /* 297f23c35a8SJoseph Chen * Just for fuel gauge to update something important, 298f23c35a8SJoseph Chen * including charge current, coulometer or other. 299f23c35a8SJoseph Chen */ 300f23c35a8SJoseph Chen soc = fuel_gauge_get_soc(fg); 301f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 302f23c35a8SJoseph Chen printf("get soc failed: %d\n", soc); 303f23c35a8SJoseph Chen continue; 304f23c35a8SJoseph Chen } 305f23c35a8SJoseph Chen 306f23c35a8SJoseph Chen printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 307f23c35a8SJoseph Chen pdata->low_power_voltage, voltage); 308f23c35a8SJoseph Chen 309f23c35a8SJoseph Chen /* System suspend */ 310f23c35a8SJoseph Chen system_suspend_enter(pdata); 311f23c35a8SJoseph Chen 312f23c35a8SJoseph Chen /* Update voltage */ 313f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 314f23c35a8SJoseph Chen if (voltage < 0) { 315f23c35a8SJoseph Chen printf("get voltage failed: %d\n", voltage); 316f23c35a8SJoseph Chen continue; 317f23c35a8SJoseph Chen } 318f23c35a8SJoseph Chen } 319f23c35a8SJoseph Chen 32079244e4cSJoseph Chen autowakeup_timer_uninit(); 32179244e4cSJoseph Chen 322f23c35a8SJoseph Chen return 0; 323f23c35a8SJoseph Chen } 324f23c35a8SJoseph Chen 325f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev) 326f1c8ecceSJoseph Chen { 327f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 328f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 329f1c8ecceSJoseph Chen const struct charge_image *image = priv->image; 330f1c8ecceSJoseph Chen struct udevice *pmic = priv->pmic; 331f1c8ecceSJoseph Chen struct udevice *fg = priv->fg; 332a8b9d026SJoseph Chen const char *preboot = env_get("preboot"); 333f1c8ecceSJoseph Chen int image_num = priv->image_num; 334f1c8ecceSJoseph Chen bool ever_lowpower_screen_off = false; 335f1c8ecceSJoseph Chen bool screen_on = true; 336f1c8ecceSJoseph Chen ulong show_start = 0, charge_start = 0, debug_start = 0; 3375e804741SJoseph Chen ulong delta; 338f1c8ecceSJoseph Chen ulong ms = 0, sec = 0; 33993aee2d9SJoseph Chen int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_SHOW_RESET; 340d6653c12SJoseph Chen int soc, voltage, current, key_state; 341f23c35a8SJoseph Chen int i, charging = 1, ret; 3428f9ff705SJoseph Chen int boot_mode; 3435e804741SJoseph Chen int first_poll_fg = 1; 344f1c8ecceSJoseph Chen 345f23c35a8SJoseph Chen /* 346f23c35a8SJoseph Chen * Check sequence: 347f23c35a8SJoseph Chen * 348f23c35a8SJoseph Chen * 1. Extrem low power charge? 349f23c35a8SJoseph Chen * 2. Preboot cmd? 350f23c35a8SJoseph Chen * 3. Valid boot mode? 351f23c35a8SJoseph Chen * 4. U-Boot charge enabled by dts config? 352f23c35a8SJoseph Chen * 5. Screen off before charge? 353f23c35a8SJoseph Chen * 6. Enter charge ! 354f23c35a8SJoseph Chen * 355f23c35a8SJoseph Chen */ 35661a7a6d6SJoseph Chen if (!fuel_gauge_bat_is_exist(fg)) { 35761a7a6d6SJoseph Chen printf("Exit charge: battery is not exist\n"); 35861a7a6d6SJoseph Chen return 0; 35961a7a6d6SJoseph Chen } 36061a7a6d6SJoseph Chen 361f23c35a8SJoseph Chen /* Extrem low power charge */ 362f23c35a8SJoseph Chen ret = charge_extrem_low_power(dev); 363f23c35a8SJoseph Chen if (ret < 0) { 364f23c35a8SJoseph Chen printf("extrem low power charge failed, ret=%d\n", ret); 365f23c35a8SJoseph Chen return ret; 366f23c35a8SJoseph Chen } 367f23c35a8SJoseph Chen 368a8b9d026SJoseph Chen /* If there is preboot command, exit */ 36961a7a6d6SJoseph Chen if (preboot && !strstr(preboot, "dvfs")) { 3707ae45834SJoseph Chen printf("Exit charge: due to preboot cmd '%s'\n", preboot); 371a8b9d026SJoseph Chen return 0; 372a8b9d026SJoseph Chen } 373a8b9d026SJoseph Chen 374f23c35a8SJoseph Chen /* Not valid charge mode, exit */ 375d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 3768f9ff705SJoseph Chen boot_mode = rockchip_get_boot_mode(); 377221b5220SJoseph Chen if ((boot_mode != BOOT_MODE_CHARGING) && 378221b5220SJoseph Chen (boot_mode != BOOT_MODE_UNDEFINE)) { 3797ae45834SJoseph Chen printf("Exit charge: due to boot mode\n"); 3808f9ff705SJoseph Chen return 0; 3818f9ff705SJoseph Chen } 3828f9ff705SJoseph Chen #endif 383221b5220SJoseph Chen 384616056c9Sshengfei Xu /* Not charger online, exit */ 385616056c9Sshengfei Xu charging = fuel_gauge_get_chrg_online(fg); 386cf49f6adSJoseph Chen if (charging <= 0) { 3877ae45834SJoseph Chen printf("Exit charge: due to charger offline\n"); 388616056c9Sshengfei Xu return 0; 389cf49f6adSJoseph Chen } 390616056c9Sshengfei Xu 391f23c35a8SJoseph Chen /* Enter android charge, set property for kernel */ 392d6653c12SJoseph Chen if (pdata->android_charge) { 393d6653c12SJoseph Chen env_update("bootargs", "androidboot.mode=charger"); 394d6653c12SJoseph Chen printf("Android charge mode\n"); 395d6653c12SJoseph Chen } 396d6653c12SJoseph Chen 397f23c35a8SJoseph Chen /* Not enable U-Boot charge, exit */ 398cf49f6adSJoseph Chen if (!pdata->uboot_charge) { 3997ae45834SJoseph Chen printf("Exit charge: due to not enable uboot charge\n"); 400d6653c12SJoseph Chen return 0; 401cf49f6adSJoseph Chen } 402d6653c12SJoseph Chen 403f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 404f1c8ecceSJoseph Chen if (voltage < 0) { 405f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 406f1c8ecceSJoseph Chen return -EINVAL; 407f1c8ecceSJoseph Chen } 408f1c8ecceSJoseph Chen 409f1c8ecceSJoseph Chen /* If low power, turn off screen */ 410d6653c12SJoseph Chen if (voltage <= pdata->screen_on_voltage + 50) { 411f1c8ecceSJoseph Chen screen_on = false; 412f1c8ecceSJoseph Chen ever_lowpower_screen_off = true; 413f23c35a8SJoseph Chen charge_show_bmp(NULL); 414f1c8ecceSJoseph Chen } 415f1c8ecceSJoseph Chen 416e7f9facbSJoseph Chen /* Auto wakeup */ 417e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 418e7f9facbSJoseph Chen printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 41979244e4cSJoseph Chen autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 420e7f9facbSJoseph Chen } 421e7f9facbSJoseph Chen 422175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */ 423175257e4SJoseph Chen #ifdef CONFIG_IRQ 424d6653c12SJoseph Chen printf("Enter U-Boot charging mode\n"); 425175257e4SJoseph Chen #else 426175257e4SJoseph Chen printf("Enter U-Boot charging mode(without IRQ)\n"); 427175257e4SJoseph Chen #endif 428f1c8ecceSJoseph Chen 429d6653c12SJoseph Chen charge_start = get_timer(0); 4305e804741SJoseph Chen delta = get_timer(0); 431e7f9facbSJoseph Chen 432f1c8ecceSJoseph Chen /* Charging ! */ 433f1c8ecceSJoseph Chen while (1) { 4345e804741SJoseph Chen /* 4355e804741SJoseph Chen * At the most time, fuel gauge is usually a i2c device, we 4365e804741SJoseph Chen * should avoid read/write all the time. We had better set 4375e804741SJoseph Chen * poll seconds to update fuel gauge info. 4385e804741SJoseph Chen */ 4395e804741SJoseph Chen if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS) 4405e804741SJoseph Chen goto show_images; 4415e804741SJoseph Chen 4425e804741SJoseph Chen delta = get_timer(0); 4435e804741SJoseph Chen 444f1c8ecceSJoseph Chen debug("step1 (%d)... \n", screen_on); 445f1c8ecceSJoseph Chen 446fd62311eSJoseph Chen /* 447fd62311eSJoseph Chen * Most fuel gauge is I2C interface, it shouldn't be interrupted 448fd62311eSJoseph Chen * during tansfer. The power key event depends on interrupt, so 449fd62311eSJoseph Chen * so we should disable local irq when update fuel gauge. 450fd62311eSJoseph Chen */ 451fd62311eSJoseph Chen local_irq_disable(); 452fd62311eSJoseph Chen 453f1c8ecceSJoseph Chen /* Step1: Is charging now ? */ 454f1c8ecceSJoseph Chen charging = fuel_gauge_get_chrg_online(fg); 455f1c8ecceSJoseph Chen if (charging <= 0) { 456f1c8ecceSJoseph Chen printf("Not charging, online=%d. Shutdown...\n", 457f1c8ecceSJoseph Chen charging); 458f1c8ecceSJoseph Chen 459f1c8ecceSJoseph Chen /* wait uart flush before shutdown */ 46079244e4cSJoseph Chen mdelay(5); 461f1c8ecceSJoseph Chen 462f1c8ecceSJoseph Chen /* PMIC shutdown */ 463f1c8ecceSJoseph Chen pmic_shutdown(pmic); 464f1c8ecceSJoseph Chen 465f1c8ecceSJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 466f1c8ecceSJoseph Chen continue; 467f1c8ecceSJoseph Chen } 468f1c8ecceSJoseph Chen 469f1c8ecceSJoseph Chen debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 470f1c8ecceSJoseph Chen 471f1c8ecceSJoseph Chen /* Step2: get soc and voltage */ 472f1c8ecceSJoseph Chen soc = fuel_gauge_get_soc(fg); 473f1c8ecceSJoseph Chen if (soc < 0 || soc > 100) { 474f1c8ecceSJoseph Chen printf("get soc failed: %d\n", soc); 475f1c8ecceSJoseph Chen continue; 476f1c8ecceSJoseph Chen } 477f1c8ecceSJoseph Chen 478f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 479f1c8ecceSJoseph Chen if (voltage < 0) { 480f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 481f1c8ecceSJoseph Chen continue; 482f1c8ecceSJoseph Chen } 483f1c8ecceSJoseph Chen 484d6653c12SJoseph Chen current = fuel_gauge_get_current(fg); 485d6653c12SJoseph Chen if (current == -ENOSYS) { 486d6653c12SJoseph Chen printf("get current failed: %d\n", current); 487d6653c12SJoseph Chen continue; 488d6653c12SJoseph Chen } 4895e804741SJoseph Chen first_poll_fg = 0; 490fd62311eSJoseph Chen 491fd62311eSJoseph Chen local_irq_enable(); 492fd62311eSJoseph Chen 4935e804741SJoseph Chen show_images: 494f1c8ecceSJoseph Chen /* 495f1c8ecceSJoseph Chen * Just for debug, otherwise there will be nothing output which 496f1c8ecceSJoseph Chen * is not good to know what happen. 497f1c8ecceSJoseph Chen */ 498f1c8ecceSJoseph Chen if (!debug_start) 499f1c8ecceSJoseph Chen debug_start = get_timer(0); 500f1c8ecceSJoseph Chen if (get_timer(debug_start) > 20000) { 501f1c8ecceSJoseph Chen debug_start = get_timer(0); 502d6653c12SJoseph Chen printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n", 503d6653c12SJoseph Chen get_timer(0)/1000, soc, voltage, 504d6653c12SJoseph Chen current, charging, screen_on); 505f1c8ecceSJoseph Chen } 506f1c8ecceSJoseph Chen 507f1c8ecceSJoseph Chen /* 508f23c35a8SJoseph Chen * If ever lowpower screen off, force screen_on=false, which 509f1c8ecceSJoseph Chen * means key event can't modify screen_on, only voltage higher 510f1c8ecceSJoseph Chen * then threshold can update screen_on=true; 511f1c8ecceSJoseph Chen */ 512f1c8ecceSJoseph Chen if (ever_lowpower_screen_off) 513f1c8ecceSJoseph Chen screen_on = false; 514f1c8ecceSJoseph Chen 515f1c8ecceSJoseph Chen /* 516f1c8ecceSJoseph Chen * Auto turn on screen when voltage higher than Vol screen on. 517f1c8ecceSJoseph Chen * 'ever_lowpower_screen_off' means enter while loop with 518f1c8ecceSJoseph Chen * screen off. 519f1c8ecceSJoseph Chen */ 520f1c8ecceSJoseph Chen if ((ever_lowpower_screen_off) && 521d6653c12SJoseph Chen (voltage > pdata->screen_on_voltage)) { 522f1c8ecceSJoseph Chen ever_lowpower_screen_off = false; 523f1c8ecceSJoseph Chen screen_on = true; 524f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 525f1c8ecceSJoseph Chen } 526f1c8ecceSJoseph Chen 527f1c8ecceSJoseph Chen /* 528f1c8ecceSJoseph Chen * IMAGE_SHOW_RESET means show_idx show be update by start_idx. 529f1c8ecceSJoseph Chen * When short key pressed event trigged, we will set show_idx 530f1c8ecceSJoseph Chen * as IMAGE_SHOW_RESET which updates images index from start_idx 531f1c8ecceSJoseph Chen * that calculate by current soc. 532f1c8ecceSJoseph Chen */ 533f1c8ecceSJoseph Chen if (show_idx == IMAGE_SHOW_RESET) { 534f1c8ecceSJoseph Chen for (i = 0; i < image_num - 2; i++) { 535f1c8ecceSJoseph Chen /* Find out which image we start to show */ 536f1c8ecceSJoseph Chen if ((soc >= image[i].soc) && 537f1c8ecceSJoseph Chen (soc < image[i + 1].soc)) { 538f1c8ecceSJoseph Chen start_idx = i; 539f1c8ecceSJoseph Chen break; 540f1c8ecceSJoseph Chen } 541f1c8ecceSJoseph Chen 542f1c8ecceSJoseph Chen if (soc >= 100) { 543f1c8ecceSJoseph Chen start_idx = image_num - 2; 544f1c8ecceSJoseph Chen break; 545f1c8ecceSJoseph Chen } 546f1c8ecceSJoseph Chen } 547f1c8ecceSJoseph Chen 548f1c8ecceSJoseph Chen debug("%s: show_idx=%d, screen_on=%d\n", 549f1c8ecceSJoseph Chen __func__, show_idx, screen_on); 550f1c8ecceSJoseph Chen 551f1c8ecceSJoseph Chen /* Mark start index and start time */ 552f1c8ecceSJoseph Chen show_idx = start_idx; 553f1c8ecceSJoseph Chen show_start = get_timer(0); 554f1c8ecceSJoseph Chen } 555f1c8ecceSJoseph Chen 556f1c8ecceSJoseph Chen debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 557f1c8ecceSJoseph Chen 558f1c8ecceSJoseph Chen /* Step3: show images */ 559f1c8ecceSJoseph Chen if (screen_on) { 56093aee2d9SJoseph Chen /* Don't call 'charge_show_bmp' unless image changed */ 56193aee2d9SJoseph Chen if (old_show_idx != show_idx) { 56293aee2d9SJoseph Chen old_show_idx = show_idx; 563f1c8ecceSJoseph Chen debug("SHOW: %s\n", image[show_idx].name); 564f23c35a8SJoseph Chen charge_show_bmp(image[show_idx].name); 56593aee2d9SJoseph Chen } 566e7f9facbSJoseph Chen /* Re calculate timeout to off screen */ 567e7f9facbSJoseph Chen if (priv->auto_screen_off_timeout == 0) 568e7f9facbSJoseph Chen priv->auto_screen_off_timeout = get_timer(0); 569f1c8ecceSJoseph Chen } else { 570e7f9facbSJoseph Chen priv->auto_screen_off_timeout = 0; 571e7f9facbSJoseph Chen 572f23c35a8SJoseph Chen system_suspend_enter(pdata); 573f1c8ecceSJoseph Chen } 574f1c8ecceSJoseph Chen 575f1c8ecceSJoseph Chen mdelay(5); 576f1c8ecceSJoseph Chen 577f1c8ecceSJoseph Chen /* Every image shows period */ 578f1c8ecceSJoseph Chen if (get_timer(show_start) > image[show_idx].period) { 579f1c8ecceSJoseph Chen show_start = get_timer(0); 580f1c8ecceSJoseph Chen /* Update to next image */ 581f1c8ecceSJoseph Chen show_idx++; 582f1c8ecceSJoseph Chen if (show_idx > (image_num - 2)) 583f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 584f1c8ecceSJoseph Chen } 585f1c8ecceSJoseph Chen 586f1c8ecceSJoseph Chen debug("step4 (%d)... \n", screen_on); 587f1c8ecceSJoseph Chen 588f1c8ecceSJoseph Chen /* 589f1c8ecceSJoseph Chen * Step4: check key event. 590f1c8ecceSJoseph Chen * 591f1c8ecceSJoseph Chen * Short key event: turn on/off screen; 592f1c8ecceSJoseph Chen * Long key event: show logo and boot system or still charging. 593f1c8ecceSJoseph Chen */ 594e7f9facbSJoseph Chen key_state = check_key_press(dev); 595b177a917SJoseph Chen if (key_state == KEY_PRESS_DOWN) { 59693aee2d9SJoseph Chen old_show_idx = IMAGE_SHOW_RESET; 59793aee2d9SJoseph Chen 598f1c8ecceSJoseph Chen /* NULL means show nothing, ie. turn off screen */ 599f1c8ecceSJoseph Chen if (screen_on) 600f23c35a8SJoseph Chen charge_show_bmp(NULL); 601f1c8ecceSJoseph Chen 602f1c8ecceSJoseph Chen /* 603f1c8ecceSJoseph Chen * Clear current image index, and show image 604f1c8ecceSJoseph Chen * from start_idx 605f1c8ecceSJoseph Chen */ 606f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 607f1c8ecceSJoseph Chen 608f1c8ecceSJoseph Chen /* 609f23c35a8SJoseph Chen * We turn off screen by charge_show_bmp(NULL), so we 610f1c8ecceSJoseph Chen * should tell while loop to stop show images any more. 611f1c8ecceSJoseph Chen * 612f1c8ecceSJoseph Chen * If screen_on=false, means this short key pressed 613f1c8ecceSJoseph Chen * event turn on the screen and we need show images. 614f1c8ecceSJoseph Chen * 615f1c8ecceSJoseph Chen * If screen_on=true, means this short key pressed 616f1c8ecceSJoseph Chen * event turn off the screen and we never show images. 617f1c8ecceSJoseph Chen */ 618f1c8ecceSJoseph Chen if (screen_on) 619f1c8ecceSJoseph Chen screen_on = false; 620f1c8ecceSJoseph Chen else 621f1c8ecceSJoseph Chen screen_on = true; 622b177a917SJoseph Chen } else if (key_state == KEY_PRESS_LONG_DOWN) { 623f1c8ecceSJoseph Chen /* Only long pressed while screen off needs screen_on true */ 624f1c8ecceSJoseph Chen if (!screen_on) 625f1c8ecceSJoseph Chen screen_on = true; 626f1c8ecceSJoseph Chen 627f1c8ecceSJoseph Chen /* Is able to boot now ? */ 628d6653c12SJoseph Chen if (soc < pdata->exit_charge_level) { 629f1c8ecceSJoseph Chen printf("soc=%d%%, threshold soc=%d%%\n", 630d6653c12SJoseph Chen soc, pdata->exit_charge_level); 631f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 632f1c8ecceSJoseph Chen show_idx = image_num - 1; 633f1c8ecceSJoseph Chen continue; 634f1c8ecceSJoseph Chen } 635f1c8ecceSJoseph Chen 636d6653c12SJoseph Chen if (voltage < pdata->exit_charge_voltage) { 637f1c8ecceSJoseph Chen printf("voltage=%dmv, threshold voltage=%dmv\n", 638d6653c12SJoseph Chen voltage, pdata->exit_charge_voltage); 639f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 640f1c8ecceSJoseph Chen show_idx = image_num - 1; 641f1c8ecceSJoseph Chen continue; 642f1c8ecceSJoseph Chen } 643f1c8ecceSJoseph Chen 644f1c8ecceSJoseph Chen /* Success exit charging */ 645f1c8ecceSJoseph Chen printf("Exit charge animation...\n"); 646f23c35a8SJoseph Chen charge_show_logo(); 647f1c8ecceSJoseph Chen break; 648f1c8ecceSJoseph Chen } else { 649f1c8ecceSJoseph Chen /* Do nothing */ 650f1c8ecceSJoseph Chen } 651f1c8ecceSJoseph Chen 652f1c8ecceSJoseph Chen debug("step5 (%d)... \n", screen_on); 653f1c8ecceSJoseph Chen 654d6653c12SJoseph Chen /* Step5: Exit by ctrl+c */ 6551367bfe3SJoseph Chen if (ctrlc()) { 656d6653c12SJoseph Chen if (voltage >= pdata->screen_on_voltage) 657f23c35a8SJoseph Chen charge_show_logo(); 6581367bfe3SJoseph Chen printf("Exit charge, due to ctrl+c\n"); 6591367bfe3SJoseph Chen break; 6601367bfe3SJoseph Chen } 661f1c8ecceSJoseph Chen } 662f1c8ecceSJoseph Chen 663e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) 664e7f9facbSJoseph Chen autowakeup_timer_uninit(); 665e7f9facbSJoseph Chen 666f1c8ecceSJoseph Chen ms = get_timer(charge_start); 667f1c8ecceSJoseph Chen if (ms >= 1000) { 668f1c8ecceSJoseph Chen sec = ms / 1000; 669f1c8ecceSJoseph Chen ms = ms % 1000; 670f1c8ecceSJoseph Chen } 671f1c8ecceSJoseph Chen 672f1c8ecceSJoseph Chen printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 673f1c8ecceSJoseph Chen sec, ms, soc, voltage); 674f1c8ecceSJoseph Chen 675f1c8ecceSJoseph Chen return 0; 676f1c8ecceSJoseph Chen } 677f1c8ecceSJoseph Chen 678f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = { 679f1c8ecceSJoseph Chen .show = charge_animation_show, 680f1c8ecceSJoseph Chen }; 681f1c8ecceSJoseph Chen 682f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev) 683f1c8ecceSJoseph Chen { 684f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 685*8b436ce5SElaine Zhang struct udevice *fg, *pmic, *rtc; 686f23c35a8SJoseph Chen int ret, soc; 687f1c8ecceSJoseph Chen 688f23c35a8SJoseph Chen /* Get PMIC: used for power off system */ 689f1c8ecceSJoseph Chen ret = uclass_get_device(UCLASS_PMIC, 0, &pmic); 690f1c8ecceSJoseph Chen if (ret) { 691cf49f6adSJoseph Chen if (ret == -ENODEV) 692cf49f6adSJoseph Chen printf("Can't find PMIC\n"); 693cf49f6adSJoseph Chen else 694f1c8ecceSJoseph Chen printf("Get UCLASS PMIC failed: %d\n", ret); 695f1c8ecceSJoseph Chen return ret; 696f1c8ecceSJoseph Chen } 697f1c8ecceSJoseph Chen priv->pmic = pmic; 698f1c8ecceSJoseph Chen 699f23c35a8SJoseph Chen /* Get fuel gauge: used for charging */ 700f1c8ecceSJoseph Chen ret = uclass_get_device(UCLASS_FG, 0, &fg); 701f1c8ecceSJoseph Chen if (ret) { 702cf49f6adSJoseph Chen if (ret == -ENODEV) 70331ab5b3bSJoseph Chen debug("Can't find FG\n"); 704cf49f6adSJoseph Chen else 70531ab5b3bSJoseph Chen debug("Get UCLASS FG failed: %d\n", ret); 706f1c8ecceSJoseph Chen return ret; 707f1c8ecceSJoseph Chen } 708f1c8ecceSJoseph Chen priv->fg = fg; 709f1c8ecceSJoseph Chen 710*8b436ce5SElaine Zhang /* Get rtc: used for power on */ 711*8b436ce5SElaine Zhang ret = uclass_get_device(UCLASS_RTC, 0, &rtc); 712*8b436ce5SElaine Zhang if (ret) { 713*8b436ce5SElaine Zhang if (ret == -ENODEV) 714*8b436ce5SElaine Zhang debug("Can't find RTC\n"); 715*8b436ce5SElaine Zhang else 716*8b436ce5SElaine Zhang debug("Get UCLASS RTC failed: %d\n", ret); 717*8b436ce5SElaine Zhang } 718*8b436ce5SElaine Zhang priv->rtc = rtc; 719*8b436ce5SElaine Zhang 72079244e4cSJoseph Chen /* Get PWRKEY: used for wakeup and turn off/on LCD */ 72131ab5b3bSJoseph Chen if (key_read(KEY_POWER) == KEY_NOT_EXIST) { 72231ab5b3bSJoseph Chen debug("Can't find power key\n"); 723f23c35a8SJoseph Chen return -EINVAL; 724f23c35a8SJoseph Chen } 725f23c35a8SJoseph Chen 726f23c35a8SJoseph Chen /* Initialize charge current */ 727f23c35a8SJoseph Chen soc = fuel_gauge_get_soc(fg); 728f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 72931ab5b3bSJoseph Chen debug("get soc failed: %d\n", soc); 730f23c35a8SJoseph Chen return -EINVAL; 731f23c35a8SJoseph Chen } 732f23c35a8SJoseph Chen 733f23c35a8SJoseph Chen /* Get charge images */ 734f1c8ecceSJoseph Chen priv->image = image; 735f1c8ecceSJoseph Chen priv->image_num = ARRAY_SIZE(image); 736f1c8ecceSJoseph Chen 737f1c8ecceSJoseph Chen printf("Enable charge animation display\n"); 738f1c8ecceSJoseph Chen 739f1c8ecceSJoseph Chen return 0; 740f1c8ecceSJoseph Chen } 741f1c8ecceSJoseph Chen 742f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = { 743d6653c12SJoseph Chen { .compatible = "rockchip,uboot-charge" }, 744f1c8ecceSJoseph Chen { }, 745f1c8ecceSJoseph Chen }; 746f1c8ecceSJoseph Chen 747f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = { 748f1c8ecceSJoseph Chen .name = "charge-animation", 749f1c8ecceSJoseph Chen .id = UCLASS_CHARGE_DISPLAY, 750f1c8ecceSJoseph Chen .probe = charge_animation_probe, 751f1c8ecceSJoseph Chen .of_match = charge_animation_ids, 752f1c8ecceSJoseph Chen .ops = &charge_animation_ops, 753f1c8ecceSJoseph Chen .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 754f1c8ecceSJoseph Chen .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 755f1c8ecceSJoseph Chen .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 756f1c8ecceSJoseph Chen }; 757