power: supply: account for const type of of_device_id.data
[linux-2.6-block.git] / drivers / power / supply / axp20x_ac_power.c
1 /*
2  * AXP20X and AXP22X PMICs' ACIN power supply driver
3  *
4  * Copyright (C) 2016 Free Electrons
5  *      Quentin Schulz <quentin.schulz@free-electrons.com>
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under  the terms of the GNU General  Public License as published by the
9  * Free Software Foundation;  either version 2 of the License, or (at your
10  * option) any later version.
11  */
12
13 #include <linux/device.h>
14 #include <linux/init.h>
15 #include <linux/interrupt.h>
16 #include <linux/kernel.h>
17 #include <linux/mfd/axp20x.h>
18 #include <linux/module.h>
19 #include <linux/of.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
22 #include <linux/power_supply.h>
23 #include <linux/regmap.h>
24 #include <linux/slab.h>
25 #include <linux/iio/consumer.h>
26
27 #define AXP20X_PWR_STATUS_ACIN_PRESENT  BIT(7)
28 #define AXP20X_PWR_STATUS_ACIN_AVAIL    BIT(6)
29
30 #define DRVNAME "axp20x-ac-power-supply"
31
32 struct axp20x_ac_power {
33         struct regmap *regmap;
34         struct power_supply *supply;
35         struct iio_channel *acin_v;
36         struct iio_channel *acin_i;
37 };
38
39 static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
40 {
41         struct axp20x_ac_power *power = devid;
42
43         power_supply_changed(power->supply);
44
45         return IRQ_HANDLED;
46 }
47
48 static int axp20x_ac_power_get_property(struct power_supply *psy,
49                                         enum power_supply_property psp,
50                                         union power_supply_propval *val)
51 {
52         struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
53         int ret, reg;
54
55         switch (psp) {
56         case POWER_SUPPLY_PROP_HEALTH:
57                 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
58                 if (ret)
59                         return ret;
60
61                 if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
62                         val->intval = POWER_SUPPLY_HEALTH_GOOD;
63                         return 0;
64                 }
65
66                 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
67                 return 0;
68
69         case POWER_SUPPLY_PROP_PRESENT:
70                 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
71                 if (ret)
72                         return ret;
73
74                 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
75                 return 0;
76
77         case POWER_SUPPLY_PROP_ONLINE:
78                 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
79                 if (ret)
80                         return ret;
81
82                 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
83                 return 0;
84
85         case POWER_SUPPLY_PROP_VOLTAGE_NOW:
86                 ret = iio_read_channel_processed(power->acin_v, &val->intval);
87                 if (ret)
88                         return ret;
89
90                 /* IIO framework gives mV but Power Supply framework gives uV */
91                 val->intval *= 1000;
92
93                 return 0;
94
95         case POWER_SUPPLY_PROP_CURRENT_NOW:
96                 ret = iio_read_channel_processed(power->acin_i, &val->intval);
97                 if (ret)
98                         return ret;
99
100                 /* IIO framework gives mA but Power Supply framework gives uA */
101                 val->intval *= 1000;
102
103                 return 0;
104
105         default:
106                 return -EINVAL;
107         }
108
109         return -EINVAL;
110 }
111
112 static enum power_supply_property axp20x_ac_power_properties[] = {
113         POWER_SUPPLY_PROP_HEALTH,
114         POWER_SUPPLY_PROP_PRESENT,
115         POWER_SUPPLY_PROP_ONLINE,
116         POWER_SUPPLY_PROP_VOLTAGE_NOW,
117         POWER_SUPPLY_PROP_CURRENT_NOW,
118 };
119
120 static enum power_supply_property axp22x_ac_power_properties[] = {
121         POWER_SUPPLY_PROP_HEALTH,
122         POWER_SUPPLY_PROP_PRESENT,
123         POWER_SUPPLY_PROP_ONLINE,
124 };
125
126 static const struct power_supply_desc axp20x_ac_power_desc = {
127         .name = "axp20x-ac",
128         .type = POWER_SUPPLY_TYPE_MAINS,
129         .properties = axp20x_ac_power_properties,
130         .num_properties = ARRAY_SIZE(axp20x_ac_power_properties),
131         .get_property = axp20x_ac_power_get_property,
132 };
133
134 static const struct power_supply_desc axp22x_ac_power_desc = {
135         .name = "axp22x-ac",
136         .type = POWER_SUPPLY_TYPE_MAINS,
137         .properties = axp22x_ac_power_properties,
138         .num_properties = ARRAY_SIZE(axp22x_ac_power_properties),
139         .get_property = axp20x_ac_power_get_property,
140 };
141
142 struct axp_data {
143         const struct power_supply_desc  *power_desc;
144         bool                            acin_adc;
145 };
146
147 static const struct axp_data axp20x_data = {
148         .power_desc = &axp20x_ac_power_desc,
149         .acin_adc = true,
150 };
151
152 static const struct axp_data axp22x_data = {
153         .power_desc = &axp22x_ac_power_desc,
154         .acin_adc = false,
155 };
156
157 static int axp20x_ac_power_probe(struct platform_device *pdev)
158 {
159         struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
160         struct power_supply_config psy_cfg = {};
161         struct axp20x_ac_power *power;
162         const struct axp_data *axp_data;
163         static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
164                 NULL };
165         int i, irq, ret;
166
167         if (!of_device_is_available(pdev->dev.of_node))
168                 return -ENODEV;
169
170         if (!axp20x) {
171                 dev_err(&pdev->dev, "Parent drvdata not set\n");
172                 return -EINVAL;
173         }
174
175         power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
176         if (!power)
177                 return -ENOMEM;
178
179         axp_data = of_device_get_match_data(&pdev->dev);
180
181         if (axp_data->acin_adc) {
182                 power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
183                 if (IS_ERR(power->acin_v)) {
184                         if (PTR_ERR(power->acin_v) == -ENODEV)
185                                 return -EPROBE_DEFER;
186                         return PTR_ERR(power->acin_v);
187                 }
188
189                 power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i");
190                 if (IS_ERR(power->acin_i)) {
191                         if (PTR_ERR(power->acin_i) == -ENODEV)
192                                 return -EPROBE_DEFER;
193                         return PTR_ERR(power->acin_i);
194                 }
195         }
196
197         power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
198
199         platform_set_drvdata(pdev, power);
200
201         psy_cfg.of_node = pdev->dev.of_node;
202         psy_cfg.drv_data = power;
203
204         power->supply = devm_power_supply_register(&pdev->dev,
205                                                    axp_data->power_desc,
206                                                    &psy_cfg);
207         if (IS_ERR(power->supply))
208                 return PTR_ERR(power->supply);
209
210         /* Request irqs after registering, as irqs may trigger immediately */
211         for (i = 0; irq_names[i]; i++) {
212                 irq = platform_get_irq_byname(pdev, irq_names[i]);
213                 if (irq < 0) {
214                         dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
215                                  irq_names[i], irq);
216                         continue;
217                 }
218                 irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
219                 ret = devm_request_any_context_irq(&pdev->dev, irq,
220                                                    axp20x_ac_power_irq, 0,
221                                                    DRVNAME, power);
222                 if (ret < 0)
223                         dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
224                                  irq_names[i], ret);
225         }
226
227         return 0;
228 }
229
230 static const struct of_device_id axp20x_ac_power_match[] = {
231         {
232                 .compatible = "x-powers,axp202-ac-power-supply",
233                 .data = &axp20x_data,
234         }, {
235                 .compatible = "x-powers,axp221-ac-power-supply",
236                 .data = &axp22x_data,
237         }, { /* sentinel */ }
238 };
239 MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
240
241 static struct platform_driver axp20x_ac_power_driver = {
242         .probe = axp20x_ac_power_probe,
243         .driver = {
244                 .name = DRVNAME,
245                 .of_match_table = axp20x_ac_power_match,
246         },
247 };
248
249 module_platform_driver(axp20x_ac_power_driver);
250
251 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
252 MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
253 MODULE_LICENSE("GPL");