aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-10 17:47:59 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-10 17:47:59 -0400
commit5f60cfd932b42c69ed3226400cb5eab152576c3a (patch)
tree70e4feba5158e76060d36a6c015be8297c294bca
parent9f9d76321659b5ebc9939101481f7c3ce228ea6e (diff)
parentd7ce6d1d5f6e307a2fbb69626cf120e20e793fe7 (diff)
Merge git://git.infradead.org/~dwmw2/battery-2.6
* git://git.infradead.org/~dwmw2/battery-2.6: [BATTERY] ds2760 W1 slave [BATTERY] One Laptop Per Child power/battery driver [BATTERY] Apple PMU driver [BATTERY] 1-Wire ds2760 chip battery driver [BATTERY] APM emulation driver for class batteries [BATTERY] pda_power platform driver [BATTERY] Universal power supply class (was: battery class)
-rw-r--r--Documentation/power_supply_class.txt167
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/power/Kconfig51
-rw-r--r--drivers/power/Makefile22
-rw-r--r--drivers/power/apm_power.c243
-rw-r--r--drivers/power/ds2760_battery.c470
-rw-r--r--drivers/power/olpc_battery.c352
-rw-r--r--drivers/power/pda_power.c261
-rw-r--r--drivers/power/pmu_battery.c215
-rw-r--r--drivers/power/power_supply.h42
-rw-r--r--drivers/power/power_supply_core.c168
-rw-r--r--drivers/power/power_supply_leds.c176
-rw-r--r--drivers/power/power_supply_sysfs.c299
-rw-r--r--drivers/w1/slaves/Kconfig13
-rw-r--r--drivers/w1/slaves/Makefile1
-rw-r--r--drivers/w1/slaves/w1_ds2760.c213
-rw-r--r--drivers/w1/slaves/w1_ds2760.h50
-rw-r--r--drivers/w1/w1_family.h1
-rw-r--r--include/linux/pda_power.h31
-rw-r--r--include/linux/power_supply.h180
21 files changed, 2958 insertions, 0 deletions
diff --git a/Documentation/power_supply_class.txt b/Documentation/power_supply_class.txt
new file mode 100644
index 000000000000..9758cf433c06
--- /dev/null
+++ b/Documentation/power_supply_class.txt
@@ -0,0 +1,167 @@
1Linux power supply class
2========================
3
4Synopsis
5~~~~~~~~
6Power supply class used to represent battery, UPS, AC or DC power supply
7properties to user-space.
8
9It defines core set of attributes, which should be applicable to (almost)
10every power supply out there. Attributes are available via sysfs and uevent
11interfaces.
12
13Each attribute has well defined meaning, up to unit of measure used. While
14the attributes provided are believed to be universally applicable to any
15power supply, specific monitoring hardware may not be able to provide them
16all, so any of them may be skipped.
17
18Power supply class is extensible, and allows to define drivers own attributes.
19The core attribute set is subject to the standard Linux evolution (i.e.
20if it will be found that some attribute is applicable to many power supply
21types or their drivers, it can be added to the core set).
22
23It also integrates with LED framework, for the purpose of providing
24typically expected feedback of battery charging/fully charged status and
25AC/USB power supply online status. (Note that specific details of the
26indication (including whether to use it at all) are fully controllable by
27user and/or specific machine defaults, per design principles of LED
28framework).
29
30
31Attributes/properties
32~~~~~~~~~~~~~~~~~~~~~
33Power supply class has predefined set of attributes, this eliminates code
34duplication across drivers. Power supply class insist on reusing its
35predefined attributes *and* their units.
36
37So, userspace gets predictable set of attributes and their units for any
38kind of power supply, and can process/present them to a user in consistent
39manner. Results for different power supplies and machines are also directly
40comparable.
41
42See drivers/power/ds2760_battery.c and drivers/power/pda_power.c for the
43example how to declare and handle attributes.
44
45
46Units
47~~~~~
48Quoting include/linux/power_supply.h:
49
50 All voltages, currents, charges, energies, time and temperatures in µV,
51 µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
52 stated. It's driver's job to convert its raw values to units in which
53 this class operates.
54
55
56Attributes/properties detailed
57~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
58
59~ ~ ~ ~ ~ ~ ~ Charge/Energy/Capacity - how to not confuse ~ ~ ~ ~ ~ ~ ~
60~ ~
61~ Because both "charge" (µAh) and "energy" (µWh) represents "capacity" ~
62~ of battery, this class distinguish these terms. Don't mix them! ~
63~ ~
64~ CHARGE_* attributes represents capacity in µAh only. ~
65~ ENERGY_* attributes represents capacity in µWh only. ~
66~ CAPACITY attribute represents capacity in *percents*, from 0 to 100. ~
67~ ~
68~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
69
70Postfixes:
71_AVG - *hardware* averaged value, use it if your hardware is really able to
72report averaged values.
73_NOW - momentary/instantaneous values.
74
75STATUS - this attribute represents operating status (charging, full,
76discharging (i.e. powering a load), etc.). This corresponds to
77BATTERY_STATUS_* values, as defined in battery.h.
78
79HEALTH - represents health of the battery, values corresponds to
80POWER_SUPPLY_HEALTH_*, defined in battery.h.
81
82VOLTAGE_MAX_DESIGN, VOLTAGE_MIN_DESIGN - design values for maximal and
83minimal power supply voltages. Maximal/minimal means values of voltages
84when battery considered "full"/"empty" at normal conditions. Yes, there is
85no direct relation between voltage and battery capacity, but some dumb
86batteries use voltage for very approximated calculation of capacity.
87Battery driver also can use this attribute just to inform userspace
88about maximal and minimal voltage thresholds of a given battery.
89
90CHARGE_FULL_DESIGN, CHARGE_EMPTY_DESIGN - design charge values, when
91battery considered full/empty.
92
93ENERGY_FULL_DESIGN, ENERGY_EMPTY_DESIGN - same as above but for energy.
94
95CHARGE_FULL, CHARGE_EMPTY - These attributes means "last remembered value
96of charge when battery became full/empty". It also could mean "value of
97charge when battery considered full/empty at given conditions (temperature,
98age)". I.e. these attributes represents real thresholds, not design values.
99
100ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.
101
102CAPACITY - capacity in percents.
103CAPACITY_LEVEL - capacity level. This corresponds to
104POWER_SUPPLY_CAPACITY_LEVEL_*.
105
106TEMP - temperature of the power supply.
107TEMP_AMBIENT - ambient temperature.
108
109TIME_TO_EMPTY - seconds left for battery to be considered empty (i.e.
110while battery powers a load)
111TIME_TO_FULL - seconds left for battery to be considered full (i.e.
112while battery is charging)
113
114
115Battery <-> external power supply interaction
116~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
117Often power supplies are acting as supplies and supplicants at the same
118time. Batteries are good example. So, batteries usually care if they're
119externally powered or not.
120
121For that case, power supply class implements notification mechanism for
122batteries.
123
124External power supply (AC) lists supplicants (batteries) names in
125"supplied_to" struct member, and each power_supply_changed() call
126issued by external power supply will notify supplicants via
127external_power_changed callback.
128
129
130QA
131~~
132Q: Where is POWER_SUPPLY_PROP_XYZ attribute?
133A: If you cannot find attribute suitable for your driver needs, feel free
134 to add it and send patch along with your driver.
135
136 The attributes available currently are the ones currently provided by the
137 drivers written.
138
139 Good candidates to add in future: model/part#, cycle_time, manufacturer,
140 etc.
141
142
143Q: I have some very specific attribute (e.g. battery color), should I add
144 this attribute to standard ones?
145A: Most likely, no. Such attribute can be placed in the driver itself, if
146 it is useful. Of course, if the attribute in question applicable to
147 large set of batteries, provided by many drivers, and/or comes from
148 some general battery specification/standard, it may be a candidate to
149 be added to the core attribute set.
150
151
152Q: Suppose, my battery monitoring chip/firmware does not provides capacity
153 in percents, but provides charge_{now,full,empty}. Should I calculate
154 percentage capacity manually, inside the driver, and register CAPACITY
155 attribute? The same question about time_to_empty/time_to_full.
156A: Most likely, no. This class is designed to export properties which are
157 directly measurable by the specific hardware available.
158
159 Inferring not available properties using some heuristics or mathematical
160 model is not subject of work for a battery driver. Such functionality
161 should be factored out, and in fact, apm_power, the driver to serve
162 legacy APM API on top of power supply class, uses a simple heuristic of
163 approximating remaining battery capacity based on its charge, current,
164 voltage and so on. But full-fledged battery model is likely not subject
165 for kernel at all, as it would require floating point calculation to deal
166 with things like differential equations and Kalman filters. This is
167 better be handled by batteryd/libbattery, yet to be written.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4e6487d461f3..7916f4b86d23 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -52,6 +52,8 @@ source "drivers/spi/Kconfig"
52 52
53source "drivers/w1/Kconfig" 53source "drivers/w1/Kconfig"
54 54
55source "drivers/power/Kconfig"
56
55source "drivers/hwmon/Kconfig" 57source "drivers/hwmon/Kconfig"
56 58
57source "drivers/mfd/Kconfig" 59source "drivers/mfd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index adad2f3d438a..503d82569449 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_I2O) += message/
61obj-$(CONFIG_RTC_LIB) += rtc/ 61obj-$(CONFIG_RTC_LIB) += rtc/
62obj-y += i2c/ 62obj-y += i2c/
63obj-$(CONFIG_W1) += w1/ 63obj-$(CONFIG_W1) += w1/
64obj-$(CONFIG_POWER_SUPPLY) += power/
64obj-$(CONFIG_HWMON) += hwmon/ 65obj-$(CONFIG_HWMON) += hwmon/
65obj-$(CONFIG_PHONE) += telephony/ 66obj-$(CONFIG_PHONE) += telephony/
66obj-$(CONFIG_MD) += md/ 67obj-$(CONFIG_MD) += md/
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
new file mode 100644
index 000000000000..ab9c3e5a7c1d
--- /dev/null
+++ b/drivers/power/Kconfig
@@ -0,0 +1,51 @@
1menuconfig POWER_SUPPLY
2 tristate "Power supply class support"
3 help
4 Say Y here to enable power supply class support. This allows
5 power supply (batteries, AC, USB) monitoring by userspace
6 via sysfs and uevent (if available) and/or APM kernel interface
7 (if selected below).
8
9if POWER_SUPPLY
10
11config POWER_SUPPLY_DEBUG
12 bool "Power supply debug"
13 help
14 Say Y here to enable debugging messages for power supply class
15 and drivers.
16
17config PDA_POWER
18 tristate "Generic PDA/phone power driver"
19 help
20 Say Y here to enable generic power driver for PDAs and phones with
21 one or two external power supplies (AC/USB) connected to main and
22 backup batteries, and optional builtin charger.
23
24config APM_POWER
25 tristate "APM emulation for class batteries"
26 depends on APM_EMULATION
27 help
28 Say Y here to enable support APM status emulation using
29 battery class devices.
30
31config BATTERY_DS2760
32 tristate "DS2760 battery driver (HP iPAQ & others)"
33 select W1
34 select W1_SLAVE_DS2760
35 help
36 Say Y here to enable support for batteries with ds2760 chip.
37
38config BATTERY_PMU
39 tristate "Apple PMU battery"
40 depends on ADB_PMU
41 help
42 Say Y here to expose battery information on Apple machines
43 through the generic battery class.
44
45config BATTERY_OLPC
46 tristate "One Laptop Per Child battery"
47 depends on X86_32 && OLPC
48 help
49 Say Y to enable support for the battery on the OLPC laptop.
50
51endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
new file mode 100644
index 000000000000..6413ded5fe5f
--- /dev/null
+++ b/drivers/power/Makefile
@@ -0,0 +1,22 @@
1power_supply-objs := power_supply_core.o
2
3ifeq ($(CONFIG_SYSFS),y)
4power_supply-objs += power_supply_sysfs.o
5endif
6
7ifeq ($(CONFIG_LEDS_TRIGGERS),y)
8power_supply-objs += power_supply_leds.o
9endif
10
11ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y)
12EXTRA_CFLAGS += -DDEBUG
13endif
14
15obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
16
17obj-$(CONFIG_PDA_POWER) += pda_power.o
18obj-$(CONFIG_APM_POWER) += apm_power.o
19
20obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
21obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
22obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
new file mode 100644
index 000000000000..042bd950d036
--- /dev/null
+++ b/drivers/power/apm_power.c
@@ -0,0 +1,243 @@
1/*
2 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
3 * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
4 *
5 * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
6 *
7 * Use consistent with the GNU GPL is permitted,
8 * provided that this copyright notice is
9 * preserved in its entirety in all copies and derived works.
10 */
11
12#include <linux/module.h>
13#include <linux/power_supply.h>
14#include <linux/apm-emulation.h>
15
16#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
17 POWER_SUPPLY_PROP_##prop, val)
18
19#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
20 prop, val)
21
22#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
23
24static struct power_supply *main_battery;
25
26static void find_main_battery(void)
27{
28 struct device *dev;
29 struct power_supply *bat, *batm;
30 union power_supply_propval full;
31 int max_charge = 0;
32
33 main_battery = NULL;
34 batm = NULL;
35 list_for_each_entry(dev, &power_supply_class->devices, node) {
36 bat = dev_get_drvdata(dev);
37 /* If none of battery devices cantains 'use_for_apm' flag,
38 choice one with maximum design charge */
39 if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) {
40 if (full.intval > max_charge) {
41 batm = bat;
42 max_charge = full.intval;
43 }
44 }
45
46 if (bat->use_for_apm)
47 main_battery = bat;
48 }
49 if (!main_battery)
50 main_battery = batm;
51
52 return;
53}
54
55static int calculate_time(int status)
56{
57 union power_supply_propval charge_full, charge_empty;
58 union power_supply_propval charge, I;
59
60 if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
61 /* if battery can't report this property, use design value */
62 if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
63 return -1;
64 }
65
66 if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
67 /* if battery can't report this property, use design value */
68 if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
69 charge_empty.intval = 0;
70 }
71
72 if (MPSY_PROP(CHARGE_AVG, &charge)) {
73 /* if battery can't report average value, use momentary */
74 if (MPSY_PROP(CHARGE_NOW, &charge))
75 return -1;
76 }
77
78 if (MPSY_PROP(CURRENT_AVG, &I)) {
79 /* if battery can't report average value, use momentary */
80 if (MPSY_PROP(CURRENT_NOW, &I))
81 return -1;
82 }
83
84 if (status == POWER_SUPPLY_STATUS_CHARGING)
85 return ((charge.intval - charge_full.intval) * 60L) /
86 I.intval;
87 else
88 return -((charge.intval - charge_empty.intval) * 60L) /
89 I.intval;
90}
91
92static int calculate_capacity(int using_charge)
93{
94 enum power_supply_property full_prop, empty_prop;
95 enum power_supply_property full_design_prop, empty_design_prop;
96 enum power_supply_property now_prop, avg_prop;
97 union power_supply_propval empty, full, cur;
98 int ret;
99
100 if (using_charge) {
101 full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
102 empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
103 full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
104 empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
105 now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
106 avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
107 } else {
108 full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
109 empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
110 full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
111 empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
112 now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
113 avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
114 }
115
116 if (_MPSY_PROP(full_prop, &full)) {
117 /* if battery can't report this property, use design value */
118 if (_MPSY_PROP(full_design_prop, &full))
119 return -1;
120 }
121
122 if (_MPSY_PROP(avg_prop, &cur)) {
123 /* if battery can't report average value, use momentary */
124 if (_MPSY_PROP(now_prop, &cur))
125 return -1;
126 }
127
128 if (_MPSY_PROP(empty_prop, &empty)) {
129 /* if battery can't report this property, use design value */
130 if (_MPSY_PROP(empty_design_prop, &empty))
131 empty.intval = 0;
132 }
133
134 if (full.intval - empty.intval)
135 ret = ((cur.intval - empty.intval) * 100L) /
136 (full.intval - empty.intval);
137 else
138 return -1;
139
140 if (ret > 100)
141 return 100;
142 else if (ret < 0)
143 return 0;
144
145 return ret;
146}
147
148static void apm_battery_apm_get_power_status(struct apm_power_info *info)
149{
150 union power_supply_propval status;
151 union power_supply_propval capacity, time_to_full, time_to_empty;
152
153 down(&power_supply_class->sem);
154 find_main_battery();
155 if (!main_battery) {
156 up(&power_supply_class->sem);
157 return;
158 }
159
160 /* status */
161
162 if (MPSY_PROP(STATUS, &status))
163 status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
164
165 /* ac line status */
166
167 if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
168 (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
169 (status.intval == POWER_SUPPLY_STATUS_FULL))
170 info->ac_line_status = APM_AC_ONLINE;
171 else
172 info->ac_line_status = APM_AC_OFFLINE;
173
174 /* battery life (i.e. capacity, in percents) */
175
176 if (MPSY_PROP(CAPACITY, &capacity) == 0) {
177 info->battery_life = capacity.intval;
178 } else {
179 /* try calculate using energy */
180 info->battery_life = calculate_capacity(0);
181 /* if failed try calculate using charge instead */
182 if (info->battery_life == -1)
183 info->battery_life = calculate_capacity(1);
184 }
185
186 /* charging status */
187
188 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
189 info->battery_status = APM_BATTERY_STATUS_CHARGING;
190 } else {
191 if (info->battery_life > 50)
192 info->battery_status = APM_BATTERY_STATUS_HIGH;
193 else if (info->battery_life > 5)
194 info->battery_status = APM_BATTERY_STATUS_LOW;
195 else
196 info->battery_status = APM_BATTERY_STATUS_CRITICAL;
197 }
198 info->battery_flag = info->battery_status;
199
200 /* time */
201
202 info->units = APM_UNITS_MINS;
203
204 if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
205 if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) {
206 if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
207 info->time = calculate_time(status.intval);
208 else
209 info->time = time_to_full.intval / 60;
210 }
211 } else {
212 if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) {
213 if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
214 info->time = calculate_time(status.intval);
215 else
216 info->time = time_to_empty.intval / 60;
217 }
218 }
219
220 up(&power_supply_class->sem);
221 return;
222}
223
224static int __init apm_battery_init(void)
225{
226 printk(KERN_INFO "APM Battery Driver\n");
227
228 apm_get_power_status = apm_battery_apm_get_power_status;
229 return 0;
230}
231
232static void __exit apm_battery_exit(void)
233{
234 apm_get_power_status = NULL;
235 return;
236}
237
238module_init(apm_battery_init);
239module_exit(apm_battery_exit);
240
241MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
242MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
243MODULE_LICENSE("GPL");
diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c
new file mode 100644
index 000000000000..00e1ea6f1de2
--- /dev/null
+++ b/drivers/power/ds2760_battery.c
@@ -0,0 +1,470 @@
1/*
2 * Driver for batteries with DS2760 chips inside.
3 *
4 * Copyright © 2007 Anton Vorontsov
5 * 2004-2007 Matt Reimer
6 * 2004 Szabolcs Gyurko
7 *
8 * Use consistent with the GNU GPL is permitted,
9 * provided that this copyright notice is
10 * preserved in its entirety in all copies and derived works.
11 *
12 * Author: Anton Vorontsov <cbou@mail.ru>
13 * February 2007
14 *
15 * Matt Reimer <mreimer@vpop.net>
16 * April 2004, 2005, 2007
17 *
18 * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
19 * September 2004
20 */
21
22#include <linux/module.h>
23#include <linux/param.h>
24#include <linux/jiffies.h>
25#include <linux/workqueue.h>
26#include <linux/pm.h>
27#include <linux/platform_device.h>
28#include <linux/power_supply.h>
29
30#include "../w1/w1.h"
31#include "../w1/slaves/w1_ds2760.h"
32
33struct ds2760_device_info {
34 struct device *dev;
35
36 /* DS2760 data, valid after calling ds2760_battery_read_status() */
37 unsigned long update_time; /* jiffies when data read */
38 char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */
39 int voltage_raw; /* units of 4.88 mV */
40 int voltage_uV; /* units of µV */
41 int current_raw; /* units of 0.625 mA */
42 int current_uA; /* units of µA */
43 int accum_current_raw; /* units of 0.25 mAh */
44 int accum_current_uAh; /* units of µAh */
45 int temp_raw; /* units of 0.125 °C */
46 int temp_C; /* units of 0.1 °C */
47 int rated_capacity; /* units of µAh */
48 int rem_capacity; /* percentage */
49 int full_active_uAh; /* units of µAh */
50 int empty_uAh; /* units of µAh */
51 int life_sec; /* units of seconds */
52 int charge_status; /* POWER_SUPPLY_STATUS_* */
53
54 int full_counter;
55 struct power_supply bat;
56 struct device *w1_dev;
57 struct workqueue_struct *monitor_wqueue;
58 struct delayed_work monitor_work;
59};
60
61static unsigned int cache_time = 1000;
62module_param(cache_time, uint, 0644);
63MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
64
65/* Some batteries have their rated capacity stored a N * 10 mAh, while
66 * others use an index into this table. */
67static int rated_capacities[] = {
68 0,
69 920, /* Samsung */
70 920, /* BYD */
71 920, /* Lishen */
72 920, /* NEC */
73 1440, /* Samsung */
74 1440, /* BYD */
75 1440, /* Lishen */
76 1440, /* NEC */
77 2880, /* Samsung */
78 2880, /* BYD */
79 2880, /* Lishen */
80 2880 /* NEC */
81};
82
83/* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C
84 * temp is in Celsius */
85static int battery_interpolate(int array[], int temp)
86{
87 int index, dt;
88
89 if (temp <= 0)
90 return array[0];
91 if (temp >= 40)
92 return array[4];
93
94 index = temp / 10;
95 dt = temp % 10;
96
97 return array[index] + (((array[index + 1] - array[index]) * dt) / 10);
98}
99
100static int ds2760_battery_read_status(struct ds2760_device_info *di)
101{
102 int ret, i, start, count, scale[5];
103
104 if (di->update_time && time_before(jiffies, di->update_time +
105 msecs_to_jiffies(cache_time)))
106 return 0;
107
108 /* The first time we read the entire contents of SRAM/EEPROM,
109 * but after that we just read the interesting bits that change. */
110 if (di->update_time == 0) {
111 start = 0;
112 count = DS2760_DATA_SIZE;
113 } else {
114 start = DS2760_VOLTAGE_MSB;
115 count = DS2760_TEMP_LSB - start + 1;
116 }
117
118 ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
119 if (ret != count) {
120 dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n",
121 di->w1_dev);
122 return 1;
123 }
124
125 di->update_time = jiffies;
126
127 /* DS2760 reports voltage in units of 4.88mV, but the battery class
128 * reports in units of uV, so convert by multiplying by 4880. */
129 di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) |
130 (di->raw[DS2760_VOLTAGE_LSB] >> 5);
131 di->voltage_uV = di->voltage_raw * 4880;
132
133 /* DS2760 reports current in signed units of 0.625mA, but the battery
134 * class reports in units of µA, so convert by multiplying by 625. */
135 di->current_raw =
136 (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) |
137 (di->raw[DS2760_CURRENT_LSB] >> 3);
138 di->current_uA = di->current_raw * 625;
139
140 /* DS2760 reports accumulated current in signed units of 0.25mAh. */
141 di->accum_current_raw =
142 (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) |
143 di->raw[DS2760_CURRENT_ACCUM_LSB];
144 di->accum_current_uAh = di->accum_current_raw * 250;
145
146 /* DS2760 reports temperature in signed units of 0.125°C, but the
147 * battery class reports in units of 1/10 °C, so we convert by
148 * multiplying by .125 * 10 = 1.25. */
149 di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) |
150 (di->raw[DS2760_TEMP_LSB] >> 5);
151 di->temp_C = di->temp_raw + (di->temp_raw / 4);
152
153 /* At least some battery monitors (e.g. HP iPAQ) store the battery's
154 * maximum rated capacity. */
155 if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities))
156 di->rated_capacity = rated_capacities[
157 (unsigned int)di->raw[DS2760_RATED_CAPACITY]];
158 else
159 di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10;
160
161 di->rated_capacity *= 1000; /* convert to µAh */
162
163 /* Calculate the full level at the present temperature. */
164 di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 |
165 di->raw[DS2760_ACTIVE_FULL + 1];
166
167 scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 |
168 di->raw[DS2760_ACTIVE_FULL + 1];
169 for (i = 1; i < 5; i++)
170 scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i];
171
172 di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
173 di->full_active_uAh *= 1000; /* convert to µAh */
174
175 /* Calculate the empty level at the present temperature. */
176 scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4];
177 for (i = 3; i >= 0; i--)
178 scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i];
179
180 di->empty_uAh = battery_interpolate(scale, di->temp_C / 10);
181 di->empty_uAh *= 1000; /* convert to µAh */
182
183 /* From Maxim Application Note 131: remaining capacity =
184 * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */
185 di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) /
186 (di->full_active_uAh - di->empty_uAh);
187
188 if (di->rem_capacity < 0)
189 di->rem_capacity = 0;
190 if (di->rem_capacity > 100)
191 di->rem_capacity = 100;
192
193 if (di->current_uA)
194 di->life_sec = -((di->accum_current_uAh - di->empty_uAh) *
195 3600L) / di->current_uA;
196 else
197 di->life_sec = 0;
198
199 return 0;
200}
201
202static void ds2760_battery_update_status(struct ds2760_device_info *di)
203{
204 int old_charge_status = di->charge_status;
205
206 ds2760_battery_read_status(di);
207
208 if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN)
209 di->full_counter = 0;
210
211 if (power_supply_am_i_supplied(&di->bat)) {
212 if (di->current_uA > 10000) {
213 di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
214 di->full_counter = 0;
215 } else if (di->current_uA < -5000) {
216 if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
217 dev_notice(di->dev, "not enough power to "
218 "charge\n");
219 di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
220 di->full_counter = 0;
221 } else if (di->current_uA < 10000 &&
222 di->charge_status != POWER_SUPPLY_STATUS_FULL) {
223
224 /* Don't consider the battery to be full unless
225 * we've seen the current < 10 mA at least two
226 * consecutive times. */
227
228 di->full_counter++;
229
230 if (di->full_counter < 2) {
231 di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
232 } else {
233 unsigned char acr[2];
234 int acr_val;
235
236 /* acr is in units of 0.25 mAh */
237 acr_val = di->full_active_uAh * 4L / 1000;
238
239 acr[0] = acr_val >> 8;
240 acr[1] = acr_val & 0xff;
241
242 if (w1_ds2760_write(di->w1_dev, acr,
243 DS2760_CURRENT_ACCUM_MSB, 2) < 2)
244 dev_warn(di->dev,
245 "ACR reset failed\n");
246
247 di->charge_status = POWER_SUPPLY_STATUS_FULL;
248 }
249 }
250 } else {
251 di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
252 di->full_counter = 0;
253 }
254
255 if (di->charge_status != old_charge_status)
256 power_supply_changed(&di->bat);
257
258 return;
259}
260
261static void ds2760_battery_work(struct work_struct *work)
262{
263 struct ds2760_device_info *di = container_of(work,
264 struct ds2760_device_info, monitor_work.work);
265 const int interval = HZ * 60;
266
267 dev_dbg(di->dev, "%s\n", __FUNCTION__);
268
269 ds2760_battery_update_status(di);
270 queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
271
272 return;
273}
274
275#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \
276 bat);
277
278static void ds2760_battery_external_power_changed(struct power_supply *psy)
279{
280 struct ds2760_device_info *di = to_ds2760_device_info(psy);
281
282 dev_dbg(di->dev, "%s\n", __FUNCTION__);
283
284 cancel_delayed_work(&di->monitor_work);
285 queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
286
287 return;
288}
289
290static int ds2760_battery_get_property(struct power_supply *psy,
291 enum power_supply_property psp,
292 union power_supply_propval *val)
293{
294 struct ds2760_device_info *di = to_ds2760_device_info(psy);
295
296 switch (psp) {
297 case POWER_SUPPLY_PROP_STATUS:
298 val->intval = di->charge_status;
299 return 0;
300 default:
301 break;
302 }
303
304 ds2760_battery_read_status(di);
305
306 switch (psp) {
307 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
308 val->intval = di->voltage_uV;
309 break;
310 case POWER_SUPPLY_PROP_CURRENT_NOW:
311 val->intval = di->current_uA;
312 break;
313 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
314 val->intval = di->rated_capacity;
315 break;
316 case POWER_SUPPLY_PROP_CHARGE_FULL:
317 val->intval = di->full_active_uAh;
318 break;
319 case POWER_SUPPLY_PROP_CHARGE_EMPTY:
320 val->intval = di->empty_uAh;
321 break;
322 case POWER_SUPPLY_PROP_CHARGE_NOW:
323 val->intval = di->accum_current_uAh;
324 break;
325 case POWER_SUPPLY_PROP_TEMP:
326 val->intval = di->temp_C;
327 break;
328 default:
329 return -EINVAL;
330 }
331
332 return 0;
333}
334
335static enum power_supply_property ds2760_battery_props[] = {
336 POWER_SUPPLY_PROP_STATUS,
337 POWER_SUPPLY_PROP_VOLTAGE_NOW,
338 POWER_SUPPLY_PROP_CURRENT_NOW,
339 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
340 POWER_SUPPLY_PROP_CHARGE_FULL,
341 POWER_SUPPLY_PROP_CHARGE_EMPTY,
342 POWER_SUPPLY_PROP_CHARGE_NOW,
343 POWER_SUPPLY_PROP_TEMP,
344};
345
346static int ds2760_battery_probe(struct platform_device *pdev)
347{
348 int retval = 0;
349 struct ds2760_device_info *di;
350 struct ds2760_platform_data *pdata;
351
352 di = kzalloc(sizeof(*di), GFP_KERNEL);
353 if (!di) {
354 retval = -ENOMEM;
355 goto di_alloc_failed;
356 }
357
358 platform_set_drvdata(pdev, di);
359
360 pdata = pdev->dev.platform_data;
361 di->dev = &pdev->dev;
362 di->w1_dev = pdev->dev.parent;
363 di->bat.name = pdev->dev.bus_id;
364 di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
365 di->bat.properties = ds2760_battery_props;
366 di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props);
367 di->bat.get_property = ds2760_battery_get_property;
368 di->bat.external_power_changed =
369 ds2760_battery_external_power_changed;
370
371 di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
372
373 retval = power_supply_register(&pdev->dev, &di->bat);
374 if (retval) {
375 dev_err(di->dev, "failed to register battery");
376 goto batt_failed;
377 }
378
379 INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
380 di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id);
381 if (!di->monitor_wqueue) {
382 retval = -ESRCH;
383 goto workqueue_failed;
384 }
385 queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
386
387 goto success;
388
389workqueue_failed:
390 power_supply_unregister(&di->bat);
391batt_failed:
392 kfree(di);
393di_alloc_failed:
394success:
395 return retval;
396}
397
398static int ds2760_battery_remove(struct platform_device *pdev)
399{
400 struct ds2760_device_info *di = platform_get_drvdata(pdev);
401
402 cancel_rearming_delayed_workqueue(di->monitor_wqueue,
403 &di->monitor_work);
404 destroy_workqueue(di->monitor_wqueue);
405 power_supply_unregister(&di->bat);
406
407 return 0;
408}
409
410#ifdef CONFIG_PM
411
412static int ds2760_battery_suspend(struct platform_device *pdev,
413 pm_message_t state)
414{
415 struct ds2760_device_info *di = platform_get_drvdata(pdev);
416
417 di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
418
419 return 0;
420}
421
422static int ds2760_battery_resume(struct platform_device *pdev)
423{
424 struct ds2760_device_info *di = platform_get_drvdata(pdev);
425
426 di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
427 power_supply_changed(&di->bat);
428
429 cancel_delayed_work(&di->monitor_work);
430 queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
431
432 return 0;
433}
434
435#else
436
437#define ds2760_battery_suspend NULL
438#define ds2760_battery_resume NULL
439
440#endif /* CONFIG_PM */
441
442static struct platform_driver ds2760_battery_driver = {
443 .driver = {
444 .name = "ds2760-battery",
445 },
446 .probe = ds2760_battery_probe,
447 .remove = ds2760_battery_remove,
448 .suspend = ds2760_battery_suspend,
449 .resume = ds2760_battery_resume,
450};
451
452static int __init ds2760_battery_init(void)
453{
454 return platform_driver_register(&ds2760_battery_driver);
455}
456
457static void __exit ds2760_battery_exit(void)
458{
459 platform_driver_unregister(&ds2760_battery_driver);
460 return;
461}
462
463module_init(ds2760_battery_init);
464module_exit(ds2760_battery_exit);
465
466MODULE_LICENSE("GPL");
467MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
468 "Matt Reimer <mreimer@vpop.net>, "
469 "Anton Vorontsov <cbou@mail.ru>");
470MODULE_DESCRIPTION("ds2760 battery driver");
diff --git a/drivers/power/olpc_battery.c b/drivers/power/olpc_battery.c
new file mode 100644
index 000000000000..878684df7667
--- /dev/null
+++ b/drivers/power/olpc_battery.c
@@ -0,0 +1,352 @@
1/*
2 * Battery driver for One Laptop Per Child board.
3 *
4 * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/module.h>
12#include <linux/err.h>
13#include <linux/platform_device.h>
14#include <linux/power_supply.h>
15#include <linux/jiffies.h>
16#include <linux/sched.h>
17#include <asm/olpc.h>
18
19
20#define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */
21#define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */
22#define EC_BAT_ACR 0x12
23#define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */
24#define EC_AMB_TEMP 0x14 /* uint16_t, *100/256, °C */
25#define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */
26#define EC_BAT_SOC 0x16 /* uint8_t, percentage */
27#define EC_BAT_SERIAL 0x17 /* uint8_t[6] */
28#define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */
29#define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */
30
31#define BAT_STAT_PRESENT 0x01
32#define BAT_STAT_FULL 0x02
33#define BAT_STAT_LOW 0x04
34#define BAT_STAT_DESTROY 0x08
35#define BAT_STAT_AC 0x10
36#define BAT_STAT_CHARGING 0x20
37#define BAT_STAT_DISCHARGING 0x40
38
39#define BAT_ERR_INFOFAIL 0x02
40#define BAT_ERR_OVERVOLTAGE 0x04
41#define BAT_ERR_OVERTEMP 0x05
42#define BAT_ERR_GAUGESTOP 0x06
43#define BAT_ERR_OUT_OF_CONTROL 0x07
44#define BAT_ERR_ID_FAIL 0x09
45#define BAT_ERR_ACR_FAIL 0x10
46
47#define BAT_ADDR_MFR_TYPE 0x5F
48
49/*********************************************************************
50 * Power
51 *********************************************************************/
52
53static int olpc_ac_get_prop(struct power_supply *psy,
54 enum power_supply_property psp,
55 union power_supply_propval *val)
56{
57 int ret = 0;
58 uint8_t status;
59
60 switch (psp) {
61 case POWER_SUPPLY_PROP_ONLINE:
62 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
63 if (ret)
64 return ret;
65
66 val->intval = !!(status & BAT_STAT_AC);
67 break;
68 default:
69 ret = -EINVAL;
70 break;
71 }
72 return ret;
73}
74
75static enum power_supply_property olpc_ac_props[] = {
76 POWER_SUPPLY_PROP_ONLINE,
77};
78
79static struct power_supply olpc_ac = {
80 .name = "olpc-ac",
81 .type = POWER_SUPPLY_TYPE_MAINS,
82 .properties = olpc_ac_props,
83 .num_properties = ARRAY_SIZE(olpc_ac_props),
84 .get_property = olpc_ac_get_prop,
85};
86
87/*********************************************************************
88 * Battery properties
89 *********************************************************************/
90static int olpc_bat_get_property(struct power_supply *psy,
91 enum power_supply_property psp,
92 union power_supply_propval *val)
93{
94 int ret = 0;
95 int16_t ec_word;
96 uint8_t ec_byte;
97
98 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1);
99 if (ret)
100 return ret;
101
102 /* Theoretically there's a race here -- the battery could be
103 removed immediately after we check whether it's present, and
104 then we query for some other property of the now-absent battery.
105 It doesn't matter though -- the EC will return the last-known
106 information, and it's as if we just ran that _little_ bit faster
107 and managed to read it out before the battery went away. */
108 if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT)
109 return -ENODEV;
110
111 switch (psp) {
112 case POWER_SUPPLY_PROP_STATUS:
113 if (olpc_platform_info.ecver > 0x44) {
114 if (ec_byte & BAT_STAT_CHARGING)
115 val->intval = POWER_SUPPLY_STATUS_CHARGING;
116 else if (ec_byte & BAT_STAT_DISCHARGING)
117 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
118 else if (ec_byte & BAT_STAT_FULL)
119 val->intval = POWER_SUPPLY_STATUS_FULL;
120 else /* er,... */
121 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
122 } else {
123 /* Older EC didn't report charge/discharge bits */
124 if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */
125 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
126 else if (ec_byte & BAT_STAT_FULL)
127 val->intval = POWER_SUPPLY_STATUS_FULL;
128 else /* Not _necessarily_ true but EC doesn't tell all yet */
129 val->intval = POWER_SUPPLY_STATUS_CHARGING;
130 break;
131 }
132 case POWER_SUPPLY_PROP_PRESENT:
133 val->intval = !!(ec_byte & BAT_STAT_PRESENT);
134 break;
135
136 case POWER_SUPPLY_PROP_HEALTH:
137 if (ec_byte & BAT_STAT_DESTROY)
138 val->intval = POWER_SUPPLY_HEALTH_DEAD;
139 else {
140 ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1);
141 if (ret)
142 return ret;
143
144 switch (ec_byte) {
145 case 0:
146 val->intval = POWER_SUPPLY_HEALTH_GOOD;
147 break;
148
149 case BAT_ERR_OVERTEMP:
150 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
151 break;
152
153 case BAT_ERR_OVERVOLTAGE:
154 val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
155 break;
156
157 case BAT_ERR_INFOFAIL:
158 case BAT_ERR_OUT_OF_CONTROL:
159 case BAT_ERR_ID_FAIL:
160 case BAT_ERR_ACR_FAIL:
161 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
162 break;
163
164 default:
165 /* Eep. We don't know this failure code */
166 return -EIO;
167 }
168 }
169 break;
170
171 case POWER_SUPPLY_PROP_MANUFACTURER:
172 ec_byte = BAT_ADDR_MFR_TYPE;
173 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
174 if (ret)
175 return ret;
176
177 switch (ec_byte >> 4) {
178 case 1:
179 val->strval = "Gold Peak";
180 break;
181 case 2:
182 val->strval = "BYD";
183 break;
184 default:
185 val->strval = "Unknown";
186 break;
187 }
188 break;
189 case POWER_SUPPLY_PROP_TECHNOLOGY:
190 ec_byte = BAT_ADDR_MFR_TYPE;
191 ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1);
192 if (ret)
193 return ret;
194
195 switch (ec_byte & 0xf) {
196 case 1:
197 val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH;
198 break;
199 case 2:
200 val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe;
201 break;
202 default:
203 val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
204 break;
205 }
206 break;
207 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
208 ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2);
209 if (ret)
210 return ret;
211
212 ec_word = be16_to_cpu(ec_word);
213 val->intval = ec_word * 9760L / 32;
214 break;
215 case POWER_SUPPLY_PROP_CURRENT_AVG:
216 ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2);
217 if (ret)
218 return ret;
219
220 ec_word = be16_to_cpu(ec_word);
221 val->intval = ec_word * 15625L / 120;
222 break;
223 case POWER_SUPPLY_PROP_CAPACITY:
224 ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1);
225 if (ret)
226 return ret;
227 val->intval = ec_byte;
228 break;
229 case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
230 if (ec_byte & BAT_STAT_FULL)
231 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
232 else if (ec_byte & BAT_STAT_LOW)
233 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
234 else
235 val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
236 break;
237 case POWER_SUPPLY_PROP_TEMP:
238 ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2);
239 if (ret)
240 return ret;
241 ec_word = be16_to_cpu(ec_word);
242 val->intval = ec_word * 100 / 256;
243 break;
244 case POWER_SUPPLY_PROP_TEMP_AMBIENT:
245 ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2);
246 if (ret)
247 return ret;
248
249 ec_word = be16_to_cpu(ec_word);
250 val->intval = ec_word * 100 / 256;
251 break;
252 default:
253 ret = -EINVAL;
254 break;
255 }
256
257 return ret;
258}
259
260static enum power_supply_property olpc_bat_props[] = {
261 POWER_SUPPLY_PROP_STATUS,
262 POWER_SUPPLY_PROP_PRESENT,
263 POWER_SUPPLY_PROP_HEALTH,
264 POWER_SUPPLY_PROP_TECHNOLOGY,
265 POWER_SUPPLY_PROP_VOLTAGE_AVG,
266 POWER_SUPPLY_PROP_CURRENT_AVG,
267 POWER_SUPPLY_PROP_CAPACITY,
268 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
269 POWER_SUPPLY_PROP_TEMP,
270 POWER_SUPPLY_PROP_TEMP_AMBIENT,
271 POWER_SUPPLY_PROP_MANUFACTURER,
272};
273
274/*********************************************************************
275 * Initialisation
276 *********************************************************************/
277
278static struct platform_device *bat_pdev;
279
280static struct power_supply olpc_bat = {
281 .properties = olpc_bat_props,
282 .num_properties = ARRAY_SIZE(olpc_bat_props),
283 .get_property = olpc_bat_get_property,
284 .use_for_apm = 1,
285};
286
287void olpc_battery_trigger_uevent(unsigned long cause)
288{
289 if (cause & EC_SCI_SRC_ACPWR)
290 kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE);
291 if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY))
292 kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE);
293}
294
295static int __init olpc_bat_init(void)
296{
297 int ret = 0;
298 uint8_t status;
299
300 if (!olpc_platform_info.ecver)
301 return -ENXIO;
302 if (olpc_platform_info.ecver < 0x43) {
303 printk(KERN_NOTICE "OLPC EC version 0x%02x too old for battery driver.\n", olpc_platform_info.ecver);
304 return -ENXIO;
305 }
306
307 ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1);
308 if (ret)
309 return ret;
310
311 /* Ignore the status. It doesn't actually matter */
312
313 bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0);
314 if (IS_ERR(bat_pdev))
315 return PTR_ERR(bat_pdev);
316
317 ret = power_supply_register(&bat_pdev->dev, &olpc_ac);
318 if (ret)
319 goto ac_failed;
320
321 olpc_bat.name = bat_pdev->name;
322
323 ret = power_supply_register(&bat_pdev->dev, &olpc_bat);
324 if (ret)
325 goto battery_failed;
326
327 olpc_register_battery_callback(&olpc_battery_trigger_uevent);
328 goto success;
329
330battery_failed:
331 power_supply_unregister(&olpc_ac);
332ac_failed:
333 platform_device_unregister(bat_pdev);
334success:
335 return ret;
336}
337
338static void __exit olpc_bat_exit(void)
339{
340 olpc_deregister_battery_callback();
341 power_supply_unregister(&olpc_bat);
342 power_supply_unregister(&olpc_ac);
343 platform_device_unregister(bat_pdev);
344 return;
345}
346
347module_init(olpc_bat_init);
348module_exit(olpc_bat_exit);
349
350MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
351MODULE_LICENSE("GPL");
352MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine");
diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c
new file mode 100644
index 000000000000..4e1eb040e148
--- /dev/null
+++ b/drivers/power/pda_power.c
@@ -0,0 +1,261 @@
1/*
2 * Common power driver for PDAs and phones with one or two external
3 * power supplies (AC/USB) connected to main and backup batteries,
4 * and optional builtin charger.
5 *
6 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/interrupt.h>
16#include <linux/power_supply.h>
17#include <linux/pda_power.h>
18#include <linux/timer.h>
19#include <linux/jiffies.h>
20
21static inline unsigned int get_irq_flags(struct resource *res)
22{
23 unsigned int flags = IRQF_DISABLED | IRQF_SHARED;
24
25 flags |= res->flags & IRQF_TRIGGER_MASK;
26
27 return flags;
28}
29
30static struct device *dev;
31static struct pda_power_pdata *pdata;
32static struct resource *ac_irq, *usb_irq;
33static struct timer_list charger_timer;
34static struct timer_list supply_timer;
35
36static int pda_power_get_property(struct power_supply *psy,
37 enum power_supply_property psp,
38 union power_supply_propval *val)
39{
40 switch (psp) {
41 case POWER_SUPPLY_PROP_ONLINE:
42 if (psy->type == POWER_SUPPLY_TYPE_MAINS)
43 val->intval = pdata->is_ac_online ?
44 pdata->is_ac_online() : 0;
45 else
46 val->intval = pdata->is_usb_online ?
47 pdata->is_usb_online() : 0;
48 break;
49 default:
50 return -EINVAL;
51 }
52 return 0;
53}
54
55static enum power_supply_property pda_power_props[] = {
56 POWER_SUPPLY_PROP_ONLINE,
57};
58
59static char *pda_power_supplied_to[] = {
60 "main-battery",
61 "backup-battery",
62};
63
64static struct power_supply pda_power_supplies[] = {
65 {
66 .name = "ac",
67 .type = POWER_SUPPLY_TYPE_MAINS,
68 .supplied_to = pda_power_supplied_to,
69 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
70 .properties = pda_power_props,
71 .num_properties = ARRAY_SIZE(pda_power_props),
72 .get_property = pda_power_get_property,
73 },
74 {
75 .name = "usb",
76 .type = POWER_SUPPLY_TYPE_USB,
77 .supplied_to = pda_power_supplied_to,
78 .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
79 .properties = pda_power_props,
80 .num_properties = ARRAY_SIZE(pda_power_props),
81 .get_property = pda_power_get_property,
82 },
83};
84
85static void update_charger(void)
86{
87 if (!pdata->set_charge)
88 return;
89
90 if (pdata->is_ac_online && pdata->is_ac_online()) {
91 dev_dbg(dev, "charger on (AC)\n");
92 pdata->set_charge(PDA_POWER_CHARGE_AC);
93 } else if (pdata->is_usb_online && pdata->is_usb_online()) {
94 dev_dbg(dev, "charger on (USB)\n");
95 pdata->set_charge(PDA_POWER_CHARGE_USB);
96 } else {
97 dev_dbg(dev, "charger off\n");
98 pdata->set_charge(0);
99 }
100
101 return;
102}
103
104static void supply_timer_func(unsigned long irq)
105{
106 if (ac_irq && irq == ac_irq->start)
107 power_supply_changed(&pda_power_supplies[0]);
108 else if (usb_irq && irq == usb_irq->start)
109 power_supply_changed(&pda_power_supplies[1]);
110 return;
111}
112
113static void charger_timer_func(unsigned long irq)
114{
115 update_charger();
116
117 /* Okay, charger set. Now wait a bit before notifying supplicants,
118 * charge power should stabilize. */
119 supply_timer.data = irq;
120 mod_timer(&supply_timer,
121 jiffies + msecs_to_jiffies(pdata->wait_for_charger));
122 return;
123}
124
125static irqreturn_t power_changed_isr(int irq, void *unused)
126{
127 /* Wait a bit before reading ac/usb line status and setting charger,
128 * because ac/usb status readings may lag from irq. */
129 charger_timer.data = irq;
130 mod_timer(&charger_timer,
131 jiffies + msecs_to_jiffies(pdata->wait_for_status));
132 return IRQ_HANDLED;
133}
134
135static int pda_power_probe(struct platform_device *pdev)
136{
137 int ret = 0;
138
139 dev = &pdev->dev;
140
141 if (pdev->id != -1) {
142 dev_err(dev, "it's meaningless to register several "
143 "pda_powers; use id = -1\n");
144 ret = -EINVAL;
145 goto wrongid;
146 }
147
148 pdata = pdev->dev.platform_data;
149
150 update_charger();
151
152 if (!pdata->wait_for_status)
153 pdata->wait_for_status = 500;
154
155 if (!pdata->wait_for_charger)
156 pdata->wait_for_charger = 500;
157
158 setup_timer(&charger_timer, charger_timer_func, 0);
159 setup_timer(&supply_timer, supply_timer_func, 0);
160
161 ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
162 usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
163 if (!ac_irq && !usb_irq) {
164 dev_err(dev, "no ac/usb irq specified\n");
165 ret = -ENODEV;
166 goto noirqs;
167 }
168
169 if (pdata->supplied_to) {
170 pda_power_supplies[0].supplied_to = pdata->supplied_to;
171 pda_power_supplies[1].supplied_to = pdata->supplied_to;
172 pda_power_supplies[0].num_supplicants = pdata->num_supplicants;
173 pda_power_supplies[1].num_supplicants = pdata->num_supplicants;
174 }
175
176 ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
177 if (ret) {
178 dev_err(dev, "failed to register %s power supply\n",
179 pda_power_supplies[0].name);
180 goto supply0_failed;
181 }
182
183 ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
184 if (ret) {
185 dev_err(dev, "failed to register %s power supply\n",
186 pda_power_supplies[1].name);
187 goto supply1_failed;
188 }
189
190 if (ac_irq) {
191 ret = request_irq(ac_irq->start, power_changed_isr,
192 get_irq_flags(ac_irq), ac_irq->name,
193 &pda_power_supplies[0]);
194 if (ret) {
195 dev_err(dev, "request ac irq failed\n");
196 goto ac_irq_failed;
197 }
198 }
199
200 if (usb_irq) {
201 ret = request_irq(usb_irq->start, power_changed_isr,
202 get_irq_flags(usb_irq), usb_irq->name,
203 &pda_power_supplies[1]);
204 if (ret) {
205 dev_err(dev, "request usb irq failed\n");
206 goto usb_irq_failed;
207 }
208 }
209
210 goto success;
211
212usb_irq_failed:
213 if (ac_irq)
214 free_irq(ac_irq->start, &pda_power_supplies[0]);
215ac_irq_failed:
216 power_supply_unregister(&pda_power_supplies[1]);
217supply1_failed:
218 power_supply_unregister(&pda_power_supplies[0]);
219supply0_failed:
220noirqs:
221wrongid:
222success:
223 return ret;
224}
225
226static int pda_power_remove(struct platform_device *pdev)
227{
228 if (usb_irq)
229 free_irq(usb_irq->start, &pda_power_supplies[1]);
230 if (ac_irq)
231 free_irq(ac_irq->start, &pda_power_supplies[0]);
232 del_timer_sync(&charger_timer);
233 del_timer_sync(&supply_timer);
234 power_supply_unregister(&pda_power_supplies[1]);
235 power_supply_unregister(&pda_power_supplies[0]);
236 return 0;
237}
238
239static struct platform_driver pda_power_pdrv = {
240 .driver = {
241 .name = "pda-power",
242 },
243 .probe = pda_power_probe,
244 .remove = pda_power_remove,
245};
246
247static int __init pda_power_init(void)
248{
249 return platform_driver_register(&pda_power_pdrv);
250}
251
252static void __exit pda_power_exit(void)
253{
254 platform_driver_unregister(&pda_power_pdrv);
255 return;
256}
257
258module_init(pda_power_init);
259module_exit(pda_power_exit);
260MODULE_LICENSE("GPL");
261MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
diff --git a/drivers/power/pmu_battery.c b/drivers/power/pmu_battery.c
new file mode 100644
index 000000000000..2fea4af0e40a
--- /dev/null
+++ b/drivers/power/pmu_battery.c
@@ -0,0 +1,215 @@
1/*
2 * Battery class driver for Apple PMU
3 *
4 * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/module.h>
12#include <linux/platform_device.h>
13#include <linux/err.h>
14#include <linux/power_supply.h>
15#include <linux/adb.h>
16#include <linux/pmu.h>
17
18static struct pmu_battery_dev {
19 struct power_supply bat;
20 struct pmu_battery_info *pbi;
21 char name[16];
22 int propval;
23} *pbats[PMU_MAX_BATTERIES];
24
25#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat)
26
27/*********************************************************************
28 * Power
29 *********************************************************************/
30
31static int pmu_get_ac_prop(struct power_supply *psy,
32 enum power_supply_property psp,
33 union power_supply_propval *val)
34{
35 switch (psp) {
36 case POWER_SUPPLY_PROP_ONLINE:
37 val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
38 (pmu_battery_count == 0);
39 break;
40 default:
41 return -EINVAL;
42 }
43
44 return 0;
45}
46
47static enum power_supply_property pmu_ac_props[] = {
48 POWER_SUPPLY_PROP_ONLINE,
49};
50
51static struct power_supply pmu_ac = {
52 .name = "pmu-ac",
53 .type = POWER_SUPPLY_TYPE_MAINS,
54 .properties = pmu_ac_props,
55 .num_properties = ARRAY_SIZE(pmu_ac_props),
56 .get_property = pmu_get_ac_prop,
57};
58
59/*********************************************************************
60 * Battery properties
61 *********************************************************************/
62
63static char *pmu_batt_types[] = {
64 "Smart", "Comet", "Hooper", "Unknown"
65};
66
67static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
68{
69 switch (pbi->flags & PMU_BATT_TYPE_MASK) {
70 case PMU_BATT_TYPE_SMART:
71 return pmu_batt_types[0];
72 case PMU_BATT_TYPE_COMET:
73 return pmu_batt_types[1];
74 case PMU_BATT_TYPE_HOOPER:
75 return pmu_batt_types[2];
76 default: break;
77 }
78 return pmu_batt_types[3];
79}
80
81static int pmu_bat_get_property(struct power_supply *psy,
82 enum power_supply_property psp,
83 union power_supply_propval *val)
84{
85 struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
86 struct pmu_battery_info *pbi = pbat->pbi;
87
88 switch (psp) {
89 case POWER_SUPPLY_PROP_STATUS:
90 if (pbi->flags & PMU_BATT_CHARGING)
91 val->intval = POWER_SUPPLY_STATUS_CHARGING;
92 else
93 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
94 break;
95 case POWER_SUPPLY_PROP_PRESENT:
96 val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
97 break;
98 case POWER_SUPPLY_PROP_MODEL_NAME:
99 val->strval = pmu_bat_get_model_name(pbi);
100 break;
101 case POWER_SUPPLY_PROP_ENERGY_AVG:
102 val->intval = pbi->charge * 1000; /* mWh -> µWh */
103 break;
104 case POWER_SUPPLY_PROP_ENERGY_FULL:
105 val->intval = pbi->max_charge * 1000; /* mWh -> µWh */
106 break;
107 case POWER_SUPPLY_PROP_CURRENT_AVG:
108 val->intval = pbi->amperage * 1000; /* mA -> µA */
109 break;
110 case POWER_SUPPLY_PROP_VOLTAGE_AVG:
111 val->intval = pbi->voltage * 1000; /* mV -> µV */
112 break;
113 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
114 val->intval = pbi->time_remaining;
115 break;
116 default:
117 return -EINVAL;
118 }
119
120 return 0;
121}
122
123static enum power_supply_property pmu_bat_props[] = {
124 POWER_SUPPLY_PROP_STATUS,
125 POWER_SUPPLY_PROP_PRESENT,
126 POWER_SUPPLY_PROP_MODEL_NAME,
127 POWER_SUPPLY_PROP_ENERGY_AVG,
128 POWER_SUPPLY_PROP_ENERGY_FULL,
129 POWER_SUPPLY_PROP_CURRENT_AVG,
130 POWER_SUPPLY_PROP_VOLTAGE_AVG,
131 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
132};
133
134/*********************************************************************
135 * Initialisation
136 *********************************************************************/
137
138static struct platform_device *bat_pdev;
139
140static int __init pmu_bat_init(void)
141{
142 int ret;
143 int i;
144
145 bat_pdev = platform_device_register_simple("pmu-battery",
146 0, NULL, 0);
147 if (IS_ERR(bat_pdev)) {
148 ret = PTR_ERR(bat_pdev);
149 goto pdev_register_failed;
150 }
151
152 ret = power_supply_register(&bat_pdev->dev, &pmu_ac);
153 if (ret)
154 goto ac_register_failed;
155
156 for (i = 0; i < pmu_battery_count; i++) {
157 struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
158 GFP_KERNEL);
159 if (!pbat)
160 break;
161
162 sprintf(pbat->name, "PMU battery %d", i);
163 pbat->bat.name = pbat->name;
164 pbat->bat.properties = pmu_bat_props;
165 pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props);
166 pbat->bat.get_property = pmu_bat_get_property;
167 pbat->pbi = &pmu_batteries[i];
168
169 ret = power_supply_register(&bat_pdev->dev, &pbat->bat);
170 if (ret) {
171 kfree(pbat);
172 goto battery_register_failed;
173 }
174 pbats[i] = pbat;
175 }
176
177 goto success;
178
179battery_register_failed:
180 while (i--) {
181 if (!pbats[i])
182 continue;
183 power_supply_unregister(&pbats[i]->bat);
184 kfree(pbats[i]);
185 }
186 power_supply_unregister(&pmu_ac);
187ac_register_failed:
188 platform_device_unregister(bat_pdev);
189pdev_register_failed:
190success:
191 return ret;
192}
193
194static void __exit pmu_bat_exit(void)
195{
196 int i;
197
198 for (i = 0; i < PMU_MAX_BATTERIES; i++) {
199 if (!pbats[i])
200 continue;
201 power_supply_unregister(&pbats[i]->bat);
202 kfree(pbats[i]);
203 }
204 power_supply_unregister(&pmu_ac);
205 platform_device_unregister(bat_pdev);
206
207 return;
208}
209
210module_init(pmu_bat_init);
211module_exit(pmu_bat_exit);
212
213MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
214MODULE_LICENSE("GPL");
215MODULE_DESCRIPTION("PMU battery driver");
diff --git a/drivers/power/power_supply.h b/drivers/power/power_supply.h
new file mode 100644
index 000000000000..a9880d468ee4
--- /dev/null
+++ b/drivers/power/power_supply.h
@@ -0,0 +1,42 @@
1/*
2 * Functions private to power supply 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#ifdef CONFIG_SYSFS
14
15extern int power_supply_create_attrs(struct power_supply *psy);
16extern void power_supply_remove_attrs(struct power_supply *psy);
17extern int power_supply_uevent(struct device *dev, char **envp, int num_envp,
18 char *buffer, int buffer_size);
19
20#else
21
22static inline int power_supply_create_attrs(struct power_supply *psy)
23{ return 0; }
24static inline void power_supply_remove_attrs(struct power_supply *psy) {}
25#define power_supply_uevent NULL
26
27#endif /* CONFIG_SYSFS */
28
29#ifdef CONFIG_LEDS_TRIGGERS
30
31extern void power_supply_update_leds(struct power_supply *psy);
32extern int power_supply_create_triggers(struct power_supply *psy);
33extern void power_supply_remove_triggers(struct power_supply *psy);
34
35#else
36
37static inline void power_supply_update_leds(struct power_supply *psy) {}
38static inline int power_supply_create_triggers(struct power_supply *psy)
39{ return 0; }
40static inline void power_supply_remove_triggers(struct power_supply *psy) {}
41
42#endif /* CONFIG_LEDS_TRIGGERS */
diff --git a/drivers/power/power_supply_core.c b/drivers/power/power_supply_core.c
new file mode 100644
index 000000000000..e87ea5156755
--- /dev/null
+++ b/drivers/power/power_supply_core.c
@@ -0,0 +1,168 @@
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>
16#include <linux/device.h>
17#include <linux/err.h>
18#include <linux/power_supply.h>
19#include "power_supply.h"
20
21struct class *power_supply_class;
22
23static void power_supply_changed_work(struct work_struct *work)
24{
25 struct power_supply *psy = container_of(work, struct power_supply,
26 changed_work);
27 int i;
28
29 dev_dbg(psy->dev, "%s\n", __FUNCTION__);
30
31 for (i = 0; i < psy->num_supplicants; i++) {
32 struct device *dev;
33
34 down(&power_supply_class->sem);
35 list_for_each_entry(dev, &power_supply_class->devices, node) {
36 struct power_supply *pst = dev_get_drvdata(dev);
37
38 if (!strcmp(psy->supplied_to[i], pst->name)) {
39 if (pst->external_power_changed)
40 pst->external_power_changed(pst);
41 }
42 }
43 up(&power_supply_class->sem);
44 }
45
46 power_supply_update_leds(psy);
47
48 kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
49
50 return;
51}
52
53void power_supply_changed(struct power_supply *psy)
54{
55 dev_dbg(psy->dev, "%s\n", __FUNCTION__);
56
57 schedule_work(&psy->changed_work);
58
59 return;
60}
61
62int power_supply_am_i_supplied(struct power_supply *psy)
63{
64 union power_supply_propval ret = {0,};
65 struct device *dev;
66
67 down(&power_supply_class->sem);
68 list_for_each_entry(dev, &power_supply_class->devices, node) {
69 struct power_supply *epsy = dev_get_drvdata(dev);
70 int i;
71
72 for (i = 0; i < epsy->num_supplicants; i++) {
73 if (!strcmp(epsy->supplied_to[i], psy->name)) {
74 if (epsy->get_property(epsy,
75 POWER_SUPPLY_PROP_ONLINE, &ret))
76 continue;
77 if (ret.intval)
78 goto out;
79 }
80 }
81 }
82out:
83 up(&power_supply_class->sem);
84
85 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval);
86
87 return ret.intval;
88}
89
90int power_supply_register(struct device *parent, struct power_supply *psy)
91{
92 int rc = 0;
93
94 psy->dev = device_create(power_supply_class, parent, 0,
95 "%s", psy->name);
96 if (IS_ERR(psy->dev)) {
97 rc = PTR_ERR(psy->dev);
98 goto dev_create_failed;
99 }
100
101 dev_set_drvdata(psy->dev, psy);
102
103 INIT_WORK(&psy->changed_work, power_supply_changed_work);
104
105 rc = power_supply_create_attrs(psy);
106 if (rc)
107 goto create_attrs_failed;
108
109 rc = power_supply_create_triggers(psy);
110 if (rc)
111 goto create_triggers_failed;
112
113 power_supply_changed(psy);
114
115 goto success;
116
117create_triggers_failed:
118 power_supply_remove_attrs(psy);
119create_attrs_failed:
120 device_unregister(psy->dev);
121dev_create_failed:
122success:
123 return rc;
124}
125
126void power_supply_unregister(struct power_supply *psy)
127{
128 flush_scheduled_work();
129 power_supply_remove_triggers(psy);
130 power_supply_remove_attrs(psy);
131 device_unregister(psy->dev);
132 return;
133}
134
135static int __init power_supply_class_init(void)
136{
137 power_supply_class = class_create(THIS_MODULE, "power_supply");
138
139 if (IS_ERR(power_supply_class))
140 return PTR_ERR(power_supply_class);
141
142 power_supply_class->dev_uevent = power_supply_uevent;
143
144 return 0;
145}
146
147static void __exit power_supply_class_exit(void)
148{
149 class_destroy(power_supply_class);
150 return;
151}
152
153EXPORT_SYMBOL_GPL(power_supply_changed);
154EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
155EXPORT_SYMBOL_GPL(power_supply_register);
156EXPORT_SYMBOL_GPL(power_supply_unregister);
157
158/* exported for the APM Power driver, APM emulation */
159EXPORT_SYMBOL_GPL(power_supply_class);
160
161subsys_initcall(power_supply_class_init);
162module_exit(power_supply_class_exit);
163
164MODULE_DESCRIPTION("Universal power supply monitor class");
165MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
166 "Szabolcs Gyurko, "
167 "Anton Vorontsov <cbou@mail.ru>");
168MODULE_LICENSE("GPL");
diff --git a/drivers/power/power_supply_leds.c b/drivers/power/power_supply_leds.c
new file mode 100644
index 000000000000..7232490bb595
--- /dev/null
+++ b/drivers/power/power_supply_leds.c
@@ -0,0 +1,176 @@
1/*
2 * LEDs triggers for power supply 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/power_supply.h>
14
15/* Battery specific LEDs triggers. */
16
17static void power_supply_update_bat_leds(struct power_supply *psy)
18{
19 union power_supply_propval status;
20
21 if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
22 return;
23
24 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval);
25
26 switch (status.intval) {
27 case POWER_SUPPLY_STATUS_FULL:
28 led_trigger_event(psy->charging_full_trig, LED_FULL);
29 led_trigger_event(psy->charging_trig, LED_OFF);
30 led_trigger_event(psy->full_trig, LED_FULL);
31 break;
32 case POWER_SUPPLY_STATUS_CHARGING:
33 led_trigger_event(psy->charging_full_trig, LED_FULL);
34 led_trigger_event(psy->charging_trig, LED_FULL);
35 led_trigger_event(psy->full_trig, LED_OFF);
36 break;
37 default:
38 led_trigger_event(psy->charging_full_trig, LED_OFF);
39 led_trigger_event(psy->charging_trig, LED_OFF);
40 led_trigger_event(psy->full_trig, LED_OFF);
41 break;
42 }
43
44 return;
45}
46
47static int power_supply_create_bat_triggers(struct power_supply *psy)
48{
49 int rc = 0;
50
51 psy->charging_full_trig_name = kmalloc(strlen(psy->name) +
52 sizeof("-charging-or-full"), GFP_KERNEL);
53 if (!psy->charging_full_trig_name)
54 goto charging_full_failed;
55
56 psy->charging_trig_name = kmalloc(strlen(psy->name) +
57 sizeof("-charging"), GFP_KERNEL);
58 if (!psy->charging_trig_name)
59 goto charging_failed;
60
61 psy->full_trig_name = kmalloc(strlen(psy->name) +
62 sizeof("-full"), GFP_KERNEL);
63 if (!psy->full_trig_name)
64 goto full_failed;
65
66 strcpy(psy->charging_full_trig_name, psy->name);
67 strcat(psy->charging_full_trig_name, "-charging-or-full");
68 strcpy(psy->charging_trig_name, psy->name);
69 strcat(psy->charging_trig_name, "-charging");
70 strcpy(psy->full_trig_name, psy->name);
71 strcat(psy->full_trig_name, "-full");
72
73 led_trigger_register_simple(psy->charging_full_trig_name,
74 &psy->charging_full_trig);
75 led_trigger_register_simple(psy->charging_trig_name,
76 &psy->charging_trig);
77 led_trigger_register_simple(psy->full_trig_name,
78 &psy->full_trig);
79
80 goto success;
81
82full_failed:
83 kfree(psy->charging_trig_name);
84charging_failed:
85 kfree(psy->charging_full_trig_name);
86charging_full_failed:
87 rc = -ENOMEM;
88success:
89 return rc;
90}
91
92static void power_supply_remove_bat_triggers(struct power_supply *psy)
93{
94 led_trigger_unregister_simple(psy->charging_full_trig);
95 led_trigger_unregister_simple(psy->charging_trig);
96 led_trigger_unregister_simple(psy->full_trig);
97 kfree(psy->full_trig_name);
98 kfree(psy->charging_trig_name);
99 kfree(psy->charging_full_trig_name);
100 return;
101}
102
103/* Generated power specific LEDs triggers. */
104
105static void power_supply_update_gen_leds(struct power_supply *psy)
106{
107 union power_supply_propval online;
108
109 if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
110 return;
111
112 dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval);
113
114 if (online.intval)
115 led_trigger_event(psy->online_trig, LED_FULL);
116 else
117 led_trigger_event(psy->online_trig, LED_OFF);
118
119 return;
120}
121
122static int power_supply_create_gen_triggers(struct power_supply *psy)
123{
124 int rc = 0;
125
126 psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"),
127 GFP_KERNEL);
128 if (!psy->online_trig_name)
129 goto online_failed;
130
131 strcpy(psy->online_trig_name, psy->name);
132 strcat(psy->online_trig_name, "-online");
133
134 led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
135
136 goto success;
137
138online_failed:
139 rc = -ENOMEM;
140success:
141 return rc;
142}
143
144static void power_supply_remove_gen_triggers(struct power_supply *psy)
145{
146 led_trigger_unregister_simple(psy->online_trig);
147 kfree(psy->online_trig_name);
148 return;
149}
150
151/* Choice what triggers to create&update. */
152
153void power_supply_update_leds(struct power_supply *psy)
154{
155 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
156 power_supply_update_bat_leds(psy);
157 else
158 power_supply_update_gen_leds(psy);
159 return;
160}
161
162int power_supply_create_triggers(struct power_supply *psy)
163{
164 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
165 return power_supply_create_bat_triggers(psy);
166 return power_supply_create_gen_triggers(psy);
167}
168
169void power_supply_remove_triggers(struct power_supply *psy)
170{
171 if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
172 power_supply_remove_bat_triggers(psy);
173 else
174 power_supply_remove_gen_triggers(psy);
175 return;
176}
diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c
new file mode 100644
index 000000000000..c07d4258d347
--- /dev/null
+++ b/drivers/power/power_supply_sysfs.c
@@ -0,0 +1,299 @@
1/*
2 * Sysfs interface for the universal power supply monitor class
3 *
4 * Copyright © 2007 David Woodhouse <dwmw2@infradead.org>
5 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
6 * Copyright © 2004 Szabolcs Gyurko
7 * Copyright © 2003 Ian Molton <spyro@f2s.com>
8 *
9 * Modified: 2004, Oct Szabolcs Gyurko
10 *
11 * You may use this code as per GPL version 2
12 */
13
14#include <linux/ctype.h>
15#include <linux/power_supply.h>
16
17/*
18 * This is because the name "current" breaks the device attr macro.
19 * The "current" word resolves to "(get_current())" so instead of
20 * "current" "(get_current())" appears in the sysfs.
21 *
22 * The source of this definition is the device.h which calls __ATTR
23 * macro in sysfs.h which calls the __stringify macro.
24 *
25 * Only modification that the name is not tried to be resolved
26 * (as a macro let's say).
27 */
28
29#define POWER_SUPPLY_ATTR(_name) \
30{ \
31 .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \
32 .show = power_supply_show_property, \
33 .store = NULL, \
34}
35
36static struct device_attribute power_supply_attrs[];
37
38static ssize_t power_supply_show_property(struct device *dev,
39 struct device_attribute *attr,
40 char *buf) {
41 static char *status_text[] = {
42 "Unknown", "Charging", "Discharging", "Not charging", "Full"
43 };
44 static char *health_text[] = {
45 "Unknown", "Good", "Overheat", "Dead", "Over voltage",
46 "Unspecified failure"
47 };
48 static char *technology_text[] = {
49 "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd"
50 };
51 static char *capacity_level_text[] = {
52 "Unknown", "Critical", "Low", "Normal", "High", "Full"
53 };
54 ssize_t ret;
55 struct power_supply *psy = dev_get_drvdata(dev);
56 const ptrdiff_t off = attr - power_supply_attrs;
57 union power_supply_propval value;
58
59 ret = psy->get_property(psy, off, &value);
60
61 if (ret < 0) {
62 if (ret != -ENODEV)
63 dev_err(dev, "driver failed to report `%s' property\n",
64 attr->attr.name);
65 return ret;
66 }
67
68 if (off == POWER_SUPPLY_PROP_STATUS)
69 return sprintf(buf, "%s\n", status_text[value.intval]);
70 else if (off == POWER_SUPPLY_PROP_HEALTH)
71 return sprintf(buf, "%s\n", health_text[value.intval]);
72 else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
73 return sprintf(buf, "%s\n", technology_text[value.intval]);
74 else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
75 return sprintf(buf, "%s\n",
76 capacity_level_text[value.intval]);
77 else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
78 return sprintf(buf, "%s\n", value.strval);
79
80 return sprintf(buf, "%d\n", value.intval);
81}
82
83/* Must be in the same order as POWER_SUPPLY_PROP_* */
84static struct device_attribute power_supply_attrs[] = {
85 /* Properties of type `int' */
86 POWER_SUPPLY_ATTR(status),
87 POWER_SUPPLY_ATTR(health),
88 POWER_SUPPLY_ATTR(present),
89 POWER_SUPPLY_ATTR(online),
90 POWER_SUPPLY_ATTR(technology),
91 POWER_SUPPLY_ATTR(voltage_max_design),
92 POWER_SUPPLY_ATTR(voltage_min_design),
93 POWER_SUPPLY_ATTR(voltage_now),
94 POWER_SUPPLY_ATTR(voltage_avg),
95 POWER_SUPPLY_ATTR(current_now),
96 POWER_SUPPLY_ATTR(current_avg),
97 POWER_SUPPLY_ATTR(charge_full_design),
98 POWER_SUPPLY_ATTR(charge_empty_design),
99 POWER_SUPPLY_ATTR(charge_full),
100 POWER_SUPPLY_ATTR(charge_empty),
101 POWER_SUPPLY_ATTR(charge_now),
102 POWER_SUPPLY_ATTR(charge_avg),
103 POWER_SUPPLY_ATTR(energy_full_design),
104 POWER_SUPPLY_ATTR(energy_empty_design),
105 POWER_SUPPLY_ATTR(energy_full),
106 POWER_SUPPLY_ATTR(energy_empty),
107 POWER_SUPPLY_ATTR(energy_now),
108 POWER_SUPPLY_ATTR(energy_avg),
109 POWER_SUPPLY_ATTR(capacity),
110 POWER_SUPPLY_ATTR(capacity_level),
111 POWER_SUPPLY_ATTR(temp),
112 POWER_SUPPLY_ATTR(temp_ambient),
113 POWER_SUPPLY_ATTR(time_to_empty_now),
114 POWER_SUPPLY_ATTR(time_to_empty_avg),
115 POWER_SUPPLY_ATTR(time_to_full_now),
116 POWER_SUPPLY_ATTR(time_to_full_avg),
117 /* Properties of type `const char *' */
118 POWER_SUPPLY_ATTR(model_name),
119 POWER_SUPPLY_ATTR(manufacturer),
120};
121
122static ssize_t power_supply_show_static_attrs(struct device *dev,
123 struct device_attribute *attr,
124 char *buf) {
125 static char *type_text[] = { "Battery", "UPS", "Mains", "USB" };
126 struct power_supply *psy = dev_get_drvdata(dev);
127
128 return sprintf(buf, "%s\n", type_text[psy->type]);
129}
130
131static struct device_attribute power_supply_static_attrs[] = {
132 __ATTR(type, 0444, power_supply_show_static_attrs, NULL),
133};
134
135int power_supply_create_attrs(struct power_supply *psy)
136{
137 int rc = 0;
138 int i, j;
139
140 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
141 rc = device_create_file(psy->dev,
142 &power_supply_static_attrs[i]);
143 if (rc)
144 goto statics_failed;
145 }
146
147 for (j = 0; j < psy->num_properties; j++) {
148 rc = device_create_file(psy->dev,
149 &power_supply_attrs[psy->properties[j]]);
150 if (rc)
151 goto dynamics_failed;
152 }
153
154 goto succeed;
155
156dynamics_failed:
157 while (j--)
158 device_remove_file(psy->dev,
159 &power_supply_attrs[psy->properties[j]]);
160statics_failed:
161 while (i--)
162 device_remove_file(psy->dev,
163 &power_supply_static_attrs[psy->properties[i]]);
164succeed:
165 return rc;
166}
167
168void power_supply_remove_attrs(struct power_supply *psy)
169{
170 int i;
171
172 for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
173 device_remove_file(psy->dev,
174 &power_supply_static_attrs[i]);
175
176 for (i = 0; i < psy->num_properties; i++)
177 device_remove_file(psy->dev,
178 &power_supply_attrs[psy->properties[i]]);
179
180 return;
181}
182
183static char *kstruprdup(const char *str, gfp_t gfp)
184{
185 char *ret, *ustr;
186
187 ustr = ret = kmalloc(strlen(str) + 1, gfp);
188
189 if (!ret)
190 return NULL;
191
192 while (*str)
193 *ustr++ = toupper(*str++);
194
195 *ustr = 0;
196
197 return ret;
198}
199
200int power_supply_uevent(struct device *dev, char **envp, int num_envp,
201 char *buffer, int buffer_size)
202{
203 struct power_supply *psy = dev_get_drvdata(dev);
204 int i = 0, length = 0, ret = 0, j;
205 char *prop_buf;
206 char *attrname;
207
208 dev_dbg(dev, "uevent\n");
209
210 if (!psy) {
211 dev_dbg(dev, "No power supply yet\n");
212 return ret;
213 }
214
215 dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
216
217 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
218 &length, "POWER_SUPPLY_NAME=%s", psy->name);
219 if (ret)
220 return ret;
221
222 prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
223 if (!prop_buf)
224 return -ENOMEM;
225
226 for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
227 struct device_attribute *attr;
228 char *line;
229
230 attr = &power_supply_static_attrs[j];
231
232 ret = power_supply_show_static_attrs(dev, attr, prop_buf);
233 if (ret < 0)
234 goto out;
235
236 line = strchr(prop_buf, '\n');
237 if (line)
238 *line = 0;
239
240 attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
241 if (!attrname) {
242 ret = -ENOMEM;
243 goto out;
244 }
245
246 dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
247
248 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
249 &length, "POWER_SUPPLY_%s=%s",
250 attrname, prop_buf);
251 kfree(attrname);
252 if (ret)
253 goto out;
254 }
255
256 dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
257
258 for (j = 0; j < psy->num_properties; j++) {
259 struct device_attribute *attr;
260 char *line;
261
262 attr = &power_supply_attrs[psy->properties[j]];
263
264 ret = power_supply_show_property(dev, attr, prop_buf);
265 if (ret == -ENODEV) {
266 /* When a battery is absent, we expect -ENODEV. Don't abort;
267 send the uevent with at least the the PRESENT=0 property */
268 ret = 0;
269 continue;
270 }
271
272 if (ret < 0)
273 goto out;
274
275 line = strchr(prop_buf, '\n');
276 if (line)
277 *line = 0;
278
279 attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
280 if (!attrname) {
281 ret = -ENOMEM;
282 goto out;
283 }
284
285 dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf);
286
287 ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
288 &length, "POWER_SUPPLY_%s=%s",
289 attrname, prop_buf);
290 kfree(attrname);
291 if (ret)
292 goto out;
293 }
294
295out:
296 free_page((unsigned long)prop_buf);
297
298 return ret;
299}
diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index 904e5aeb696c..df95d6c2cefa 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -35,4 +35,17 @@ config W1_SLAVE_DS2433_CRC
35 Each block has 30 bytes of data and a two byte CRC16. 35 Each block has 30 bytes of data and a two byte CRC16.
36 Full block writes are only allowed if the CRC is valid. 36 Full block writes are only allowed if the CRC is valid.
37 37
38config W1_SLAVE_DS2760
39 tristate "Dallas 2760 battery monitor chip (HP iPAQ & others)"
40 depends on W1
41 help
42 If you enable this you will have the DS2760 battery monitor
43 chip support.
44
45 The battery monitor chip is used in many batteries/devices
46 as the one who is responsible for charging/discharging/monitoring
47 Li+ batteries.
48
49 If you are unsure, say N.
50
38endmenu 51endmenu
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 725dcfdfddb4..a8eb7524df1d 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -5,4 +5,5 @@
5obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o 5obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o
6obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o 6obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o
7obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o 7obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o
8obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o
8 9
diff --git a/drivers/w1/slaves/w1_ds2760.c b/drivers/w1/slaves/w1_ds2760.c
new file mode 100644
index 000000000000..88a37fbccc3f
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.c
@@ -0,0 +1,213 @@
1/*
2 * 1-Wire implementation for the ds2760 chip
3 *
4 * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
5 *
6 * Use consistent with the GNU GPL is permitted,
7 * provided that this copyright notice is
8 * preserved in its entirety in all copies and derived works.
9 *
10 */
11
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/device.h>
15#include <linux/types.h>
16#include <linux/platform_device.h>
17#include <linux/mutex.h>
18#include <linux/idr.h>
19
20#include "../w1.h"
21#include "../w1_int.h"
22#include "../w1_family.h"
23#include "w1_ds2760.h"
24
25static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count,
26 int io)
27{
28 struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
29
30 if (!dev)
31 return 0;
32
33 mutex_lock(&sl->master->mutex);
34
35 if (addr > DS2760_DATA_SIZE || addr < 0) {
36 count = 0;
37 goto out;
38 }
39 if (addr + count > DS2760_DATA_SIZE)
40 count = DS2760_DATA_SIZE - addr;
41
42 if (!w1_reset_select_slave(sl)) {
43 if (!io) {
44 w1_write_8(sl->master, W1_DS2760_READ_DATA);
45 w1_write_8(sl->master, addr);
46 count = w1_read_block(sl->master, buf, count);
47 } else {
48 w1_write_8(sl->master, W1_DS2760_WRITE_DATA);
49 w1_write_8(sl->master, addr);
50 w1_write_block(sl->master, buf, count);
51 /* XXX w1_write_block returns void, not n_written */
52 }
53 }
54
55out:
56 mutex_unlock(&sl->master->mutex);
57
58 return count;
59}
60
61int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count)
62{
63 return w1_ds2760_io(dev, buf, addr, count, 0);
64}
65
66int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count)
67{
68 return w1_ds2760_io(dev, buf, addr, count, 1);
69}
70
71static ssize_t w1_ds2760_read_bin(struct kobject *kobj, char *buf, loff_t off,
72 size_t count)
73{
74 struct device *dev = container_of(kobj, struct device, kobj);
75 return w1_ds2760_read(dev, buf, off, count);
76}
77
78static struct bin_attribute w1_ds2760_bin_attr = {
79 .attr = {
80 .name = "w1_slave",
81 .mode = S_IRUGO,
82 .owner = THIS_MODULE,
83 },
84 .size = DS2760_DATA_SIZE,
85 .read = w1_ds2760_read_bin,
86};
87
88static DEFINE_IDR(bat_idr);
89static DEFINE_MUTEX(bat_idr_lock);
90
91static int new_bat_id(void)
92{
93 int ret;
94
95 while (1) {
96 int id;
97
98 ret = idr_pre_get(&bat_idr, GFP_KERNEL);
99 if (ret == 0)
100 return -ENOMEM;
101
102 mutex_lock(&bat_idr_lock);
103 ret = idr_get_new(&bat_idr, NULL, &id);
104 mutex_unlock(&bat_idr_lock);
105
106 if (ret == 0) {
107 ret = id & MAX_ID_MASK;
108 break;
109 } else if (ret == -EAGAIN) {
110 continue;
111 } else {
112 break;
113 }
114 }
115
116 return ret;
117}
118
119static void release_bat_id(int id)
120{
121 mutex_lock(&bat_idr_lock);
122 idr_remove(&bat_idr, id);
123 mutex_unlock(&bat_idr_lock);
124
125 return;
126}
127
128static int w1_ds2760_add_slave(struct w1_slave *sl)
129{
130 int ret;
131 int id;
132 struct platform_device *pdev;
133
134 id = new_bat_id();
135 if (id < 0) {
136 ret = id;
137 goto noid;
138 }
139
140 pdev = platform_device_alloc("ds2760-battery", id);
141 if (!pdev) {
142 ret = -ENOMEM;
143 goto pdev_alloc_failed;
144 }
145 pdev->dev.parent = &sl->dev;
146
147 ret = platform_device_add(pdev);
148 if (ret)
149 goto pdev_add_failed;
150
151 ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
152 if (ret)
153 goto bin_attr_failed;
154
155 dev_set_drvdata(&sl->dev, pdev);
156
157 goto success;
158
159bin_attr_failed:
160pdev_add_failed:
161 platform_device_unregister(pdev);
162pdev_alloc_failed:
163 release_bat_id(id);
164noid:
165success:
166 return ret;
167}
168
169static void w1_ds2760_remove_slave(struct w1_slave *sl)
170{
171 struct platform_device *pdev = dev_get_drvdata(&sl->dev);
172 int id = pdev->id;
173
174 platform_device_unregister(pdev);
175 release_bat_id(id);
176 sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr);
177
178 return;
179}
180
181static struct w1_family_ops w1_ds2760_fops = {
182 .add_slave = w1_ds2760_add_slave,
183 .remove_slave = w1_ds2760_remove_slave,
184};
185
186static struct w1_family w1_ds2760_family = {
187 .fid = W1_FAMILY_DS2760,
188 .fops = &w1_ds2760_fops,
189};
190
191static int __init w1_ds2760_init(void)
192{
193 printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor "
194 " chip - (c) 2004-2005, Szabolcs Gyurko\n");
195 idr_init(&bat_idr);
196 return w1_register_family(&w1_ds2760_family);
197}
198
199static void __exit w1_ds2760_exit(void)
200{
201 w1_unregister_family(&w1_ds2760_family);
202 idr_destroy(&bat_idr);
203}
204
205EXPORT_SYMBOL(w1_ds2760_read);
206EXPORT_SYMBOL(w1_ds2760_write);
207
208module_init(w1_ds2760_init);
209module_exit(w1_ds2760_exit);
210
211MODULE_LICENSE("GPL");
212MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>");
213MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip");
diff --git a/drivers/w1/slaves/w1_ds2760.h b/drivers/w1/slaves/w1_ds2760.h
new file mode 100644
index 000000000000..f1302429cb02
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds2760.h
@@ -0,0 +1,50 @@
1/*
2 * 1-Wire implementation for the ds2760 chip
3 *
4 * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
5 *
6 * Use consistent with the GNU GPL is permitted,
7 * provided that this copyright notice is
8 * preserved in its entirety in all copies and derived works.
9 *
10 */
11
12#ifndef __w1_ds2760_h__
13#define __w1_ds2760_h__
14
15/* Known commands to the DS2760 chip */
16#define W1_DS2760_SWAP 0xAA
17#define W1_DS2760_READ_DATA 0x69
18#define W1_DS2760_WRITE_DATA 0x6C
19#define W1_DS2760_COPY_DATA 0x48
20#define W1_DS2760_RECALL_DATA 0xB8
21#define W1_DS2760_LOCK 0x6A
22
23/* Number of valid register addresses */
24#define DS2760_DATA_SIZE 0x40
25
26#define DS2760_PROTECTION_REG 0x00
27#define DS2760_STATUS_REG 0x01
28#define DS2760_EEPROM_REG 0x07
29#define DS2760_SPECIAL_FEATURE_REG 0x08
30#define DS2760_VOLTAGE_MSB 0x0c
31#define DS2760_VOLTAGE_LSB 0x0d
32#define DS2760_CURRENT_MSB 0x0e
33#define DS2760_CURRENT_LSB 0x0f
34#define DS2760_CURRENT_ACCUM_MSB 0x10
35#define DS2760_CURRENT_ACCUM_LSB 0x11
36#define DS2760_TEMP_MSB 0x18
37#define DS2760_TEMP_LSB 0x19
38#define DS2760_EEPROM_BLOCK0 0x20
39#define DS2760_ACTIVE_FULL 0x20
40#define DS2760_EEPROM_BLOCK1 0x30
41#define DS2760_RATED_CAPACITY 0x32
42#define DS2760_CURRENT_OFFSET_BIAS 0x33
43#define DS2760_ACTIVE_EMPTY 0x3b
44
45extern int w1_ds2760_read(struct device *dev, char *buf, int addr,
46 size_t count);
47extern int w1_ds2760_write(struct device *dev, char *buf, int addr,
48 size_t count);
49
50#endif /* !__w1_ds2760_h__ */
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 1e2ac40c2c14..ef1e1dafa19a 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -33,6 +33,7 @@
33#define W1_THERM_DS1822 0x22 33#define W1_THERM_DS1822 0x22
34#define W1_EEPROM_DS2433 0x23 34#define W1_EEPROM_DS2433 0x23
35#define W1_THERM_DS18B20 0x28 35#define W1_THERM_DS18B20 0x28
36#define W1_FAMILY_DS2760 0x30
36 37
37#define MAXNAMELEN 32 38#define MAXNAMELEN 32
38 39
diff --git a/include/linux/pda_power.h b/include/linux/pda_power.h
new file mode 100644
index 000000000000..1375f15797e7
--- /dev/null
+++ b/include/linux/pda_power.h
@@ -0,0 +1,31 @@
1/*
2 * Common power driver for PDAs and phones with one or two external
3 * power supplies (AC/USB) connected to main and backup batteries,
4 * and optional builtin charger.
5 *
6 * Copyright © 2007 Anton Vorontsov <cbou@mail.ru>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#ifndef __PDA_POWER_H__
14#define __PDA_POWER_H__
15
16#define PDA_POWER_CHARGE_AC (1 << 0)
17#define PDA_POWER_CHARGE_USB (1 << 1)
18
19struct pda_power_pdata {
20 int (*is_ac_online)(void);
21 int (*is_usb_online)(void);
22 void (*set_charge)(int flags);
23
24 char **supplied_to;
25 size_t num_supplicants;
26
27 unsigned int wait_for_status; /* msecs, default is 500 */
28 unsigned int wait_for_charger; /* msecs, default is 500 */
29};
30
31#endif /* __PDA_POWER_H__ */
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
new file mode 100644
index 000000000000..606c0957997f
--- /dev/null
+++ b/include/linux/power_supply.h
@@ -0,0 +1,180 @@
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#ifndef __LINUX_POWER_SUPPLY_H__
14#define __LINUX_POWER_SUPPLY_H__
15
16#include <linux/device.h>
17#include <linux/workqueue.h>
18#include <linux/leds.h>
19
20/*
21 * All voltages, currents, charges, energies, time and temperatures in uV,
22 * µA, µAh, µWh, seconds and tenths of degree Celsius unless otherwise
23 * stated. It's driver's job to convert its raw values to units in which
24 * this class operates.
25 */
26
27/*
28 * For systems where the charger determines the maximum battery capacity
29 * the min and max fields should be used to present these values to user
30 * space. Unused/unknown fields will not appear in sysfs.
31 */
32
33enum {
34 POWER_SUPPLY_STATUS_UNKNOWN = 0,
35 POWER_SUPPLY_STATUS_CHARGING,
36 POWER_SUPPLY_STATUS_DISCHARGING,
37 POWER_SUPPLY_STATUS_NOT_CHARGING,
38 POWER_SUPPLY_STATUS_FULL,
39};
40
41enum {
42 POWER_SUPPLY_HEALTH_UNKNOWN = 0,
43 POWER_SUPPLY_HEALTH_GOOD,
44 POWER_SUPPLY_HEALTH_OVERHEAT,
45 POWER_SUPPLY_HEALTH_DEAD,
46 POWER_SUPPLY_HEALTH_OVERVOLTAGE,
47 POWER_SUPPLY_HEALTH_UNSPEC_FAILURE,
48};
49
50enum {
51 POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
52 POWER_SUPPLY_TECHNOLOGY_NiMH,
53 POWER_SUPPLY_TECHNOLOGY_LION,
54 POWER_SUPPLY_TECHNOLOGY_LIPO,
55 POWER_SUPPLY_TECHNOLOGY_LiFe,
56 POWER_SUPPLY_TECHNOLOGY_NiCd,
57};
58
59enum {
60 POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
61 POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
62 POWER_SUPPLY_CAPACITY_LEVEL_LOW,
63 POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
64 POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
65 POWER_SUPPLY_CAPACITY_LEVEL_FULL,
66};
67
68enum power_supply_property {
69 /* Properties of type `int' */
70 POWER_SUPPLY_PROP_STATUS = 0,
71 POWER_SUPPLY_PROP_HEALTH,
72 POWER_SUPPLY_PROP_PRESENT,
73 POWER_SUPPLY_PROP_ONLINE,
74 POWER_SUPPLY_PROP_TECHNOLOGY,
75 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
76 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
77 POWER_SUPPLY_PROP_VOLTAGE_NOW,
78 POWER_SUPPLY_PROP_VOLTAGE_AVG,
79 POWER_SUPPLY_PROP_CURRENT_NOW,
80 POWER_SUPPLY_PROP_CURRENT_AVG,
81 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
82 POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
83 POWER_SUPPLY_PROP_CHARGE_FULL,
84 POWER_SUPPLY_PROP_CHARGE_EMPTY,
85 POWER_SUPPLY_PROP_CHARGE_NOW,
86 POWER_SUPPLY_PROP_CHARGE_AVG,
87 POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
88 POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
89 POWER_SUPPLY_PROP_ENERGY_FULL,
90 POWER_SUPPLY_PROP_ENERGY_EMPTY,
91 POWER_SUPPLY_PROP_ENERGY_NOW,
92 POWER_SUPPLY_PROP_ENERGY_AVG,
93 POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
94 POWER_SUPPLY_PROP_CAPACITY_LEVEL,
95 POWER_SUPPLY_PROP_TEMP,
96 POWER_SUPPLY_PROP_TEMP_AMBIENT,
97 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
98 POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
99 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
100 POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
101 /* Properties of type `const char *' */
102 POWER_SUPPLY_PROP_MODEL_NAME,
103 POWER_SUPPLY_PROP_MANUFACTURER,
104};
105
106enum power_supply_type {
107 POWER_SUPPLY_TYPE_BATTERY = 0,
108 POWER_SUPPLY_TYPE_UPS,
109 POWER_SUPPLY_TYPE_MAINS,
110 POWER_SUPPLY_TYPE_USB,
111};
112
113union power_supply_propval {
114 int intval;
115 const char *strval;
116};
117
118struct power_supply {
119 const char *name;
120 enum power_supply_type type;
121 enum power_supply_property *properties;
122 size_t num_properties;
123
124 char **supplied_to;
125 size_t num_supplicants;
126
127 int (*get_property)(struct power_supply *psy,
128 enum power_supply_property psp,
129 union power_supply_propval *val);
130 void (*external_power_changed)(struct power_supply *psy);
131
132 /* For APM emulation, think legacy userspace. */
133 int use_for_apm;
134
135 /* private */
136 struct device *dev;
137 struct work_struct changed_work;
138
139#ifdef CONFIG_LEDS_TRIGGERS
140 struct led_trigger *charging_full_trig;
141 char *charging_full_trig_name;
142 struct led_trigger *charging_trig;
143 char *charging_trig_name;
144 struct led_trigger *full_trig;
145 char *full_trig_name;
146 struct led_trigger *online_trig;
147 char *online_trig_name;
148#endif
149};
150
151/*
152 * This is recommended structure to specify static power supply parameters.
153 * Generic one, parametrizable for different power supplies. Power supply
154 * class itself does not use it, but that's what implementing most platform
155 * drivers, should try reuse for consistency.
156 */
157
158struct power_supply_info {
159 const char *name;
160 int technology;
161 int voltage_max_design;
162 int voltage_min_design;
163 int charge_full_design;
164 int charge_empty_design;
165 int energy_full_design;
166 int energy_empty_design;
167 int use_for_apm;
168};
169
170extern void power_supply_changed(struct power_supply *psy);
171extern int power_supply_am_i_supplied(struct power_supply *psy);
172
173extern int power_supply_register(struct device *parent,
174 struct power_supply *psy);
175extern void power_supply_unregister(struct power_supply *psy);
176
177/* For APM emulation, think legacy userspace. */
178extern struct class *power_supply_class;
179
180#endif /* __LINUX_POWER_SUPPLY_H__ */