Initial commit
[kernel/linux-3.0.git] / drivers / staging / nvec / nvec_power.c
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"
9
10 struct nvec_power
11 {
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];
32 };
33
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,
49 };
50
51 enum {
52         AC,
53         BAT,
54 };
55
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;
65         };
66 };
67
68 static struct power_supply nvec_bat_psy;
69 static struct power_supply nvec_psy;
70
71 static int nvec_power_notifier(struct notifier_block *nb,
72                                  unsigned long event_type, void *data)
73 {
74         struct nvec_power *power = container_of(nb, struct nvec_power, notifier);
75         struct bat_response *res = (struct bat_response *)data;
76
77         if (event_type != NVEC_SYS)
78                 return NOTIFY_DONE;
79
80         if(res->sub_type == 0)
81         {
82                 if (power->on != res->plu)
83                 {
84                         power->on = res->plu;
85                         power_supply_changed(&nvec_psy);
86                 }
87                 return NOTIFY_STOP;
88         }
89         return NOTIFY_OK;
90 }
91
92 static const int bat_init[] =
93 {
94         LAST_FULL_CHARGE_CAPACITY, DESIGN_CAPACITY, CRITICAL_CAPACITY,
95         MANUFACTURER, MODEL, TYPE,
96 };
97
98 static void get_bat_mfg_data(struct nvec_power *power)
99 {
100         int i;
101         char buf[] = { '\x02', '\x00' };
102
103         for (i = 0; i < ARRAY_SIZE(bat_init); i++)
104         {
105                 buf[1] = bat_init[i];
106                 nvec_write_async(power->nvec, buf, 2);
107         }
108 }
109
110 static int nvec_power_bat_notifier(struct notifier_block *nb,
111                                  unsigned long event_type, void *data)
112 {
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;
116
117         if (event_type != NVEC_BAT)
118                 return NOTIFY_DONE;
119
120         switch(res->sub_type)
121         {
122                 case SLOT_STATUS:
123                         if (res->plc[0] & 1)
124                         {
125                                 if (power->bat_present == 0)
126                                 {
127                                         status_changed = 1;
128                                         get_bat_mfg_data(power);
129                                 }
130
131                                 power->bat_present = 1;
132
133                                 switch ((res->plc[0] >> 1) & 3)
134                                 {
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;
146                                 }
147                         } else {
148                                 if (power->bat_present == 1)
149                                         status_changed = 1;
150
151                                 power->bat_present = 0;
152                                 power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
153                         }
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;
205         }
206
207         return NOTIFY_STOP;
208 }
209
210 static int nvec_power_get_property(struct power_supply *psy,
211                                 enum power_supply_property psp,
212                                 union power_supply_propval *val)
213 {
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;
221         }
222         return 0;
223 }
224
225 static int nvec_battery_get_property(struct power_supply *psy,
226                                 enum power_supply_property psp,
227                                 union power_supply_propval *val)
228 {
229         struct nvec_power *power = dev_get_drvdata(psy->dev->parent);
230
231         switch(psp)
232         {
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;
280                 }
281         return 0;
282 }
283
284 static enum power_supply_property nvec_power_props[] = {
285         POWER_SUPPLY_PROP_ONLINE,
286 };
287
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,
306 };
307
308 static char *nvec_power_supplied_to[] = {
309         "battery",
310 };
311
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,
318 };
319
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,
328 };
329
330 static int counter = 0;
331 static int const bat_iter[] =
332 {
333         SLOT_STATUS, VOLTAGE, CURRENT, CAPACITY_REMAINING,
334 #ifdef EC_FULL_DIAG
335         AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
336 #endif
337 };
338
339 static void nvec_power_poll(struct work_struct *work)
340 {
341         char buf[] = { '\x01', '\x00' };
342         struct nvec_power *power = container_of(work, struct nvec_power,
343                  poller.work);
344
345         if (counter >= ARRAY_SIZE(bat_iter))
346                 counter = 0;
347
348 /* AC status via sys req */
349         nvec_write_async(power->nvec, buf, 2);
350         msleep(100);
351
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);
357
358 //      printk("%02x %02x\n", buf[0], buf[1]);
359
360         schedule_delayed_work(to_delayed_work(work), msecs_to_jiffies(5000));
361 };
362
363 static int __devinit nvec_power_probe(struct platform_device *pdev)
364 {
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);
368
369         dev_set_drvdata(&pdev->dev, power);
370         power->nvec = nvec;
371
372         switch (pdev->id) {
373         case AC:
374                 psy = &nvec_psy;
375
376                 power->notifier.notifier_call = nvec_power_notifier;
377
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;
383
384                 power->notifier.notifier_call = nvec_power_bat_notifier;
385                 break;
386         default:
387                 kfree(power);
388                 return -ENODEV;
389         }
390
391         nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
392
393         if (pdev->id == BAT)
394                 get_bat_mfg_data(power);
395
396         return power_supply_register(&pdev->dev, psy);
397 }
398
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,
405         }
406 };
407
408 static int __init nvec_power_init(void) 
409 {
410         return platform_driver_register(&nvec_power_driver);
411 }
412
413 module_init(nvec_power_init);
414
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");