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>
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
);
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
);
56 case POWER_SUPPLY_PROP_HEALTH
:
57 ret
= regmap_read(power
->regmap
, AXP20X_PWR_INPUT_STATUS
, ®
);
61 if (reg
& AXP20X_PWR_STATUS_ACIN_PRESENT
) {
62 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
66 val
->intval
= POWER_SUPPLY_HEALTH_UNKNOWN
;
69 case POWER_SUPPLY_PROP_PRESENT
:
70 ret
= regmap_read(power
->regmap
, AXP20X_PWR_INPUT_STATUS
, ®
);
74 val
->intval
= !!(reg
& AXP20X_PWR_STATUS_ACIN_PRESENT
);
77 case POWER_SUPPLY_PROP_ONLINE
:
78 ret
= regmap_read(power
->regmap
, AXP20X_PWR_INPUT_STATUS
, ®
);
82 val
->intval
= !!(reg
& AXP20X_PWR_STATUS_ACIN_AVAIL
);
85 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
86 ret
= iio_read_channel_processed(power
->acin_v
, &val
->intval
);
90 /* IIO framework gives mV but Power Supply framework gives uV */
95 case POWER_SUPPLY_PROP_CURRENT_NOW
:
96 ret
= iio_read_channel_processed(power
->acin_i
, &val
->intval
);
100 /* IIO framework gives mA but Power Supply framework gives uA */
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
= {
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
= {
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
,
143 const struct power_supply_desc
*power_desc
;
147 static const struct axp_data axp20x_data
= {
148 .power_desc
= &axp20x_ac_power_desc
,
152 static const struct axp_data axp22x_data
= {
153 .power_desc
= &axp22x_ac_power_desc
,
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",
167 if (!of_device_is_available(pdev
->dev
.of_node
))
171 dev_err(&pdev
->dev
, "Parent drvdata not set\n");
175 power
= devm_kzalloc(&pdev
->dev
, sizeof(*power
), GFP_KERNEL
);
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
,
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
]);
214 dev_warn(&pdev
->dev
, "No IRQ for %s: %d\n",
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,
223 dev_warn(&pdev
->dev
, "Error requesting %s IRQ: %d\n",
230 static const struct of_device_id axp20x_ac_power_match
[] = {
232 .compatible
= "x-powers,axp202-ac-power-supply",
233 .data
= (void *)&axp20x_data
,
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
,
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");