~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to drivers/staging/nvec/nvec_power.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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");