1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * (C) Copyright 2017 Rockchip Electronics Co., Ltd
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <asm/io.h>
8*4882a593Smuzhiyun #include <common.h>
9*4882a593Smuzhiyun #include <boot_rkimg.h>
10*4882a593Smuzhiyun #include <console.h>
11*4882a593Smuzhiyun #include <dm.h>
12*4882a593Smuzhiyun #include <errno.h>
13*4882a593Smuzhiyun #include <key.h>
14*4882a593Smuzhiyun #include <led.h>
15*4882a593Smuzhiyun #include <rtc.h>
16*4882a593Smuzhiyun #include <pwm.h>
17*4882a593Smuzhiyun #include <asm/arch/rockchip_smccc.h>
18*4882a593Smuzhiyun #include <asm/suspend.h>
19*4882a593Smuzhiyun #include <linux/input.h>
20*4882a593Smuzhiyun #include <power/charge_display.h>
21*4882a593Smuzhiyun #include <power/charge_animation.h>
22*4882a593Smuzhiyun #include <power/rockchip_pm.h>
23*4882a593Smuzhiyun #include <power/fuel_gauge.h>
24*4882a593Smuzhiyun #include <power/pmic.h>
25*4882a593Smuzhiyun #include <power/rk8xx_pmic.h>
26*4882a593Smuzhiyun #include <power/regulator.h>
27*4882a593Smuzhiyun #include <video_rockchip.h>
28*4882a593Smuzhiyun #ifdef CONFIG_IRQ
29*4882a593Smuzhiyun #include <irq-generic.h>
30*4882a593Smuzhiyun #include <rk_timer_irq.h>
31*4882a593Smuzhiyun #endif
32*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
33*4882a593Smuzhiyun #include <rk_eink.h>
34*4882a593Smuzhiyun #endif
35*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun #define IMAGE_RECALC_IDX -1
38*4882a593Smuzhiyun #define IMAGE_SOC_100_IDX(n) ((n) - 2)
39*4882a593Smuzhiyun #define IMAGE_LOWPOWER_IDX(n) ((n) - 1)
40*4882a593Smuzhiyun #define SYSTEM_SUSPEND_DELAY_MS 5000
41*4882a593Smuzhiyun #define FUEL_GAUGE_POLL_MS 1000
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun #define LED_CHARGING_NAME "battery_charging"
44*4882a593Smuzhiyun #define LED_CHARGING_FULL_NAME "battery_full"
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun struct charge_image {
47*4882a593Smuzhiyun const char *name;
48*4882a593Smuzhiyun int soc;
49*4882a593Smuzhiyun int period; /* ms */
50*4882a593Smuzhiyun };
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun struct charge_animation_priv {
53*4882a593Smuzhiyun struct udevice *pmic;
54*4882a593Smuzhiyun struct udevice *fg;
55*4882a593Smuzhiyun struct udevice *charger;
56*4882a593Smuzhiyun struct udevice *rtc;
57*4882a593Smuzhiyun #ifdef CONFIG_LED
58*4882a593Smuzhiyun struct udevice *led_charging;
59*4882a593Smuzhiyun struct udevice *led_full;
60*4882a593Smuzhiyun #endif
61*4882a593Smuzhiyun const struct charge_image *image;
62*4882a593Smuzhiyun int image_num;
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun int auto_wakeup_key_state;
65*4882a593Smuzhiyun ulong auto_screen_off_timeout; /* ms */
66*4882a593Smuzhiyun ulong suspend_delay_timeout; /* ms */
67*4882a593Smuzhiyun };
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /*
70*4882a593Smuzhiyun * IF you want to use your own charge images, please:
71*4882a593Smuzhiyun *
72*4882a593Smuzhiyun * 1. Update the following 'image[]' to point to your own images;
73*4882a593Smuzhiyun * 2. You must set the failed image as last one and soc = -1 !!!
74*4882a593Smuzhiyun */
75*4882a593Smuzhiyun static const struct charge_image image[] = {
76*4882a593Smuzhiyun { .name = "battery_0.bmp", .soc = 5, .period = 600 },
77*4882a593Smuzhiyun { .name = "battery_1.bmp", .soc = 20, .period = 600 },
78*4882a593Smuzhiyun { .name = "battery_2.bmp", .soc = 40, .period = 600 },
79*4882a593Smuzhiyun { .name = "battery_3.bmp", .soc = 60, .period = 600 },
80*4882a593Smuzhiyun { .name = "battery_4.bmp", .soc = 80, .period = 600 },
81*4882a593Smuzhiyun { .name = "battery_5.bmp", .soc = 100, .period = 600 },
82*4882a593Smuzhiyun { .name = "battery_fail.bmp", .soc = -1, .period = 1000 },
83*4882a593Smuzhiyun };
84*4882a593Smuzhiyun
regulators_parse_assigned_mem_state(struct udevice * dev)85*4882a593Smuzhiyun static int regulators_parse_assigned_mem_state(struct udevice *dev)
86*4882a593Smuzhiyun {
87*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
88*4882a593Smuzhiyun struct regulator_mem *mem;
89*4882a593Smuzhiyun const fdt32_t *list1;
90*4882a593Smuzhiyun const fdt32_t *list2;
91*4882a593Smuzhiyun int size1, size2;
92*4882a593Smuzhiyun int i, ret;
93*4882a593Smuzhiyun uint32_t phandle;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /* Must be both exist or not */
96*4882a593Smuzhiyun list1 = dev_read_prop(dev, "regulator-on-in-mem", &size1);
97*4882a593Smuzhiyun list2 = dev_read_prop(dev, "regulator-off-in-mem", &size2);
98*4882a593Smuzhiyun if (!list1 && !list2)
99*4882a593Smuzhiyun return 0;
100*4882a593Smuzhiyun if (list1 && !list2)
101*4882a593Smuzhiyun return -EINVAL;
102*4882a593Smuzhiyun else if (!list1 && list2)
103*4882a593Smuzhiyun return -EINVAL;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun size1 = size1 / sizeof(*list1);
106*4882a593Smuzhiyun size2 = size2 / sizeof(*list2);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun pdata->regulators_mem =
109*4882a593Smuzhiyun calloc(size1 + size2, sizeof(*pdata->regulators_mem));
110*4882a593Smuzhiyun if (!pdata->regulators_mem)
111*4882a593Smuzhiyun return -ENOMEM;
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun mem = pdata->regulators_mem;
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun for (i = 0; i < size1; i++, mem++) {
116*4882a593Smuzhiyun mem->enable = true;
117*4882a593Smuzhiyun phandle = fdt32_to_cpu(*list1++);
118*4882a593Smuzhiyun ret = uclass_get_device_by_phandle_id(UCLASS_REGULATOR,
119*4882a593Smuzhiyun phandle, &mem->dev);
120*4882a593Smuzhiyun if (ret)
121*4882a593Smuzhiyun return ret;
122*4882a593Smuzhiyun }
123*4882a593Smuzhiyun for (i = 0; i < size2; i++, mem++) {
124*4882a593Smuzhiyun mem->enable = false;
125*4882a593Smuzhiyun phandle = fdt32_to_cpu(*list2++);
126*4882a593Smuzhiyun ret = uclass_get_device_by_phandle_id(UCLASS_REGULATOR,
127*4882a593Smuzhiyun phandle, &mem->dev);
128*4882a593Smuzhiyun if (ret)
129*4882a593Smuzhiyun return ret;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun #ifdef DEBUG
133*4882a593Smuzhiyun printf("assigned regulator mem:\n");
134*4882a593Smuzhiyun for (mem = pdata->regulators_mem; mem->dev; mem++)
135*4882a593Smuzhiyun printf(" %20s: suspend %s\n", mem->dev->name,
136*4882a593Smuzhiyun mem->enable ? "enabling" : "disabled");
137*4882a593Smuzhiyun #endif
138*4882a593Smuzhiyun return 0;
139*4882a593Smuzhiyun }
140*4882a593Smuzhiyun
regulators_enable_assigned_state_mem(struct udevice * dev)141*4882a593Smuzhiyun static int regulators_enable_assigned_state_mem(struct udevice *dev)
142*4882a593Smuzhiyun {
143*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
144*4882a593Smuzhiyun struct regulator_mem *mem;
145*4882a593Smuzhiyun int ret;
146*4882a593Smuzhiyun
147*4882a593Smuzhiyun for (mem = pdata->regulators_mem; mem->dev; mem++) {
148*4882a593Smuzhiyun ret = regulator_set_suspend_enable(mem->dev, mem->enable);
149*4882a593Smuzhiyun if (ret)
150*4882a593Smuzhiyun printf("%s: suspend failed, ret=%d\n",
151*4882a593Smuzhiyun mem->dev->name, ret);
152*4882a593Smuzhiyun }
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun return 0;
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun
regulators_suspend(struct udevice * dev)157*4882a593Smuzhiyun static void regulators_suspend(struct udevice *dev)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun if (pdata->regulators_mem)
162*4882a593Smuzhiyun regulators_enable_assigned_state_mem(dev);
163*4882a593Smuzhiyun else
164*4882a593Smuzhiyun regulators_enable_state_mem(false);
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
pmics_ops(bool suspend)167*4882a593Smuzhiyun static void pmics_ops(bool suspend)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun struct udevice *dev;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun for (uclass_first_device(UCLASS_PMIC, &dev);
172*4882a593Smuzhiyun dev;
173*4882a593Smuzhiyun uclass_next_device(&dev)) {
174*4882a593Smuzhiyun if (suspend)
175*4882a593Smuzhiyun pmic_suspend(dev);
176*4882a593Smuzhiyun else
177*4882a593Smuzhiyun pmic_resume(dev);
178*4882a593Smuzhiyun }
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
pmics_suspend(void)181*4882a593Smuzhiyun static void pmics_suspend(void)
182*4882a593Smuzhiyun {
183*4882a593Smuzhiyun pmics_ops(true);
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun
pmics_resume(void)186*4882a593Smuzhiyun static void pmics_resume(void)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun pmics_ops(false);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
charge_animation_ofdata_to_platdata(struct udevice * dev)191*4882a593Smuzhiyun static int charge_animation_ofdata_to_platdata(struct udevice *dev)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun /* charge mode */
196*4882a593Smuzhiyun pdata->uboot_charge =
197*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,uboot-charge-on", 0);
198*4882a593Smuzhiyun pdata->android_charge =
199*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,android-charge-on", 0);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun pdata->auto_exit_charge =
202*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,uboot-exit-charge-auto", 0);
203*4882a593Smuzhiyun pdata->exit_charge_level =
204*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,uboot-exit-charge-level", 0);
205*4882a593Smuzhiyun pdata->exit_charge_voltage =
206*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,uboot-exit-charge-voltage", 0);
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun pdata->low_power_voltage =
209*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,uboot-low-power-voltage", 0);
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun pdata->screen_on_voltage =
212*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,screen-on-voltage", 0);
213*4882a593Smuzhiyun pdata->system_suspend =
214*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,system-suspend", 0);
215*4882a593Smuzhiyun
216*4882a593Smuzhiyun pdata->auto_wakeup_interval =
217*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,auto-wakeup-interval", 0);
218*4882a593Smuzhiyun pdata->auto_wakeup_screen_invert =
219*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,auto-wakeup-screen-invert", 0);
220*4882a593Smuzhiyun
221*4882a593Smuzhiyun pdata->auto_off_screen_interval =
222*4882a593Smuzhiyun dev_read_u32_default(dev, "rockchip,auto-off-screen-interval", 15);
223*4882a593Smuzhiyun
224*4882a593Smuzhiyun if (pdata->screen_on_voltage > pdata->exit_charge_voltage)
225*4882a593Smuzhiyun pdata->screen_on_voltage = pdata->exit_charge_voltage;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun if (pdata->auto_exit_charge && !pdata->auto_wakeup_interval)
228*4882a593Smuzhiyun pdata->auto_wakeup_interval = 10;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun /* Not allow failure */
231*4882a593Smuzhiyun if (regulators_parse_assigned_mem_state(dev)) {
232*4882a593Smuzhiyun printf("Failed to parse assigned mem state\n");
233*4882a593Smuzhiyun return -EINVAL;
234*4882a593Smuzhiyun }
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun debug("mode: uboot=%d, android=%d; exit: soc=%d%%, voltage=%dmv;\n"
237*4882a593Smuzhiyun "lp_voltage=%d%%, screen_on=%dmv\n",
238*4882a593Smuzhiyun pdata->uboot_charge, pdata->android_charge,
239*4882a593Smuzhiyun pdata->exit_charge_level, pdata->exit_charge_voltage,
240*4882a593Smuzhiyun pdata->low_power_voltage, pdata->screen_on_voltage);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun return 0;
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
check_key_press(struct udevice * dev)245*4882a593Smuzhiyun static int check_key_press(struct udevice *dev)
246*4882a593Smuzhiyun {
247*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
248*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
249*4882a593Smuzhiyun u32 event;
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun #ifdef CONFIG_DM_RTC
252*4882a593Smuzhiyun if (priv->rtc && rtc_alarm_trigger(priv->rtc)) {
253*4882a593Smuzhiyun printf("rtc alarm trigger...\n");
254*4882a593Smuzhiyun return KEY_PRESS_LONG_DOWN;
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun #endif
257*4882a593Smuzhiyun event = key_read(KEY_POWER);
258*4882a593Smuzhiyun if (event < 0)
259*4882a593Smuzhiyun printf("read power key failed: %d\n", event);
260*4882a593Smuzhiyun else if (event == KEY_PRESS_DOWN)
261*4882a593Smuzhiyun printf("power key pressed...\n");
262*4882a593Smuzhiyun else if (event == KEY_PRESS_LONG_DOWN)
263*4882a593Smuzhiyun printf("power key long pressed...\n");
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun /* auto screen invert ? */
266*4882a593Smuzhiyun if (pdata->auto_wakeup_interval &&
267*4882a593Smuzhiyun pdata->auto_wakeup_screen_invert) {
268*4882a593Smuzhiyun if (priv->auto_wakeup_key_state == KEY_PRESS_DOWN) {
269*4882a593Smuzhiyun /* Value is updated in timer interrupt */
270*4882a593Smuzhiyun priv->auto_wakeup_key_state = KEY_PRESS_NONE;
271*4882a593Smuzhiyun event = KEY_PRESS_DOWN;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun
275*4882a593Smuzhiyun /* auto screen off (while not enable auto screen invert) ? */
276*4882a593Smuzhiyun if (!pdata->auto_wakeup_screen_invert &&
277*4882a593Smuzhiyun pdata->auto_off_screen_interval) {
278*4882a593Smuzhiyun if (priv->auto_screen_off_timeout &&
279*4882a593Smuzhiyun get_timer(priv->auto_screen_off_timeout) >
280*4882a593Smuzhiyun pdata->auto_off_screen_interval * 1000) { /* 1000ms */
281*4882a593Smuzhiyun event = KEY_PRESS_DOWN;
282*4882a593Smuzhiyun printf("Auto screen off\n");
283*4882a593Smuzhiyun }
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun return event;
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun /*
290*4882a593Smuzhiyun * If not enable CONFIG_IRQ, cpu can't suspend to ATF or wfi, so that wakeup
291*4882a593Smuzhiyun * period timer is useless.
292*4882a593Smuzhiyun */
293*4882a593Smuzhiyun #if !defined(CONFIG_IRQ) || !defined(CONFIG_ARM_CPU_SUSPEND)
system_suspend_enter(struct udevice * dev)294*4882a593Smuzhiyun static int system_suspend_enter(struct udevice *dev)
295*4882a593Smuzhiyun {
296*4882a593Smuzhiyun return 0;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun
autowakeup_timer_init(struct udevice * dev,uint32_t seconds)299*4882a593Smuzhiyun static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds) {}
autowakeup_timer_uninit(void)300*4882a593Smuzhiyun static void autowakeup_timer_uninit(void) {}
301*4882a593Smuzhiyun
302*4882a593Smuzhiyun #else
system_suspend_enter(struct udevice * dev)303*4882a593Smuzhiyun static int system_suspend_enter(struct udevice *dev)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
306*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun /*
309*4882a593Smuzhiyun * When cpu is in wfi and we try to give a long key press event without
310*4882a593Smuzhiyun * key release, cpu would wakeup and enter wfi again immediately. So
311*4882a593Smuzhiyun * here is the problem: cpu can only wakeup when long key released.
312*4882a593Smuzhiyun *
313*4882a593Smuzhiyun * Actually, we want cpu can detect long key event without key release,
314*4882a593Smuzhiyun * so we give a suspend delay timeout for cpu to detect this.
315*4882a593Smuzhiyun */
316*4882a593Smuzhiyun if (priv->suspend_delay_timeout &&
317*4882a593Smuzhiyun get_timer(priv->suspend_delay_timeout) <= SYSTEM_SUSPEND_DELAY_MS)
318*4882a593Smuzhiyun return 0;
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun if (pdata->system_suspend && IS_ENABLED(CONFIG_ARM_SMCCC)) {
321*4882a593Smuzhiyun printf("\nSystem suspend: ");
322*4882a593Smuzhiyun putc('0');
323*4882a593Smuzhiyun local_irq_disable();
324*4882a593Smuzhiyun putc('1');
325*4882a593Smuzhiyun regulators_suspend(dev);
326*4882a593Smuzhiyun putc('2');
327*4882a593Smuzhiyun pmics_suspend();
328*4882a593Smuzhiyun putc('3');
329*4882a593Smuzhiyun irqs_suspend();
330*4882a593Smuzhiyun putc('4');
331*4882a593Smuzhiyun device_suspend();
332*4882a593Smuzhiyun putc('5');
333*4882a593Smuzhiyun putc('\n');
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun /* Trap into ATF for low power mode */
336*4882a593Smuzhiyun cpu_suspend(0, psci_system_suspend);
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun putc('\n');
339*4882a593Smuzhiyun putc('4');
340*4882a593Smuzhiyun device_resume();
341*4882a593Smuzhiyun putc('3');
342*4882a593Smuzhiyun irqs_resume();
343*4882a593Smuzhiyun putc('2');
344*4882a593Smuzhiyun pmics_resume();
345*4882a593Smuzhiyun putc('1');
346*4882a593Smuzhiyun local_irq_enable();
347*4882a593Smuzhiyun putc('0');
348*4882a593Smuzhiyun putc('\n');
349*4882a593Smuzhiyun } else {
350*4882a593Smuzhiyun irqs_suspend();
351*4882a593Smuzhiyun printf("\nWfi\n");
352*4882a593Smuzhiyun wfi();
353*4882a593Smuzhiyun putc('1');
354*4882a593Smuzhiyun irqs_resume();
355*4882a593Smuzhiyun }
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun priv->suspend_delay_timeout = get_timer(0);
358*4882a593Smuzhiyun
359*4882a593Smuzhiyun /*
360*4882a593Smuzhiyun * We must wait for key release event finish, otherwise
361*4882a593Smuzhiyun * we may read key state too early.
362*4882a593Smuzhiyun */
363*4882a593Smuzhiyun mdelay(300);
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun return 0;
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun
autowake_timer_handler(int irq,void * data)368*4882a593Smuzhiyun static void autowake_timer_handler(int irq, void *data)
369*4882a593Smuzhiyun {
370*4882a593Smuzhiyun struct udevice *dev = data;
371*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
372*4882a593Smuzhiyun static long long count;
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun priv->auto_wakeup_key_state = KEY_PRESS_DOWN;
377*4882a593Smuzhiyun printf("auto wakeup count: %lld\n", ++count);
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun
autowakeup_timer_init(struct udevice * dev,uint32_t seconds)380*4882a593Smuzhiyun static void autowakeup_timer_init(struct udevice *dev, uint32_t seconds)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun uint64_t period = 24000000ULL * seconds;
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun /* Disable before conifg */
385*4882a593Smuzhiyun writel(0, TIMER_BASE + TIMER_CTRL);
386*4882a593Smuzhiyun
387*4882a593Smuzhiyun /* Config */
388*4882a593Smuzhiyun writel((uint32_t)period, TIMER_BASE + TIMER_LOAD_COUNT0);
389*4882a593Smuzhiyun writel((uint32_t)(period >> 32), TIMER_BASE + TIMER_LOAD_COUNT1);
390*4882a593Smuzhiyun writel(TIMER_CLR_INT, TIMER_BASE + TIMER_INTSTATUS);
391*4882a593Smuzhiyun writel(TIMER_EN | TIMER_INT_EN, TIMER_BASE + TIMER_CTRL);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun /* IRQ */
394*4882a593Smuzhiyun irq_install_handler(TIMER_IRQ, autowake_timer_handler, dev);
395*4882a593Smuzhiyun irq_handler_enable(TIMER_IRQ);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
autowakeup_timer_uninit(void)398*4882a593Smuzhiyun static void autowakeup_timer_uninit(void)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun writel(0, TIMER_BASE + TIMER_CTRL);
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun irq_handler_disable(TIMER_IRQ);
403*4882a593Smuzhiyun irq_free_handler(TIMER_IRQ);
404*4882a593Smuzhiyun }
405*4882a593Smuzhiyun #endif
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun #ifdef CONFIG_DRM_ROCKCHIP
charge_show_bmp(const char * name)408*4882a593Smuzhiyun static void charge_show_bmp(const char *name)
409*4882a593Smuzhiyun {
410*4882a593Smuzhiyun rockchip_show_bmp(name);
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun
charge_show_logo(void)413*4882a593Smuzhiyun static void charge_show_logo(void)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun rockchip_show_logo();
416*4882a593Smuzhiyun }
417*4882a593Smuzhiyun #else
charge_show_bmp(const char * name)418*4882a593Smuzhiyun static void charge_show_bmp(const char *name) {}
charge_show_logo(void)419*4882a593Smuzhiyun static void charge_show_logo(void) {}
420*4882a593Smuzhiyun #endif
421*4882a593Smuzhiyun
422*4882a593Smuzhiyun #ifdef CONFIG_LED
leds_update(struct udevice * dev,int soc)423*4882a593Smuzhiyun static int leds_update(struct udevice *dev, int soc)
424*4882a593Smuzhiyun {
425*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
426*4882a593Smuzhiyun static int old_soc = -1;
427*4882a593Smuzhiyun int ret, ledst;
428*4882a593Smuzhiyun
429*4882a593Smuzhiyun if (old_soc == soc)
430*4882a593Smuzhiyun return 0;
431*4882a593Smuzhiyun
432*4882a593Smuzhiyun old_soc = soc;
433*4882a593Smuzhiyun if (priv->led_charging) {
434*4882a593Smuzhiyun ledst = (soc < 100) ? LEDST_ON : LEDST_OFF;
435*4882a593Smuzhiyun ret = led_set_state(priv->led_charging, ledst);
436*4882a593Smuzhiyun if (ret) {
437*4882a593Smuzhiyun printf("set charging led %s failed, ret=%d\n",
438*4882a593Smuzhiyun (ledst == LEDST_ON) ? "ON" : "OFF", ret);
439*4882a593Smuzhiyun return ret;
440*4882a593Smuzhiyun }
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun if (priv->led_full) {
444*4882a593Smuzhiyun ledst = (soc == 100) ? LEDST_ON : LEDST_OFF;
445*4882a593Smuzhiyun ret = led_set_state(priv->led_full, ledst);
446*4882a593Smuzhiyun if (ret) {
447*4882a593Smuzhiyun printf("set charging full led %s failed, ret=%d\n",
448*4882a593Smuzhiyun ledst == LEDST_ON ? "ON" : "OFF", ret);
449*4882a593Smuzhiyun return ret;
450*4882a593Smuzhiyun }
451*4882a593Smuzhiyun }
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun return 0;
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun #else
leds_update(struct udevice * dev,int soc)456*4882a593Smuzhiyun static int leds_update(struct udevice *dev, int soc) { return 0; }
457*4882a593Smuzhiyun #endif
458*4882a593Smuzhiyun
fg_charger_get_chrg_online(struct udevice * dev)459*4882a593Smuzhiyun static int fg_charger_get_chrg_online(struct udevice *dev)
460*4882a593Smuzhiyun {
461*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
462*4882a593Smuzhiyun struct udevice *charger;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun charger = priv->charger ? : priv->fg;
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun return fuel_gauge_get_chrg_online(charger);
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun
sys_shutdown(struct udevice * dev)469*4882a593Smuzhiyun static int sys_shutdown(struct udevice *dev)
470*4882a593Smuzhiyun {
471*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
472*4882a593Smuzhiyun struct udevice *pmic = priv->pmic;
473*4882a593Smuzhiyun struct udevice *fg = priv->fg;
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun /*
476*4882a593Smuzhiyun * Call the fuel/charge again to update something specific
477*4882a593Smuzhiyun * before shutdown. This fix a scene:
478*4882a593Smuzhiyun *
479*4882a593Smuzhiyun * Plug out charger which auto wakeup cpu from a long time system suspend,
480*4882a593Smuzhiyun * fuel/charge need to update something before shutdown.
481*4882a593Smuzhiyun */
482*4882a593Smuzhiyun fg_charger_get_chrg_online(dev);
483*4882a593Smuzhiyun fuel_gauge_get_voltage(fg);
484*4882a593Smuzhiyun fuel_gauge_update_get_soc(fg);
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun flushc();
487*4882a593Smuzhiyun mdelay(50);
488*4882a593Smuzhiyun pmic_shutdown(pmic);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun mdelay(500);
491*4882a593Smuzhiyun printf("Cpu should never reach here, shutdown failed !\n");
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun return 0;
494*4882a593Smuzhiyun }
495*4882a593Smuzhiyun
charge_extrem_low_power(struct udevice * dev)496*4882a593Smuzhiyun static int charge_extrem_low_power(struct udevice *dev)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
499*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
500*4882a593Smuzhiyun struct udevice *fg = priv->fg;
501*4882a593Smuzhiyun int voltage, soc, charging = 1;
502*4882a593Smuzhiyun int first_poll_fg = 1;
503*4882a593Smuzhiyun static int timer_initialized;
504*4882a593Smuzhiyun
505*4882a593Smuzhiyun voltage = fuel_gauge_get_voltage(fg);
506*4882a593Smuzhiyun if (voltage < 0)
507*4882a593Smuzhiyun return -EINVAL;
508*4882a593Smuzhiyun
509*4882a593Smuzhiyun while (voltage < pdata->low_power_voltage + 50) {
510*4882a593Smuzhiyun if (!first_poll_fg)
511*4882a593Smuzhiyun mdelay(FUEL_GAUGE_POLL_MS);
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun first_poll_fg = 0;
514*4882a593Smuzhiyun
515*4882a593Smuzhiyun /* Check charger online */
516*4882a593Smuzhiyun charging = fg_charger_get_chrg_online(dev);
517*4882a593Smuzhiyun if (charging <= 0) {
518*4882a593Smuzhiyun printf("%s: Not charging, online=%d. Shutdown...\n",
519*4882a593Smuzhiyun __func__, charging);
520*4882a593Smuzhiyun sys_shutdown(dev);
521*4882a593Smuzhiyun continue;
522*4882a593Smuzhiyun }
523*4882a593Smuzhiyun
524*4882a593Smuzhiyun /* Enable auto wakeup */
525*4882a593Smuzhiyun if (!timer_initialized) {
526*4882a593Smuzhiyun timer_initialized = 1;
527*4882a593Smuzhiyun autowakeup_timer_init(dev, 5);
528*4882a593Smuzhiyun }
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun /*
531*4882a593Smuzhiyun * Just for fuel gauge to update something important,
532*4882a593Smuzhiyun * including charge current, coulometer or other.
533*4882a593Smuzhiyun */
534*4882a593Smuzhiyun soc = fuel_gauge_update_get_soc(fg);
535*4882a593Smuzhiyun if (soc < 0 || soc > 100) {
536*4882a593Smuzhiyun printf("get soc failed: %d\n", soc);
537*4882a593Smuzhiyun continue;
538*4882a593Smuzhiyun }
539*4882a593Smuzhiyun
540*4882a593Smuzhiyun /* Update led */
541*4882a593Smuzhiyun leds_update(dev, soc);
542*4882a593Smuzhiyun
543*4882a593Smuzhiyun printf("Extrem low power, force charging... threshold=%dmv, now=%dmv\n",
544*4882a593Smuzhiyun pdata->low_power_voltage, voltage);
545*4882a593Smuzhiyun
546*4882a593Smuzhiyun /* System suspend */
547*4882a593Smuzhiyun system_suspend_enter(dev);
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun /* Update voltage */
550*4882a593Smuzhiyun voltage = fuel_gauge_get_voltage(fg);
551*4882a593Smuzhiyun if (voltage < 0) {
552*4882a593Smuzhiyun printf("get voltage failed: %d\n", voltage);
553*4882a593Smuzhiyun continue;
554*4882a593Smuzhiyun }
555*4882a593Smuzhiyun
556*4882a593Smuzhiyun if (ctrlc()) {
557*4882a593Smuzhiyun printf("Extrem low charge: exit by ctrl+c\n");
558*4882a593Smuzhiyun break;
559*4882a593Smuzhiyun }
560*4882a593Smuzhiyun }
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun autowakeup_timer_uninit();
563*4882a593Smuzhiyun
564*4882a593Smuzhiyun return 0;
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun
charge_animation_show(struct udevice * dev)567*4882a593Smuzhiyun static int charge_animation_show(struct udevice *dev)
568*4882a593Smuzhiyun {
569*4882a593Smuzhiyun struct charge_animation_pdata *pdata = dev_get_platdata(dev);
570*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
571*4882a593Smuzhiyun const struct charge_image *image = priv->image;
572*4882a593Smuzhiyun struct udevice *fg = priv->fg;
573*4882a593Smuzhiyun const char *preboot = env_get("preboot");
574*4882a593Smuzhiyun int image_num = priv->image_num;
575*4882a593Smuzhiyun bool ever_lowpower_screen_off = false;
576*4882a593Smuzhiyun bool screen_on = true;
577*4882a593Smuzhiyun ulong show_start = 0, charge_start = 0, debug_start = 0;
578*4882a593Smuzhiyun ulong delta;
579*4882a593Smuzhiyun ulong ms = 0, sec = 0;
580*4882a593Smuzhiyun int start_idx = 0, show_idx = -1, old_show_idx = IMAGE_RECALC_IDX;
581*4882a593Smuzhiyun int soc, voltage, current, key_state;
582*4882a593Smuzhiyun int i, charging = 1, ret;
583*4882a593Smuzhiyun int boot_mode;
584*4882a593Smuzhiyun int first_poll_fg = 1;
585*4882a593Smuzhiyun bool lp_shutdown = false;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun /*
588*4882a593Smuzhiyun * Check sequence:
589*4882a593Smuzhiyun *
590*4882a593Smuzhiyun * 1. Extrem low power charge?
591*4882a593Smuzhiyun * 2. Preboot cmd?
592*4882a593Smuzhiyun * 3. Valid boot mode?
593*4882a593Smuzhiyun * 4. U-Boot charge enabled by dts config?
594*4882a593Smuzhiyun * 5. Screen off before charge?
595*4882a593Smuzhiyun * 6. Enter charge !
596*4882a593Smuzhiyun *
597*4882a593Smuzhiyun */
598*4882a593Smuzhiyun if (!fuel_gauge_bat_is_exist(fg)) {
599*4882a593Smuzhiyun printf("Exit charge: battery is not exist\n");
600*4882a593Smuzhiyun return 0;
601*4882a593Smuzhiyun }
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun /* Extrem low power charge */
604*4882a593Smuzhiyun ret = charge_extrem_low_power(dev);
605*4882a593Smuzhiyun if (ret < 0) {
606*4882a593Smuzhiyun printf("extrem low power charge failed, ret=%d\n", ret);
607*4882a593Smuzhiyun return ret;
608*4882a593Smuzhiyun }
609*4882a593Smuzhiyun
610*4882a593Smuzhiyun /* If there is preboot command, exit */
611*4882a593Smuzhiyun if (preboot && !strstr(preboot, "dvfs")) {
612*4882a593Smuzhiyun printf("Exit charge: due to preboot cmd '%s'\n", preboot);
613*4882a593Smuzhiyun return 0;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun /* Not valid charge mode, exit */
617*4882a593Smuzhiyun #ifdef CONFIG_RKIMG_BOOTLOADER
618*4882a593Smuzhiyun boot_mode = rockchip_get_boot_mode();
619*4882a593Smuzhiyun if ((boot_mode != BOOT_MODE_CHARGING) &&
620*4882a593Smuzhiyun (boot_mode != BOOT_MODE_UNDEFINE)) {
621*4882a593Smuzhiyun printf("Exit charge: due to boot mode\n");
622*4882a593Smuzhiyun return 0;
623*4882a593Smuzhiyun }
624*4882a593Smuzhiyun #endif
625*4882a593Smuzhiyun /* No charger online + low power? shutdown */
626*4882a593Smuzhiyun charging = fg_charger_get_chrg_online(dev);
627*4882a593Smuzhiyun if (charging <= 0 && pdata->auto_exit_charge) {
628*4882a593Smuzhiyun soc = fuel_gauge_update_get_soc(fg);
629*4882a593Smuzhiyun voltage = fuel_gauge_get_voltage(fg);
630*4882a593Smuzhiyun if (soc < pdata->exit_charge_level) {
631*4882a593Smuzhiyun printf("soc(%d%%) < exit_charge_level(%d%%)\n",
632*4882a593Smuzhiyun soc, pdata->exit_charge_level);
633*4882a593Smuzhiyun lp_shutdown = true;
634*4882a593Smuzhiyun }
635*4882a593Smuzhiyun if (voltage < pdata->exit_charge_voltage) {
636*4882a593Smuzhiyun printf("voltage(%d) < exit_charge_voltage(%d)\n",
637*4882a593Smuzhiyun voltage, pdata->exit_charge_voltage);
638*4882a593Smuzhiyun lp_shutdown = true;
639*4882a593Smuzhiyun }
640*4882a593Smuzhiyun if (lp_shutdown) {
641*4882a593Smuzhiyun printf("Not charging and low power, Shutdown...\n");
642*4882a593Smuzhiyun show_idx = IMAGE_LOWPOWER_IDX(image_num);
643*4882a593Smuzhiyun charge_show_bmp(image[show_idx].name);
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun sys_shutdown(dev);
646*4882a593Smuzhiyun }
647*4882a593Smuzhiyun }
648*4882a593Smuzhiyun
649*4882a593Smuzhiyun /* No charger online, exit */
650*4882a593Smuzhiyun if (charging <= 0) {
651*4882a593Smuzhiyun printf("Exit charge: due to charger offline\n");
652*4882a593Smuzhiyun return 0;
653*4882a593Smuzhiyun }
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun /* Enter android charge, set property for kernel */
656*4882a593Smuzhiyun if (pdata->android_charge) {
657*4882a593Smuzhiyun env_update("bootargs", "androidboot.mode=charger");
658*4882a593Smuzhiyun printf("Android charge mode\n");
659*4882a593Smuzhiyun }
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun /* Not enable U-Boot charge, exit */
662*4882a593Smuzhiyun if (!pdata->uboot_charge) {
663*4882a593Smuzhiyun printf("Exit charge: due to not enable uboot charge\n");
664*4882a593Smuzhiyun return 0;
665*4882a593Smuzhiyun }
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun voltage = fuel_gauge_get_voltage(fg);
668*4882a593Smuzhiyun if (voltage < 0) {
669*4882a593Smuzhiyun printf("get voltage failed: %d\n", voltage);
670*4882a593Smuzhiyun return -EINVAL;
671*4882a593Smuzhiyun }
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun /* If low power, turn off screen */
674*4882a593Smuzhiyun if (voltage <= pdata->screen_on_voltage + 50) {
675*4882a593Smuzhiyun screen_on = false;
676*4882a593Smuzhiyun ever_lowpower_screen_off = true;
677*4882a593Smuzhiyun charge_show_bmp(NULL);
678*4882a593Smuzhiyun }
679*4882a593Smuzhiyun
680*4882a593Smuzhiyun /* Auto wakeup */
681*4882a593Smuzhiyun if (pdata->auto_wakeup_interval) {
682*4882a593Smuzhiyun printf("Auto wakeup: %dS\n", pdata->auto_wakeup_interval);
683*4882a593Smuzhiyun autowakeup_timer_init(dev, pdata->auto_wakeup_interval);
684*4882a593Smuzhiyun }
685*4882a593Smuzhiyun
686*4882a593Smuzhiyun /* Give a message warning when CONFIG_IRQ is not enabled */
687*4882a593Smuzhiyun #ifdef CONFIG_IRQ
688*4882a593Smuzhiyun printf("Enter U-Boot charging mode\n");
689*4882a593Smuzhiyun #else
690*4882a593Smuzhiyun printf("Enter U-Boot charging mode(IRQ)\n");
691*4882a593Smuzhiyun #endif
692*4882a593Smuzhiyun
693*4882a593Smuzhiyun charge_start = get_timer(0);
694*4882a593Smuzhiyun delta = get_timer(0);
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun /* Charging ! */
697*4882a593Smuzhiyun while (1) {
698*4882a593Smuzhiyun /*
699*4882a593Smuzhiyun * At the most time, fuel gauge is usually a i2c device, we
700*4882a593Smuzhiyun * should avoid read/write all the time. We had better set
701*4882a593Smuzhiyun * poll seconds to update fuel gauge info.
702*4882a593Smuzhiyun */
703*4882a593Smuzhiyun if (!first_poll_fg && get_timer(delta) < FUEL_GAUGE_POLL_MS)
704*4882a593Smuzhiyun goto show_images;
705*4882a593Smuzhiyun
706*4882a593Smuzhiyun delta = get_timer(0);
707*4882a593Smuzhiyun
708*4882a593Smuzhiyun debug("step1 (%d)... \n", screen_on);
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun /*
711*4882a593Smuzhiyun * Most fuel gauge is I2C interface, it shouldn't be interrupted
712*4882a593Smuzhiyun * during transfer. The power key event depends on interrupt, so
713*4882a593Smuzhiyun * we should disable local irq when update fuel gauge.
714*4882a593Smuzhiyun */
715*4882a593Smuzhiyun local_irq_disable();
716*4882a593Smuzhiyun
717*4882a593Smuzhiyun /* Step1: Is charging now ? */
718*4882a593Smuzhiyun charging = fg_charger_get_chrg_online(dev);
719*4882a593Smuzhiyun if (charging <= 0) {
720*4882a593Smuzhiyun printf("Not charging, online=%d. Shutdown...\n",
721*4882a593Smuzhiyun charging);
722*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
723*4882a593Smuzhiyun /*
724*4882a593Smuzhiyun * If charger is plug out during charging, display poweroff
725*4882a593Smuzhiyun * image before device power off.
726*4882a593Smuzhiyun * Irq must be enable if CONFIG_IRQ is defined, because
727*4882a593Smuzhiyun * ebc need to wait irq to indicate frame is complete.
728*4882a593Smuzhiyun */
729*4882a593Smuzhiyun local_irq_enable();
730*4882a593Smuzhiyun
731*4882a593Smuzhiyun ret = rockchip_eink_show_charge_logo(EINK_LOGO_POWEROFF);
732*4882a593Smuzhiyun if (ret != 0)
733*4882a593Smuzhiyun printf("Eink display reset logo failed\n");
734*4882a593Smuzhiyun
735*4882a593Smuzhiyun local_irq_disable();
736*4882a593Smuzhiyun #endif
737*4882a593Smuzhiyun sys_shutdown(dev);
738*4882a593Smuzhiyun continue;
739*4882a593Smuzhiyun }
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun debug("step2 (%d)... show_idx=%d\n", screen_on, show_idx);
742*4882a593Smuzhiyun
743*4882a593Smuzhiyun /* Step2: get soc and voltage */
744*4882a593Smuzhiyun soc = fuel_gauge_update_get_soc(fg);
745*4882a593Smuzhiyun if (soc < 0 || soc > 100) {
746*4882a593Smuzhiyun printf("get soc failed: %d\n", soc);
747*4882a593Smuzhiyun continue;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun
750*4882a593Smuzhiyun voltage = fuel_gauge_get_voltage(fg);
751*4882a593Smuzhiyun if (voltage < 0) {
752*4882a593Smuzhiyun printf("get voltage failed: %d\n", voltage);
753*4882a593Smuzhiyun continue;
754*4882a593Smuzhiyun }
755*4882a593Smuzhiyun
756*4882a593Smuzhiyun current = fuel_gauge_get_current(fg);
757*4882a593Smuzhiyun if (current == -ENOSYS) {
758*4882a593Smuzhiyun printf("get current failed: %d\n", current);
759*4882a593Smuzhiyun continue;
760*4882a593Smuzhiyun }
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun first_poll_fg = 0;
763*4882a593Smuzhiyun local_irq_enable();
764*4882a593Smuzhiyun
765*4882a593Smuzhiyun if (pdata->auto_exit_charge) {
766*4882a593Smuzhiyun /* Is able to boot now ? */
767*4882a593Smuzhiyun if (pdata->exit_charge_level &&
768*4882a593Smuzhiyun soc >= pdata->exit_charge_level) {
769*4882a593Smuzhiyun printf("soc(%d%%) exit charge animation...\n",
770*4882a593Smuzhiyun soc);
771*4882a593Smuzhiyun break;
772*4882a593Smuzhiyun }
773*4882a593Smuzhiyun if (pdata->exit_charge_voltage &&
774*4882a593Smuzhiyun voltage >= pdata->exit_charge_voltage) {
775*4882a593Smuzhiyun printf("vol(%d) exit charge animation...\n",
776*4882a593Smuzhiyun voltage);
777*4882a593Smuzhiyun break;
778*4882a593Smuzhiyun }
779*4882a593Smuzhiyun }
780*4882a593Smuzhiyun
781*4882a593Smuzhiyun show_images:
782*4882a593Smuzhiyun /*
783*4882a593Smuzhiyun * Just for debug, otherwise there will be nothing output which
784*4882a593Smuzhiyun * is not good to know what happen.
785*4882a593Smuzhiyun */
786*4882a593Smuzhiyun if (!debug_start)
787*4882a593Smuzhiyun debug_start = get_timer(0);
788*4882a593Smuzhiyun if (get_timer(debug_start) > 30000) {
789*4882a593Smuzhiyun debug_start = get_timer(0);
790*4882a593Smuzhiyun printf("[%8ld]: soc=%d%%, vol=%dmv, c=%dma, "
791*4882a593Smuzhiyun "online=%d, screen_on=%d\n",
792*4882a593Smuzhiyun get_timer(0) / 1000, soc, voltage,
793*4882a593Smuzhiyun current, charging, screen_on);
794*4882a593Smuzhiyun }
795*4882a593Smuzhiyun
796*4882a593Smuzhiyun /* Update leds */
797*4882a593Smuzhiyun leds_update(dev, soc);
798*4882a593Smuzhiyun
799*4882a593Smuzhiyun /*
800*4882a593Smuzhiyun * If ever lowpower screen off, force screen_on=false, which
801*4882a593Smuzhiyun * means key event can't modify screen_on, only voltage higher
802*4882a593Smuzhiyun * then threshold can update screen_on=true;
803*4882a593Smuzhiyun */
804*4882a593Smuzhiyun if (ever_lowpower_screen_off)
805*4882a593Smuzhiyun screen_on = false;
806*4882a593Smuzhiyun
807*4882a593Smuzhiyun /*
808*4882a593Smuzhiyun * Auto turn on screen when voltage higher than Vol screen on.
809*4882a593Smuzhiyun * 'ever_lowpower_screen_off' means enter the while(1) loop with
810*4882a593Smuzhiyun * screen off.
811*4882a593Smuzhiyun */
812*4882a593Smuzhiyun if ((ever_lowpower_screen_off) &&
813*4882a593Smuzhiyun (voltage > pdata->screen_on_voltage)) {
814*4882a593Smuzhiyun ever_lowpower_screen_off = false;
815*4882a593Smuzhiyun screen_on = true;
816*4882a593Smuzhiyun show_idx = IMAGE_RECALC_IDX;
817*4882a593Smuzhiyun }
818*4882a593Smuzhiyun
819*4882a593Smuzhiyun /*
820*4882a593Smuzhiyun * IMAGE_RECALC_IDX means show_idx show be update by start_idx.
821*4882a593Smuzhiyun * When short key pressed event trigged, we will set show_idx
822*4882a593Smuzhiyun * as IMAGE_RECALC_IDX which updates images index from start_idx
823*4882a593Smuzhiyun * that calculate by current soc.
824*4882a593Smuzhiyun */
825*4882a593Smuzhiyun if (show_idx == IMAGE_RECALC_IDX) {
826*4882a593Smuzhiyun for (i = 0; i < IMAGE_SOC_100_IDX(image_num); i++) {
827*4882a593Smuzhiyun /* Find out which image we start to show */
828*4882a593Smuzhiyun if ((soc >= image[i].soc) && (soc < image[i + 1].soc)) {
829*4882a593Smuzhiyun start_idx = i;
830*4882a593Smuzhiyun break;
831*4882a593Smuzhiyun }
832*4882a593Smuzhiyun
833*4882a593Smuzhiyun if (soc >= 100) {
834*4882a593Smuzhiyun start_idx = IMAGE_SOC_100_IDX(image_num);
835*4882a593Smuzhiyun break;
836*4882a593Smuzhiyun }
837*4882a593Smuzhiyun }
838*4882a593Smuzhiyun
839*4882a593Smuzhiyun debug("%s: show_idx=%d, screen_on=%d\n",
840*4882a593Smuzhiyun __func__, show_idx, screen_on);
841*4882a593Smuzhiyun
842*4882a593Smuzhiyun /* Mark start index and start time */
843*4882a593Smuzhiyun show_idx = start_idx;
844*4882a593Smuzhiyun show_start = get_timer(0);
845*4882a593Smuzhiyun }
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun debug("step3 (%d)... show_idx=%d\n", screen_on, show_idx);
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
850*4882a593Smuzhiyun /*
851*4882a593Smuzhiyun * Device is auto wakeup from suspend, if it's eink display,
852*4882a593Smuzhiyun * screen will display the last image after suspend, so
853*4882a593Smuzhiyun * we should update the image to show the approximate
854*4882a593Smuzhiyun * battery power if battery is charging to next level.
855*4882a593Smuzhiyun */
856*4882a593Smuzhiyun if (pdata->auto_wakeup_interval &&
857*4882a593Smuzhiyun priv->auto_wakeup_key_state == KEY_PRESS_DOWN &&
858*4882a593Smuzhiyun !screen_on) {
859*4882a593Smuzhiyun if (soc >= image[old_show_idx + 1].soc &&
860*4882a593Smuzhiyun soc < 100) {
861*4882a593Smuzhiyun int ret;
862*4882a593Smuzhiyun int logo_type = EINK_LOGO_CHARGING_0;
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun logo_type = logo_type << (old_show_idx + 1);
865*4882a593Smuzhiyun ret = rockchip_eink_show_charge_logo(logo_type);
866*4882a593Smuzhiyun /*
867*4882a593Smuzhiyun * only change the logic if eink is
868*4882a593Smuzhiyun * actually exist
869*4882a593Smuzhiyun */
870*4882a593Smuzhiyun if (ret == 0) {
871*4882a593Smuzhiyun printf("Update image id[%d] for eink\n",
872*4882a593Smuzhiyun old_show_idx + 1);
873*4882a593Smuzhiyun old_show_idx++;
874*4882a593Smuzhiyun }
875*4882a593Smuzhiyun }
876*4882a593Smuzhiyun }
877*4882a593Smuzhiyun /*
878*4882a593Smuzhiyun * If battery capacity is charged to 100%, exit charging
879*4882a593Smuzhiyun * animation and boot android system.
880*4882a593Smuzhiyun */
881*4882a593Smuzhiyun if (soc >= 100) {
882*4882a593Smuzhiyun int ret;
883*4882a593Smuzhiyun int logo_type = EINK_LOGO_CHARGING_5;
884*4882a593Smuzhiyun
885*4882a593Smuzhiyun ret = rockchip_eink_show_charge_logo(logo_type);
886*4882a593Smuzhiyun /* Only change the logic if eink is acutally exist */
887*4882a593Smuzhiyun if (ret == 0) {
888*4882a593Smuzhiyun printf("battery FULL,exit charge animation\n");
889*4882a593Smuzhiyun mdelay(20);
890*4882a593Smuzhiyun break;
891*4882a593Smuzhiyun }
892*4882a593Smuzhiyun }
893*4882a593Smuzhiyun #endif
894*4882a593Smuzhiyun /* Step3: show images */
895*4882a593Smuzhiyun if (screen_on) {
896*4882a593Smuzhiyun /* Don't call 'charge_show_bmp' unless image changed */
897*4882a593Smuzhiyun if (old_show_idx != show_idx) {
898*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
899*4882a593Smuzhiyun int logo_type = EINK_LOGO_CHARGING_0;
900*4882a593Smuzhiyun
901*4882a593Smuzhiyun rockchip_eink_show_charge_logo(logo_type <<
902*4882a593Smuzhiyun show_idx);
903*4882a593Smuzhiyun #endif
904*4882a593Smuzhiyun old_show_idx = show_idx;
905*4882a593Smuzhiyun debug("SHOW: %s\n", image[show_idx].name);
906*4882a593Smuzhiyun charge_show_bmp(image[show_idx].name);
907*4882a593Smuzhiyun }
908*4882a593Smuzhiyun /* Re-calculate timeout to off screen */
909*4882a593Smuzhiyun if (priv->auto_screen_off_timeout == 0)
910*4882a593Smuzhiyun priv->auto_screen_off_timeout = get_timer(0);
911*4882a593Smuzhiyun } else {
912*4882a593Smuzhiyun /* Entering low power suspend mode !!! */
913*4882a593Smuzhiyun priv->auto_screen_off_timeout = 0;
914*4882a593Smuzhiyun system_suspend_enter(dev);
915*4882a593Smuzhiyun }
916*4882a593Smuzhiyun
917*4882a593Smuzhiyun mdelay(5);
918*4882a593Smuzhiyun
919*4882a593Smuzhiyun /* It's time to show next image ? */
920*4882a593Smuzhiyun if (get_timer(show_start) > image[show_idx].period) {
921*4882a593Smuzhiyun show_start = get_timer(0);
922*4882a593Smuzhiyun show_idx++;
923*4882a593Smuzhiyun if (show_idx > IMAGE_SOC_100_IDX(image_num))
924*4882a593Smuzhiyun show_idx = IMAGE_RECALC_IDX;
925*4882a593Smuzhiyun }
926*4882a593Smuzhiyun
927*4882a593Smuzhiyun debug("step4 (%d)... \n", screen_on);
928*4882a593Smuzhiyun
929*4882a593Smuzhiyun /*
930*4882a593Smuzhiyun * Step4: check key event.
931*4882a593Smuzhiyun *
932*4882a593Smuzhiyun * Short key event: turn on/off screen;
933*4882a593Smuzhiyun * Long key event: show logo and boot system or still charging.
934*4882a593Smuzhiyun */
935*4882a593Smuzhiyun key_state = check_key_press(dev);
936*4882a593Smuzhiyun if (key_state == KEY_PRESS_DOWN) {
937*4882a593Smuzhiyun /* Clear current image index, recalc image index */
938*4882a593Smuzhiyun old_show_idx = IMAGE_RECALC_IDX;
939*4882a593Smuzhiyun show_idx = IMAGE_RECALC_IDX;
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun /*
942*4882a593Smuzhiyun * Reverse the screen state
943*4882a593Smuzhiyun *
944*4882a593Smuzhiyun * If screen_on=false, means this short key pressed
945*4882a593Smuzhiyun * event turn on the screen and we need show images.
946*4882a593Smuzhiyun *
947*4882a593Smuzhiyun * If screen_on=true, means this short key pressed
948*4882a593Smuzhiyun * event turn off the screen and we never show images.
949*4882a593Smuzhiyun */
950*4882a593Smuzhiyun if (screen_on) {
951*4882a593Smuzhiyun #ifdef CONFIG_ROCKCHIP_EINK_DISPLAY
952*4882a593Smuzhiyun int type = EINK_LOGO_CHARGING_0 << start_idx;
953*4882a593Smuzhiyun /*
954*4882a593Smuzhiyun * Show current battery capacity before suspend
955*4882a593Smuzhiyun * if it's eink display, because eink screen
956*4882a593Smuzhiyun * will continue to display the last image
957*4882a593Smuzhiyun * after suspend, so user can get the
958*4882a593Smuzhiyun * approximate capacity by image displayed.
959*4882a593Smuzhiyun */
960*4882a593Smuzhiyun ret = rockchip_eink_show_charge_logo(type);
961*4882a593Smuzhiyun /* only change the logic if eink display ok */
962*4882a593Smuzhiyun if (ret == 0)
963*4882a593Smuzhiyun old_show_idx = start_idx;
964*4882a593Smuzhiyun #endif
965*4882a593Smuzhiyun charge_show_bmp(NULL); /* Turn off screen */
966*4882a593Smuzhiyun screen_on = false;
967*4882a593Smuzhiyun priv->suspend_delay_timeout = get_timer(0);
968*4882a593Smuzhiyun } else {
969*4882a593Smuzhiyun screen_on = true;
970*4882a593Smuzhiyun }
971*4882a593Smuzhiyun
972*4882a593Smuzhiyun printf("screen %s\n", screen_on ? "on" : "off");
973*4882a593Smuzhiyun } else if (key_state == KEY_PRESS_LONG_DOWN) {
974*4882a593Smuzhiyun /* Set screen_on=true anyway when key long pressed */
975*4882a593Smuzhiyun if (!screen_on)
976*4882a593Smuzhiyun screen_on = true;
977*4882a593Smuzhiyun
978*4882a593Smuzhiyun printf("screen %s\n", screen_on ? "on" : "off");
979*4882a593Smuzhiyun
980*4882a593Smuzhiyun /* Is able to boot now ? */
981*4882a593Smuzhiyun if (soc < pdata->exit_charge_level) {
982*4882a593Smuzhiyun printf("soc=%d%%, threshold soc=%d%%\n",
983*4882a593Smuzhiyun soc, pdata->exit_charge_level);
984*4882a593Smuzhiyun printf("Low power, unable to boot, charging...\n");
985*4882a593Smuzhiyun show_idx = IMAGE_LOWPOWER_IDX(image_num);
986*4882a593Smuzhiyun continue;
987*4882a593Smuzhiyun }
988*4882a593Smuzhiyun
989*4882a593Smuzhiyun if (voltage < pdata->exit_charge_voltage) {
990*4882a593Smuzhiyun printf("voltage=%dmv, threshold voltage=%dmv\n",
991*4882a593Smuzhiyun voltage, pdata->exit_charge_voltage);
992*4882a593Smuzhiyun printf("Low power, unable to boot, charging...\n");
993*4882a593Smuzhiyun show_idx = IMAGE_LOWPOWER_IDX(image_num);
994*4882a593Smuzhiyun continue;
995*4882a593Smuzhiyun }
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun /* Success exit charging */
998*4882a593Smuzhiyun printf("Exit charge animation...\n");
999*4882a593Smuzhiyun charge_show_logo();
1000*4882a593Smuzhiyun break;
1001*4882a593Smuzhiyun } else {
1002*4882a593Smuzhiyun /* Do nothing */
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun
1005*4882a593Smuzhiyun debug("step5 (%d)... \n", screen_on);
1006*4882a593Smuzhiyun
1007*4882a593Smuzhiyun /* Step5: Exit by ctrl+c */
1008*4882a593Smuzhiyun if (ctrlc()) {
1009*4882a593Smuzhiyun if (voltage >= pdata->screen_on_voltage)
1010*4882a593Smuzhiyun charge_show_logo();
1011*4882a593Smuzhiyun printf("Exit charge, due to ctrl+c\n");
1012*4882a593Smuzhiyun break;
1013*4882a593Smuzhiyun }
1014*4882a593Smuzhiyun }
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun if (pdata->auto_wakeup_interval)
1017*4882a593Smuzhiyun autowakeup_timer_uninit();
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun ms = get_timer(charge_start);
1020*4882a593Smuzhiyun if (ms >= 1000) {
1021*4882a593Smuzhiyun sec = ms / 1000;
1022*4882a593Smuzhiyun ms = ms % 1000;
1023*4882a593Smuzhiyun }
1024*4882a593Smuzhiyun
1025*4882a593Smuzhiyun printf("charging time total: %lu.%lus, soc=%d%%, vol=%dmv\n",
1026*4882a593Smuzhiyun sec, ms, soc, voltage);
1027*4882a593Smuzhiyun
1028*4882a593Smuzhiyun return 0;
1029*4882a593Smuzhiyun }
1030*4882a593Smuzhiyun
fg_charger_get_device(struct udevice ** fuel_gauge,struct udevice ** charger)1031*4882a593Smuzhiyun static int fg_charger_get_device(struct udevice **fuel_gauge,
1032*4882a593Smuzhiyun struct udevice **charger)
1033*4882a593Smuzhiyun {
1034*4882a593Smuzhiyun struct udevice *dev;
1035*4882a593Smuzhiyun struct uclass *uc;
1036*4882a593Smuzhiyun int ret, cap;
1037*4882a593Smuzhiyun
1038*4882a593Smuzhiyun *fuel_gauge = NULL,
1039*4882a593Smuzhiyun *charger = NULL;
1040*4882a593Smuzhiyun
1041*4882a593Smuzhiyun ret = uclass_get(UCLASS_FG, &uc);
1042*4882a593Smuzhiyun if (ret)
1043*4882a593Smuzhiyun return ret;
1044*4882a593Smuzhiyun
1045*4882a593Smuzhiyun for (uclass_first_device(UCLASS_FG, &dev);
1046*4882a593Smuzhiyun dev;
1047*4882a593Smuzhiyun uclass_next_device(&dev)) {
1048*4882a593Smuzhiyun cap = fuel_gauge_capability(dev);
1049*4882a593Smuzhiyun if (cap == (FG_CAP_CHARGER | FG_CAP_FUEL_GAUGE)) {
1050*4882a593Smuzhiyun *fuel_gauge = dev;
1051*4882a593Smuzhiyun *charger = NULL;
1052*4882a593Smuzhiyun } else if (cap == FG_CAP_FUEL_GAUGE) {
1053*4882a593Smuzhiyun *fuel_gauge = dev;
1054*4882a593Smuzhiyun } else if (cap == FG_CAP_CHARGER) {
1055*4882a593Smuzhiyun *charger = dev;
1056*4882a593Smuzhiyun }
1057*4882a593Smuzhiyun }
1058*4882a593Smuzhiyun
1059*4882a593Smuzhiyun return (*fuel_gauge) ? 0 : -ENODEV;
1060*4882a593Smuzhiyun }
1061*4882a593Smuzhiyun
1062*4882a593Smuzhiyun static const struct dm_charge_display_ops charge_animation_ops = {
1063*4882a593Smuzhiyun .show = charge_animation_show,
1064*4882a593Smuzhiyun };
1065*4882a593Smuzhiyun
charge_animation_probe(struct udevice * dev)1066*4882a593Smuzhiyun static int charge_animation_probe(struct udevice *dev)
1067*4882a593Smuzhiyun {
1068*4882a593Smuzhiyun struct charge_animation_priv *priv = dev_get_priv(dev);
1069*4882a593Smuzhiyun int ret, soc;
1070*4882a593Smuzhiyun
1071*4882a593Smuzhiyun /* Get PMIC: used for power off system */
1072*4882a593Smuzhiyun ret = uclass_get_device(UCLASS_PMIC, 0, &priv->pmic);
1073*4882a593Smuzhiyun if (ret) {
1074*4882a593Smuzhiyun if (ret == -ENODEV)
1075*4882a593Smuzhiyun printf("Can't find PMIC\n");
1076*4882a593Smuzhiyun else
1077*4882a593Smuzhiyun printf("Get UCLASS PMIC failed: %d\n", ret);
1078*4882a593Smuzhiyun return ret;
1079*4882a593Smuzhiyun }
1080*4882a593Smuzhiyun
1081*4882a593Smuzhiyun /* Get fuel gauge and charger(If need) */
1082*4882a593Smuzhiyun ret = fg_charger_get_device(&priv->fg, &priv->charger);
1083*4882a593Smuzhiyun if (ret) {
1084*4882a593Smuzhiyun if (ret == -ENODEV)
1085*4882a593Smuzhiyun debug("Can't find FG\n");
1086*4882a593Smuzhiyun else
1087*4882a593Smuzhiyun debug("Get UCLASS FG failed: %d\n", ret);
1088*4882a593Smuzhiyun return ret;
1089*4882a593Smuzhiyun }
1090*4882a593Smuzhiyun
1091*4882a593Smuzhiyun /* Get rtc: used for power on */
1092*4882a593Smuzhiyun ret = uclass_get_device(UCLASS_RTC, 0, &priv->rtc);
1093*4882a593Smuzhiyun if (ret) {
1094*4882a593Smuzhiyun if (ret == -ENODEV)
1095*4882a593Smuzhiyun debug("Can't find RTC\n");
1096*4882a593Smuzhiyun else
1097*4882a593Smuzhiyun debug("Get UCLASS RTC failed: %d\n", ret);
1098*4882a593Smuzhiyun }
1099*4882a593Smuzhiyun
1100*4882a593Smuzhiyun /* Get PWRKEY: used for wakeup and turn off/on LCD */
1101*4882a593Smuzhiyun if (!key_exist(KEY_POWER)) {
1102*4882a593Smuzhiyun debug("Can't find power key\n");
1103*4882a593Smuzhiyun return -EINVAL;
1104*4882a593Smuzhiyun }
1105*4882a593Smuzhiyun
1106*4882a593Smuzhiyun /* Initialize charge current */
1107*4882a593Smuzhiyun soc = fuel_gauge_update_get_soc(priv->fg);
1108*4882a593Smuzhiyun if (soc < 0 || soc > 100) {
1109*4882a593Smuzhiyun debug("get soc failed: %d\n", soc);
1110*4882a593Smuzhiyun return -EINVAL;
1111*4882a593Smuzhiyun }
1112*4882a593Smuzhiyun
1113*4882a593Smuzhiyun /* Get leds */
1114*4882a593Smuzhiyun #ifdef CONFIG_LED
1115*4882a593Smuzhiyun ret = led_get_by_label(LED_CHARGING_NAME, &priv->led_charging);
1116*4882a593Smuzhiyun if (!ret)
1117*4882a593Smuzhiyun printf("Found Charging LED\n");
1118*4882a593Smuzhiyun ret = led_get_by_label(LED_CHARGING_FULL_NAME, &priv->led_full);
1119*4882a593Smuzhiyun if (!ret)
1120*4882a593Smuzhiyun printf("Found Charging-Full LED\n");
1121*4882a593Smuzhiyun #endif
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun /* Get charge images */
1124*4882a593Smuzhiyun priv->image = image;
1125*4882a593Smuzhiyun priv->image_num = ARRAY_SIZE(image);
1126*4882a593Smuzhiyun
1127*4882a593Smuzhiyun printf("Enable charge animation display\n");
1128*4882a593Smuzhiyun
1129*4882a593Smuzhiyun return 0;
1130*4882a593Smuzhiyun }
1131*4882a593Smuzhiyun
1132*4882a593Smuzhiyun static const struct udevice_id charge_animation_ids[] = {
1133*4882a593Smuzhiyun { .compatible = "rockchip,uboot-charge" },
1134*4882a593Smuzhiyun { },
1135*4882a593Smuzhiyun };
1136*4882a593Smuzhiyun
1137*4882a593Smuzhiyun U_BOOT_DRIVER(charge_animation) = {
1138*4882a593Smuzhiyun .name = "charge-animation",
1139*4882a593Smuzhiyun .id = UCLASS_CHARGE_DISPLAY,
1140*4882a593Smuzhiyun .probe = charge_animation_probe,
1141*4882a593Smuzhiyun .of_match = charge_animation_ids,
1142*4882a593Smuzhiyun .ops = &charge_animation_ops,
1143*4882a593Smuzhiyun .ofdata_to_platdata = charge_animation_ofdata_to_platdata,
1144*4882a593Smuzhiyun .platdata_auto_alloc_size = sizeof(struct charge_animation_pdata),
1145*4882a593Smuzhiyun .priv_auto_alloc_size = sizeof(struct charge_animation_priv),
1146*4882a593Smuzhiyun };
1147