hc
2023-12-09 b22da3d8526a935aa31e086e63f60ff3246cb61c
kernel/drivers/power/supply/axp20x_ac_power.c
....@@ -1,13 +1,9 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * AXP20X and AXP22X PMICs' ACIN power supply driver
34 *
45 * Copyright (C) 2016 Free Electrons
56 * 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.
117 */
128
139 #include <linux/device.h>
....@@ -19,6 +15,7 @@
1915 #include <linux/of.h>
2016 #include <linux/of_device.h>
2117 #include <linux/platform_device.h>
18
+#include <linux/pm.h>
2219 #include <linux/power_supply.h>
2320 #include <linux/regmap.h>
2421 #include <linux/slab.h>
....@@ -27,6 +24,19 @@
2724 #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7)
2825 #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6)
2926
27
+#define AXP813_ACIN_PATH_SEL BIT(7)
28
+#define AXP813_ACIN_PATH_SEL_TO_BIT(x) (!!(x) << 7)
29
+
30
+#define AXP813_VHOLD_MASK GENMASK(5, 3)
31
+#define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3)
32
+#define AXP813_VHOLD_REG_TO_UV(x) \
33
+ (((((x) & AXP813_VHOLD_MASK) >> 3) + 40) * 100000)
34
+
35
+#define AXP813_CURR_LIMIT_MASK GENMASK(2, 0)
36
+#define AXP813_CURR_LIMIT_UA_TO_BIT(x) (((x) / 500000) - 3)
37
+#define AXP813_CURR_LIMIT_REG_TO_UA(x) \
38
+ ((((x) & AXP813_CURR_LIMIT_MASK) + 3) * 500000)
39
+
3040 #define DRVNAME "axp20x-ac-power-supply"
3141
3242 struct axp20x_ac_power {
....@@ -34,6 +44,9 @@
3444 struct power_supply *supply;
3545 struct iio_channel *acin_v;
3646 struct iio_channel *acin_i;
47
+ bool has_acin_path_sel;
48
+ unsigned int num_irqs;
49
+ unsigned int irqs[];
3750 };
3851
3952 static irqreturn_t axp20x_ac_power_irq(int irq, void *devid)
....@@ -80,6 +93,17 @@
8093 return ret;
8194
8295 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL);
96
+
97
+ /* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */
98
+ if (val->intval && power->has_acin_path_sel) {
99
+ ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL,
100
+ &reg);
101
+ if (ret)
102
+ return ret;
103
+
104
+ val->intval = !!(reg & AXP813_ACIN_PATH_SEL);
105
+ }
106
+
83107 return 0;
84108
85109 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
....@@ -102,11 +126,75 @@
102126
103127 return 0;
104128
129
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
130
+ ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
131
+ if (ret)
132
+ return ret;
133
+
134
+ val->intval = AXP813_VHOLD_REG_TO_UV(reg);
135
+
136
+ return 0;
137
+
138
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
139
+ ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, &reg);
140
+ if (ret)
141
+ return ret;
142
+
143
+ val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg);
144
+ /* AXP813 datasheet defines values 11x as 4000mA */
145
+ if (val->intval > 4000000)
146
+ val->intval = 4000000;
147
+
148
+ return 0;
149
+
105150 default:
106151 return -EINVAL;
107152 }
108153
109154 return -EINVAL;
155
+}
156
+
157
+static int axp813_ac_power_set_property(struct power_supply *psy,
158
+ enum power_supply_property psp,
159
+ const union power_supply_propval *val)
160
+{
161
+ struct axp20x_ac_power *power = power_supply_get_drvdata(psy);
162
+
163
+ switch (psp) {
164
+ case POWER_SUPPLY_PROP_ONLINE:
165
+ return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
166
+ AXP813_ACIN_PATH_SEL,
167
+ AXP813_ACIN_PATH_SEL_TO_BIT(val->intval));
168
+
169
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN:
170
+ if (val->intval < 4000000 || val->intval > 4700000)
171
+ return -EINVAL;
172
+
173
+ return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
174
+ AXP813_VHOLD_MASK,
175
+ AXP813_VHOLD_UV_TO_BIT(val->intval));
176
+
177
+ case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT:
178
+ if (val->intval < 1500000 || val->intval > 4000000)
179
+ return -EINVAL;
180
+
181
+ return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL,
182
+ AXP813_CURR_LIMIT_MASK,
183
+ AXP813_CURR_LIMIT_UA_TO_BIT(val->intval));
184
+
185
+ default:
186
+ return -EINVAL;
187
+ }
188
+
189
+ return -EINVAL;
190
+}
191
+
192
+static int axp813_ac_power_prop_writeable(struct power_supply *psy,
193
+ enum power_supply_property psp)
194
+{
195
+ return psp == POWER_SUPPLY_PROP_ONLINE ||
196
+ psp == POWER_SUPPLY_PROP_VOLTAGE_MIN ||
197
+ psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT;
110198 }
111199
112200 static enum power_supply_property axp20x_ac_power_properties[] = {
....@@ -121,6 +209,14 @@
121209 POWER_SUPPLY_PROP_HEALTH,
122210 POWER_SUPPLY_PROP_PRESENT,
123211 POWER_SUPPLY_PROP_ONLINE,
212
+};
213
+
214
+static enum power_supply_property axp813_ac_power_properties[] = {
215
+ POWER_SUPPLY_PROP_HEALTH,
216
+ POWER_SUPPLY_PROP_PRESENT,
217
+ POWER_SUPPLY_PROP_ONLINE,
218
+ POWER_SUPPLY_PROP_VOLTAGE_MIN,
219
+ POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT,
124220 };
125221
126222 static const struct power_supply_desc axp20x_ac_power_desc = {
....@@ -139,20 +235,89 @@
139235 .get_property = axp20x_ac_power_get_property,
140236 };
141237
238
+static const struct power_supply_desc axp813_ac_power_desc = {
239
+ .name = "axp813-ac",
240
+ .type = POWER_SUPPLY_TYPE_MAINS,
241
+ .properties = axp813_ac_power_properties,
242
+ .num_properties = ARRAY_SIZE(axp813_ac_power_properties),
243
+ .property_is_writeable = axp813_ac_power_prop_writeable,
244
+ .get_property = axp20x_ac_power_get_property,
245
+ .set_property = axp813_ac_power_set_property,
246
+};
247
+
248
+static const char * const axp20x_irq_names[] = {
249
+ "ACIN_PLUGIN",
250
+ "ACIN_REMOVAL",
251
+};
252
+
142253 struct axp_data {
143254 const struct power_supply_desc *power_desc;
255
+ const char * const *irq_names;
256
+ unsigned int num_irq_names;
144257 bool acin_adc;
258
+ bool acin_path_sel;
145259 };
146260
147261 static const struct axp_data axp20x_data = {
148
- .power_desc = &axp20x_ac_power_desc,
149
- .acin_adc = true,
262
+ .power_desc = &axp20x_ac_power_desc,
263
+ .irq_names = axp20x_irq_names,
264
+ .num_irq_names = ARRAY_SIZE(axp20x_irq_names),
265
+ .acin_adc = true,
266
+ .acin_path_sel = false,
150267 };
151268
152269 static const struct axp_data axp22x_data = {
153
- .power_desc = &axp22x_ac_power_desc,
154
- .acin_adc = false,
270
+ .power_desc = &axp22x_ac_power_desc,
271
+ .irq_names = axp20x_irq_names,
272
+ .num_irq_names = ARRAY_SIZE(axp20x_irq_names),
273
+ .acin_adc = false,
274
+ .acin_path_sel = false,
155275 };
276
+
277
+static const struct axp_data axp813_data = {
278
+ .power_desc = &axp813_ac_power_desc,
279
+ .irq_names = axp20x_irq_names,
280
+ .num_irq_names = ARRAY_SIZE(axp20x_irq_names),
281
+ .acin_adc = false,
282
+ .acin_path_sel = true,
283
+};
284
+
285
+#ifdef CONFIG_PM_SLEEP
286
+static int axp20x_ac_power_suspend(struct device *dev)
287
+{
288
+ struct axp20x_ac_power *power = dev_get_drvdata(dev);
289
+ int i = 0;
290
+
291
+ /*
292
+ * Allow wake via ACIN_PLUGIN only.
293
+ *
294
+ * As nested threaded IRQs are not automatically disabled during
295
+ * suspend, we must explicitly disable the remainder of the IRQs.
296
+ */
297
+ if (device_may_wakeup(&power->supply->dev))
298
+ enable_irq_wake(power->irqs[i++]);
299
+ while (i < power->num_irqs)
300
+ disable_irq(power->irqs[i++]);
301
+
302
+ return 0;
303
+}
304
+
305
+static int axp20x_ac_power_resume(struct device *dev)
306
+{
307
+ struct axp20x_ac_power *power = dev_get_drvdata(dev);
308
+ int i = 0;
309
+
310
+ if (device_may_wakeup(&power->supply->dev))
311
+ disable_irq_wake(power->irqs[i++]);
312
+ while (i < power->num_irqs)
313
+ enable_irq(power->irqs[i++]);
314
+
315
+ return 0;
316
+}
317
+#endif
318
+
319
+static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend,
320
+ axp20x_ac_power_resume);
156321
157322 static int axp20x_ac_power_probe(struct platform_device *pdev)
158323 {
....@@ -160,8 +325,6 @@
160325 struct power_supply_config psy_cfg = {};
161326 struct axp20x_ac_power *power;
162327 const struct axp_data *axp_data;
163
- static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL",
164
- NULL };
165328 int i, irq, ret;
166329
167330 if (!of_device_is_available(pdev->dev.of_node))
....@@ -172,11 +335,13 @@
172335 return -EINVAL;
173336 }
174337
175
- power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
338
+ axp_data = of_device_get_match_data(&pdev->dev);
339
+
340
+ power = devm_kzalloc(&pdev->dev,
341
+ struct_size(power, irqs, axp_data->num_irq_names),
342
+ GFP_KERNEL);
176343 if (!power)
177344 return -ENOMEM;
178
-
179
- axp_data = of_device_get_match_data(&pdev->dev);
180345
181346 if (axp_data->acin_adc) {
182347 power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v");
....@@ -195,6 +360,8 @@
195360 }
196361
197362 power->regmap = dev_get_regmap(pdev->dev.parent, NULL);
363
+ power->has_acin_path_sel = axp_data->acin_path_sel;
364
+ power->num_irqs = axp_data->num_irq_names;
198365
199366 platform_set_drvdata(pdev, power);
200367
....@@ -208,20 +375,22 @@
208375 return PTR_ERR(power->supply);
209376
210377 /* 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]);
378
+ for (i = 0; i < axp_data->num_irq_names; i++) {
379
+ irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]);
213380 if (irq < 0) {
214
- dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
215
- irq_names[i], irq);
216
- continue;
381
+ dev_err(&pdev->dev, "No IRQ for %s: %d\n",
382
+ axp_data->irq_names[i], irq);
383
+ return irq;
217384 }
218
- irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
219
- ret = devm_request_any_context_irq(&pdev->dev, irq,
385
+ power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
386
+ ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i],
220387 axp20x_ac_power_irq, 0,
221388 DRVNAME, power);
222
- if (ret < 0)
223
- dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
224
- irq_names[i], ret);
389
+ if (ret < 0) {
390
+ dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n",
391
+ axp_data->irq_names[i], ret);
392
+ return ret;
393
+ }
225394 }
226395
227396 return 0;
....@@ -234,6 +403,9 @@
234403 }, {
235404 .compatible = "x-powers,axp221-ac-power-supply",
236405 .data = &axp22x_data,
406
+ }, {
407
+ .compatible = "x-powers,axp813-ac-power-supply",
408
+ .data = &axp813_data,
237409 }, { /* sentinel */ }
238410 };
239411 MODULE_DEVICE_TABLE(of, axp20x_ac_power_match);
....@@ -241,8 +413,9 @@
241413 static struct platform_driver axp20x_ac_power_driver = {
242414 .probe = axp20x_ac_power_probe,
243415 .driver = {
244
- .name = DRVNAME,
245
- .of_match_table = axp20x_ac_power_match,
416
+ .name = DRVNAME,
417
+ .of_match_table = axp20x_ac_power_match,
418
+ .pm = &axp20x_ac_power_pm_ops,
246419 },
247420 };
248421