1
#include <linux/module.h>
2
#include <linux/platform_device.h>
4
#include <linux/power_supply.h>
5
#include <linux/slab.h>
6
#include <linux/workqueue.h>
7
#include <linux/delay.h>
12
struct notifier_block notifier;
13
struct delayed_work poller;
14
struct nvec_chip *nvec;
22
int charge_full_design;
24
int critical_capacity;
40
AVERAGING_TIME_INTERVAL,
42
LAST_FULL_CHARGE_CAPACITY,
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)
80
if(res->sub_type == 0)
82
if (power->on != res->plu)
85
power_supply_changed(&nvec_psy);
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)
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)
120
switch(res->sub_type)
125
if (power->bat_present == 0)
128
get_bat_mfg_data(power);
131
power->bat_present = 1;
133
switch ((res->plc[0] >> 1) & 3)
136
power->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
139
power->bat_status = POWER_SUPPLY_STATUS_CHARGING;
142
power->bat_status = POWER_SUPPLY_STATUS_DISCHARGING;
145
power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
148
if (power->bat_present == 1)
151
power->bat_present = 0;
152
power->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
154
power->bat_cap = res->plc[1];
156
power_supply_changed(&nvec_bat_psy);
159
power->bat_voltage_now = res->plu * 1000;
162
power->time_remain = res->plu * 3600;
165
power->bat_current_now = res->pls * 1000;
167
case AVERAGE_CURRENT:
168
power->bat_current_avg = res->pls * 1000;
170
case CAPACITY_REMAINING:
171
power->capacity_remain = res->plu * 1000;
173
case LAST_FULL_CHARGE_CAPACITY:
174
power->charge_last_full = res->plu * 1000;
176
case DESIGN_CAPACITY:
177
power->charge_full_design = res->plu * 1000;
179
case CRITICAL_CAPACITY:
180
power->critical_capacity = res->plu * 1000;
183
power->bat_temperature = res->plu - 2732;
186
memcpy(power->bat_manu, &res->plc, res->length-2);
187
power->bat_model[res->length-2] = '\0';
190
memcpy(power->bat_model, &res->plc, res->length-2);
191
power->bat_model[res->length-2] = '\0';
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;
201
power->bat_type_enum = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
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);
216
case POWER_SUPPLY_PROP_ONLINE:
217
val->intval = power->on;
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);
233
case POWER_SUPPLY_PROP_STATUS:
234
val->intval = power->bat_status;
236
case POWER_SUPPLY_PROP_CAPACITY:
237
val->intval = power->bat_cap;
239
case POWER_SUPPLY_PROP_PRESENT:
240
val->intval = power->bat_present;
242
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
243
val->intval = power->bat_voltage_now;
245
case POWER_SUPPLY_PROP_CURRENT_NOW:
246
val->intval = power->bat_current_now;
248
case POWER_SUPPLY_PROP_CURRENT_AVG:
249
val->intval = power->bat_current_avg;
251
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
252
val->intval = power->time_remain;
254
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
255
val->intval = power->charge_full_design;
257
case POWER_SUPPLY_PROP_CHARGE_FULL:
258
val->intval = power->charge_last_full;
260
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
261
val->intval = power->critical_capacity;
263
case POWER_SUPPLY_PROP_CHARGE_NOW:
264
val->intval = power->capacity_remain;
266
case POWER_SUPPLY_PROP_TEMP:
267
val->intval = power->bat_temperature;
269
case POWER_SUPPLY_PROP_MANUFACTURER:
270
val->strval = power->bat_manu;
272
case POWER_SUPPLY_PROP_MODEL_NAME:
273
val->strval = power->bat_model;
275
case POWER_SUPPLY_PROP_TECHNOLOGY:
276
val->intval = power->bat_type_enum;
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,
295
POWER_SUPPLY_PROP_CURRENT_AVG,
296
POWER_SUPPLY_PROP_TEMP,
297
POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
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[] = {
312
static struct power_supply nvec_bat_psy = {
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 = {
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,
335
AVERAGE_CURRENT, TEMPERATURE, TIME_REMAINING,
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,
345
if (counter >= ARRAY_SIZE(bat_iter))
348
/* AC status via sys req */
349
nvec_write_async(power->nvec, buf, 2);
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);
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));
384
power->notifier.notifier_call = nvec_power_bat_notifier;
391
nvec_register_notifier(nvec, &power->notifier, NVEC_SYS);
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),
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");