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> 14f1c8ecceSJoseph Chen #include <pwm.h> 15d3ff9cf9SKever Yang #include <irq-generic.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> 26e7f9facbSJoseph Chen #include <rk_timer_irq.h> 27f1c8ecceSJoseph Chen #include <video_rockchip.h> 28f1c8ecceSJoseph Chen 29f1c8ecceSJoseph Chen DECLARE_GLOBAL_DATA_PTR; 30f1c8ecceSJoseph Chen 31f1c8ecceSJoseph Chen #define IMAGE_SHOW_RESET -1 32f1c8ecceSJoseph Chen 33f1c8ecceSJoseph Chen struct charge_image { 34f1c8ecceSJoseph Chen const char *name; 35f1c8ecceSJoseph Chen int soc; 36f1c8ecceSJoseph Chen int period; /* ms */ 37f1c8ecceSJoseph Chen }; 38f1c8ecceSJoseph Chen 39f1c8ecceSJoseph Chen struct charge_animation_priv { 40f1c8ecceSJoseph Chen struct udevice *pmic; 41f1c8ecceSJoseph Chen struct udevice *fg; 42f1c8ecceSJoseph Chen const struct charge_image *image; 43f1c8ecceSJoseph Chen int image_num; 44e7f9facbSJoseph Chen 45e7f9facbSJoseph Chen int auto_wakeup_key_state; 46e7f9facbSJoseph Chen ulong auto_screen_off_timeout; 47f1c8ecceSJoseph Chen }; 48f1c8ecceSJoseph Chen 49f1c8ecceSJoseph Chen /* 50f1c8ecceSJoseph Chen * IF you want to use your own charge images, please: 51f1c8ecceSJoseph Chen * 52f1c8ecceSJoseph Chen * 1. Update the following 'image[]' to point to your own images; 53f1c8ecceSJoseph Chen * 2. You must set the failed image as last one and soc = -1 !!! 54f1c8ecceSJoseph Chen */ 55f1c8ecceSJoseph Chen static const struct charge_image image[] = { 56f1c8ecceSJoseph Chen { .name = "battery_0.bmp", .soc = 5, .period = 600 }, 57f1c8ecceSJoseph Chen { .name = "battery_1.bmp", .soc = 20, .period = 600 }, 58f1c8ecceSJoseph Chen { .name = "battery_2.bmp", .soc = 40, .period = 600 }, 59f1c8ecceSJoseph Chen { .name = "battery_3.bmp", .soc = 60, .period = 600 }, 60f1c8ecceSJoseph Chen { .name = "battery_4.bmp", .soc = 80, .period = 600 }, 61f1c8ecceSJoseph Chen { .name = "battery_5.bmp", .soc = 100, .period = 600 }, 62f1c8ecceSJoseph Chen { .name = "battery_fail.bmp", .soc = -1, .period = 1000 }, 63f1c8ecceSJoseph Chen }; 64f1c8ecceSJoseph Chen 65f1c8ecceSJoseph Chen static int charge_animation_ofdata_to_platdata(struct udevice *dev) 66f1c8ecceSJoseph Chen { 67f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 68f1c8ecceSJoseph Chen 69d6653c12SJoseph Chen /* charge mode */ 70d6653c12SJoseph Chen pdata->uboot_charge = 71d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0); 72d6653c12SJoseph Chen pdata->android_charge = 73d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,android-charge-on", 0); 74f1c8ecceSJoseph Chen 75d6653c12SJoseph Chen pdata->exit_charge_level = 76d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0); 77d6653c12SJoseph Chen pdata->exit_charge_voltage = 78d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0); 79f23c35a8SJoseph Chen 80f23c35a8SJoseph Chen pdata->low_power_voltage = 81f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0); 82f23c35a8SJoseph Chen 83d6653c12SJoseph Chen pdata->screen_on_voltage = 84d6653c12SJoseph Chen dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0); 85f23c35a8SJoseph Chen pdata->system_suspend = 86f23c35a8SJoseph Chen dev_read_u32_default(dev, "rockchip,system-suspend", 0); 87f1c8ecceSJoseph Chen 88e7f9facbSJoseph Chen pdata->auto_wakeup_interval = 89e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0); 90e7f9facbSJoseph Chen pdata->auto_wakeup_screen_invert = 91e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0); 92e7f9facbSJoseph Chen 93e7f9facbSJoseph Chen pdata->auto_off_screen_interval = 94e7f9facbSJoseph Chen dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15); 95e7f9facbSJoseph Chen 96f23c35a8SJoseph Chen if (pdata->screen_on_voltage > pdata->exit_charge_voltage) 97f23c35a8SJoseph Chen pdata->screen_on_voltage = pdata->exit_charge_voltage; 98f1c8ecceSJoseph Chen 99d6653c12SJoseph Chen debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n" 100f23c35a8SJoseph Chen "lp_voltage=%d%%, screen_on=%dmv\n", 101d6653c12SJoseph Chen pdata->uboot_charge, pdata->android_charge, 102d6653c12SJoseph Chen pdata->exit_charge_level, pdata->exit_charge_voltage, 103f23c35a8SJoseph Chen pdata->low_power_voltage, pdata->screen_on_voltage); 104f1c8ecceSJoseph Chen 105f1c8ecceSJoseph Chen return 0; 106f1c8ecceSJoseph Chen } 107f1c8ecceSJoseph Chen 108e7f9facbSJoseph Chen static int check_key_press(struct udevice *dev) 109f1c8ecceSJoseph Chen { 110e7f9facbSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 111e7f9facbSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 112b177a917SJoseph Chen u32 state; 113f1c8ecceSJoseph Chen 114d4e1125bSJoseph Chen state = platform_key_read(KEY_POWER); 115b177a917SJoseph Chen if (state < 0) 116f1c8ecceSJoseph Chen printf("read power key failed: %d\n", state); 117f1c8ecceSJoseph Chen 118e7f9facbSJoseph Chen /* Fixup key state for following cases */ 119e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 120e7f9facbSJoseph Chen if (pdata->auto_wakeup_screen_invert) { 121e7f9facbSJoseph Chen if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) { 122e7f9facbSJoseph Chen /* Value is updated in timer interrupt */ 123e7f9facbSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_NONE; 124e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 125e7f9facbSJoseph Chen } 126e7f9facbSJoseph Chen } 127e7f9facbSJoseph Chen } else if (pdata->auto_off_screen_interval) { 128e7f9facbSJoseph Chen if (get_timer(priv->auto_screen_off_timeout) > 129e7f9facbSJoseph Chen pdata->auto_off_screen_interval * 1000) { /* 1000ms */ 130e7f9facbSJoseph Chen state = KEY_PRESS_DOWN; 131e7f9facbSJoseph Chen printf("Auto screen off\n"); 132e7f9facbSJoseph Chen } 133e7f9facbSJoseph Chen } 134e7f9facbSJoseph Chen 135b177a917SJoseph Chen return state; 136f1c8ecceSJoseph Chen } 137f1c8ecceSJoseph Chen 138f23c35a8SJoseph Chen static int system_suspend_enter(struct charge_animation_pdata *pdata) 139b177a917SJoseph Chen { 140b177a917SJoseph Chen /* 141b177a917SJoseph Chen * TODO: enter low power mode: 142b177a917SJoseph Chen * 3. auto turn off screen when timout; 143b177a917SJoseph Chen * 4. power key wakeup; 144f23c35a8SJoseph Chen * 5. timer period wakeup for pmic fg 145b177a917SJoseph Chen */ 146f23c35a8SJoseph Chen if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) { 147b177a917SJoseph Chen printf("\nSystem suspend: "); 148b177a917SJoseph Chen putc('1'); 149b177a917SJoseph Chen local_irq_disable(); 150b177a917SJoseph Chen putc('2'); 151b177a917SJoseph Chen irqs_suspend(); 152b177a917SJoseph Chen putc('3'); 15388949342SJoseph Chen device_suspend(); 15488949342SJoseph Chen putc('4'); 155b177a917SJoseph Chen putc('\n'); 156b177a917SJoseph Chen 157b177a917SJoseph Chen /* Trap into ATF for low power mode */ 158b177a917SJoseph Chen cpu_suspend(0, psci_system_suspend); 159b177a917SJoseph Chen 160b177a917SJoseph Chen putc('\n'); 16188949342SJoseph Chen putc('4'); 16288949342SJoseph Chen device_resume(); 163b177a917SJoseph Chen putc('3'); 164b177a917SJoseph Chen irqs_resume(); 165b177a917SJoseph Chen putc('2'); 166b177a917SJoseph Chen local_irq_enable(); 167b177a917SJoseph Chen putc('1'); 168b177a917SJoseph Chen putc('\n'); 1698fc5ae06SJoseph Chen } else { 1708fc5ae06SJoseph Chen printf("\nWfi\n"); 1718fc5ae06SJoseph Chen wfi(); 1728fc5ae06SJoseph Chen } 173b177a917SJoseph Chen 174b177a917SJoseph Chen /* 175b177a917SJoseph Chen * We must wait for key release event finish, otherwise 176b177a917SJoseph Chen * we may read key state too early. 177b177a917SJoseph Chen */ 178b177a917SJoseph Chen mdelay(300); 179b177a917SJoseph Chen 180b177a917SJoseph Chen return 0; 181f1c8ecceSJoseph Chen } 182f1c8ecceSJoseph Chen 183*79244e4cSJoseph Chen static void timer_irq_handler(int irq, void *data) 184*79244e4cSJoseph Chen { 185*79244e4cSJoseph Chen struct udevice *dev = data; 186*79244e4cSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 187*79244e4cSJoseph Chen static long long count; 188*79244e4cSJoseph Chen 189*79244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 190*79244e4cSJoseph Chen 191*79244e4cSJoseph Chen priv->auto_wakeup_key_state = KEY_PRESS_DOWN; 192*79244e4cSJoseph Chen printf("auto wakeup count: %lld\n", ++count); 193*79244e4cSJoseph Chen } 194*79244e4cSJoseph Chen 195*79244e4cSJoseph Chen static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) 196*79244e4cSJoseph Chen { 197*79244e4cSJoseph Chen uint64_t period = 24000000ULL * seconds; 198*79244e4cSJoseph Chen 199*79244e4cSJoseph Chen /* Disable before conifg */ 200*79244e4cSJoseph Chen writel(0, TIMER_BASE + TIMER_CTRL); 201*79244e4cSJoseph Chen 202*79244e4cSJoseph Chen /* Config */ 203*79244e4cSJoseph Chen writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0); 204*79244e4cSJoseph Chen writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1); 205*79244e4cSJoseph Chen writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS); 206*79244e4cSJoseph Chen writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL); 207*79244e4cSJoseph Chen 208*79244e4cSJoseph Chen /* IRQ */ 209*79244e4cSJoseph Chen irq_install_handler(TIMER_IRQ, timer_irq_handler, dev); 210*79244e4cSJoseph Chen irq_handler_enable(TIMER_IRQ); 211*79244e4cSJoseph Chen } 212*79244e4cSJoseph Chen 213*79244e4cSJoseph Chen static void autowakeup_timer_uninit(void) 214*79244e4cSJoseph Chen { 215*79244e4cSJoseph Chen irq_free_handler(TIMER_IRQ); 216*79244e4cSJoseph Chen } 217*79244e4cSJoseph Chen 218f23c35a8SJoseph Chen #ifdef CONFIG_DRM_ROCKCHIP 219f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) 220f23c35a8SJoseph Chen { 221f23c35a8SJoseph Chen rockchip_show_bmp(name); 222f23c35a8SJoseph Chen } 223f23c35a8SJoseph Chen 224f23c35a8SJoseph Chen static void charge_show_logo(void) 225f23c35a8SJoseph Chen { 226f23c35a8SJoseph Chen rockchip_show_logo(); 227f23c35a8SJoseph Chen } 228f23c35a8SJoseph Chen #else 229f23c35a8SJoseph Chen static void charge_show_bmp(const char *name) {} 230f23c35a8SJoseph Chen static void charge_show_logo(void) {} 231f23c35a8SJoseph Chen #endif 232f23c35a8SJoseph Chen 233f23c35a8SJoseph Chen static int charge_extrem_low_power(struct udevice *dev) 234f23c35a8SJoseph Chen { 235f23c35a8SJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 236f23c35a8SJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 237f23c35a8SJoseph Chen struct udevice *pmic = priv->pmic; 238f23c35a8SJoseph Chen struct udevice *fg = priv->fg; 239f23c35a8SJoseph Chen int voltage, soc, charging = 1; 240*79244e4cSJoseph Chen static int timer_initialized; 241f23c35a8SJoseph Chen 242f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 243f23c35a8SJoseph Chen if (voltage < 0) 244f23c35a8SJoseph Chen return -EINVAL; 245f23c35a8SJoseph Chen 246f23c35a8SJoseph Chen while (voltage < pdata->low_power_voltage + 50) { 247f23c35a8SJoseph Chen /* Check charger online */ 248f23c35a8SJoseph Chen charging = fuel_gauge_get_chrg_online(fg); 249f23c35a8SJoseph Chen if (charging <= 0) { 250*79244e4cSJoseph Chen printf("%s: Not charging, online=%d. Shutdown...\n", 251*79244e4cSJoseph Chen __func__, charging); 252f23c35a8SJoseph Chen /* wait uart flush before shutdown */ 253*79244e4cSJoseph Chen mdelay(5); 254f23c35a8SJoseph Chen /* PMIC shutdown */ 255f23c35a8SJoseph Chen pmic_shutdown(pmic); 256f23c35a8SJoseph Chen 257f23c35a8SJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 258f23c35a8SJoseph Chen continue; 259f23c35a8SJoseph Chen } 260f23c35a8SJoseph Chen 261*79244e4cSJoseph Chen /* Enable auto wakeup */ 262*79244e4cSJoseph Chen if (!timer_initialized) { 263*79244e4cSJoseph Chen timer_initialized = 1; 264*79244e4cSJoseph Chen autowakeup_timer_init(dev, 5); 265*79244e4cSJoseph Chen } 266*79244e4cSJoseph Chen 267f23c35a8SJoseph Chen /* 268f23c35a8SJoseph Chen * Just for fuel gauge to update something important, 269f23c35a8SJoseph Chen * including charge current, coulometer or other. 270f23c35a8SJoseph Chen */ 271f23c35a8SJoseph Chen soc = fuel_gauge_get_soc(fg); 272f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 273f23c35a8SJoseph Chen printf("get soc failed: %d\n", soc); 274f23c35a8SJoseph Chen continue; 275f23c35a8SJoseph Chen } 276f23c35a8SJoseph Chen 277f23c35a8SJoseph Chen printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n", 278f23c35a8SJoseph Chen pdata->low_power_voltage, voltage); 279f23c35a8SJoseph Chen 280f23c35a8SJoseph Chen /* System suspend */ 281f23c35a8SJoseph Chen system_suspend_enter(pdata); 282f23c35a8SJoseph Chen 283f23c35a8SJoseph Chen /* Update voltage */ 284f23c35a8SJoseph Chen voltage = fuel_gauge_get_voltage(fg); 285f23c35a8SJoseph Chen if (voltage < 0) { 286f23c35a8SJoseph Chen printf("get voltage failed: %d\n", voltage); 287f23c35a8SJoseph Chen continue; 288f23c35a8SJoseph Chen } 289f23c35a8SJoseph Chen } 290f23c35a8SJoseph Chen 291*79244e4cSJoseph Chen autowakeup_timer_uninit(); 292*79244e4cSJoseph Chen 293f23c35a8SJoseph Chen return 0; 294f23c35a8SJoseph Chen } 295f23c35a8SJoseph Chen 296f1c8ecceSJoseph Chen static int charge_animation_show(struct udevice *dev) 297f1c8ecceSJoseph Chen { 298f1c8ecceSJoseph Chen struct charge_animation_pdata *pdata = dev_get_platdata(dev); 299f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 300f1c8ecceSJoseph Chen const struct charge_image *image = priv->image; 301f1c8ecceSJoseph Chen struct udevice *pmic = priv->pmic; 302f1c8ecceSJoseph Chen struct udevice *fg = priv->fg; 303a8b9d026SJoseph Chen const char *preboot = env_get("preboot"); 304f1c8ecceSJoseph Chen int image_num = priv->image_num; 305f1c8ecceSJoseph Chen bool ever_lowpower_screen_off = false; 306f1c8ecceSJoseph Chen bool screen_on = true; 307f1c8ecceSJoseph Chen ulong show_start = 0, charge_start = 0, debug_start = 0; 308f1c8ecceSJoseph Chen ulong ms = 0, sec = 0; 309f1c8ecceSJoseph Chen int start_idx = 0, show_idx = -1; 310d6653c12SJoseph Chen int soc, voltage, current, key_state; 311f23c35a8SJoseph Chen int i, charging = 1, ret; 3128f9ff705SJoseph Chen int boot_mode; 313f1c8ecceSJoseph Chen 314f23c35a8SJoseph Chen /* 315f23c35a8SJoseph Chen * Check sequence: 316f23c35a8SJoseph Chen * 317f23c35a8SJoseph Chen * 1. Extrem low power charge? 318f23c35a8SJoseph Chen * 2. Preboot cmd? 319f23c35a8SJoseph Chen * 3. Valid boot mode? 320f23c35a8SJoseph Chen * 4. U-Boot charge enabled by dts config? 321f23c35a8SJoseph Chen * 5. Screen off before charge? 322f23c35a8SJoseph Chen * 6. Enter charge ! 323f23c35a8SJoseph Chen * 324f23c35a8SJoseph Chen */ 325f23c35a8SJoseph Chen /* Extrem low power charge */ 326f23c35a8SJoseph Chen ret = charge_extrem_low_power(dev); 327f23c35a8SJoseph Chen if (ret < 0) { 328f23c35a8SJoseph Chen printf("extrem low power charge failed, ret=%d\n", ret); 329f23c35a8SJoseph Chen return ret; 330f23c35a8SJoseph Chen } 331f23c35a8SJoseph Chen 332a8b9d026SJoseph Chen /* If there is preboot command, exit */ 333a8b9d026SJoseph Chen if (preboot) { 334a8b9d026SJoseph Chen debug("preboot: %s\n", preboot); 335a8b9d026SJoseph Chen return 0; 336a8b9d026SJoseph Chen } 337a8b9d026SJoseph Chen 338f23c35a8SJoseph Chen /* Not valid charge mode, exit */ 339d3ff9cf9SKever Yang #ifdef CONFIG_RKIMG_BOOTLOADER 3408f9ff705SJoseph Chen boot_mode = rockchip_get_boot_mode(); 341221b5220SJoseph Chen if ((boot_mode != BOOT_MODE_CHARGING) && 342221b5220SJoseph Chen (boot_mode != BOOT_MODE_UNDEFINE)) { 3438f9ff705SJoseph Chen debug("exit charge, due to boot mode: %d\n", boot_mode); 3448f9ff705SJoseph Chen return 0; 3458f9ff705SJoseph Chen } 3468f9ff705SJoseph Chen #endif 347221b5220SJoseph Chen 348616056c9Sshengfei Xu /* Not charger online, exit */ 349616056c9Sshengfei Xu charging = fuel_gauge_get_chrg_online(fg); 350616056c9Sshengfei Xu if (charging <= 0) 351616056c9Sshengfei Xu return 0; 352616056c9Sshengfei Xu 353f23c35a8SJoseph Chen /* Enter android charge, set property for kernel */ 354d6653c12SJoseph Chen if (pdata->android_charge) { 355d6653c12SJoseph Chen env_update("bootargs", "androidboot.mode=charger"); 356d6653c12SJoseph Chen printf("Android charge mode\n"); 357d6653c12SJoseph Chen } 358d6653c12SJoseph Chen 359f23c35a8SJoseph Chen /* Not enable U-Boot charge, exit */ 360d6653c12SJoseph Chen if (!pdata->uboot_charge) 361d6653c12SJoseph Chen return 0; 362d6653c12SJoseph Chen 363f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 364f1c8ecceSJoseph Chen if (voltage < 0) { 365f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 366f1c8ecceSJoseph Chen return -EINVAL; 367f1c8ecceSJoseph Chen } 368f1c8ecceSJoseph Chen 369f1c8ecceSJoseph Chen /* If low power, turn off screen */ 370d6653c12SJoseph Chen if (voltage <= pdata->screen_on_voltage + 50) { 371f1c8ecceSJoseph Chen screen_on = false; 372f1c8ecceSJoseph Chen ever_lowpower_screen_off = true; 373f23c35a8SJoseph Chen charge_show_bmp(NULL); 374f1c8ecceSJoseph Chen } 375f1c8ecceSJoseph Chen 376e7f9facbSJoseph Chen /* Auto wakeup */ 377e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) { 378e7f9facbSJoseph Chen printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval); 379*79244e4cSJoseph Chen autowakeup_timer_init(dev, pdata->auto_wakeup_interval); 380e7f9facbSJoseph Chen } 381e7f9facbSJoseph Chen 382d6653c12SJoseph Chen printf("Enter U-Boot charging mode\n"); 383f1c8ecceSJoseph Chen 384d6653c12SJoseph Chen charge_start = get_timer(0); 385e7f9facbSJoseph Chen 386f1c8ecceSJoseph Chen /* Charging ! */ 387f1c8ecceSJoseph Chen while (1) { 388f1c8ecceSJoseph Chen debug("step1 (%d)... \n", screen_on); 389f1c8ecceSJoseph Chen 390f1c8ecceSJoseph Chen /* Step1: Is charging now ? */ 391f1c8ecceSJoseph Chen charging = fuel_gauge_get_chrg_online(fg); 392f1c8ecceSJoseph Chen if (charging <= 0) { 393f1c8ecceSJoseph Chen printf("Not charging, online=%d. Shutdown...\n", 394f1c8ecceSJoseph Chen charging); 395f1c8ecceSJoseph Chen 396f1c8ecceSJoseph Chen /* wait uart flush before shutdown */ 397*79244e4cSJoseph Chen mdelay(5); 398f1c8ecceSJoseph Chen 399f1c8ecceSJoseph Chen /* PMIC shutdown */ 400f1c8ecceSJoseph Chen pmic_shutdown(pmic); 401f1c8ecceSJoseph Chen 402f1c8ecceSJoseph Chen printf("Cpu should never reach here, shutdown failed !\n"); 403f1c8ecceSJoseph Chen continue; 404f1c8ecceSJoseph Chen } 405f1c8ecceSJoseph Chen 406f1c8ecceSJoseph Chen debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx); 407f1c8ecceSJoseph Chen 408f1c8ecceSJoseph Chen /* Step2: get soc and voltage */ 409f1c8ecceSJoseph Chen soc = fuel_gauge_get_soc(fg); 410f1c8ecceSJoseph Chen if (soc < 0 || soc > 100) { 411f1c8ecceSJoseph Chen printf("get soc failed: %d\n", soc); 412f1c8ecceSJoseph Chen continue; 413f1c8ecceSJoseph Chen } 414f1c8ecceSJoseph Chen 415f1c8ecceSJoseph Chen voltage = fuel_gauge_get_voltage(fg); 416f1c8ecceSJoseph Chen if (voltage < 0) { 417f1c8ecceSJoseph Chen printf("get voltage failed: %d\n", voltage); 418f1c8ecceSJoseph Chen continue; 419f1c8ecceSJoseph Chen } 420f1c8ecceSJoseph Chen 421d6653c12SJoseph Chen current = fuel_gauge_get_current(fg); 422d6653c12SJoseph Chen if (current == -ENOSYS) { 423d6653c12SJoseph Chen printf("get current failed: %d\n", current); 424d6653c12SJoseph Chen continue; 425d6653c12SJoseph Chen } 426d6653c12SJoseph Chen 427f1c8ecceSJoseph Chen /* 428f1c8ecceSJoseph Chen * Just for debug, otherwise there will be nothing output which 429f1c8ecceSJoseph Chen * is not good to know what happen. 430f1c8ecceSJoseph Chen */ 431f1c8ecceSJoseph Chen if (!debug_start) 432f1c8ecceSJoseph Chen debug_start = get_timer(0); 433f1c8ecceSJoseph Chen if (get_timer(debug_start) > 20000) { 434f1c8ecceSJoseph Chen debug_start = get_timer(0); 435d6653c12SJoseph Chen printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, online=%d, screen_on=%d\n", 436d6653c12SJoseph Chen get_timer(0)/1000, soc, voltage, 437d6653c12SJoseph Chen current, charging, screen_on); 438f1c8ecceSJoseph Chen } 439f1c8ecceSJoseph Chen 440f1c8ecceSJoseph Chen /* 441f23c35a8SJoseph Chen * If ever lowpower screen off, force screen_on=false, which 442f1c8ecceSJoseph Chen * means key event can't modify screen_on, only voltage higher 443f1c8ecceSJoseph Chen * then threshold can update screen_on=true; 444f1c8ecceSJoseph Chen */ 445f1c8ecceSJoseph Chen if (ever_lowpower_screen_off) 446f1c8ecceSJoseph Chen screen_on = false; 447f1c8ecceSJoseph Chen 448f1c8ecceSJoseph Chen /* 449f1c8ecceSJoseph Chen * Auto turn on screen when voltage higher than Vol screen on. 450f1c8ecceSJoseph Chen * 'ever_lowpower_screen_off' means enter while loop with 451f1c8ecceSJoseph Chen * screen off. 452f1c8ecceSJoseph Chen */ 453f1c8ecceSJoseph Chen if ((ever_lowpower_screen_off) && 454d6653c12SJoseph Chen (voltage > pdata->screen_on_voltage)) { 455f1c8ecceSJoseph Chen ever_lowpower_screen_off = false; 456f1c8ecceSJoseph Chen screen_on = true; 457f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 458f1c8ecceSJoseph Chen } 459f1c8ecceSJoseph Chen 460f1c8ecceSJoseph Chen /* 461f1c8ecceSJoseph Chen * IMAGE_SHOW_RESET means show_idx show be update by start_idx. 462f1c8ecceSJoseph Chen * When short key pressed event trigged, we will set show_idx 463f1c8ecceSJoseph Chen * as IMAGE_SHOW_RESET which updates images index from start_idx 464f1c8ecceSJoseph Chen * that calculate by current soc. 465f1c8ecceSJoseph Chen */ 466f1c8ecceSJoseph Chen if (show_idx == IMAGE_SHOW_RESET) { 467f1c8ecceSJoseph Chen for (i = 0; i < image_num - 2; i++) { 468f1c8ecceSJoseph Chen /* Find out which image we start to show */ 469f1c8ecceSJoseph Chen if ((soc >= image[i].soc) && 470f1c8ecceSJoseph Chen (soc < image[i + 1].soc)) { 471f1c8ecceSJoseph Chen start_idx = i; 472f1c8ecceSJoseph Chen break; 473f1c8ecceSJoseph Chen } 474f1c8ecceSJoseph Chen 475f1c8ecceSJoseph Chen if (soc >= 100) { 476f1c8ecceSJoseph Chen start_idx = image_num - 2; 477f1c8ecceSJoseph Chen break; 478f1c8ecceSJoseph Chen } 479f1c8ecceSJoseph Chen } 480f1c8ecceSJoseph Chen 481f1c8ecceSJoseph Chen debug("%s: show_idx=%d, screen_on=%d\n", 482f1c8ecceSJoseph Chen __func__, show_idx, screen_on); 483f1c8ecceSJoseph Chen 484f1c8ecceSJoseph Chen /* Mark start index and start time */ 485f1c8ecceSJoseph Chen show_idx = start_idx; 486f1c8ecceSJoseph Chen show_start = get_timer(0); 487f1c8ecceSJoseph Chen } 488f1c8ecceSJoseph Chen 489f1c8ecceSJoseph Chen debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx); 490f1c8ecceSJoseph Chen 491f1c8ecceSJoseph Chen /* Step3: show images */ 492f1c8ecceSJoseph Chen if (screen_on) { 493f1c8ecceSJoseph Chen debug("SHOW: %s\n", image[show_idx].name); 494f23c35a8SJoseph Chen charge_show_bmp(image[show_idx].name); 495e7f9facbSJoseph Chen 496e7f9facbSJoseph Chen /* Re calculate timeout to off screen */ 497e7f9facbSJoseph Chen if (priv->auto_screen_off_timeout == 0) 498e7f9facbSJoseph Chen priv->auto_screen_off_timeout = get_timer(0); 499f1c8ecceSJoseph Chen } else { 500e7f9facbSJoseph Chen priv->auto_screen_off_timeout = 0; 501e7f9facbSJoseph Chen 502f23c35a8SJoseph Chen system_suspend_enter(pdata); 503f1c8ecceSJoseph Chen } 504f1c8ecceSJoseph Chen 505f1c8ecceSJoseph Chen mdelay(5); 506f1c8ecceSJoseph Chen 507f1c8ecceSJoseph Chen /* Every image shows period */ 508f1c8ecceSJoseph Chen if (get_timer(show_start) > image[show_idx].period) { 509f1c8ecceSJoseph Chen show_start = get_timer(0); 510f1c8ecceSJoseph Chen /* Update to next image */ 511f1c8ecceSJoseph Chen show_idx++; 512f1c8ecceSJoseph Chen if (show_idx > (image_num - 2)) 513f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 514f1c8ecceSJoseph Chen } 515f1c8ecceSJoseph Chen 516f1c8ecceSJoseph Chen debug("step4 (%d)... \n", screen_on); 517f1c8ecceSJoseph Chen 518f1c8ecceSJoseph Chen /* 519f1c8ecceSJoseph Chen * Step4: check key event. 520f1c8ecceSJoseph Chen * 521f1c8ecceSJoseph Chen * Short key event: turn on/off screen; 522f1c8ecceSJoseph Chen * Long key event: show logo and boot system or still charging. 523f1c8ecceSJoseph Chen */ 524e7f9facbSJoseph Chen key_state = check_key_press(dev); 525b177a917SJoseph Chen if (key_state == KEY_PRESS_DOWN) { 526f1c8ecceSJoseph Chen /* NULL means show nothing, ie. turn off screen */ 527f1c8ecceSJoseph Chen if (screen_on) 528f23c35a8SJoseph Chen charge_show_bmp(NULL); 529f1c8ecceSJoseph Chen 530f1c8ecceSJoseph Chen /* 531f1c8ecceSJoseph Chen * Clear current image index, and show image 532f1c8ecceSJoseph Chen * from start_idx 533f1c8ecceSJoseph Chen */ 534f1c8ecceSJoseph Chen show_idx = IMAGE_SHOW_RESET; 535f1c8ecceSJoseph Chen 536f1c8ecceSJoseph Chen /* 537f23c35a8SJoseph Chen * We turn off screen by charge_show_bmp(NULL), so we 538f1c8ecceSJoseph Chen * should tell while loop to stop show images any more. 539f1c8ecceSJoseph Chen * 540f1c8ecceSJoseph Chen * If screen_on=false, means this short key pressed 541f1c8ecceSJoseph Chen * event turn on the screen and we need show images. 542f1c8ecceSJoseph Chen * 543f1c8ecceSJoseph Chen * If screen_on=true, means this short key pressed 544f1c8ecceSJoseph Chen * event turn off the screen and we never show images. 545f1c8ecceSJoseph Chen */ 546f1c8ecceSJoseph Chen if (screen_on) 547f1c8ecceSJoseph Chen screen_on = false; 548f1c8ecceSJoseph Chen else 549f1c8ecceSJoseph Chen screen_on = true; 550b177a917SJoseph Chen } else if (key_state == KEY_PRESS_LONG_DOWN) { 551f1c8ecceSJoseph Chen /* Only long pressed while screen off needs screen_on true */ 552f1c8ecceSJoseph Chen if (!screen_on) 553f1c8ecceSJoseph Chen screen_on = true; 554f1c8ecceSJoseph Chen 555f1c8ecceSJoseph Chen /* Is able to boot now ? */ 556d6653c12SJoseph Chen if (soc < pdata->exit_charge_level) { 557f1c8ecceSJoseph Chen printf("soc=%d%%, threshold soc=%d%%\n", 558d6653c12SJoseph Chen soc, pdata->exit_charge_level); 559f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 560f1c8ecceSJoseph Chen show_idx = image_num - 1; 561f1c8ecceSJoseph Chen continue; 562f1c8ecceSJoseph Chen } 563f1c8ecceSJoseph Chen 564d6653c12SJoseph Chen if (voltage < pdata->exit_charge_voltage) { 565f1c8ecceSJoseph Chen printf("voltage=%dmv, threshold voltage=%dmv\n", 566d6653c12SJoseph Chen voltage, pdata->exit_charge_voltage); 567f1c8ecceSJoseph Chen printf("Low power, unable to boot, charging...\n"); 568f1c8ecceSJoseph Chen show_idx = image_num - 1; 569f1c8ecceSJoseph Chen continue; 570f1c8ecceSJoseph Chen } 571f1c8ecceSJoseph Chen 572f1c8ecceSJoseph Chen /* Success exit charging */ 573f1c8ecceSJoseph Chen printf("Exit charge animation...\n"); 574f23c35a8SJoseph Chen charge_show_logo(); 575f1c8ecceSJoseph Chen break; 576f1c8ecceSJoseph Chen } else { 577f1c8ecceSJoseph Chen /* Do nothing */ 578f1c8ecceSJoseph Chen } 579f1c8ecceSJoseph Chen 580f1c8ecceSJoseph Chen debug("step5 (%d)... \n", screen_on); 581f1c8ecceSJoseph Chen 582d6653c12SJoseph Chen /* Step5: Exit by ctrl+c */ 5831367bfe3SJoseph Chen if (ctrlc()) { 584d6653c12SJoseph Chen if (voltage >= pdata->screen_on_voltage) 585f23c35a8SJoseph Chen charge_show_logo(); 5861367bfe3SJoseph Chen printf("Exit charge, due to ctrl+c\n"); 5871367bfe3SJoseph Chen break; 5881367bfe3SJoseph Chen } 589f1c8ecceSJoseph Chen } 590f1c8ecceSJoseph Chen 591e7f9facbSJoseph Chen if (pdata->auto_wakeup_interval) 592e7f9facbSJoseph Chen autowakeup_timer_uninit(); 593e7f9facbSJoseph Chen 594f1c8ecceSJoseph Chen ms = get_timer(charge_start); 595f1c8ecceSJoseph Chen if (ms >= 1000) { 596f1c8ecceSJoseph Chen sec = ms / 1000; 597f1c8ecceSJoseph Chen ms = ms % 1000; 598f1c8ecceSJoseph Chen } 599f1c8ecceSJoseph Chen 600f1c8ecceSJoseph Chen printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n", 601f1c8ecceSJoseph Chen sec, ms, soc, voltage); 602f1c8ecceSJoseph Chen 603f1c8ecceSJoseph Chen return 0; 604f1c8ecceSJoseph Chen } 605f1c8ecceSJoseph Chen 606f1c8ecceSJoseph Chen static const struct dm_charge_display_ops charge_animation_ops = { 607f1c8ecceSJoseph Chen .show = charge_animation_show, 608f1c8ecceSJoseph Chen }; 609f1c8ecceSJoseph Chen 610f1c8ecceSJoseph Chen static int charge_animation_probe(struct udevice *dev) 611f1c8ecceSJoseph Chen { 612f1c8ecceSJoseph Chen struct charge_animation_priv *priv = dev_get_priv(dev); 613d4e1125bSJoseph Chen struct udevice *fg, *pmic; 614f23c35a8SJoseph Chen int ret, soc; 615f1c8ecceSJoseph Chen 616f23c35a8SJoseph Chen /* Get PMIC: used for power off system */ 617f1c8ecceSJoseph Chen ret = uclass_get_device(UCLASS_PMIC, 0, &pmic); 618f1c8ecceSJoseph Chen if (ret) { 619f1c8ecceSJoseph Chen printf("Get UCLASS PMIC failed: %d\n", ret); 620f1c8ecceSJoseph Chen return ret; 621f1c8ecceSJoseph Chen } 622f1c8ecceSJoseph Chen priv->pmic = pmic; 623f1c8ecceSJoseph Chen 624f23c35a8SJoseph Chen /* Get fuel gauge: used for charging */ 625f1c8ecceSJoseph Chen ret = uclass_get_device(UCLASS_FG, 0, &fg); 626f1c8ecceSJoseph Chen if (ret) { 627f1c8ecceSJoseph Chen printf("Get UCLASS FG failed: %d\n", ret); 628f1c8ecceSJoseph Chen return ret; 629f1c8ecceSJoseph Chen } 630f1c8ecceSJoseph Chen priv->fg = fg; 631f1c8ecceSJoseph Chen 632*79244e4cSJoseph Chen /* Get PWRKEY: used for wakeup and turn off/on LCD */ 633f23c35a8SJoseph Chen ret = platform_key_read(KEY_POWER); 634f23c35a8SJoseph Chen if (ret == KEY_NOT_EXIST) { 635f23c35a8SJoseph Chen printf("Can't find power key\n"); 636f23c35a8SJoseph Chen return -EINVAL; 637f23c35a8SJoseph Chen } 638f23c35a8SJoseph Chen 639f23c35a8SJoseph Chen /* Initialize charge current */ 640f23c35a8SJoseph Chen soc = fuel_gauge_get_soc(fg); 641f23c35a8SJoseph Chen if (soc < 0 || soc > 100) { 642f23c35a8SJoseph Chen printf("get soc failed: %d\n", soc); 643f23c35a8SJoseph Chen return -EINVAL; 644f23c35a8SJoseph Chen } 645f23c35a8SJoseph Chen 646f23c35a8SJoseph Chen /* Get charge images */ 647f1c8ecceSJoseph Chen priv->image = image; 648f1c8ecceSJoseph Chen priv->image_num = ARRAY_SIZE(image); 649f1c8ecceSJoseph Chen 650f1c8ecceSJoseph Chen printf("Enable charge animation display\n"); 651f1c8ecceSJoseph Chen 652f1c8ecceSJoseph Chen return 0; 653f1c8ecceSJoseph Chen } 654f1c8ecceSJoseph Chen 655f1c8ecceSJoseph Chen static const struct udevice_id charge_animation_ids[] = { 656d6653c12SJoseph Chen { .compatible = "rockchip,uboot-charge" }, 657f1c8ecceSJoseph Chen { }, 658f1c8ecceSJoseph Chen }; 659f1c8ecceSJoseph Chen 660f1c8ecceSJoseph Chen U_BOOT_DRIVER(charge_animation) = { 661f1c8ecceSJoseph Chen .name = "charge-animation", 662f1c8ecceSJoseph Chen .id = UCLASS_CHARGE_DISPLAY, 663f1c8ecceSJoseph Chen .probe = charge_animation_probe, 664f1c8ecceSJoseph Chen .of_match = charge_animation_ids, 665f1c8ecceSJoseph Chen .ops = &charge_animation_ops, 666f1c8ecceSJoseph Chen .ofdata_to_platdata = charge_animation_ofdata_to_platdata, 667f1c8ecceSJoseph Chen .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata), 668f1c8ecceSJoseph Chen .priv_auto_alloc_size = sizeof(struct charge_animation_priv), 669f1c8ecceSJoseph Chen }; 670