power: supply: account for const type of of_device_id.data
[linux-2.6-block.git] / drivers / power / supply / axp20x_ac_power.c
CommitLineData
744cc304
QS
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
32struct 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
39static 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
48static 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
112static 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
120static 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
126static 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
134static 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
142struct axp_data {
143 const struct power_supply_desc *power_desc;
144 bool acin_adc;
145};
146
147static const struct axp_data axp20x_data = {
148 .power_desc = &axp20x_ac_power_desc,
149 .acin_adc = true,
150};
151
152static const struct axp_data axp22x_data = {
153 .power_desc = &axp22x_ac_power_desc,
154 .acin_adc = false,
155};
156
157static 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;
a488ca6e 162 const struct axp_data *axp_data;
744cc304
QS
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
a488ca6e 179 axp_data = of_device_get_match_data(&pdev->dev);
744cc304
QS
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
230static const struct of_device_id axp20x_ac_power_match[] = {
231 {
232 .compatible = "x-powers,axp202-ac-power-supply",
a488ca6e 233 .data = &axp20x_data,
744cc304
QS
234 }, {
235 .compatible = "x-powers,axp221-ac-power-supply",
a488ca6e 236 .data = &axp22x_data,
744cc304
QS
237 }, { /* sentinel */ }
238};
239MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
240
241static 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
249module_platform_driver(axp20x_ac_power_driver);
250
251MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>");
252MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver");
253MODULE_LICENSE("GPL");