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/platform_device.h> 17 #include <linux/pm.h> 18 #include <linux/power_supply.h> 19 #include <linux/regmap.h> 20 #include <linux/slab.h> 21 #include <linux/iio/consumer.h> 22 23 #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7) 24 #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) 25 26 #define AXP813_ACIN_PATH_SEL BIT(7) 27 #define AXP813_ACIN_PATH_SEL_TO_BIT(x) (!!(x) << 7) 28 29 #define AXP813_VHOLD_MASK GENMASK(5, 3) 30 #define AXP813_VHOLD_UV_TO_BIT(x) ((((x) / 100000) - 40) << 3) 31 #define AXP813_VHOLD_REG_TO_UV(x) \ 32 (((((x) & AXP813_VHOLD_MASK) >> 3) + 40) * 100000) 33 34 #define AXP813_CURR_LIMIT_MASK GENMASK(2, 0) 35 #define AXP813_CURR_LIMIT_UA_TO_BIT(x) (((x) / 500000) - 3) 36 #define AXP813_CURR_LIMIT_REG_TO_UA(x) \ 37 ((((x) & AXP813_CURR_LIMIT_MASK) + 3) * 500000) 38 39 #define DRVNAME "axp20x-ac-power-supply" 40 41 struct axp20x_ac_power { 42 struct regmap *regmap; 43 struct power_supply *supply; 44 struct iio_channel *acin_v; 45 struct iio_channel *acin_i; 46 bool has_acin_path_sel; 47 unsigned int num_irqs; 48 unsigned int irqs[]; 49 }; 50 51 static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) 52 { 53 struct axp20x_ac_power *power = devid; 54 55 power_supply_changed(power->supply); 56 57 return IRQ_HANDLED; 58 } 59 60 static int axp20x_ac_power_get_property(struct power_supply *psy, 61 enum power_supply_property psp, 62 union power_supply_propval *val) 63 { 64 struct axp20x_ac_power *power = power_supply_get_drvdata(psy); 65 int ret, reg; 66 67 switch (psp) { 68 case POWER_SUPPLY_PROP_HEALTH: 69 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 70 if (ret) 71 return ret; 72 73 if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) { 74 val->intval = POWER_SUPPLY_HEALTH_GOOD; 75 return 0; 76 } 77 78 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 79 return 0; 80 81 case POWER_SUPPLY_PROP_PRESENT: 82 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 83 if (ret) 84 return ret; 85 86 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT); 87 return 0; 88 89 case POWER_SUPPLY_PROP_ONLINE: 90 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 91 if (ret) 92 return ret; 93 94 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL); 95 96 /* ACIN_PATH_SEL disables ACIN even if ACIN_AVAIL is set. */ 97 if (val->intval && power->has_acin_path_sel) { 98 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, 99 ®); 100 if (ret) 101 return ret; 102 103 val->intval = !!(reg & AXP813_ACIN_PATH_SEL); 104 } 105 106 return 0; 107 108 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 109 ret = iio_read_channel_processed(power->acin_v, &val->intval); 110 if (ret) 111 return ret; 112 113 /* IIO framework gives mV but Power Supply framework gives uV */ 114 val->intval *= 1000; 115 116 return 0; 117 118 case POWER_SUPPLY_PROP_CURRENT_NOW: 119 ret = iio_read_channel_processed(power->acin_i, &val->intval); 120 if (ret) 121 return ret; 122 123 /* IIO framework gives mA but Power Supply framework gives uA */ 124 val->intval *= 1000; 125 126 return 0; 127 128 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 129 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, ®); 130 if (ret) 131 return ret; 132 133 val->intval = AXP813_VHOLD_REG_TO_UV(reg); 134 135 return 0; 136 137 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 138 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, ®); 139 if (ret) 140 return ret; 141 142 val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg); 143 /* AXP813 datasheet defines values 11x as 4000mA */ 144 if (val->intval > 4000000) 145 val->intval = 4000000; 146 147 return 0; 148 149 default: 150 return -EINVAL; 151 } 152 153 return -EINVAL; 154 } 155 156 static int axp813_ac_power_set_property(struct power_supply *psy, 157 enum power_supply_property psp, 158 const union power_supply_propval *val) 159 { 160 struct axp20x_ac_power *power = power_supply_get_drvdata(psy); 161 162 switch (psp) { 163 case POWER_SUPPLY_PROP_ONLINE: 164 return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, 165 AXP813_ACIN_PATH_SEL, 166 AXP813_ACIN_PATH_SEL_TO_BIT(val->intval)); 167 168 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 169 if (val->intval < 4000000 || val->intval > 4700000) 170 return -EINVAL; 171 172 return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, 173 AXP813_VHOLD_MASK, 174 AXP813_VHOLD_UV_TO_BIT(val->intval)); 175 176 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 177 if (val->intval < 1500000 || val->intval > 4000000) 178 return -EINVAL; 179 180 return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, 181 AXP813_CURR_LIMIT_MASK, 182 AXP813_CURR_LIMIT_UA_TO_BIT(val->intval)); 183 184 default: 185 return -EINVAL; 186 } 187 188 return -EINVAL; 189 } 190 191 static int axp813_ac_power_prop_writeable(struct power_supply *psy, 192 enum power_supply_property psp) 193 { 194 return psp == POWER_SUPPLY_PROP_ONLINE || 195 psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || 196 psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; 197 } 198 199 static enum power_supply_property axp20x_ac_power_properties[] = { 200 POWER_SUPPLY_PROP_HEALTH, 201 POWER_SUPPLY_PROP_PRESENT, 202 POWER_SUPPLY_PROP_ONLINE, 203 POWER_SUPPLY_PROP_VOLTAGE_NOW, 204 POWER_SUPPLY_PROP_CURRENT_NOW, 205 }; 206 207 static enum power_supply_property axp22x_ac_power_properties[] = { 208 POWER_SUPPLY_PROP_HEALTH, 209 POWER_SUPPLY_PROP_PRESENT, 210 POWER_SUPPLY_PROP_ONLINE, 211 }; 212 213 static enum power_supply_property axp813_ac_power_properties[] = { 214 POWER_SUPPLY_PROP_HEALTH, 215 POWER_SUPPLY_PROP_PRESENT, 216 POWER_SUPPLY_PROP_ONLINE, 217 POWER_SUPPLY_PROP_VOLTAGE_MIN, 218 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 219 }; 220 221 static const struct power_supply_desc axp20x_ac_power_desc = { 222 .name = "axp20x-ac", 223 .type = POWER_SUPPLY_TYPE_MAINS, 224 .properties = axp20x_ac_power_properties, 225 .num_properties = ARRAY_SIZE(axp20x_ac_power_properties), 226 .get_property = axp20x_ac_power_get_property, 227 }; 228 229 static const struct power_supply_desc axp22x_ac_power_desc = { 230 .name = "axp22x-ac", 231 .type = POWER_SUPPLY_TYPE_MAINS, 232 .properties = axp22x_ac_power_properties, 233 .num_properties = ARRAY_SIZE(axp22x_ac_power_properties), 234 .get_property = axp20x_ac_power_get_property, 235 }; 236 237 static const struct power_supply_desc axp813_ac_power_desc = { 238 .name = "axp813-ac", 239 .type = POWER_SUPPLY_TYPE_MAINS, 240 .properties = axp813_ac_power_properties, 241 .num_properties = ARRAY_SIZE(axp813_ac_power_properties), 242 .property_is_writeable = axp813_ac_power_prop_writeable, 243 .get_property = axp20x_ac_power_get_property, 244 .set_property = axp813_ac_power_set_property, 245 }; 246 247 static const char * const axp20x_irq_names[] = { 248 "ACIN_PLUGIN", 249 "ACIN_REMOVAL", 250 }; 251 252 struct axp_data { 253 const struct power_supply_desc *power_desc; 254 const char * const *irq_names; 255 unsigned int num_irq_names; 256 bool acin_adc; 257 bool acin_path_sel; 258 }; 259 260 static const struct axp_data axp20x_data = { 261 .power_desc = &axp20x_ac_power_desc, 262 .irq_names = axp20x_irq_names, 263 .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 264 .acin_adc = true, 265 .acin_path_sel = false, 266 }; 267 268 static const struct axp_data axp22x_data = { 269 .power_desc = &axp22x_ac_power_desc, 270 .irq_names = axp20x_irq_names, 271 .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 272 .acin_adc = false, 273 .acin_path_sel = false, 274 }; 275 276 static const struct axp_data axp813_data = { 277 .power_desc = &axp813_ac_power_desc, 278 .irq_names = axp20x_irq_names, 279 .num_irq_names = ARRAY_SIZE(axp20x_irq_names), 280 .acin_adc = false, 281 .acin_path_sel = true, 282 }; 283 284 #ifdef CONFIG_PM_SLEEP 285 static int axp20x_ac_power_suspend(struct device *dev) 286 { 287 struct axp20x_ac_power *power = dev_get_drvdata(dev); 288 int i = 0; 289 290 /* 291 * Allow wake via ACIN_PLUGIN only. 292 * 293 * As nested threaded IRQs are not automatically disabled during 294 * suspend, we must explicitly disable the remainder of the IRQs. 295 */ 296 if (device_may_wakeup(&power->supply->dev)) 297 enable_irq_wake(power->irqs[i++]); 298 while (i < power->num_irqs) 299 disable_irq(power->irqs[i++]); 300 301 return 0; 302 } 303 304 static int axp20x_ac_power_resume(struct device *dev) 305 { 306 struct axp20x_ac_power *power = dev_get_drvdata(dev); 307 int i = 0; 308 309 if (device_may_wakeup(&power->supply->dev)) 310 disable_irq_wake(power->irqs[i++]); 311 while (i < power->num_irqs) 312 enable_irq(power->irqs[i++]); 313 314 return 0; 315 } 316 #endif 317 318 static SIMPLE_DEV_PM_OPS(axp20x_ac_power_pm_ops, axp20x_ac_power_suspend, 319 axp20x_ac_power_resume); 320 321 static int axp20x_ac_power_probe(struct platform_device *pdev) 322 { 323 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 324 struct power_supply_config psy_cfg = {}; 325 struct axp20x_ac_power *power; 326 const struct axp_data *axp_data; 327 int i, irq, ret; 328 329 if (!of_device_is_available(pdev->dev.of_node)) 330 return -ENODEV; 331 332 if (!axp20x) { 333 dev_err(&pdev->dev, "Parent drvdata not set\n"); 334 return -EINVAL; 335 } 336 337 axp_data = of_device_get_match_data(&pdev->dev); 338 339 power = devm_kzalloc(&pdev->dev, 340 struct_size(power, irqs, axp_data->num_irq_names), 341 GFP_KERNEL); 342 if (!power) 343 return -ENOMEM; 344 345 if (axp_data->acin_adc) { 346 power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); 347 if (IS_ERR(power->acin_v)) { 348 if (PTR_ERR(power->acin_v) == -ENODEV) 349 return -EPROBE_DEFER; 350 return PTR_ERR(power->acin_v); 351 } 352 353 power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i"); 354 if (IS_ERR(power->acin_i)) { 355 if (PTR_ERR(power->acin_i) == -ENODEV) 356 return -EPROBE_DEFER; 357 return PTR_ERR(power->acin_i); 358 } 359 } 360 361 power->regmap = dev_get_regmap(pdev->dev.parent, NULL); 362 power->has_acin_path_sel = axp_data->acin_path_sel; 363 power->num_irqs = axp_data->num_irq_names; 364 365 platform_set_drvdata(pdev, power); 366 367 psy_cfg.of_node = pdev->dev.of_node; 368 psy_cfg.drv_data = power; 369 370 power->supply = devm_power_supply_register(&pdev->dev, 371 axp_data->power_desc, 372 &psy_cfg); 373 if (IS_ERR(power->supply)) 374 return PTR_ERR(power->supply); 375 376 /* Request irqs after registering, as irqs may trigger immediately */ 377 for (i = 0; i < axp_data->num_irq_names; i++) { 378 irq = platform_get_irq_byname(pdev, axp_data->irq_names[i]); 379 if (irq < 0) 380 return irq; 381 382 power->irqs[i] = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 383 ret = devm_request_any_context_irq(&pdev->dev, power->irqs[i], 384 axp20x_ac_power_irq, 0, 385 DRVNAME, power); 386 if (ret < 0) { 387 dev_err(&pdev->dev, "Error requesting %s IRQ: %d\n", 388 axp_data->irq_names[i], ret); 389 return ret; 390 } 391 } 392 393 return 0; 394 } 395 396 static const struct of_device_id axp20x_ac_power_match[] = { 397 { 398 .compatible = "x-powers,axp202-ac-power-supply", 399 .data = &axp20x_data, 400 }, { 401 .compatible = "x-powers,axp221-ac-power-supply", 402 .data = &axp22x_data, 403 }, { 404 .compatible = "x-powers,axp813-ac-power-supply", 405 .data = &axp813_data, 406 }, { /* sentinel */ } 407 }; 408 MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); 409 410 static struct platform_driver axp20x_ac_power_driver = { 411 .probe = axp20x_ac_power_probe, 412 .driver = { 413 .name = DRVNAME, 414 .of_match_table = axp20x_ac_power_match, 415 .pm = &axp20x_ac_power_pm_ops, 416 }, 417 }; 418 419 module_platform_driver(axp20x_ac_power_driver); 420 421 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 422 MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); 423 MODULE_LICENSE("GPL"); 424