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 32255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 33255e5751SWenping Zhang #include <rk_eink.h> 34255e5751SWenping Zhang #endif 35f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR; 36f1c8ecceSJoseph Chen 37ebe3d004SJoseph Chen #define IMAGE_RESET_IDX -1 38ebe3d004SJoseph Chen #define IMAGE_SOC_100_IDX(n) ((n) - 2) 39ebe3d004SJoseph Chen #define IMAGE_LOWPOWER_IDX(n) ((n) - 1) 402e68f6b5SJoseph Chen #define SYSTEM_SUSPEND_DELAY_MS 5000 415e804741SJoseph Chen #define FUEL_GAUGE_POLL_MS 1000 42f1c8ecceSJoseph Chen 431b3009deSJoseph Chen #define LED_CHARGING_NAME "battery_charging" 441b3009deSJoseph Chen #define LED_CHARGING_FULL_NAME "battery_full" 451b3009deSJoseph Chen 46f1c8ecceSJoseph Chen struct charge_image { 47f1c8ecceSJoseph Chen const char *name; 48f1c8ecceSJoseph Chen int soc; 49f1c8ecceSJoseph Chen int period; /* ms */ 50f1c8ecceSJoseph Chen }; 51f1c8ecceSJoseph Chen 52f1c8ecceSJoseph Chen struct charge_animation_priv { 53f1c8ecceSJoseph Chen struct udevice *pmic; 54f1c8ecceSJoseph Chen struct udevice *fg; 554d083e3fSJoseph Chen struct udevice *charger; 568b436ce5SElaine Zhang struct udevice *rtc; 571b3009deSJoseph Chen #ifdef CONFIG_LED 581b3009deSJoseph Chen struct udevice *led_charging; 591b3009deSJoseph Chen struct udevice *led_full; 601b3009deSJoseph Chen #endif 61f1c8ecceSJoseph Chen const struct charge_image *image; 62f1c8ecceSJoseph Chen int image_num; 63e7f9facbSJoseph Chen 64e7f9facbSJoseph Chen int auto_wakeup_key_state; 652e68f6b5SJoseph Chen ulong auto_screen_off_timeout; /* ms */ 662e68f6b5SJoseph Chen ulong suspend_delay_timeout; /* ms */ 67f1c8ecceSJoseph Chen }; 68f1c8ecceSJoseph Chen 69f1c8ecceSJoseph Chen /* 70f1c8ecceSJoseph Chen * IF you want to use your own charge images, please: 71f1c8ecceSJoseph Chen * 72f1c8ecceSJoseph Chen * 1. Update the following 'image[]' to point to your own images; 73f1c8ecceSJoseph Chen * 2. You must set the failed image as last one and soc = -1 !!! 74f1c8ecceSJoseph Chen */ 75f1c8ecceSJoseph Chen static const struct charge_image image[] = { 76f1c8ecceSJoseph Chen { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 77f1c8ecceSJoseph Chen { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 78f1c8ecceSJoseph Chen { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 79f1c8ecceSJoseph Chen { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 80f1c8ecceSJoseph Chen { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 81f1c8ecceSJoseph Chen { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 82f1c8ecceSJoseph Chen { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 83f1c8ecceSJoseph Chen }; 84f1c8ecceSJoseph Chen 85f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev) 86f1c8ecceSJoseph Chen { 87f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 88f1c8ecceSJoseph Chen 89d6653c12SJoseph Chen /* charge mode */ 90d6653c12SJoseph Chen pdata->uboot_charge = 91d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 92d6653c12SJoseph Chen pdata->android_charge = 93d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 94f1c8ecceSJoseph Chen 95038c1ecaSShunqing Chen pdata->auto_exit_charge = 96038c1ecaSShunqing Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-auto", 0); 97d6653c12SJoseph Chen pdata->exit_charge_level = 98d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 99d6653c12SJoseph Chen pdata->exit_charge_voltage = 100d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 101f23c35a8SJoseph Chen 102f23c35a8SJoseph Chen pdata->low_power_voltage = 103f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 104f23c35a8SJoseph Chen 105d6653c12SJoseph Chen pdata->screen_on_voltage = 106d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 107f23c35a8SJoseph Chen pdata->system_suspend = 108f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,system-suspend", 0); 109f1c8ecceSJoseph Chen 110e7f9facbSJoseph Chen pdata->auto_wakeup_interval = 111e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 112e7f9facbSJoseph Chen pdata->auto_wakeup_screen_invert = 113e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 114e7f9facbSJoseph Chen 115e7f9facbSJoseph Chen pdata->auto_off_screen_interval = 116e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 117e7f9facbSJoseph Chen 118f23c35a8SJoseph Chen if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 119f23c35a8SJoseph Chen pdata->screen_on_voltage = pdata->exit_charge_voltage; 120f1c8ecceSJoseph Chen 121038c1ecaSShunqing Chen if (pdata->auto_exit_charge && !pdata->auto_wakeup_interval) 122038c1ecaSShunqing Chen pdata->auto_wakeup_interval = 10; 123038c1ecaSShunqing Chen 124d6653c12SJoseph Chen debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 125f23c35a8SJoseph Chen "lp_voltage=%d%%, screen_on=%dmv\n", 126d6653c12SJoseph Chen pdata->uboot_charge, pdata->android_charge, 127d6653c12SJoseph Chen pdata->exit_charge_level, pdata->exit_charge_voltage, 128f23c35a8SJoseph Chen pdata->low_power_voltage, pdata->screen_on_voltage); 129f1c8ecceSJoseph Chen 130f1c8ecceSJoseph Chen return 0; 131f1c8ecceSJoseph Chen } 132f1c8ecceSJoseph Chen 133e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev) 134f1c8ecceSJoseph Chen { 135e7f9facbSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 136e7f9facbSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 1378b436ce5SElaine Zhang u32 state, rtc_state = 0; 1388b436ce5SElaine Zhang 1398b436ce5SElaine Zhang #ifdef CONFIG_DM_RTC 1408b436ce5SElaine Zhang if (priv->rtc) 1418b436ce5SElaine Zhang rtc_state = rtc_alarm_trigger(priv->rtc); 1428b436ce5SElaine Zhang #endif 1438b436ce5SElaine Zhang if (rtc_state) { 1448b436ce5SElaine Zhang printf("rtc alarm trigger...\n"); 1458b436ce5SElaine Zhang return KEY_PRESS_LONG_DOWN; 1468b436ce5SElaine Zhang } 147f1c8ecceSJoseph Chen 148787a62ebSJoseph Chen state = key_read(KEY_POWER); 149b177a917SJoseph Chen if (state < 0) 150f1c8ecceSJoseph Chen printf("read power key failed: %d\n", state); 15193aee2d9SJoseph Chen else if (state == KEY_PRESS_DOWN) 15293aee2d9SJoseph Chen printf("power key pressed...\n"); 15393aee2d9SJoseph Chen else if (state == KEY_PRESS_LONG_DOWN) 15493aee2d9SJoseph Chen printf("power key long pressed...\n"); 155f1c8ecceSJoseph Chen 156e7f9facbSJoseph Chen /* Fixup key state for following cases */ 157e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 158e7f9facbSJoseph Chen if (pdata->auto_wakeup_screen_invert) { 159e7f9facbSJoseph Chen if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 160e7f9facbSJoseph Chen /* Value is updated in timer interrupt */ 161e7f9facbSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_NONE; 162e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 163e7f9facbSJoseph Chen } 164e7f9facbSJoseph Chen } 16523858492SShunqing Chen } 16623858492SShunqing Chen if (!pdata->auto_wakeup_screen_invert && 16723858492SShunqing Chen pdata->auto_off_screen_interval) { 16893aee2d9SJoseph Chen if (priv->auto_screen_off_timeout && 16993aee2d9SJoseph Chen get_timer(priv->auto_screen_off_timeout) > 170e7f9facbSJoseph Chen pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 171e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 172e7f9facbSJoseph Chen printf("Auto screen off\n"); 173e7f9facbSJoseph Chen } 174e7f9facbSJoseph Chen } 175e7f9facbSJoseph Chen 176b177a917SJoseph Chen return state; 177f1c8ecceSJoseph Chen } 178f1c8ecceSJoseph Chen 179175257e4SJoseph Chen /* 180175257e4SJoseph Chen * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup 181175257e4SJoseph Chen * period timer is useless. 182175257e4SJoseph Chen */ 1833415d4ebSJoseph Chen #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND) 1842e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev) 185175257e4SJoseph Chen { 186175257e4SJoseph Chen return 0; 187175257e4SJoseph Chen } 188175257e4SJoseph Chen 189175257e4SJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {} 190175257e4SJoseph Chen static void autowakeup_timer_uninit(void) {} 191175257e4SJoseph Chen 192175257e4SJoseph Chen #else 1932e68f6b5SJoseph Chen static int system_suspend_enter(struct udevice *dev) 194b177a917SJoseph Chen { 1952e68f6b5SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 1962e68f6b5SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 1972e68f6b5SJoseph Chen 1982e68f6b5SJoseph Chen /* 1992e68f6b5SJoseph Chen * When cpu is in wfi and we try to give a long key press event without 2002e68f6b5SJoseph Chen * key release, cpu would wakeup and enter wfi again immediately. So 2012e68f6b5SJoseph Chen * here is the problem: cpu can only wakeup when long key released. 2022e68f6b5SJoseph Chen * 2032e68f6b5SJoseph Chen * Actually, we want cpu can detect long key event without key release, 2042e68f6b5SJoseph Chen * so we give a suspend delay timeout for cpu to detect this. 2052e68f6b5SJoseph Chen */ 2062e68f6b5SJoseph Chen if (priv->suspend_delay_timeout && 2072e68f6b5SJoseph Chen get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS) 2082e68f6b5SJoseph Chen return 0; 2092e68f6b5SJoseph Chen 210f23c35a8SJoseph Chen if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 211b177a917SJoseph Chen printf("\nSystem suspend: "); 212992f4e77SJoseph Chen putc('0'); 213b177a917SJoseph Chen local_irq_disable(); 21406b61291SJoseph Chen putc('1'); 21506b61291SJoseph Chen regulators_enable_state_mem(false); 216b177a917SJoseph Chen putc('2'); 21706b61291SJoseph Chen pmic_suspend(priv->pmic); 218b177a917SJoseph Chen putc('3'); 21906b61291SJoseph Chen irqs_suspend(); 22088949342SJoseph Chen putc('4'); 22106b61291SJoseph Chen device_suspend(); 22206b61291SJoseph Chen putc('5'); 223b177a917SJoseph Chen putc('\n'); 224b177a917SJoseph Chen 225b177a917SJoseph Chen /* Trap into ATF for low power mode */ 226b177a917SJoseph Chen cpu_suspend(0, psci_system_suspend); 227b177a917SJoseph Chen 228b177a917SJoseph Chen putc('\n'); 22988949342SJoseph Chen putc('4'); 23088949342SJoseph Chen device_resume(); 231b177a917SJoseph Chen putc('3'); 232b177a917SJoseph Chen irqs_resume(); 233b177a917SJoseph Chen putc('2'); 23406b61291SJoseph Chen pmic_resume(priv->pmic); 235b177a917SJoseph Chen putc('1'); 23606b61291SJoseph Chen local_irq_enable(); 23706b61291SJoseph Chen putc('0'); 238b177a917SJoseph Chen putc('\n'); 2398fc5ae06SJoseph Chen } else { 2405eac14dbSJoseph Chen irqs_suspend(); 2418fc5ae06SJoseph Chen printf("\nWfi\n"); 2428fc5ae06SJoseph Chen wfi(); 2432e68f6b5SJoseph Chen putc('1'); 2445eac14dbSJoseph Chen irqs_resume(); 2458fc5ae06SJoseph Chen } 246b177a917SJoseph Chen 2472e68f6b5SJoseph Chen priv->suspend_delay_timeout = get_timer(0); 2482e68f6b5SJoseph Chen 249b177a917SJoseph Chen /* 250b177a917SJoseph Chen * We must wait for key release event finish, otherwise 251b177a917SJoseph Chen * we may read key state too early. 252b177a917SJoseph Chen */ 253b177a917SJoseph Chen mdelay(300); 254b177a917SJoseph Chen 255b177a917SJoseph Chen return 0; 256f1c8ecceSJoseph Chen } 257ebe3d004SJoseph Chen 25879244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data) 25979244e4cSJoseph Chen { 26079244e4cSJoseph Chen struct udevice *dev = data; 26179244e4cSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 26279244e4cSJoseph Chen static long long count; 26379244e4cSJoseph Chen 26479244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 26579244e4cSJoseph Chen 26679244e4cSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 26779244e4cSJoseph Chen printf("auto wakeup count: %lld\n", ++count); 26879244e4cSJoseph Chen } 26979244e4cSJoseph Chen 27079244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 27179244e4cSJoseph Chen { 27279244e4cSJoseph Chen uint64_t period = 24000000ULL * seconds; 27379244e4cSJoseph Chen 27479244e4cSJoseph Chen /* Disable before conifg */ 27579244e4cSJoseph Chen writel(0, TIMER_BASE + TIMER_CTRL); 27679244e4cSJoseph Chen 27779244e4cSJoseph Chen /* Config */ 27879244e4cSJoseph Chen writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 27979244e4cSJoseph Chen writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 28079244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 28179244e4cSJoseph Chen writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 28279244e4cSJoseph Chen 28379244e4cSJoseph Chen /* IRQ */ 28479244e4cSJoseph Chen irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 28579244e4cSJoseph Chen irq_handler_enable(TIMER_IRQ); 28679244e4cSJoseph Chen } 28779244e4cSJoseph Chen 28879244e4cSJoseph Chen static void autowakeup_timer_uninit(void) 28979244e4cSJoseph Chen { 290094465a9SJoseph Chen writel(0, TIMER_BASE + TIMER_CTRL); 291094465a9SJoseph Chen 292094465a9SJoseph Chen irq_handler_disable(TIMER_IRQ); 29379244e4cSJoseph Chen irq_free_handler(TIMER_IRQ); 29479244e4cSJoseph Chen } 295175257e4SJoseph Chen #endif 29679244e4cSJoseph Chen 297f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP 298f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) 299f23c35a8SJoseph Chen { 300f23c35a8SJoseph Chen rockchip_show_bmp(name); 301f23c35a8SJoseph Chen } 302f23c35a8SJoseph Chen 303f23c35a8SJoseph Chen static void charge_show_logo(void) 304f23c35a8SJoseph Chen { 305f23c35a8SJoseph Chen rockchip_show_logo(); 306f23c35a8SJoseph Chen } 307f23c35a8SJoseph Chen #else 308f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {} 309f23c35a8SJoseph Chen static void charge_show_logo(void) {} 310f23c35a8SJoseph Chen #endif 311f23c35a8SJoseph Chen 3121b3009deSJoseph Chen #ifdef CONFIG_LED 3131b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) 3141b3009deSJoseph Chen { 3151b3009deSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 3161b3009deSJoseph Chen static int old_soc = -1; 3171b3009deSJoseph Chen int ret, ledst; 3181b3009deSJoseph Chen 3191b3009deSJoseph Chen if (old_soc == soc) 3201b3009deSJoseph Chen return 0; 3211b3009deSJoseph Chen 3221b3009deSJoseph Chen old_soc = soc; 3231b3009deSJoseph Chen if (priv->led_charging) { 3241b3009deSJoseph Chen ledst = (soc < 100) ? LEDST_ON : LEDST_OFF; 3251b3009deSJoseph Chen ret = led_set_state(priv->led_charging, ledst); 3261b3009deSJoseph Chen if (ret) { 3271b3009deSJoseph Chen printf("set charging led %s failed, ret=%d\n", 3281b3009deSJoseph Chen (ledst == LEDST_ON) ? "ON" : "OFF", ret); 3291b3009deSJoseph Chen return ret; 3301b3009deSJoseph Chen } 3311b3009deSJoseph Chen } 3321b3009deSJoseph Chen 3331b3009deSJoseph Chen if (priv->led_full) { 3341b3009deSJoseph Chen ledst = (soc == 100) ? LEDST_ON : LEDST_OFF; 3351b3009deSJoseph Chen ret = led_set_state(priv->led_full, ledst); 3361b3009deSJoseph Chen if (ret) { 3371b3009deSJoseph Chen printf("set charging full led %s failed, ret=%d\n", 3381b3009deSJoseph Chen ledst == LEDST_ON ? "ON" : "OFF", ret); 3391b3009deSJoseph Chen return ret; 3401b3009deSJoseph Chen } 3411b3009deSJoseph Chen } 3421b3009deSJoseph Chen 3431b3009deSJoseph Chen return 0; 3441b3009deSJoseph Chen } 3451b3009deSJoseph Chen #else 3461b3009deSJoseph Chen static int leds_update(struct udevice *dev, int soc) { return 0; } 3471b3009deSJoseph Chen #endif 3481b3009deSJoseph Chen 3494d083e3fSJoseph Chen static int fg_charger_get_chrg_online(struct udevice *dev) 3504d083e3fSJoseph Chen { 3514d083e3fSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 3524d083e3fSJoseph Chen struct udevice *charger; 3534d083e3fSJoseph Chen 3544d083e3fSJoseph Chen charger = priv->charger ? : priv->fg; 3554d083e3fSJoseph Chen 3564d083e3fSJoseph Chen return fuel_gauge_get_chrg_online(charger); 3574d083e3fSJoseph Chen } 3584d083e3fSJoseph Chen 359f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev) 360f23c35a8SJoseph Chen { 361f23c35a8SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 362f23c35a8SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 363f23c35a8SJoseph Chen struct udevice *pmic = priv->pmic; 364f23c35a8SJoseph Chen struct udevice *fg = priv->fg; 365f23c35a8SJoseph Chen int voltage, soc, charging = 1; 36679244e4cSJoseph Chen static int timer_initialized; 3671b3009deSJoseph Chen int ret; 368f23c35a8SJoseph Chen 369f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 370f23c35a8SJoseph Chen if (voltage < 0) 371f23c35a8SJoseph Chen return -EINVAL; 372f23c35a8SJoseph Chen 373f23c35a8SJoseph Chen while (voltage < pdata->low_power_voltage + 50) { 374f23c35a8SJoseph Chen /* Check charger online */ 3754d083e3fSJoseph Chen charging = fg_charger_get_chrg_online(dev); 376f23c35a8SJoseph Chen if (charging <= 0) { 37779244e4cSJoseph Chen printf("%s: Not charging, online=%d. Shutdown...\n", 37879244e4cSJoseph Chen __func__, charging); 379f23c35a8SJoseph Chen /* wait uart flush before shutdown */ 38079244e4cSJoseph Chen mdelay(5); 381f23c35a8SJoseph Chen /* PMIC shutdown */ 382f23c35a8SJoseph Chen pmic_shutdown(pmic); 383f23c35a8SJoseph Chen 384f23c35a8SJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 385f23c35a8SJoseph Chen continue; 386f23c35a8SJoseph Chen } 387f23c35a8SJoseph Chen 38879244e4cSJoseph Chen /* Enable auto wakeup */ 38979244e4cSJoseph Chen if (!timer_initialized) { 39079244e4cSJoseph Chen timer_initialized = 1; 39179244e4cSJoseph Chen autowakeup_timer_init(dev, 5); 39279244e4cSJoseph Chen } 39379244e4cSJoseph Chen 394f23c35a8SJoseph Chen /* 395f23c35a8SJoseph Chen * Just for fuel gauge to update something important, 396f23c35a8SJoseph Chen * including charge current, coulometer or other. 397f23c35a8SJoseph Chen */ 3984f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(fg); 399f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 400f23c35a8SJoseph Chen printf("get soc failed: %d\n", soc); 401f23c35a8SJoseph Chen continue; 402f23c35a8SJoseph Chen } 403f23c35a8SJoseph Chen 4041b3009deSJoseph Chen /* Update led */ 4051b3009deSJoseph Chen ret = leds_update(dev, soc); 4061b3009deSJoseph Chen if (ret) 4071b3009deSJoseph Chen printf("update led failed: %d\n", ret); 4081b3009deSJoseph Chen 409f23c35a8SJoseph Chen printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 410f23c35a8SJoseph Chen pdata->low_power_voltage, voltage); 411f23c35a8SJoseph Chen 412f23c35a8SJoseph Chen /* System suspend */ 4132e68f6b5SJoseph Chen system_suspend_enter(dev); 414f23c35a8SJoseph Chen 415f23c35a8SJoseph Chen /* Update voltage */ 416f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 417f23c35a8SJoseph Chen if (voltage < 0) { 418f23c35a8SJoseph Chen printf("get voltage failed: %d\n", voltage); 419f23c35a8SJoseph Chen continue; 420f23c35a8SJoseph Chen } 4215fc2a70cSJoseph Chen 4225fc2a70cSJoseph Chen if (ctrlc()) { 4235fc2a70cSJoseph Chen printf("Extrem low charge: exit by ctrl+c\n"); 4245fc2a70cSJoseph Chen break; 4255fc2a70cSJoseph Chen } 426f23c35a8SJoseph Chen } 427f23c35a8SJoseph Chen 42879244e4cSJoseph Chen autowakeup_timer_uninit(); 42979244e4cSJoseph Chen 430f23c35a8SJoseph Chen return 0; 431f23c35a8SJoseph Chen } 432f23c35a8SJoseph Chen 433f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev) 434f1c8ecceSJoseph Chen { 435f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 436f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 437f1c8ecceSJoseph Chen const struct charge_image *image = priv->image; 438f1c8ecceSJoseph Chen struct udevice *pmic = priv->pmic; 439f1c8ecceSJoseph Chen struct udevice *fg = priv->fg; 440a8b9d026SJoseph Chen const char *preboot = env_get("preboot"); 441f1c8ecceSJoseph Chen int image_num = priv->image_num; 442f1c8ecceSJoseph Chen bool ever_lowpower_screen_off = false; 443f1c8ecceSJoseph Chen bool screen_on = true; 444f1c8ecceSJoseph Chen ulong show_start = 0, charge_start = 0, debug_start = 0; 4455e804741SJoseph Chen ulong delta; 446f1c8ecceSJoseph Chen ulong ms = 0, sec = 0; 447ebe3d004SJoseph Chen int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RESET_IDX; 448d6653c12SJoseph Chen int soc, voltage, current, key_state; 449f23c35a8SJoseph Chen int i, charging = 1, ret; 4508f9ff705SJoseph Chen int boot_mode; 4515e804741SJoseph Chen int first_poll_fg = 1; 452038c1ecaSShunqing Chen bool exit_charge = false; 453f1c8ecceSJoseph Chen 454f23c35a8SJoseph Chen /* 455f23c35a8SJoseph Chen * Check sequence: 456f23c35a8SJoseph Chen * 457f23c35a8SJoseph Chen * 1. Extrem low power charge? 458f23c35a8SJoseph Chen * 2. Preboot cmd? 459f23c35a8SJoseph Chen * 3. Valid boot mode? 460f23c35a8SJoseph Chen * 4. U-Boot charge enabled by dts config? 461f23c35a8SJoseph Chen * 5. Screen off before charge? 462f23c35a8SJoseph Chen * 6. Enter charge ! 463f23c35a8SJoseph Chen * 464f23c35a8SJoseph Chen */ 46561a7a6d6SJoseph Chen if (!fuel_gauge_bat_is_exist(fg)) { 46661a7a6d6SJoseph Chen printf("Exit charge: battery is not exist\n"); 46761a7a6d6SJoseph Chen return 0; 46861a7a6d6SJoseph Chen } 46961a7a6d6SJoseph Chen 470f23c35a8SJoseph Chen /* Extrem low power charge */ 471f23c35a8SJoseph Chen ret = charge_extrem_low_power(dev); 472f23c35a8SJoseph Chen if (ret < 0) { 473f23c35a8SJoseph Chen printf("extrem low power charge failed, ret=%d\n", ret); 474f23c35a8SJoseph Chen return ret; 475f23c35a8SJoseph Chen } 476f23c35a8SJoseph Chen 477a8b9d026SJoseph Chen /* If there is preboot command, exit */ 47861a7a6d6SJoseph Chen if (preboot && !strstr(preboot, "dvfs")) { 4797ae45834SJoseph Chen printf("Exit charge: due to preboot cmd '%s'\n", preboot); 480a8b9d026SJoseph Chen return 0; 481a8b9d026SJoseph Chen } 482a8b9d026SJoseph Chen 483f23c35a8SJoseph Chen /* Not valid charge mode, exit */ 484d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 4858f9ff705SJoseph Chen boot_mode = rockchip_get_boot_mode(); 486221b5220SJoseph Chen if ((boot_mode != BOOT_MODE_CHARGING) && 487221b5220SJoseph Chen (boot_mode != BOOT_MODE_UNDEFINE)) { 4887ae45834SJoseph Chen printf("Exit charge: due to boot mode\n"); 4898f9ff705SJoseph Chen return 0; 4908f9ff705SJoseph Chen } 4918f9ff705SJoseph Chen #endif 492038c1ecaSShunqing Chen charging = fg_charger_get_chrg_online(dev); 493038c1ecaSShunqing Chen /* Not charger online and low power, shutdown */ 494038c1ecaSShunqing Chen if (charging <= 0 && pdata->auto_exit_charge) { 495038c1ecaSShunqing Chen soc = fuel_gauge_update_get_soc(fg); 496038c1ecaSShunqing Chen voltage = fuel_gauge_get_voltage(fg); 497038c1ecaSShunqing Chen if (soc < pdata->exit_charge_level) { 498038c1ecaSShunqing Chen printf("soc(%d%%) < exit_charge_level(%d%%)\n", 499038c1ecaSShunqing Chen soc, pdata->exit_charge_level); 500038c1ecaSShunqing Chen exit_charge = true; 501038c1ecaSShunqing Chen } 502038c1ecaSShunqing Chen if (voltage < pdata->exit_charge_voltage) { 503038c1ecaSShunqing Chen printf("voltage(%d) < exit_charge_voltage(%d)\n", 504038c1ecaSShunqing Chen voltage, pdata->exit_charge_voltage); 505038c1ecaSShunqing Chen exit_charge = true; 506038c1ecaSShunqing Chen } 507038c1ecaSShunqing Chen if (exit_charge) { 508038c1ecaSShunqing Chen printf("Not charging and low power, Shutdown...\n"); 509038c1ecaSShunqing Chen show_idx = IMAGE_LOWPOWER_IDX(image_num); 510038c1ecaSShunqing Chen charge_show_bmp(image[show_idx].name); 511038c1ecaSShunqing Chen mdelay(1000); 512038c1ecaSShunqing Chen pmic_shutdown(pmic); 513038c1ecaSShunqing Chen } 514038c1ecaSShunqing Chen } 515221b5220SJoseph Chen 516616056c9Sshengfei Xu /* Not charger online, exit */ 517cf49f6adSJoseph Chen if (charging <= 0) { 5187ae45834SJoseph Chen printf("Exit charge: due to charger offline\n"); 519616056c9Sshengfei Xu return 0; 520cf49f6adSJoseph Chen } 521616056c9Sshengfei Xu 522f23c35a8SJoseph Chen /* Enter android charge, set property for kernel */ 523d6653c12SJoseph Chen if (pdata->android_charge) { 524d6653c12SJoseph Chen env_update("bootargs", "androidboot.mode=charger"); 525d6653c12SJoseph Chen printf("Android charge mode\n"); 526d6653c12SJoseph Chen } 527d6653c12SJoseph Chen 528f23c35a8SJoseph Chen /* Not enable U-Boot charge, exit */ 529cf49f6adSJoseph Chen if (!pdata->uboot_charge) { 5307ae45834SJoseph Chen printf("Exit charge: due to not enable uboot charge\n"); 531d6653c12SJoseph Chen return 0; 532cf49f6adSJoseph Chen } 533d6653c12SJoseph Chen 534f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 535f1c8ecceSJoseph Chen if (voltage < 0) { 536f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 537f1c8ecceSJoseph Chen return -EINVAL; 538f1c8ecceSJoseph Chen } 539f1c8ecceSJoseph Chen 540f1c8ecceSJoseph Chen /* If low power, turn off screen */ 541d6653c12SJoseph Chen if (voltage <= pdata->screen_on_voltage + 50) { 542f1c8ecceSJoseph Chen screen_on = false; 543f1c8ecceSJoseph Chen ever_lowpower_screen_off = true; 544f23c35a8SJoseph Chen charge_show_bmp(NULL); 545f1c8ecceSJoseph Chen } 546f1c8ecceSJoseph Chen 547e7f9facbSJoseph Chen /* Auto wakeup */ 548e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 549e7f9facbSJoseph Chen printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 55079244e4cSJoseph Chen autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 551e7f9facbSJoseph Chen } 552e7f9facbSJoseph Chen 553175257e4SJoseph Chen /* Give a message warning when CONFIG_IRQ is not enabled */ 554175257e4SJoseph Chen #ifdef CONFIG_IRQ 555d6653c12SJoseph Chen printf("Enter U-Boot charging mode\n"); 556175257e4SJoseph Chen #else 557175257e4SJoseph Chen printf("Enter U-Boot charging mode(without IRQ)\n"); 558175257e4SJoseph Chen #endif 559f1c8ecceSJoseph Chen 560d6653c12SJoseph Chen charge_start = get_timer(0); 5615e804741SJoseph Chen delta = get_timer(0); 562e7f9facbSJoseph Chen 563f1c8ecceSJoseph Chen /* Charging ! */ 564f1c8ecceSJoseph Chen while (1) { 5655e804741SJoseph Chen /* 5665e804741SJoseph Chen * At the most time, fuel gauge is usually a i2c device, we 5675e804741SJoseph Chen * should avoid read/write all the time. We had better set 5685e804741SJoseph Chen * poll seconds to update fuel gauge info. 5695e804741SJoseph Chen */ 5705e804741SJoseph Chen if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS) 5715e804741SJoseph Chen goto show_images; 5725e804741SJoseph Chen 5735e804741SJoseph Chen delta = get_timer(0); 5745e804741SJoseph Chen 575f1c8ecceSJoseph Chen debug("step1 (%d)... \n", screen_on); 576f1c8ecceSJoseph Chen 577fd62311eSJoseph Chen /* 578fd62311eSJoseph Chen * Most fuel gauge is I2C interface, it shouldn't be interrupted 579ebe3d004SJoseph Chen * during transfer. The power key event depends on interrupt, so 580ebe3d004SJoseph Chen * we should disable local irq when update fuel gauge. 581fd62311eSJoseph Chen */ 582fd62311eSJoseph Chen local_irq_disable(); 583fd62311eSJoseph Chen 584f1c8ecceSJoseph Chen /* Step1: Is charging now ? */ 5854d083e3fSJoseph Chen charging = fg_charger_get_chrg_online(dev); 586f1c8ecceSJoseph Chen if (charging <= 0) { 587f1c8ecceSJoseph Chen printf("Not charging, online=%d. Shutdown...\n", 588f1c8ecceSJoseph Chen charging); 589255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 590255e5751SWenping Zhang /* 591*77bac292SZorro Liu * If charger is plug out during charging, display poweroff 592*77bac292SZorro Liu * image before device power off. 593255e5751SWenping Zhang * Irq must be enable if CONFIG_IRQ is defined, because 594255e5751SWenping Zhang * ebc need to wait irq to indicate frame is complete. 595255e5751SWenping Zhang */ 596255e5751SWenping Zhang #ifdef CONFIG_IRQ 597255e5751SWenping Zhang local_irq_enable(); 598255e5751SWenping Zhang #endif 599*77bac292SZorro Liu ret = rockchip_eink_show_charge_logo(EINK_LOGO_POWEROFF); 600255e5751SWenping Zhang if (ret != 0) 601255e5751SWenping Zhang printf("Eink display reset logo failed\n"); 602255e5751SWenping Zhang #ifdef CONFIG_IRQ 603255e5751SWenping Zhang local_irq_disable(); 604255e5751SWenping Zhang #endif 605255e5751SWenping Zhang #endif 606f1c8ecceSJoseph Chen /* wait uart flush before shutdown */ 60779244e4cSJoseph Chen mdelay(5); 608f1c8ecceSJoseph Chen 609f1c8ecceSJoseph Chen /* PMIC shutdown */ 610f1c8ecceSJoseph Chen pmic_shutdown(pmic); 611f1c8ecceSJoseph Chen 612f1c8ecceSJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 613f1c8ecceSJoseph Chen continue; 614f1c8ecceSJoseph Chen } 615f1c8ecceSJoseph Chen 616f1c8ecceSJoseph Chen debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 617f1c8ecceSJoseph Chen 618f1c8ecceSJoseph Chen /* Step2: get soc and voltage */ 6194f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(fg); 620f1c8ecceSJoseph Chen if (soc < 0 || soc > 100) { 621f1c8ecceSJoseph Chen printf("get soc failed: %d\n", soc); 622f1c8ecceSJoseph Chen continue; 623f1c8ecceSJoseph Chen } 624f1c8ecceSJoseph Chen 625f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 626f1c8ecceSJoseph Chen if (voltage < 0) { 627f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 628f1c8ecceSJoseph Chen continue; 629f1c8ecceSJoseph Chen } 630f1c8ecceSJoseph Chen 631d6653c12SJoseph Chen current = fuel_gauge_get_current(fg); 632d6653c12SJoseph Chen if (current == -ENOSYS) { 633d6653c12SJoseph Chen printf("get current failed: %d\n", current); 634d6653c12SJoseph Chen continue; 635d6653c12SJoseph Chen } 636fd62311eSJoseph Chen 637ebe3d004SJoseph Chen first_poll_fg = 0; 638fd62311eSJoseph Chen local_irq_enable(); 639fd62311eSJoseph Chen 640038c1ecaSShunqing Chen if (pdata->auto_exit_charge) { 641038c1ecaSShunqing Chen /* Is able to boot now ? */ 642038c1ecaSShunqing Chen if (pdata->exit_charge_level && 643038c1ecaSShunqing Chen soc >= pdata->exit_charge_level) { 644038c1ecaSShunqing Chen printf("soc(%d%%) exit charge animation...\n", 645038c1ecaSShunqing Chen soc); 646038c1ecaSShunqing Chen break; 647038c1ecaSShunqing Chen } 648038c1ecaSShunqing Chen if (pdata->exit_charge_voltage && 649038c1ecaSShunqing Chen voltage >= pdata->exit_charge_voltage) { 650038c1ecaSShunqing Chen printf("vol(%d) exit charge animation...\n", 651038c1ecaSShunqing Chen voltage); 652038c1ecaSShunqing Chen break; 653038c1ecaSShunqing Chen } 654038c1ecaSShunqing Chen } 655038c1ecaSShunqing Chen 6565e804741SJoseph Chen show_images: 657f1c8ecceSJoseph Chen /* 658f1c8ecceSJoseph Chen * Just for debug, otherwise there will be nothing output which 659f1c8ecceSJoseph Chen * is not good to know what happen. 660f1c8ecceSJoseph Chen */ 661f1c8ecceSJoseph Chen if (!debug_start) 662f1c8ecceSJoseph Chen debug_start = get_timer(0); 663f1c8ecceSJoseph Chen if (get_timer(debug_start) > 20000) { 664f1c8ecceSJoseph Chen debug_start = get_timer(0); 665ebe3d004SJoseph Chen printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, " 666ebe3d004SJoseph Chen "online=%d, screen_on=%d\n", 667d6653c12SJoseph Chen get_timer(0)/1000, soc, voltage, 668d6653c12SJoseph Chen current, charging, screen_on); 669f1c8ecceSJoseph Chen } 670f1c8ecceSJoseph Chen 6711b3009deSJoseph Chen /* Update leds */ 6721b3009deSJoseph Chen ret = leds_update(dev, soc); 6731b3009deSJoseph Chen if (ret) 6741b3009deSJoseph Chen printf("update led failed: %d\n", ret); 6751b3009deSJoseph Chen 676f1c8ecceSJoseph Chen /* 677f23c35a8SJoseph Chen * If ever lowpower screen off, force screen_on=false, which 678f1c8ecceSJoseph Chen * means key event can't modify screen_on, only voltage higher 679f1c8ecceSJoseph Chen * then threshold can update screen_on=true; 680f1c8ecceSJoseph Chen */ 681f1c8ecceSJoseph Chen if (ever_lowpower_screen_off) 682f1c8ecceSJoseph Chen screen_on = false; 683f1c8ecceSJoseph Chen 684f1c8ecceSJoseph Chen /* 685f1c8ecceSJoseph Chen * Auto turn on screen when voltage higher than Vol screen on. 686ebe3d004SJoseph Chen * 'ever_lowpower_screen_off' means enter the while(1) loop with 687f1c8ecceSJoseph Chen * screen off. 688f1c8ecceSJoseph Chen */ 689f1c8ecceSJoseph Chen if ((ever_lowpower_screen_off) && 690d6653c12SJoseph Chen (voltage > pdata->screen_on_voltage)) { 691f1c8ecceSJoseph Chen ever_lowpower_screen_off = false; 692f1c8ecceSJoseph Chen screen_on = true; 693ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 694f1c8ecceSJoseph Chen } 695f1c8ecceSJoseph Chen 696f1c8ecceSJoseph Chen /* 697ebe3d004SJoseph Chen * IMAGE_RESET_IDX means show_idx show be update by start_idx. 698f1c8ecceSJoseph Chen * When short key pressed event trigged, we will set show_idx 699ebe3d004SJoseph Chen * as IMAGE_RESET_IDX which updates images index from start_idx 700f1c8ecceSJoseph Chen * that calculate by current soc. 701f1c8ecceSJoseph Chen */ 702ebe3d004SJoseph Chen if (show_idx == IMAGE_RESET_IDX) { 703ebe3d004SJoseph Chen for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) { 704f1c8ecceSJoseph Chen /* Find out which image we start to show */ 705f1c8ecceSJoseph Chen if ((soc >= image[i].soc) && 706f1c8ecceSJoseph Chen (soc < image[i + 1].soc)) { 707f1c8ecceSJoseph Chen start_idx = i; 708f1c8ecceSJoseph Chen break; 709f1c8ecceSJoseph Chen } 710f1c8ecceSJoseph Chen 711f1c8ecceSJoseph Chen if (soc >= 100) { 712ebe3d004SJoseph Chen start_idx = IMAGE_SOC_100_IDX(image_num); 713f1c8ecceSJoseph Chen break; 714f1c8ecceSJoseph Chen } 715f1c8ecceSJoseph Chen } 716f1c8ecceSJoseph Chen 717f1c8ecceSJoseph Chen debug("%s: show_idx=%d, screen_on=%d\n", 718f1c8ecceSJoseph Chen __func__, show_idx, screen_on); 719f1c8ecceSJoseph Chen 720f1c8ecceSJoseph Chen /* Mark start index and start time */ 721f1c8ecceSJoseph Chen show_idx = start_idx; 722f1c8ecceSJoseph Chen show_start = get_timer(0); 723f1c8ecceSJoseph Chen } 724f1c8ecceSJoseph Chen 725f1c8ecceSJoseph Chen debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 726f1c8ecceSJoseph Chen 727255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 728255e5751SWenping Zhang /* 729255e5751SWenping Zhang * Device is auto wakeup from suspend, if it's eink display, 730255e5751SWenping Zhang * screen will display the last image after suspend, so 731255e5751SWenping Zhang * we should update the image to show the approximate 732255e5751SWenping Zhang * battery power if battery is charging to next level. 733255e5751SWenping Zhang */ 734255e5751SWenping Zhang if (pdata->auto_wakeup_interval && 735255e5751SWenping Zhang priv->auto_wakeup_key_state == KEY_PRESS_DOWN && 736255e5751SWenping Zhang !screen_on) { 737255e5751SWenping Zhang if (soc >= image[old_show_idx + 1].soc && 738255e5751SWenping Zhang soc < 100) { 739255e5751SWenping Zhang int ret; 740255e5751SWenping Zhang int logo_type = EINK_LOGO_CHARGING_0; 741255e5751SWenping Zhang 742255e5751SWenping Zhang logo_type = logo_type << (old_show_idx + 1); 743255e5751SWenping Zhang ret = rockchip_eink_show_charge_logo(logo_type); 744255e5751SWenping Zhang /* 745255e5751SWenping Zhang * only change the logic if eink is 746255e5751SWenping Zhang * actually exist 747255e5751SWenping Zhang */ 748255e5751SWenping Zhang if (ret == 0) { 749255e5751SWenping Zhang printf("Update image id[%d] for eink\n", 750255e5751SWenping Zhang old_show_idx + 1); 751255e5751SWenping Zhang old_show_idx++; 752255e5751SWenping Zhang } 753255e5751SWenping Zhang } 754255e5751SWenping Zhang } 755255e5751SWenping Zhang /* 756255e5751SWenping Zhang * If battery capacity is charged to 100%, exit charging 757255e5751SWenping Zhang * animation and boot android system. 758255e5751SWenping Zhang */ 759255e5751SWenping Zhang if (soc >= 100) { 760255e5751SWenping Zhang int ret; 761255e5751SWenping Zhang int logo_type = EINK_LOGO_CHARGING_5; 762255e5751SWenping Zhang 763255e5751SWenping Zhang ret = rockchip_eink_show_charge_logo(logo_type); 764255e5751SWenping Zhang /* Only change the logic if eink is acutally exist */ 765255e5751SWenping Zhang if (ret == 0) { 766255e5751SWenping Zhang printf("battery FULL,exit charge animation\n"); 767255e5751SWenping Zhang mdelay(20); 768255e5751SWenping Zhang break; 769255e5751SWenping Zhang } 770255e5751SWenping Zhang } 771255e5751SWenping Zhang #endif 772f1c8ecceSJoseph Chen /* Step3: show images */ 773f1c8ecceSJoseph Chen if (screen_on) { 77493aee2d9SJoseph Chen /* Don't call 'charge_show_bmp' unless image changed */ 77593aee2d9SJoseph Chen if (old_show_idx != show_idx) { 776255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 777255e5751SWenping Zhang int logo_type = EINK_LOGO_CHARGING_0; 778255e5751SWenping Zhang 779255e5751SWenping Zhang rockchip_eink_show_charge_logo(logo_type << 780255e5751SWenping Zhang show_idx); 781255e5751SWenping Zhang #endif 78293aee2d9SJoseph Chen old_show_idx = show_idx; 783f1c8ecceSJoseph Chen debug("SHOW: %s\n", image[show_idx].name); 784f23c35a8SJoseph Chen charge_show_bmp(image[show_idx].name); 78593aee2d9SJoseph Chen } 786ebe3d004SJoseph Chen /* Re-calculate timeout to off screen */ 787e7f9facbSJoseph Chen if (priv->auto_screen_off_timeout == 0) 788e7f9facbSJoseph Chen priv->auto_screen_off_timeout = get_timer(0); 789f1c8ecceSJoseph Chen } else { 790e7f9facbSJoseph Chen priv->auto_screen_off_timeout = 0; 7912e68f6b5SJoseph Chen system_suspend_enter(dev); 792f1c8ecceSJoseph Chen } 793f1c8ecceSJoseph Chen 794f1c8ecceSJoseph Chen mdelay(5); 795f1c8ecceSJoseph Chen 796f1c8ecceSJoseph Chen /* Every image shows period */ 797f1c8ecceSJoseph Chen if (get_timer(show_start) > image[show_idx].period) { 798f1c8ecceSJoseph Chen show_start = get_timer(0); 799f1c8ecceSJoseph Chen /* Update to next image */ 800f1c8ecceSJoseph Chen show_idx++; 801ebe3d004SJoseph Chen if (show_idx > IMAGE_SOC_100_IDX(image_num)) 802ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 803f1c8ecceSJoseph Chen } 804f1c8ecceSJoseph Chen 805f1c8ecceSJoseph Chen debug("step4 (%d)... \n", screen_on); 806f1c8ecceSJoseph Chen 807f1c8ecceSJoseph Chen /* 808f1c8ecceSJoseph Chen * Step4: check key event. 809f1c8ecceSJoseph Chen * 810f1c8ecceSJoseph Chen * Short key event: turn on/off screen; 811f1c8ecceSJoseph Chen * Long key event: show logo and boot system or still charging. 812f1c8ecceSJoseph Chen */ 813e7f9facbSJoseph Chen key_state = check_key_press(dev); 814b177a917SJoseph Chen if (key_state == KEY_PRESS_DOWN) { 815f1c8ecceSJoseph Chen /* 816f1c8ecceSJoseph Chen * Clear current image index, and show image 817f1c8ecceSJoseph Chen * from start_idx 818f1c8ecceSJoseph Chen */ 819ebe3d004SJoseph Chen old_show_idx = IMAGE_RESET_IDX; 820ebe3d004SJoseph Chen show_idx = IMAGE_RESET_IDX; 821f1c8ecceSJoseph Chen 822f1c8ecceSJoseph Chen /* 823ebe3d004SJoseph Chen * Reverse the screen state 824f1c8ecceSJoseph Chen * 825f1c8ecceSJoseph Chen * If screen_on=false, means this short key pressed 826f1c8ecceSJoseph Chen * event turn on the screen and we need show images. 827f1c8ecceSJoseph Chen * 828f1c8ecceSJoseph Chen * If screen_on=true, means this short key pressed 829f1c8ecceSJoseph Chen * event turn off the screen and we never show images. 830f1c8ecceSJoseph Chen */ 831ebe3d004SJoseph Chen if (screen_on) { 832255e5751SWenping Zhang #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY 833255e5751SWenping Zhang int type = EINK_LOGO_CHARGING_0 << start_idx; 834255e5751SWenping Zhang /* 835255e5751SWenping Zhang * Show current battery capacity before suspend 836255e5751SWenping Zhang * if it's eink display, because eink screen 837255e5751SWenping Zhang * will continue to display the last image 838255e5751SWenping Zhang * after suspend, so user can get the 839255e5751SWenping Zhang * approximate capacity by image displayed. 840255e5751SWenping Zhang */ 841255e5751SWenping Zhang ret = rockchip_eink_show_charge_logo(type); 842255e5751SWenping Zhang /* only change the logic if eink display ok */ 843255e5751SWenping Zhang if (ret == 0) 844255e5751SWenping Zhang old_show_idx = start_idx; 845255e5751SWenping Zhang #endif 846ebe3d004SJoseph Chen charge_show_bmp(NULL); /* Turn off screen */ 847f1c8ecceSJoseph Chen screen_on = false; 8482e68f6b5SJoseph Chen priv->suspend_delay_timeout = get_timer(0); 849ebe3d004SJoseph Chen } else { 850f1c8ecceSJoseph Chen screen_on = true; 851ebe3d004SJoseph Chen } 8522e68f6b5SJoseph Chen 8532e68f6b5SJoseph Chen printf("screen %s\n", screen_on ? "on" : "off"); 854b177a917SJoseph Chen } else if (key_state == KEY_PRESS_LONG_DOWN) { 855ebe3d004SJoseph Chen /* Set screen_on=true anyway when key long pressed */ 856f1c8ecceSJoseph Chen if (!screen_on) 857f1c8ecceSJoseph Chen screen_on = true; 858f1c8ecceSJoseph Chen 8592e68f6b5SJoseph Chen printf("screen %s\n", screen_on ? "on" : "off"); 8602e68f6b5SJoseph Chen 861f1c8ecceSJoseph Chen /* Is able to boot now ? */ 862d6653c12SJoseph Chen if (soc < pdata->exit_charge_level) { 863f1c8ecceSJoseph Chen printf("soc=%d%%, threshold soc=%d%%\n", 864d6653c12SJoseph Chen soc, pdata->exit_charge_level); 865f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 866ebe3d004SJoseph Chen show_idx = IMAGE_LOWPOWER_IDX(image_num); 867f1c8ecceSJoseph Chen continue; 868f1c8ecceSJoseph Chen } 869f1c8ecceSJoseph Chen 870d6653c12SJoseph Chen if (voltage < pdata->exit_charge_voltage) { 871f1c8ecceSJoseph Chen printf("voltage=%dmv, threshold voltage=%dmv\n", 872d6653c12SJoseph Chen voltage, pdata->exit_charge_voltage); 873f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 874ebe3d004SJoseph Chen show_idx = IMAGE_LOWPOWER_IDX(image_num); 875f1c8ecceSJoseph Chen continue; 876f1c8ecceSJoseph Chen } 877f1c8ecceSJoseph Chen 878f1c8ecceSJoseph Chen /* Success exit charging */ 879f1c8ecceSJoseph Chen printf("Exit charge animation...\n"); 880f23c35a8SJoseph Chen charge_show_logo(); 881f1c8ecceSJoseph Chen break; 882f1c8ecceSJoseph Chen } else { 883f1c8ecceSJoseph Chen /* Do nothing */ 884f1c8ecceSJoseph Chen } 885f1c8ecceSJoseph Chen 886f1c8ecceSJoseph Chen debug("step5 (%d)... \n", screen_on); 887f1c8ecceSJoseph Chen 888d6653c12SJoseph Chen /* Step5: Exit by ctrl+c */ 8891367bfe3SJoseph Chen if (ctrlc()) { 890d6653c12SJoseph Chen if (voltage >= pdata->screen_on_voltage) 891f23c35a8SJoseph Chen charge_show_logo(); 8921367bfe3SJoseph Chen printf("Exit charge, due to ctrl+c\n"); 8931367bfe3SJoseph Chen break; 8941367bfe3SJoseph Chen } 895f1c8ecceSJoseph Chen } 896f1c8ecceSJoseph Chen 897e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) 898e7f9facbSJoseph Chen autowakeup_timer_uninit(); 899e7f9facbSJoseph Chen 900f1c8ecceSJoseph Chen ms = get_timer(charge_start); 901f1c8ecceSJoseph Chen if (ms >= 1000) { 902f1c8ecceSJoseph Chen sec = ms / 1000; 903f1c8ecceSJoseph Chen ms = ms % 1000; 904f1c8ecceSJoseph Chen } 905f1c8ecceSJoseph Chen 906f1c8ecceSJoseph Chen printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 907f1c8ecceSJoseph Chen sec, ms, soc, voltage); 908f1c8ecceSJoseph Chen 909f1c8ecceSJoseph Chen return 0; 910f1c8ecceSJoseph Chen } 911f1c8ecceSJoseph Chen 9124d083e3fSJoseph Chen static int fg_charger_get_device(struct udevice **fuel_gauge, 9134d083e3fSJoseph Chen struct udevice **charger) 9144d083e3fSJoseph Chen { 9154d083e3fSJoseph Chen struct udevice *dev; 9164d083e3fSJoseph Chen struct uclass *uc; 9174d083e3fSJoseph Chen int ret, cap; 9184d083e3fSJoseph Chen 9194d083e3fSJoseph Chen *fuel_gauge = NULL, 9204d083e3fSJoseph Chen *charger = NULL; 9214d083e3fSJoseph Chen 9224d083e3fSJoseph Chen ret = uclass_get(UCLASS_FG, &uc); 9234d083e3fSJoseph Chen if (ret) 9244d083e3fSJoseph Chen return ret; 9254d083e3fSJoseph Chen 9264d083e3fSJoseph Chen for (uclass_first_device(UCLASS_FG, &dev); 9274d083e3fSJoseph Chen dev; 9284d083e3fSJoseph Chen uclass_next_device(&dev)) { 9294d083e3fSJoseph Chen cap = fuel_gauge_capability(dev); 9304d083e3fSJoseph Chen if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) { 9314d083e3fSJoseph Chen *fuel_gauge = dev; 9324d083e3fSJoseph Chen *charger = NULL; 9334d083e3fSJoseph Chen } else if (cap == FG_CAP_FUEL_GAUGE) { 9344d083e3fSJoseph Chen *fuel_gauge = dev; 9354d083e3fSJoseph Chen } else if (cap == FG_CAP_CHARGER) { 9364d083e3fSJoseph Chen *charger = dev; 9374d083e3fSJoseph Chen } 9384d083e3fSJoseph Chen } 9394d083e3fSJoseph Chen 9404d083e3fSJoseph Chen return (*fuel_gauge) ? 0 : -ENODEV; 9414d083e3fSJoseph Chen } 9424d083e3fSJoseph Chen 943f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = { 944f1c8ecceSJoseph Chen .show = charge_animation_show, 945f1c8ecceSJoseph Chen }; 946f1c8ecceSJoseph Chen 947f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev) 948f1c8ecceSJoseph Chen { 949f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 950f23c35a8SJoseph Chen int ret, soc; 951f1c8ecceSJoseph Chen 952f23c35a8SJoseph Chen /* Get PMIC: used for power off system */ 953ebe3d004SJoseph Chen ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic); 954f1c8ecceSJoseph Chen if (ret) { 955cf49f6adSJoseph Chen if (ret == -ENODEV) 956cf49f6adSJoseph Chen printf("Can't find PMIC\n"); 957cf49f6adSJoseph Chen else 958f1c8ecceSJoseph Chen printf("Get UCLASS PMIC failed: %d\n", ret); 959f1c8ecceSJoseph Chen return ret; 960f1c8ecceSJoseph Chen } 961f1c8ecceSJoseph Chen 9624d083e3fSJoseph Chen /* Get fuel gauge and charger(If need) */ 9634d083e3fSJoseph Chen ret = fg_charger_get_device(&priv->fg, &priv->charger); 964f1c8ecceSJoseph Chen if (ret) { 965cf49f6adSJoseph Chen if (ret == -ENODEV) 96631ab5b3bSJoseph Chen debug("Can't find FG\n"); 967cf49f6adSJoseph Chen else 96831ab5b3bSJoseph Chen debug("Get UCLASS FG failed: %d\n", ret); 969f1c8ecceSJoseph Chen return ret; 970f1c8ecceSJoseph Chen } 971f1c8ecceSJoseph Chen 9728b436ce5SElaine Zhang /* Get rtc: used for power on */ 973ebe3d004SJoseph Chen ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc); 9748b436ce5SElaine Zhang if (ret) { 9758b436ce5SElaine Zhang if (ret == -ENODEV) 9768b436ce5SElaine Zhang debug("Can't find RTC\n"); 9778b436ce5SElaine Zhang else 9788b436ce5SElaine Zhang debug("Get UCLASS RTC failed: %d\n", ret); 9798b436ce5SElaine Zhang } 9808b436ce5SElaine Zhang 98179244e4cSJoseph Chen /* Get PWRKEY: used for wakeup and turn off/on LCD */ 98231ab5b3bSJoseph Chen if (key_read(KEY_POWER) == KEY_NOT_EXIST) { 98331ab5b3bSJoseph Chen debug("Can't find power key\n"); 984f23c35a8SJoseph Chen return -EINVAL; 985f23c35a8SJoseph Chen } 986f23c35a8SJoseph Chen 987f23c35a8SJoseph Chen /* Initialize charge current */ 9884f9cab27SJoseph Chen soc = fuel_gauge_update_get_soc(priv->fg); 989f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 99031ab5b3bSJoseph Chen debug("get soc failed: %d\n", soc); 991f23c35a8SJoseph Chen return -EINVAL; 992f23c35a8SJoseph Chen } 993f23c35a8SJoseph Chen 9941b3009deSJoseph Chen /* Get leds */ 9951b3009deSJoseph Chen #ifdef CONFIG_LED 9961b3009deSJoseph Chen ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging); 9971b3009deSJoseph Chen if (!ret) 9981b3009deSJoseph Chen printf("Found Charging LED\n"); 9991b3009deSJoseph Chen ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full); 10001b3009deSJoseph Chen if (!ret) 10011b3009deSJoseph Chen printf("Found Charging-Full LED\n"); 10021b3009deSJoseph Chen #endif 10031b3009deSJoseph Chen 1004f23c35a8SJoseph Chen /* Get charge images */ 1005f1c8ecceSJoseph Chen priv->image = image; 1006f1c8ecceSJoseph Chen priv->image_num = ARRAY_SIZE(image); 1007f1c8ecceSJoseph Chen 1008f1c8ecceSJoseph Chen printf("Enable charge animation display\n"); 1009f1c8ecceSJoseph Chen 1010f1c8ecceSJoseph Chen return 0; 1011f1c8ecceSJoseph Chen } 1012f1c8ecceSJoseph Chen 1013f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = { 1014d6653c12SJoseph Chen { .compatible = "rockchip,uboot-charge" }, 1015f1c8ecceSJoseph Chen { }, 1016f1c8ecceSJoseph Chen }; 1017f1c8ecceSJoseph Chen 1018f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = { 1019f1c8ecceSJoseph Chen .name = "charge-animation", 1020f1c8ecceSJoseph Chen .id = UCLASS_CHARGE_DISPLAY, 1021f1c8ecceSJoseph Chen .probe = charge_animation_probe, 1022f1c8ecceSJoseph Chen .of_match = charge_animation_ids, 1023f1c8ecceSJoseph Chen .ops = &charge_animation_ops, 1024f1c8ecceSJoseph Chen .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 1025f1c8ecceSJoseph Chen .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 1026f1c8ecceSJoseph Chen .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 1027f1c8ecceSJoseph Chen }; 1028