Commit 7b3d54a8c30d2c524889a05d0c1334813d516b93
1 parent
5ebf6e6a96
Exists in
master
and in
7 other branches
Power supply class and drivers: remove non obligatory return statements
Per Jeff Garzik request. Signed-off-by: Jeff Garzik <jeff@garzik.org> Signed-off-by: Anton Vorontsov <cbou@mail.ru>
Showing 9 changed files with 0 additions and 39 deletions Inline Diff
drivers/power/apm_power.c
1 | /* | 1 | /* |
2 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | 2 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> |
3 | * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru> | 3 | * Copyright © 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru> |
4 | * | 4 | * |
5 | * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru> | 5 | * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru> |
6 | * | 6 | * |
7 | * Use consistent with the GNU GPL is permitted, | 7 | * Use consistent with the GNU GPL is permitted, |
8 | * provided that this copyright notice is | 8 | * provided that this copyright notice is |
9 | * preserved in its entirety in all copies and derived works. | 9 | * preserved in its entirety in all copies and derived works. |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
13 | #include <linux/power_supply.h> | 13 | #include <linux/power_supply.h> |
14 | #include <linux/apm-emulation.h> | 14 | #include <linux/apm-emulation.h> |
15 | 15 | ||
16 | #define PSY_PROP(psy, prop, val) psy->get_property(psy, \ | 16 | #define PSY_PROP(psy, prop, val) psy->get_property(psy, \ |
17 | POWER_SUPPLY_PROP_##prop, val) | 17 | POWER_SUPPLY_PROP_##prop, val) |
18 | 18 | ||
19 | #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ | 19 | #define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \ |
20 | prop, val) | 20 | prop, val) |
21 | 21 | ||
22 | #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) | 22 | #define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val) |
23 | 23 | ||
24 | static struct power_supply *main_battery; | 24 | static struct power_supply *main_battery; |
25 | 25 | ||
26 | static void find_main_battery(void) | 26 | static void find_main_battery(void) |
27 | { | 27 | { |
28 | struct device *dev; | 28 | struct device *dev; |
29 | struct power_supply *bat, *batm; | 29 | struct power_supply *bat, *batm; |
30 | union power_supply_propval full; | 30 | union power_supply_propval full; |
31 | int max_charge = 0; | 31 | int max_charge = 0; |
32 | 32 | ||
33 | main_battery = NULL; | 33 | main_battery = NULL; |
34 | batm = NULL; | 34 | batm = NULL; |
35 | list_for_each_entry(dev, &power_supply_class->devices, node) { | 35 | list_for_each_entry(dev, &power_supply_class->devices, node) { |
36 | bat = dev_get_drvdata(dev); | 36 | bat = dev_get_drvdata(dev); |
37 | /* If none of battery devices cantains 'use_for_apm' flag, | 37 | /* If none of battery devices cantains 'use_for_apm' flag, |
38 | choice one with maximum design charge */ | 38 | choice one with maximum design charge */ |
39 | if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { | 39 | if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) { |
40 | if (full.intval > max_charge) { | 40 | if (full.intval > max_charge) { |
41 | batm = bat; | 41 | batm = bat; |
42 | max_charge = full.intval; | 42 | max_charge = full.intval; |
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | if (bat->use_for_apm) | 46 | if (bat->use_for_apm) |
47 | main_battery = bat; | 47 | main_battery = bat; |
48 | } | 48 | } |
49 | if (!main_battery) | 49 | if (!main_battery) |
50 | main_battery = batm; | 50 | main_battery = batm; |
51 | |||
52 | return; | ||
53 | } | 51 | } |
54 | 52 | ||
55 | static int calculate_time(int status) | 53 | static int calculate_time(int status) |
56 | { | 54 | { |
57 | union power_supply_propval charge_full, charge_empty; | 55 | union power_supply_propval charge_full, charge_empty; |
58 | union power_supply_propval charge, I; | 56 | union power_supply_propval charge, I; |
59 | 57 | ||
60 | if (MPSY_PROP(CHARGE_FULL, &charge_full)) { | 58 | if (MPSY_PROP(CHARGE_FULL, &charge_full)) { |
61 | /* if battery can't report this property, use design value */ | 59 | /* if battery can't report this property, use design value */ |
62 | if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) | 60 | if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full)) |
63 | return -1; | 61 | return -1; |
64 | } | 62 | } |
65 | 63 | ||
66 | if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { | 64 | if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) { |
67 | /* if battery can't report this property, use design value */ | 65 | /* if battery can't report this property, use design value */ |
68 | if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) | 66 | if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty)) |
69 | charge_empty.intval = 0; | 67 | charge_empty.intval = 0; |
70 | } | 68 | } |
71 | 69 | ||
72 | if (MPSY_PROP(CHARGE_AVG, &charge)) { | 70 | if (MPSY_PROP(CHARGE_AVG, &charge)) { |
73 | /* if battery can't report average value, use momentary */ | 71 | /* if battery can't report average value, use momentary */ |
74 | if (MPSY_PROP(CHARGE_NOW, &charge)) | 72 | if (MPSY_PROP(CHARGE_NOW, &charge)) |
75 | return -1; | 73 | return -1; |
76 | } | 74 | } |
77 | 75 | ||
78 | if (MPSY_PROP(CURRENT_AVG, &I)) { | 76 | if (MPSY_PROP(CURRENT_AVG, &I)) { |
79 | /* if battery can't report average value, use momentary */ | 77 | /* if battery can't report average value, use momentary */ |
80 | if (MPSY_PROP(CURRENT_NOW, &I)) | 78 | if (MPSY_PROP(CURRENT_NOW, &I)) |
81 | return -1; | 79 | return -1; |
82 | } | 80 | } |
83 | 81 | ||
84 | if (status == POWER_SUPPLY_STATUS_CHARGING) | 82 | if (status == POWER_SUPPLY_STATUS_CHARGING) |
85 | return ((charge.intval - charge_full.intval) * 60L) / | 83 | return ((charge.intval - charge_full.intval) * 60L) / |
86 | I.intval; | 84 | I.intval; |
87 | else | 85 | else |
88 | return -((charge.intval - charge_empty.intval) * 60L) / | 86 | return -((charge.intval - charge_empty.intval) * 60L) / |
89 | I.intval; | 87 | I.intval; |
90 | } | 88 | } |
91 | 89 | ||
92 | static int calculate_capacity(int using_charge) | 90 | static int calculate_capacity(int using_charge) |
93 | { | 91 | { |
94 | enum power_supply_property full_prop, empty_prop; | 92 | enum power_supply_property full_prop, empty_prop; |
95 | enum power_supply_property full_design_prop, empty_design_prop; | 93 | enum power_supply_property full_design_prop, empty_design_prop; |
96 | enum power_supply_property now_prop, avg_prop; | 94 | enum power_supply_property now_prop, avg_prop; |
97 | union power_supply_propval empty, full, cur; | 95 | union power_supply_propval empty, full, cur; |
98 | int ret; | 96 | int ret; |
99 | 97 | ||
100 | if (using_charge) { | 98 | if (using_charge) { |
101 | full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; | 99 | full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; |
102 | empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; | 100 | empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; |
103 | full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; | 101 | full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; |
104 | empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; | 102 | empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; |
105 | now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; | 103 | now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; |
106 | avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; | 104 | avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; |
107 | } else { | 105 | } else { |
108 | full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; | 106 | full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; |
109 | empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; | 107 | empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; |
110 | full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; | 108 | full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; |
111 | empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; | 109 | empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; |
112 | now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; | 110 | now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; |
113 | avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; | 111 | avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; |
114 | } | 112 | } |
115 | 113 | ||
116 | if (_MPSY_PROP(full_prop, &full)) { | 114 | if (_MPSY_PROP(full_prop, &full)) { |
117 | /* if battery can't report this property, use design value */ | 115 | /* if battery can't report this property, use design value */ |
118 | if (_MPSY_PROP(full_design_prop, &full)) | 116 | if (_MPSY_PROP(full_design_prop, &full)) |
119 | return -1; | 117 | return -1; |
120 | } | 118 | } |
121 | 119 | ||
122 | if (_MPSY_PROP(avg_prop, &cur)) { | 120 | if (_MPSY_PROP(avg_prop, &cur)) { |
123 | /* if battery can't report average value, use momentary */ | 121 | /* if battery can't report average value, use momentary */ |
124 | if (_MPSY_PROP(now_prop, &cur)) | 122 | if (_MPSY_PROP(now_prop, &cur)) |
125 | return -1; | 123 | return -1; |
126 | } | 124 | } |
127 | 125 | ||
128 | if (_MPSY_PROP(empty_prop, &empty)) { | 126 | if (_MPSY_PROP(empty_prop, &empty)) { |
129 | /* if battery can't report this property, use design value */ | 127 | /* if battery can't report this property, use design value */ |
130 | if (_MPSY_PROP(empty_design_prop, &empty)) | 128 | if (_MPSY_PROP(empty_design_prop, &empty)) |
131 | empty.intval = 0; | 129 | empty.intval = 0; |
132 | } | 130 | } |
133 | 131 | ||
134 | if (full.intval - empty.intval) | 132 | if (full.intval - empty.intval) |
135 | ret = ((cur.intval - empty.intval) * 100L) / | 133 | ret = ((cur.intval - empty.intval) * 100L) / |
136 | (full.intval - empty.intval); | 134 | (full.intval - empty.intval); |
137 | else | 135 | else |
138 | return -1; | 136 | return -1; |
139 | 137 | ||
140 | if (ret > 100) | 138 | if (ret > 100) |
141 | return 100; | 139 | return 100; |
142 | else if (ret < 0) | 140 | else if (ret < 0) |
143 | return 0; | 141 | return 0; |
144 | 142 | ||
145 | return ret; | 143 | return ret; |
146 | } | 144 | } |
147 | 145 | ||
148 | static void apm_battery_apm_get_power_status(struct apm_power_info *info) | 146 | static void apm_battery_apm_get_power_status(struct apm_power_info *info) |
149 | { | 147 | { |
150 | union power_supply_propval status; | 148 | union power_supply_propval status; |
151 | union power_supply_propval capacity, time_to_full, time_to_empty; | 149 | union power_supply_propval capacity, time_to_full, time_to_empty; |
152 | 150 | ||
153 | down(&power_supply_class->sem); | 151 | down(&power_supply_class->sem); |
154 | find_main_battery(); | 152 | find_main_battery(); |
155 | if (!main_battery) { | 153 | if (!main_battery) { |
156 | up(&power_supply_class->sem); | 154 | up(&power_supply_class->sem); |
157 | return; | 155 | return; |
158 | } | 156 | } |
159 | 157 | ||
160 | /* status */ | 158 | /* status */ |
161 | 159 | ||
162 | if (MPSY_PROP(STATUS, &status)) | 160 | if (MPSY_PROP(STATUS, &status)) |
163 | status.intval = POWER_SUPPLY_STATUS_UNKNOWN; | 161 | status.intval = POWER_SUPPLY_STATUS_UNKNOWN; |
164 | 162 | ||
165 | /* ac line status */ | 163 | /* ac line status */ |
166 | 164 | ||
167 | if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) || | 165 | if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) || |
168 | (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) || | 166 | (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) || |
169 | (status.intval == POWER_SUPPLY_STATUS_FULL)) | 167 | (status.intval == POWER_SUPPLY_STATUS_FULL)) |
170 | info->ac_line_status = APM_AC_ONLINE; | 168 | info->ac_line_status = APM_AC_ONLINE; |
171 | else | 169 | else |
172 | info->ac_line_status = APM_AC_OFFLINE; | 170 | info->ac_line_status = APM_AC_OFFLINE; |
173 | 171 | ||
174 | /* battery life (i.e. capacity, in percents) */ | 172 | /* battery life (i.e. capacity, in percents) */ |
175 | 173 | ||
176 | if (MPSY_PROP(CAPACITY, &capacity) == 0) { | 174 | if (MPSY_PROP(CAPACITY, &capacity) == 0) { |
177 | info->battery_life = capacity.intval; | 175 | info->battery_life = capacity.intval; |
178 | } else { | 176 | } else { |
179 | /* try calculate using energy */ | 177 | /* try calculate using energy */ |
180 | info->battery_life = calculate_capacity(0); | 178 | info->battery_life = calculate_capacity(0); |
181 | /* if failed try calculate using charge instead */ | 179 | /* if failed try calculate using charge instead */ |
182 | if (info->battery_life == -1) | 180 | if (info->battery_life == -1) |
183 | info->battery_life = calculate_capacity(1); | 181 | info->battery_life = calculate_capacity(1); |
184 | } | 182 | } |
185 | 183 | ||
186 | /* charging status */ | 184 | /* charging status */ |
187 | 185 | ||
188 | if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { | 186 | if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { |
189 | info->battery_status = APM_BATTERY_STATUS_CHARGING; | 187 | info->battery_status = APM_BATTERY_STATUS_CHARGING; |
190 | } else { | 188 | } else { |
191 | if (info->battery_life > 50) | 189 | if (info->battery_life > 50) |
192 | info->battery_status = APM_BATTERY_STATUS_HIGH; | 190 | info->battery_status = APM_BATTERY_STATUS_HIGH; |
193 | else if (info->battery_life > 5) | 191 | else if (info->battery_life > 5) |
194 | info->battery_status = APM_BATTERY_STATUS_LOW; | 192 | info->battery_status = APM_BATTERY_STATUS_LOW; |
195 | else | 193 | else |
196 | info->battery_status = APM_BATTERY_STATUS_CRITICAL; | 194 | info->battery_status = APM_BATTERY_STATUS_CRITICAL; |
197 | } | 195 | } |
198 | info->battery_flag = info->battery_status; | 196 | info->battery_flag = info->battery_status; |
199 | 197 | ||
200 | /* time */ | 198 | /* time */ |
201 | 199 | ||
202 | info->units = APM_UNITS_MINS; | 200 | info->units = APM_UNITS_MINS; |
203 | 201 | ||
204 | if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { | 202 | if (status.intval == POWER_SUPPLY_STATUS_CHARGING) { |
205 | if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { | 203 | if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) { |
206 | if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) | 204 | if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) |
207 | info->time = calculate_time(status.intval); | 205 | info->time = calculate_time(status.intval); |
208 | else | 206 | else |
209 | info->time = time_to_full.intval / 60; | 207 | info->time = time_to_full.intval / 60; |
210 | } | 208 | } |
211 | } else { | 209 | } else { |
212 | if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { | 210 | if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) { |
213 | if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) | 211 | if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) |
214 | info->time = calculate_time(status.intval); | 212 | info->time = calculate_time(status.intval); |
215 | else | 213 | else |
216 | info->time = time_to_empty.intval / 60; | 214 | info->time = time_to_empty.intval / 60; |
217 | } | 215 | } |
218 | } | 216 | } |
219 | 217 | ||
220 | up(&power_supply_class->sem); | 218 | up(&power_supply_class->sem); |
221 | return; | ||
222 | } | 219 | } |
223 | 220 | ||
224 | static int __init apm_battery_init(void) | 221 | static int __init apm_battery_init(void) |
225 | { | 222 | { |
226 | printk(KERN_INFO "APM Battery Driver\n"); | 223 | printk(KERN_INFO "APM Battery Driver\n"); |
227 | 224 | ||
228 | apm_get_power_status = apm_battery_apm_get_power_status; | 225 | apm_get_power_status = apm_battery_apm_get_power_status; |
229 | return 0; | 226 | return 0; |
230 | } | 227 | } |
231 | 228 | ||
232 | static void __exit apm_battery_exit(void) | 229 | static void __exit apm_battery_exit(void) |
233 | { | 230 | { |
234 | apm_get_power_status = NULL; | 231 | apm_get_power_status = NULL; |
235 | return; | ||
236 | } | 232 | } |
237 | 233 | ||
238 | module_init(apm_battery_init); | 234 | module_init(apm_battery_init); |
239 | module_exit(apm_battery_exit); | 235 | module_exit(apm_battery_exit); |
240 | 236 | ||
241 | MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); | 237 | MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>"); |
242 | MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); | 238 | MODULE_DESCRIPTION("APM emulation driver for battery monitoring class"); |
243 | MODULE_LICENSE("GPL"); | 239 | MODULE_LICENSE("GPL"); |
244 | 240 |
drivers/power/ds2760_battery.c
1 | /* | 1 | /* |
2 | * Driver for batteries with DS2760 chips inside. | 2 | * Driver for batteries with DS2760 chips inside. |
3 | * | 3 | * |
4 | * Copyright © 2007 Anton Vorontsov | 4 | * Copyright © 2007 Anton Vorontsov |
5 | * 2004-2007 Matt Reimer | 5 | * 2004-2007 Matt Reimer |
6 | * 2004 Szabolcs Gyurko | 6 | * 2004 Szabolcs Gyurko |
7 | * | 7 | * |
8 | * Use consistent with the GNU GPL is permitted, | 8 | * Use consistent with the GNU GPL is permitted, |
9 | * provided that this copyright notice is | 9 | * provided that this copyright notice is |
10 | * preserved in its entirety in all copies and derived works. | 10 | * preserved in its entirety in all copies and derived works. |
11 | * | 11 | * |
12 | * Author: Anton Vorontsov <cbou@mail.ru> | 12 | * Author: Anton Vorontsov <cbou@mail.ru> |
13 | * February 2007 | 13 | * February 2007 |
14 | * | 14 | * |
15 | * Matt Reimer <mreimer@vpop.net> | 15 | * Matt Reimer <mreimer@vpop.net> |
16 | * April 2004, 2005, 2007 | 16 | * April 2004, 2005, 2007 |
17 | * | 17 | * |
18 | * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> | 18 | * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> |
19 | * September 2004 | 19 | * September 2004 |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #include <linux/module.h> | 22 | #include <linux/module.h> |
23 | #include <linux/param.h> | 23 | #include <linux/param.h> |
24 | #include <linux/jiffies.h> | 24 | #include <linux/jiffies.h> |
25 | #include <linux/workqueue.h> | 25 | #include <linux/workqueue.h> |
26 | #include <linux/pm.h> | 26 | #include <linux/pm.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/power_supply.h> | 28 | #include <linux/power_supply.h> |
29 | 29 | ||
30 | #include "../w1/w1.h" | 30 | #include "../w1/w1.h" |
31 | #include "../w1/slaves/w1_ds2760.h" | 31 | #include "../w1/slaves/w1_ds2760.h" |
32 | 32 | ||
33 | struct ds2760_device_info { | 33 | struct ds2760_device_info { |
34 | struct device *dev; | 34 | struct device *dev; |
35 | 35 | ||
36 | /* DS2760 data, valid after calling ds2760_battery_read_status() */ | 36 | /* DS2760 data, valid after calling ds2760_battery_read_status() */ |
37 | unsigned long update_time; /* jiffies when data read */ | 37 | unsigned long update_time; /* jiffies when data read */ |
38 | char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ | 38 | char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */ |
39 | int voltage_raw; /* units of 4.88 mV */ | 39 | int voltage_raw; /* units of 4.88 mV */ |
40 | int voltage_uV; /* units of µV */ | 40 | int voltage_uV; /* units of µV */ |
41 | int current_raw; /* units of 0.625 mA */ | 41 | int current_raw; /* units of 0.625 mA */ |
42 | int current_uA; /* units of µA */ | 42 | int current_uA; /* units of µA */ |
43 | int accum_current_raw; /* units of 0.25 mAh */ | 43 | int accum_current_raw; /* units of 0.25 mAh */ |
44 | int accum_current_uAh; /* units of µAh */ | 44 | int accum_current_uAh; /* units of µAh */ |
45 | int temp_raw; /* units of 0.125 °C */ | 45 | int temp_raw; /* units of 0.125 °C */ |
46 | int temp_C; /* units of 0.1 °C */ | 46 | int temp_C; /* units of 0.1 °C */ |
47 | int rated_capacity; /* units of µAh */ | 47 | int rated_capacity; /* units of µAh */ |
48 | int rem_capacity; /* percentage */ | 48 | int rem_capacity; /* percentage */ |
49 | int full_active_uAh; /* units of µAh */ | 49 | int full_active_uAh; /* units of µAh */ |
50 | int empty_uAh; /* units of µAh */ | 50 | int empty_uAh; /* units of µAh */ |
51 | int life_sec; /* units of seconds */ | 51 | int life_sec; /* units of seconds */ |
52 | int charge_status; /* POWER_SUPPLY_STATUS_* */ | 52 | int charge_status; /* POWER_SUPPLY_STATUS_* */ |
53 | 53 | ||
54 | int full_counter; | 54 | int full_counter; |
55 | struct power_supply bat; | 55 | struct power_supply bat; |
56 | struct device *w1_dev; | 56 | struct device *w1_dev; |
57 | struct workqueue_struct *monitor_wqueue; | 57 | struct workqueue_struct *monitor_wqueue; |
58 | struct delayed_work monitor_work; | 58 | struct delayed_work monitor_work; |
59 | }; | 59 | }; |
60 | 60 | ||
61 | static unsigned int cache_time = 1000; | 61 | static unsigned int cache_time = 1000; |
62 | module_param(cache_time, uint, 0644); | 62 | module_param(cache_time, uint, 0644); |
63 | MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); | 63 | MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); |
64 | 64 | ||
65 | /* Some batteries have their rated capacity stored a N * 10 mAh, while | 65 | /* Some batteries have their rated capacity stored a N * 10 mAh, while |
66 | * others use an index into this table. */ | 66 | * others use an index into this table. */ |
67 | static int rated_capacities[] = { | 67 | static int rated_capacities[] = { |
68 | 0, | 68 | 0, |
69 | 920, /* Samsung */ | 69 | 920, /* Samsung */ |
70 | 920, /* BYD */ | 70 | 920, /* BYD */ |
71 | 920, /* Lishen */ | 71 | 920, /* Lishen */ |
72 | 920, /* NEC */ | 72 | 920, /* NEC */ |
73 | 1440, /* Samsung */ | 73 | 1440, /* Samsung */ |
74 | 1440, /* BYD */ | 74 | 1440, /* BYD */ |
75 | 1440, /* Lishen */ | 75 | 1440, /* Lishen */ |
76 | 1440, /* NEC */ | 76 | 1440, /* NEC */ |
77 | 2880, /* Samsung */ | 77 | 2880, /* Samsung */ |
78 | 2880, /* BYD */ | 78 | 2880, /* BYD */ |
79 | 2880, /* Lishen */ | 79 | 2880, /* Lishen */ |
80 | 2880 /* NEC */ | 80 | 2880 /* NEC */ |
81 | }; | 81 | }; |
82 | 82 | ||
83 | /* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C | 83 | /* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C |
84 | * temp is in Celsius */ | 84 | * temp is in Celsius */ |
85 | static int battery_interpolate(int array[], int temp) | 85 | static int battery_interpolate(int array[], int temp) |
86 | { | 86 | { |
87 | int index, dt; | 87 | int index, dt; |
88 | 88 | ||
89 | if (temp <= 0) | 89 | if (temp <= 0) |
90 | return array[0]; | 90 | return array[0]; |
91 | if (temp >= 40) | 91 | if (temp >= 40) |
92 | return array[4]; | 92 | return array[4]; |
93 | 93 | ||
94 | index = temp / 10; | 94 | index = temp / 10; |
95 | dt = temp % 10; | 95 | dt = temp % 10; |
96 | 96 | ||
97 | return array[index] + (((array[index + 1] - array[index]) * dt) / 10); | 97 | return array[index] + (((array[index + 1] - array[index]) * dt) / 10); |
98 | } | 98 | } |
99 | 99 | ||
100 | static int ds2760_battery_read_status(struct ds2760_device_info *di) | 100 | static int ds2760_battery_read_status(struct ds2760_device_info *di) |
101 | { | 101 | { |
102 | int ret, i, start, count, scale[5]; | 102 | int ret, i, start, count, scale[5]; |
103 | 103 | ||
104 | if (di->update_time && time_before(jiffies, di->update_time + | 104 | if (di->update_time && time_before(jiffies, di->update_time + |
105 | msecs_to_jiffies(cache_time))) | 105 | msecs_to_jiffies(cache_time))) |
106 | return 0; | 106 | return 0; |
107 | 107 | ||
108 | /* The first time we read the entire contents of SRAM/EEPROM, | 108 | /* The first time we read the entire contents of SRAM/EEPROM, |
109 | * but after that we just read the interesting bits that change. */ | 109 | * but after that we just read the interesting bits that change. */ |
110 | if (di->update_time == 0) { | 110 | if (di->update_time == 0) { |
111 | start = 0; | 111 | start = 0; |
112 | count = DS2760_DATA_SIZE; | 112 | count = DS2760_DATA_SIZE; |
113 | } else { | 113 | } else { |
114 | start = DS2760_VOLTAGE_MSB; | 114 | start = DS2760_VOLTAGE_MSB; |
115 | count = DS2760_TEMP_LSB - start + 1; | 115 | count = DS2760_TEMP_LSB - start + 1; |
116 | } | 116 | } |
117 | 117 | ||
118 | ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); | 118 | ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count); |
119 | if (ret != count) { | 119 | if (ret != count) { |
120 | dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", | 120 | dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n", |
121 | di->w1_dev); | 121 | di->w1_dev); |
122 | return 1; | 122 | return 1; |
123 | } | 123 | } |
124 | 124 | ||
125 | di->update_time = jiffies; | 125 | di->update_time = jiffies; |
126 | 126 | ||
127 | /* DS2760 reports voltage in units of 4.88mV, but the battery class | 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. */ | 128 | * reports in units of uV, so convert by multiplying by 4880. */ |
129 | di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | | 129 | di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) | |
130 | (di->raw[DS2760_VOLTAGE_LSB] >> 5); | 130 | (di->raw[DS2760_VOLTAGE_LSB] >> 5); |
131 | di->voltage_uV = di->voltage_raw * 4880; | 131 | di->voltage_uV = di->voltage_raw * 4880; |
132 | 132 | ||
133 | /* DS2760 reports current in signed units of 0.625mA, but the battery | 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. */ | 134 | * class reports in units of µA, so convert by multiplying by 625. */ |
135 | di->current_raw = | 135 | di->current_raw = |
136 | (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | | 136 | (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) | |
137 | (di->raw[DS2760_CURRENT_LSB] >> 3); | 137 | (di->raw[DS2760_CURRENT_LSB] >> 3); |
138 | di->current_uA = di->current_raw * 625; | 138 | di->current_uA = di->current_raw * 625; |
139 | 139 | ||
140 | /* DS2760 reports accumulated current in signed units of 0.25mAh. */ | 140 | /* DS2760 reports accumulated current in signed units of 0.25mAh. */ |
141 | di->accum_current_raw = | 141 | di->accum_current_raw = |
142 | (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | | 142 | (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) | |
143 | di->raw[DS2760_CURRENT_ACCUM_LSB]; | 143 | di->raw[DS2760_CURRENT_ACCUM_LSB]; |
144 | di->accum_current_uAh = di->accum_current_raw * 250; | 144 | di->accum_current_uAh = di->accum_current_raw * 250; |
145 | 145 | ||
146 | /* DS2760 reports temperature in signed units of 0.125°C, but the | 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 | 147 | * battery class reports in units of 1/10 °C, so we convert by |
148 | * multiplying by .125 * 10 = 1.25. */ | 148 | * multiplying by .125 * 10 = 1.25. */ |
149 | di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | | 149 | di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) | |
150 | (di->raw[DS2760_TEMP_LSB] >> 5); | 150 | (di->raw[DS2760_TEMP_LSB] >> 5); |
151 | di->temp_C = di->temp_raw + (di->temp_raw / 4); | 151 | di->temp_C = di->temp_raw + (di->temp_raw / 4); |
152 | 152 | ||
153 | /* At least some battery monitors (e.g. HP iPAQ) store the battery's | 153 | /* At least some battery monitors (e.g. HP iPAQ) store the battery's |
154 | * maximum rated capacity. */ | 154 | * maximum rated capacity. */ |
155 | if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) | 155 | if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities)) |
156 | di->rated_capacity = rated_capacities[ | 156 | di->rated_capacity = rated_capacities[ |
157 | (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; | 157 | (unsigned int)di->raw[DS2760_RATED_CAPACITY]]; |
158 | else | 158 | else |
159 | di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; | 159 | di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10; |
160 | 160 | ||
161 | di->rated_capacity *= 1000; /* convert to µAh */ | 161 | di->rated_capacity *= 1000; /* convert to µAh */ |
162 | 162 | ||
163 | /* Calculate the full level at the present temperature. */ | 163 | /* Calculate the full level at the present temperature. */ |
164 | di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | | 164 | di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 | |
165 | di->raw[DS2760_ACTIVE_FULL + 1]; | 165 | di->raw[DS2760_ACTIVE_FULL + 1]; |
166 | 166 | ||
167 | scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | | 167 | scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 | |
168 | di->raw[DS2760_ACTIVE_FULL + 1]; | 168 | di->raw[DS2760_ACTIVE_FULL + 1]; |
169 | for (i = 1; i < 5; i++) | 169 | for (i = 1; i < 5; i++) |
170 | scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; | 170 | scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; |
171 | 171 | ||
172 | di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); | 172 | di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); |
173 | di->full_active_uAh *= 1000; /* convert to µAh */ | 173 | di->full_active_uAh *= 1000; /* convert to µAh */ |
174 | 174 | ||
175 | /* Calculate the empty level at the present temperature. */ | 175 | /* Calculate the empty level at the present temperature. */ |
176 | scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; | 176 | scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4]; |
177 | for (i = 3; i >= 0; i--) | 177 | for (i = 3; i >= 0; i--) |
178 | scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; | 178 | scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i]; |
179 | 179 | ||
180 | di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); | 180 | di->empty_uAh = battery_interpolate(scale, di->temp_C / 10); |
181 | di->empty_uAh *= 1000; /* convert to µAh */ | 181 | di->empty_uAh *= 1000; /* convert to µAh */ |
182 | 182 | ||
183 | /* From Maxim Application Note 131: remaining capacity = | 183 | /* From Maxim Application Note 131: remaining capacity = |
184 | * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ | 184 | * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */ |
185 | di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / | 185 | di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) / |
186 | (di->full_active_uAh - di->empty_uAh); | 186 | (di->full_active_uAh - di->empty_uAh); |
187 | 187 | ||
188 | if (di->rem_capacity < 0) | 188 | if (di->rem_capacity < 0) |
189 | di->rem_capacity = 0; | 189 | di->rem_capacity = 0; |
190 | if (di->rem_capacity > 100) | 190 | if (di->rem_capacity > 100) |
191 | di->rem_capacity = 100; | 191 | di->rem_capacity = 100; |
192 | 192 | ||
193 | if (di->current_uA) | 193 | if (di->current_uA) |
194 | di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * | 194 | di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * |
195 | 3600L) / di->current_uA; | 195 | 3600L) / di->current_uA; |
196 | else | 196 | else |
197 | di->life_sec = 0; | 197 | di->life_sec = 0; |
198 | 198 | ||
199 | return 0; | 199 | return 0; |
200 | } | 200 | } |
201 | 201 | ||
202 | static void ds2760_battery_update_status(struct ds2760_device_info *di) | 202 | static void ds2760_battery_update_status(struct ds2760_device_info *di) |
203 | { | 203 | { |
204 | int old_charge_status = di->charge_status; | 204 | int old_charge_status = di->charge_status; |
205 | 205 | ||
206 | ds2760_battery_read_status(di); | 206 | ds2760_battery_read_status(di); |
207 | 207 | ||
208 | if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) | 208 | if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN) |
209 | di->full_counter = 0; | 209 | di->full_counter = 0; |
210 | 210 | ||
211 | if (power_supply_am_i_supplied(&di->bat)) { | 211 | if (power_supply_am_i_supplied(&di->bat)) { |
212 | if (di->current_uA > 10000) { | 212 | if (di->current_uA > 10000) { |
213 | di->charge_status = POWER_SUPPLY_STATUS_CHARGING; | 213 | di->charge_status = POWER_SUPPLY_STATUS_CHARGING; |
214 | di->full_counter = 0; | 214 | di->full_counter = 0; |
215 | } else if (di->current_uA < -5000) { | 215 | } else if (di->current_uA < -5000) { |
216 | if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) | 216 | if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING) |
217 | dev_notice(di->dev, "not enough power to " | 217 | dev_notice(di->dev, "not enough power to " |
218 | "charge\n"); | 218 | "charge\n"); |
219 | di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; | 219 | di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
220 | di->full_counter = 0; | 220 | di->full_counter = 0; |
221 | } else if (di->current_uA < 10000 && | 221 | } else if (di->current_uA < 10000 && |
222 | di->charge_status != POWER_SUPPLY_STATUS_FULL) { | 222 | di->charge_status != POWER_SUPPLY_STATUS_FULL) { |
223 | 223 | ||
224 | /* Don't consider the battery to be full unless | 224 | /* Don't consider the battery to be full unless |
225 | * we've seen the current < 10 mA at least two | 225 | * we've seen the current < 10 mA at least two |
226 | * consecutive times. */ | 226 | * consecutive times. */ |
227 | 227 | ||
228 | di->full_counter++; | 228 | di->full_counter++; |
229 | 229 | ||
230 | if (di->full_counter < 2) { | 230 | if (di->full_counter < 2) { |
231 | di->charge_status = POWER_SUPPLY_STATUS_CHARGING; | 231 | di->charge_status = POWER_SUPPLY_STATUS_CHARGING; |
232 | } else { | 232 | } else { |
233 | unsigned char acr[2]; | 233 | unsigned char acr[2]; |
234 | int acr_val; | 234 | int acr_val; |
235 | 235 | ||
236 | /* acr is in units of 0.25 mAh */ | 236 | /* acr is in units of 0.25 mAh */ |
237 | acr_val = di->full_active_uAh * 4L / 1000; | 237 | acr_val = di->full_active_uAh * 4L / 1000; |
238 | 238 | ||
239 | acr[0] = acr_val >> 8; | 239 | acr[0] = acr_val >> 8; |
240 | acr[1] = acr_val & 0xff; | 240 | acr[1] = acr_val & 0xff; |
241 | 241 | ||
242 | if (w1_ds2760_write(di->w1_dev, acr, | 242 | if (w1_ds2760_write(di->w1_dev, acr, |
243 | DS2760_CURRENT_ACCUM_MSB, 2) < 2) | 243 | DS2760_CURRENT_ACCUM_MSB, 2) < 2) |
244 | dev_warn(di->dev, | 244 | dev_warn(di->dev, |
245 | "ACR reset failed\n"); | 245 | "ACR reset failed\n"); |
246 | 246 | ||
247 | di->charge_status = POWER_SUPPLY_STATUS_FULL; | 247 | di->charge_status = POWER_SUPPLY_STATUS_FULL; |
248 | } | 248 | } |
249 | } | 249 | } |
250 | } else { | 250 | } else { |
251 | di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; | 251 | di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; |
252 | di->full_counter = 0; | 252 | di->full_counter = 0; |
253 | } | 253 | } |
254 | 254 | ||
255 | if (di->charge_status != old_charge_status) | 255 | if (di->charge_status != old_charge_status) |
256 | power_supply_changed(&di->bat); | 256 | power_supply_changed(&di->bat); |
257 | |||
258 | return; | ||
259 | } | 257 | } |
260 | 258 | ||
261 | static void ds2760_battery_work(struct work_struct *work) | 259 | static void ds2760_battery_work(struct work_struct *work) |
262 | { | 260 | { |
263 | struct ds2760_device_info *di = container_of(work, | 261 | struct ds2760_device_info *di = container_of(work, |
264 | struct ds2760_device_info, monitor_work.work); | 262 | struct ds2760_device_info, monitor_work.work); |
265 | const int interval = HZ * 60; | 263 | const int interval = HZ * 60; |
266 | 264 | ||
267 | dev_dbg(di->dev, "%s\n", __FUNCTION__); | 265 | dev_dbg(di->dev, "%s\n", __FUNCTION__); |
268 | 266 | ||
269 | ds2760_battery_update_status(di); | 267 | ds2760_battery_update_status(di); |
270 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); | 268 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval); |
271 | |||
272 | return; | ||
273 | } | 269 | } |
274 | 270 | ||
275 | #define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ | 271 | #define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \ |
276 | bat); | 272 | bat); |
277 | 273 | ||
278 | static void ds2760_battery_external_power_changed(struct power_supply *psy) | 274 | static void ds2760_battery_external_power_changed(struct power_supply *psy) |
279 | { | 275 | { |
280 | struct ds2760_device_info *di = to_ds2760_device_info(psy); | 276 | struct ds2760_device_info *di = to_ds2760_device_info(psy); |
281 | 277 | ||
282 | dev_dbg(di->dev, "%s\n", __FUNCTION__); | 278 | dev_dbg(di->dev, "%s\n", __FUNCTION__); |
283 | 279 | ||
284 | cancel_delayed_work(&di->monitor_work); | 280 | cancel_delayed_work(&di->monitor_work); |
285 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); | 281 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10); |
286 | |||
287 | return; | ||
288 | } | 282 | } |
289 | 283 | ||
290 | static int ds2760_battery_get_property(struct power_supply *psy, | 284 | static int ds2760_battery_get_property(struct power_supply *psy, |
291 | enum power_supply_property psp, | 285 | enum power_supply_property psp, |
292 | union power_supply_propval *val) | 286 | union power_supply_propval *val) |
293 | { | 287 | { |
294 | struct ds2760_device_info *di = to_ds2760_device_info(psy); | 288 | struct ds2760_device_info *di = to_ds2760_device_info(psy); |
295 | 289 | ||
296 | switch (psp) { | 290 | switch (psp) { |
297 | case POWER_SUPPLY_PROP_STATUS: | 291 | case POWER_SUPPLY_PROP_STATUS: |
298 | val->intval = di->charge_status; | 292 | val->intval = di->charge_status; |
299 | return 0; | 293 | return 0; |
300 | default: | 294 | default: |
301 | break; | 295 | break; |
302 | } | 296 | } |
303 | 297 | ||
304 | ds2760_battery_read_status(di); | 298 | ds2760_battery_read_status(di); |
305 | 299 | ||
306 | switch (psp) { | 300 | switch (psp) { |
307 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: | 301 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
308 | val->intval = di->voltage_uV; | 302 | val->intval = di->voltage_uV; |
309 | break; | 303 | break; |
310 | case POWER_SUPPLY_PROP_CURRENT_NOW: | 304 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
311 | val->intval = di->current_uA; | 305 | val->intval = di->current_uA; |
312 | break; | 306 | break; |
313 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: | 307 | case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: |
314 | val->intval = di->rated_capacity; | 308 | val->intval = di->rated_capacity; |
315 | break; | 309 | break; |
316 | case POWER_SUPPLY_PROP_CHARGE_FULL: | 310 | case POWER_SUPPLY_PROP_CHARGE_FULL: |
317 | val->intval = di->full_active_uAh; | 311 | val->intval = di->full_active_uAh; |
318 | break; | 312 | break; |
319 | case POWER_SUPPLY_PROP_CHARGE_EMPTY: | 313 | case POWER_SUPPLY_PROP_CHARGE_EMPTY: |
320 | val->intval = di->empty_uAh; | 314 | val->intval = di->empty_uAh; |
321 | break; | 315 | break; |
322 | case POWER_SUPPLY_PROP_CHARGE_NOW: | 316 | case POWER_SUPPLY_PROP_CHARGE_NOW: |
323 | val->intval = di->accum_current_uAh; | 317 | val->intval = di->accum_current_uAh; |
324 | break; | 318 | break; |
325 | case POWER_SUPPLY_PROP_TEMP: | 319 | case POWER_SUPPLY_PROP_TEMP: |
326 | val->intval = di->temp_C; | 320 | val->intval = di->temp_C; |
327 | break; | 321 | break; |
328 | default: | 322 | default: |
329 | return -EINVAL; | 323 | return -EINVAL; |
330 | } | 324 | } |
331 | 325 | ||
332 | return 0; | 326 | return 0; |
333 | } | 327 | } |
334 | 328 | ||
335 | static enum power_supply_property ds2760_battery_props[] = { | 329 | static enum power_supply_property ds2760_battery_props[] = { |
336 | POWER_SUPPLY_PROP_STATUS, | 330 | POWER_SUPPLY_PROP_STATUS, |
337 | POWER_SUPPLY_PROP_VOLTAGE_NOW, | 331 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
338 | POWER_SUPPLY_PROP_CURRENT_NOW, | 332 | POWER_SUPPLY_PROP_CURRENT_NOW, |
339 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, | 333 | POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, |
340 | POWER_SUPPLY_PROP_CHARGE_FULL, | 334 | POWER_SUPPLY_PROP_CHARGE_FULL, |
341 | POWER_SUPPLY_PROP_CHARGE_EMPTY, | 335 | POWER_SUPPLY_PROP_CHARGE_EMPTY, |
342 | POWER_SUPPLY_PROP_CHARGE_NOW, | 336 | POWER_SUPPLY_PROP_CHARGE_NOW, |
343 | POWER_SUPPLY_PROP_TEMP, | 337 | POWER_SUPPLY_PROP_TEMP, |
344 | }; | 338 | }; |
345 | 339 | ||
346 | static int ds2760_battery_probe(struct platform_device *pdev) | 340 | static int ds2760_battery_probe(struct platform_device *pdev) |
347 | { | 341 | { |
348 | int retval = 0; | 342 | int retval = 0; |
349 | struct ds2760_device_info *di; | 343 | struct ds2760_device_info *di; |
350 | struct ds2760_platform_data *pdata; | 344 | struct ds2760_platform_data *pdata; |
351 | 345 | ||
352 | di = kzalloc(sizeof(*di), GFP_KERNEL); | 346 | di = kzalloc(sizeof(*di), GFP_KERNEL); |
353 | if (!di) { | 347 | if (!di) { |
354 | retval = -ENOMEM; | 348 | retval = -ENOMEM; |
355 | goto di_alloc_failed; | 349 | goto di_alloc_failed; |
356 | } | 350 | } |
357 | 351 | ||
358 | platform_set_drvdata(pdev, di); | 352 | platform_set_drvdata(pdev, di); |
359 | 353 | ||
360 | pdata = pdev->dev.platform_data; | 354 | pdata = pdev->dev.platform_data; |
361 | di->dev = &pdev->dev; | 355 | di->dev = &pdev->dev; |
362 | di->w1_dev = pdev->dev.parent; | 356 | di->w1_dev = pdev->dev.parent; |
363 | di->bat.name = pdev->dev.bus_id; | 357 | di->bat.name = pdev->dev.bus_id; |
364 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; | 358 | di->bat.type = POWER_SUPPLY_TYPE_BATTERY; |
365 | di->bat.properties = ds2760_battery_props; | 359 | di->bat.properties = ds2760_battery_props; |
366 | di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); | 360 | di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props); |
367 | di->bat.get_property = ds2760_battery_get_property; | 361 | di->bat.get_property = ds2760_battery_get_property; |
368 | di->bat.external_power_changed = | 362 | di->bat.external_power_changed = |
369 | ds2760_battery_external_power_changed; | 363 | ds2760_battery_external_power_changed; |
370 | 364 | ||
371 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; | 365 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; |
372 | 366 | ||
373 | retval = power_supply_register(&pdev->dev, &di->bat); | 367 | retval = power_supply_register(&pdev->dev, &di->bat); |
374 | if (retval) { | 368 | if (retval) { |
375 | dev_err(di->dev, "failed to register battery"); | 369 | dev_err(di->dev, "failed to register battery"); |
376 | goto batt_failed; | 370 | goto batt_failed; |
377 | } | 371 | } |
378 | 372 | ||
379 | INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); | 373 | INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work); |
380 | di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); | 374 | di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id); |
381 | if (!di->monitor_wqueue) { | 375 | if (!di->monitor_wqueue) { |
382 | retval = -ESRCH; | 376 | retval = -ESRCH; |
383 | goto workqueue_failed; | 377 | goto workqueue_failed; |
384 | } | 378 | } |
385 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); | 379 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1); |
386 | 380 | ||
387 | goto success; | 381 | goto success; |
388 | 382 | ||
389 | workqueue_failed: | 383 | workqueue_failed: |
390 | power_supply_unregister(&di->bat); | 384 | power_supply_unregister(&di->bat); |
391 | batt_failed: | 385 | batt_failed: |
392 | kfree(di); | 386 | kfree(di); |
393 | di_alloc_failed: | 387 | di_alloc_failed: |
394 | success: | 388 | success: |
395 | return retval; | 389 | return retval; |
396 | } | 390 | } |
397 | 391 | ||
398 | static int ds2760_battery_remove(struct platform_device *pdev) | 392 | static int ds2760_battery_remove(struct platform_device *pdev) |
399 | { | 393 | { |
400 | struct ds2760_device_info *di = platform_get_drvdata(pdev); | 394 | struct ds2760_device_info *di = platform_get_drvdata(pdev); |
401 | 395 | ||
402 | cancel_rearming_delayed_workqueue(di->monitor_wqueue, | 396 | cancel_rearming_delayed_workqueue(di->monitor_wqueue, |
403 | &di->monitor_work); | 397 | &di->monitor_work); |
404 | destroy_workqueue(di->monitor_wqueue); | 398 | destroy_workqueue(di->monitor_wqueue); |
405 | power_supply_unregister(&di->bat); | 399 | power_supply_unregister(&di->bat); |
406 | 400 | ||
407 | return 0; | 401 | return 0; |
408 | } | 402 | } |
409 | 403 | ||
410 | #ifdef CONFIG_PM | 404 | #ifdef CONFIG_PM |
411 | 405 | ||
412 | static int ds2760_battery_suspend(struct platform_device *pdev, | 406 | static int ds2760_battery_suspend(struct platform_device *pdev, |
413 | pm_message_t state) | 407 | pm_message_t state) |
414 | { | 408 | { |
415 | struct ds2760_device_info *di = platform_get_drvdata(pdev); | 409 | struct ds2760_device_info *di = platform_get_drvdata(pdev); |
416 | 410 | ||
417 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; | 411 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; |
418 | 412 | ||
419 | return 0; | 413 | return 0; |
420 | } | 414 | } |
421 | 415 | ||
422 | static int ds2760_battery_resume(struct platform_device *pdev) | 416 | static int ds2760_battery_resume(struct platform_device *pdev) |
423 | { | 417 | { |
424 | struct ds2760_device_info *di = platform_get_drvdata(pdev); | 418 | struct ds2760_device_info *di = platform_get_drvdata(pdev); |
425 | 419 | ||
426 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; | 420 | di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; |
427 | power_supply_changed(&di->bat); | 421 | power_supply_changed(&di->bat); |
428 | 422 | ||
429 | cancel_delayed_work(&di->monitor_work); | 423 | cancel_delayed_work(&di->monitor_work); |
430 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); | 424 | queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ); |
431 | 425 | ||
432 | return 0; | 426 | return 0; |
433 | } | 427 | } |
434 | 428 | ||
435 | #else | 429 | #else |
436 | 430 | ||
437 | #define ds2760_battery_suspend NULL | 431 | #define ds2760_battery_suspend NULL |
438 | #define ds2760_battery_resume NULL | 432 | #define ds2760_battery_resume NULL |
439 | 433 | ||
440 | #endif /* CONFIG_PM */ | 434 | #endif /* CONFIG_PM */ |
441 | 435 | ||
442 | static struct platform_driver ds2760_battery_driver = { | 436 | static struct platform_driver ds2760_battery_driver = { |
443 | .driver = { | 437 | .driver = { |
444 | .name = "ds2760-battery", | 438 | .name = "ds2760-battery", |
445 | }, | 439 | }, |
446 | .probe = ds2760_battery_probe, | 440 | .probe = ds2760_battery_probe, |
447 | .remove = ds2760_battery_remove, | 441 | .remove = ds2760_battery_remove, |
448 | .suspend = ds2760_battery_suspend, | 442 | .suspend = ds2760_battery_suspend, |
449 | .resume = ds2760_battery_resume, | 443 | .resume = ds2760_battery_resume, |
450 | }; | 444 | }; |
451 | 445 | ||
452 | static int __init ds2760_battery_init(void) | 446 | static int __init ds2760_battery_init(void) |
453 | { | 447 | { |
454 | return platform_driver_register(&ds2760_battery_driver); | 448 | return platform_driver_register(&ds2760_battery_driver); |
455 | } | 449 | } |
456 | 450 | ||
457 | static void __exit ds2760_battery_exit(void) | 451 | static void __exit ds2760_battery_exit(void) |
458 | { | 452 | { |
459 | platform_driver_unregister(&ds2760_battery_driver); | 453 | platform_driver_unregister(&ds2760_battery_driver); |
460 | return; | ||
461 | } | 454 | } |
462 | 455 | ||
463 | module_init(ds2760_battery_init); | 456 | module_init(ds2760_battery_init); |
464 | module_exit(ds2760_battery_exit); | 457 | module_exit(ds2760_battery_exit); |
465 | 458 | ||
466 | MODULE_LICENSE("GPL"); | 459 | MODULE_LICENSE("GPL"); |
467 | MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " | 460 | MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, " |
468 | "Matt Reimer <mreimer@vpop.net>, " | 461 | "Matt Reimer <mreimer@vpop.net>, " |
469 | "Anton Vorontsov <cbou@mail.ru>"); | 462 | "Anton Vorontsov <cbou@mail.ru>"); |
470 | MODULE_DESCRIPTION("ds2760 battery driver"); | 463 | MODULE_DESCRIPTION("ds2760 battery driver"); |
471 | 464 |
drivers/power/olpc_battery.c
1 | /* | 1 | /* |
2 | * Battery driver for One Laptop Per Child board. | 2 | * Battery driver for One Laptop Per Child board. |
3 | * | 3 | * |
4 | * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> | 4 | * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/err.h> | 12 | #include <linux/err.h> |
13 | #include <linux/platform_device.h> | 13 | #include <linux/platform_device.h> |
14 | #include <linux/power_supply.h> | 14 | #include <linux/power_supply.h> |
15 | #include <linux/jiffies.h> | 15 | #include <linux/jiffies.h> |
16 | #include <linux/sched.h> | 16 | #include <linux/sched.h> |
17 | #include <asm/olpc.h> | 17 | #include <asm/olpc.h> |
18 | 18 | ||
19 | 19 | ||
20 | #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ | 20 | #define EC_BAT_VOLTAGE 0x10 /* uint16_t, *9.76/32, mV */ |
21 | #define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */ | 21 | #define EC_BAT_CURRENT 0x11 /* int16_t, *15.625/120, mA */ |
22 | #define EC_BAT_ACR 0x12 | 22 | #define EC_BAT_ACR 0x12 |
23 | #define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */ | 23 | #define EC_BAT_TEMP 0x13 /* uint16_t, *100/256, °C */ |
24 | #define EC_AMB_TEMP 0x14 /* 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 */ | 25 | #define EC_BAT_STATUS 0x15 /* uint8_t, bitmask */ |
26 | #define EC_BAT_SOC 0x16 /* uint8_t, percentage */ | 26 | #define EC_BAT_SOC 0x16 /* uint8_t, percentage */ |
27 | #define EC_BAT_SERIAL 0x17 /* uint8_t[6] */ | 27 | #define EC_BAT_SERIAL 0x17 /* uint8_t[6] */ |
28 | #define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */ | 28 | #define EC_BAT_EEPROM 0x18 /* uint8_t adr as input, uint8_t output */ |
29 | #define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */ | 29 | #define EC_BAT_ERRCODE 0x1f /* uint8_t, bitmask */ |
30 | 30 | ||
31 | #define BAT_STAT_PRESENT 0x01 | 31 | #define BAT_STAT_PRESENT 0x01 |
32 | #define BAT_STAT_FULL 0x02 | 32 | #define BAT_STAT_FULL 0x02 |
33 | #define BAT_STAT_LOW 0x04 | 33 | #define BAT_STAT_LOW 0x04 |
34 | #define BAT_STAT_DESTROY 0x08 | 34 | #define BAT_STAT_DESTROY 0x08 |
35 | #define BAT_STAT_AC 0x10 | 35 | #define BAT_STAT_AC 0x10 |
36 | #define BAT_STAT_CHARGING 0x20 | 36 | #define BAT_STAT_CHARGING 0x20 |
37 | #define BAT_STAT_DISCHARGING 0x40 | 37 | #define BAT_STAT_DISCHARGING 0x40 |
38 | 38 | ||
39 | #define BAT_ERR_INFOFAIL 0x02 | 39 | #define BAT_ERR_INFOFAIL 0x02 |
40 | #define BAT_ERR_OVERVOLTAGE 0x04 | 40 | #define BAT_ERR_OVERVOLTAGE 0x04 |
41 | #define BAT_ERR_OVERTEMP 0x05 | 41 | #define BAT_ERR_OVERTEMP 0x05 |
42 | #define BAT_ERR_GAUGESTOP 0x06 | 42 | #define BAT_ERR_GAUGESTOP 0x06 |
43 | #define BAT_ERR_OUT_OF_CONTROL 0x07 | 43 | #define BAT_ERR_OUT_OF_CONTROL 0x07 |
44 | #define BAT_ERR_ID_FAIL 0x09 | 44 | #define BAT_ERR_ID_FAIL 0x09 |
45 | #define BAT_ERR_ACR_FAIL 0x10 | 45 | #define BAT_ERR_ACR_FAIL 0x10 |
46 | 46 | ||
47 | #define BAT_ADDR_MFR_TYPE 0x5F | 47 | #define BAT_ADDR_MFR_TYPE 0x5F |
48 | 48 | ||
49 | /********************************************************************* | 49 | /********************************************************************* |
50 | * Power | 50 | * Power |
51 | *********************************************************************/ | 51 | *********************************************************************/ |
52 | 52 | ||
53 | static int olpc_ac_get_prop(struct power_supply *psy, | 53 | static int olpc_ac_get_prop(struct power_supply *psy, |
54 | enum power_supply_property psp, | 54 | enum power_supply_property psp, |
55 | union power_supply_propval *val) | 55 | union power_supply_propval *val) |
56 | { | 56 | { |
57 | int ret = 0; | 57 | int ret = 0; |
58 | uint8_t status; | 58 | uint8_t status; |
59 | 59 | ||
60 | switch (psp) { | 60 | switch (psp) { |
61 | case POWER_SUPPLY_PROP_ONLINE: | 61 | case POWER_SUPPLY_PROP_ONLINE: |
62 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); | 62 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); |
63 | if (ret) | 63 | if (ret) |
64 | return ret; | 64 | return ret; |
65 | 65 | ||
66 | val->intval = !!(status & BAT_STAT_AC); | 66 | val->intval = !!(status & BAT_STAT_AC); |
67 | break; | 67 | break; |
68 | default: | 68 | default: |
69 | ret = -EINVAL; | 69 | ret = -EINVAL; |
70 | break; | 70 | break; |
71 | } | 71 | } |
72 | return ret; | 72 | return ret; |
73 | } | 73 | } |
74 | 74 | ||
75 | static enum power_supply_property olpc_ac_props[] = { | 75 | static enum power_supply_property olpc_ac_props[] = { |
76 | POWER_SUPPLY_PROP_ONLINE, | 76 | POWER_SUPPLY_PROP_ONLINE, |
77 | }; | 77 | }; |
78 | 78 | ||
79 | static struct power_supply olpc_ac = { | 79 | static struct power_supply olpc_ac = { |
80 | .name = "olpc-ac", | 80 | .name = "olpc-ac", |
81 | .type = POWER_SUPPLY_TYPE_MAINS, | 81 | .type = POWER_SUPPLY_TYPE_MAINS, |
82 | .properties = olpc_ac_props, | 82 | .properties = olpc_ac_props, |
83 | .num_properties = ARRAY_SIZE(olpc_ac_props), | 83 | .num_properties = ARRAY_SIZE(olpc_ac_props), |
84 | .get_property = olpc_ac_get_prop, | 84 | .get_property = olpc_ac_get_prop, |
85 | }; | 85 | }; |
86 | 86 | ||
87 | /********************************************************************* | 87 | /********************************************************************* |
88 | * Battery properties | 88 | * Battery properties |
89 | *********************************************************************/ | 89 | *********************************************************************/ |
90 | static int olpc_bat_get_property(struct power_supply *psy, | 90 | static int olpc_bat_get_property(struct power_supply *psy, |
91 | enum power_supply_property psp, | 91 | enum power_supply_property psp, |
92 | union power_supply_propval *val) | 92 | union power_supply_propval *val) |
93 | { | 93 | { |
94 | int ret = 0; | 94 | int ret = 0; |
95 | int16_t ec_word; | 95 | int16_t ec_word; |
96 | uint8_t ec_byte; | 96 | uint8_t ec_byte; |
97 | 97 | ||
98 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); | 98 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &ec_byte, 1); |
99 | if (ret) | 99 | if (ret) |
100 | return ret; | 100 | return ret; |
101 | 101 | ||
102 | /* Theoretically there's a race here -- the battery could be | 102 | /* Theoretically there's a race here -- the battery could be |
103 | removed immediately after we check whether it's present, and | 103 | removed immediately after we check whether it's present, and |
104 | then we query for some other property of the now-absent battery. | 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 | 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 | 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. */ | 107 | and managed to read it out before the battery went away. */ |
108 | if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT) | 108 | if (!(ec_byte & BAT_STAT_PRESENT) && psp != POWER_SUPPLY_PROP_PRESENT) |
109 | return -ENODEV; | 109 | return -ENODEV; |
110 | 110 | ||
111 | switch (psp) { | 111 | switch (psp) { |
112 | case POWER_SUPPLY_PROP_STATUS: | 112 | case POWER_SUPPLY_PROP_STATUS: |
113 | if (olpc_platform_info.ecver > 0x44) { | 113 | if (olpc_platform_info.ecver > 0x44) { |
114 | if (ec_byte & BAT_STAT_CHARGING) | 114 | if (ec_byte & BAT_STAT_CHARGING) |
115 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 115 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
116 | else if (ec_byte & BAT_STAT_DISCHARGING) | 116 | else if (ec_byte & BAT_STAT_DISCHARGING) |
117 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 117 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
118 | else if (ec_byte & BAT_STAT_FULL) | 118 | else if (ec_byte & BAT_STAT_FULL) |
119 | val->intval = POWER_SUPPLY_STATUS_FULL; | 119 | val->intval = POWER_SUPPLY_STATUS_FULL; |
120 | else /* er,... */ | 120 | else /* er,... */ |
121 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; | 121 | val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; |
122 | } else { | 122 | } else { |
123 | /* Older EC didn't report charge/discharge bits */ | 123 | /* Older EC didn't report charge/discharge bits */ |
124 | if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */ | 124 | if (!(ec_byte & BAT_STAT_AC)) /* No AC means discharging */ |
125 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 125 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
126 | else if (ec_byte & BAT_STAT_FULL) | 126 | else if (ec_byte & BAT_STAT_FULL) |
127 | val->intval = POWER_SUPPLY_STATUS_FULL; | 127 | val->intval = POWER_SUPPLY_STATUS_FULL; |
128 | else /* Not _necessarily_ true but EC doesn't tell all yet */ | 128 | else /* Not _necessarily_ true but EC doesn't tell all yet */ |
129 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 129 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
130 | break; | 130 | break; |
131 | } | 131 | } |
132 | case POWER_SUPPLY_PROP_PRESENT: | 132 | case POWER_SUPPLY_PROP_PRESENT: |
133 | val->intval = !!(ec_byte & BAT_STAT_PRESENT); | 133 | val->intval = !!(ec_byte & BAT_STAT_PRESENT); |
134 | break; | 134 | break; |
135 | 135 | ||
136 | case POWER_SUPPLY_PROP_HEALTH: | 136 | case POWER_SUPPLY_PROP_HEALTH: |
137 | if (ec_byte & BAT_STAT_DESTROY) | 137 | if (ec_byte & BAT_STAT_DESTROY) |
138 | val->intval = POWER_SUPPLY_HEALTH_DEAD; | 138 | val->intval = POWER_SUPPLY_HEALTH_DEAD; |
139 | else { | 139 | else { |
140 | ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); | 140 | ret = olpc_ec_cmd(EC_BAT_ERRCODE, NULL, 0, &ec_byte, 1); |
141 | if (ret) | 141 | if (ret) |
142 | return ret; | 142 | return ret; |
143 | 143 | ||
144 | switch (ec_byte) { | 144 | switch (ec_byte) { |
145 | case 0: | 145 | case 0: |
146 | val->intval = POWER_SUPPLY_HEALTH_GOOD; | 146 | val->intval = POWER_SUPPLY_HEALTH_GOOD; |
147 | break; | 147 | break; |
148 | 148 | ||
149 | case BAT_ERR_OVERTEMP: | 149 | case BAT_ERR_OVERTEMP: |
150 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; | 150 | val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; |
151 | break; | 151 | break; |
152 | 152 | ||
153 | case BAT_ERR_OVERVOLTAGE: | 153 | case BAT_ERR_OVERVOLTAGE: |
154 | val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; | 154 | val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; |
155 | break; | 155 | break; |
156 | 156 | ||
157 | case BAT_ERR_INFOFAIL: | 157 | case BAT_ERR_INFOFAIL: |
158 | case BAT_ERR_OUT_OF_CONTROL: | 158 | case BAT_ERR_OUT_OF_CONTROL: |
159 | case BAT_ERR_ID_FAIL: | 159 | case BAT_ERR_ID_FAIL: |
160 | case BAT_ERR_ACR_FAIL: | 160 | case BAT_ERR_ACR_FAIL: |
161 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; | 161 | val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; |
162 | break; | 162 | break; |
163 | 163 | ||
164 | default: | 164 | default: |
165 | /* Eep. We don't know this failure code */ | 165 | /* Eep. We don't know this failure code */ |
166 | return -EIO; | 166 | return -EIO; |
167 | } | 167 | } |
168 | } | 168 | } |
169 | break; | 169 | break; |
170 | 170 | ||
171 | case POWER_SUPPLY_PROP_MANUFACTURER: | 171 | case POWER_SUPPLY_PROP_MANUFACTURER: |
172 | ec_byte = BAT_ADDR_MFR_TYPE; | 172 | ec_byte = BAT_ADDR_MFR_TYPE; |
173 | ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); | 173 | ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); |
174 | if (ret) | 174 | if (ret) |
175 | return ret; | 175 | return ret; |
176 | 176 | ||
177 | switch (ec_byte >> 4) { | 177 | switch (ec_byte >> 4) { |
178 | case 1: | 178 | case 1: |
179 | val->strval = "Gold Peak"; | 179 | val->strval = "Gold Peak"; |
180 | break; | 180 | break; |
181 | case 2: | 181 | case 2: |
182 | val->strval = "BYD"; | 182 | val->strval = "BYD"; |
183 | break; | 183 | break; |
184 | default: | 184 | default: |
185 | val->strval = "Unknown"; | 185 | val->strval = "Unknown"; |
186 | break; | 186 | break; |
187 | } | 187 | } |
188 | break; | 188 | break; |
189 | case POWER_SUPPLY_PROP_TECHNOLOGY: | 189 | case POWER_SUPPLY_PROP_TECHNOLOGY: |
190 | ec_byte = BAT_ADDR_MFR_TYPE; | 190 | ec_byte = BAT_ADDR_MFR_TYPE; |
191 | ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); | 191 | ret = olpc_ec_cmd(EC_BAT_EEPROM, &ec_byte, 1, &ec_byte, 1); |
192 | if (ret) | 192 | if (ret) |
193 | return ret; | 193 | return ret; |
194 | 194 | ||
195 | switch (ec_byte & 0xf) { | 195 | switch (ec_byte & 0xf) { |
196 | case 1: | 196 | case 1: |
197 | val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; | 197 | val->intval = POWER_SUPPLY_TECHNOLOGY_NiMH; |
198 | break; | 198 | break; |
199 | case 2: | 199 | case 2: |
200 | val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; | 200 | val->intval = POWER_SUPPLY_TECHNOLOGY_LiFe; |
201 | break; | 201 | break; |
202 | default: | 202 | default: |
203 | val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; | 203 | val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; |
204 | break; | 204 | break; |
205 | } | 205 | } |
206 | break; | 206 | break; |
207 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | 207 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: |
208 | ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); | 208 | ret = olpc_ec_cmd(EC_BAT_VOLTAGE, NULL, 0, (void *)&ec_word, 2); |
209 | if (ret) | 209 | if (ret) |
210 | return ret; | 210 | return ret; |
211 | 211 | ||
212 | ec_word = be16_to_cpu(ec_word); | 212 | ec_word = be16_to_cpu(ec_word); |
213 | val->intval = ec_word * 9760L / 32; | 213 | val->intval = ec_word * 9760L / 32; |
214 | break; | 214 | break; |
215 | case POWER_SUPPLY_PROP_CURRENT_AVG: | 215 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
216 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); | 216 | ret = olpc_ec_cmd(EC_BAT_CURRENT, NULL, 0, (void *)&ec_word, 2); |
217 | if (ret) | 217 | if (ret) |
218 | return ret; | 218 | return ret; |
219 | 219 | ||
220 | ec_word = be16_to_cpu(ec_word); | 220 | ec_word = be16_to_cpu(ec_word); |
221 | val->intval = ec_word * 15625L / 120; | 221 | val->intval = ec_word * 15625L / 120; |
222 | break; | 222 | break; |
223 | case POWER_SUPPLY_PROP_CAPACITY: | 223 | case POWER_SUPPLY_PROP_CAPACITY: |
224 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); | 224 | ret = olpc_ec_cmd(EC_BAT_SOC, NULL, 0, &ec_byte, 1); |
225 | if (ret) | 225 | if (ret) |
226 | return ret; | 226 | return ret; |
227 | val->intval = ec_byte; | 227 | val->intval = ec_byte; |
228 | break; | 228 | break; |
229 | case POWER_SUPPLY_PROP_CAPACITY_LEVEL: | 229 | case POWER_SUPPLY_PROP_CAPACITY_LEVEL: |
230 | if (ec_byte & BAT_STAT_FULL) | 230 | if (ec_byte & BAT_STAT_FULL) |
231 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; | 231 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL; |
232 | else if (ec_byte & BAT_STAT_LOW) | 232 | else if (ec_byte & BAT_STAT_LOW) |
233 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; | 233 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW; |
234 | else | 234 | else |
235 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; | 235 | val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; |
236 | break; | 236 | break; |
237 | case POWER_SUPPLY_PROP_TEMP: | 237 | case POWER_SUPPLY_PROP_TEMP: |
238 | ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); | 238 | ret = olpc_ec_cmd(EC_BAT_TEMP, NULL, 0, (void *)&ec_word, 2); |
239 | if (ret) | 239 | if (ret) |
240 | return ret; | 240 | return ret; |
241 | ec_word = be16_to_cpu(ec_word); | 241 | ec_word = be16_to_cpu(ec_word); |
242 | val->intval = ec_word * 100 / 256; | 242 | val->intval = ec_word * 100 / 256; |
243 | break; | 243 | break; |
244 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: | 244 | case POWER_SUPPLY_PROP_TEMP_AMBIENT: |
245 | ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); | 245 | ret = olpc_ec_cmd(EC_AMB_TEMP, NULL, 0, (void *)&ec_word, 2); |
246 | if (ret) | 246 | if (ret) |
247 | return ret; | 247 | return ret; |
248 | 248 | ||
249 | ec_word = be16_to_cpu(ec_word); | 249 | ec_word = be16_to_cpu(ec_word); |
250 | val->intval = ec_word * 100 / 256; | 250 | val->intval = ec_word * 100 / 256; |
251 | break; | 251 | break; |
252 | default: | 252 | default: |
253 | ret = -EINVAL; | 253 | ret = -EINVAL; |
254 | break; | 254 | break; |
255 | } | 255 | } |
256 | 256 | ||
257 | return ret; | 257 | return ret; |
258 | } | 258 | } |
259 | 259 | ||
260 | static enum power_supply_property olpc_bat_props[] = { | 260 | static enum power_supply_property olpc_bat_props[] = { |
261 | POWER_SUPPLY_PROP_STATUS, | 261 | POWER_SUPPLY_PROP_STATUS, |
262 | POWER_SUPPLY_PROP_PRESENT, | 262 | POWER_SUPPLY_PROP_PRESENT, |
263 | POWER_SUPPLY_PROP_HEALTH, | 263 | POWER_SUPPLY_PROP_HEALTH, |
264 | POWER_SUPPLY_PROP_TECHNOLOGY, | 264 | POWER_SUPPLY_PROP_TECHNOLOGY, |
265 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | 265 | POWER_SUPPLY_PROP_VOLTAGE_AVG, |
266 | POWER_SUPPLY_PROP_CURRENT_AVG, | 266 | POWER_SUPPLY_PROP_CURRENT_AVG, |
267 | POWER_SUPPLY_PROP_CAPACITY, | 267 | POWER_SUPPLY_PROP_CAPACITY, |
268 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, | 268 | POWER_SUPPLY_PROP_CAPACITY_LEVEL, |
269 | POWER_SUPPLY_PROP_TEMP, | 269 | POWER_SUPPLY_PROP_TEMP, |
270 | POWER_SUPPLY_PROP_TEMP_AMBIENT, | 270 | POWER_SUPPLY_PROP_TEMP_AMBIENT, |
271 | POWER_SUPPLY_PROP_MANUFACTURER, | 271 | POWER_SUPPLY_PROP_MANUFACTURER, |
272 | }; | 272 | }; |
273 | 273 | ||
274 | /********************************************************************* | 274 | /********************************************************************* |
275 | * Initialisation | 275 | * Initialisation |
276 | *********************************************************************/ | 276 | *********************************************************************/ |
277 | 277 | ||
278 | static struct platform_device *bat_pdev; | 278 | static struct platform_device *bat_pdev; |
279 | 279 | ||
280 | static struct power_supply olpc_bat = { | 280 | static struct power_supply olpc_bat = { |
281 | .properties = olpc_bat_props, | 281 | .properties = olpc_bat_props, |
282 | .num_properties = ARRAY_SIZE(olpc_bat_props), | 282 | .num_properties = ARRAY_SIZE(olpc_bat_props), |
283 | .get_property = olpc_bat_get_property, | 283 | .get_property = olpc_bat_get_property, |
284 | .use_for_apm = 1, | 284 | .use_for_apm = 1, |
285 | }; | 285 | }; |
286 | 286 | ||
287 | void olpc_battery_trigger_uevent(unsigned long cause) | 287 | void olpc_battery_trigger_uevent(unsigned long cause) |
288 | { | 288 | { |
289 | if (cause & EC_SCI_SRC_ACPWR) | 289 | if (cause & EC_SCI_SRC_ACPWR) |
290 | kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); | 290 | kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE); |
291 | if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) | 291 | if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY)) |
292 | kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); | 292 | kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE); |
293 | } | 293 | } |
294 | 294 | ||
295 | static int __init olpc_bat_init(void) | 295 | static int __init olpc_bat_init(void) |
296 | { | 296 | { |
297 | int ret = 0; | 297 | int ret = 0; |
298 | uint8_t status; | 298 | uint8_t status; |
299 | 299 | ||
300 | if (!olpc_platform_info.ecver) | 300 | if (!olpc_platform_info.ecver) |
301 | return -ENXIO; | 301 | return -ENXIO; |
302 | if (olpc_platform_info.ecver < 0x43) { | 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); | 303 | printk(KERN_NOTICE "OLPC EC version 0x%02x too old for battery driver.\n", olpc_platform_info.ecver); |
304 | return -ENXIO; | 304 | return -ENXIO; |
305 | } | 305 | } |
306 | 306 | ||
307 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); | 307 | ret = olpc_ec_cmd(EC_BAT_STATUS, NULL, 0, &status, 1); |
308 | if (ret) | 308 | if (ret) |
309 | return ret; | 309 | return ret; |
310 | 310 | ||
311 | /* Ignore the status. It doesn't actually matter */ | 311 | /* Ignore the status. It doesn't actually matter */ |
312 | 312 | ||
313 | bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); | 313 | bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0); |
314 | if (IS_ERR(bat_pdev)) | 314 | if (IS_ERR(bat_pdev)) |
315 | return PTR_ERR(bat_pdev); | 315 | return PTR_ERR(bat_pdev); |
316 | 316 | ||
317 | ret = power_supply_register(&bat_pdev->dev, &olpc_ac); | 317 | ret = power_supply_register(&bat_pdev->dev, &olpc_ac); |
318 | if (ret) | 318 | if (ret) |
319 | goto ac_failed; | 319 | goto ac_failed; |
320 | 320 | ||
321 | olpc_bat.name = bat_pdev->name; | 321 | olpc_bat.name = bat_pdev->name; |
322 | 322 | ||
323 | ret = power_supply_register(&bat_pdev->dev, &olpc_bat); | 323 | ret = power_supply_register(&bat_pdev->dev, &olpc_bat); |
324 | if (ret) | 324 | if (ret) |
325 | goto battery_failed; | 325 | goto battery_failed; |
326 | 326 | ||
327 | olpc_register_battery_callback(&olpc_battery_trigger_uevent); | 327 | olpc_register_battery_callback(&olpc_battery_trigger_uevent); |
328 | goto success; | 328 | goto success; |
329 | 329 | ||
330 | battery_failed: | 330 | battery_failed: |
331 | power_supply_unregister(&olpc_ac); | 331 | power_supply_unregister(&olpc_ac); |
332 | ac_failed: | 332 | ac_failed: |
333 | platform_device_unregister(bat_pdev); | 333 | platform_device_unregister(bat_pdev); |
334 | success: | 334 | success: |
335 | return ret; | 335 | return ret; |
336 | } | 336 | } |
337 | 337 | ||
338 | static void __exit olpc_bat_exit(void) | 338 | static void __exit olpc_bat_exit(void) |
339 | { | 339 | { |
340 | olpc_deregister_battery_callback(); | 340 | olpc_deregister_battery_callback(); |
341 | power_supply_unregister(&olpc_bat); | 341 | power_supply_unregister(&olpc_bat); |
342 | power_supply_unregister(&olpc_ac); | 342 | power_supply_unregister(&olpc_ac); |
343 | platform_device_unregister(bat_pdev); | 343 | platform_device_unregister(bat_pdev); |
344 | return; | ||
345 | } | 344 | } |
346 | 345 | ||
347 | module_init(olpc_bat_init); | 346 | module_init(olpc_bat_init); |
348 | module_exit(olpc_bat_exit); | 347 | module_exit(olpc_bat_exit); |
349 | 348 | ||
350 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 349 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
351 | MODULE_LICENSE("GPL"); | 350 | MODULE_LICENSE("GPL"); |
352 | MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine"); | 351 | MODULE_DESCRIPTION("Battery driver for One Laptop Per Child 'XO' machine"); |
353 | 352 |
drivers/power/pda_power.c
1 | /* | 1 | /* |
2 | * Common power driver for PDAs and phones with one or two external | 2 | * Common power driver for PDAs and phones with one or two external |
3 | * power supplies (AC/USB) connected to main and backup batteries, | 3 | * power supplies (AC/USB) connected to main and backup batteries, |
4 | * and optional builtin charger. | 4 | * and optional builtin charger. |
5 | * | 5 | * |
6 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | 6 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> |
7 | * | 7 | * |
8 | * This program is free software; you can redistribute it and/or modify | 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 | 9 | * it under the terms of the GNU General Public License version 2 as |
10 | * published by the Free Software Foundation. | 10 | * published by the Free Software Foundation. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/platform_device.h> | 14 | #include <linux/platform_device.h> |
15 | #include <linux/interrupt.h> | 15 | #include <linux/interrupt.h> |
16 | #include <linux/power_supply.h> | 16 | #include <linux/power_supply.h> |
17 | #include <linux/pda_power.h> | 17 | #include <linux/pda_power.h> |
18 | #include <linux/timer.h> | 18 | #include <linux/timer.h> |
19 | #include <linux/jiffies.h> | 19 | #include <linux/jiffies.h> |
20 | 20 | ||
21 | static inline unsigned int get_irq_flags(struct resource *res) | 21 | static inline unsigned int get_irq_flags(struct resource *res) |
22 | { | 22 | { |
23 | unsigned int flags = IRQF_DISABLED | IRQF_SHARED; | 23 | unsigned int flags = IRQF_DISABLED | IRQF_SHARED; |
24 | 24 | ||
25 | flags |= res->flags & IRQF_TRIGGER_MASK; | 25 | flags |= res->flags & IRQF_TRIGGER_MASK; |
26 | 26 | ||
27 | return flags; | 27 | return flags; |
28 | } | 28 | } |
29 | 29 | ||
30 | static struct device *dev; | 30 | static struct device *dev; |
31 | static struct pda_power_pdata *pdata; | 31 | static struct pda_power_pdata *pdata; |
32 | static struct resource *ac_irq, *usb_irq; | 32 | static struct resource *ac_irq, *usb_irq; |
33 | static struct timer_list charger_timer; | 33 | static struct timer_list charger_timer; |
34 | static struct timer_list supply_timer; | 34 | static struct timer_list supply_timer; |
35 | 35 | ||
36 | static int pda_power_get_property(struct power_supply *psy, | 36 | static int pda_power_get_property(struct power_supply *psy, |
37 | enum power_supply_property psp, | 37 | enum power_supply_property psp, |
38 | union power_supply_propval *val) | 38 | union power_supply_propval *val) |
39 | { | 39 | { |
40 | switch (psp) { | 40 | switch (psp) { |
41 | case POWER_SUPPLY_PROP_ONLINE: | 41 | case POWER_SUPPLY_PROP_ONLINE: |
42 | if (psy->type == POWER_SUPPLY_TYPE_MAINS) | 42 | if (psy->type == POWER_SUPPLY_TYPE_MAINS) |
43 | val->intval = pdata->is_ac_online ? | 43 | val->intval = pdata->is_ac_online ? |
44 | pdata->is_ac_online() : 0; | 44 | pdata->is_ac_online() : 0; |
45 | else | 45 | else |
46 | val->intval = pdata->is_usb_online ? | 46 | val->intval = pdata->is_usb_online ? |
47 | pdata->is_usb_online() : 0; | 47 | pdata->is_usb_online() : 0; |
48 | break; | 48 | break; |
49 | default: | 49 | default: |
50 | return -EINVAL; | 50 | return -EINVAL; |
51 | } | 51 | } |
52 | return 0; | 52 | return 0; |
53 | } | 53 | } |
54 | 54 | ||
55 | static enum power_supply_property pda_power_props[] = { | 55 | static enum power_supply_property pda_power_props[] = { |
56 | POWER_SUPPLY_PROP_ONLINE, | 56 | POWER_SUPPLY_PROP_ONLINE, |
57 | }; | 57 | }; |
58 | 58 | ||
59 | static char *pda_power_supplied_to[] = { | 59 | static char *pda_power_supplied_to[] = { |
60 | "main-battery", | 60 | "main-battery", |
61 | "backup-battery", | 61 | "backup-battery", |
62 | }; | 62 | }; |
63 | 63 | ||
64 | static struct power_supply pda_power_supplies[] = { | 64 | static struct power_supply pda_power_supplies[] = { |
65 | { | 65 | { |
66 | .name = "ac", | 66 | .name = "ac", |
67 | .type = POWER_SUPPLY_TYPE_MAINS, | 67 | .type = POWER_SUPPLY_TYPE_MAINS, |
68 | .supplied_to = pda_power_supplied_to, | 68 | .supplied_to = pda_power_supplied_to, |
69 | .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), | 69 | .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), |
70 | .properties = pda_power_props, | 70 | .properties = pda_power_props, |
71 | .num_properties = ARRAY_SIZE(pda_power_props), | 71 | .num_properties = ARRAY_SIZE(pda_power_props), |
72 | .get_property = pda_power_get_property, | 72 | .get_property = pda_power_get_property, |
73 | }, | 73 | }, |
74 | { | 74 | { |
75 | .name = "usb", | 75 | .name = "usb", |
76 | .type = POWER_SUPPLY_TYPE_USB, | 76 | .type = POWER_SUPPLY_TYPE_USB, |
77 | .supplied_to = pda_power_supplied_to, | 77 | .supplied_to = pda_power_supplied_to, |
78 | .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), | 78 | .num_supplicants = ARRAY_SIZE(pda_power_supplied_to), |
79 | .properties = pda_power_props, | 79 | .properties = pda_power_props, |
80 | .num_properties = ARRAY_SIZE(pda_power_props), | 80 | .num_properties = ARRAY_SIZE(pda_power_props), |
81 | .get_property = pda_power_get_property, | 81 | .get_property = pda_power_get_property, |
82 | }, | 82 | }, |
83 | }; | 83 | }; |
84 | 84 | ||
85 | static void update_charger(void) | 85 | static void update_charger(void) |
86 | { | 86 | { |
87 | if (!pdata->set_charge) | 87 | if (!pdata->set_charge) |
88 | return; | 88 | return; |
89 | 89 | ||
90 | if (pdata->is_ac_online && pdata->is_ac_online()) { | 90 | if (pdata->is_ac_online && pdata->is_ac_online()) { |
91 | dev_dbg(dev, "charger on (AC)\n"); | 91 | dev_dbg(dev, "charger on (AC)\n"); |
92 | pdata->set_charge(PDA_POWER_CHARGE_AC); | 92 | pdata->set_charge(PDA_POWER_CHARGE_AC); |
93 | } else if (pdata->is_usb_online && pdata->is_usb_online()) { | 93 | } else if (pdata->is_usb_online && pdata->is_usb_online()) { |
94 | dev_dbg(dev, "charger on (USB)\n"); | 94 | dev_dbg(dev, "charger on (USB)\n"); |
95 | pdata->set_charge(PDA_POWER_CHARGE_USB); | 95 | pdata->set_charge(PDA_POWER_CHARGE_USB); |
96 | } else { | 96 | } else { |
97 | dev_dbg(dev, "charger off\n"); | 97 | dev_dbg(dev, "charger off\n"); |
98 | pdata->set_charge(0); | 98 | pdata->set_charge(0); |
99 | } | 99 | } |
100 | |||
101 | return; | ||
102 | } | 100 | } |
103 | 101 | ||
104 | static void supply_timer_func(unsigned long power_supply_ptr) | 102 | static void supply_timer_func(unsigned long power_supply_ptr) |
105 | { | 103 | { |
106 | void *power_supply = (void *)power_supply_ptr; | 104 | void *power_supply = (void *)power_supply_ptr; |
107 | 105 | ||
108 | power_supply_changed(power_supply); | 106 | power_supply_changed(power_supply); |
109 | return; | ||
110 | } | 107 | } |
111 | 108 | ||
112 | static void charger_timer_func(unsigned long power_supply_ptr) | 109 | static void charger_timer_func(unsigned long power_supply_ptr) |
113 | { | 110 | { |
114 | update_charger(); | 111 | update_charger(); |
115 | 112 | ||
116 | /* Okay, charger set. Now wait a bit before notifying supplicants, | 113 | /* Okay, charger set. Now wait a bit before notifying supplicants, |
117 | * charge power should stabilize. */ | 114 | * charge power should stabilize. */ |
118 | supply_timer.data = power_supply_ptr; | 115 | supply_timer.data = power_supply_ptr; |
119 | mod_timer(&supply_timer, | 116 | mod_timer(&supply_timer, |
120 | jiffies + msecs_to_jiffies(pdata->wait_for_charger)); | 117 | jiffies + msecs_to_jiffies(pdata->wait_for_charger)); |
121 | return; | ||
122 | } | 118 | } |
123 | 119 | ||
124 | static irqreturn_t power_changed_isr(int irq, void *power_supply) | 120 | static irqreturn_t power_changed_isr(int irq, void *power_supply) |
125 | { | 121 | { |
126 | /* Wait a bit before reading ac/usb line status and setting charger, | 122 | /* Wait a bit before reading ac/usb line status and setting charger, |
127 | * because ac/usb status readings may lag from irq. */ | 123 | * because ac/usb status readings may lag from irq. */ |
128 | charger_timer.data = (unsigned long)power_supply; | 124 | charger_timer.data = (unsigned long)power_supply; |
129 | mod_timer(&charger_timer, | 125 | mod_timer(&charger_timer, |
130 | jiffies + msecs_to_jiffies(pdata->wait_for_status)); | 126 | jiffies + msecs_to_jiffies(pdata->wait_for_status)); |
131 | return IRQ_HANDLED; | 127 | return IRQ_HANDLED; |
132 | } | 128 | } |
133 | 129 | ||
134 | static int pda_power_probe(struct platform_device *pdev) | 130 | static int pda_power_probe(struct platform_device *pdev) |
135 | { | 131 | { |
136 | int ret = 0; | 132 | int ret = 0; |
137 | 133 | ||
138 | dev = &pdev->dev; | 134 | dev = &pdev->dev; |
139 | 135 | ||
140 | if (pdev->id != -1) { | 136 | if (pdev->id != -1) { |
141 | dev_err(dev, "it's meaningless to register several " | 137 | dev_err(dev, "it's meaningless to register several " |
142 | "pda_powers; use id = -1\n"); | 138 | "pda_powers; use id = -1\n"); |
143 | ret = -EINVAL; | 139 | ret = -EINVAL; |
144 | goto wrongid; | 140 | goto wrongid; |
145 | } | 141 | } |
146 | 142 | ||
147 | pdata = pdev->dev.platform_data; | 143 | pdata = pdev->dev.platform_data; |
148 | 144 | ||
149 | update_charger(); | 145 | update_charger(); |
150 | 146 | ||
151 | if (!pdata->wait_for_status) | 147 | if (!pdata->wait_for_status) |
152 | pdata->wait_for_status = 500; | 148 | pdata->wait_for_status = 500; |
153 | 149 | ||
154 | if (!pdata->wait_for_charger) | 150 | if (!pdata->wait_for_charger) |
155 | pdata->wait_for_charger = 500; | 151 | pdata->wait_for_charger = 500; |
156 | 152 | ||
157 | setup_timer(&charger_timer, charger_timer_func, 0); | 153 | setup_timer(&charger_timer, charger_timer_func, 0); |
158 | setup_timer(&supply_timer, supply_timer_func, 0); | 154 | setup_timer(&supply_timer, supply_timer_func, 0); |
159 | 155 | ||
160 | ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); | 156 | ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac"); |
161 | usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); | 157 | usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb"); |
162 | if (!ac_irq && !usb_irq) { | 158 | if (!ac_irq && !usb_irq) { |
163 | dev_err(dev, "no ac/usb irq specified\n"); | 159 | dev_err(dev, "no ac/usb irq specified\n"); |
164 | ret = -ENODEV; | 160 | ret = -ENODEV; |
165 | goto noirqs; | 161 | goto noirqs; |
166 | } | 162 | } |
167 | 163 | ||
168 | if (pdata->supplied_to) { | 164 | if (pdata->supplied_to) { |
169 | pda_power_supplies[0].supplied_to = pdata->supplied_to; | 165 | pda_power_supplies[0].supplied_to = pdata->supplied_to; |
170 | pda_power_supplies[1].supplied_to = pdata->supplied_to; | 166 | pda_power_supplies[1].supplied_to = pdata->supplied_to; |
171 | pda_power_supplies[0].num_supplicants = pdata->num_supplicants; | 167 | pda_power_supplies[0].num_supplicants = pdata->num_supplicants; |
172 | pda_power_supplies[1].num_supplicants = pdata->num_supplicants; | 168 | pda_power_supplies[1].num_supplicants = pdata->num_supplicants; |
173 | } | 169 | } |
174 | 170 | ||
175 | ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); | 171 | ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); |
176 | if (ret) { | 172 | if (ret) { |
177 | dev_err(dev, "failed to register %s power supply\n", | 173 | dev_err(dev, "failed to register %s power supply\n", |
178 | pda_power_supplies[0].name); | 174 | pda_power_supplies[0].name); |
179 | goto supply0_failed; | 175 | goto supply0_failed; |
180 | } | 176 | } |
181 | 177 | ||
182 | ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); | 178 | ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); |
183 | if (ret) { | 179 | if (ret) { |
184 | dev_err(dev, "failed to register %s power supply\n", | 180 | dev_err(dev, "failed to register %s power supply\n", |
185 | pda_power_supplies[1].name); | 181 | pda_power_supplies[1].name); |
186 | goto supply1_failed; | 182 | goto supply1_failed; |
187 | } | 183 | } |
188 | 184 | ||
189 | if (ac_irq) { | 185 | if (ac_irq) { |
190 | ret = request_irq(ac_irq->start, power_changed_isr, | 186 | ret = request_irq(ac_irq->start, power_changed_isr, |
191 | get_irq_flags(ac_irq), ac_irq->name, | 187 | get_irq_flags(ac_irq), ac_irq->name, |
192 | &pda_power_supplies[0]); | 188 | &pda_power_supplies[0]); |
193 | if (ret) { | 189 | if (ret) { |
194 | dev_err(dev, "request ac irq failed\n"); | 190 | dev_err(dev, "request ac irq failed\n"); |
195 | goto ac_irq_failed; | 191 | goto ac_irq_failed; |
196 | } | 192 | } |
197 | } | 193 | } |
198 | 194 | ||
199 | if (usb_irq) { | 195 | if (usb_irq) { |
200 | ret = request_irq(usb_irq->start, power_changed_isr, | 196 | ret = request_irq(usb_irq->start, power_changed_isr, |
201 | get_irq_flags(usb_irq), usb_irq->name, | 197 | get_irq_flags(usb_irq), usb_irq->name, |
202 | &pda_power_supplies[1]); | 198 | &pda_power_supplies[1]); |
203 | if (ret) { | 199 | if (ret) { |
204 | dev_err(dev, "request usb irq failed\n"); | 200 | dev_err(dev, "request usb irq failed\n"); |
205 | goto usb_irq_failed; | 201 | goto usb_irq_failed; |
206 | } | 202 | } |
207 | } | 203 | } |
208 | 204 | ||
209 | goto success; | 205 | goto success; |
210 | 206 | ||
211 | usb_irq_failed: | 207 | usb_irq_failed: |
212 | if (ac_irq) | 208 | if (ac_irq) |
213 | free_irq(ac_irq->start, &pda_power_supplies[0]); | 209 | free_irq(ac_irq->start, &pda_power_supplies[0]); |
214 | ac_irq_failed: | 210 | ac_irq_failed: |
215 | power_supply_unregister(&pda_power_supplies[1]); | 211 | power_supply_unregister(&pda_power_supplies[1]); |
216 | supply1_failed: | 212 | supply1_failed: |
217 | power_supply_unregister(&pda_power_supplies[0]); | 213 | power_supply_unregister(&pda_power_supplies[0]); |
218 | supply0_failed: | 214 | supply0_failed: |
219 | noirqs: | 215 | noirqs: |
220 | wrongid: | 216 | wrongid: |
221 | success: | 217 | success: |
222 | return ret; | 218 | return ret; |
223 | } | 219 | } |
224 | 220 | ||
225 | static int pda_power_remove(struct platform_device *pdev) | 221 | static int pda_power_remove(struct platform_device *pdev) |
226 | { | 222 | { |
227 | if (usb_irq) | 223 | if (usb_irq) |
228 | free_irq(usb_irq->start, &pda_power_supplies[1]); | 224 | free_irq(usb_irq->start, &pda_power_supplies[1]); |
229 | if (ac_irq) | 225 | if (ac_irq) |
230 | free_irq(ac_irq->start, &pda_power_supplies[0]); | 226 | free_irq(ac_irq->start, &pda_power_supplies[0]); |
231 | del_timer_sync(&charger_timer); | 227 | del_timer_sync(&charger_timer); |
232 | del_timer_sync(&supply_timer); | 228 | del_timer_sync(&supply_timer); |
233 | power_supply_unregister(&pda_power_supplies[1]); | 229 | power_supply_unregister(&pda_power_supplies[1]); |
234 | power_supply_unregister(&pda_power_supplies[0]); | 230 | power_supply_unregister(&pda_power_supplies[0]); |
235 | return 0; | 231 | return 0; |
236 | } | 232 | } |
237 | 233 | ||
238 | static struct platform_driver pda_power_pdrv = { | 234 | static struct platform_driver pda_power_pdrv = { |
239 | .driver = { | 235 | .driver = { |
240 | .name = "pda-power", | 236 | .name = "pda-power", |
241 | }, | 237 | }, |
242 | .probe = pda_power_probe, | 238 | .probe = pda_power_probe, |
243 | .remove = pda_power_remove, | 239 | .remove = pda_power_remove, |
244 | }; | 240 | }; |
245 | 241 | ||
246 | static int __init pda_power_init(void) | 242 | static int __init pda_power_init(void) |
247 | { | 243 | { |
248 | return platform_driver_register(&pda_power_pdrv); | 244 | return platform_driver_register(&pda_power_pdrv); |
249 | } | 245 | } |
250 | 246 | ||
251 | static void __exit pda_power_exit(void) | 247 | static void __exit pda_power_exit(void) |
252 | { | 248 | { |
253 | platform_driver_unregister(&pda_power_pdrv); | 249 | platform_driver_unregister(&pda_power_pdrv); |
254 | return; | ||
255 | } | 250 | } |
256 | 251 | ||
257 | module_init(pda_power_init); | 252 | module_init(pda_power_init); |
258 | module_exit(pda_power_exit); | 253 | module_exit(pda_power_exit); |
259 | MODULE_LICENSE("GPL"); | 254 | MODULE_LICENSE("GPL"); |
260 | MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); | 255 | MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); |
261 | 256 |
drivers/power/pmu_battery.c
1 | /* | 1 | /* |
2 | * Battery class driver for Apple PMU | 2 | * Battery class driver for Apple PMU |
3 | * | 3 | * |
4 | * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> | 4 | * Copyright © 2006 David Woodhouse <dwmw2@infradead.org> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 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 | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/platform_device.h> | 12 | #include <linux/platform_device.h> |
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/power_supply.h> | 14 | #include <linux/power_supply.h> |
15 | #include <linux/adb.h> | 15 | #include <linux/adb.h> |
16 | #include <linux/pmu.h> | 16 | #include <linux/pmu.h> |
17 | 17 | ||
18 | static struct pmu_battery_dev { | 18 | static struct pmu_battery_dev { |
19 | struct power_supply bat; | 19 | struct power_supply bat; |
20 | struct pmu_battery_info *pbi; | 20 | struct pmu_battery_info *pbi; |
21 | char name[16]; | 21 | char name[16]; |
22 | int propval; | 22 | int propval; |
23 | } *pbats[PMU_MAX_BATTERIES]; | 23 | } *pbats[PMU_MAX_BATTERIES]; |
24 | 24 | ||
25 | #define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) | 25 | #define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat) |
26 | 26 | ||
27 | /********************************************************************* | 27 | /********************************************************************* |
28 | * Power | 28 | * Power |
29 | *********************************************************************/ | 29 | *********************************************************************/ |
30 | 30 | ||
31 | static int pmu_get_ac_prop(struct power_supply *psy, | 31 | static int pmu_get_ac_prop(struct power_supply *psy, |
32 | enum power_supply_property psp, | 32 | enum power_supply_property psp, |
33 | union power_supply_propval *val) | 33 | union power_supply_propval *val) |
34 | { | 34 | { |
35 | switch (psp) { | 35 | switch (psp) { |
36 | case POWER_SUPPLY_PROP_ONLINE: | 36 | case POWER_SUPPLY_PROP_ONLINE: |
37 | val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || | 37 | val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) || |
38 | (pmu_battery_count == 0); | 38 | (pmu_battery_count == 0); |
39 | break; | 39 | break; |
40 | default: | 40 | default: |
41 | return -EINVAL; | 41 | return -EINVAL; |
42 | } | 42 | } |
43 | 43 | ||
44 | return 0; | 44 | return 0; |
45 | } | 45 | } |
46 | 46 | ||
47 | static enum power_supply_property pmu_ac_props[] = { | 47 | static enum power_supply_property pmu_ac_props[] = { |
48 | POWER_SUPPLY_PROP_ONLINE, | 48 | POWER_SUPPLY_PROP_ONLINE, |
49 | }; | 49 | }; |
50 | 50 | ||
51 | static struct power_supply pmu_ac = { | 51 | static struct power_supply pmu_ac = { |
52 | .name = "pmu-ac", | 52 | .name = "pmu-ac", |
53 | .type = POWER_SUPPLY_TYPE_MAINS, | 53 | .type = POWER_SUPPLY_TYPE_MAINS, |
54 | .properties = pmu_ac_props, | 54 | .properties = pmu_ac_props, |
55 | .num_properties = ARRAY_SIZE(pmu_ac_props), | 55 | .num_properties = ARRAY_SIZE(pmu_ac_props), |
56 | .get_property = pmu_get_ac_prop, | 56 | .get_property = pmu_get_ac_prop, |
57 | }; | 57 | }; |
58 | 58 | ||
59 | /********************************************************************* | 59 | /********************************************************************* |
60 | * Battery properties | 60 | * Battery properties |
61 | *********************************************************************/ | 61 | *********************************************************************/ |
62 | 62 | ||
63 | static char *pmu_batt_types[] = { | 63 | static char *pmu_batt_types[] = { |
64 | "Smart", "Comet", "Hooper", "Unknown" | 64 | "Smart", "Comet", "Hooper", "Unknown" |
65 | }; | 65 | }; |
66 | 66 | ||
67 | static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) | 67 | static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi) |
68 | { | 68 | { |
69 | switch (pbi->flags & PMU_BATT_TYPE_MASK) { | 69 | switch (pbi->flags & PMU_BATT_TYPE_MASK) { |
70 | case PMU_BATT_TYPE_SMART: | 70 | case PMU_BATT_TYPE_SMART: |
71 | return pmu_batt_types[0]; | 71 | return pmu_batt_types[0]; |
72 | case PMU_BATT_TYPE_COMET: | 72 | case PMU_BATT_TYPE_COMET: |
73 | return pmu_batt_types[1]; | 73 | return pmu_batt_types[1]; |
74 | case PMU_BATT_TYPE_HOOPER: | 74 | case PMU_BATT_TYPE_HOOPER: |
75 | return pmu_batt_types[2]; | 75 | return pmu_batt_types[2]; |
76 | default: break; | 76 | default: break; |
77 | } | 77 | } |
78 | return pmu_batt_types[3]; | 78 | return pmu_batt_types[3]; |
79 | } | 79 | } |
80 | 80 | ||
81 | static int pmu_bat_get_property(struct power_supply *psy, | 81 | static int pmu_bat_get_property(struct power_supply *psy, |
82 | enum power_supply_property psp, | 82 | enum power_supply_property psp, |
83 | union power_supply_propval *val) | 83 | union power_supply_propval *val) |
84 | { | 84 | { |
85 | struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); | 85 | struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy); |
86 | struct pmu_battery_info *pbi = pbat->pbi; | 86 | struct pmu_battery_info *pbi = pbat->pbi; |
87 | 87 | ||
88 | switch (psp) { | 88 | switch (psp) { |
89 | case POWER_SUPPLY_PROP_STATUS: | 89 | case POWER_SUPPLY_PROP_STATUS: |
90 | if (pbi->flags & PMU_BATT_CHARGING) | 90 | if (pbi->flags & PMU_BATT_CHARGING) |
91 | val->intval = POWER_SUPPLY_STATUS_CHARGING; | 91 | val->intval = POWER_SUPPLY_STATUS_CHARGING; |
92 | else | 92 | else |
93 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; | 93 | val->intval = POWER_SUPPLY_STATUS_DISCHARGING; |
94 | break; | 94 | break; |
95 | case POWER_SUPPLY_PROP_PRESENT: | 95 | case POWER_SUPPLY_PROP_PRESENT: |
96 | val->intval = !!(pbi->flags & PMU_BATT_PRESENT); | 96 | val->intval = !!(pbi->flags & PMU_BATT_PRESENT); |
97 | break; | 97 | break; |
98 | case POWER_SUPPLY_PROP_MODEL_NAME: | 98 | case POWER_SUPPLY_PROP_MODEL_NAME: |
99 | val->strval = pmu_bat_get_model_name(pbi); | 99 | val->strval = pmu_bat_get_model_name(pbi); |
100 | break; | 100 | break; |
101 | case POWER_SUPPLY_PROP_ENERGY_AVG: | 101 | case POWER_SUPPLY_PROP_ENERGY_AVG: |
102 | val->intval = pbi->charge * 1000; /* mWh -> µWh */ | 102 | val->intval = pbi->charge * 1000; /* mWh -> µWh */ |
103 | break; | 103 | break; |
104 | case POWER_SUPPLY_PROP_ENERGY_FULL: | 104 | case POWER_SUPPLY_PROP_ENERGY_FULL: |
105 | val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ | 105 | val->intval = pbi->max_charge * 1000; /* mWh -> µWh */ |
106 | break; | 106 | break; |
107 | case POWER_SUPPLY_PROP_CURRENT_AVG: | 107 | case POWER_SUPPLY_PROP_CURRENT_AVG: |
108 | val->intval = pbi->amperage * 1000; /* mA -> µA */ | 108 | val->intval = pbi->amperage * 1000; /* mA -> µA */ |
109 | break; | 109 | break; |
110 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: | 110 | case POWER_SUPPLY_PROP_VOLTAGE_AVG: |
111 | val->intval = pbi->voltage * 1000; /* mV -> µV */ | 111 | val->intval = pbi->voltage * 1000; /* mV -> µV */ |
112 | break; | 112 | break; |
113 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: | 113 | case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: |
114 | val->intval = pbi->time_remaining; | 114 | val->intval = pbi->time_remaining; |
115 | break; | 115 | break; |
116 | default: | 116 | default: |
117 | return -EINVAL; | 117 | return -EINVAL; |
118 | } | 118 | } |
119 | 119 | ||
120 | return 0; | 120 | return 0; |
121 | } | 121 | } |
122 | 122 | ||
123 | static enum power_supply_property pmu_bat_props[] = { | 123 | static enum power_supply_property pmu_bat_props[] = { |
124 | POWER_SUPPLY_PROP_STATUS, | 124 | POWER_SUPPLY_PROP_STATUS, |
125 | POWER_SUPPLY_PROP_PRESENT, | 125 | POWER_SUPPLY_PROP_PRESENT, |
126 | POWER_SUPPLY_PROP_MODEL_NAME, | 126 | POWER_SUPPLY_PROP_MODEL_NAME, |
127 | POWER_SUPPLY_PROP_ENERGY_AVG, | 127 | POWER_SUPPLY_PROP_ENERGY_AVG, |
128 | POWER_SUPPLY_PROP_ENERGY_FULL, | 128 | POWER_SUPPLY_PROP_ENERGY_FULL, |
129 | POWER_SUPPLY_PROP_CURRENT_AVG, | 129 | POWER_SUPPLY_PROP_CURRENT_AVG, |
130 | POWER_SUPPLY_PROP_VOLTAGE_AVG, | 130 | POWER_SUPPLY_PROP_VOLTAGE_AVG, |
131 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, | 131 | POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, |
132 | }; | 132 | }; |
133 | 133 | ||
134 | /********************************************************************* | 134 | /********************************************************************* |
135 | * Initialisation | 135 | * Initialisation |
136 | *********************************************************************/ | 136 | *********************************************************************/ |
137 | 137 | ||
138 | static struct platform_device *bat_pdev; | 138 | static struct platform_device *bat_pdev; |
139 | 139 | ||
140 | static int __init pmu_bat_init(void) | 140 | static int __init pmu_bat_init(void) |
141 | { | 141 | { |
142 | int ret; | 142 | int ret; |
143 | int i; | 143 | int i; |
144 | 144 | ||
145 | bat_pdev = platform_device_register_simple("pmu-battery", | 145 | bat_pdev = platform_device_register_simple("pmu-battery", |
146 | 0, NULL, 0); | 146 | 0, NULL, 0); |
147 | if (IS_ERR(bat_pdev)) { | 147 | if (IS_ERR(bat_pdev)) { |
148 | ret = PTR_ERR(bat_pdev); | 148 | ret = PTR_ERR(bat_pdev); |
149 | goto pdev_register_failed; | 149 | goto pdev_register_failed; |
150 | } | 150 | } |
151 | 151 | ||
152 | ret = power_supply_register(&bat_pdev->dev, &pmu_ac); | 152 | ret = power_supply_register(&bat_pdev->dev, &pmu_ac); |
153 | if (ret) | 153 | if (ret) |
154 | goto ac_register_failed; | 154 | goto ac_register_failed; |
155 | 155 | ||
156 | for (i = 0; i < pmu_battery_count; i++) { | 156 | for (i = 0; i < pmu_battery_count; i++) { |
157 | struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), | 157 | struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat), |
158 | GFP_KERNEL); | 158 | GFP_KERNEL); |
159 | if (!pbat) | 159 | if (!pbat) |
160 | break; | 160 | break; |
161 | 161 | ||
162 | sprintf(pbat->name, "PMU battery %d", i); | 162 | sprintf(pbat->name, "PMU battery %d", i); |
163 | pbat->bat.name = pbat->name; | 163 | pbat->bat.name = pbat->name; |
164 | pbat->bat.properties = pmu_bat_props; | 164 | pbat->bat.properties = pmu_bat_props; |
165 | pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); | 165 | pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props); |
166 | pbat->bat.get_property = pmu_bat_get_property; | 166 | pbat->bat.get_property = pmu_bat_get_property; |
167 | pbat->pbi = &pmu_batteries[i]; | 167 | pbat->pbi = &pmu_batteries[i]; |
168 | 168 | ||
169 | ret = power_supply_register(&bat_pdev->dev, &pbat->bat); | 169 | ret = power_supply_register(&bat_pdev->dev, &pbat->bat); |
170 | if (ret) { | 170 | if (ret) { |
171 | kfree(pbat); | 171 | kfree(pbat); |
172 | goto battery_register_failed; | 172 | goto battery_register_failed; |
173 | } | 173 | } |
174 | pbats[i] = pbat; | 174 | pbats[i] = pbat; |
175 | } | 175 | } |
176 | 176 | ||
177 | goto success; | 177 | goto success; |
178 | 178 | ||
179 | battery_register_failed: | 179 | battery_register_failed: |
180 | while (i--) { | 180 | while (i--) { |
181 | if (!pbats[i]) | 181 | if (!pbats[i]) |
182 | continue; | 182 | continue; |
183 | power_supply_unregister(&pbats[i]->bat); | 183 | power_supply_unregister(&pbats[i]->bat); |
184 | kfree(pbats[i]); | 184 | kfree(pbats[i]); |
185 | } | 185 | } |
186 | power_supply_unregister(&pmu_ac); | 186 | power_supply_unregister(&pmu_ac); |
187 | ac_register_failed: | 187 | ac_register_failed: |
188 | platform_device_unregister(bat_pdev); | 188 | platform_device_unregister(bat_pdev); |
189 | pdev_register_failed: | 189 | pdev_register_failed: |
190 | success: | 190 | success: |
191 | return ret; | 191 | return ret; |
192 | } | 192 | } |
193 | 193 | ||
194 | static void __exit pmu_bat_exit(void) | 194 | static void __exit pmu_bat_exit(void) |
195 | { | 195 | { |
196 | int i; | 196 | int i; |
197 | 197 | ||
198 | for (i = 0; i < PMU_MAX_BATTERIES; i++) { | 198 | for (i = 0; i < PMU_MAX_BATTERIES; i++) { |
199 | if (!pbats[i]) | 199 | if (!pbats[i]) |
200 | continue; | 200 | continue; |
201 | power_supply_unregister(&pbats[i]->bat); | 201 | power_supply_unregister(&pbats[i]->bat); |
202 | kfree(pbats[i]); | 202 | kfree(pbats[i]); |
203 | } | 203 | } |
204 | power_supply_unregister(&pmu_ac); | 204 | power_supply_unregister(&pmu_ac); |
205 | platform_device_unregister(bat_pdev); | 205 | platform_device_unregister(bat_pdev); |
206 | |||
207 | return; | ||
208 | } | 206 | } |
209 | 207 | ||
210 | module_init(pmu_bat_init); | 208 | module_init(pmu_bat_init); |
211 | module_exit(pmu_bat_exit); | 209 | module_exit(pmu_bat_exit); |
212 | 210 | ||
213 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); | 211 | MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); |
214 | MODULE_LICENSE("GPL"); | 212 | MODULE_LICENSE("GPL"); |
215 | MODULE_DESCRIPTION("PMU battery driver"); | 213 | MODULE_DESCRIPTION("PMU battery driver"); |
216 | 214 |
drivers/power/power_supply_core.c
1 | /* | 1 | /* |
2 | * Universal power supply monitor class | 2 | * Universal power supply monitor class |
3 | * | 3 | * |
4 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | 4 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> |
5 | * Copyright © 2004 Szabolcs Gyurko | 5 | * Copyright © 2004 Szabolcs Gyurko |
6 | * Copyright © 2003 Ian Molton <spyro@f2s.com> | 6 | * Copyright © 2003 Ian Molton <spyro@f2s.com> |
7 | * | 7 | * |
8 | * Modified: 2004, Oct Szabolcs Gyurko | 8 | * Modified: 2004, Oct Szabolcs Gyurko |
9 | * | 9 | * |
10 | * You may use this code as per GPL version 2 | 10 | * You may use this code as per GPL version 2 |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/types.h> | 14 | #include <linux/types.h> |
15 | #include <linux/init.h> | 15 | #include <linux/init.h> |
16 | #include <linux/device.h> | 16 | #include <linux/device.h> |
17 | #include <linux/err.h> | 17 | #include <linux/err.h> |
18 | #include <linux/power_supply.h> | 18 | #include <linux/power_supply.h> |
19 | #include "power_supply.h" | 19 | #include "power_supply.h" |
20 | 20 | ||
21 | struct class *power_supply_class; | 21 | struct class *power_supply_class; |
22 | 22 | ||
23 | static void power_supply_changed_work(struct work_struct *work) | 23 | static void power_supply_changed_work(struct work_struct *work) |
24 | { | 24 | { |
25 | struct power_supply *psy = container_of(work, struct power_supply, | 25 | struct power_supply *psy = container_of(work, struct power_supply, |
26 | changed_work); | 26 | changed_work); |
27 | int i; | 27 | int i; |
28 | 28 | ||
29 | dev_dbg(psy->dev, "%s\n", __FUNCTION__); | 29 | dev_dbg(psy->dev, "%s\n", __FUNCTION__); |
30 | 30 | ||
31 | for (i = 0; i < psy->num_supplicants; i++) { | 31 | for (i = 0; i < psy->num_supplicants; i++) { |
32 | struct device *dev; | 32 | struct device *dev; |
33 | 33 | ||
34 | down(&power_supply_class->sem); | 34 | down(&power_supply_class->sem); |
35 | list_for_each_entry(dev, &power_supply_class->devices, node) { | 35 | list_for_each_entry(dev, &power_supply_class->devices, node) { |
36 | struct power_supply *pst = dev_get_drvdata(dev); | 36 | struct power_supply *pst = dev_get_drvdata(dev); |
37 | 37 | ||
38 | if (!strcmp(psy->supplied_to[i], pst->name)) { | 38 | if (!strcmp(psy->supplied_to[i], pst->name)) { |
39 | if (pst->external_power_changed) | 39 | if (pst->external_power_changed) |
40 | pst->external_power_changed(pst); | 40 | pst->external_power_changed(pst); |
41 | } | 41 | } |
42 | } | 42 | } |
43 | up(&power_supply_class->sem); | 43 | up(&power_supply_class->sem); |
44 | } | 44 | } |
45 | 45 | ||
46 | power_supply_update_leds(psy); | 46 | power_supply_update_leds(psy); |
47 | 47 | ||
48 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); | 48 | kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE); |
49 | |||
50 | return; | ||
51 | } | 49 | } |
52 | 50 | ||
53 | void power_supply_changed(struct power_supply *psy) | 51 | void power_supply_changed(struct power_supply *psy) |
54 | { | 52 | { |
55 | dev_dbg(psy->dev, "%s\n", __FUNCTION__); | 53 | dev_dbg(psy->dev, "%s\n", __FUNCTION__); |
56 | 54 | ||
57 | schedule_work(&psy->changed_work); | 55 | schedule_work(&psy->changed_work); |
58 | |||
59 | return; | ||
60 | } | 56 | } |
61 | 57 | ||
62 | int power_supply_am_i_supplied(struct power_supply *psy) | 58 | int power_supply_am_i_supplied(struct power_supply *psy) |
63 | { | 59 | { |
64 | union power_supply_propval ret = {0,}; | 60 | union power_supply_propval ret = {0,}; |
65 | struct device *dev; | 61 | struct device *dev; |
66 | 62 | ||
67 | down(&power_supply_class->sem); | 63 | down(&power_supply_class->sem); |
68 | list_for_each_entry(dev, &power_supply_class->devices, node) { | 64 | list_for_each_entry(dev, &power_supply_class->devices, node) { |
69 | struct power_supply *epsy = dev_get_drvdata(dev); | 65 | struct power_supply *epsy = dev_get_drvdata(dev); |
70 | int i; | 66 | int i; |
71 | 67 | ||
72 | for (i = 0; i < epsy->num_supplicants; i++) { | 68 | for (i = 0; i < epsy->num_supplicants; i++) { |
73 | if (!strcmp(epsy->supplied_to[i], psy->name)) { | 69 | if (!strcmp(epsy->supplied_to[i], psy->name)) { |
74 | if (epsy->get_property(epsy, | 70 | if (epsy->get_property(epsy, |
75 | POWER_SUPPLY_PROP_ONLINE, &ret)) | 71 | POWER_SUPPLY_PROP_ONLINE, &ret)) |
76 | continue; | 72 | continue; |
77 | if (ret.intval) | 73 | if (ret.intval) |
78 | goto out; | 74 | goto out; |
79 | } | 75 | } |
80 | } | 76 | } |
81 | } | 77 | } |
82 | out: | 78 | out: |
83 | up(&power_supply_class->sem); | 79 | up(&power_supply_class->sem); |
84 | 80 | ||
85 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval); | 81 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval); |
86 | 82 | ||
87 | return ret.intval; | 83 | return ret.intval; |
88 | } | 84 | } |
89 | 85 | ||
90 | int power_supply_register(struct device *parent, struct power_supply *psy) | 86 | int power_supply_register(struct device *parent, struct power_supply *psy) |
91 | { | 87 | { |
92 | int rc = 0; | 88 | int rc = 0; |
93 | 89 | ||
94 | psy->dev = device_create(power_supply_class, parent, 0, | 90 | psy->dev = device_create(power_supply_class, parent, 0, |
95 | "%s", psy->name); | 91 | "%s", psy->name); |
96 | if (IS_ERR(psy->dev)) { | 92 | if (IS_ERR(psy->dev)) { |
97 | rc = PTR_ERR(psy->dev); | 93 | rc = PTR_ERR(psy->dev); |
98 | goto dev_create_failed; | 94 | goto dev_create_failed; |
99 | } | 95 | } |
100 | 96 | ||
101 | dev_set_drvdata(psy->dev, psy); | 97 | dev_set_drvdata(psy->dev, psy); |
102 | 98 | ||
103 | INIT_WORK(&psy->changed_work, power_supply_changed_work); | 99 | INIT_WORK(&psy->changed_work, power_supply_changed_work); |
104 | 100 | ||
105 | rc = power_supply_create_attrs(psy); | 101 | rc = power_supply_create_attrs(psy); |
106 | if (rc) | 102 | if (rc) |
107 | goto create_attrs_failed; | 103 | goto create_attrs_failed; |
108 | 104 | ||
109 | rc = power_supply_create_triggers(psy); | 105 | rc = power_supply_create_triggers(psy); |
110 | if (rc) | 106 | if (rc) |
111 | goto create_triggers_failed; | 107 | goto create_triggers_failed; |
112 | 108 | ||
113 | power_supply_changed(psy); | 109 | power_supply_changed(psy); |
114 | 110 | ||
115 | goto success; | 111 | goto success; |
116 | 112 | ||
117 | create_triggers_failed: | 113 | create_triggers_failed: |
118 | power_supply_remove_attrs(psy); | 114 | power_supply_remove_attrs(psy); |
119 | create_attrs_failed: | 115 | create_attrs_failed: |
120 | device_unregister(psy->dev); | 116 | device_unregister(psy->dev); |
121 | dev_create_failed: | 117 | dev_create_failed: |
122 | success: | 118 | success: |
123 | return rc; | 119 | return rc; |
124 | } | 120 | } |
125 | 121 | ||
126 | void power_supply_unregister(struct power_supply *psy) | 122 | void power_supply_unregister(struct power_supply *psy) |
127 | { | 123 | { |
128 | flush_scheduled_work(); | 124 | flush_scheduled_work(); |
129 | power_supply_remove_triggers(psy); | 125 | power_supply_remove_triggers(psy); |
130 | power_supply_remove_attrs(psy); | 126 | power_supply_remove_attrs(psy); |
131 | device_unregister(psy->dev); | 127 | device_unregister(psy->dev); |
132 | return; | ||
133 | } | 128 | } |
134 | 129 | ||
135 | static int __init power_supply_class_init(void) | 130 | static int __init power_supply_class_init(void) |
136 | { | 131 | { |
137 | power_supply_class = class_create(THIS_MODULE, "power_supply"); | 132 | power_supply_class = class_create(THIS_MODULE, "power_supply"); |
138 | 133 | ||
139 | if (IS_ERR(power_supply_class)) | 134 | if (IS_ERR(power_supply_class)) |
140 | return PTR_ERR(power_supply_class); | 135 | return PTR_ERR(power_supply_class); |
141 | 136 | ||
142 | power_supply_class->dev_uevent = power_supply_uevent; | 137 | power_supply_class->dev_uevent = power_supply_uevent; |
143 | 138 | ||
144 | return 0; | 139 | return 0; |
145 | } | 140 | } |
146 | 141 | ||
147 | static void __exit power_supply_class_exit(void) | 142 | static void __exit power_supply_class_exit(void) |
148 | { | 143 | { |
149 | class_destroy(power_supply_class); | 144 | class_destroy(power_supply_class); |
150 | return; | ||
151 | } | 145 | } |
152 | 146 | ||
153 | EXPORT_SYMBOL_GPL(power_supply_changed); | 147 | EXPORT_SYMBOL_GPL(power_supply_changed); |
154 | EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); | 148 | EXPORT_SYMBOL_GPL(power_supply_am_i_supplied); |
155 | EXPORT_SYMBOL_GPL(power_supply_register); | 149 | EXPORT_SYMBOL_GPL(power_supply_register); |
156 | EXPORT_SYMBOL_GPL(power_supply_unregister); | 150 | EXPORT_SYMBOL_GPL(power_supply_unregister); |
157 | 151 | ||
158 | /* exported for the APM Power driver, APM emulation */ | 152 | /* exported for the APM Power driver, APM emulation */ |
159 | EXPORT_SYMBOL_GPL(power_supply_class); | 153 | EXPORT_SYMBOL_GPL(power_supply_class); |
160 | 154 | ||
161 | subsys_initcall(power_supply_class_init); | 155 | subsys_initcall(power_supply_class_init); |
162 | module_exit(power_supply_class_exit); | 156 | module_exit(power_supply_class_exit); |
163 | 157 | ||
164 | MODULE_DESCRIPTION("Universal power supply monitor class"); | 158 | MODULE_DESCRIPTION("Universal power supply monitor class"); |
165 | MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " | 159 | MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, " |
166 | "Szabolcs Gyurko, " | 160 | "Szabolcs Gyurko, " |
167 | "Anton Vorontsov <cbou@mail.ru>"); | 161 | "Anton Vorontsov <cbou@mail.ru>"); |
168 | MODULE_LICENSE("GPL"); | 162 | MODULE_LICENSE("GPL"); |
169 | 163 |
drivers/power/power_supply_leds.c
1 | /* | 1 | /* |
2 | * LEDs triggers for power supply class | 2 | * LEDs triggers for power supply class |
3 | * | 3 | * |
4 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | 4 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> |
5 | * Copyright © 2004 Szabolcs Gyurko | 5 | * Copyright © 2004 Szabolcs Gyurko |
6 | * Copyright © 2003 Ian Molton <spyro@f2s.com> | 6 | * Copyright © 2003 Ian Molton <spyro@f2s.com> |
7 | * | 7 | * |
8 | * Modified: 2004, Oct Szabolcs Gyurko | 8 | * Modified: 2004, Oct Szabolcs Gyurko |
9 | * | 9 | * |
10 | * You may use this code as per GPL version 2 | 10 | * You may use this code as per GPL version 2 |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/power_supply.h> | 13 | #include <linux/power_supply.h> |
14 | 14 | ||
15 | /* Battery specific LEDs triggers. */ | 15 | /* Battery specific LEDs triggers. */ |
16 | 16 | ||
17 | static void power_supply_update_bat_leds(struct power_supply *psy) | 17 | static void power_supply_update_bat_leds(struct power_supply *psy) |
18 | { | 18 | { |
19 | union power_supply_propval status; | 19 | union power_supply_propval status; |
20 | 20 | ||
21 | if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) | 21 | if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status)) |
22 | return; | 22 | return; |
23 | 23 | ||
24 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval); | 24 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval); |
25 | 25 | ||
26 | switch (status.intval) { | 26 | switch (status.intval) { |
27 | case POWER_SUPPLY_STATUS_FULL: | 27 | case POWER_SUPPLY_STATUS_FULL: |
28 | led_trigger_event(psy->charging_full_trig, LED_FULL); | 28 | led_trigger_event(psy->charging_full_trig, LED_FULL); |
29 | led_trigger_event(psy->charging_trig, LED_OFF); | 29 | led_trigger_event(psy->charging_trig, LED_OFF); |
30 | led_trigger_event(psy->full_trig, LED_FULL); | 30 | led_trigger_event(psy->full_trig, LED_FULL); |
31 | break; | 31 | break; |
32 | case POWER_SUPPLY_STATUS_CHARGING: | 32 | case POWER_SUPPLY_STATUS_CHARGING: |
33 | led_trigger_event(psy->charging_full_trig, LED_FULL); | 33 | led_trigger_event(psy->charging_full_trig, LED_FULL); |
34 | led_trigger_event(psy->charging_trig, LED_FULL); | 34 | led_trigger_event(psy->charging_trig, LED_FULL); |
35 | led_trigger_event(psy->full_trig, LED_OFF); | 35 | led_trigger_event(psy->full_trig, LED_OFF); |
36 | break; | 36 | break; |
37 | default: | 37 | default: |
38 | led_trigger_event(psy->charging_full_trig, LED_OFF); | 38 | led_trigger_event(psy->charging_full_trig, LED_OFF); |
39 | led_trigger_event(psy->charging_trig, LED_OFF); | 39 | led_trigger_event(psy->charging_trig, LED_OFF); |
40 | led_trigger_event(psy->full_trig, LED_OFF); | 40 | led_trigger_event(psy->full_trig, LED_OFF); |
41 | break; | 41 | break; |
42 | } | 42 | } |
43 | |||
44 | return; | ||
45 | } | 43 | } |
46 | 44 | ||
47 | static int power_supply_create_bat_triggers(struct power_supply *psy) | 45 | static int power_supply_create_bat_triggers(struct power_supply *psy) |
48 | { | 46 | { |
49 | int rc = 0; | 47 | int rc = 0; |
50 | 48 | ||
51 | psy->charging_full_trig_name = kmalloc(strlen(psy->name) + | 49 | psy->charging_full_trig_name = kmalloc(strlen(psy->name) + |
52 | sizeof("-charging-or-full"), GFP_KERNEL); | 50 | sizeof("-charging-or-full"), GFP_KERNEL); |
53 | if (!psy->charging_full_trig_name) | 51 | if (!psy->charging_full_trig_name) |
54 | goto charging_full_failed; | 52 | goto charging_full_failed; |
55 | 53 | ||
56 | psy->charging_trig_name = kmalloc(strlen(psy->name) + | 54 | psy->charging_trig_name = kmalloc(strlen(psy->name) + |
57 | sizeof("-charging"), GFP_KERNEL); | 55 | sizeof("-charging"), GFP_KERNEL); |
58 | if (!psy->charging_trig_name) | 56 | if (!psy->charging_trig_name) |
59 | goto charging_failed; | 57 | goto charging_failed; |
60 | 58 | ||
61 | psy->full_trig_name = kmalloc(strlen(psy->name) + | 59 | psy->full_trig_name = kmalloc(strlen(psy->name) + |
62 | sizeof("-full"), GFP_KERNEL); | 60 | sizeof("-full"), GFP_KERNEL); |
63 | if (!psy->full_trig_name) | 61 | if (!psy->full_trig_name) |
64 | goto full_failed; | 62 | goto full_failed; |
65 | 63 | ||
66 | strcpy(psy->charging_full_trig_name, psy->name); | 64 | strcpy(psy->charging_full_trig_name, psy->name); |
67 | strcat(psy->charging_full_trig_name, "-charging-or-full"); | 65 | strcat(psy->charging_full_trig_name, "-charging-or-full"); |
68 | strcpy(psy->charging_trig_name, psy->name); | 66 | strcpy(psy->charging_trig_name, psy->name); |
69 | strcat(psy->charging_trig_name, "-charging"); | 67 | strcat(psy->charging_trig_name, "-charging"); |
70 | strcpy(psy->full_trig_name, psy->name); | 68 | strcpy(psy->full_trig_name, psy->name); |
71 | strcat(psy->full_trig_name, "-full"); | 69 | strcat(psy->full_trig_name, "-full"); |
72 | 70 | ||
73 | led_trigger_register_simple(psy->charging_full_trig_name, | 71 | led_trigger_register_simple(psy->charging_full_trig_name, |
74 | &psy->charging_full_trig); | 72 | &psy->charging_full_trig); |
75 | led_trigger_register_simple(psy->charging_trig_name, | 73 | led_trigger_register_simple(psy->charging_trig_name, |
76 | &psy->charging_trig); | 74 | &psy->charging_trig); |
77 | led_trigger_register_simple(psy->full_trig_name, | 75 | led_trigger_register_simple(psy->full_trig_name, |
78 | &psy->full_trig); | 76 | &psy->full_trig); |
79 | 77 | ||
80 | goto success; | 78 | goto success; |
81 | 79 | ||
82 | full_failed: | 80 | full_failed: |
83 | kfree(psy->charging_trig_name); | 81 | kfree(psy->charging_trig_name); |
84 | charging_failed: | 82 | charging_failed: |
85 | kfree(psy->charging_full_trig_name); | 83 | kfree(psy->charging_full_trig_name); |
86 | charging_full_failed: | 84 | charging_full_failed: |
87 | rc = -ENOMEM; | 85 | rc = -ENOMEM; |
88 | success: | 86 | success: |
89 | return rc; | 87 | return rc; |
90 | } | 88 | } |
91 | 89 | ||
92 | static void power_supply_remove_bat_triggers(struct power_supply *psy) | 90 | static void power_supply_remove_bat_triggers(struct power_supply *psy) |
93 | { | 91 | { |
94 | led_trigger_unregister_simple(psy->charging_full_trig); | 92 | led_trigger_unregister_simple(psy->charging_full_trig); |
95 | led_trigger_unregister_simple(psy->charging_trig); | 93 | led_trigger_unregister_simple(psy->charging_trig); |
96 | led_trigger_unregister_simple(psy->full_trig); | 94 | led_trigger_unregister_simple(psy->full_trig); |
97 | kfree(psy->full_trig_name); | 95 | kfree(psy->full_trig_name); |
98 | kfree(psy->charging_trig_name); | 96 | kfree(psy->charging_trig_name); |
99 | kfree(psy->charging_full_trig_name); | 97 | kfree(psy->charging_full_trig_name); |
100 | return; | ||
101 | } | 98 | } |
102 | 99 | ||
103 | /* Generated power specific LEDs triggers. */ | 100 | /* Generated power specific LEDs triggers. */ |
104 | 101 | ||
105 | static void power_supply_update_gen_leds(struct power_supply *psy) | 102 | static void power_supply_update_gen_leds(struct power_supply *psy) |
106 | { | 103 | { |
107 | union power_supply_propval online; | 104 | union power_supply_propval online; |
108 | 105 | ||
109 | if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) | 106 | if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online)) |
110 | return; | 107 | return; |
111 | 108 | ||
112 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval); | 109 | dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval); |
113 | 110 | ||
114 | if (online.intval) | 111 | if (online.intval) |
115 | led_trigger_event(psy->online_trig, LED_FULL); | 112 | led_trigger_event(psy->online_trig, LED_FULL); |
116 | else | 113 | else |
117 | led_trigger_event(psy->online_trig, LED_OFF); | 114 | led_trigger_event(psy->online_trig, LED_OFF); |
118 | |||
119 | return; | ||
120 | } | 115 | } |
121 | 116 | ||
122 | static int power_supply_create_gen_triggers(struct power_supply *psy) | 117 | static int power_supply_create_gen_triggers(struct power_supply *psy) |
123 | { | 118 | { |
124 | int rc = 0; | 119 | int rc = 0; |
125 | 120 | ||
126 | psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), | 121 | psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"), |
127 | GFP_KERNEL); | 122 | GFP_KERNEL); |
128 | if (!psy->online_trig_name) | 123 | if (!psy->online_trig_name) |
129 | goto online_failed; | 124 | goto online_failed; |
130 | 125 | ||
131 | strcpy(psy->online_trig_name, psy->name); | 126 | strcpy(psy->online_trig_name, psy->name); |
132 | strcat(psy->online_trig_name, "-online"); | 127 | strcat(psy->online_trig_name, "-online"); |
133 | 128 | ||
134 | led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); | 129 | led_trigger_register_simple(psy->online_trig_name, &psy->online_trig); |
135 | 130 | ||
136 | goto success; | 131 | goto success; |
137 | 132 | ||
138 | online_failed: | 133 | online_failed: |
139 | rc = -ENOMEM; | 134 | rc = -ENOMEM; |
140 | success: | 135 | success: |
141 | return rc; | 136 | return rc; |
142 | } | 137 | } |
143 | 138 | ||
144 | static void power_supply_remove_gen_triggers(struct power_supply *psy) | 139 | static void power_supply_remove_gen_triggers(struct power_supply *psy) |
145 | { | 140 | { |
146 | led_trigger_unregister_simple(psy->online_trig); | 141 | led_trigger_unregister_simple(psy->online_trig); |
147 | kfree(psy->online_trig_name); | 142 | kfree(psy->online_trig_name); |
148 | return; | ||
149 | } | 143 | } |
150 | 144 | ||
151 | /* Choice what triggers to create&update. */ | 145 | /* Choice what triggers to create&update. */ |
152 | 146 | ||
153 | void power_supply_update_leds(struct power_supply *psy) | 147 | void power_supply_update_leds(struct power_supply *psy) |
154 | { | 148 | { |
155 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) | 149 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) |
156 | power_supply_update_bat_leds(psy); | 150 | power_supply_update_bat_leds(psy); |
157 | else | 151 | else |
158 | power_supply_update_gen_leds(psy); | 152 | power_supply_update_gen_leds(psy); |
159 | return; | ||
160 | } | 153 | } |
161 | 154 | ||
162 | int power_supply_create_triggers(struct power_supply *psy) | 155 | int power_supply_create_triggers(struct power_supply *psy) |
163 | { | 156 | { |
164 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) | 157 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) |
165 | return power_supply_create_bat_triggers(psy); | 158 | return power_supply_create_bat_triggers(psy); |
166 | return power_supply_create_gen_triggers(psy); | 159 | return power_supply_create_gen_triggers(psy); |
167 | } | 160 | } |
168 | 161 | ||
169 | void power_supply_remove_triggers(struct power_supply *psy) | 162 | void power_supply_remove_triggers(struct power_supply *psy) |
170 | { | 163 | { |
171 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) | 164 | if (psy->type == POWER_SUPPLY_TYPE_BATTERY) |
172 | power_supply_remove_bat_triggers(psy); | 165 | power_supply_remove_bat_triggers(psy); |
173 | else | 166 | else |
174 | power_supply_remove_gen_triggers(psy); | 167 | power_supply_remove_gen_triggers(psy); |
175 | return; | ||
176 | } | 168 | } |
177 | 169 |
drivers/power/power_supply_sysfs.c
1 | /* | 1 | /* |
2 | * Sysfs interface for the universal power supply monitor class | 2 | * Sysfs interface for the universal power supply monitor class |
3 | * | 3 | * |
4 | * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> | 4 | * Copyright © 2007 David Woodhouse <dwmw2@infradead.org> |
5 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> | 5 | * Copyright © 2007 Anton Vorontsov <cbou@mail.ru> |
6 | * Copyright © 2004 Szabolcs Gyurko | 6 | * Copyright © 2004 Szabolcs Gyurko |
7 | * Copyright © 2003 Ian Molton <spyro@f2s.com> | 7 | * Copyright © 2003 Ian Molton <spyro@f2s.com> |
8 | * | 8 | * |
9 | * Modified: 2004, Oct Szabolcs Gyurko | 9 | * Modified: 2004, Oct Szabolcs Gyurko |
10 | * | 10 | * |
11 | * You may use this code as per GPL version 2 | 11 | * You may use this code as per GPL version 2 |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include <linux/ctype.h> | 14 | #include <linux/ctype.h> |
15 | #include <linux/power_supply.h> | 15 | #include <linux/power_supply.h> |
16 | 16 | ||
17 | /* | 17 | /* |
18 | * This is because the name "current" breaks the device attr macro. | 18 | * This is because the name "current" breaks the device attr macro. |
19 | * The "current" word resolves to "(get_current())" so instead of | 19 | * The "current" word resolves to "(get_current())" so instead of |
20 | * "current" "(get_current())" appears in the sysfs. | 20 | * "current" "(get_current())" appears in the sysfs. |
21 | * | 21 | * |
22 | * The source of this definition is the device.h which calls __ATTR | 22 | * The source of this definition is the device.h which calls __ATTR |
23 | * macro in sysfs.h which calls the __stringify macro. | 23 | * macro in sysfs.h which calls the __stringify macro. |
24 | * | 24 | * |
25 | * Only modification that the name is not tried to be resolved | 25 | * Only modification that the name is not tried to be resolved |
26 | * (as a macro let's say). | 26 | * (as a macro let's say). |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #define POWER_SUPPLY_ATTR(_name) \ | 29 | #define POWER_SUPPLY_ATTR(_name) \ |
30 | { \ | 30 | { \ |
31 | .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ | 31 | .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \ |
32 | .show = power_supply_show_property, \ | 32 | .show = power_supply_show_property, \ |
33 | .store = NULL, \ | 33 | .store = NULL, \ |
34 | } | 34 | } |
35 | 35 | ||
36 | static struct device_attribute power_supply_attrs[]; | 36 | static struct device_attribute power_supply_attrs[]; |
37 | 37 | ||
38 | static ssize_t power_supply_show_property(struct device *dev, | 38 | static ssize_t power_supply_show_property(struct device *dev, |
39 | struct device_attribute *attr, | 39 | struct device_attribute *attr, |
40 | char *buf) { | 40 | char *buf) { |
41 | static char *status_text[] = { | 41 | static char *status_text[] = { |
42 | "Unknown", "Charging", "Discharging", "Not charging", "Full" | 42 | "Unknown", "Charging", "Discharging", "Not charging", "Full" |
43 | }; | 43 | }; |
44 | static char *health_text[] = { | 44 | static char *health_text[] = { |
45 | "Unknown", "Good", "Overheat", "Dead", "Over voltage", | 45 | "Unknown", "Good", "Overheat", "Dead", "Over voltage", |
46 | "Unspecified failure" | 46 | "Unspecified failure" |
47 | }; | 47 | }; |
48 | static char *technology_text[] = { | 48 | static char *technology_text[] = { |
49 | "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd" | 49 | "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd" |
50 | }; | 50 | }; |
51 | static char *capacity_level_text[] = { | 51 | static char *capacity_level_text[] = { |
52 | "Unknown", "Critical", "Low", "Normal", "High", "Full" | 52 | "Unknown", "Critical", "Low", "Normal", "High", "Full" |
53 | }; | 53 | }; |
54 | ssize_t ret; | 54 | ssize_t ret; |
55 | struct power_supply *psy = dev_get_drvdata(dev); | 55 | struct power_supply *psy = dev_get_drvdata(dev); |
56 | const ptrdiff_t off = attr - power_supply_attrs; | 56 | const ptrdiff_t off = attr - power_supply_attrs; |
57 | union power_supply_propval value; | 57 | union power_supply_propval value; |
58 | 58 | ||
59 | ret = psy->get_property(psy, off, &value); | 59 | ret = psy->get_property(psy, off, &value); |
60 | 60 | ||
61 | if (ret < 0) { | 61 | if (ret < 0) { |
62 | if (ret != -ENODEV) | 62 | if (ret != -ENODEV) |
63 | dev_err(dev, "driver failed to report `%s' property\n", | 63 | dev_err(dev, "driver failed to report `%s' property\n", |
64 | attr->attr.name); | 64 | attr->attr.name); |
65 | return ret; | 65 | return ret; |
66 | } | 66 | } |
67 | 67 | ||
68 | if (off == POWER_SUPPLY_PROP_STATUS) | 68 | if (off == POWER_SUPPLY_PROP_STATUS) |
69 | return sprintf(buf, "%s\n", status_text[value.intval]); | 69 | return sprintf(buf, "%s\n", status_text[value.intval]); |
70 | else if (off == POWER_SUPPLY_PROP_HEALTH) | 70 | else if (off == POWER_SUPPLY_PROP_HEALTH) |
71 | return sprintf(buf, "%s\n", health_text[value.intval]); | 71 | return sprintf(buf, "%s\n", health_text[value.intval]); |
72 | else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) | 72 | else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) |
73 | return sprintf(buf, "%s\n", technology_text[value.intval]); | 73 | return sprintf(buf, "%s\n", technology_text[value.intval]); |
74 | else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) | 74 | else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) |
75 | return sprintf(buf, "%s\n", | 75 | return sprintf(buf, "%s\n", |
76 | capacity_level_text[value.intval]); | 76 | capacity_level_text[value.intval]); |
77 | else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) | 77 | else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) |
78 | return sprintf(buf, "%s\n", value.strval); | 78 | return sprintf(buf, "%s\n", value.strval); |
79 | 79 | ||
80 | return sprintf(buf, "%d\n", value.intval); | 80 | return sprintf(buf, "%d\n", value.intval); |
81 | } | 81 | } |
82 | 82 | ||
83 | /* Must be in the same order as POWER_SUPPLY_PROP_* */ | 83 | /* Must be in the same order as POWER_SUPPLY_PROP_* */ |
84 | static struct device_attribute power_supply_attrs[] = { | 84 | static struct device_attribute power_supply_attrs[] = { |
85 | /* Properties of type `int' */ | 85 | /* Properties of type `int' */ |
86 | POWER_SUPPLY_ATTR(status), | 86 | POWER_SUPPLY_ATTR(status), |
87 | POWER_SUPPLY_ATTR(health), | 87 | POWER_SUPPLY_ATTR(health), |
88 | POWER_SUPPLY_ATTR(present), | 88 | POWER_SUPPLY_ATTR(present), |
89 | POWER_SUPPLY_ATTR(online), | 89 | POWER_SUPPLY_ATTR(online), |
90 | POWER_SUPPLY_ATTR(technology), | 90 | POWER_SUPPLY_ATTR(technology), |
91 | POWER_SUPPLY_ATTR(voltage_max_design), | 91 | POWER_SUPPLY_ATTR(voltage_max_design), |
92 | POWER_SUPPLY_ATTR(voltage_min_design), | 92 | POWER_SUPPLY_ATTR(voltage_min_design), |
93 | POWER_SUPPLY_ATTR(voltage_now), | 93 | POWER_SUPPLY_ATTR(voltage_now), |
94 | POWER_SUPPLY_ATTR(voltage_avg), | 94 | POWER_SUPPLY_ATTR(voltage_avg), |
95 | POWER_SUPPLY_ATTR(current_now), | 95 | POWER_SUPPLY_ATTR(current_now), |
96 | POWER_SUPPLY_ATTR(current_avg), | 96 | POWER_SUPPLY_ATTR(current_avg), |
97 | POWER_SUPPLY_ATTR(charge_full_design), | 97 | POWER_SUPPLY_ATTR(charge_full_design), |
98 | POWER_SUPPLY_ATTR(charge_empty_design), | 98 | POWER_SUPPLY_ATTR(charge_empty_design), |
99 | POWER_SUPPLY_ATTR(charge_full), | 99 | POWER_SUPPLY_ATTR(charge_full), |
100 | POWER_SUPPLY_ATTR(charge_empty), | 100 | POWER_SUPPLY_ATTR(charge_empty), |
101 | POWER_SUPPLY_ATTR(charge_now), | 101 | POWER_SUPPLY_ATTR(charge_now), |
102 | POWER_SUPPLY_ATTR(charge_avg), | 102 | POWER_SUPPLY_ATTR(charge_avg), |
103 | POWER_SUPPLY_ATTR(energy_full_design), | 103 | POWER_SUPPLY_ATTR(energy_full_design), |
104 | POWER_SUPPLY_ATTR(energy_empty_design), | 104 | POWER_SUPPLY_ATTR(energy_empty_design), |
105 | POWER_SUPPLY_ATTR(energy_full), | 105 | POWER_SUPPLY_ATTR(energy_full), |
106 | POWER_SUPPLY_ATTR(energy_empty), | 106 | POWER_SUPPLY_ATTR(energy_empty), |
107 | POWER_SUPPLY_ATTR(energy_now), | 107 | POWER_SUPPLY_ATTR(energy_now), |
108 | POWER_SUPPLY_ATTR(energy_avg), | 108 | POWER_SUPPLY_ATTR(energy_avg), |
109 | POWER_SUPPLY_ATTR(capacity), | 109 | POWER_SUPPLY_ATTR(capacity), |
110 | POWER_SUPPLY_ATTR(capacity_level), | 110 | POWER_SUPPLY_ATTR(capacity_level), |
111 | POWER_SUPPLY_ATTR(temp), | 111 | POWER_SUPPLY_ATTR(temp), |
112 | POWER_SUPPLY_ATTR(temp_ambient), | 112 | POWER_SUPPLY_ATTR(temp_ambient), |
113 | POWER_SUPPLY_ATTR(time_to_empty_now), | 113 | POWER_SUPPLY_ATTR(time_to_empty_now), |
114 | POWER_SUPPLY_ATTR(time_to_empty_avg), | 114 | POWER_SUPPLY_ATTR(time_to_empty_avg), |
115 | POWER_SUPPLY_ATTR(time_to_full_now), | 115 | POWER_SUPPLY_ATTR(time_to_full_now), |
116 | POWER_SUPPLY_ATTR(time_to_full_avg), | 116 | POWER_SUPPLY_ATTR(time_to_full_avg), |
117 | /* Properties of type `const char *' */ | 117 | /* Properties of type `const char *' */ |
118 | POWER_SUPPLY_ATTR(model_name), | 118 | POWER_SUPPLY_ATTR(model_name), |
119 | POWER_SUPPLY_ATTR(manufacturer), | 119 | POWER_SUPPLY_ATTR(manufacturer), |
120 | }; | 120 | }; |
121 | 121 | ||
122 | static ssize_t power_supply_show_static_attrs(struct device *dev, | 122 | static ssize_t power_supply_show_static_attrs(struct device *dev, |
123 | struct device_attribute *attr, | 123 | struct device_attribute *attr, |
124 | char *buf) { | 124 | char *buf) { |
125 | static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; | 125 | static char *type_text[] = { "Battery", "UPS", "Mains", "USB" }; |
126 | struct power_supply *psy = dev_get_drvdata(dev); | 126 | struct power_supply *psy = dev_get_drvdata(dev); |
127 | 127 | ||
128 | return sprintf(buf, "%s\n", type_text[psy->type]); | 128 | return sprintf(buf, "%s\n", type_text[psy->type]); |
129 | } | 129 | } |
130 | 130 | ||
131 | static struct device_attribute power_supply_static_attrs[] = { | 131 | static struct device_attribute power_supply_static_attrs[] = { |
132 | __ATTR(type, 0444, power_supply_show_static_attrs, NULL), | 132 | __ATTR(type, 0444, power_supply_show_static_attrs, NULL), |
133 | }; | 133 | }; |
134 | 134 | ||
135 | int power_supply_create_attrs(struct power_supply *psy) | 135 | int power_supply_create_attrs(struct power_supply *psy) |
136 | { | 136 | { |
137 | int rc = 0; | 137 | int rc = 0; |
138 | int i, j; | 138 | int i, j; |
139 | 139 | ||
140 | for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { | 140 | for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) { |
141 | rc = device_create_file(psy->dev, | 141 | rc = device_create_file(psy->dev, |
142 | &power_supply_static_attrs[i]); | 142 | &power_supply_static_attrs[i]); |
143 | if (rc) | 143 | if (rc) |
144 | goto statics_failed; | 144 | goto statics_failed; |
145 | } | 145 | } |
146 | 146 | ||
147 | for (j = 0; j < psy->num_properties; j++) { | 147 | for (j = 0; j < psy->num_properties; j++) { |
148 | rc = device_create_file(psy->dev, | 148 | rc = device_create_file(psy->dev, |
149 | &power_supply_attrs[psy->properties[j]]); | 149 | &power_supply_attrs[psy->properties[j]]); |
150 | if (rc) | 150 | if (rc) |
151 | goto dynamics_failed; | 151 | goto dynamics_failed; |
152 | } | 152 | } |
153 | 153 | ||
154 | goto succeed; | 154 | goto succeed; |
155 | 155 | ||
156 | dynamics_failed: | 156 | dynamics_failed: |
157 | while (j--) | 157 | while (j--) |
158 | device_remove_file(psy->dev, | 158 | device_remove_file(psy->dev, |
159 | &power_supply_attrs[psy->properties[j]]); | 159 | &power_supply_attrs[psy->properties[j]]); |
160 | statics_failed: | 160 | statics_failed: |
161 | while (i--) | 161 | while (i--) |
162 | device_remove_file(psy->dev, | 162 | device_remove_file(psy->dev, |
163 | &power_supply_static_attrs[psy->properties[i]]); | 163 | &power_supply_static_attrs[psy->properties[i]]); |
164 | succeed: | 164 | succeed: |
165 | return rc; | 165 | return rc; |
166 | } | 166 | } |
167 | 167 | ||
168 | void power_supply_remove_attrs(struct power_supply *psy) | 168 | void power_supply_remove_attrs(struct power_supply *psy) |
169 | { | 169 | { |
170 | int i; | 170 | int i; |
171 | 171 | ||
172 | for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) | 172 | for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) |
173 | device_remove_file(psy->dev, | 173 | device_remove_file(psy->dev, |
174 | &power_supply_static_attrs[i]); | 174 | &power_supply_static_attrs[i]); |
175 | 175 | ||
176 | for (i = 0; i < psy->num_properties; i++) | 176 | for (i = 0; i < psy->num_properties; i++) |
177 | device_remove_file(psy->dev, | 177 | device_remove_file(psy->dev, |
178 | &power_supply_attrs[psy->properties[i]]); | 178 | &power_supply_attrs[psy->properties[i]]); |
179 | |||
180 | return; | ||
181 | } | 179 | } |
182 | 180 | ||
183 | static char *kstruprdup(const char *str, gfp_t gfp) | 181 | static char *kstruprdup(const char *str, gfp_t gfp) |
184 | { | 182 | { |
185 | char *ret, *ustr; | 183 | char *ret, *ustr; |
186 | 184 | ||
187 | ustr = ret = kmalloc(strlen(str) + 1, gfp); | 185 | ustr = ret = kmalloc(strlen(str) + 1, gfp); |
188 | 186 | ||
189 | if (!ret) | 187 | if (!ret) |
190 | return NULL; | 188 | return NULL; |
191 | 189 | ||
192 | while (*str) | 190 | while (*str) |
193 | *ustr++ = toupper(*str++); | 191 | *ustr++ = toupper(*str++); |
194 | 192 | ||
195 | *ustr = 0; | 193 | *ustr = 0; |
196 | 194 | ||
197 | return ret; | 195 | return ret; |
198 | } | 196 | } |
199 | 197 | ||
200 | int power_supply_uevent(struct device *dev, char **envp, int num_envp, | 198 | int power_supply_uevent(struct device *dev, char **envp, int num_envp, |
201 | char *buffer, int buffer_size) | 199 | char *buffer, int buffer_size) |
202 | { | 200 | { |
203 | struct power_supply *psy = dev_get_drvdata(dev); | 201 | struct power_supply *psy = dev_get_drvdata(dev); |
204 | int i = 0, length = 0, ret = 0, j; | 202 | int i = 0, length = 0, ret = 0, j; |
205 | char *prop_buf; | 203 | char *prop_buf; |
206 | char *attrname; | 204 | char *attrname; |
207 | 205 | ||
208 | dev_dbg(dev, "uevent\n"); | 206 | dev_dbg(dev, "uevent\n"); |
209 | 207 | ||
210 | if (!psy) { | 208 | if (!psy) { |
211 | dev_dbg(dev, "No power supply yet\n"); | 209 | dev_dbg(dev, "No power supply yet\n"); |
212 | return ret; | 210 | return ret; |
213 | } | 211 | } |
214 | 212 | ||
215 | dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); | 213 | dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name); |
216 | 214 | ||
217 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, | 215 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, |
218 | &length, "POWER_SUPPLY_NAME=%s", psy->name); | 216 | &length, "POWER_SUPPLY_NAME=%s", psy->name); |
219 | if (ret) | 217 | if (ret) |
220 | return ret; | 218 | return ret; |
221 | 219 | ||
222 | prop_buf = (char *)get_zeroed_page(GFP_KERNEL); | 220 | prop_buf = (char *)get_zeroed_page(GFP_KERNEL); |
223 | if (!prop_buf) | 221 | if (!prop_buf) |
224 | return -ENOMEM; | 222 | return -ENOMEM; |
225 | 223 | ||
226 | for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { | 224 | for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) { |
227 | struct device_attribute *attr; | 225 | struct device_attribute *attr; |
228 | char *line; | 226 | char *line; |
229 | 227 | ||
230 | attr = &power_supply_static_attrs[j]; | 228 | attr = &power_supply_static_attrs[j]; |
231 | 229 | ||
232 | ret = power_supply_show_static_attrs(dev, attr, prop_buf); | 230 | ret = power_supply_show_static_attrs(dev, attr, prop_buf); |
233 | if (ret < 0) | 231 | if (ret < 0) |
234 | goto out; | 232 | goto out; |
235 | 233 | ||
236 | line = strchr(prop_buf, '\n'); | 234 | line = strchr(prop_buf, '\n'); |
237 | if (line) | 235 | if (line) |
238 | *line = 0; | 236 | *line = 0; |
239 | 237 | ||
240 | attrname = kstruprdup(attr->attr.name, GFP_KERNEL); | 238 | attrname = kstruprdup(attr->attr.name, GFP_KERNEL); |
241 | if (!attrname) { | 239 | if (!attrname) { |
242 | ret = -ENOMEM; | 240 | ret = -ENOMEM; |
243 | goto out; | 241 | goto out; |
244 | } | 242 | } |
245 | 243 | ||
246 | dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); | 244 | dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf); |
247 | 245 | ||
248 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, | 246 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, |
249 | &length, "POWER_SUPPLY_%s=%s", | 247 | &length, "POWER_SUPPLY_%s=%s", |
250 | attrname, prop_buf); | 248 | attrname, prop_buf); |
251 | kfree(attrname); | 249 | kfree(attrname); |
252 | if (ret) | 250 | if (ret) |
253 | goto out; | 251 | goto out; |
254 | } | 252 | } |
255 | 253 | ||
256 | dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); | 254 | dev_dbg(dev, "%zd dynamic props\n", psy->num_properties); |
257 | 255 | ||
258 | for (j = 0; j < psy->num_properties; j++) { | 256 | for (j = 0; j < psy->num_properties; j++) { |
259 | struct device_attribute *attr; | 257 | struct device_attribute *attr; |
260 | char *line; | 258 | char *line; |
261 | 259 | ||
262 | attr = &power_supply_attrs[psy->properties[j]]; | 260 | attr = &power_supply_attrs[psy->properties[j]]; |
263 | 261 | ||
264 | ret = power_supply_show_property(dev, attr, prop_buf); | 262 | ret = power_supply_show_property(dev, attr, prop_buf); |
265 | if (ret == -ENODEV) { | 263 | if (ret == -ENODEV) { |
266 | /* When a battery is absent, we expect -ENODEV. Don't abort; | 264 | /* When a battery is absent, we expect -ENODEV. Don't abort; |
267 | send the uevent with at least the the PRESENT=0 property */ | 265 | send the uevent with at least the the PRESENT=0 property */ |
268 | ret = 0; | 266 | ret = 0; |
269 | continue; | 267 | continue; |
270 | } | 268 | } |
271 | 269 | ||
272 | if (ret < 0) | 270 | if (ret < 0) |
273 | goto out; | 271 | goto out; |
274 | 272 | ||
275 | line = strchr(prop_buf, '\n'); | 273 | line = strchr(prop_buf, '\n'); |
276 | if (line) | 274 | if (line) |
277 | *line = 0; | 275 | *line = 0; |
278 | 276 | ||
279 | attrname = kstruprdup(attr->attr.name, GFP_KERNEL); | 277 | attrname = kstruprdup(attr->attr.name, GFP_KERNEL); |
280 | if (!attrname) { | 278 | if (!attrname) { |
281 | ret = -ENOMEM; | 279 | ret = -ENOMEM; |
282 | goto out; | 280 | goto out; |
283 | } | 281 | } |
284 | 282 | ||
285 | dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); | 283 | dev_dbg(dev, "prop %s=%s\n", attrname, prop_buf); |
286 | 284 | ||
287 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, | 285 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, |
288 | &length, "POWER_SUPPLY_%s=%s", | 286 | &length, "POWER_SUPPLY_%s=%s", |
289 | attrname, prop_buf); | 287 | attrname, prop_buf); |
290 | kfree(attrname); | 288 | kfree(attrname); |
291 | if (ret) | 289 | if (ret) |
292 | goto out; | 290 | goto out; |
293 | } | 291 | } |
294 | 292 | ||
295 | out: | 293 | out: |
296 | free_page((unsigned long)prop_buf); | 294 | free_page((unsigned long)prop_buf); |
297 | 295 | ||
298 | return ret; | 296 | return ret; |
299 | } | 297 | } |
300 | 298 |
drivers/w1/slaves/w1_ds2760.c
1 | /* | 1 | /* |
2 | * 1-Wire implementation for the ds2760 chip | 2 | * 1-Wire implementation for the ds2760 chip |
3 | * | 3 | * |
4 | * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> | 4 | * Copyright © 2004-2005, Szabolcs Gyurko <szabolcs.gyurko@tlt.hu> |
5 | * | 5 | * |
6 | * Use consistent with the GNU GPL is permitted, | 6 | * Use consistent with the GNU GPL is permitted, |
7 | * provided that this copyright notice is | 7 | * provided that this copyright notice is |
8 | * preserved in its entirety in all copies and derived works. | 8 | * preserved in its entirety in all copies and derived works. |
9 | * | 9 | * |
10 | */ | 10 | */ |
11 | 11 | ||
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/types.h> | 15 | #include <linux/types.h> |
16 | #include <linux/platform_device.h> | 16 | #include <linux/platform_device.h> |
17 | #include <linux/mutex.h> | 17 | #include <linux/mutex.h> |
18 | #include <linux/idr.h> | 18 | #include <linux/idr.h> |
19 | 19 | ||
20 | #include "../w1.h" | 20 | #include "../w1.h" |
21 | #include "../w1_int.h" | 21 | #include "../w1_int.h" |
22 | #include "../w1_family.h" | 22 | #include "../w1_family.h" |
23 | #include "w1_ds2760.h" | 23 | #include "w1_ds2760.h" |
24 | 24 | ||
25 | static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, | 25 | static int w1_ds2760_io(struct device *dev, char *buf, int addr, size_t count, |
26 | int io) | 26 | int io) |
27 | { | 27 | { |
28 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); | 28 | struct w1_slave *sl = container_of(dev, struct w1_slave, dev); |
29 | 29 | ||
30 | if (!dev) | 30 | if (!dev) |
31 | return 0; | 31 | return 0; |
32 | 32 | ||
33 | mutex_lock(&sl->master->mutex); | 33 | mutex_lock(&sl->master->mutex); |
34 | 34 | ||
35 | if (addr > DS2760_DATA_SIZE || addr < 0) { | 35 | if (addr > DS2760_DATA_SIZE || addr < 0) { |
36 | count = 0; | 36 | count = 0; |
37 | goto out; | 37 | goto out; |
38 | } | 38 | } |
39 | if (addr + count > DS2760_DATA_SIZE) | 39 | if (addr + count > DS2760_DATA_SIZE) |
40 | count = DS2760_DATA_SIZE - addr; | 40 | count = DS2760_DATA_SIZE - addr; |
41 | 41 | ||
42 | if (!w1_reset_select_slave(sl)) { | 42 | if (!w1_reset_select_slave(sl)) { |
43 | if (!io) { | 43 | if (!io) { |
44 | w1_write_8(sl->master, W1_DS2760_READ_DATA); | 44 | w1_write_8(sl->master, W1_DS2760_READ_DATA); |
45 | w1_write_8(sl->master, addr); | 45 | w1_write_8(sl->master, addr); |
46 | count = w1_read_block(sl->master, buf, count); | 46 | count = w1_read_block(sl->master, buf, count); |
47 | } else { | 47 | } else { |
48 | w1_write_8(sl->master, W1_DS2760_WRITE_DATA); | 48 | w1_write_8(sl->master, W1_DS2760_WRITE_DATA); |
49 | w1_write_8(sl->master, addr); | 49 | w1_write_8(sl->master, addr); |
50 | w1_write_block(sl->master, buf, count); | 50 | w1_write_block(sl->master, buf, count); |
51 | /* XXX w1_write_block returns void, not n_written */ | 51 | /* XXX w1_write_block returns void, not n_written */ |
52 | } | 52 | } |
53 | } | 53 | } |
54 | 54 | ||
55 | out: | 55 | out: |
56 | mutex_unlock(&sl->master->mutex); | 56 | mutex_unlock(&sl->master->mutex); |
57 | 57 | ||
58 | return count; | 58 | return count; |
59 | } | 59 | } |
60 | 60 | ||
61 | int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count) | 61 | int w1_ds2760_read(struct device *dev, char *buf, int addr, size_t count) |
62 | { | 62 | { |
63 | return w1_ds2760_io(dev, buf, addr, count, 0); | 63 | return w1_ds2760_io(dev, buf, addr, count, 0); |
64 | } | 64 | } |
65 | 65 | ||
66 | int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count) | 66 | int w1_ds2760_write(struct device *dev, char *buf, int addr, size_t count) |
67 | { | 67 | { |
68 | return w1_ds2760_io(dev, buf, addr, count, 1); | 68 | return w1_ds2760_io(dev, buf, addr, count, 1); |
69 | } | 69 | } |
70 | 70 | ||
71 | static ssize_t w1_ds2760_read_bin(struct kobject *kobj, char *buf, loff_t off, | 71 | static ssize_t w1_ds2760_read_bin(struct kobject *kobj, char *buf, loff_t off, |
72 | size_t count) | 72 | size_t count) |
73 | { | 73 | { |
74 | struct device *dev = container_of(kobj, struct device, kobj); | 74 | struct device *dev = container_of(kobj, struct device, kobj); |
75 | return w1_ds2760_read(dev, buf, off, count); | 75 | return w1_ds2760_read(dev, buf, off, count); |
76 | } | 76 | } |
77 | 77 | ||
78 | static struct bin_attribute w1_ds2760_bin_attr = { | 78 | static struct bin_attribute w1_ds2760_bin_attr = { |
79 | .attr = { | 79 | .attr = { |
80 | .name = "w1_slave", | 80 | .name = "w1_slave", |
81 | .mode = S_IRUGO, | 81 | .mode = S_IRUGO, |
82 | .owner = THIS_MODULE, | 82 | .owner = THIS_MODULE, |
83 | }, | 83 | }, |
84 | .size = DS2760_DATA_SIZE, | 84 | .size = DS2760_DATA_SIZE, |
85 | .read = w1_ds2760_read_bin, | 85 | .read = w1_ds2760_read_bin, |
86 | }; | 86 | }; |
87 | 87 | ||
88 | static DEFINE_IDR(bat_idr); | 88 | static DEFINE_IDR(bat_idr); |
89 | static DEFINE_MUTEX(bat_idr_lock); | 89 | static DEFINE_MUTEX(bat_idr_lock); |
90 | 90 | ||
91 | static int new_bat_id(void) | 91 | static int new_bat_id(void) |
92 | { | 92 | { |
93 | int ret; | 93 | int ret; |
94 | 94 | ||
95 | while (1) { | 95 | while (1) { |
96 | int id; | 96 | int id; |
97 | 97 | ||
98 | ret = idr_pre_get(&bat_idr, GFP_KERNEL); | 98 | ret = idr_pre_get(&bat_idr, GFP_KERNEL); |
99 | if (ret == 0) | 99 | if (ret == 0) |
100 | return -ENOMEM; | 100 | return -ENOMEM; |
101 | 101 | ||
102 | mutex_lock(&bat_idr_lock); | 102 | mutex_lock(&bat_idr_lock); |
103 | ret = idr_get_new(&bat_idr, NULL, &id); | 103 | ret = idr_get_new(&bat_idr, NULL, &id); |
104 | mutex_unlock(&bat_idr_lock); | 104 | mutex_unlock(&bat_idr_lock); |
105 | 105 | ||
106 | if (ret == 0) { | 106 | if (ret == 0) { |
107 | ret = id & MAX_ID_MASK; | 107 | ret = id & MAX_ID_MASK; |
108 | break; | 108 | break; |
109 | } else if (ret == -EAGAIN) { | 109 | } else if (ret == -EAGAIN) { |
110 | continue; | 110 | continue; |
111 | } else { | 111 | } else { |
112 | break; | 112 | break; |
113 | } | 113 | } |
114 | } | 114 | } |
115 | 115 | ||
116 | return ret; | 116 | return ret; |
117 | } | 117 | } |
118 | 118 | ||
119 | static void release_bat_id(int id) | 119 | static void release_bat_id(int id) |
120 | { | 120 | { |
121 | mutex_lock(&bat_idr_lock); | 121 | mutex_lock(&bat_idr_lock); |
122 | idr_remove(&bat_idr, id); | 122 | idr_remove(&bat_idr, id); |
123 | mutex_unlock(&bat_idr_lock); | 123 | mutex_unlock(&bat_idr_lock); |
124 | |||
125 | return; | ||
126 | } | 124 | } |
127 | 125 | ||
128 | static int w1_ds2760_add_slave(struct w1_slave *sl) | 126 | static int w1_ds2760_add_slave(struct w1_slave *sl) |
129 | { | 127 | { |
130 | int ret; | 128 | int ret; |
131 | int id; | 129 | int id; |
132 | struct platform_device *pdev; | 130 | struct platform_device *pdev; |
133 | 131 | ||
134 | id = new_bat_id(); | 132 | id = new_bat_id(); |
135 | if (id < 0) { | 133 | if (id < 0) { |
136 | ret = id; | 134 | ret = id; |
137 | goto noid; | 135 | goto noid; |
138 | } | 136 | } |
139 | 137 | ||
140 | pdev = platform_device_alloc("ds2760-battery", id); | 138 | pdev = platform_device_alloc("ds2760-battery", id); |
141 | if (!pdev) { | 139 | if (!pdev) { |
142 | ret = -ENOMEM; | 140 | ret = -ENOMEM; |
143 | goto pdev_alloc_failed; | 141 | goto pdev_alloc_failed; |
144 | } | 142 | } |
145 | pdev->dev.parent = &sl->dev; | 143 | pdev->dev.parent = &sl->dev; |
146 | 144 | ||
147 | ret = platform_device_add(pdev); | 145 | ret = platform_device_add(pdev); |
148 | if (ret) | 146 | if (ret) |
149 | goto pdev_add_failed; | 147 | goto pdev_add_failed; |
150 | 148 | ||
151 | ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); | 149 | ret = sysfs_create_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); |
152 | if (ret) | 150 | if (ret) |
153 | goto bin_attr_failed; | 151 | goto bin_attr_failed; |
154 | 152 | ||
155 | dev_set_drvdata(&sl->dev, pdev); | 153 | dev_set_drvdata(&sl->dev, pdev); |
156 | 154 | ||
157 | goto success; | 155 | goto success; |
158 | 156 | ||
159 | bin_attr_failed: | 157 | bin_attr_failed: |
160 | pdev_add_failed: | 158 | pdev_add_failed: |
161 | platform_device_unregister(pdev); | 159 | platform_device_unregister(pdev); |
162 | pdev_alloc_failed: | 160 | pdev_alloc_failed: |
163 | release_bat_id(id); | 161 | release_bat_id(id); |
164 | noid: | 162 | noid: |
165 | success: | 163 | success: |
166 | return ret; | 164 | return ret; |
167 | } | 165 | } |
168 | 166 | ||
169 | static void w1_ds2760_remove_slave(struct w1_slave *sl) | 167 | static void w1_ds2760_remove_slave(struct w1_slave *sl) |
170 | { | 168 | { |
171 | struct platform_device *pdev = dev_get_drvdata(&sl->dev); | 169 | struct platform_device *pdev = dev_get_drvdata(&sl->dev); |
172 | int id = pdev->id; | 170 | int id = pdev->id; |
173 | 171 | ||
174 | platform_device_unregister(pdev); | 172 | platform_device_unregister(pdev); |
175 | release_bat_id(id); | 173 | release_bat_id(id); |
176 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); | 174 | sysfs_remove_bin_file(&sl->dev.kobj, &w1_ds2760_bin_attr); |
177 | |||
178 | return; | ||
179 | } | 175 | } |
180 | 176 | ||
181 | static struct w1_family_ops w1_ds2760_fops = { | 177 | static struct w1_family_ops w1_ds2760_fops = { |
182 | .add_slave = w1_ds2760_add_slave, | 178 | .add_slave = w1_ds2760_add_slave, |
183 | .remove_slave = w1_ds2760_remove_slave, | 179 | .remove_slave = w1_ds2760_remove_slave, |
184 | }; | 180 | }; |
185 | 181 | ||
186 | static struct w1_family w1_ds2760_family = { | 182 | static struct w1_family w1_ds2760_family = { |
187 | .fid = W1_FAMILY_DS2760, | 183 | .fid = W1_FAMILY_DS2760, |
188 | .fops = &w1_ds2760_fops, | 184 | .fops = &w1_ds2760_fops, |
189 | }; | 185 | }; |
190 | 186 | ||
191 | static int __init w1_ds2760_init(void) | 187 | static int __init w1_ds2760_init(void) |
192 | { | 188 | { |
193 | printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " | 189 | printk(KERN_INFO "1-Wire driver for the DS2760 battery monitor " |
194 | " chip - (c) 2004-2005, Szabolcs Gyurko\n"); | 190 | " chip - (c) 2004-2005, Szabolcs Gyurko\n"); |
195 | idr_init(&bat_idr); | 191 | idr_init(&bat_idr); |
196 | return w1_register_family(&w1_ds2760_family); | 192 | return w1_register_family(&w1_ds2760_family); |
197 | } | 193 | } |
198 | 194 | ||
199 | static void __exit w1_ds2760_exit(void) | 195 | static void __exit w1_ds2760_exit(void) |
200 | { | 196 | { |
201 | w1_unregister_family(&w1_ds2760_family); | 197 | w1_unregister_family(&w1_ds2760_family); |
202 | idr_destroy(&bat_idr); | 198 | idr_destroy(&bat_idr); |
203 | } | 199 | } |
204 | 200 | ||
205 | EXPORT_SYMBOL(w1_ds2760_read); | 201 | EXPORT_SYMBOL(w1_ds2760_read); |
206 | EXPORT_SYMBOL(w1_ds2760_write); | 202 | EXPORT_SYMBOL(w1_ds2760_write); |
207 | 203 | ||
208 | module_init(w1_ds2760_init); | 204 | module_init(w1_ds2760_init); |
209 | module_exit(w1_ds2760_exit); | 205 | module_exit(w1_ds2760_exit); |
210 | 206 | ||
211 | MODULE_LICENSE("GPL"); | 207 | MODULE_LICENSE("GPL"); |
212 | MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>"); | 208 | MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>"); |
213 | MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); | 209 | MODULE_DESCRIPTION("1-wire Driver Dallas 2760 battery monitor chip"); |
214 | 210 |