Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/upstream-linus
[linux-btrfs-devel.git] / drivers / staging / nvec / nvec_power.c
blobdf164add837b2996abf086c58e49591c4511528a
1 #include <linux/module.h>
2 #include <linux/platform_device.h>
3 #include <linux/err.h>
4 #include <linux/power_supply.h>
5 #include <linux/slab.h>
6 #include <linux/workqueue.h>
7 #include <linux/delay.h>
8 #include "nvec.h"
10 struct nvec_power
12 struct notifier_block notifier;
13 struct delayed_work poller;
14 struct nvec_chip *nvec;
15 int on;
16 int bat_present;
17 int bat_status;
18 int bat_voltage_now;
19 int bat_current_now;
20 int bat_current_avg;
21 int time_remain;
22 int charge_full_design;
23 int charge_last_full;
24 int critical_capacity;
25 int capacity_remain;
26 int bat_temperature;
27 int bat_cap;
28 int bat_type_enum;
29 char bat_manu[30];
30 char bat_model[30];
31 char bat_type[30];
34 enum {
35 SLOT_STATUS,
36 VOLTAGE,
37 TIME_REMAINING,
38 CURRENT,
39 AVERAGE_CURRENT,
40 AVERAGING_TIME_INTERVAL,
41 CAPACITY_REMAINING,
42 LAST_FULL_CHARGE_CAPACITY,
43 DESIGN_CAPACITY,
44 CRITICAL_CAPACITY,
45 TEMPERATURE,
46 MANUFACTURER,
47 MODEL,
48 TYPE,
51 enum {
52 AC,
53 BAT,
56 struct bat_response {
57 u8 event_type;
58 u8 length;
59 u8 sub_type;
60 u8 status;
61 union { /* payload */
62 char plc[30];
63 u16 plu;
64 s16 pls;
68 static struct power_supply nvec_bat_psy;
69 static struct power_supply nvec_psy;
71 static int nvec_power_notifier(struct notifier_block *nb,
72 unsigned long event_type, void *data)
74 struct nvec_power *power = container_of(nb, struct nvec_power, notifier);
75 struct bat_response *res = (struct bat_response *)data;
77 if (event_type != NVEC_SYS)
78 return NOTIFY_DONE;
80 if(res->sub_type == 0)
82 if (power->on != res->plu)
84 power->on = res->plu;
85 power_supply_changed(&nvec_psy);
87 return NOTIFY_STOP;
89 return NOTIFY_OK;
92 static const int bat_init[] =
94 LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
95 MANUFACTURER, MODEL, TYPE,
98 static void get_bat_mfg_data(struct nvec_power *power)
100 int i;
101 char buf[] = { '\x02', '\x00' };
103 for (i = 0; i < ARRAY_SIZE(bat_init); i++)
105 buf[1] = bat_init[i];
106 nvec_write_async(power->nvec, buf, 2);
110 static int nvec_power_bat_notifier(struct notifier_block *nb,
111 unsigned long event_type, void *data)
113 struct nvec_power *power = container_of(nb, struct nvec_power, notifier);
114 struct bat_response *res = (struct bat_response *)data;
115 int status_changed = 0;
117 if (event_type != NVEC_BAT)
118 return NOTIFY_DONE;
120 switch(res->sub_type)
122 case SLOT_STATUS:
123 if (res->plc[0] & 1)
125 if (power->bat_present == 0)
127 status_changed = 1;
128 get_bat_mfg_data(power);
131 power->bat_present = 1;
133 switch ((res->plc[0] >> 1) & 3)
135 case 0:
136 power->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
137 break;
138 case 1:
139 power->bat_status = POWER_SUPPLY_STATUS_CHARGING;
140 break;
141 case 2:
142 power->bat_status = POWER_SUPPLY_STATUS_DISCHARGING;
143 break;
144 default:
145 power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
147 } else {
148 if (power->bat_present == 1)
149 status_changed = 1;
151 power->bat_present = 0;
152 power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
154 power->bat_cap = res->plc[1];
155 if (status_changed)
156 power_supply_changed(&nvec_bat_psy);
157 break;
158 case VOLTAGE:
159 power->bat_voltage_now = res->plu * 1000;
160 break;
161 case TIME_REMAINING:
162 power->time_remain = res->plu * 3600;
163 break;
164 case CURRENT:
165 power->bat_current_now = res->pls * 1000;
166 break;
167 case AVERAGE_CURRENT:
168 power->bat_current_avg = res->pls * 1000;
169 break;
170 case CAPACITY_REMAINING:
171 power->capacity_remain = res->plu * 1000;
172 break;
173 case LAST_FULL_CHARGE_CAPACITY:
174 power->charge_last_full = res->plu * 1000;
175 break;
176 case DESIGN_CAPACITY:
177 power->charge_full_design = res->plu * 1000;
178 break;
179 case CRITICAL_CAPACITY:
180 power->critical_capacity = res->plu * 1000;
181 break;
182 case TEMPERATURE:
183 power->bat_temperature = res->plu - 2732;
184 break;
185 case MANUFACTURER:
186 memcpy(power->bat_manu, &res->plc, res->length-2);
187 power->bat_model[res->length-2] = '\0';
188 break;
189 case MODEL:
190 memcpy(power->bat_model, &res->plc, res->length-2);
191 power->bat_model[res->length-2] = '\0';
192 break;
193 case TYPE:
194 memcpy(power->bat_type, &res->plc, res->length-2);
195 power->bat_type[res->length-2] = '\0';
196 /* this differs a little from the spec
197 fill in more if you find some */
198 if (!strncmp(power->bat_type, "Li", 30))
199 power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_LION;
200 else
201 power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
202 break;
203 default:
204 return NOTIFY_STOP;
207 return NOTIFY_STOP;
210 static int nvec_power_get_property(struct power_supply *psy,
211 enum power_supply_property psp,
212 union power_supply_propval *val)
214 struct nvec_power *power = dev_get_drvdata(psy->dev->parent);
215 switch (psp) {
216 case POWER_SUPPLY_PROP_ONLINE:
217 val->intval = power->on;
218 break;
219 default:
220 return -EINVAL;
222 return 0;
225 static int nvec_battery_get_property(struct power_supply *psy,
226 enum power_supply_property psp,
227 union power_supply_propval *val)
229 struct nvec_power *power = dev_get_drvdata(psy->dev->parent);
231 switch(psp)
233 case POWER_SUPPLY_PROP_STATUS:
234 val->intval = power->bat_status;
235 break;
236 case POWER_SUPPLY_PROP_CAPACITY:
237 val->intval = power->bat_cap;
238 break;
239 case POWER_SUPPLY_PROP_PRESENT:
240 val->intval = power->bat_present;
241 break;
242 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
243 val->intval = power->bat_voltage_now;
244 break;
245 case POWER_SUPPLY_PROP_CURRENT_NOW:
246 val->intval = power->bat_current_now;
247 break;
248 case POWER_SUPPLY_PROP_CURRENT_AVG:
249 val->intval = power->bat_current_avg;
250 break;
251 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
252 val->intval = power->time_remain;
253 break;
254 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
255 val->intval = power->charge_full_design;
256 break;
257 case POWER_SUPPLY_PROP_CHARGE_FULL:
258 val->intval = power->charge_last_full;
259 break;
260 case POWER_SUPPLY_PROP_CHARGE_EMPTY:
261 val->intval = power->critical_capacity;
262 break;
263 case POWER_SUPPLY_PROP_CHARGE_NOW:
264 val->intval = power->capacity_remain;
265 break;
266 case POWER_SUPPLY_PROP_TEMP:
267 val->intval = power->bat_temperature;
268 break;
269 case POWER_SUPPLY_PROP_MANUFACTURER:
270 val->strval = power->bat_manu;
271 break;
272 case POWER_SUPPLY_PROP_MODEL_NAME:
273 val->strval = power->bat_model;
274 break;
275 case POWER_SUPPLY_PROP_TECHNOLOGY:
276 val->intval = power->bat_type_enum;
277 break;
278 default:
279 return -EINVAL;
281 return 0;
284 static enum power_supply_property nvec_power_props[] = {
285 POWER_SUPPLY_PROP_ONLINE,
288 static enum power_supply_property nvec_battery_props[] = {
289 POWER_SUPPLY_PROP_STATUS,
290 POWER_SUPPLY_PROP_PRESENT,
291 POWER_SUPPLY_PROP_CAPACITY,
292 POWER_SUPPLY_PROP_VOLTAGE_NOW,
293 POWER_SUPPLY_PROP_CURRENT_NOW,
294 #ifdef EC_FULL_DIAG
295 POWER_SUPPLY_PROP_CURRENT_AVG,
296 POWER_SUPPLY_PROP_TEMP,
297 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
298 #endif
299 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
300 POWER_SUPPLY_PROP_CHARGE_FULL,
301 POWER_SUPPLY_PROP_CHARGE_EMPTY,
302 POWER_SUPPLY_PROP_CHARGE_NOW,
303 POWER_SUPPLY_PROP_MANUFACTURER,
304 POWER_SUPPLY_PROP_MODEL_NAME,
305 POWER_SUPPLY_PROP_TECHNOLOGY,
308 static char *nvec_power_supplied_to[] = {
309 "battery",
312 static struct power_supply nvec_bat_psy = {
313 .name = "battery",
314 .type = POWER_SUPPLY_TYPE_BATTERY,
315 .properties = nvec_battery_props,
316 .num_properties = ARRAY_SIZE(nvec_battery_props),
317 .get_property = nvec_battery_get_property,
320 static struct power_supply nvec_psy = {
321 .name = "ac",
322 .type = POWER_SUPPLY_TYPE_MAINS,
323 .supplied_to = nvec_power_supplied_to,
324 .num_supplicants = ARRAY_SIZE(nvec_power_supplied_to),
325 .properties = nvec_power_props,
326 .num_properties = ARRAY_SIZE(nvec_power_props),
327 .get_property = nvec_power_get_property,
330 static int counter = 0;
331 static int const bat_iter[] =
333 SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
334 #ifdef EC_FULL_DIAG
335 AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
336 #endif
339 static void nvec_power_poll(struct work_struct *work)
341 char buf[] = { '\x01', '\x00' };
342 struct nvec_power *power = container_of(work, struct nvec_power,
343 poller.work);
345 if (counter >= ARRAY_SIZE(bat_iter))
346 counter = 0;
348 /* AC status via sys req */
349 nvec_write_async(power->nvec, buf, 2);
350 msleep(100);
352 /* select a battery request function via round robin
353 doing it all at once seems to overload the power supply */
354 buf[0] = '\x02'; /* battery */
355 buf[1] = bat_iter[counter++];
356 nvec_write_async(power->nvec, buf, 2);
358 // printk("%02x %02x\n", buf[0], buf[1]);
360 schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
363 static int __devinit nvec_power_probe(struct platform_device *pdev)
365 struct power_supply *psy;
366 struct nvec_power *power = kzalloc(sizeof(struct nvec_power), GFP_NOWAIT);
367 struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
369 dev_set_drvdata(&pdev->dev, power);
370 power->nvec = nvec;
372 switch (pdev->id) {
373 case AC:
374 psy = &nvec_psy;
376 power->notifier.notifier_call = nvec_power_notifier;
378 INIT_DELAYED_WORK(&power->poller, nvec_power_poll);
379 schedule_delayed_work(&power->poller, msecs_to_jiffies(5000));
380 break;
381 case BAT:
382 psy = &nvec_bat_psy;
384 power->notifier.notifier_call = nvec_power_bat_notifier;
385 break;
386 default:
387 kfree(power);
388 return -ENODEV;
391 nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
393 if (pdev->id == BAT)
394 get_bat_mfg_data(power);
396 return power_supply_register(&pdev->dev, psy);
399 static struct platform_driver nvec_power_driver = {
400 .probe = nvec_power_probe,
401 // .remove = __devexit_p(nvec_power_remove),
402 .driver = {
403 .name = "nvec-power",
404 .owner = THIS_MODULE,
408 static int __init nvec_power_init(void)
410 return platform_driver_register(&nvec_power_driver);
413 module_init(nvec_power_init);
415 MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
416 MODULE_LICENSE("GPL");
417 MODULE_DESCRIPTION("NVEC battery and AC driver");
418 MODULE_ALIAS("platform:nvec-power");