GNU Linux-libre 4.14.332-gnu1
[releases.git] / drivers / power / supply / twl4030_madc_battery.c
1 /*
2  * Dumb driver for LiIon batteries using TWL4030 madc.
3  *
4  * Copyright 2013 Golden Delicious Computers
5  * Lukas Märdian <lukas@goldelico.com>
6  *
7  * Based on dumb driver for gta01 battery
8  * Copyright 2009 Openmoko, Inc
9  * Balaji Rao <balajirrao@openmoko.org>
10  */
11
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>
22
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;
29 };
30
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,
42 };
43
44 static int madc_read(struct iio_channel *channel)
45 {
46         int val, err;
47         err = iio_read_channel_processed(channel, &val);
48         if (err < 0)
49                 return err;
50
51         return val;
52 }
53
54 static int twl4030_madc_bat_get_charging_status(struct twl4030_madc_battery *bt)
55 {
56         return (madc_read(bt->channel_ichg) > 0) ? 1 : 0;
57 }
58
59 static int twl4030_madc_bat_get_voltage(struct twl4030_madc_battery *bt)
60 {
61         return madc_read(bt->channel_vbat);
62 }
63
64 static int twl4030_madc_bat_get_current(struct twl4030_madc_battery *bt)
65 {
66         return madc_read(bt->channel_ichg) * 1000;
67 }
68
69 static int twl4030_madc_bat_get_temp(struct twl4030_madc_battery *bt)
70 {
71         return madc_read(bt->channel_temp) * 10;
72 }
73
74 static int twl4030_madc_bat_voltscale(struct twl4030_madc_battery *bat,
75                                         int volt)
76 {
77         struct twl4030_madc_bat_calibration *calibration;
78         int i, res = 0;
79
80         /* choose charging curve */
81         if (twl4030_madc_bat_get_charging_status(bat))
82                 calibration = bat->pdata->charging;
83         else
84                 calibration = bat->pdata->discharging;
85
86         if (volt > calibration[0].voltage) {
87                 res = calibration[0].level;
88         } else {
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);
99                                 break;
100                         }
101                 }
102         }
103         return res;
104 }
105
106 static int twl4030_madc_bat_get_property(struct power_supply *psy,
107                                         enum power_supply_property psp,
108                                         union power_supply_propval *val)
109 {
110         struct twl4030_madc_battery *bat = power_supply_get_drvdata(psy);
111
112         switch (psp) {
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;
117                 else {
118                         if (twl4030_madc_bat_get_charging_status(bat))
119                                 val->intval = POWER_SUPPLY_STATUS_CHARGING;
120                         else
121                                 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
122                 }
123                 break;
124         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
125                 val->intval = twl4030_madc_bat_get_voltage(bat) * 1000;
126                 break;
127         case POWER_SUPPLY_PROP_TECHNOLOGY:
128                 val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
129                 break;
130         case POWER_SUPPLY_PROP_CURRENT_NOW:
131                 val->intval = twl4030_madc_bat_get_current(bat);
132                 break;
133         case POWER_SUPPLY_PROP_PRESENT:
134                 /* assume battery is always present */
135                 val->intval = 1;
136                 break;
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;
141                         break;
142                 }
143         case POWER_SUPPLY_PROP_CAPACITY:
144                 val->intval = twl4030_madc_bat_voltscale(bat,
145                                         twl4030_madc_bat_get_voltage(bat));
146                 break;
147         case POWER_SUPPLY_PROP_CHARGE_FULL:
148                 val->intval = bat->pdata->capacity;
149                 break;
150         case POWER_SUPPLY_PROP_TEMP:
151                 val->intval = twl4030_madc_bat_get_temp(bat);
152                 break;
153         case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: {
154                         int percent = twl4030_madc_bat_voltscale(bat,
155                                         twl4030_madc_bat_get_voltage(bat));
156                         /* in mAh */
157                         int chg = (percent * (bat->pdata->capacity/1000))/100;
158
159                         /* assume discharge with 400 mA (ca. 1.5W) */
160                         val->intval = (3600l * chg) / 400;
161                         break;
162                 }
163         default:
164                 return -EINVAL;
165         }
166
167         return 0;
168 }
169
170 static void twl4030_madc_bat_ext_changed(struct power_supply *psy)
171 {
172         power_supply_changed(psy);
173 }
174
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,
182
183 };
184
185 static int twl4030_cmp(const void *a, const void *b)
186 {
187         return ((struct twl4030_madc_bat_calibration *)b)->voltage -
188                 ((struct twl4030_madc_bat_calibration *)a)->voltage;
189 }
190
191 static int twl4030_madc_battery_probe(struct platform_device *pdev)
192 {
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 = {};
196         int ret = 0;
197
198         twl4030_madc_bat = devm_kzalloc(&pdev->dev, sizeof(*twl4030_madc_bat),
199                                 GFP_KERNEL);
200         if (!twl4030_madc_bat)
201                 return -ENOMEM;
202
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);
206                 goto err;
207         }
208
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);
212                 goto err_temp;
213         }
214
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);
218                 goto err_ichg;
219         }
220
221         /* sort charging and discharging calibration data */
222         sort(pdata->charging, pdata->charging_size,
223                 sizeof(struct twl4030_madc_bat_calibration),
224                 twl4030_cmp, NULL);
225         sort(pdata->discharging, pdata->discharging_size,
226                 sizeof(struct twl4030_madc_bat_calibration),
227                 twl4030_cmp, NULL);
228
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,
234                                                       &psy_cfg);
235         if (IS_ERR(twl4030_madc_bat->psy)) {
236                 ret = PTR_ERR(twl4030_madc_bat->psy);
237                 goto err_vbat;
238         }
239
240         return 0;
241
242 err_vbat:
243         iio_channel_release(twl4030_madc_bat->channel_vbat);
244 err_ichg:
245         iio_channel_release(twl4030_madc_bat->channel_ichg);
246 err_temp:
247         iio_channel_release(twl4030_madc_bat->channel_temp);
248 err:
249         return ret;
250 }
251
252 static int twl4030_madc_battery_remove(struct platform_device *pdev)
253 {
254         struct twl4030_madc_battery *bat = platform_get_drvdata(pdev);
255
256         power_supply_unregister(bat->psy);
257
258         iio_channel_release(bat->channel_vbat);
259         iio_channel_release(bat->channel_ichg);
260         iio_channel_release(bat->channel_temp);
261
262         return 0;
263 }
264
265 static struct platform_driver twl4030_madc_battery_driver = {
266         .driver = {
267                 .name = "twl4030_madc_battery",
268         },
269         .probe  = twl4030_madc_battery_probe,
270         .remove = twl4030_madc_battery_remove,
271 };
272 module_platform_driver(twl4030_madc_battery_driver);
273
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");