Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[linux/fpc-iii.git] / drivers / power / supply / axp20x_ac_power.c
blob38f4e87cf24dfc5d08938932b3151fbbcee510d0
1 /*
2 * AXP20X and AXP22X PMICs' ACIN power supply driver
4 * Copyright (C) 2016 Free Electrons
5 * Quentin Schulz <quentin.schulz@free-electrons.com>
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.
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>
27 #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
28 #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6)
30 #define DRVNAME "axp20x-ac-power-supply"
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;
39 static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
41 struct axp20x_ac_power *power = devid;
43 power_supply_changed(power->supply);
45 return IRQ_HANDLED;
48 static int axp20x_ac_power_get_property(struct power_supply *psy,
49 enum power_supply_property psp,
50 union power_supply_propval *val)
52 struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
53 int ret, reg;
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;
61 if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) {
62 val->intval = POWER_SUPPLY_HEALTH_GOOD;
63 return 0;
66 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
67 return 0;
69 case POWER_SUPPLY_PROP_PRESENT:
70 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
71 if (ret)
72 return ret;
74 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT);
75 return 0;
77 case POWER_SUPPLY_PROP_ONLINE:
78 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &reg);
79 if (ret)
80 return ret;
82 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
83 return 0;
85 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
86 ret = iio_read_channel_processed(power->acin_v, &val->intval);
87 if (ret)
88 return ret;
90 /* IIO framework gives mV but Power Supply framework gives uV */
91 val->intval *= 1000;
93 return 0;
95 case POWER_SUPPLY_PROP_CURRENT_NOW:
96 ret = iio_read_channel_processed(power->acin_i, &val->intval);
97 if (ret)
98 return ret;
100 /* IIO framework gives mA but Power Supply framework gives uA */
101 val->intval *= 1000;
103 return 0;
105 default:
106 return -EINVAL;
109 return -EINVAL;
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,
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,
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,
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,
142 struct axp_data {
143 const struct power_supply_desc *power_desc;
144 bool acin_adc;
147 static const struct axp_data axp20x_data = {
148 .power_desc = &axp20x_ac_power_desc,
149 .acin_adc = true,
152 static const struct axp_data axp22x_data = {
153 .power_desc = &axp22x_ac_power_desc,
154 .acin_adc = false,
157 static int axp20x_ac_power_probe(struct platform_device *pdev)
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;
167 if (!of_device_is_available(pdev->dev.of_node))
168 return -ENODEV;
170 if (!axp20x) {
171 dev_err(&pdev->dev, "Parent drvdata not set\n");
172 return -EINVAL;
175 power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
176 if (!power)
177 return -ENOMEM;
179 axp_data = (struct axp_data *)of_device_get_match_data(&pdev->dev);
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);
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);
197 power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
199 platform_set_drvdata(pdev, power);
201 psy_cfg.of_node = pdev->dev.of_node;
202 psy_cfg.drv_data = power;
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);
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;
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);
227 return 0;
230 static const struct of_device_id axp20x_ac_power_match[] = {
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 */ }
239 MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
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,
249 module_platform_driver(axp20x_ac_power_driver);
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");