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> 141b3009deSJoseph Chen #include <led.h> 158b436ce5SElaine Zhang #include <rtc.h> 16f1c8ecceSJoseph Chen #include <pwm.h> 17d3ff9cf9SKever Yang #include <asm/arch/rockchip_smccc.h> 18d3ff9cf9SKever Yang #include <asm/suspend.h> 19d3ff9cf9SKever Yang #include <linux/input.h> 20f1c8ecceSJoseph Chen #include <power/charge_display.h> 21ac1dc0c3SJoseph Chen #include <power/charge_animation.h> 2288949342SJoseph Chen #include <power/rockchip_pm.h> 23f1c8ecceSJoseph Chen #include <power/fuel_gauge.h> 24f1c8ecceSJoseph Chen #include <power/pmic.h> 25f1c8ecceSJoseph Chen #include <power/rk8xx_pmic.h> 26f1c8ecceSJoseph Chen #include <power/regulator.h> 27f1c8ecceSJoseph Chen #include <video_rockchip.h> 28175257e4SJoseph Chen #ifdef CONFIG_IRQ 29175257e4SJoseph Chen #include <irq-generic.h> 30175257e4SJoseph Chen #include <rk_timer_irq.h> 31175257e4SJoseph Chen #endif 32f1c8ecceSJoseph Chen 33f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR; 34f1c8ecceSJoseph Chen 35ebe3d004SJoseph Chen #define IMAGE_RESET_IDX -1 36ebe3d004SJoseph Chen #define IMAGE_SOC_100_IDX(n) ((n) - 2) 37ebe3d004SJoseph Chen #define IMAGE_LOWPOWER_IDX(n) ((n) - 1) 382e68f6b5SJoseph Chen #define SYSTEM_SUSPEND_DELAY_MS 5000 395e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS 1000 40f1c8ecceSJoseph Chen 411b3009deSJoseph Chen #define LED_CHARGING_NAME "battery_charging" 421b3009deSJoseph Chen #define LED_CHARGING_FULL_NAME "battery_full" 431b3009deSJoseph Chen 44f1c8ecceSJoseph Chen struct charge_image { 45f1c8ecceSJoseph Chen const char *name; 46f1c8ecceSJoseph Chen int soc; 47f1c8ecceSJoseph Chen int period; /* ms */ 48f1c8ecceSJoseph Chen }; 49f1c8ecceSJoseph Chen 50f1c8ecceSJoseph Chen struct charge_animation_priv { 51f1c8ecceSJoseph Chen struct udevice *pmic; 52f1c8ecceSJoseph Chen struct udevice *fg; 534d083e3fSJoseph Chen struct udevice *charger; 548b436ce5SElaine Zhang struct udevice *rtc; 551b3009deSJoseph Chen #ifdef CONFIG_LED 561b3009deSJoseph Chen struct udevice *led_charging; 571b3009deSJoseph Chen struct udevice *led_full; 581b3009deSJoseph Chen #endif 59f1c8ecceSJoseph Chen const struct charge_image *image; 60f1c8ecceSJoseph Chen int image_num; 61e7f9facbSJoseph Chen 62e7f9facbSJoseph Chen int auto_wakeup_key_state; 632e68f6b5SJoseph Chen ulong auto_screen_off_timeout; /* ms */ 642e68f6b5SJoseph Chen ulong suspend_delay_timeout; /* ms */ 65f1c8ecceSJoseph Chen }; 66f1c8ecceSJoseph Chen 67f1c8ecceSJoseph Chen /* 68f1c8ecceSJoseph Chen * IF you want to use your own charge images, please: 69f1c8ecceSJoseph Chen * 70f1c8ecceSJoseph Chen * 1. Update the following 'image[]' to point to your own images; 71f1c8ecceSJoseph Chen * 2. You must set the failed image as last one and soc = -1 !!! 72f1c8ecceSJoseph Chen */ 73f1c8ecceSJoseph Chen static const struct charge_image image[] = { 74f1c8ecceSJoseph Chen { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 75f1c8ecceSJoseph Chen { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 76f1c8ecceSJoseph Chen { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 77f1c8ecceSJoseph Chen { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 78f1c8ecceSJoseph Chen { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 79f1c8ecceSJoseph Chen { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 80f1c8ecceSJoseph Chen { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 81f1c8ecceSJoseph Chen }; 82f1c8ecceSJoseph Chen 83f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev) 84f1c8ecceSJoseph Chen { 85f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 86f1c8ecceSJoseph Chen 87d6653c12SJoseph Chen /* charge mode */ 88d6653c12SJoseph Chen pdata->uboot_charge = 89d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 90d6653c12SJoseph Chen pdata->android_charge = 91d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 92f1c8ecceSJoseph Chen 93d6653c12SJoseph Chen pdata->exit_charge_level = 94d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 95d6653c12SJoseph Chen pdata->exit_charge_voltage = 96d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 97f23c35a8SJoseph Chen 98f23c35a8SJoseph Chen pdata->low_power_voltage = 99f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 100f23c35a8SJoseph Chen 101d6653c12SJoseph Chen pdata->screen_on_voltage = 102d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 103f23c35a8SJoseph Chen pdata->system_suspend = 104f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,system-suspend", 0); 105f1c8ecceSJoseph Chen 106e7f9facbSJoseph Chen pdata->auto_wakeup_interval = 107e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 108e7f9facbSJoseph Chen pdata->auto_wakeup_screen_invert = 109e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 110e7f9facbSJoseph Chen 111e7f9facbSJoseph Chen pdata->auto_off_screen_interval = 112e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 113e7f9facbSJoseph Chen 114f23c35a8SJoseph Chen if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 115f23c35a8SJoseph Chen pdata->screen_on_voltage = pdata->exit_charge_voltage; 116f1c8ecceSJoseph Chen 117d6653c12SJoseph Chen debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 118f23c35a8SJoseph Chen "lp_voltage=%d%%, screen_on=%dmv\n", 119d6653c12SJoseph Chen pdata->uboot_charge, pdata->android_charge, 120d6653c12SJoseph Chen pdata->exit_charge_level, pdata->exit_charge_voltage, 121f23c35a8SJoseph Chen pdata->low_power_voltage, pdata->screen_on_voltage); 122f1c8ecceSJoseph Chen 123f1c8ecceSJoseph Chen return 0; 124f1c8ecceSJoseph Chen } 125f1c8ecceSJoseph Chen 126e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev) 127f1c8ecceSJoseph Chen { 128e7f9facbSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 129e7f9facbSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 1308b436ce5SElaine Zhang u32 state, rtc_state = 0; 1318b436ce5SElaine Zhang 1328b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC 1338b436ce5SElaine Zhang if (priv->rtc) 1348b436ce5SElaine Zhang rtc_state = rtc_alarm_trigger(priv->rtc); 1358b436ce5SElaine Zhang #endif 1368b436ce5SElaine Zhang if (rtc_state) { 1378b436ce5SElaine Zhang printf("rtc alarm trigger...\n"); 1388b436ce5SElaine Zhang return KEY_PRESS_LONG_DOWN; 1398b436ce5SElaine Zhang } 140f1c8ecceSJoseph Chen 141787a62ebSJoseph Chen state = key_read(KEY_POWER); 142b177a917SJoseph Chen if (state < 0) 143f1c8ecceSJoseph Chen printf("read power key failed: %d\n", state); 14493aee2d9SJoseph Chen else if (state == KEY_PRESS_DOWN) 14593aee2d9SJoseph Chen printf("power key pressed...\n"); 14693aee2d9SJoseph Chen else if (state == KEY_PRESS_LONG_DOWN) 14793aee2d9SJoseph Chen printf("power key long pressed...\n"); 148f1c8ecceSJoseph Chen 149e7f9facbSJoseph Chen /* Fixup key state for following cases */ 150e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 151e7f9facbSJoseph Chen if (pdata->auto_wakeup_screen_invert) { 152e7f9facbSJoseph Chen if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 153e7f9facbSJoseph Chen /* Value is updated in timer interrupt */ 154e7f9facbSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_NONE; 155e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 156e7f9facbSJoseph Chen } 157e7f9facbSJoseph Chen } 158e7f9facbSJoseph Chen } else if (pdata->auto_off_screen_interval) { 15993aee2d9SJoseph Chen if (priv->auto_screen_off_timeout && 16093aee2d9SJoseph Chen get_timer(priv->auto_screen_off_timeout) > 161e7f9facbSJoseph Chen pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 162e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 163e7f9facbSJoseph Chen printf("Auto screen off\n"); 164e7f9facbSJoseph Chen } 165e7f9facbSJoseph Chen } 166e7f9facbSJoseph Chen 167b177a917SJoseph Chen return state; 168f1c8ecceSJoseph Chen } 169f1c8ecceSJoseph Chen 170175257e4SJoseph Chen /* 171175257e4SJoseph Chen * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup 172175257e4SJoseph Chen * period timer is useless. 173175257e4SJoseph Chen */ 1743415d4ebSJoseph Chen #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND) 1752e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev) 176175257e4SJoseph Chen { 177175257e4SJoseph Chen return 0; 178175257e4SJoseph Chen } 179175257e4SJoseph Chen 180175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {} 181175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {} 182175257e4SJoseph Chen 183175257e4SJoseph Chen #else 1842e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev) 185b177a917SJoseph Chen { 1862e68f6b5SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 1872e68f6b5SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 1882e68f6b5SJoseph Chen 1892e68f6b5SJoseph Chen /* 1902e68f6b5SJoseph Chen * When cpu is in wfi and we try to give a long key press event without 1912e68f6b5SJoseph Chen * key release, cpu would wakeup and enter wfi again immediately. So 1922e68f6b5SJoseph Chen * here is the problem: cpu can only wakeup when long key released. 1932e68f6b5SJoseph Chen * 1942e68f6b5SJoseph Chen * Actually, we want cpu can detect long key event without key release, 1952e68f6b5SJoseph Chen * so we give a suspend delay timeout for cpu to detect this. 1962e68f6b5SJoseph Chen */ 1972e68f6b5SJoseph Chen if (priv->suspend_delay_timeout && 1982e68f6b5SJoseph Chen get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS) 1992e68f6b5SJoseph Chen return 0; 2002e68f6b5SJoseph Chen 201f23c35a8SJoseph Chen if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 202b177a917SJoseph Chen printf("\nSystem suspend: "); 203992f4e77SJoseph Chen putc('0'); 204992f4e77SJoseph Chen regulators_enable_state_mem(false); 205b177a917SJoseph Chen putc('1'); 206b177a917SJoseph Chen local_irq_disable(); 207b177a917SJoseph Chen putc('2'); 208b177a917SJoseph Chen irqs_suspend(); 209b177a917SJoseph Chen putc('3'); 21088949342SJoseph Chen device_suspend(); 21188949342SJoseph Chen putc('4'); 212b177a917SJoseph Chen putc('\n'); 213b177a917SJoseph Chen 214b177a917SJoseph Chen /* Trap into ATF for low power mode */ 215b177a917SJoseph Chen cpu_suspend(0, psci_system_suspend); 216b177a917SJoseph Chen 217b177a917SJoseph Chen putc('\n'); 21888949342SJoseph Chen putc('4'); 21988949342SJoseph Chen device_resume(); 220b177a917SJoseph Chen putc('3'); 221b177a917SJoseph Chen irqs_resume(); 222b177a917SJoseph Chen putc('2'); 223b177a917SJoseph Chen local_irq_enable(); 224b177a917SJoseph Chen putc('1'); 225b177a917SJoseph Chen putc('\n'); 2268fc5ae06SJoseph Chen } else { 227*5eac14dbSJoseph Chen irqs_suspend(); 2288fc5ae06SJoseph Chen printf("\nWfi\n"); 2298fc5ae06SJoseph Chen wfi(); 2302e68f6b5SJoseph Chen putc('1'); 231*5eac14dbSJoseph Chen irqs_resume(); 2328fc5ae06SJoseph Chen } 233b177a917SJoseph Chen 2342e68f6b5SJoseph Chen priv->suspend_delay_timeout = get_timer(0); 2352e68f6b5SJoseph Chen 236b177a917SJoseph Chen /* 237b177a917SJoseph Chen * We must wait for key release event finish, otherwise 238b177a917SJoseph Chen * we may read key state too early. 239b177a917SJoseph Chen */ 240b177a917SJoseph Chen mdelay(300); 241b177a917SJoseph Chen 242b177a917SJoseph Chen return 0; 243f1c8ecceSJoseph Chen } 244ebe3d004SJoseph Chen 24579244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data) 24679244e4cSJoseph Chen { 24779244e4cSJoseph Chen struct udevice *dev = data; 24879244e4cSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 24979244e4cSJoseph Chen static long long count; 25079244e4cSJoseph Chen 25179244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 25279244e4cSJoseph Chen 25379244e4cSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 25479244e4cSJoseph Chen printf("auto wakeup count: %lld\n", ++count); 25579244e4cSJoseph Chen } 25679244e4cSJoseph Chen 25779244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 25879244e4cSJoseph Chen { 25979244e4cSJoseph Chen uint64_t period = 24000000ULL * seconds; 26079244e4cSJoseph Chen 26179244e4cSJoseph Chen /* Disable before conifg */ 26279244e4cSJoseph Chen writel(0, TIMER_BASE + TIMER_CTRL); 26379244e4cSJoseph Chen 26479244e4cSJoseph Chen /* Config */ 26579244e4cSJoseph Chen writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 26679244e4cSJoseph Chen writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 26779244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 26879244e4cSJoseph Chen writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 26979244e4cSJoseph Chen 27079244e4cSJoseph Chen /* IRQ */ 27179244e4cSJoseph Chen irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 27279244e4cSJoseph Chen irq_handler_enable(TIMER_IRQ); 27379244e4cSJoseph Chen } 27479244e4cSJoseph Chen 27579244e4cSJoseph Chen static void autowakeup_timer_uninit(void) 27679244e4cSJoseph Chen { 27779244e4cSJoseph Chen irq_free_handler(TIMER_IRQ); 27879244e4cSJoseph Chen } 279175257e4SJoseph Chen #endif 28079244e4cSJoseph Chen 281f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP 282f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) 283f23c35a8SJoseph Chen { 284f23c35a8SJoseph Chen rockchip_show_bmp(name); 285f23c35a8SJoseph Chen } 286f23c35a8SJoseph Chen 287f23c35a8SJoseph Chen static void charge_show_logo(void) 288f23c35a8SJoseph Chen { 289f23c35a8SJoseph Chen rockchip_show_logo(); 290f23c35a8SJoseph Chen } 291f23c35a8SJoseph Chen #else 292f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {} 293f23c35a8SJoseph Chen static void charge_show_logo(void) {} 294f23c35a8SJoseph Chen #endif 295f23c35a8SJoseph Chen 2961b3009deSJoseph Chen #ifdef CONFIG_LED 2971b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) 2981b3009deSJoseph Chen { 2991b3009deSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 3001b3009deSJoseph Chen static int old_soc = -1; 3011b3009deSJoseph Chen int ret, ledst; 3021b3009deSJoseph Chen 3031b3009deSJoseph Chen if (old_soc == soc) 3041b3009deSJoseph Chen return 0; 3051b3009deSJoseph Chen 3061b3009deSJoseph Chen old_soc = soc; 3071b3009deSJoseph Chen if (priv->led_charging) { 3081b3009deSJoseph Chen ledst = (soc < 100) ? LEDST_ON : LEDST_OFF; 3091b3009deSJoseph Chen ret = led_set_state(priv->led_charging, ledst); 3101b3009deSJoseph Chen if (ret) { 3111b3009deSJoseph Chen printf("set charging led %s failed, ret=%d\n", 3121b3009deSJoseph Chen (ledst == LEDST_ON) ? "ON" : "OFF", ret); 3131b3009deSJoseph Chen return ret; 3141b3009deSJoseph Chen } 3151b3009deSJoseph Chen } 3161b3009deSJoseph Chen 3171b3009deSJoseph Chen if (priv->led_full) { 3181b3009deSJoseph Chen ledst = (soc == 100) ? LEDST_ON : LEDST_OFF; 3191b3009deSJoseph Chen ret = led_set_state(priv->led_full, ledst); 3201b3009deSJoseph Chen if (ret) { 3211b3009deSJoseph Chen printf("set charging full led %s failed, ret=%d\n", 3221b3009deSJoseph Chen ledst == LEDST_ON ? "ON" : "OFF", ret); 3231b3009deSJoseph Chen return ret; 3241b3009deSJoseph Chen } 3251b3009deSJoseph Chen } 3261b3009deSJoseph Chen 3271b3009deSJoseph Chen return 0; 3281b3009deSJoseph Chen } 3291b3009deSJoseph Chen #else 3301b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) { return 0; } 3311b3009deSJoseph Chen #endif 3321b3009deSJoseph Chen 3334d083e3fSJoseph Chen static int fg_charger_get_chrg_online(struct udevice *dev) 3344d083e3fSJoseph Chen { 3354d083e3fSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 3364d083e3fSJoseph Chen struct udevice *charger; 3374d083e3fSJoseph Chen 3384d083e3fSJoseph Chen charger = priv->charger ? : priv->fg; 3394d083e3fSJoseph Chen 3404d083e3fSJoseph Chen return fuel_gauge_get_chrg_online(charger); 3414d083e3fSJoseph Chen } 3424d083e3fSJoseph Chen 343f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev) 344f23c35a8SJoseph Chen { 345f23c35a8SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 346f23c35a8SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 347f23c35a8SJoseph Chen struct udevice *pmic = priv->pmic; 348f23c35a8SJoseph Chen struct udevice *fg = priv->fg; 349f23c35a8SJoseph Chen int voltage, soc, charging = 1; 35079244e4cSJoseph Chen static int timer_initialized; 3511b3009deSJoseph Chen int ret; 352f23c35a8SJoseph Chen 353f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 354f23c35a8SJoseph Chen if (voltage < 0) 355f23c35a8SJoseph Chen return -EINVAL; 356f23c35a8SJoseph Chen 357f23c35a8SJoseph Chen while (voltage < pdata->low_power_voltage + 50) { 358f23c35a8SJoseph Chen /* Check charger online */ 3594d083e3fSJoseph Chen charging = fg_charger_get_chrg_online(dev); 360f23c35a8SJoseph Chen if (charging <= 0) { 36179244e4cSJoseph Chen printf("%s: Not charging, online=%d. Shutdown...\n", 36279244e4cSJoseph Chen __func__, charging); 363f23c35a8SJoseph Chen /* wait uart flush before shutdown */ 36479244e4cSJoseph Chen mdelay(5); 365f23c35a8SJoseph Chen /* PMIC shutdown */ 366f23c35a8SJoseph Chen pmic_shutdown(pmic); 367f23c35a8SJoseph Chen 368f23c35a8SJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 369f23c35a8SJoseph Chen continue; 370f23c35a8SJoseph Chen } 371f23c35a8SJoseph Chen 37279244e4cSJoseph Chen /* Enable auto wakeup */ 37379244e4cSJoseph Chen if (!timer_initialized) { 37479244e4cSJoseph Chen timer_initialized = 1; 37579244e4cSJoseph Chen autowakeup_timer_init(dev, 5); 37679244e4cSJoseph Chen } 37779244e4cSJoseph Chen 378f23c35a8SJoseph Chen /* 379f23c35a8SJoseph Chen * Just for fuel gauge to update something important, 380f23c35a8SJoseph Chen * including charge current, coulometer or other. 381f23c35a8SJoseph Chen */ 3824f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(fg); 383f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 384f23c35a8SJoseph Chen printf("get soc failed: %d\n", soc); 385f23c35a8SJoseph Chen continue; 386f23c35a8SJoseph Chen } 387f23c35a8SJoseph Chen 3881b3009deSJoseph Chen /* Update led */ 3891b3009deSJoseph Chen ret = leds_update(dev, soc); 3901b3009deSJoseph Chen if (ret) 3911b3009deSJoseph Chen printf("update led failed: %d\n", ret); 3921b3009deSJoseph Chen 393f23c35a8SJoseph Chen printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 394f23c35a8SJoseph Chen pdata->low_power_voltage, voltage); 395f23c35a8SJoseph Chen 396f23c35a8SJoseph Chen /* System suspend */ 3972e68f6b5SJoseph Chen system_suspend_enter(dev); 398f23c35a8SJoseph Chen 399f23c35a8SJoseph Chen /* Update voltage */ 400f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 401f23c35a8SJoseph Chen if (voltage < 0) { 402f23c35a8SJoseph Chen printf("get voltage failed: %d\n", voltage); 403f23c35a8SJoseph Chen continue; 404f23c35a8SJoseph Chen } 4055fc2a70cSJoseph Chen 4065fc2a70cSJoseph Chen if (ctrlc()) { 4075fc2a70cSJoseph Chen printf("Extrem low charge: exit by ctrl+c\n"); 4085fc2a70cSJoseph Chen break; 4095fc2a70cSJoseph Chen } 410f23c35a8SJoseph Chen } 411f23c35a8SJoseph Chen 41279244e4cSJoseph Chen autowakeup_timer_uninit(); 41379244e4cSJoseph Chen 414f23c35a8SJoseph Chen return 0; 415f23c35a8SJoseph Chen } 416f23c35a8SJoseph Chen 417f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev) 418f1c8ecceSJoseph Chen { 419f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 420f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 421f1c8ecceSJoseph Chen const struct charge_image *image = priv->image; 422f1c8ecceSJoseph Chen struct udevice *pmic = priv->pmic; 423f1c8ecceSJoseph Chen struct udevice *fg = priv->fg; 424a8b9d026SJoseph Chen const char *preboot = env_get("preboot"); 425f1c8ecceSJoseph Chen int image_num = priv->image_num; 426f1c8ecceSJoseph Chen bool ever_lowpower_screen_off = false; 427f1c8ecceSJoseph Chen bool screen_on = true; 428f1c8ecceSJoseph Chen ulong show_start = 0, charge_start = 0, debug_start = 0; 4295e804741SJoseph Chen ulong delta; 430f1c8ecceSJoseph Chen ulong ms = 0, sec = 0; 431ebe3d004SJoseph Chen int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RESET_IDX; 432d6653c12SJoseph Chen int soc, voltage, current, key_state; 433f23c35a8SJoseph Chen int i, charging = 1, ret; 4348f9ff705SJoseph Chen int boot_mode; 4355e804741SJoseph Chen int first_poll_fg = 1; 436f1c8ecceSJoseph Chen 437f23c35a8SJoseph Chen /* 438f23c35a8SJoseph Chen * Check sequence: 439f23c35a8SJoseph Chen * 440f23c35a8SJoseph Chen * 1. Extrem low power charge? 441f23c35a8SJoseph Chen * 2. Preboot cmd? 442f23c35a8SJoseph Chen * 3. Valid boot mode? 443f23c35a8SJoseph Chen * 4. U-Boot charge enabled by dts config? 444f23c35a8SJoseph Chen * 5. Screen off before charge? 445f23c35a8SJoseph Chen * 6. Enter charge ! 446f23c35a8SJoseph Chen * 447f23c35a8SJoseph Chen */ 44861a7a6d6SJoseph Chen if (!fuel_gauge_bat_is_exist(fg)) { 44961a7a6d6SJoseph Chen printf("Exit charge: battery is not exist\n"); 45061a7a6d6SJoseph Chen return 0; 45161a7a6d6SJoseph Chen } 45261a7a6d6SJoseph Chen 453f23c35a8SJoseph Chen /* Extrem low power charge */ 454f23c35a8SJoseph Chen ret = charge_extrem_low_power(dev); 455f23c35a8SJoseph Chen if (ret < 0) { 456f23c35a8SJoseph Chen printf("extrem low power charge failed, ret=%d\n", ret); 457f23c35a8SJoseph Chen return ret; 458f23c35a8SJoseph Chen } 459f23c35a8SJoseph Chen 460a8b9d026SJoseph Chen /* If there is preboot command, exit */ 46161a7a6d6SJoseph Chen if (preboot && !strstr(preboot, "dvfs")) { 4627ae45834SJoseph Chen printf("Exit charge: due to preboot cmd '%s'\n", preboot); 463a8b9d026SJoseph Chen return 0; 464a8b9d026SJoseph Chen } 465a8b9d026SJoseph Chen 466f23c35a8SJoseph Chen /* Not valid charge mode, exit */ 467d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 4688f9ff705SJoseph Chen boot_mode = rockchip_get_boot_mode(); 469221b5220SJoseph Chen if ((boot_mode != BOOT_MODE_CHARGING) && 470221b5220SJoseph Chen (boot_mode != BOOT_MODE_UNDEFINE)) { 4717ae45834SJoseph Chen printf("Exit charge: due to boot mode\n"); 4728f9ff705SJoseph Chen return 0; 4738f9ff705SJoseph Chen } 4748f9ff705SJoseph Chen #endif 475221b5220SJoseph Chen 476616056c9Sshengfei Xu /* Not charger online, exit */ 4774d083e3fSJoseph Chen charging = fg_charger_get_chrg_online(dev); 478cf49f6adSJoseph Chen if (charging <= 0) { 4797ae45834SJoseph Chen printf("Exit charge: due to charger offline\n"); 480616056c9Sshengfei Xu return 0; 481cf49f6adSJoseph Chen } 482616056c9Sshengfei Xu 483f23c35a8SJoseph Chen /* Enter android charge, set property for kernel */ 484d6653c12SJoseph Chen if (pdata->android_charge) { 485d6653c12SJoseph Chen env_update("bootargs", "androidboot.mode=charger"); 486d6653c12SJoseph Chen printf("Android charge mode\n"); 487d6653c12SJoseph Chen } 488d6653c12SJoseph Chen 489f23c35a8SJoseph Chen /* Not enable U-Boot charge, exit */ 490cf49f6adSJoseph Chen if (!pdata->uboot_charge) { 4917ae45834SJoseph Chen printf("Exit charge: due to not enable uboot charge\n"); 492d6653c12SJoseph Chen return 0; 493cf49f6adSJoseph Chen } 494d6653c12SJoseph Chen 495f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 496f1c8ecceSJoseph Chen if (voltage < 0) { 497f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 498f1c8ecceSJoseph Chen return -EINVAL; 499f1c8ecceSJoseph Chen } 500f1c8ecceSJoseph Chen 501f1c8ecceSJoseph Chen /* If low power, turn off screen */ 502d6653c12SJoseph Chen if (voltage <= pdata->screen_on_voltage + 50) { 503f1c8ecceSJoseph Chen screen_on = false; 504f1c8ecceSJoseph Chen ever_lowpower_screen_off = true; 505f23c35a8SJoseph Chen charge_show_bmp(NULL); 506f1c8ecceSJoseph Chen } 507f1c8ecceSJoseph Chen 508e7f9facbSJoseph Chen /* Auto wakeup */ 509e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 510e7f9facbSJoseph Chen printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 51179244e4cSJoseph Chen autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 512e7f9facbSJoseph Chen } 513e7f9facbSJoseph Chen 514175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */ 515175257e4SJoseph Chen #ifdef CONFIG_IRQ 516d6653c12SJoseph Chen printf("Enter U-Boot charging mode\n"); 517175257e4SJoseph Chen #else 518175257e4SJoseph Chen printf("Enter U-Boot charging mode(without IRQ)\n"); 519175257e4SJoseph Chen #endif 520f1c8ecceSJoseph Chen 521d6653c12SJoseph Chen charge_start = get_timer(0); 5225e804741SJoseph Chen delta = get_timer(0); 523e7f9facbSJoseph Chen 524f1c8ecceSJoseph Chen /* Charging ! */ 525f1c8ecceSJoseph Chen while (1) { 5265e804741SJoseph Chen /* 5275e804741SJoseph Chen * At the most time, fuel gauge is usually a i2c device, we 5285e804741SJoseph Chen * should avoid read/write all the time. We had better set 5295e804741SJoseph Chen * poll seconds to update fuel gauge info. 5305e804741SJoseph Chen */ 5315e804741SJoseph Chen if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS) 5325e804741SJoseph Chen goto show_images; 5335e804741SJoseph Chen 5345e804741SJoseph Chen delta = get_timer(0); 5355e804741SJoseph Chen 536f1c8ecceSJoseph Chen debug("step1 (%d)... \n", screen_on); 537f1c8ecceSJoseph Chen 538fd62311eSJoseph Chen /* 539fd62311eSJoseph Chen * Most fuel gauge is I2C interface, it shouldn't be interrupted 540ebe3d004SJoseph Chen * during transfer. The power key event depends on interrupt, so 541ebe3d004SJoseph Chen * we should disable local irq when update fuel gauge. 542fd62311eSJoseph Chen */ 543fd62311eSJoseph Chen local_irq_disable(); 544fd62311eSJoseph Chen 545f1c8ecceSJoseph Chen /* Step1: Is charging now ? */ 5464d083e3fSJoseph Chen charging = fg_charger_get_chrg_online(dev); 547f1c8ecceSJoseph Chen if (charging <= 0) { 548f1c8ecceSJoseph Chen printf("Not charging, online=%d. Shutdown...\n", 549f1c8ecceSJoseph Chen charging); 550f1c8ecceSJoseph Chen 551f1c8ecceSJoseph Chen /* wait uart flush before shutdown */ 55279244e4cSJoseph Chen mdelay(5); 553f1c8ecceSJoseph Chen 554f1c8ecceSJoseph Chen /* PMIC shutdown */ 555f1c8ecceSJoseph Chen pmic_shutdown(pmic); 556f1c8ecceSJoseph Chen 557f1c8ecceSJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 558f1c8ecceSJoseph Chen continue; 559f1c8ecceSJoseph Chen } 560f1c8ecceSJoseph Chen 561f1c8ecceSJoseph Chen debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 562f1c8ecceSJoseph Chen 563f1c8ecceSJoseph Chen /* Step2: get soc and voltage */ 5644f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(fg); 565f1c8ecceSJoseph Chen if (soc < 0 || soc > 100) { 566f1c8ecceSJoseph Chen printf("get soc failed: %d\n", soc); 567f1c8ecceSJoseph Chen continue; 568f1c8ecceSJoseph Chen } 569f1c8ecceSJoseph Chen 570f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 571f1c8ecceSJoseph Chen if (voltage < 0) { 572f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 573f1c8ecceSJoseph Chen continue; 574f1c8ecceSJoseph Chen } 575f1c8ecceSJoseph Chen 576d6653c12SJoseph Chen current = fuel_gauge_get_current(fg); 577d6653c12SJoseph Chen if (current == -ENOSYS) { 578d6653c12SJoseph Chen printf("get current failed: %d\n", current); 579d6653c12SJoseph Chen continue; 580d6653c12SJoseph Chen } 581fd62311eSJoseph Chen 582ebe3d004SJoseph Chen first_poll_fg = 0; 583fd62311eSJoseph Chen local_irq_enable(); 584fd62311eSJoseph Chen 5855e804741SJoseph Chen show_images: 586f1c8ecceSJoseph Chen /* 587f1c8ecceSJoseph Chen * Just for debug, otherwise there will be nothing output which 588f1c8ecceSJoseph Chen * is not good to know what happen. 589f1c8ecceSJoseph Chen */ 590f1c8ecceSJoseph Chen if (!debug_start) 591f1c8ecceSJoseph Chen debug_start = get_timer(0); 592f1c8ecceSJoseph Chen if (get_timer(debug_start) > 20000) { 593f1c8ecceSJoseph Chen debug_start = get_timer(0); 594ebe3d004SJoseph Chen printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, " 595ebe3d004SJoseph Chen "online=%d, screen_on=%d\n", 596d6653c12SJoseph Chen get_timer(0)/1000, soc, voltage, 597d6653c12SJoseph Chen current, charging, screen_on); 598f1c8ecceSJoseph Chen } 599f1c8ecceSJoseph Chen 6001b3009deSJoseph Chen /* Update leds */ 6011b3009deSJoseph Chen ret = leds_update(dev, soc); 6021b3009deSJoseph Chen if (ret) 6031b3009deSJoseph Chen printf("update led failed: %d\n", ret); 6041b3009deSJoseph Chen 605f1c8ecceSJoseph Chen /* 606f23c35a8SJoseph Chen * If ever lowpower screen off, force screen_on=false, which 607f1c8ecceSJoseph Chen * means key event can't modify screen_on, only voltage higher 608f1c8ecceSJoseph Chen * then threshold can update screen_on=true; 609f1c8ecceSJoseph Chen */ 610f1c8ecceSJoseph Chen if (ever_lowpower_screen_off) 611f1c8ecceSJoseph Chen screen_on = false; 612f1c8ecceSJoseph Chen 613f1c8ecceSJoseph Chen /* 614f1c8ecceSJoseph Chen * Auto turn on screen when voltage higher than Vol screen on. 615ebe3d004SJoseph Chen * 'ever_lowpower_screen_off' means enter the while(1) loop with 616f1c8ecceSJoseph Chen * screen off. 617f1c8ecceSJoseph Chen */ 618f1c8ecceSJoseph Chen if ((ever_lowpower_screen_off) && 619d6653c12SJoseph Chen (voltage > pdata->screen_on_voltage)) { 620f1c8ecceSJoseph Chen ever_lowpower_screen_off = false; 621f1c8ecceSJoseph Chen screen_on = true; 622ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 623f1c8ecceSJoseph Chen } 624f1c8ecceSJoseph Chen 625f1c8ecceSJoseph Chen /* 626ebe3d004SJoseph Chen * IMAGE_RESET_IDX means show_idx show be update by start_idx. 627f1c8ecceSJoseph Chen * When short key pressed event trigged, we will set show_idx 628ebe3d004SJoseph Chen * as IMAGE_RESET_IDX which updates images index from start_idx 629f1c8ecceSJoseph Chen * that calculate by current soc. 630f1c8ecceSJoseph Chen */ 631ebe3d004SJoseph Chen if (show_idx == IMAGE_RESET_IDX) { 632ebe3d004SJoseph Chen for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) { 633f1c8ecceSJoseph Chen /* Find out which image we start to show */ 634f1c8ecceSJoseph Chen if ((soc >= image[i].soc) && 635f1c8ecceSJoseph Chen (soc < image[i + 1].soc)) { 636f1c8ecceSJoseph Chen start_idx = i; 637f1c8ecceSJoseph Chen break; 638f1c8ecceSJoseph Chen } 639f1c8ecceSJoseph Chen 640f1c8ecceSJoseph Chen if (soc >= 100) { 641ebe3d004SJoseph Chen start_idx = IMAGE_SOC_100_IDX(image_num); 642f1c8ecceSJoseph Chen break; 643f1c8ecceSJoseph Chen } 644f1c8ecceSJoseph Chen } 645f1c8ecceSJoseph Chen 646f1c8ecceSJoseph Chen debug("%s: show_idx=%d, screen_on=%d\n", 647f1c8ecceSJoseph Chen __func__, show_idx, screen_on); 648f1c8ecceSJoseph Chen 649f1c8ecceSJoseph Chen /* Mark start index and start time */ 650f1c8ecceSJoseph Chen show_idx = start_idx; 651f1c8ecceSJoseph Chen show_start = get_timer(0); 652f1c8ecceSJoseph Chen } 653f1c8ecceSJoseph Chen 654f1c8ecceSJoseph Chen debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 655f1c8ecceSJoseph Chen 656f1c8ecceSJoseph Chen /* Step3: show images */ 657f1c8ecceSJoseph Chen if (screen_on) { 65893aee2d9SJoseph Chen /* Don't call 'charge_show_bmp' unless image changed */ 65993aee2d9SJoseph Chen if (old_show_idx != show_idx) { 66093aee2d9SJoseph Chen old_show_idx = show_idx; 661f1c8ecceSJoseph Chen debug("SHOW: %s\n", image[show_idx].name); 662f23c35a8SJoseph Chen charge_show_bmp(image[show_idx].name); 66393aee2d9SJoseph Chen } 664ebe3d004SJoseph Chen /* Re-calculate timeout to off screen */ 665e7f9facbSJoseph Chen if (priv->auto_screen_off_timeout == 0) 666e7f9facbSJoseph Chen priv->auto_screen_off_timeout = get_timer(0); 667f1c8ecceSJoseph Chen } else { 668e7f9facbSJoseph Chen priv->auto_screen_off_timeout = 0; 6692e68f6b5SJoseph Chen system_suspend_enter(dev); 670f1c8ecceSJoseph Chen } 671f1c8ecceSJoseph Chen 672f1c8ecceSJoseph Chen mdelay(5); 673f1c8ecceSJoseph Chen 674f1c8ecceSJoseph Chen /* Every image shows period */ 675f1c8ecceSJoseph Chen if (get_timer(show_start) > image[show_idx].period) { 676f1c8ecceSJoseph Chen show_start = get_timer(0); 677f1c8ecceSJoseph Chen /* Update to next image */ 678f1c8ecceSJoseph Chen show_idx++; 679ebe3d004SJoseph Chen if (show_idx > IMAGE_SOC_100_IDX(image_num)) 680ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 681f1c8ecceSJoseph Chen } 682f1c8ecceSJoseph Chen 683f1c8ecceSJoseph Chen debug("step4 (%d)... \n", screen_on); 684f1c8ecceSJoseph Chen 685f1c8ecceSJoseph Chen /* 686f1c8ecceSJoseph Chen * Step4: check key event. 687f1c8ecceSJoseph Chen * 688f1c8ecceSJoseph Chen * Short key event: turn on/off screen; 689f1c8ecceSJoseph Chen * Long key event: show logo and boot system or still charging. 690f1c8ecceSJoseph Chen */ 691e7f9facbSJoseph Chen key_state = check_key_press(dev); 692b177a917SJoseph Chen if (key_state == KEY_PRESS_DOWN) { 693f1c8ecceSJoseph Chen /* 694f1c8ecceSJoseph Chen * Clear current image index, and show image 695f1c8ecceSJoseph Chen * from start_idx 696f1c8ecceSJoseph Chen */ 697ebe3d004SJoseph Chen old_show_idx = IMAGE_RESET_IDX; 698ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 699f1c8ecceSJoseph Chen 700f1c8ecceSJoseph Chen /* 701ebe3d004SJoseph Chen * Reverse the screen state 702f1c8ecceSJoseph Chen * 703f1c8ecceSJoseph Chen * If screen_on=false, means this short key pressed 704f1c8ecceSJoseph Chen * event turn on the screen and we need show images. 705f1c8ecceSJoseph Chen * 706f1c8ecceSJoseph Chen * If screen_on=true, means this short key pressed 707f1c8ecceSJoseph Chen * event turn off the screen and we never show images. 708f1c8ecceSJoseph Chen */ 709ebe3d004SJoseph Chen if (screen_on) { 710ebe3d004SJoseph Chen charge_show_bmp(NULL); /* Turn off screen */ 711f1c8ecceSJoseph Chen screen_on = false; 7122e68f6b5SJoseph Chen priv->suspend_delay_timeout = get_timer(0); 713ebe3d004SJoseph Chen } else { 714f1c8ecceSJoseph Chen screen_on = true; 715ebe3d004SJoseph Chen } 7162e68f6b5SJoseph Chen 7172e68f6b5SJoseph Chen printf("screen %s\n", screen_on ? "on" : "off"); 718b177a917SJoseph Chen } else if (key_state == KEY_PRESS_LONG_DOWN) { 719ebe3d004SJoseph Chen /* Set screen_on=true anyway when key long pressed */ 720f1c8ecceSJoseph Chen if (!screen_on) 721f1c8ecceSJoseph Chen screen_on = true; 722f1c8ecceSJoseph Chen 7232e68f6b5SJoseph Chen printf("screen %s\n", screen_on ? "on" : "off"); 7242e68f6b5SJoseph Chen 725f1c8ecceSJoseph Chen /* Is able to boot now ? */ 726d6653c12SJoseph Chen if (soc < pdata->exit_charge_level) { 727f1c8ecceSJoseph Chen printf("soc=%d%%, threshold soc=%d%%\n", 728d6653c12SJoseph Chen soc, pdata->exit_charge_level); 729f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 730ebe3d004SJoseph Chen show_idx = IMAGE_LOWPOWER_IDX(image_num); 731f1c8ecceSJoseph Chen continue; 732f1c8ecceSJoseph Chen } 733f1c8ecceSJoseph Chen 734d6653c12SJoseph Chen if (voltage < pdata->exit_charge_voltage) { 735f1c8ecceSJoseph Chen printf("voltage=%dmv, threshold voltage=%dmv\n", 736d6653c12SJoseph Chen voltage, pdata->exit_charge_voltage); 737f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 738ebe3d004SJoseph Chen show_idx = IMAGE_LOWPOWER_IDX(image_num); 739f1c8ecceSJoseph Chen continue; 740f1c8ecceSJoseph Chen } 741f1c8ecceSJoseph Chen 742f1c8ecceSJoseph Chen /* Success exit charging */ 743f1c8ecceSJoseph Chen printf("Exit charge animation...\n"); 744f23c35a8SJoseph Chen charge_show_logo(); 745f1c8ecceSJoseph Chen break; 746f1c8ecceSJoseph Chen } else { 747f1c8ecceSJoseph Chen /* Do nothing */ 748f1c8ecceSJoseph Chen } 749f1c8ecceSJoseph Chen 750f1c8ecceSJoseph Chen debug("step5 (%d)... \n", screen_on); 751f1c8ecceSJoseph Chen 752d6653c12SJoseph Chen /* Step5: Exit by ctrl+c */ 7531367bfe3SJoseph Chen if (ctrlc()) { 754d6653c12SJoseph Chen if (voltage >= pdata->screen_on_voltage) 755f23c35a8SJoseph Chen charge_show_logo(); 7561367bfe3SJoseph Chen printf("Exit charge, due to ctrl+c\n"); 7571367bfe3SJoseph Chen break; 7581367bfe3SJoseph Chen } 759f1c8ecceSJoseph Chen } 760f1c8ecceSJoseph Chen 761e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) 762e7f9facbSJoseph Chen autowakeup_timer_uninit(); 763e7f9facbSJoseph Chen 764f1c8ecceSJoseph Chen ms = get_timer(charge_start); 765f1c8ecceSJoseph Chen if (ms >= 1000) { 766f1c8ecceSJoseph Chen sec = ms / 1000; 767f1c8ecceSJoseph Chen ms = ms % 1000; 768f1c8ecceSJoseph Chen } 769f1c8ecceSJoseph Chen 770f1c8ecceSJoseph Chen printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 771f1c8ecceSJoseph Chen sec, ms, soc, voltage); 772f1c8ecceSJoseph Chen 773f1c8ecceSJoseph Chen return 0; 774f1c8ecceSJoseph Chen } 775f1c8ecceSJoseph Chen 7764d083e3fSJoseph Chen static int fg_charger_get_device(struct udevice **fuel_gauge, 7774d083e3fSJoseph Chen struct udevice **charger) 7784d083e3fSJoseph Chen { 7794d083e3fSJoseph Chen struct udevice *dev; 7804d083e3fSJoseph Chen struct uclass *uc; 7814d083e3fSJoseph Chen int ret, cap; 7824d083e3fSJoseph Chen 7834d083e3fSJoseph Chen *fuel_gauge = NULL, 7844d083e3fSJoseph Chen *charger = NULL; 7854d083e3fSJoseph Chen 7864d083e3fSJoseph Chen ret = uclass_get(UCLASS_FG, &uc); 7874d083e3fSJoseph Chen if (ret) 7884d083e3fSJoseph Chen return ret; 7894d083e3fSJoseph Chen 7904d083e3fSJoseph Chen for (uclass_first_device(UCLASS_FG, &dev); 7914d083e3fSJoseph Chen dev; 7924d083e3fSJoseph Chen uclass_next_device(&dev)) { 7934d083e3fSJoseph Chen cap = fuel_gauge_capability(dev); 7944d083e3fSJoseph Chen if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) { 7954d083e3fSJoseph Chen *fuel_gauge = dev; 7964d083e3fSJoseph Chen *charger = NULL; 7974d083e3fSJoseph Chen } else if (cap == FG_CAP_FUEL_GAUGE) { 7984d083e3fSJoseph Chen *fuel_gauge = dev; 7994d083e3fSJoseph Chen } else if (cap == FG_CAP_CHARGER) { 8004d083e3fSJoseph Chen *charger = dev; 8014d083e3fSJoseph Chen } 8024d083e3fSJoseph Chen } 8034d083e3fSJoseph Chen 8044d083e3fSJoseph Chen return (*fuel_gauge) ? 0 : -ENODEV; 8054d083e3fSJoseph Chen } 8064d083e3fSJoseph Chen 807f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = { 808f1c8ecceSJoseph Chen .show = charge_animation_show, 809f1c8ecceSJoseph Chen }; 810f1c8ecceSJoseph Chen 811f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev) 812f1c8ecceSJoseph Chen { 813f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 814f23c35a8SJoseph Chen int ret, soc; 815f1c8ecceSJoseph Chen 816f23c35a8SJoseph Chen /* Get PMIC: used for power off system */ 817ebe3d004SJoseph Chen ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic); 818f1c8ecceSJoseph Chen if (ret) { 819cf49f6adSJoseph Chen if (ret == -ENODEV) 820cf49f6adSJoseph Chen printf("Can't find PMIC\n"); 821cf49f6adSJoseph Chen else 822f1c8ecceSJoseph Chen printf("Get UCLASS PMIC failed: %d\n", ret); 823f1c8ecceSJoseph Chen return ret; 824f1c8ecceSJoseph Chen } 825f1c8ecceSJoseph Chen 8264d083e3fSJoseph Chen /* Get fuel gauge and charger(If need) */ 8274d083e3fSJoseph Chen ret = fg_charger_get_device(&priv->fg, &priv->charger); 828f1c8ecceSJoseph Chen if (ret) { 829cf49f6adSJoseph Chen if (ret == -ENODEV) 83031ab5b3bSJoseph Chen debug("Can't find FG\n"); 831cf49f6adSJoseph Chen else 83231ab5b3bSJoseph Chen debug("Get UCLASS FG failed: %d\n", ret); 833f1c8ecceSJoseph Chen return ret; 834f1c8ecceSJoseph Chen } 835f1c8ecceSJoseph Chen 8368b436ce5SElaine Zhang /* Get rtc: used for power on */ 837ebe3d004SJoseph Chen ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc); 8388b436ce5SElaine Zhang if (ret) { 8398b436ce5SElaine Zhang if (ret == -ENODEV) 8408b436ce5SElaine Zhang debug("Can't find RTC\n"); 8418b436ce5SElaine Zhang else 8428b436ce5SElaine Zhang debug("Get UCLASS RTC failed: %d\n", ret); 8438b436ce5SElaine Zhang } 8448b436ce5SElaine Zhang 84579244e4cSJoseph Chen /* Get PWRKEY: used for wakeup and turn off/on LCD */ 84631ab5b3bSJoseph Chen if (key_read(KEY_POWER) == KEY_NOT_EXIST) { 84731ab5b3bSJoseph Chen debug("Can't find power key\n"); 848f23c35a8SJoseph Chen return -EINVAL; 849f23c35a8SJoseph Chen } 850f23c35a8SJoseph Chen 851f23c35a8SJoseph Chen /* Initialize charge current */ 8524f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(priv->fg); 853f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 85431ab5b3bSJoseph Chen debug("get soc failed: %d\n", soc); 855f23c35a8SJoseph Chen return -EINVAL; 856f23c35a8SJoseph Chen } 857f23c35a8SJoseph Chen 8581b3009deSJoseph Chen /* Get leds */ 8591b3009deSJoseph Chen #ifdef CONFIG_LED 8601b3009deSJoseph Chen ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging); 8611b3009deSJoseph Chen if (!ret) 8621b3009deSJoseph Chen printf("Found Charging LED\n"); 8631b3009deSJoseph Chen ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full); 8641b3009deSJoseph Chen if (!ret) 8651b3009deSJoseph Chen printf("Found Charging-Full LED\n"); 8661b3009deSJoseph Chen #endif 8671b3009deSJoseph Chen 868f23c35a8SJoseph Chen /* Get charge images */ 869f1c8ecceSJoseph Chen priv->image = image; 870f1c8ecceSJoseph Chen priv->image_num = ARRAY_SIZE(image); 871f1c8ecceSJoseph Chen 872f1c8ecceSJoseph Chen printf("Enable charge animation display\n"); 873f1c8ecceSJoseph Chen 874f1c8ecceSJoseph Chen return 0; 875f1c8ecceSJoseph Chen } 876f1c8ecceSJoseph Chen 877f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = { 878d6653c12SJoseph Chen { .compatible = "rockchip,uboot-charge" }, 879f1c8ecceSJoseph Chen { }, 880f1c8ecceSJoseph Chen }; 881f1c8ecceSJoseph Chen 882f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = { 883f1c8ecceSJoseph Chen .name = "charge-animation", 884f1c8ecceSJoseph Chen .id = UCLASS_CHARGE_DISPLAY, 885f1c8ecceSJoseph Chen .probe = charge_animation_probe, 886f1c8ecceSJoseph Chen .of_match = charge_animation_ids, 887f1c8ecceSJoseph Chen .ops = &charge_animation_ops, 888f1c8ecceSJoseph Chen .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 889f1c8ecceSJoseph Chen .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 890f1c8ecceSJoseph Chen .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 891f1c8ecceSJoseph Chen }; 892