1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Generic battery driver code using IIO
3*4882a593Smuzhiyun * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
4*4882a593Smuzhiyun * based on jz4740-battery.c
5*4882a593Smuzhiyun * based on s3c_adc_battery.c
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
8*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive for
9*4882a593Smuzhiyun * more details.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun #include <linux/interrupt.h>
13*4882a593Smuzhiyun #include <linux/platform_device.h>
14*4882a593Smuzhiyun #include <linux/power_supply.h>
15*4882a593Smuzhiyun #include <linux/gpio.h>
16*4882a593Smuzhiyun #include <linux/err.h>
17*4882a593Smuzhiyun #include <linux/timer.h>
18*4882a593Smuzhiyun #include <linux/jiffies.h>
19*4882a593Smuzhiyun #include <linux/errno.h>
20*4882a593Smuzhiyun #include <linux/init.h>
21*4882a593Smuzhiyun #include <linux/module.h>
22*4882a593Smuzhiyun #include <linux/slab.h>
23*4882a593Smuzhiyun #include <linux/iio/consumer.h>
24*4882a593Smuzhiyun #include <linux/iio/types.h>
25*4882a593Smuzhiyun #include <linux/power/generic-adc-battery.h>
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun #define JITTER_DEFAULT 10 /* hope 10ms is enough */
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun enum gab_chan_type {
30*4882a593Smuzhiyun GAB_VOLTAGE = 0,
31*4882a593Smuzhiyun GAB_CURRENT,
32*4882a593Smuzhiyun GAB_POWER,
33*4882a593Smuzhiyun GAB_MAX_CHAN_TYPE
34*4882a593Smuzhiyun };
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun /*
37*4882a593Smuzhiyun * gab_chan_name suggests the standard channel names for commonly used
38*4882a593Smuzhiyun * channel types.
39*4882a593Smuzhiyun */
40*4882a593Smuzhiyun static const char *const gab_chan_name[] = {
41*4882a593Smuzhiyun [GAB_VOLTAGE] = "voltage",
42*4882a593Smuzhiyun [GAB_CURRENT] = "current",
43*4882a593Smuzhiyun [GAB_POWER] = "power",
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun struct gab {
47*4882a593Smuzhiyun struct power_supply *psy;
48*4882a593Smuzhiyun struct power_supply_desc psy_desc;
49*4882a593Smuzhiyun struct iio_channel *channel[GAB_MAX_CHAN_TYPE];
50*4882a593Smuzhiyun struct gab_platform_data *pdata;
51*4882a593Smuzhiyun struct delayed_work bat_work;
52*4882a593Smuzhiyun int level;
53*4882a593Smuzhiyun int status;
54*4882a593Smuzhiyun bool cable_plugged;
55*4882a593Smuzhiyun };
56*4882a593Smuzhiyun
to_generic_bat(struct power_supply * psy)57*4882a593Smuzhiyun static struct gab *to_generic_bat(struct power_supply *psy)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun return power_supply_get_drvdata(psy);
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
gab_ext_power_changed(struct power_supply * psy)62*4882a593Smuzhiyun static void gab_ext_power_changed(struct power_supply *psy)
63*4882a593Smuzhiyun {
64*4882a593Smuzhiyun struct gab *adc_bat = to_generic_bat(psy);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun static const enum power_supply_property gab_props[] = {
70*4882a593Smuzhiyun POWER_SUPPLY_PROP_STATUS,
71*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
72*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
73*4882a593Smuzhiyun POWER_SUPPLY_PROP_CHARGE_NOW,
74*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
75*4882a593Smuzhiyun POWER_SUPPLY_PROP_CURRENT_NOW,
76*4882a593Smuzhiyun POWER_SUPPLY_PROP_TECHNOLOGY,
77*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
78*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
79*4882a593Smuzhiyun POWER_SUPPLY_PROP_MODEL_NAME,
80*4882a593Smuzhiyun };
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun /*
83*4882a593Smuzhiyun * This properties are set based on the received platform data and this
84*4882a593Smuzhiyun * should correspond one-to-one with enum chan_type.
85*4882a593Smuzhiyun */
86*4882a593Smuzhiyun static const enum power_supply_property gab_dyn_props[] = {
87*4882a593Smuzhiyun POWER_SUPPLY_PROP_VOLTAGE_NOW,
88*4882a593Smuzhiyun POWER_SUPPLY_PROP_CURRENT_NOW,
89*4882a593Smuzhiyun POWER_SUPPLY_PROP_POWER_NOW,
90*4882a593Smuzhiyun };
91*4882a593Smuzhiyun
gab_charge_finished(struct gab * adc_bat)92*4882a593Smuzhiyun static bool gab_charge_finished(struct gab *adc_bat)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun struct gab_platform_data *pdata = adc_bat->pdata;
95*4882a593Smuzhiyun bool ret = gpio_get_value(pdata->gpio_charge_finished);
96*4882a593Smuzhiyun bool inv = pdata->gpio_inverted;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (!gpio_is_valid(pdata->gpio_charge_finished))
99*4882a593Smuzhiyun return false;
100*4882a593Smuzhiyun return ret ^ inv;
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
gab_get_status(struct gab * adc_bat)103*4882a593Smuzhiyun static int gab_get_status(struct gab *adc_bat)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun struct gab_platform_data *pdata = adc_bat->pdata;
106*4882a593Smuzhiyun struct power_supply_info *bat_info;
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun bat_info = &pdata->battery_info;
109*4882a593Smuzhiyun if (adc_bat->level == bat_info->charge_full_design)
110*4882a593Smuzhiyun return POWER_SUPPLY_STATUS_FULL;
111*4882a593Smuzhiyun return adc_bat->status;
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun
gab_prop_to_chan(enum power_supply_property psp)114*4882a593Smuzhiyun static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp)
115*4882a593Smuzhiyun {
116*4882a593Smuzhiyun switch (psp) {
117*4882a593Smuzhiyun case POWER_SUPPLY_PROP_POWER_NOW:
118*4882a593Smuzhiyun return GAB_POWER;
119*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_NOW:
120*4882a593Smuzhiyun return GAB_VOLTAGE;
121*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CURRENT_NOW:
122*4882a593Smuzhiyun return GAB_CURRENT;
123*4882a593Smuzhiyun default:
124*4882a593Smuzhiyun WARN_ON(1);
125*4882a593Smuzhiyun break;
126*4882a593Smuzhiyun }
127*4882a593Smuzhiyun return GAB_POWER;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
read_channel(struct gab * adc_bat,enum power_supply_property psp,int * result)130*4882a593Smuzhiyun static int read_channel(struct gab *adc_bat, enum power_supply_property psp,
131*4882a593Smuzhiyun int *result)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun int ret;
134*4882a593Smuzhiyun int chan_index;
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun chan_index = gab_prop_to_chan(psp);
137*4882a593Smuzhiyun ret = iio_read_channel_processed(adc_bat->channel[chan_index],
138*4882a593Smuzhiyun result);
139*4882a593Smuzhiyun if (ret < 0)
140*4882a593Smuzhiyun pr_err("read channel error\n");
141*4882a593Smuzhiyun return ret;
142*4882a593Smuzhiyun }
143*4882a593Smuzhiyun
gab_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)144*4882a593Smuzhiyun static int gab_get_property(struct power_supply *psy,
145*4882a593Smuzhiyun enum power_supply_property psp, union power_supply_propval *val)
146*4882a593Smuzhiyun {
147*4882a593Smuzhiyun struct gab *adc_bat;
148*4882a593Smuzhiyun struct gab_platform_data *pdata;
149*4882a593Smuzhiyun struct power_supply_info *bat_info;
150*4882a593Smuzhiyun int result = 0;
151*4882a593Smuzhiyun int ret = 0;
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun adc_bat = to_generic_bat(psy);
154*4882a593Smuzhiyun if (!adc_bat) {
155*4882a593Smuzhiyun dev_err(&psy->dev, "no battery infos ?!\n");
156*4882a593Smuzhiyun return -EINVAL;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun pdata = adc_bat->pdata;
159*4882a593Smuzhiyun bat_info = &pdata->battery_info;
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun switch (psp) {
162*4882a593Smuzhiyun case POWER_SUPPLY_PROP_STATUS:
163*4882a593Smuzhiyun val->intval = gab_get_status(adc_bat);
164*4882a593Smuzhiyun break;
165*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
166*4882a593Smuzhiyun val->intval = 0;
167*4882a593Smuzhiyun break;
168*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_NOW:
169*4882a593Smuzhiyun val->intval = pdata->cal_charge(result);
170*4882a593Smuzhiyun break;
171*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_NOW:
172*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CURRENT_NOW:
173*4882a593Smuzhiyun case POWER_SUPPLY_PROP_POWER_NOW:
174*4882a593Smuzhiyun ret = read_channel(adc_bat, psp, &result);
175*4882a593Smuzhiyun if (ret < 0)
176*4882a593Smuzhiyun goto err;
177*4882a593Smuzhiyun val->intval = result;
178*4882a593Smuzhiyun break;
179*4882a593Smuzhiyun case POWER_SUPPLY_PROP_TECHNOLOGY:
180*4882a593Smuzhiyun val->intval = bat_info->technology;
181*4882a593Smuzhiyun break;
182*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
183*4882a593Smuzhiyun val->intval = bat_info->voltage_min_design;
184*4882a593Smuzhiyun break;
185*4882a593Smuzhiyun case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
186*4882a593Smuzhiyun val->intval = bat_info->voltage_max_design;
187*4882a593Smuzhiyun break;
188*4882a593Smuzhiyun case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
189*4882a593Smuzhiyun val->intval = bat_info->charge_full_design;
190*4882a593Smuzhiyun break;
191*4882a593Smuzhiyun case POWER_SUPPLY_PROP_MODEL_NAME:
192*4882a593Smuzhiyun val->strval = bat_info->name;
193*4882a593Smuzhiyun break;
194*4882a593Smuzhiyun default:
195*4882a593Smuzhiyun return -EINVAL;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun err:
198*4882a593Smuzhiyun return ret;
199*4882a593Smuzhiyun }
200*4882a593Smuzhiyun
gab_work(struct work_struct * work)201*4882a593Smuzhiyun static void gab_work(struct work_struct *work)
202*4882a593Smuzhiyun {
203*4882a593Smuzhiyun struct gab *adc_bat;
204*4882a593Smuzhiyun struct delayed_work *delayed_work;
205*4882a593Smuzhiyun bool is_plugged;
206*4882a593Smuzhiyun int status;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun delayed_work = to_delayed_work(work);
209*4882a593Smuzhiyun adc_bat = container_of(delayed_work, struct gab, bat_work);
210*4882a593Smuzhiyun status = adc_bat->status;
211*4882a593Smuzhiyun
212*4882a593Smuzhiyun is_plugged = power_supply_am_i_supplied(adc_bat->psy);
213*4882a593Smuzhiyun adc_bat->cable_plugged = is_plugged;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (!is_plugged)
216*4882a593Smuzhiyun adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
217*4882a593Smuzhiyun else if (gab_charge_finished(adc_bat))
218*4882a593Smuzhiyun adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
219*4882a593Smuzhiyun else
220*4882a593Smuzhiyun adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
221*4882a593Smuzhiyun
222*4882a593Smuzhiyun if (status != adc_bat->status)
223*4882a593Smuzhiyun power_supply_changed(adc_bat->psy);
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
gab_charged(int irq,void * dev_id)226*4882a593Smuzhiyun static irqreturn_t gab_charged(int irq, void *dev_id)
227*4882a593Smuzhiyun {
228*4882a593Smuzhiyun struct gab *adc_bat = dev_id;
229*4882a593Smuzhiyun struct gab_platform_data *pdata = adc_bat->pdata;
230*4882a593Smuzhiyun int delay;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
233*4882a593Smuzhiyun schedule_delayed_work(&adc_bat->bat_work,
234*4882a593Smuzhiyun msecs_to_jiffies(delay));
235*4882a593Smuzhiyun return IRQ_HANDLED;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
gab_probe(struct platform_device * pdev)238*4882a593Smuzhiyun static int gab_probe(struct platform_device *pdev)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun struct gab *adc_bat;
241*4882a593Smuzhiyun struct power_supply_desc *psy_desc;
242*4882a593Smuzhiyun struct power_supply_config psy_cfg = {};
243*4882a593Smuzhiyun struct gab_platform_data *pdata = pdev->dev.platform_data;
244*4882a593Smuzhiyun enum power_supply_property *properties;
245*4882a593Smuzhiyun int ret = 0;
246*4882a593Smuzhiyun int chan;
247*4882a593Smuzhiyun int index = ARRAY_SIZE(gab_props);
248*4882a593Smuzhiyun bool any = false;
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
251*4882a593Smuzhiyun if (!adc_bat) {
252*4882a593Smuzhiyun dev_err(&pdev->dev, "failed to allocate memory\n");
253*4882a593Smuzhiyun return -ENOMEM;
254*4882a593Smuzhiyun }
255*4882a593Smuzhiyun
256*4882a593Smuzhiyun psy_cfg.drv_data = adc_bat;
257*4882a593Smuzhiyun psy_desc = &adc_bat->psy_desc;
258*4882a593Smuzhiyun psy_desc->name = pdata->battery_info.name;
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun /* bootup default values for the battery */
261*4882a593Smuzhiyun adc_bat->cable_plugged = false;
262*4882a593Smuzhiyun adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
263*4882a593Smuzhiyun psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
264*4882a593Smuzhiyun psy_desc->get_property = gab_get_property;
265*4882a593Smuzhiyun psy_desc->external_power_changed = gab_ext_power_changed;
266*4882a593Smuzhiyun adc_bat->pdata = pdata;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun /*
269*4882a593Smuzhiyun * copying the static properties and allocating extra memory for holding
270*4882a593Smuzhiyun * the extra configurable properties received from platform data.
271*4882a593Smuzhiyun */
272*4882a593Smuzhiyun properties = kcalloc(ARRAY_SIZE(gab_props) +
273*4882a593Smuzhiyun ARRAY_SIZE(gab_chan_name),
274*4882a593Smuzhiyun sizeof(*properties),
275*4882a593Smuzhiyun GFP_KERNEL);
276*4882a593Smuzhiyun if (!properties) {
277*4882a593Smuzhiyun ret = -ENOMEM;
278*4882a593Smuzhiyun goto first_mem_fail;
279*4882a593Smuzhiyun }
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun memcpy(properties, gab_props, sizeof(gab_props));
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun /*
284*4882a593Smuzhiyun * getting channel from iio and copying the battery properties
285*4882a593Smuzhiyun * based on the channel supported by consumer device.
286*4882a593Smuzhiyun */
287*4882a593Smuzhiyun for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
288*4882a593Smuzhiyun adc_bat->channel[chan] = iio_channel_get(&pdev->dev,
289*4882a593Smuzhiyun gab_chan_name[chan]);
290*4882a593Smuzhiyun if (IS_ERR(adc_bat->channel[chan])) {
291*4882a593Smuzhiyun ret = PTR_ERR(adc_bat->channel[chan]);
292*4882a593Smuzhiyun adc_bat->channel[chan] = NULL;
293*4882a593Smuzhiyun } else {
294*4882a593Smuzhiyun /* copying properties for supported channels only */
295*4882a593Smuzhiyun int index2;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun for (index2 = 0; index2 < index; index2++) {
298*4882a593Smuzhiyun if (properties[index2] == gab_dyn_props[chan])
299*4882a593Smuzhiyun break; /* already known */
300*4882a593Smuzhiyun }
301*4882a593Smuzhiyun if (index2 == index) /* really new */
302*4882a593Smuzhiyun properties[index++] = gab_dyn_props[chan];
303*4882a593Smuzhiyun any = true;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun /* none of the channels are supported so let's bail out */
308*4882a593Smuzhiyun if (!any) {
309*4882a593Smuzhiyun ret = -ENODEV;
310*4882a593Smuzhiyun goto second_mem_fail;
311*4882a593Smuzhiyun }
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun /*
314*4882a593Smuzhiyun * Total number of properties is equal to static properties
315*4882a593Smuzhiyun * plus the dynamic properties.Some properties may not be set
316*4882a593Smuzhiyun * as come channels may be not be supported by the device.So
317*4882a593Smuzhiyun * we need to take care of that.
318*4882a593Smuzhiyun */
319*4882a593Smuzhiyun psy_desc->properties = properties;
320*4882a593Smuzhiyun psy_desc->num_properties = index;
321*4882a593Smuzhiyun
322*4882a593Smuzhiyun adc_bat->psy = power_supply_register(&pdev->dev, psy_desc, &psy_cfg);
323*4882a593Smuzhiyun if (IS_ERR(adc_bat->psy)) {
324*4882a593Smuzhiyun ret = PTR_ERR(adc_bat->psy);
325*4882a593Smuzhiyun goto err_reg_fail;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
328*4882a593Smuzhiyun INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work);
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun if (gpio_is_valid(pdata->gpio_charge_finished)) {
331*4882a593Smuzhiyun int irq;
332*4882a593Smuzhiyun ret = gpio_request(pdata->gpio_charge_finished, "charged");
333*4882a593Smuzhiyun if (ret)
334*4882a593Smuzhiyun goto gpio_req_fail;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun irq = gpio_to_irq(pdata->gpio_charge_finished);
337*4882a593Smuzhiyun ret = request_any_context_irq(irq, gab_charged,
338*4882a593Smuzhiyun IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
339*4882a593Smuzhiyun "battery charged", adc_bat);
340*4882a593Smuzhiyun if (ret < 0)
341*4882a593Smuzhiyun goto err_gpio;
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun platform_set_drvdata(pdev, adc_bat);
345*4882a593Smuzhiyun
346*4882a593Smuzhiyun /* Schedule timer to check current status */
347*4882a593Smuzhiyun schedule_delayed_work(&adc_bat->bat_work,
348*4882a593Smuzhiyun msecs_to_jiffies(0));
349*4882a593Smuzhiyun return 0;
350*4882a593Smuzhiyun
351*4882a593Smuzhiyun err_gpio:
352*4882a593Smuzhiyun gpio_free(pdata->gpio_charge_finished);
353*4882a593Smuzhiyun gpio_req_fail:
354*4882a593Smuzhiyun power_supply_unregister(adc_bat->psy);
355*4882a593Smuzhiyun err_reg_fail:
356*4882a593Smuzhiyun for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
357*4882a593Smuzhiyun if (adc_bat->channel[chan])
358*4882a593Smuzhiyun iio_channel_release(adc_bat->channel[chan]);
359*4882a593Smuzhiyun }
360*4882a593Smuzhiyun second_mem_fail:
361*4882a593Smuzhiyun kfree(properties);
362*4882a593Smuzhiyun first_mem_fail:
363*4882a593Smuzhiyun return ret;
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun
gab_remove(struct platform_device * pdev)366*4882a593Smuzhiyun static int gab_remove(struct platform_device *pdev)
367*4882a593Smuzhiyun {
368*4882a593Smuzhiyun int chan;
369*4882a593Smuzhiyun struct gab *adc_bat = platform_get_drvdata(pdev);
370*4882a593Smuzhiyun struct gab_platform_data *pdata = adc_bat->pdata;
371*4882a593Smuzhiyun
372*4882a593Smuzhiyun power_supply_unregister(adc_bat->psy);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun if (gpio_is_valid(pdata->gpio_charge_finished)) {
375*4882a593Smuzhiyun free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat);
376*4882a593Smuzhiyun gpio_free(pdata->gpio_charge_finished);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
380*4882a593Smuzhiyun if (adc_bat->channel[chan])
381*4882a593Smuzhiyun iio_channel_release(adc_bat->channel[chan]);
382*4882a593Smuzhiyun }
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun kfree(adc_bat->psy_desc.properties);
385*4882a593Smuzhiyun cancel_delayed_work_sync(&adc_bat->bat_work);
386*4882a593Smuzhiyun return 0;
387*4882a593Smuzhiyun }
388*4882a593Smuzhiyun
gab_suspend(struct device * dev)389*4882a593Smuzhiyun static int __maybe_unused gab_suspend(struct device *dev)
390*4882a593Smuzhiyun {
391*4882a593Smuzhiyun struct gab *adc_bat = dev_get_drvdata(dev);
392*4882a593Smuzhiyun
393*4882a593Smuzhiyun cancel_delayed_work_sync(&adc_bat->bat_work);
394*4882a593Smuzhiyun adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
395*4882a593Smuzhiyun return 0;
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun
gab_resume(struct device * dev)398*4882a593Smuzhiyun static int __maybe_unused gab_resume(struct device *dev)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun struct gab *adc_bat = dev_get_drvdata(dev);
401*4882a593Smuzhiyun struct gab_platform_data *pdata = adc_bat->pdata;
402*4882a593Smuzhiyun int delay;
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
405*4882a593Smuzhiyun
406*4882a593Smuzhiyun /* Schedule timer to check current status */
407*4882a593Smuzhiyun schedule_delayed_work(&adc_bat->bat_work,
408*4882a593Smuzhiyun msecs_to_jiffies(delay));
409*4882a593Smuzhiyun return 0;
410*4882a593Smuzhiyun }
411*4882a593Smuzhiyun
412*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(gab_pm_ops, gab_suspend, gab_resume);
413*4882a593Smuzhiyun
414*4882a593Smuzhiyun static struct platform_driver gab_driver = {
415*4882a593Smuzhiyun .driver = {
416*4882a593Smuzhiyun .name = "generic-adc-battery",
417*4882a593Smuzhiyun .pm = &gab_pm_ops,
418*4882a593Smuzhiyun },
419*4882a593Smuzhiyun .probe = gab_probe,
420*4882a593Smuzhiyun .remove = gab_remove,
421*4882a593Smuzhiyun };
422*4882a593Smuzhiyun module_platform_driver(gab_driver);
423*4882a593Smuzhiyun
424*4882a593Smuzhiyun MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
425*4882a593Smuzhiyun MODULE_DESCRIPTION("generic battery driver using IIO");
426*4882a593Smuzhiyun MODULE_LICENSE("GPL");
427