1 /* 2 * AXP20x PMIC USB power supply status driver 3 * 4 * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com> 5 * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org> 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/bitops.h> 14 #include <linux/device.h> 15 #include <linux/init.h> 16 #include <linux/interrupt.h> 17 #include <linux/kernel.h> 18 #include <linux/mfd/axp20x.h> 19 #include <linux/module.h> 20 #include <linux/of.h> 21 #include <linux/of_device.h> 22 #include <linux/platform_device.h> 23 #include <linux/power_supply.h> 24 #include <linux/regmap.h> 25 #include <linux/slab.h> 26 #include <linux/iio/consumer.h> 27 28 #define DRVNAME "axp20x-usb-power-supply" 29 30 #define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5) 31 #define AXP20X_PWR_STATUS_VBUS_USED BIT(4) 32 33 #define AXP20X_USB_STATUS_VBUS_VALID BIT(2) 34 35 #define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000) 36 #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) 37 #define AXP20X_VBUS_VHOLD_OFFSET 3 38 #define AXP20X_VBUS_CLIMIT_MASK 3 39 #define AXP20X_VBUC_CLIMIT_900mA 0 40 #define AXP20X_VBUC_CLIMIT_500mA 1 41 #define AXP20X_VBUC_CLIMIT_100mA 2 42 #define AXP20X_VBUC_CLIMIT_NONE 3 43 44 #define AXP20X_ADC_EN1_VBUS_CURR BIT(2) 45 #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) 46 47 #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) 48 49 struct axp20x_usb_power { 50 struct device_node *np; 51 struct regmap *regmap; 52 struct power_supply *supply; 53 enum axp20x_variants axp20x_id; 54 struct iio_channel *vbus_v; 55 struct iio_channel *vbus_i; 56 }; 57 58 static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) 59 { 60 struct axp20x_usb_power *power = devid; 61 62 power_supply_changed(power->supply); 63 64 return IRQ_HANDLED; 65 } 66 67 static int axp20x_usb_power_get_property(struct power_supply *psy, 68 enum power_supply_property psp, union power_supply_propval *val) 69 { 70 struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 71 unsigned int input, v; 72 int ret; 73 74 switch (psp) { 75 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 76 ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 77 if (ret) 78 return ret; 79 80 val->intval = AXP20X_VBUS_VHOLD_uV(v); 81 return 0; 82 case POWER_SUPPLY_PROP_VOLTAGE_NOW: 83 if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 84 ret = iio_read_channel_processed(power->vbus_v, 85 &val->intval); 86 if (ret) 87 return ret; 88 89 /* 90 * IIO framework gives mV but Power Supply framework 91 * gives uV. 92 */ 93 val->intval *= 1000; 94 return 0; 95 } 96 97 ret = axp20x_read_variable_width(power->regmap, 98 AXP20X_VBUS_V_ADC_H, 12); 99 if (ret < 0) 100 return ret; 101 102 val->intval = ret * 1700; /* 1 step = 1.7 mV */ 103 return 0; 104 case POWER_SUPPLY_PROP_CURRENT_MAX: 105 ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); 106 if (ret) 107 return ret; 108 109 switch (v & AXP20X_VBUS_CLIMIT_MASK) { 110 case AXP20X_VBUC_CLIMIT_100mA: 111 if (power->axp20x_id == AXP221_ID) 112 val->intval = -1; /* No 100mA limit */ 113 else 114 val->intval = 100000; 115 break; 116 case AXP20X_VBUC_CLIMIT_500mA: 117 val->intval = 500000; 118 break; 119 case AXP20X_VBUC_CLIMIT_900mA: 120 val->intval = 900000; 121 break; 122 case AXP20X_VBUC_CLIMIT_NONE: 123 val->intval = -1; 124 break; 125 } 126 return 0; 127 case POWER_SUPPLY_PROP_CURRENT_NOW: 128 if (IS_ENABLED(CONFIG_AXP20X_ADC)) { 129 ret = iio_read_channel_processed(power->vbus_i, 130 &val->intval); 131 if (ret) 132 return ret; 133 134 /* 135 * IIO framework gives mA but Power Supply framework 136 * gives uA. 137 */ 138 val->intval *= 1000; 139 return 0; 140 } 141 142 ret = axp20x_read_variable_width(power->regmap, 143 AXP20X_VBUS_I_ADC_H, 12); 144 if (ret < 0) 145 return ret; 146 147 val->intval = ret * 375; /* 1 step = 0.375 mA */ 148 return 0; 149 default: 150 break; 151 } 152 153 /* All the properties below need the input-status reg value */ 154 ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input); 155 if (ret) 156 return ret; 157 158 switch (psp) { 159 case POWER_SUPPLY_PROP_HEALTH: 160 if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) { 161 val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; 162 break; 163 } 164 165 val->intval = POWER_SUPPLY_HEALTH_GOOD; 166 167 if (power->axp20x_id == AXP202_ID) { 168 ret = regmap_read(power->regmap, 169 AXP20X_USB_OTG_STATUS, &v); 170 if (ret) 171 return ret; 172 173 if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) 174 val->intval = 175 POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 176 } 177 break; 178 case POWER_SUPPLY_PROP_PRESENT: 179 val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT); 180 break; 181 case POWER_SUPPLY_PROP_ONLINE: 182 val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED); 183 break; 184 default: 185 return -EINVAL; 186 } 187 188 return 0; 189 } 190 191 static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, 192 int intval) 193 { 194 int val; 195 196 switch (intval) { 197 case 4000000: 198 case 4100000: 199 case 4200000: 200 case 4300000: 201 case 4400000: 202 case 4500000: 203 case 4600000: 204 case 4700000: 205 val = (intval - 4000000) / 100000; 206 return regmap_update_bits(power->regmap, 207 AXP20X_VBUS_IPSOUT_MGMT, 208 AXP20X_VBUS_VHOLD_MASK, 209 val << AXP20X_VBUS_VHOLD_OFFSET); 210 default: 211 return -EINVAL; 212 } 213 214 return -EINVAL; 215 } 216 217 static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, 218 int intval) 219 { 220 int val; 221 222 switch (intval) { 223 case 100000: 224 if (power->axp20x_id == AXP221_ID) 225 return -EINVAL; 226 /* fall through */ 227 case 500000: 228 case 900000: 229 val = (900000 - intval) / 400000; 230 return regmap_update_bits(power->regmap, 231 AXP20X_VBUS_IPSOUT_MGMT, 232 AXP20X_VBUS_CLIMIT_MASK, val); 233 default: 234 return -EINVAL; 235 } 236 237 return -EINVAL; 238 } 239 240 static int axp20x_usb_power_set_property(struct power_supply *psy, 241 enum power_supply_property psp, 242 const union power_supply_propval *val) 243 { 244 struct axp20x_usb_power *power = power_supply_get_drvdata(psy); 245 246 switch (psp) { 247 case POWER_SUPPLY_PROP_VOLTAGE_MIN: 248 return axp20x_usb_power_set_voltage_min(power, val->intval); 249 250 case POWER_SUPPLY_PROP_CURRENT_MAX: 251 return axp20x_usb_power_set_current_max(power, val->intval); 252 253 default: 254 return -EINVAL; 255 } 256 257 return -EINVAL; 258 } 259 260 static int axp20x_usb_power_prop_writeable(struct power_supply *psy, 261 enum power_supply_property psp) 262 { 263 return psp == POWER_SUPPLY_PROP_VOLTAGE_MIN || 264 psp == POWER_SUPPLY_PROP_CURRENT_MAX; 265 } 266 267 static enum power_supply_property axp20x_usb_power_properties[] = { 268 POWER_SUPPLY_PROP_HEALTH, 269 POWER_SUPPLY_PROP_PRESENT, 270 POWER_SUPPLY_PROP_ONLINE, 271 POWER_SUPPLY_PROP_VOLTAGE_MIN, 272 POWER_SUPPLY_PROP_VOLTAGE_NOW, 273 POWER_SUPPLY_PROP_CURRENT_MAX, 274 POWER_SUPPLY_PROP_CURRENT_NOW, 275 }; 276 277 static enum power_supply_property axp22x_usb_power_properties[] = { 278 POWER_SUPPLY_PROP_HEALTH, 279 POWER_SUPPLY_PROP_PRESENT, 280 POWER_SUPPLY_PROP_ONLINE, 281 POWER_SUPPLY_PROP_VOLTAGE_MIN, 282 POWER_SUPPLY_PROP_CURRENT_MAX, 283 }; 284 285 static const struct power_supply_desc axp20x_usb_power_desc = { 286 .name = "axp20x-usb", 287 .type = POWER_SUPPLY_TYPE_USB, 288 .properties = axp20x_usb_power_properties, 289 .num_properties = ARRAY_SIZE(axp20x_usb_power_properties), 290 .property_is_writeable = axp20x_usb_power_prop_writeable, 291 .get_property = axp20x_usb_power_get_property, 292 .set_property = axp20x_usb_power_set_property, 293 }; 294 295 static const struct power_supply_desc axp22x_usb_power_desc = { 296 .name = "axp20x-usb", 297 .type = POWER_SUPPLY_TYPE_USB, 298 .properties = axp22x_usb_power_properties, 299 .num_properties = ARRAY_SIZE(axp22x_usb_power_properties), 300 .property_is_writeable = axp20x_usb_power_prop_writeable, 301 .get_property = axp20x_usb_power_get_property, 302 .set_property = axp20x_usb_power_set_property, 303 }; 304 305 static int configure_iio_channels(struct platform_device *pdev, 306 struct axp20x_usb_power *power) 307 { 308 power->vbus_v = devm_iio_channel_get(&pdev->dev, "vbus_v"); 309 if (IS_ERR(power->vbus_v)) { 310 if (PTR_ERR(power->vbus_v) == -ENODEV) 311 return -EPROBE_DEFER; 312 return PTR_ERR(power->vbus_v); 313 } 314 315 power->vbus_i = devm_iio_channel_get(&pdev->dev, "vbus_i"); 316 if (IS_ERR(power->vbus_i)) { 317 if (PTR_ERR(power->vbus_i) == -ENODEV) 318 return -EPROBE_DEFER; 319 return PTR_ERR(power->vbus_i); 320 } 321 322 return 0; 323 } 324 325 static int configure_adc_registers(struct axp20x_usb_power *power) 326 { 327 /* Enable vbus voltage and current measurement */ 328 return regmap_update_bits(power->regmap, AXP20X_ADC_EN1, 329 AXP20X_ADC_EN1_VBUS_CURR | 330 AXP20X_ADC_EN1_VBUS_VOLT, 331 AXP20X_ADC_EN1_VBUS_CURR | 332 AXP20X_ADC_EN1_VBUS_VOLT); 333 } 334 335 static int axp20x_usb_power_probe(struct platform_device *pdev) 336 { 337 struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); 338 struct power_supply_config psy_cfg = {}; 339 struct axp20x_usb_power *power; 340 static const char * const axp20x_irq_names[] = { "VBUS_PLUGIN", 341 "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID", NULL }; 342 static const char * const axp22x_irq_names[] = { 343 "VBUS_PLUGIN", "VBUS_REMOVAL", NULL }; 344 const char * const *irq_names; 345 const struct power_supply_desc *usb_power_desc; 346 int i, irq, ret; 347 348 if (!of_device_is_available(pdev->dev.of_node)) 349 return -ENODEV; 350 351 if (!axp20x) { 352 dev_err(&pdev->dev, "Parent drvdata not set\n"); 353 return -EINVAL; 354 } 355 356 power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL); 357 if (!power) 358 return -ENOMEM; 359 360 power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( 361 &pdev->dev); 362 363 power->np = pdev->dev.of_node; 364 power->regmap = axp20x->regmap; 365 366 if (power->axp20x_id == AXP202_ID) { 367 /* Enable vbus valid checking */ 368 ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON, 369 AXP20X_VBUS_MON_VBUS_VALID, 370 AXP20X_VBUS_MON_VBUS_VALID); 371 if (ret) 372 return ret; 373 374 if (IS_ENABLED(CONFIG_AXP20X_ADC)) 375 ret = configure_iio_channels(pdev, power); 376 else 377 ret = configure_adc_registers(power); 378 379 if (ret) 380 return ret; 381 382 usb_power_desc = &axp20x_usb_power_desc; 383 irq_names = axp20x_irq_names; 384 } else if (power->axp20x_id == AXP221_ID || 385 power->axp20x_id == AXP223_ID) { 386 usb_power_desc = &axp22x_usb_power_desc; 387 irq_names = axp22x_irq_names; 388 } else { 389 dev_err(&pdev->dev, "Unsupported AXP variant: %ld\n", 390 axp20x->variant); 391 return -EINVAL; 392 } 393 394 psy_cfg.of_node = pdev->dev.of_node; 395 psy_cfg.drv_data = power; 396 397 power->supply = devm_power_supply_register(&pdev->dev, usb_power_desc, 398 &psy_cfg); 399 if (IS_ERR(power->supply)) 400 return PTR_ERR(power->supply); 401 402 /* Request irqs after registering, as irqs may trigger immediately */ 403 for (i = 0; irq_names[i]; i++) { 404 irq = platform_get_irq_byname(pdev, irq_names[i]); 405 if (irq < 0) { 406 dev_warn(&pdev->dev, "No IRQ for %s: %d\n", 407 irq_names[i], irq); 408 continue; 409 } 410 irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq); 411 ret = devm_request_any_context_irq(&pdev->dev, irq, 412 axp20x_usb_power_irq, 0, DRVNAME, power); 413 if (ret < 0) 414 dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n", 415 irq_names[i], ret); 416 } 417 418 return 0; 419 } 420 421 static const struct of_device_id axp20x_usb_power_match[] = { 422 { 423 .compatible = "x-powers,axp202-usb-power-supply", 424 .data = (void *)AXP202_ID, 425 }, { 426 .compatible = "x-powers,axp221-usb-power-supply", 427 .data = (void *)AXP221_ID, 428 }, { 429 .compatible = "x-powers,axp223-usb-power-supply", 430 .data = (void *)AXP223_ID, 431 }, { /* sentinel */ } 432 }; 433 MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); 434 435 static struct platform_driver axp20x_usb_power_driver = { 436 .probe = axp20x_usb_power_probe, 437 .driver = { 438 .name = DRVNAME, 439 .of_match_table = axp20x_usb_power_match, 440 }, 441 }; 442 443 module_platform_driver(axp20x_usb_power_driver); 444 445 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 446 MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver"); 447 MODULE_LICENSE("GPL"); 448