drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / power / tuxonice_power_off.c
CommitLineData
6fa3eb70
S
1/*
2 * kernel/power/tuxonice_power_off.c
3 *
4 * Copyright (C) 2006-2010 Nigel Cunningham (nigel at tuxonice net)
5 *
6 * This file is released under the GPLv2.
7 *
8 * Support for powering down.
9 */
10
11#include <linux/device.h>
12#include <linux/suspend.h>
13#include <linux/mm.h>
14#include <linux/pm.h>
15#include <linux/reboot.h>
16#include <linux/cpu.h>
17#include <linux/console.h>
18#include <linux/fs.h>
19#include "tuxonice.h"
20#include "tuxonice_ui.h"
21#include "tuxonice_power_off.h"
22#include "tuxonice_sysfs.h"
23#include "tuxonice_modules.h"
24#include "tuxonice_io.h"
25
26unsigned long toi_poweroff_method; /* 0 - Kernel power off */
27EXPORT_SYMBOL_GPL(toi_poweroff_method);
28
29static int wake_delay;
30static char lid_state_file[256], wake_alarm_dir[256];
31static struct file *lid_file, *alarm_file, *epoch_file;
32static int post_wake_state = -1;
33
34static int did_suspend_to_both;
35
36int hybrid_sleep_mode(void)
37{
38 int retval = 0;
39 if (toi_poweroff_method == 3 && did_suspend_to_both == 1)
40 retval = 1;
41 return retval;
42}
43EXPORT_SYMBOL_GPL(hybrid_sleep_mode);
44
45/*
46 * __toi_power_down
47 * Functionality : Powers down or reboots the computer once the image
48 * has been written to disk.
49 * Key Assumptions : Able to reboot/power down via code called or that
50 * the warning emitted if the calls fail will be visible
51 * to the user (ie printk resumes devices).
52 */
53
54static void __toi_power_down(int method)
55{
56 int error;
57
58 toi_cond_pause(1, test_action_state(TOI_REBOOT) ? "Ready to reboot." : "Powering down.");
59
60 if (test_result_state(TOI_ABORTED))
61 goto out;
62
63 if (test_action_state(TOI_REBOOT))
64 kernel_restart(NULL);
65
66 switch (method) {
67 case 0:
68 break;
69 case 3:
70 /*
71 * Re-read the overwritten part of pageset2 to make post-resume
72 * faster.
73 */
74 if (read_pageset2(1))
75 panic("Attempt to reload pagedir 2 failed. " "Try rebooting.");
76
77 pm_prepare_console();
78
79 error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
80 if (!error) {
81 pm_restore_gfp_mask();
82 error = suspend_devices_and_enter(PM_SUSPEND_MEM);
83 pm_restrict_gfp_mask();
84 if (!error)
85 did_suspend_to_both = 1;
86 }
87 pm_notifier_call_chain(PM_POST_SUSPEND);
88 pm_restore_console();
89
90 /* Success - we're now post-resume-from-ram */
91 if (did_suspend_to_both)
92 return;
93
94 /* Failed to suspend to ram - do normal power off */
95 break;
96 case 4:
97 /*
98 * If succeeds, doesn't return. If fails, do a simple
99 * powerdown.
100 */
101 hibernation_platform_enter();
102 break;
103 case 5:
104 /* Historic entry only now */
105 break;
106 }
107
108 if (method && method != 5)
109 toi_cond_pause(1, "Falling back to alternate power off method.");
110
111 if (test_result_state(TOI_ABORTED))
112 goto out;
113
114 kernel_power_off();
115 kernel_halt();
116 toi_cond_pause(1, "Powerdown failed.");
117 while (1)
118 cpu_relax();
119
120 out:
121 if (read_pageset2(1))
122 panic("Attempt to reload pagedir 2 failed. Try rebooting.");
123 return;
124}
125
126#define CLOSE_FILE(file) \
127 if (file) { \
128 filp_close(file, NULL); file = NULL; \
129 }
130
131static void powerdown_cleanup(int toi_or_resume)
132{
133 if (!toi_or_resume)
134 return;
135
136 CLOSE_FILE(lid_file);
137 CLOSE_FILE(alarm_file);
138 CLOSE_FILE(epoch_file);
139}
140
141static void open_file(char *format, char *arg, struct file **var, int mode, char *desc)
142{
143 char buf[256];
144
145 if (strlen(arg)) {
146 sprintf(buf, format, arg);
147 *var = filp_open(buf, mode, 0);
148 if (IS_ERR(*var) || !*var) {
149 printk(KERN_INFO "Failed to open %s file '%s' (%p).\n", desc, buf, *var);
150 *var = NULL;
151 }
152 }
153}
154
155static int powerdown_init(int toi_or_resume)
156{
157 if (!toi_or_resume)
158 return 0;
159
160 did_suspend_to_both = 0;
161
162 open_file("/proc/acpi/button/%s/state", lid_state_file, &lid_file, O_RDONLY, "lid");
163
164 if (strlen(wake_alarm_dir)) {
165 open_file("/sys/class/rtc/%s/wakealarm", wake_alarm_dir,
166 &alarm_file, O_WRONLY, "alarm");
167
168 open_file("/sys/class/rtc/%s/since_epoch", wake_alarm_dir,
169 &epoch_file, O_RDONLY, "epoch");
170 }
171
172 return 0;
173}
174
175static int lid_closed(void)
176{
177 char array[25];
178 ssize_t size;
179 loff_t pos = 0;
180
181 if (!lid_file)
182 return 0;
183
184 size = vfs_read(lid_file, (char __user *)array, 25, &pos);
185 if ((int)size < 1) {
186 printk(KERN_INFO "Failed to read lid state file (%d).\n", (int)size);
187 return 0;
188 }
189
190 if (!strcmp(array, "state: closed\n"))
191 return 1;
192
193 return 0;
194}
195
196static void write_alarm_file(int value)
197{
198 ssize_t size;
199 char buf[40];
200 loff_t pos = 0;
201
202 if (!alarm_file)
203 return;
204
205 sprintf(buf, "%d\n", value);
206
207 size = vfs_write(alarm_file, (char __user *)buf, strlen(buf), &pos);
208
209 if (size < 0)
210 printk(KERN_INFO "Error %d writing alarm value %s.\n", (int)size, buf);
211}
212
213/**
214 * toi_check_resleep: See whether to powerdown again after waking.
215 *
216 * After waking, check whether we should powerdown again in a (usually
217 * different) way. We only do this if the lid switch is still closed.
218 */
219void toi_check_resleep(void)
220{
221 /* We only return if we suspended to ram and woke. */
222 if (lid_closed() && post_wake_state >= 0)
223 __toi_power_down(post_wake_state);
224}
225
226void toi_power_down(void)
227{
228 if (alarm_file && wake_delay) {
229 char array[25];
230 loff_t pos = 0;
231 size_t size = vfs_read(epoch_file, (char __user *)array, 25,
232 &pos);
233
234 if (((int)size) < 1)
235 printk(KERN_INFO "Failed to read epoch file (%d).\n", (int)size);
236 else {
237 unsigned long since_epoch;
238 if (!strict_strtoul(array, 0, &since_epoch)) {
239 /* Clear any wakeup time. */
240 write_alarm_file(0);
241
242 /* Set new wakeup time. */
243 write_alarm_file(since_epoch + wake_delay);
244 }
245 }
246 }
247
248 __toi_power_down(toi_poweroff_method);
249
250 toi_check_resleep();
251}
252EXPORT_SYMBOL_GPL(toi_power_down);
253
254static struct toi_sysfs_data sysfs_params[] = {
255#if defined(CONFIG_ACPI)
256 SYSFS_STRING("lid_file", SYSFS_RW, lid_state_file, 256, 0, NULL),
257 SYSFS_INT("wake_delay", SYSFS_RW, &wake_delay, 0, INT_MAX, 0, NULL),
258 SYSFS_STRING("wake_alarm_dir", SYSFS_RW, wake_alarm_dir, 256, 0, NULL),
259 SYSFS_INT("post_wake_state", SYSFS_RW, &post_wake_state, -1, 5, 0,
260 NULL),
261#endif
262 SYSFS_UL("powerdown_method", SYSFS_RW, &toi_poweroff_method, 0, 5, 0),
263 SYSFS_INT("did_suspend_to_both", SYSFS_READONLY, &did_suspend_to_both,
264 0, 0, 0, NULL)
265};
266
267static struct toi_module_ops powerdown_ops = {
268 .type = MISC_HIDDEN_MODULE,
269 .name = "poweroff",
270 .initialise = powerdown_init,
271 .cleanup = powerdown_cleanup,
272 .directory = "[ROOT]",
273 .module = THIS_MODULE,
274 .sysfs_data = sysfs_params,
275 .num_sysfs_entries = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data),
276};
277
278int toi_poweroff_init(void)
279{
280 return toi_register_module(&powerdown_ops);
281}
282
283void toi_poweroff_exit(void)
284{
285 toi_unregister_module(&powerdown_ops);
286}