Commit | Line | Data |
---|---|---|
4a11b59d AV |
1 | /* |
2 | * Universal power supply monitor class | |
3 | * | |
4 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | |
5 | * Copyright © 2004 Szabolcs Gyurko | |
6 | * Copyright © 2003 Ian Molton <spyro@f2s.com> | |
7 | * | |
8 | * Modified: 2004, Oct Szabolcs Gyurko | |
9 | * | |
10 | * You may use this code as per GPL version 2 | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/types.h> | |
15 | #include <linux/init.h> | |
5f487cd3 | 16 | #include <linux/slab.h> |
4a11b59d | 17 | #include <linux/device.h> |
d36240d2 | 18 | #include <linux/notifier.h> |
4a11b59d AV |
19 | #include <linux/err.h> |
20 | #include <linux/power_supply.h> | |
3be330bf | 21 | #include <linux/thermal.h> |
4a11b59d AV |
22 | #include "power_supply.h" |
23 | ||
ff3417e7 | 24 | /* exported for the APM Power driver, APM emulation */ |
4a11b59d | 25 | struct class *power_supply_class; |
ff3417e7 | 26 | EXPORT_SYMBOL_GPL(power_supply_class); |
4a11b59d | 27 | |
d36240d2 PR |
28 | ATOMIC_NOTIFIER_HEAD(power_supply_notifier); |
29 | EXPORT_SYMBOL_GPL(power_supply_notifier); | |
30 | ||
5f487cd3 AV |
31 | static struct device_type power_supply_dev_type; |
32 | ||
5e0848c6 RK |
33 | static bool __power_supply_is_supplied_by(struct power_supply *supplier, |
34 | struct power_supply *supply) | |
35 | { | |
36 | int i; | |
37 | ||
38 | if (!supply->supplied_from && !supplier->supplied_to) | |
39 | return false; | |
40 | ||
41 | /* Support both supplied_to and supplied_from modes */ | |
42 | if (supply->supplied_from) { | |
297d716f | 43 | if (!supplier->desc->name) |
5e0848c6 RK |
44 | return false; |
45 | for (i = 0; i < supply->num_supplies; i++) | |
297d716f | 46 | if (!strcmp(supplier->desc->name, supply->supplied_from[i])) |
5e0848c6 RK |
47 | return true; |
48 | } else { | |
297d716f | 49 | if (!supply->desc->name) |
5e0848c6 RK |
50 | return false; |
51 | for (i = 0; i < supplier->num_supplicants; i++) | |
297d716f | 52 | if (!strcmp(supplier->supplied_to[i], supply->desc->name)) |
5e0848c6 RK |
53 | return true; |
54 | } | |
55 | ||
56 | return false; | |
57 | } | |
58 | ||
443cad92 DY |
59 | static int __power_supply_changed_work(struct device *dev, void *data) |
60 | { | |
e80cf421 | 61 | struct power_supply *psy = data; |
443cad92 | 62 | struct power_supply *pst = dev_get_drvdata(dev); |
443cad92 | 63 | |
5e0848c6 | 64 | if (__power_supply_is_supplied_by(psy, pst)) { |
297d716f KK |
65 | if (pst->desc->external_power_changed) |
66 | pst->desc->external_power_changed(pst); | |
5e0848c6 RK |
67 | } |
68 | ||
443cad92 DY |
69 | return 0; |
70 | } | |
71 | ||
4a11b59d AV |
72 | static void power_supply_changed_work(struct work_struct *work) |
73 | { | |
948dcf96 | 74 | unsigned long flags; |
4a11b59d AV |
75 | struct power_supply *psy = container_of(work, struct power_supply, |
76 | changed_work); | |
4a11b59d | 77 | |
297d716f | 78 | dev_dbg(&psy->dev, "%s\n", __func__); |
4a11b59d | 79 | |
948dcf96 | 80 | spin_lock_irqsave(&psy->changed_lock, flags); |
061f3806 VK |
81 | /* |
82 | * Check 'changed' here to avoid issues due to race between | |
83 | * power_supply_changed() and this routine. In worst case | |
84 | * power_supply_changed() can be called again just before we take above | |
85 | * lock. During the first call of this routine we will mark 'changed' as | |
86 | * false and it will stay false for the next call as well. | |
87 | */ | |
88 | if (likely(psy->changed)) { | |
948dcf96 ZM |
89 | psy->changed = false; |
90 | spin_unlock_irqrestore(&psy->changed_lock, flags); | |
91 | class_for_each_device(power_supply_class, NULL, psy, | |
92 | __power_supply_changed_work); | |
93 | power_supply_update_leds(psy); | |
d36240d2 PR |
94 | atomic_notifier_call_chain(&power_supply_notifier, |
95 | PSY_EVENT_PROP_CHANGED, psy); | |
297d716f | 96 | kobject_uevent(&psy->dev.kobj, KOBJ_CHANGE); |
948dcf96 ZM |
97 | spin_lock_irqsave(&psy->changed_lock, flags); |
98 | } | |
061f3806 | 99 | |
948dcf96 | 100 | /* |
061f3806 VK |
101 | * Hold the wakeup_source until all events are processed. |
102 | * power_supply_changed() might have called again and have set 'changed' | |
103 | * to true. | |
948dcf96 | 104 | */ |
061f3806 | 105 | if (likely(!psy->changed)) |
297d716f | 106 | pm_relax(&psy->dev); |
948dcf96 | 107 | spin_unlock_irqrestore(&psy->changed_lock, flags); |
4a11b59d AV |
108 | } |
109 | ||
110 | void power_supply_changed(struct power_supply *psy) | |
111 | { | |
948dcf96 ZM |
112 | unsigned long flags; |
113 | ||
297d716f | 114 | dev_dbg(&psy->dev, "%s\n", __func__); |
4a11b59d | 115 | |
948dcf96 ZM |
116 | spin_lock_irqsave(&psy->changed_lock, flags); |
117 | psy->changed = true; | |
297d716f | 118 | pm_stay_awake(&psy->dev); |
948dcf96 | 119 | spin_unlock_irqrestore(&psy->changed_lock, flags); |
4a11b59d | 120 | schedule_work(&psy->changed_work); |
4a11b59d | 121 | } |
ff3417e7 | 122 | EXPORT_SYMBOL_GPL(power_supply_changed); |
4a11b59d | 123 | |
f6e0b081 RK |
124 | #ifdef CONFIG_OF |
125 | #include <linux/of.h> | |
126 | ||
127 | static int __power_supply_populate_supplied_from(struct device *dev, | |
128 | void *data) | |
129 | { | |
e80cf421 | 130 | struct power_supply *psy = data; |
f6e0b081 RK |
131 | struct power_supply *epsy = dev_get_drvdata(dev); |
132 | struct device_node *np; | |
133 | int i = 0; | |
134 | ||
135 | do { | |
136 | np = of_parse_phandle(psy->of_node, "power-supplies", i++); | |
137 | if (!np) | |
a0f93b42 | 138 | break; |
f6e0b081 RK |
139 | |
140 | if (np == epsy->of_node) { | |
297d716f KK |
141 | dev_info(&psy->dev, "%s: Found supply : %s\n", |
142 | psy->desc->name, epsy->desc->name); | |
143 | psy->supplied_from[i-1] = (char *)epsy->desc->name; | |
f6e0b081 | 144 | psy->num_supplies++; |
2054d6e9 | 145 | of_node_put(np); |
f6e0b081 RK |
146 | break; |
147 | } | |
2054d6e9 | 148 | of_node_put(np); |
f6e0b081 RK |
149 | } while (np); |
150 | ||
151 | return 0; | |
152 | } | |
153 | ||
154 | static int power_supply_populate_supplied_from(struct power_supply *psy) | |
155 | { | |
156 | int error; | |
157 | ||
158 | error = class_for_each_device(power_supply_class, NULL, psy, | |
159 | __power_supply_populate_supplied_from); | |
160 | ||
297d716f | 161 | dev_dbg(&psy->dev, "%s %d\n", __func__, error); |
f6e0b081 RK |
162 | |
163 | return error; | |
164 | } | |
165 | ||
166 | static int __power_supply_find_supply_from_node(struct device *dev, | |
167 | void *data) | |
168 | { | |
e80cf421 | 169 | struct device_node *np = data; |
f6e0b081 RK |
170 | struct power_supply *epsy = dev_get_drvdata(dev); |
171 | ||
585b0087 | 172 | /* returning non-zero breaks out of class_for_each_device loop */ |
f6e0b081 | 173 | if (epsy->of_node == np) |
585b0087 | 174 | return 1; |
f6e0b081 RK |
175 | |
176 | return 0; | |
177 | } | |
178 | ||
179 | static int power_supply_find_supply_from_node(struct device_node *supply_node) | |
180 | { | |
181 | int error; | |
f6e0b081 RK |
182 | |
183 | /* | |
585b0087 VK |
184 | * class_for_each_device() either returns its own errors or values |
185 | * returned by __power_supply_find_supply_from_node(). | |
186 | * | |
187 | * __power_supply_find_supply_from_node() will return 0 (no match) | |
188 | * or 1 (match). | |
189 | * | |
190 | * We return 0 if class_for_each_device() returned 1, -EPROBE_DEFER if | |
191 | * it returned 0, or error as returned by it. | |
f6e0b081 RK |
192 | */ |
193 | error = class_for_each_device(power_supply_class, NULL, supply_node, | |
194 | __power_supply_find_supply_from_node); | |
195 | ||
585b0087 | 196 | return error ? (error == 1 ? 0 : error) : -EPROBE_DEFER; |
f6e0b081 RK |
197 | } |
198 | ||
199 | static int power_supply_check_supplies(struct power_supply *psy) | |
200 | { | |
201 | struct device_node *np; | |
202 | int cnt = 0; | |
203 | ||
204 | /* If there is already a list honor it */ | |
205 | if (psy->supplied_from && psy->num_supplies > 0) | |
206 | return 0; | |
207 | ||
208 | /* No device node found, nothing to do */ | |
209 | if (!psy->of_node) | |
210 | return 0; | |
211 | ||
212 | do { | |
213 | int ret; | |
214 | ||
215 | np = of_parse_phandle(psy->of_node, "power-supplies", cnt++); | |
216 | if (!np) | |
a0f93b42 | 217 | break; |
f6e0b081 RK |
218 | |
219 | ret = power_supply_find_supply_from_node(np); | |
8468b029 VK |
220 | of_node_put(np); |
221 | ||
f6e0b081 | 222 | if (ret) { |
297d716f | 223 | dev_dbg(&psy->dev, "Failed to find supply!\n"); |
f5b89aff | 224 | return ret; |
f6e0b081 RK |
225 | } |
226 | } while (np); | |
227 | ||
f9c85486 VK |
228 | /* Missing valid "power-supplies" entries */ |
229 | if (cnt == 1) | |
230 | return 0; | |
231 | ||
f6e0b081 | 232 | /* All supplies found, allocate char ** array for filling */ |
297d716f | 233 | psy->supplied_from = devm_kzalloc(&psy->dev, sizeof(psy->supplied_from), |
f6e0b081 RK |
234 | GFP_KERNEL); |
235 | if (!psy->supplied_from) { | |
297d716f | 236 | dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); |
f6e0b081 RK |
237 | return -ENOMEM; |
238 | } | |
239 | ||
297d716f KK |
240 | *psy->supplied_from = devm_kzalloc(&psy->dev, |
241 | sizeof(char *) * (cnt - 1), | |
f6e0b081 RK |
242 | GFP_KERNEL); |
243 | if (!*psy->supplied_from) { | |
297d716f | 244 | dev_err(&psy->dev, "Couldn't allocate memory for supply list\n"); |
f6e0b081 RK |
245 | return -ENOMEM; |
246 | } | |
247 | ||
248 | return power_supply_populate_supplied_from(psy); | |
249 | } | |
250 | #else | |
251 | static inline int power_supply_check_supplies(struct power_supply *psy) | |
252 | { | |
253 | return 0; | |
254 | } | |
255 | #endif | |
256 | ||
443cad92 | 257 | static int __power_supply_am_i_supplied(struct device *dev, void *data) |
4a11b59d AV |
258 | { |
259 | union power_supply_propval ret = {0,}; | |
e80cf421 | 260 | struct power_supply *psy = data; |
443cad92 | 261 | struct power_supply *epsy = dev_get_drvdata(dev); |
443cad92 | 262 | |
5e0848c6 | 263 | if (__power_supply_is_supplied_by(epsy, psy)) |
297d716f KK |
264 | if (!epsy->desc->get_property(epsy, POWER_SUPPLY_PROP_ONLINE, |
265 | &ret)) | |
1c42a389 | 266 | return ret.intval; |
5e0848c6 | 267 | |
443cad92 DY |
268 | return 0; |
269 | } | |
270 | ||
271 | int power_supply_am_i_supplied(struct power_supply *psy) | |
272 | { | |
273 | int error; | |
274 | ||
93562b53 | 275 | error = class_for_each_device(power_supply_class, NULL, psy, |
443cad92 | 276 | __power_supply_am_i_supplied); |
4a11b59d | 277 | |
297d716f | 278 | dev_dbg(&psy->dev, "%s %d\n", __func__, error); |
4a11b59d | 279 | |
443cad92 | 280 | return error; |
4a11b59d | 281 | } |
ff3417e7 | 282 | EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); |
4a11b59d | 283 | |
942ed161 MG |
284 | static int __power_supply_is_system_supplied(struct device *dev, void *data) |
285 | { | |
286 | union power_supply_propval ret = {0,}; | |
287 | struct power_supply *psy = dev_get_drvdata(dev); | |
2530daa1 | 288 | unsigned int *count = data; |
942ed161 | 289 | |
2530daa1 | 290 | (*count)++; |
297d716f KK |
291 | if (psy->desc->type != POWER_SUPPLY_TYPE_BATTERY) |
292 | if (!psy->desc->get_property(psy, POWER_SUPPLY_PROP_ONLINE, | |
293 | &ret)) | |
942ed161 | 294 | return ret.intval; |
1c42a389 | 295 | |
942ed161 MG |
296 | return 0; |
297 | } | |
298 | ||
299 | int power_supply_is_system_supplied(void) | |
300 | { | |
301 | int error; | |
2530daa1 | 302 | unsigned int count = 0; |
942ed161 | 303 | |
2530daa1 | 304 | error = class_for_each_device(power_supply_class, NULL, &count, |
942ed161 MG |
305 | __power_supply_is_system_supplied); |
306 | ||
2530daa1 JD |
307 | /* |
308 | * If no power class device was found at all, most probably we are | |
309 | * running on a desktop system, so assume we are on mains power. | |
310 | */ | |
311 | if (count == 0) | |
312 | return 1; | |
313 | ||
942ed161 MG |
314 | return error; |
315 | } | |
ff3417e7 | 316 | EXPORT_SYMBOL_GPL(power_supply_is_system_supplied); |
942ed161 | 317 | |
e5f5ccb6 DM |
318 | int power_supply_set_battery_charged(struct power_supply *psy) |
319 | { | |
bc154056 | 320 | if (atomic_read(&psy->use_cnt) >= 0 && |
297d716f KK |
321 | psy->desc->type == POWER_SUPPLY_TYPE_BATTERY && |
322 | psy->desc->set_charged) { | |
323 | psy->desc->set_charged(psy); | |
e5f5ccb6 DM |
324 | return 0; |
325 | } | |
326 | ||
327 | return -EINVAL; | |
328 | } | |
329 | EXPORT_SYMBOL_GPL(power_supply_set_battery_charged); | |
330 | ||
9f3b795a | 331 | static int power_supply_match_device_by_name(struct device *dev, const void *data) |
e5f5ccb6 DM |
332 | { |
333 | const char *name = data; | |
334 | struct power_supply *psy = dev_get_drvdata(dev); | |
335 | ||
297d716f | 336 | return strcmp(psy->desc->name, name) == 0; |
e5f5ccb6 DM |
337 | } |
338 | ||
1a352462 KK |
339 | /** |
340 | * power_supply_get_by_name() - Search for a power supply and returns its ref | |
341 | * @name: Power supply name to fetch | |
342 | * | |
343 | * If power supply was found, it increases reference count for the | |
344 | * internal power supply's device. The user should power_supply_put() | |
345 | * after usage. | |
346 | * | |
347 | * Return: On success returns a reference to a power supply with | |
348 | * matching name equals to @name, a NULL otherwise. | |
349 | */ | |
9f3b795a | 350 | struct power_supply *power_supply_get_by_name(const char *name) |
e5f5ccb6 DM |
351 | { |
352 | struct device *dev = class_find_device(power_supply_class, NULL, name, | |
353 | power_supply_match_device_by_name); | |
354 | ||
355 | return dev ? dev_get_drvdata(dev) : NULL; | |
356 | } | |
357 | EXPORT_SYMBOL_GPL(power_supply_get_by_name); | |
358 | ||
1a352462 KK |
359 | /** |
360 | * power_supply_put() - Drop reference obtained with power_supply_get_by_name | |
361 | * @psy: Reference to put | |
362 | * | |
363 | * The reference to power supply should be put before unregistering | |
364 | * the power supply. | |
365 | */ | |
366 | void power_supply_put(struct power_supply *psy) | |
367 | { | |
368 | might_sleep(); | |
369 | ||
370 | put_device(&psy->dev); | |
371 | } | |
372 | EXPORT_SYMBOL_GPL(power_supply_put); | |
373 | ||
abce9770 SR |
374 | #ifdef CONFIG_OF |
375 | static int power_supply_match_device_node(struct device *dev, const void *data) | |
376 | { | |
377 | return dev->parent && dev->parent->of_node == data; | |
378 | } | |
379 | ||
1a352462 KK |
380 | /** |
381 | * power_supply_get_by_phandle() - Search for a power supply and returns its ref | |
382 | * @np: Pointer to device node holding phandle property | |
383 | * @phandle_name: Name of property holding a power supply name | |
384 | * | |
385 | * If power supply was found, it increases reference count for the | |
386 | * internal power supply's device. The user should power_supply_put() | |
387 | * after usage. | |
388 | * | |
389 | * Return: On success returns a reference to a power supply with | |
390 | * matching name equals to value under @property, NULL or ERR_PTR otherwise. | |
391 | */ | |
abce9770 SR |
392 | struct power_supply *power_supply_get_by_phandle(struct device_node *np, |
393 | const char *property) | |
394 | { | |
395 | struct device_node *power_supply_np; | |
396 | struct device *dev; | |
397 | ||
398 | power_supply_np = of_parse_phandle(np, property, 0); | |
399 | if (!power_supply_np) | |
400 | return ERR_PTR(-ENODEV); | |
401 | ||
402 | dev = class_find_device(power_supply_class, NULL, power_supply_np, | |
403 | power_supply_match_device_node); | |
404 | ||
405 | of_node_put(power_supply_np); | |
406 | ||
407 | return dev ? dev_get_drvdata(dev) : NULL; | |
408 | } | |
409 | EXPORT_SYMBOL_GPL(power_supply_get_by_phandle); | |
410 | #endif /* CONFIG_OF */ | |
411 | ||
bc154056 KK |
412 | int power_supply_get_property(struct power_supply *psy, |
413 | enum power_supply_property psp, | |
414 | union power_supply_propval *val) | |
415 | { | |
416 | if (atomic_read(&psy->use_cnt) <= 0) | |
417 | return -ENODEV; | |
418 | ||
297d716f | 419 | return psy->desc->get_property(psy, psp, val); |
bc154056 KK |
420 | } |
421 | EXPORT_SYMBOL_GPL(power_supply_get_property); | |
422 | ||
423 | int power_supply_set_property(struct power_supply *psy, | |
424 | enum power_supply_property psp, | |
425 | const union power_supply_propval *val) | |
426 | { | |
297d716f | 427 | if (atomic_read(&psy->use_cnt) <= 0 || !psy->desc->set_property) |
bc154056 KK |
428 | return -ENODEV; |
429 | ||
297d716f | 430 | return psy->desc->set_property(psy, psp, val); |
bc154056 KK |
431 | } |
432 | EXPORT_SYMBOL_GPL(power_supply_set_property); | |
433 | ||
434 | int power_supply_property_is_writeable(struct power_supply *psy, | |
435 | enum power_supply_property psp) | |
436 | { | |
297d716f KK |
437 | if (atomic_read(&psy->use_cnt) <= 0 || |
438 | !psy->desc->property_is_writeable) | |
bc154056 KK |
439 | return -ENODEV; |
440 | ||
297d716f | 441 | return psy->desc->property_is_writeable(psy, psp); |
bc154056 KK |
442 | } |
443 | EXPORT_SYMBOL_GPL(power_supply_property_is_writeable); | |
444 | ||
445 | void power_supply_external_power_changed(struct power_supply *psy) | |
446 | { | |
297d716f KK |
447 | if (atomic_read(&psy->use_cnt) <= 0 || |
448 | !psy->desc->external_power_changed) | |
bc154056 KK |
449 | return; |
450 | ||
297d716f | 451 | psy->desc->external_power_changed(psy); |
bc154056 KK |
452 | } |
453 | EXPORT_SYMBOL_GPL(power_supply_external_power_changed); | |
454 | ||
83516651 JF |
455 | int power_supply_powers(struct power_supply *psy, struct device *dev) |
456 | { | |
297d716f | 457 | return sysfs_create_link(&psy->dev.kobj, &dev->kobj, "powers"); |
83516651 JF |
458 | } |
459 | EXPORT_SYMBOL_GPL(power_supply_powers); | |
460 | ||
5f487cd3 AV |
461 | static void power_supply_dev_release(struct device *dev) |
462 | { | |
297d716f | 463 | struct power_supply *psy = container_of(dev, struct power_supply, dev); |
5f487cd3 | 464 | pr_debug("device: '%s': %s\n", dev_name(dev), __func__); |
297d716f | 465 | kfree(psy); |
5f487cd3 AV |
466 | } |
467 | ||
d36240d2 PR |
468 | int power_supply_reg_notifier(struct notifier_block *nb) |
469 | { | |
470 | return atomic_notifier_chain_register(&power_supply_notifier, nb); | |
471 | } | |
472 | EXPORT_SYMBOL_GPL(power_supply_reg_notifier); | |
473 | ||
474 | void power_supply_unreg_notifier(struct notifier_block *nb) | |
475 | { | |
476 | atomic_notifier_chain_unregister(&power_supply_notifier, nb); | |
477 | } | |
478 | EXPORT_SYMBOL_GPL(power_supply_unreg_notifier); | |
479 | ||
3be330bf JT |
480 | #ifdef CONFIG_THERMAL |
481 | static int power_supply_read_temp(struct thermal_zone_device *tzd, | |
482 | unsigned long *temp) | |
483 | { | |
484 | struct power_supply *psy; | |
485 | union power_supply_propval val; | |
486 | int ret; | |
487 | ||
488 | WARN_ON(tzd == NULL); | |
489 | psy = tzd->devdata; | |
297d716f | 490 | ret = psy->desc->get_property(psy, POWER_SUPPLY_PROP_TEMP, &val); |
3be330bf JT |
491 | |
492 | /* Convert tenths of degree Celsius to milli degree Celsius. */ | |
493 | if (!ret) | |
494 | *temp = val.intval * 100; | |
495 | ||
496 | return ret; | |
497 | } | |
498 | ||
499 | static struct thermal_zone_device_ops psy_tzd_ops = { | |
500 | .get_temp = power_supply_read_temp, | |
501 | }; | |
502 | ||
503 | static int psy_register_thermal(struct power_supply *psy) | |
504 | { | |
505 | int i; | |
506 | ||
297d716f | 507 | if (psy->desc->no_thermal) |
a69d82b9 KK |
508 | return 0; |
509 | ||
3be330bf | 510 | /* Register battery zone device psy reports temperature */ |
297d716f KK |
511 | for (i = 0; i < psy->desc->num_properties; i++) { |
512 | if (psy->desc->properties[i] == POWER_SUPPLY_PROP_TEMP) { | |
513 | psy->tzd = thermal_zone_device_register(psy->desc->name, | |
514 | 0, 0, psy, &psy_tzd_ops, NULL, 0, 0); | |
9d2410c7 | 515 | return PTR_ERR_OR_ZERO(psy->tzd); |
3be330bf JT |
516 | } |
517 | } | |
518 | return 0; | |
519 | } | |
520 | ||
521 | static void psy_unregister_thermal(struct power_supply *psy) | |
522 | { | |
523 | if (IS_ERR_OR_NULL(psy->tzd)) | |
524 | return; | |
525 | thermal_zone_device_unregister(psy->tzd); | |
526 | } | |
952aeeb3 RP |
527 | |
528 | /* thermal cooling device callbacks */ | |
529 | static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, | |
530 | unsigned long *state) | |
531 | { | |
532 | struct power_supply *psy; | |
533 | union power_supply_propval val; | |
534 | int ret; | |
535 | ||
536 | psy = tcd->devdata; | |
297d716f | 537 | ret = psy->desc->get_property(psy, |
952aeeb3 RP |
538 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, &val); |
539 | if (!ret) | |
540 | *state = val.intval; | |
541 | ||
542 | return ret; | |
543 | } | |
544 | ||
545 | static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd, | |
546 | unsigned long *state) | |
547 | { | |
548 | struct power_supply *psy; | |
549 | union power_supply_propval val; | |
550 | int ret; | |
551 | ||
552 | psy = tcd->devdata; | |
297d716f | 553 | ret = psy->desc->get_property(psy, |
952aeeb3 RP |
554 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); |
555 | if (!ret) | |
556 | *state = val.intval; | |
557 | ||
558 | return ret; | |
559 | } | |
560 | ||
561 | static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, | |
562 | unsigned long state) | |
563 | { | |
564 | struct power_supply *psy; | |
565 | union power_supply_propval val; | |
566 | int ret; | |
567 | ||
568 | psy = tcd->devdata; | |
569 | val.intval = state; | |
297d716f | 570 | ret = psy->desc->set_property(psy, |
952aeeb3 RP |
571 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, &val); |
572 | ||
573 | return ret; | |
574 | } | |
575 | ||
576 | static struct thermal_cooling_device_ops psy_tcd_ops = { | |
577 | .get_max_state = ps_get_max_charge_cntl_limit, | |
578 | .get_cur_state = ps_get_cur_chrage_cntl_limit, | |
579 | .set_cur_state = ps_set_cur_charge_cntl_limit, | |
580 | }; | |
581 | ||
582 | static int psy_register_cooler(struct power_supply *psy) | |
583 | { | |
584 | int i; | |
585 | ||
586 | /* Register for cooling device if psy can control charging */ | |
297d716f KK |
587 | for (i = 0; i < psy->desc->num_properties; i++) { |
588 | if (psy->desc->properties[i] == | |
952aeeb3 RP |
589 | POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT) { |
590 | psy->tcd = thermal_cooling_device_register( | |
297d716f | 591 | (char *)psy->desc->name, |
952aeeb3 | 592 | psy, &psy_tcd_ops); |
9d2410c7 | 593 | return PTR_ERR_OR_ZERO(psy->tcd); |
952aeeb3 RP |
594 | } |
595 | } | |
596 | return 0; | |
597 | } | |
598 | ||
599 | static void psy_unregister_cooler(struct power_supply *psy) | |
600 | { | |
601 | if (IS_ERR_OR_NULL(psy->tcd)) | |
602 | return; | |
603 | thermal_cooling_device_unregister(psy->tcd); | |
604 | } | |
3be330bf JT |
605 | #else |
606 | static int psy_register_thermal(struct power_supply *psy) | |
607 | { | |
608 | return 0; | |
609 | } | |
610 | ||
611 | static void psy_unregister_thermal(struct power_supply *psy) | |
612 | { | |
613 | } | |
952aeeb3 RP |
614 | |
615 | static int psy_register_cooler(struct power_supply *psy) | |
616 | { | |
617 | return 0; | |
618 | } | |
619 | ||
620 | static void psy_unregister_cooler(struct power_supply *psy) | |
621 | { | |
622 | } | |
3be330bf JT |
623 | #endif |
624 | ||
297d716f KK |
625 | static struct power_supply *__must_check |
626 | __power_supply_register(struct device *parent, | |
627 | const struct power_supply_desc *desc, | |
2dc9215d KK |
628 | const struct power_supply_config *cfg, |
629 | bool ws) | |
4a11b59d | 630 | { |
5f487cd3 | 631 | struct device *dev; |
297d716f | 632 | struct power_supply *psy; |
5f487cd3 | 633 | int rc; |
4a11b59d | 634 | |
297d716f KK |
635 | psy = kzalloc(sizeof(*psy), GFP_KERNEL); |
636 | if (!psy) | |
637 | return ERR_PTR(-ENOMEM); | |
638 | ||
639 | dev = &psy->dev; | |
4a11b59d | 640 | |
5f487cd3 | 641 | device_initialize(dev); |
4a11b59d | 642 | |
5f487cd3 AV |
643 | dev->class = power_supply_class; |
644 | dev->type = &power_supply_dev_type; | |
645 | dev->parent = parent; | |
646 | dev->release = power_supply_dev_release; | |
647 | dev_set_drvdata(dev, psy); | |
297d716f | 648 | psy->desc = desc; |
bc154056 | 649 | atomic_inc(&psy->use_cnt); |
2dc9215d KK |
650 | if (cfg) { |
651 | psy->drv_data = cfg->drv_data; | |
652 | psy->of_node = cfg->of_node; | |
653 | psy->supplied_to = cfg->supplied_to; | |
654 | psy->num_supplicants = cfg->num_supplicants; | |
655 | } | |
5f487cd3 | 656 | |
297d716f | 657 | rc = dev_set_name(dev, "%s", desc->name); |
80c6463e SK |
658 | if (rc) |
659 | goto dev_set_name_failed; | |
660 | ||
97774672 LPC |
661 | INIT_WORK(&psy->changed_work, power_supply_changed_work); |
662 | ||
f6e0b081 RK |
663 | rc = power_supply_check_supplies(psy); |
664 | if (rc) { | |
665 | dev_info(dev, "Not all required supplies found, defer probe\n"); | |
666 | goto check_supplies_failed; | |
667 | } | |
668 | ||
948dcf96 | 669 | spin_lock_init(&psy->changed_lock); |
9113e260 | 670 | rc = device_init_wakeup(dev, ws); |
948dcf96 ZM |
671 | if (rc) |
672 | goto wakeup_init_failed; | |
673 | ||
5f487cd3 | 674 | rc = device_add(dev); |
4a11b59d | 675 | if (rc) |
5f487cd3 AV |
676 | goto device_add_failed; |
677 | ||
3be330bf JT |
678 | rc = psy_register_thermal(psy); |
679 | if (rc) | |
680 | goto register_thermal_failed; | |
681 | ||
952aeeb3 RP |
682 | rc = psy_register_cooler(psy); |
683 | if (rc) | |
684 | goto register_cooler_failed; | |
685 | ||
4a11b59d AV |
686 | rc = power_supply_create_triggers(psy); |
687 | if (rc) | |
688 | goto create_triggers_failed; | |
689 | ||
690 | power_supply_changed(psy); | |
691 | ||
297d716f | 692 | return psy; |
4a11b59d AV |
693 | |
694 | create_triggers_failed: | |
952aeeb3 RP |
695 | psy_unregister_cooler(psy); |
696 | register_cooler_failed: | |
3be330bf JT |
697 | psy_unregister_thermal(psy); |
698 | register_thermal_failed: | |
3a2dbd61 | 699 | device_del(dev); |
5f487cd3 | 700 | device_add_failed: |
80c6463e | 701 | wakeup_init_failed: |
f6e0b081 | 702 | check_supplies_failed: |
80c6463e | 703 | dev_set_name_failed: |
3a2dbd61 | 704 | put_device(dev); |
297d716f | 705 | return ERR_PTR(rc); |
4a11b59d | 706 | } |
9113e260 | 707 | |
297d716f KK |
708 | /** |
709 | * power_supply_register() - Register new power supply | |
710 | * @parent: Device to be a parent of power supply's device | |
711 | * @desc: Description of power supply, must be valid through whole | |
712 | * lifetime of this power supply | |
713 | * @cfg: Run-time specific configuration accessed during registering, | |
714 | * may be NULL | |
715 | * | |
716 | * Return: A pointer to newly allocated power_supply on success | |
717 | * or ERR_PTR otherwise. | |
718 | * Use power_supply_unregister() on returned power_supply pointer to release | |
719 | * resources. | |
720 | */ | |
721 | struct power_supply *__must_check power_supply_register(struct device *parent, | |
722 | const struct power_supply_desc *desc, | |
2dc9215d | 723 | const struct power_supply_config *cfg) |
9113e260 | 724 | { |
297d716f | 725 | return __power_supply_register(parent, desc, cfg, true); |
9113e260 | 726 | } |
ff3417e7 | 727 | EXPORT_SYMBOL_GPL(power_supply_register); |
4a11b59d | 728 | |
297d716f KK |
729 | /** |
730 | * power_supply_register() - Register new non-waking-source power supply | |
731 | * @parent: Device to be a parent of power supply's device | |
732 | * @desc: Description of power supply, must be valid through whole | |
733 | * lifetime of this power supply | |
734 | * @cfg: Run-time specific configuration accessed during registering, | |
735 | * may be NULL | |
736 | * | |
737 | * Return: A pointer to newly allocated power_supply on success | |
738 | * or ERR_PTR otherwise. | |
739 | * Use power_supply_unregister() on returned power_supply pointer to release | |
740 | * resources. | |
741 | */ | |
742 | struct power_supply *__must_check | |
743 | power_supply_register_no_ws(struct device *parent, | |
744 | const struct power_supply_desc *desc, | |
2dc9215d | 745 | const struct power_supply_config *cfg) |
9113e260 | 746 | { |
297d716f | 747 | return __power_supply_register(parent, desc, cfg, false); |
9113e260 ZR |
748 | } |
749 | EXPORT_SYMBOL_GPL(power_supply_register_no_ws); | |
750 | ||
5d8a4219 N |
751 | static void devm_power_supply_release(struct device *dev, void *res) |
752 | { | |
753 | struct power_supply **psy = res; | |
754 | ||
755 | power_supply_unregister(*psy); | |
756 | } | |
757 | ||
297d716f KK |
758 | /** |
759 | * power_supply_register() - Register managed power supply | |
760 | * @parent: Device to be a parent of power supply's device | |
761 | * @desc: Description of power supply, must be valid through whole | |
762 | * lifetime of this power supply | |
763 | * @cfg: Run-time specific configuration accessed during registering, | |
764 | * may be NULL | |
765 | * | |
766 | * Return: A pointer to newly allocated power_supply on success | |
767 | * or ERR_PTR otherwise. | |
768 | * The returned power_supply pointer will be automatically unregistered | |
769 | * on driver detach. | |
770 | */ | |
771 | struct power_supply *__must_check | |
772 | devm_power_supply_register(struct device *parent, | |
773 | const struct power_supply_desc *desc, | |
2dc9215d | 774 | const struct power_supply_config *cfg) |
5d8a4219 | 775 | { |
297d716f KK |
776 | struct power_supply **ptr, *psy; |
777 | ||
778 | ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); | |
5d8a4219 N |
779 | |
780 | if (!ptr) | |
297d716f KK |
781 | return ERR_PTR(-ENOMEM); |
782 | psy = __power_supply_register(parent, desc, cfg, true); | |
783 | if (IS_ERR(psy)) { | |
5d8a4219 | 784 | devres_free(ptr); |
297d716f | 785 | } else { |
5d8a4219 N |
786 | *ptr = psy; |
787 | devres_add(parent, ptr); | |
788 | } | |
297d716f | 789 | return psy; |
5d8a4219 N |
790 | } |
791 | EXPORT_SYMBOL_GPL(devm_power_supply_register); | |
792 | ||
297d716f KK |
793 | /** |
794 | * power_supply_register() - Register managed non-waking-source power supply | |
795 | * @parent: Device to be a parent of power supply's device | |
796 | * @desc: Description of power supply, must be valid through whole | |
797 | * lifetime of this power supply | |
798 | * @cfg: Run-time specific configuration accessed during registering, | |
799 | * may be NULL | |
800 | * | |
801 | * Return: A pointer to newly allocated power_supply on success | |
802 | * or ERR_PTR otherwise. | |
803 | * The returned power_supply pointer will be automatically unregistered | |
804 | * on driver detach. | |
805 | */ | |
806 | struct power_supply *__must_check | |
807 | devm_power_supply_register_no_ws(struct device *parent, | |
808 | const struct power_supply_desc *desc, | |
2dc9215d | 809 | const struct power_supply_config *cfg) |
5d8a4219 | 810 | { |
297d716f KK |
811 | struct power_supply **ptr, *psy; |
812 | ||
813 | ptr = devres_alloc(devm_power_supply_release, sizeof(*ptr), GFP_KERNEL); | |
5d8a4219 N |
814 | |
815 | if (!ptr) | |
297d716f KK |
816 | return ERR_PTR(-ENOMEM); |
817 | psy = __power_supply_register(parent, desc, cfg, false); | |
818 | if (IS_ERR(psy)) { | |
5d8a4219 | 819 | devres_free(ptr); |
297d716f | 820 | } else { |
5d8a4219 N |
821 | *ptr = psy; |
822 | devres_add(parent, ptr); | |
823 | } | |
297d716f | 824 | return psy; |
5d8a4219 N |
825 | } |
826 | EXPORT_SYMBOL_GPL(devm_power_supply_register_no_ws); | |
827 | ||
297d716f KK |
828 | /** |
829 | * power_supply_unregister() - Remove this power supply from system | |
830 | * @psy: Pointer to power supply to unregister | |
831 | * | |
832 | * Remove this power supply from the system. The resources of power supply | |
833 | * will be freed here or on last power_supply_put() call. | |
834 | */ | |
4a11b59d AV |
835 | void power_supply_unregister(struct power_supply *psy) |
836 | { | |
bc154056 | 837 | WARN_ON(atomic_dec_return(&psy->use_cnt)); |
bc51e7ff | 838 | cancel_work_sync(&psy->changed_work); |
297d716f | 839 | sysfs_remove_link(&psy->dev.kobj, "powers"); |
4a11b59d | 840 | power_supply_remove_triggers(psy); |
952aeeb3 | 841 | psy_unregister_cooler(psy); |
3be330bf | 842 | psy_unregister_thermal(psy); |
297d716f KK |
843 | device_init_wakeup(&psy->dev, false); |
844 | device_unregister(&psy->dev); | |
4a11b59d | 845 | } |
ff3417e7 | 846 | EXPORT_SYMBOL_GPL(power_supply_unregister); |
4a11b59d | 847 | |
e44ea364 KK |
848 | void *power_supply_get_drvdata(struct power_supply *psy) |
849 | { | |
850 | return psy->drv_data; | |
851 | } | |
852 | EXPORT_SYMBOL_GPL(power_supply_get_drvdata); | |
853 | ||
4a11b59d AV |
854 | static int __init power_supply_class_init(void) |
855 | { | |
856 | power_supply_class = class_create(THIS_MODULE, "power_supply"); | |
857 | ||
858 | if (IS_ERR(power_supply_class)) | |
859 | return PTR_ERR(power_supply_class); | |
860 | ||
861 | power_supply_class->dev_uevent = power_supply_uevent; | |
5f487cd3 | 862 | power_supply_init_attrs(&power_supply_dev_type); |
4a11b59d AV |
863 | |
864 | return 0; | |
865 | } | |
866 | ||
867 | static void __exit power_supply_class_exit(void) | |
868 | { | |
869 | class_destroy(power_supply_class); | |
4a11b59d AV |
870 | } |
871 | ||
4a11b59d AV |
872 | subsys_initcall(power_supply_class_init); |
873 | module_exit(power_supply_class_exit); | |
874 | ||
875 | MODULE_DESCRIPTION("Universal power supply monitor class"); | |
876 | MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " | |
877 | "Szabolcs Gyurko, " | |
878 | "Anton Vorontsov <cbou@mail.ru>"); | |
879 | MODULE_LICENSE("GPL"); |