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");