2 * Dumb driver for LiIon batteries using TWL4030 madc.
4 * Copyright 2013 Golden Delicious Computers
5 * Lukas Märdian <lukas@goldelico.com>
7 * Based on dumb driver for gta01 battery
8 * Copyright 2009 Openmoko, Inc
9 * Balaji Rao <balajirrao@openmoko.org>
12 #include <linux/module.h>
13 #include <linux/param.h>
14 #include <linux/delay.h>
15 #include <linux/workqueue.h>
16 #include <linux/platform_device.h>
17 #include <linux/power_supply.h>
18 #include <linux/slab.h>
19 #include <linux/sort.h>
20 #include <linux/power/twl4030_madc_battery.h>
21 #include <linux/iio/consumer.h>
23 struct twl4030_madc_battery
{
24 struct power_supply
*psy
;
25 struct twl4030_madc_bat_platform_data
*pdata
;
26 struct iio_channel
*channel_temp
;
27 struct iio_channel
*channel_ichg
;
28 struct iio_channel
*channel_vbat
;
31 static enum power_supply_property twl4030_madc_bat_props
[] = {
32 POWER_SUPPLY_PROP_PRESENT
,
33 POWER_SUPPLY_PROP_STATUS
,
34 POWER_SUPPLY_PROP_TECHNOLOGY
,
35 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
36 POWER_SUPPLY_PROP_CURRENT_NOW
,
37 POWER_SUPPLY_PROP_CAPACITY
,
38 POWER_SUPPLY_PROP_CHARGE_FULL
,
39 POWER_SUPPLY_PROP_CHARGE_NOW
,
40 POWER_SUPPLY_PROP_TEMP
,
41 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
,
44 static int madc_read(struct iio_channel
*channel
)
47 err
= iio_read_channel_processed(channel
, &val
);
54 static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery
*bt
)
56 return (madc_read(bt
->channel_ichg
) > 0) ? 1 : 0;
59 static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery
*bt
)
61 return madc_read(bt
->channel_vbat
);
64 static int twl4030_madc_bat_get_current(struct twl4030_madc_battery
*bt
)
66 return madc_read(bt
->channel_ichg
) * 1000;
69 static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery
*bt
)
71 return madc_read(bt
->channel_temp
) * 10;
74 static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery
*bat
,
77 struct twl4030_madc_bat_calibration
*calibration
;
80 /* choose charging curve */
81 if (twl4030_madc_bat_get_charging_status(bat
))
82 calibration
= bat
->pdata
->charging
;
84 calibration
= bat
->pdata
->discharging
;
86 if (volt
> calibration
[0].voltage
) {
87 res
= calibration
[0].level
;
89 for (i
= 0; calibration
[i
+1].voltage
>= 0; i
++) {
90 if (volt
<= calibration
[i
].voltage
&&
91 volt
>= calibration
[i
+1].voltage
) {
92 /* interval found - interpolate within range */
93 res
= calibration
[i
].level
-
94 ((calibration
[i
].voltage
- volt
) *
95 (calibration
[i
].level
-
96 calibration
[i
+1].level
)) /
97 (calibration
[i
].voltage
-
98 calibration
[i
+1].voltage
);
106 static int twl4030_madc_bat_get_property(struct power_supply
*psy
,
107 enum power_supply_property psp
,
108 union power_supply_propval
*val
)
110 struct twl4030_madc_battery
*bat
= power_supply_get_drvdata(psy
);
113 case POWER_SUPPLY_PROP_STATUS
:
114 if (twl4030_madc_bat_voltscale(bat
,
115 twl4030_madc_bat_get_voltage(bat
)) > 95)
116 val
->intval
= POWER_SUPPLY_STATUS_FULL
;
118 if (twl4030_madc_bat_get_charging_status(bat
))
119 val
->intval
= POWER_SUPPLY_STATUS_CHARGING
;
121 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
124 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
125 val
->intval
= twl4030_madc_bat_get_voltage(bat
) * 1000;
127 case POWER_SUPPLY_PROP_TECHNOLOGY
:
128 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
130 case POWER_SUPPLY_PROP_CURRENT_NOW
:
131 val
->intval
= twl4030_madc_bat_get_current(bat
);
133 case POWER_SUPPLY_PROP_PRESENT
:
134 /* assume battery is always present */
137 case POWER_SUPPLY_PROP_CHARGE_NOW
: {
138 int percent
= twl4030_madc_bat_voltscale(bat
,
139 twl4030_madc_bat_get_voltage(bat
));
140 val
->intval
= (percent
* bat
->pdata
->capacity
) / 100;
143 case POWER_SUPPLY_PROP_CAPACITY
:
144 val
->intval
= twl4030_madc_bat_voltscale(bat
,
145 twl4030_madc_bat_get_voltage(bat
));
147 case POWER_SUPPLY_PROP_CHARGE_FULL
:
148 val
->intval
= bat
->pdata
->capacity
;
150 case POWER_SUPPLY_PROP_TEMP
:
151 val
->intval
= twl4030_madc_bat_get_temp(bat
);
153 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
: {
154 int percent
= twl4030_madc_bat_voltscale(bat
,
155 twl4030_madc_bat_get_voltage(bat
));
157 int chg
= (percent
* (bat
->pdata
->capacity
/1000))/100;
159 /* assume discharge with 400 mA (ca. 1.5W) */
160 val
->intval
= (3600l * chg
) / 400;
170 static void twl4030_madc_bat_ext_changed(struct power_supply
*psy
)
172 power_supply_changed(psy
);
175 static const struct power_supply_desc twl4030_madc_bat_desc
= {
176 .name
= "twl4030_battery",
177 .type
= POWER_SUPPLY_TYPE_BATTERY
,
178 .properties
= twl4030_madc_bat_props
,
179 .num_properties
= ARRAY_SIZE(twl4030_madc_bat_props
),
180 .get_property
= twl4030_madc_bat_get_property
,
181 .external_power_changed
= twl4030_madc_bat_ext_changed
,
185 static int twl4030_cmp(const void *a
, const void *b
)
187 return ((struct twl4030_madc_bat_calibration
*)b
)->voltage
-
188 ((struct twl4030_madc_bat_calibration
*)a
)->voltage
;
191 static int twl4030_madc_battery_probe(struct platform_device
*pdev
)
193 struct twl4030_madc_battery
*twl4030_madc_bat
;
194 struct twl4030_madc_bat_platform_data
*pdata
= pdev
->dev
.platform_data
;
195 struct power_supply_config psy_cfg
= {};
198 twl4030_madc_bat
= devm_kzalloc(&pdev
->dev
, sizeof(*twl4030_madc_bat
),
200 if (!twl4030_madc_bat
)
203 twl4030_madc_bat
->channel_temp
= iio_channel_get(&pdev
->dev
, "temp");
204 if (IS_ERR(twl4030_madc_bat
->channel_temp
)) {
205 ret
= PTR_ERR(twl4030_madc_bat
->channel_temp
);
209 twl4030_madc_bat
->channel_ichg
= iio_channel_get(&pdev
->dev
, "ichg");
210 if (IS_ERR(twl4030_madc_bat
->channel_ichg
)) {
211 ret
= PTR_ERR(twl4030_madc_bat
->channel_ichg
);
215 twl4030_madc_bat
->channel_vbat
= iio_channel_get(&pdev
->dev
, "vbat");
216 if (IS_ERR(twl4030_madc_bat
->channel_vbat
)) {
217 ret
= PTR_ERR(twl4030_madc_bat
->channel_vbat
);
221 /* sort charging and discharging calibration data */
222 sort(pdata
->charging
, pdata
->charging_size
,
223 sizeof(struct twl4030_madc_bat_calibration
),
225 sort(pdata
->discharging
, pdata
->discharging_size
,
226 sizeof(struct twl4030_madc_bat_calibration
),
229 twl4030_madc_bat
->pdata
= pdata
;
230 platform_set_drvdata(pdev
, twl4030_madc_bat
);
231 psy_cfg
.drv_data
= twl4030_madc_bat
;
232 twl4030_madc_bat
->psy
= power_supply_register(&pdev
->dev
,
233 &twl4030_madc_bat_desc
,
235 if (IS_ERR(twl4030_madc_bat
->psy
)) {
236 ret
= PTR_ERR(twl4030_madc_bat
->psy
);
243 iio_channel_release(twl4030_madc_bat
->channel_vbat
);
245 iio_channel_release(twl4030_madc_bat
->channel_ichg
);
247 iio_channel_release(twl4030_madc_bat
->channel_temp
);
252 static int twl4030_madc_battery_remove(struct platform_device
*pdev
)
254 struct twl4030_madc_battery
*bat
= platform_get_drvdata(pdev
);
256 power_supply_unregister(bat
->psy
);
258 iio_channel_release(bat
->channel_vbat
);
259 iio_channel_release(bat
->channel_ichg
);
260 iio_channel_release(bat
->channel_temp
);
265 static struct platform_driver twl4030_madc_battery_driver
= {
267 .name
= "twl4030_madc_battery",
269 .probe
= twl4030_madc_battery_probe
,
270 .remove
= twl4030_madc_battery_remove
,
272 module_platform_driver(twl4030_madc_battery_driver
);
274 MODULE_LICENSE("GPL");
275 MODULE_AUTHOR("Lukas Märdian <lukas@goldelico.com>");
276 MODULE_DESCRIPTION("twl4030_madc battery driver");
277 MODULE_ALIAS("platform:twl4030_madc_battery");