1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Generic battery driver using IIO |
4 | * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com> |
5 | * Copyright (c) 2023, Sebastian Reichel <sre@kernel.org> |
6 | */ |
7 | #include <linux/interrupt.h> |
8 | #include <linux/platform_device.h> |
9 | #include <linux/power_supply.h> |
10 | #include <linux/gpio/consumer.h> |
11 | #include <linux/err.h> |
12 | #include <linux/timer.h> |
13 | #include <linux/jiffies.h> |
14 | #include <linux/errno.h> |
15 | #include <linux/init.h> |
16 | #include <linux/module.h> |
17 | #include <linux/slab.h> |
18 | #include <linux/iio/consumer.h> |
19 | #include <linux/iio/types.h> |
20 | #include <linux/of.h> |
21 | #include <linux/devm-helpers.h> |
22 | |
23 | #define JITTER_DEFAULT 10 /* hope 10ms is enough */ |
24 | |
25 | enum gab_chan_type { |
26 | GAB_VOLTAGE = 0, |
27 | GAB_CURRENT, |
28 | GAB_POWER, |
29 | GAB_TEMP, |
30 | GAB_MAX_CHAN_TYPE |
31 | }; |
32 | |
33 | /* |
34 | * gab_chan_name suggests the standard channel names for commonly used |
35 | * channel types. |
36 | */ |
37 | static const char *const gab_chan_name[] = { |
38 | [GAB_VOLTAGE] = "voltage" , |
39 | [GAB_CURRENT] = "current" , |
40 | [GAB_POWER] = "power" , |
41 | [GAB_TEMP] = "temperature" , |
42 | }; |
43 | |
44 | struct gab { |
45 | struct power_supply *psy; |
46 | struct power_supply_desc psy_desc; |
47 | struct iio_channel *channel[GAB_MAX_CHAN_TYPE]; |
48 | struct delayed_work bat_work; |
49 | int status; |
50 | struct gpio_desc *charge_finished; |
51 | }; |
52 | |
53 | static struct gab *to_generic_bat(struct power_supply *psy) |
54 | { |
55 | return power_supply_get_drvdata(psy); |
56 | } |
57 | |
58 | static void gab_ext_power_changed(struct power_supply *psy) |
59 | { |
60 | struct gab *adc_bat = to_generic_bat(psy); |
61 | |
62 | schedule_delayed_work(dwork: &adc_bat->bat_work, delay: msecs_to_jiffies(m: 0)); |
63 | } |
64 | |
65 | static const enum power_supply_property gab_props[] = { |
66 | POWER_SUPPLY_PROP_STATUS, |
67 | }; |
68 | |
69 | /* |
70 | * This properties are set based on the received platform data and this |
71 | * should correspond one-to-one with enum chan_type. |
72 | */ |
73 | static const enum power_supply_property gab_dyn_props[] = { |
74 | POWER_SUPPLY_PROP_VOLTAGE_NOW, |
75 | POWER_SUPPLY_PROP_CURRENT_NOW, |
76 | POWER_SUPPLY_PROP_POWER_NOW, |
77 | POWER_SUPPLY_PROP_TEMP, |
78 | }; |
79 | |
80 | static bool gab_charge_finished(struct gab *adc_bat) |
81 | { |
82 | if (!adc_bat->charge_finished) |
83 | return false; |
84 | return gpiod_get_value(desc: adc_bat->charge_finished); |
85 | } |
86 | |
87 | static int gab_read_channel(struct gab *adc_bat, enum gab_chan_type channel, |
88 | int *result) |
89 | { |
90 | int ret; |
91 | |
92 | ret = iio_read_channel_processed(chan: adc_bat->channel[channel], val: result); |
93 | if (ret < 0) |
94 | dev_err(&adc_bat->psy->dev, "read channel error: %d\n" , ret); |
95 | else |
96 | *result *= 1000; |
97 | |
98 | return ret; |
99 | } |
100 | |
101 | static int gab_get_property(struct power_supply *psy, |
102 | enum power_supply_property psp, union power_supply_propval *val) |
103 | { |
104 | struct gab *adc_bat = to_generic_bat(psy); |
105 | |
106 | switch (psp) { |
107 | case POWER_SUPPLY_PROP_STATUS: |
108 | val->intval = adc_bat->status; |
109 | return 0; |
110 | case POWER_SUPPLY_PROP_VOLTAGE_NOW: |
111 | return gab_read_channel(adc_bat, channel: GAB_VOLTAGE, result: &val->intval); |
112 | case POWER_SUPPLY_PROP_CURRENT_NOW: |
113 | return gab_read_channel(adc_bat, channel: GAB_CURRENT, result: &val->intval); |
114 | case POWER_SUPPLY_PROP_POWER_NOW: |
115 | return gab_read_channel(adc_bat, channel: GAB_POWER, result: &val->intval); |
116 | case POWER_SUPPLY_PROP_TEMP: |
117 | return gab_read_channel(adc_bat, channel: GAB_TEMP, result: &val->intval); |
118 | default: |
119 | return -EINVAL; |
120 | } |
121 | } |
122 | |
123 | static void gab_work(struct work_struct *work) |
124 | { |
125 | struct gab *adc_bat; |
126 | struct delayed_work *delayed_work; |
127 | int status; |
128 | |
129 | delayed_work = to_delayed_work(work); |
130 | adc_bat = container_of(delayed_work, struct gab, bat_work); |
131 | status = adc_bat->status; |
132 | |
133 | if (!power_supply_am_i_supplied(psy: adc_bat->psy)) |
134 | adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; |
135 | else if (gab_charge_finished(adc_bat)) |
136 | adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; |
137 | else |
138 | adc_bat->status = POWER_SUPPLY_STATUS_CHARGING; |
139 | |
140 | if (status != adc_bat->status) |
141 | power_supply_changed(psy: adc_bat->psy); |
142 | } |
143 | |
144 | static irqreturn_t gab_charged(int irq, void *dev_id) |
145 | { |
146 | struct gab *adc_bat = dev_id; |
147 | |
148 | schedule_delayed_work(dwork: &adc_bat->bat_work, |
149 | delay: msecs_to_jiffies(JITTER_DEFAULT)); |
150 | |
151 | return IRQ_HANDLED; |
152 | } |
153 | |
154 | static int gab_probe(struct platform_device *pdev) |
155 | { |
156 | struct gab *adc_bat; |
157 | struct power_supply_desc *psy_desc; |
158 | struct power_supply_config psy_cfg = {}; |
159 | enum power_supply_property *properties; |
160 | int ret = 0; |
161 | int chan; |
162 | int index = ARRAY_SIZE(gab_props); |
163 | bool any = false; |
164 | |
165 | adc_bat = devm_kzalloc(dev: &pdev->dev, size: sizeof(*adc_bat), GFP_KERNEL); |
166 | if (!adc_bat) |
167 | return -ENOMEM; |
168 | |
169 | psy_cfg.of_node = pdev->dev.of_node; |
170 | psy_cfg.drv_data = adc_bat; |
171 | psy_desc = &adc_bat->psy_desc; |
172 | psy_desc->name = dev_name(dev: &pdev->dev); |
173 | |
174 | /* bootup default values for the battery */ |
175 | adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING; |
176 | psy_desc->type = POWER_SUPPLY_TYPE_BATTERY; |
177 | psy_desc->get_property = gab_get_property; |
178 | psy_desc->external_power_changed = gab_ext_power_changed; |
179 | |
180 | /* |
181 | * copying the static properties and allocating extra memory for holding |
182 | * the extra configurable properties received from platform data. |
183 | */ |
184 | properties = devm_kcalloc(dev: &pdev->dev, |
185 | ARRAY_SIZE(gab_props) + |
186 | ARRAY_SIZE(gab_chan_name), |
187 | size: sizeof(*properties), |
188 | GFP_KERNEL); |
189 | if (!properties) |
190 | return -ENOMEM; |
191 | |
192 | memcpy(properties, gab_props, sizeof(gab_props)); |
193 | |
194 | /* |
195 | * getting channel from iio and copying the battery properties |
196 | * based on the channel supported by consumer device. |
197 | */ |
198 | for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) { |
199 | adc_bat->channel[chan] = devm_iio_channel_get(dev: &pdev->dev, consumer_channel: gab_chan_name[chan]); |
200 | if (IS_ERR(ptr: adc_bat->channel[chan])) { |
201 | ret = PTR_ERR(ptr: adc_bat->channel[chan]); |
202 | if (ret != -ENODEV) |
203 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Failed to get ADC channel %s\n" , gab_chan_name[chan]); |
204 | adc_bat->channel[chan] = NULL; |
205 | } else if (adc_bat->channel[chan]) { |
206 | /* copying properties for supported channels only */ |
207 | int index2; |
208 | |
209 | for (index2 = 0; index2 < index; index2++) { |
210 | if (properties[index2] == gab_dyn_props[chan]) |
211 | break; /* already known */ |
212 | } |
213 | if (index2 == index) /* really new */ |
214 | properties[index++] = gab_dyn_props[chan]; |
215 | any = true; |
216 | } |
217 | } |
218 | |
219 | /* none of the channels are supported so let's bail out */ |
220 | if (!any) |
221 | return dev_err_probe(dev: &pdev->dev, err: -ENODEV, fmt: "Failed to get any ADC channel\n" ); |
222 | |
223 | /* |
224 | * Total number of properties is equal to static properties |
225 | * plus the dynamic properties.Some properties may not be set |
226 | * as come channels may be not be supported by the device.So |
227 | * we need to take care of that. |
228 | */ |
229 | psy_desc->properties = properties; |
230 | psy_desc->num_properties = index; |
231 | |
232 | adc_bat->psy = devm_power_supply_register(parent: &pdev->dev, desc: psy_desc, cfg: &psy_cfg); |
233 | if (IS_ERR(ptr: adc_bat->psy)) |
234 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: adc_bat->psy), fmt: "Failed to register power-supply device\n" ); |
235 | |
236 | ret = devm_delayed_work_autocancel(dev: &pdev->dev, w: &adc_bat->bat_work, worker: gab_work); |
237 | if (ret) |
238 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Failed to register delayed work\n" ); |
239 | |
240 | adc_bat->charge_finished = devm_gpiod_get_optional(dev: &pdev->dev, con_id: "charged" , flags: GPIOD_IN); |
241 | if (adc_bat->charge_finished) { |
242 | int irq; |
243 | |
244 | irq = gpiod_to_irq(desc: adc_bat->charge_finished); |
245 | ret = devm_request_any_context_irq(dev: &pdev->dev, irq, handler: gab_charged, |
246 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, |
247 | devname: "battery charged" , dev_id: adc_bat); |
248 | if (ret < 0) |
249 | return dev_err_probe(dev: &pdev->dev, err: ret, fmt: "Failed to register irq\n" ); |
250 | } |
251 | |
252 | platform_set_drvdata(pdev, data: adc_bat); |
253 | |
254 | /* Schedule timer to check current status */ |
255 | schedule_delayed_work(dwork: &adc_bat->bat_work, |
256 | delay: msecs_to_jiffies(m: 0)); |
257 | return 0; |
258 | } |
259 | |
260 | static int __maybe_unused gab_suspend(struct device *dev) |
261 | { |
262 | struct gab *adc_bat = dev_get_drvdata(dev); |
263 | |
264 | cancel_delayed_work_sync(dwork: &adc_bat->bat_work); |
265 | adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN; |
266 | return 0; |
267 | } |
268 | |
269 | static int __maybe_unused gab_resume(struct device *dev) |
270 | { |
271 | struct gab *adc_bat = dev_get_drvdata(dev); |
272 | |
273 | /* Schedule timer to check current status */ |
274 | schedule_delayed_work(dwork: &adc_bat->bat_work, |
275 | delay: msecs_to_jiffies(JITTER_DEFAULT)); |
276 | |
277 | return 0; |
278 | } |
279 | |
280 | static SIMPLE_DEV_PM_OPS(gab_pm_ops, gab_suspend, gab_resume); |
281 | |
282 | static const struct of_device_id gab_match[] = { |
283 | { .compatible = "adc-battery" }, |
284 | { } |
285 | }; |
286 | MODULE_DEVICE_TABLE(of, gab_match); |
287 | |
288 | static struct platform_driver gab_driver = { |
289 | .driver = { |
290 | .name = "generic-adc-battery" , |
291 | .pm = &gab_pm_ops, |
292 | .of_match_table = gab_match, |
293 | }, |
294 | .probe = gab_probe, |
295 | }; |
296 | module_platform_driver(gab_driver); |
297 | |
298 | MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>" ); |
299 | MODULE_DESCRIPTION("generic battery driver using IIO" ); |
300 | MODULE_LICENSE("GPL" ); |
301 | |