aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQuentin Schulz <quentin.schulz@free-electrons.com>2017-01-27 03:54:43 -0500
committerSebastian Reichel <sre@kernel.org>2017-01-29 17:15:18 -0500
commit744cc304a18f1c9de4f3215fbe93fe878f934179 (patch)
tree706be76248208a0ff17e6d749e4555edbf832354
parent38fa69682fe91295f42cf2153bd1cf74eea35740 (diff)
power: supply: add AC power supply driver for AXP20X and AXP22X PMICs
The X-Powers AXP20X and AXP22X PMICs expose the status of AC power supply. Moreover, the AXP20X can also expose the current current and voltage values of the AC power supply. This adds the driver which exposes the status of the AC power supply of the AXP20X and AXP22X PMICs. Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com> Acked-by: Jonathan Cameron <jic23@kernel.org> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> [removed unused elements from struct axp20x_ac_power] Signed-off-by: Sebastian Reichel <sre@kernel.org>
-rw-r--r--drivers/power/supply/Kconfig12
-rw-r--r--drivers/power/supply/Makefile1
-rw-r--r--drivers/power/supply/axp20x_ac_power.c253
3 files changed, 266 insertions, 0 deletions
diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig
index 5f134cf8988c..da54ac88f068 100644
--- a/drivers/power/supply/Kconfig
+++ b/drivers/power/supply/Kconfig
@@ -220,6 +220,18 @@ config BATTERY_DA9150
220 This driver can also be built as a module. If so, the module will be 220 This driver can also be built as a module. If so, the module will be
221 called da9150-fg. 221 called da9150-fg.
222 222
223config CHARGER_AXP20X
224 tristate "X-Powers AXP20X and AXP22X AC power supply driver"
225 depends on MFD_AXP20X
226 depends on AXP20X_ADC
227 depends on IIO
228 help
229 Say Y here to enable support for X-Powers AXP20X and AXP22X PMICs' AC
230 power supply.
231
232 This driver can also be built as a module. If so, the module will be
233 called axp20x_ac_power.
234
223config AXP288_CHARGER 235config AXP288_CHARGER
224 tristate "X-Powers AXP288 Charger" 236 tristate "X-Powers AXP288 Charger"
225 depends on MFD_AXP20X && EXTCON_AXP288 237 depends on MFD_AXP20X && EXTCON_AXP288
diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile
index ed48324ea2e9..3789a2c06fdf 100644
--- a/drivers/power/supply/Makefile
+++ b/drivers/power/supply/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_TEST_POWER) += test_power.o
18 18
19obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o 19obj-$(CONFIG_BATTERY_88PM860X) += 88pm860x_battery.o
20obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o 20obj-$(CONFIG_BATTERY_ACT8945A) += act8945a_charger.o
21obj-$(CONFIG_CHARGER_AXP20X) += axp20x_ac_power.o
21obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o 22obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
22obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o 23obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o
23obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o 24obj-$(CONFIG_BATTERY_DS2781) += ds2781_battery.o
diff --git a/drivers/power/supply/axp20x_ac_power.c b/drivers/power/supply/axp20x_ac_power.c
new file mode 100644
index 000000000000..38f4e87cf24d
--- /dev/null
+++ b/drivers/power/supply/axp20x_ac_power.c
@@ -0,0 +1,253 @@
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;
162 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 = (struct 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
230static const struct of_device_id axp20x_ac_power_match[] = {
231 {
232 .compatible = "x-powers,axp202-ac-power-supply",
233 .data = (void *)&axp20x_data,
234 }, {
235 .compatible = "x-powers,axp221-ac-power-supply",
236 .data = (void *)&axp22x_data,
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");