axp20x_ac_power.c (11125B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * AXP20X and AXP22X PMICs' ACIN power supply driver 4 * 5 * Copyright (C) 2016 Free Electrons 6 * Quentin Schulz <quentin.schulz@free-electrons.com> 7 */ 8 9#include <linux/device.h> 10#include <linux/init.h> 11#include <linux/interrupt.h> 12#include <linux/kernel.h> 13#include <linux/mfd/axp20x.h> 14#include <linux/module.h> 15#include <linux/of.h> 16#include <linux/of_device.h> 17#include <linux/platform_device.h> 18#include <linux/pm.h> 19#include <linux/power_supply.h> 20#include <linux/regmap.h> 21#include <linux/slab.h> 22#include <linux/iio/consumer.h> 23 24#define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7) 25#define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) 26 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 40#define DRVNAME "axp20x-ac-power-supply" 41 42struct axp20x_ac_power { 43 struct regmap *regmap; 44 struct power_supply *supply; 45 struct iio_channel *acin_v; 46 struct iio_channel *acin_i; 47 bool has_acin_path_sel; 48 unsigned int num_irqs; 49 unsigned int irqs[]; 50}; 51 52static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) 53{ 54 struct axp20x_ac_power *power = devid; 55 56 power_supply_changed(power->supply); 57 58 return IRQ_HANDLED; 59} 60 61static int axp20x_ac_power_get_property(struct power_supply *psy, 62 enum power_supply_property psp, 63 union power_supply_propval *val) 64{ 65 struct axp20x_ac_power *power = power_supply_get_drvdata(psy); 66 int ret, reg; 67 68 switch (psp) { 69 case POWER_SUPPLY_PROP_HEALTH: 70 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 71 if (ret) 72 return ret; 73 74 if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) { 75 val->intval = POWER_SUPPLY_HEALTH_GOOD; 76 return 0; 77 } 78 79 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 80 return 0; 81 82 case POWER_SUPPLY_PROP_PRESENT: 83 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 84 if (ret) 85 return ret; 86 87 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT); 88 return 0; 89 90 case POWER_SUPPLY_PROP_ONLINE: 91 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 92 if (ret) 93 return ret; 94 95 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 ®); 101 if (ret) 102 return ret; 103 104 val->intval = !!(reg & AXP813_ACIN_PATH_SEL); 105 } 106 107 return 0; 108 109 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 110 ret = iio_read_channel_processed(power->acin_v, &val->intval); 111 if (ret) 112 return ret; 113 114 /* IIO framework gives mV but Power Supply framework gives uV */ 115 val->intval *= 1000; 116 117 return 0; 118 119 case POWER_SUPPLY_PROP_CURRENT_NOW: 120 ret = iio_read_channel_processed(power->acin_i, &val->intval); 121 if (ret) 122 return ret; 123 124 /* IIO framework gives mA but Power Supply framework gives uA */ 125 val->intval *= 1000; 126 127 return 0; 128 129 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 130 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, ®); 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, ®); 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 150 default: 151 return -EINVAL; 152 } 153 154 return -EINVAL; 155} 156 157static 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 192static 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; 198} 199 200static enum power_supply_property axp20x_ac_power_properties[] = { 201 POWER_SUPPLY_PROP_HEALTH, 202 POWER_SUPPLY_PROP_PRESENT, 203 POWER_SUPPLY_PROP_ONLINE, 204 POWER_SUPPLY_PROP_VOLTAGE_NOW, 205 POWER_SUPPLY_PROP_CURRENT_NOW, 206}; 207 208static enum power_supply_property axp22x_ac_power_properties[] = { 209 POWER_SUPPLY_PROP_HEALTH, 210 POWER_SUPPLY_PROP_PRESENT, 211 POWER_SUPPLY_PROP_ONLINE, 212}; 213 214static 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, 220}; 221 222static const struct power_supply_desc axp20x_ac_power_desc = { 223 .name = "axp20x-ac", 224 .type = POWER_SUPPLY_TYPE_MAINS, 225 .properties = axp20x_ac_power_properties, 226 .num_properties = ARRAY_SIZE(axp20x_ac_power_properties), 227 .get_property = axp20x_ac_power_get_property, 228}; 229 230static const struct power_supply_desc axp22x_ac_power_desc = { 231 .name = "axp22x-ac", 232 .type = POWER_SUPPLY_TYPE_MAINS, 233 .properties = axp22x_ac_power_properties, 234 .num_properties = ARRAY_SIZE(axp22x_ac_power_properties), 235 .get_property = axp20x_ac_power_get_property, 236}; 237 238static 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 248static const char * const axp20x_irq_names[] = { 249 "ACIN_PLUGIN", 250 "ACIN_REMOVAL", 251}; 252 253struct axp_data { 254 const struct power_supply_desc *power_desc; 255 const char * const *irq_names; 256 unsigned int num_irq_names; 257 bool acin_adc; 258 bool acin_path_sel; 259}; 260 261static const struct axp_data axp20x_data = { 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, 267}; 268 269static const struct axp_data axp22x_data = { 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, 275}; 276 277static 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 286static 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 305static 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 319static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend, 320 axp20x_ac_power_resume); 321 322static int axp20x_ac_power_probe(struct platform_device *pdev) 323{ 324 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 325 struct power_supply_config psy_cfg = {}; 326 struct axp20x_ac_power *power; 327 const struct axp_data *axp_data; 328 int i, irq, ret; 329 330 if (!of_device_is_available(pdev->dev.of_node)) 331 return -ENODEV; 332 333 if (!axp20x) { 334 dev_err(&pdev->dev, "Parent drvdata not set\n"); 335 return -EINVAL; 336 } 337 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); 343 if (!power) 344 return -ENOMEM; 345 346 if (axp_data->acin_adc) { 347 power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); 348 if (IS_ERR(power->acin_v)) { 349 if (PTR_ERR(power->acin_v) == -ENODEV) 350 return -EPROBE_DEFER; 351 return PTR_ERR(power->acin_v); 352 } 353 354 power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i"); 355 if (IS_ERR(power->acin_i)) { 356 if (PTR_ERR(power->acin_i) == -ENODEV) 357 return -EPROBE_DEFER; 358 return PTR_ERR(power->acin_i); 359 } 360 } 361 362 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; 365 366 platform_set_drvdata(pdev, power); 367 368 psy_cfg.of_node = pdev->dev.of_node; 369 psy_cfg.drv_data = power; 370 371 power->supply = devm_power_supply_register(&pdev->dev, 372 axp_data->power_desc, 373 &psy_cfg); 374 if (IS_ERR(power->supply)) 375 return PTR_ERR(power->supply); 376 377 /* Request irqs after registering, as irqs may trigger immediately */ 378 for (i = 0; i < axp_data->num_irq_names; i++) { 379 irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); 380 if (irq < 0) 381 return irq; 382 383 power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 384 ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], 385 axp20x_ac_power_irq, 0, 386 DRVNAME, power); 387 if (ret < 0) { 388 dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", 389 axp_data->irq_names[i], ret); 390 return ret; 391 } 392 } 393 394 return 0; 395} 396 397static const struct of_device_id axp20x_ac_power_match[] = { 398 { 399 .compatible = "x-powers,axp202-ac-power-supply", 400 .data = &axp20x_data, 401 }, { 402 .compatible = "x-powers,axp221-ac-power-supply", 403 .data = &axp22x_data, 404 }, { 405 .compatible = "x-powers,axp813-ac-power-supply", 406 .data = &axp813_data, 407 }, { /* sentinel */ } 408}; 409MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); 410 411static struct platform_driver axp20x_ac_power_driver = { 412 .probe = axp20x_ac_power_probe, 413 .driver = { 414 .name = DRVNAME, 415 .of_match_table = axp20x_ac_power_match, 416 .pm = &axp20x_ac_power_pm_ops, 417 }, 418}; 419 420module_platform_driver(axp20x_ac_power_driver); 421 422MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 423MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); 424MODULE_LICENSE("GPL");