drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / power / tuxonice_sysfs.c
CommitLineData
6fa3eb70
S
1/*
2 * kernel/power/tuxonice_sysfs.c
3 *
4 * Copyright (C) 2002-2010 Nigel Cunningham (nigel at tuxonice net)
5 *
6 * This file is released under the GPLv2.
7 *
8 * This file contains support for sysfs entries for tuning TuxOnIce.
9 *
10 * We have a generic handler that deals with the most common cases, and
11 * hooks for special handlers to use.
12 */
13
14#include <linux/suspend.h>
15
16#include "tuxonice_sysfs.h"
17#include "tuxonice.h"
18#include "tuxonice_storage.h"
19#include "tuxonice_alloc.h"
20
21static int toi_sysfs_initialised;
22
23static void toi_initialise_sysfs(void);
24
25static struct toi_sysfs_data sysfs_params[];
26
27#define to_sysfs_data(_attr) container_of(_attr, struct toi_sysfs_data, attr)
28
29static void toi_main_wrapper(void)
30{
31 toi_try_hibernate();
32}
33
34static ssize_t toi_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
35{
36 struct toi_sysfs_data *sysfs_data = to_sysfs_data(attr);
37 int len = 0;
38 int full_prep = sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ;
39
40 if (full_prep && toi_start_anything(0))
41 return -EBUSY;
42
43 if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ)
44 toi_prepare_usm();
45
46 switch (sysfs_data->type) {
47 case TOI_SYSFS_DATA_CUSTOM:
48 len = (sysfs_data->data.special.read_sysfs) ?
49 (sysfs_data->data.special.read_sysfs) (page, PAGE_SIZE)
50 : 0;
51 break;
52 case TOI_SYSFS_DATA_BIT:
53 len = sprintf(page, "%d\n",
54 -test_bit(sysfs_data->data.bit.bit, sysfs_data->data.bit.bit_vector));
55 break;
56 case TOI_SYSFS_DATA_INTEGER:
57 len = sprintf(page, "%d\n", *(sysfs_data->data.integer.variable));
58 break;
59 case TOI_SYSFS_DATA_LONG:
60 len = sprintf(page, "%ld\n", *(sysfs_data->data.a_long.variable));
61 break;
62 case TOI_SYSFS_DATA_UL:
63 len = sprintf(page, "%lu\n", *(sysfs_data->data.ul.variable));
64 break;
65 case TOI_SYSFS_DATA_STRING:
66 len = sprintf(page, "%s\n", sysfs_data->data.string.variable);
67 break;
68 }
69
70 if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_READ)
71 toi_cleanup_usm();
72
73 if (full_prep)
74 toi_finish_anything(0);
75
76 return len;
77}
78
79#define BOUND(_variable, _type) do { \
80 if (*_variable < sysfs_data->data._type.minimum) \
81 *_variable = sysfs_data->data._type.minimum; \
82 else if (*_variable > sysfs_data->data._type.maximum) \
83 *_variable = sysfs_data->data._type.maximum; \
84} while (0)
85
86static ssize_t toi_attr_store(struct kobject *kobj, struct attribute *attr,
87 const char *my_buf, size_t count)
88{
89 int assigned_temp_buffer = 0, result = count;
90 struct toi_sysfs_data *sysfs_data = to_sysfs_data(attr);
91
92 if (toi_start_anything((sysfs_data->flags & SYSFS_HIBERNATE_OR_RESUME)))
93 return -EBUSY;
94
95 ((char *)my_buf)[count] = 0;
96
97 if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_WRITE)
98 toi_prepare_usm();
99
100 switch (sysfs_data->type) {
101 case TOI_SYSFS_DATA_CUSTOM:
102 if (sysfs_data->data.special.write_sysfs)
103 result = (sysfs_data->data.special.write_sysfs) (my_buf, count);
104 break;
105 case TOI_SYSFS_DATA_BIT:
106 {
107 unsigned long value;
108 result = strict_strtoul(my_buf, 0, &value);
109 if (result)
110 break;
111 if (value)
112 set_bit(sysfs_data->data.bit.bit,
113 (sysfs_data->data.bit.bit_vector));
114 else
115 clear_bit(sysfs_data->data.bit.bit,
116 (sysfs_data->data.bit.bit_vector));
117 }
118 break;
119 case TOI_SYSFS_DATA_INTEGER:
120 {
121 long temp;
122 result = strict_strtol(my_buf, 0, &temp);
123 if (result)
124 break;
125 *(sysfs_data->data.integer.variable) = (int)temp;
126 BOUND(sysfs_data->data.integer.variable, integer);
127 break;
128 }
129 case TOI_SYSFS_DATA_LONG:
130 {
131 long *variable = sysfs_data->data.a_long.variable;
132 result = strict_strtol(my_buf, 0, variable);
133 if (result)
134 break;
135 BOUND(variable, a_long);
136 break;
137 }
138 case TOI_SYSFS_DATA_UL:
139 {
140 unsigned long *variable = sysfs_data->data.ul.variable;
141 result = strict_strtoul(my_buf, 0, variable);
142 if (result)
143 break;
144 BOUND(variable, ul);
145 break;
146 }
147 break;
148 case TOI_SYSFS_DATA_STRING:
149 {
150 int copy_len = count;
151 char *variable = sysfs_data->data.string.variable;
152
153 if (sysfs_data->data.string.max_length &&
154 (copy_len > sysfs_data->data.string.max_length))
155 copy_len = sysfs_data->data.string.max_length;
156
157 if (!variable) {
158 variable = (char *)toi_get_zeroed_page(31, TOI_ATOMIC_GFP);
159 sysfs_data->data.string.variable = variable;
160 assigned_temp_buffer = 1;
161 }
162 strncpy(variable, my_buf, copy_len);
163 if (copy_len && my_buf[copy_len - 1] == '\n')
164 variable[count - 1] = 0;
165 variable[count] = 0;
166 }
167 break;
168 }
169
170 if (!result)
171 result = count;
172
173 /* Side effect routine? */
174 if (result == count && sysfs_data->write_side_effect)
175 sysfs_data->write_side_effect();
176
177 /* Free temporary buffers */
178 if (assigned_temp_buffer) {
179 toi_free_page(31, (unsigned long)sysfs_data->data.string.variable);
180 sysfs_data->data.string.variable = NULL;
181 }
182
183 if (sysfs_data->flags & SYSFS_NEEDS_SM_FOR_WRITE)
184 toi_cleanup_usm();
185
186 toi_finish_anything(sysfs_data->flags & SYSFS_HIBERNATE_OR_RESUME);
187
188 return result;
189}
190
191static struct sysfs_ops toi_sysfs_ops = {
192 .show = &toi_attr_show,
193 .store = &toi_attr_store,
194};
195
196static struct kobj_type toi_ktype = {
197 .sysfs_ops = &toi_sysfs_ops,
198};
199
200struct kobject *tuxonice_kobj;
201
202/* Non-module sysfs entries.
203 *
204 * This array contains entries that are automatically registered at
205 * boot. Modules and the console code register their own entries separately.
206 */
207
208static struct toi_sysfs_data sysfs_params[] = {
209 SYSFS_CUSTOM("do_hibernate", SYSFS_WRITEONLY, NULL, NULL,
210 SYSFS_HIBERNATING, toi_main_wrapper),
211 SYSFS_CUSTOM("do_resume", SYSFS_WRITEONLY, NULL, NULL,
212 SYSFS_RESUMING, toi_try_resume)
213};
214
215void remove_toi_sysdir(struct kobject *kobj)
216{
217 if (!kobj)
218 return;
219
220 kobject_put(kobj);
221}
222
223struct kobject *make_toi_sysdir(char *name)
224{
225 struct kobject *kobj = kobject_create_and_add(name, tuxonice_kobj);
226
227 if (!kobj) {
228 printk(KERN_INFO "TuxOnIce: Can't allocate kobject for sysfs " "dir!\n");
229 return NULL;
230 }
231
232 kobj->ktype = &toi_ktype;
233
234 return kobj;
235}
236
237/* toi_register_sysfs_file
238 *
239 * Helper for registering a new /sysfs/tuxonice entry.
240 */
241
242int toi_register_sysfs_file(struct kobject *kobj, struct toi_sysfs_data *toi_sysfs_data)
243{
244 int result;
245
246 if (!toi_sysfs_initialised)
247 toi_initialise_sysfs();
248
249 result = sysfs_create_file(kobj, &toi_sysfs_data->attr);
250 if (result)
251 printk(KERN_INFO "TuxOnIce: sysfs_create_file for %s "
252 "returned %d.\n", toi_sysfs_data->attr.name, result);
253 kobj->ktype = &toi_ktype;
254
255 return result;
256}
257EXPORT_SYMBOL_GPL(toi_register_sysfs_file);
258
259/* toi_unregister_sysfs_file
260 *
261 * Helper for removing unwanted /sys/power/tuxonice entries.
262 *
263 */
264void toi_unregister_sysfs_file(struct kobject *kobj, struct toi_sysfs_data *toi_sysfs_data)
265{
266 sysfs_remove_file(kobj, &toi_sysfs_data->attr);
267}
268EXPORT_SYMBOL_GPL(toi_unregister_sysfs_file);
269
270void toi_cleanup_sysfs(void)
271{
272 int i, numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data);
273
274 if (!toi_sysfs_initialised)
275 return;
276
277 for (i = 0; i < numfiles; i++)
278 toi_unregister_sysfs_file(tuxonice_kobj, &sysfs_params[i]);
279
280 kobject_put(tuxonice_kobj);
281 toi_sysfs_initialised = 0;
282}
283
284/* toi_initialise_sysfs
285 *
286 * Initialise the /sysfs/tuxonice directory.
287 */
288
289static void toi_initialise_sysfs(void)
290{
291 int i;
292 int numfiles = sizeof(sysfs_params) / sizeof(struct toi_sysfs_data);
293
294 if (toi_sysfs_initialised)
295 return;
296
297 /* Make our TuxOnIce directory a child of /sys/power */
298 tuxonice_kobj = kobject_create_and_add("tuxonice", power_kobj);
299 if (!tuxonice_kobj)
300 return;
301
302 toi_sysfs_initialised = 1;
303
304 for (i = 0; i < numfiles; i++)
305 toi_register_sysfs_file(tuxonice_kobj, &sysfs_params[i]);
306}
307
308int toi_sysfs_init(void)
309{
310 toi_initialise_sysfs();
311 return 0;
312}
313
314void toi_sysfs_exit(void)
315{
316 toi_cleanup_sysfs();
317}