1 /* 2 * AXP20X and AXP22X PMICs' ACIN power supply driver 3 * 4 * Copyright (C) 2016 Free Electrons 5 * 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. 11 */ 12 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> 26 27 #define AXP20X_PWR_STATUS_ACIN_PRESENT BIT(7) 28 #define AXP20X_PWR_STATUS_ACIN_AVAIL BIT(6) 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 42 struct 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 }; 48 49 static irqreturn_t axp20x_ac_power_irq(int irq, void *devid) 50 { 51 struct axp20x_ac_power *power = devid; 52 53 power_supply_changed(power->supply); 54 55 return IRQ_HANDLED; 56 } 57 58 static int axp20x_ac_power_get_property(struct power_supply *psy, 59 enum power_supply_property psp, 60 union power_supply_propval *val) 61 { 62 struct axp20x_ac_power *power = power_supply_get_drvdata(psy); 63 int ret, reg; 64 65 switch (psp) { 66 case POWER_SUPPLY_PROP_HEALTH: 67 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 68 if (ret) 69 return ret; 70 71 if (reg & AXP20X_PWR_STATUS_ACIN_PRESENT) { 72 val->intval = POWER_SUPPLY_HEALTH_GOOD; 73 return 0; 74 } 75 76 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 77 return 0; 78 79 case POWER_SUPPLY_PROP_PRESENT: 80 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 81 if (ret) 82 return ret; 83 84 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_PRESENT); 85 return 0; 86 87 case POWER_SUPPLY_PROP_ONLINE: 88 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, ®); 89 if (ret) 90 return ret; 91 92 val->intval = !!(reg & AXP20X_PWR_STATUS_ACIN_AVAIL); 93 return 0; 94 95 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 96 ret = iio_read_channel_processed(power->acin_v, &val->intval); 97 if (ret) 98 return ret; 99 100 /* IIO framework gives mV but Power Supply framework gives uV */ 101 val->intval *= 1000; 102 103 return 0; 104 105 case POWER_SUPPLY_PROP_CURRENT_NOW: 106 ret = iio_read_channel_processed(power->acin_i, &val->intval); 107 if (ret) 108 return ret; 109 110 /* IIO framework gives mA but Power Supply framework gives uA */ 111 val->intval *= 1000; 112 113 return 0; 114 115 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 116 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, ®); 117 if (ret) 118 return ret; 119 120 val->intval = AXP813_VHOLD_REG_TO_UV(reg); 121 122 return 0; 123 124 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 125 ret = regmap_read(power->regmap, AXP813_ACIN_PATH_CTRL, ®); 126 if (ret) 127 return ret; 128 129 val->intval = AXP813_CURR_LIMIT_REG_TO_UA(reg); 130 /* AXP813 datasheet defines values 11x as 4000mA */ 131 if (val->intval > 4000000) 132 val->intval = 4000000; 133 134 return 0; 135 136 default: 137 return -EINVAL; 138 } 139 140 return -EINVAL; 141 } 142 143 static int axp813_ac_power_set_property(struct power_supply *psy, 144 enum power_supply_property psp, 145 const union power_supply_propval *val) 146 { 147 struct axp20x_ac_power *power = power_supply_get_drvdata(psy); 148 149 switch (psp) { 150 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 151 if (val->intval < 4000000 || val->intval > 4700000) 152 return -EINVAL; 153 154 return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, 155 AXP813_VHOLD_MASK, 156 AXP813_VHOLD_UV_TO_BIT(val->intval)); 157 158 case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: 159 if (val->intval < 1500000 || val->intval > 4000000) 160 return -EINVAL; 161 162 return regmap_update_bits(power->regmap, AXP813_ACIN_PATH_CTRL, 163 AXP813_CURR_LIMIT_MASK, 164 AXP813_CURR_LIMIT_UA_TO_BIT(val->intval)); 165 166 default: 167 return -EINVAL; 168 } 169 170 return -EINVAL; 171 } 172 173 static int axp813_ac_power_prop_writeable(struct power_supply *psy, 174 enum power_supply_property psp) 175 { 176 return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || 177 psp == POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; 178 } 179 180 static enum power_supply_property axp20x_ac_power_properties[] = { 181 POWER_SUPPLY_PROP_HEALTH, 182 POWER_SUPPLY_PROP_PRESENT, 183 POWER_SUPPLY_PROP_ONLINE, 184 POWER_SUPPLY_PROP_VOLTAGE_NOW, 185 POWER_SUPPLY_PROP_CURRENT_NOW, 186 }; 187 188 static enum power_supply_property axp22x_ac_power_properties[] = { 189 POWER_SUPPLY_PROP_HEALTH, 190 POWER_SUPPLY_PROP_PRESENT, 191 POWER_SUPPLY_PROP_ONLINE, 192 }; 193 194 static enum power_supply_property axp813_ac_power_properties[] = { 195 POWER_SUPPLY_PROP_HEALTH, 196 POWER_SUPPLY_PROP_PRESENT, 197 POWER_SUPPLY_PROP_ONLINE, 198 POWER_SUPPLY_PROP_VOLTAGE_MIN, 199 POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT, 200 }; 201 202 static const struct power_supply_desc axp20x_ac_power_desc = { 203 .name = "axp20x-ac", 204 .type = POWER_SUPPLY_TYPE_MAINS, 205 .properties = axp20x_ac_power_properties, 206 .num_properties = ARRAY_SIZE(axp20x_ac_power_properties), 207 .get_property = axp20x_ac_power_get_property, 208 }; 209 210 static const struct power_supply_desc axp22x_ac_power_desc = { 211 .name = "axp22x-ac", 212 .type = POWER_SUPPLY_TYPE_MAINS, 213 .properties = axp22x_ac_power_properties, 214 .num_properties = ARRAY_SIZE(axp22x_ac_power_properties), 215 .get_property = axp20x_ac_power_get_property, 216 }; 217 218 static const struct power_supply_desc axp813_ac_power_desc = { 219 .name = "axp813-ac", 220 .type = POWER_SUPPLY_TYPE_MAINS, 221 .properties = axp813_ac_power_properties, 222 .num_properties = ARRAY_SIZE(axp813_ac_power_properties), 223 .property_is_writeable = axp813_ac_power_prop_writeable, 224 .get_property = axp20x_ac_power_get_property, 225 .set_property = axp813_ac_power_set_property, 226 }; 227 228 struct axp_data { 229 const struct power_supply_desc *power_desc; 230 bool acin_adc; 231 }; 232 233 static const struct axp_data axp20x_data = { 234 .power_desc = &axp20x_ac_power_desc, 235 .acin_adc = true, 236 }; 237 238 static const struct axp_data axp22x_data = { 239 .power_desc = &axp22x_ac_power_desc, 240 .acin_adc = false, 241 }; 242 243 static const struct axp_data axp813_data = { 244 .power_desc = &axp813_ac_power_desc, 245 .acin_adc = false, 246 }; 247 248 static int axp20x_ac_power_probe(struct platform_device *pdev) 249 { 250 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 251 struct power_supply_config psy_cfg = {}; 252 struct axp20x_ac_power *power; 253 const struct axp_data *axp_data; 254 static const char * const irq_names[] = { "ACIN_PLUGIN", "ACIN_REMOVAL", 255 NULL }; 256 int i, irq, ret; 257 258 if (!of_device_is_available(pdev->dev.of_node)) 259 return -ENODEV; 260 261 if (!axp20x) { 262 dev_err(&pdev->dev, "Parent drvdata not set\n"); 263 return -EINVAL; 264 } 265 266 power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); 267 if (!power) 268 return -ENOMEM; 269 270 axp_data = of_device_get_match_data(&pdev->dev); 271 272 if (axp_data->acin_adc) { 273 power->acin_v = devm_iio_channel_get(&pdev->dev, "acin_v"); 274 if (IS_ERR(power->acin_v)) { 275 if (PTR_ERR(power->acin_v) == -ENODEV) 276 return -EPROBE_DEFER; 277 return PTR_ERR(power->acin_v); 278 } 279 280 power->acin_i = devm_iio_channel_get(&pdev->dev, "acin_i"); 281 if (IS_ERR(power->acin_i)) { 282 if (PTR_ERR(power->acin_i) == -ENODEV) 283 return -EPROBE_DEFER; 284 return PTR_ERR(power->acin_i); 285 } 286 } 287 288 power->regmap = dev_get_regmap(pdev->dev.parent, NULL); 289 290 platform_set_drvdata(pdev, power); 291 292 psy_cfg.of_node = pdev->dev.of_node; 293 psy_cfg.drv_data = power; 294 295 power->supply = devm_power_supply_register(&pdev->dev, 296 axp_data->power_desc, 297 &psy_cfg); 298 if (IS_ERR(power->supply)) 299 return PTR_ERR(power->supply); 300 301 /* Request irqs after registering, as irqs may trigger immediately */ 302 for (i = 0; irq_names[i]; i++) { 303 irq = platform_get_irq_byname(pdev, irq_names[i]); 304 if (irq < 0) { 305 dev_warn(&pdev->dev, "No IRQ for %s: %d\n", 306 irq_names[i], irq); 307 continue; 308 } 309 irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 310 ret = devm_request_any_context_irq(&pdev->dev, irq, 311 axp20x_ac_power_irq, 0, 312 DRVNAME, power); 313 if (ret < 0) 314 dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", 315 irq_names[i], ret); 316 } 317 318 return 0; 319 } 320 321 static const struct of_device_id axp20x_ac_power_match[] = { 322 { 323 .compatible = "x-powers,axp202-ac-power-supply", 324 .data = &axp20x_data, 325 }, { 326 .compatible = "x-powers,axp221-ac-power-supply", 327 .data = &axp22x_data, 328 }, { 329 .compatible = "x-powers,axp813-ac-power-supply", 330 .data = &axp813_data, 331 }, { /* sentinel */ } 332 }; 333 MODULE_DEVICE_TABLE(of, axp20x_ac_power_match); 334 335 static struct platform_driver axp20x_ac_power_driver = { 336 .probe = axp20x_ac_power_probe, 337 .driver = { 338 .name = DRVNAME, 339 .of_match_table = axp20x_ac_power_match, 340 }, 341 }; 342 343 module_platform_driver(axp20x_ac_power_driver); 344 345 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@free-electrons.com>"); 346 MODULE_DESCRIPTION("AXP20X and AXP22X PMICs' AC power supply driver"); 347 MODULE_LICENSE("GPL"); 348