2 * nvec_power: power supply driver for a NVIDIA compliant embedded controller
4 * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
6 * Authors: Ilya Petrov <ilya.muromec@gmail.com>
7 * Marc Dietrich <marvin24@gmx.de>
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file "COPYING" in the main directory of this archive
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/err.h>
18 #include <linux/power_supply.h>
19 #include <linux/slab.h>
20 #include <linux/workqueue.h>
21 #include <linux/delay.h>
26 struct notifier_block notifier
;
27 struct delayed_work poller
;
28 struct nvec_chip
*nvec
;
36 int charge_full_design
;
38 int critical_capacity
;
54 AVERAGING_TIME_INTERVAL
,
56 LAST_FULL_CHARGE_CAPACITY
,
83 static struct power_supply nvec_bat_psy
;
84 static struct power_supply nvec_psy
;
86 static int nvec_power_notifier(struct notifier_block
*nb
,
87 unsigned long event_type
, void *data
)
89 struct nvec_power
*power
=
90 container_of(nb
, struct nvec_power
, notifier
);
91 struct bat_response
*res
= (struct bat_response
*)data
;
93 if (event_type
!= NVEC_SYS
)
96 if (res
->sub_type
== 0) {
97 if (power
->on
!= res
->plu
) {
99 power_supply_changed(&nvec_psy
);
106 static const int bat_init
[] = {
107 LAST_FULL_CHARGE_CAPACITY
, DESIGN_CAPACITY
, CRITICAL_CAPACITY
,
108 MANUFACTURER
, MODEL
, TYPE
,
111 static void get_bat_mfg_data(struct nvec_power
*power
)
114 char buf
[] = { '\x02', '\x00' };
116 for (i
= 0; i
< ARRAY_SIZE(bat_init
); i
++) {
117 buf
[1] = bat_init
[i
];
118 nvec_write_async(power
->nvec
, buf
, 2);
122 static int nvec_power_bat_notifier(struct notifier_block
*nb
,
123 unsigned long event_type
, void *data
)
125 struct nvec_power
*power
=
126 container_of(nb
, struct nvec_power
, notifier
);
127 struct bat_response
*res
= (struct bat_response
*)data
;
128 int status_changed
= 0;
130 if (event_type
!= NVEC_BAT
)
133 switch (res
->sub_type
) {
135 if (res
->plc
[0] & 1) {
136 if (power
->bat_present
== 0) {
138 get_bat_mfg_data(power
);
141 power
->bat_present
= 1;
143 switch ((res
->plc
[0] >> 1) & 3) {
146 POWER_SUPPLY_STATUS_NOT_CHARGING
;
150 POWER_SUPPLY_STATUS_CHARGING
;
154 POWER_SUPPLY_STATUS_DISCHARGING
;
157 power
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
160 if (power
->bat_present
== 1)
163 power
->bat_present
= 0;
164 power
->bat_status
= POWER_SUPPLY_STATUS_UNKNOWN
;
166 power
->bat_cap
= res
->plc
[1];
168 power_supply_changed(&nvec_bat_psy
);
171 power
->bat_voltage_now
= res
->plu
* 1000;
174 power
->time_remain
= res
->plu
* 3600;
177 power
->bat_current_now
= res
->pls
* 1000;
179 case AVERAGE_CURRENT
:
180 power
->bat_current_avg
= res
->pls
* 1000;
182 case CAPACITY_REMAINING
:
183 power
->capacity_remain
= res
->plu
* 1000;
185 case LAST_FULL_CHARGE_CAPACITY
:
186 power
->charge_last_full
= res
->plu
* 1000;
188 case DESIGN_CAPACITY
:
189 power
->charge_full_design
= res
->plu
* 1000;
191 case CRITICAL_CAPACITY
:
192 power
->critical_capacity
= res
->plu
* 1000;
195 power
->bat_temperature
= res
->plu
- 2732;
198 memcpy(power
->bat_manu
, &res
->plc
, res
->length
- 2);
199 power
->bat_model
[res
->length
- 2] = '\0';
202 memcpy(power
->bat_model
, &res
->plc
, res
->length
- 2);
203 power
->bat_model
[res
->length
- 2] = '\0';
206 memcpy(power
->bat_type
, &res
->plc
, res
->length
- 2);
207 power
->bat_type
[res
->length
- 2] = '\0';
208 /* this differs a little from the spec
209 fill in more if you find some */
210 if (!strncmp(power
->bat_type
, "Li", 30))
211 power
->bat_type_enum
= POWER_SUPPLY_TECHNOLOGY_LION
;
213 power
->bat_type_enum
= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
222 static int nvec_power_get_property(struct power_supply
*psy
,
223 enum power_supply_property psp
,
224 union power_supply_propval
*val
)
226 struct nvec_power
*power
= dev_get_drvdata(psy
->dev
->parent
);
228 case POWER_SUPPLY_PROP_ONLINE
:
229 val
->intval
= power
->on
;
237 static int nvec_battery_get_property(struct power_supply
*psy
,
238 enum power_supply_property psp
,
239 union power_supply_propval
*val
)
241 struct nvec_power
*power
= dev_get_drvdata(psy
->dev
->parent
);
244 case POWER_SUPPLY_PROP_STATUS
:
245 val
->intval
= power
->bat_status
;
247 case POWER_SUPPLY_PROP_CAPACITY
:
248 val
->intval
= power
->bat_cap
;
250 case POWER_SUPPLY_PROP_PRESENT
:
251 val
->intval
= power
->bat_present
;
253 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
254 val
->intval
= power
->bat_voltage_now
;
256 case POWER_SUPPLY_PROP_CURRENT_NOW
:
257 val
->intval
= power
->bat_current_now
;
259 case POWER_SUPPLY_PROP_CURRENT_AVG
:
260 val
->intval
= power
->bat_current_avg
;
262 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
:
263 val
->intval
= power
->time_remain
;
265 case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
:
266 val
->intval
= power
->charge_full_design
;
268 case POWER_SUPPLY_PROP_CHARGE_FULL
:
269 val
->intval
= power
->charge_last_full
;
271 case POWER_SUPPLY_PROP_CHARGE_EMPTY
:
272 val
->intval
= power
->critical_capacity
;
274 case POWER_SUPPLY_PROP_CHARGE_NOW
:
275 val
->intval
= power
->capacity_remain
;
277 case POWER_SUPPLY_PROP_TEMP
:
278 val
->intval
= power
->bat_temperature
;
280 case POWER_SUPPLY_PROP_MANUFACTURER
:
281 val
->strval
= power
->bat_manu
;
283 case POWER_SUPPLY_PROP_MODEL_NAME
:
284 val
->strval
= power
->bat_model
;
286 case POWER_SUPPLY_PROP_TECHNOLOGY
:
287 val
->intval
= power
->bat_type_enum
;
295 static enum power_supply_property nvec_power_props
[] = {
296 POWER_SUPPLY_PROP_ONLINE
,
299 static enum power_supply_property nvec_battery_props
[] = {
300 POWER_SUPPLY_PROP_STATUS
,
301 POWER_SUPPLY_PROP_PRESENT
,
302 POWER_SUPPLY_PROP_CAPACITY
,
303 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
304 POWER_SUPPLY_PROP_CURRENT_NOW
,
306 POWER_SUPPLY_PROP_CURRENT_AVG
,
307 POWER_SUPPLY_PROP_TEMP
,
308 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
,
310 POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN
,
311 POWER_SUPPLY_PROP_CHARGE_FULL
,
312 POWER_SUPPLY_PROP_CHARGE_EMPTY
,
313 POWER_SUPPLY_PROP_CHARGE_NOW
,
314 POWER_SUPPLY_PROP_MANUFACTURER
,
315 POWER_SUPPLY_PROP_MODEL_NAME
,
316 POWER_SUPPLY_PROP_TECHNOLOGY
,
319 static char *nvec_power_supplied_to
[] = {
323 static struct power_supply nvec_bat_psy
= {
325 .type
= POWER_SUPPLY_TYPE_BATTERY
,
326 .properties
= nvec_battery_props
,
327 .num_properties
= ARRAY_SIZE(nvec_battery_props
),
328 .get_property
= nvec_battery_get_property
,
331 static struct power_supply nvec_psy
= {
333 .type
= POWER_SUPPLY_TYPE_MAINS
,
334 .supplied_to
= nvec_power_supplied_to
,
335 .num_supplicants
= ARRAY_SIZE(nvec_power_supplied_to
),
336 .properties
= nvec_power_props
,
337 .num_properties
= ARRAY_SIZE(nvec_power_props
),
338 .get_property
= nvec_power_get_property
,
342 static int const bat_iter
[] = {
343 SLOT_STATUS
, VOLTAGE
, CURRENT
, CAPACITY_REMAINING
,
345 AVERAGE_CURRENT
, TEMPERATURE
, TIME_REMAINING
,
349 static void nvec_power_poll(struct work_struct
*work
)
351 char buf
[] = { '\x01', '\x00' };
352 struct nvec_power
*power
= container_of(work
, struct nvec_power
,
355 if (counter
>= ARRAY_SIZE(bat_iter
))
358 /* AC status via sys req */
359 nvec_write_async(power
->nvec
, buf
, 2);
362 /* select a battery request function via round robin
363 doing it all at once seems to overload the power supply */
364 buf
[0] = '\x02'; /* battery */
365 buf
[1] = bat_iter
[counter
++];
366 nvec_write_async(power
->nvec
, buf
, 2);
368 schedule_delayed_work(to_delayed_work(work
), msecs_to_jiffies(5000));
371 static int __devinit
nvec_power_probe(struct platform_device
*pdev
)
373 struct power_supply
*psy
;
374 struct nvec_power
*power
=
375 kzalloc(sizeof(struct nvec_power
), GFP_NOWAIT
);
376 struct nvec_chip
*nvec
= dev_get_drvdata(pdev
->dev
.parent
);
378 dev_set_drvdata(&pdev
->dev
, power
);
385 power
->notifier
.notifier_call
= nvec_power_notifier
;
387 INIT_DELAYED_WORK(&power
->poller
, nvec_power_poll
);
388 schedule_delayed_work(&power
->poller
, msecs_to_jiffies(5000));
393 power
->notifier
.notifier_call
= nvec_power_bat_notifier
;
400 nvec_register_notifier(nvec
, &power
->notifier
, NVEC_SYS
);
403 get_bat_mfg_data(power
);
405 return power_supply_register(&pdev
->dev
, psy
);
408 static struct platform_driver nvec_power_driver
= {
409 .probe
= nvec_power_probe
,
411 .name
= "nvec-power",
412 .owner
= THIS_MODULE
,
416 static int __init
nvec_power_init(void)
418 return platform_driver_register(&nvec_power_driver
);
421 module_init(nvec_power_init
);
423 MODULE_AUTHOR("Ilya Petrov <ilya.muromec@gmail.com>");
424 MODULE_LICENSE("GPL");
425 MODULE_DESCRIPTION("NVEC battery and AC driver");
426 MODULE_ALIAS("platform:nvec-power");