max8903_charger.c (11199B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver 4 * 5 * Copyright (C) 2011 Samsung Electronics 6 * MyungJoo Ham <myungjoo.ham@samsung.com> 7 */ 8 9#include <linux/gpio/consumer.h> 10#include <linux/interrupt.h> 11#include <linux/module.h> 12#include <linux/of.h> 13#include <linux/of_device.h> 14#include <linux/slab.h> 15#include <linux/power_supply.h> 16#include <linux/platform_device.h> 17 18struct max8903_data { 19 struct device *dev; 20 struct power_supply *psy; 21 struct power_supply_desc psy_desc; 22 /* 23 * GPIOs 24 * chg, flt, dcm and usus are optional. 25 * dok or uok must be present. 26 * If dok is present, cen must be present. 27 */ 28 struct gpio_desc *cen; /* Charger Enable input */ 29 struct gpio_desc *dok; /* DC (Adapter) Power OK output */ 30 struct gpio_desc *uok; /* USB Power OK output */ 31 struct gpio_desc *chg; /* Charger status output */ 32 struct gpio_desc *flt; /* Fault output */ 33 struct gpio_desc *dcm; /* Current-Limit Mode input (1: DC, 2: USB) */ 34 struct gpio_desc *usus; /* USB Suspend Input (1: suspended) */ 35 bool fault; 36 bool usb_in; 37 bool ta_in; 38}; 39 40static enum power_supply_property max8903_charger_props[] = { 41 POWER_SUPPLY_PROP_STATUS, /* Charger status output */ 42 POWER_SUPPLY_PROP_ONLINE, /* External power source */ 43 POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ 44}; 45 46static int max8903_get_property(struct power_supply *psy, 47 enum power_supply_property psp, 48 union power_supply_propval *val) 49{ 50 struct max8903_data *data = power_supply_get_drvdata(psy); 51 52 switch (psp) { 53 case POWER_SUPPLY_PROP_STATUS: 54 val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 55 if (data->chg) { 56 if (gpiod_get_value(data->chg)) 57 /* CHG asserted */ 58 val->intval = POWER_SUPPLY_STATUS_CHARGING; 59 else if (data->usb_in || data->ta_in) 60 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 61 else 62 val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 63 } 64 break; 65 case POWER_SUPPLY_PROP_ONLINE: 66 val->intval = 0; 67 if (data->usb_in || data->ta_in) 68 val->intval = 1; 69 break; 70 case POWER_SUPPLY_PROP_HEALTH: 71 val->intval = POWER_SUPPLY_HEALTH_GOOD; 72 if (data->fault) 73 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; 74 break; 75 default: 76 return -EINVAL; 77 } 78 79 return 0; 80} 81 82static irqreturn_t max8903_dcin(int irq, void *_data) 83{ 84 struct max8903_data *data = _data; 85 bool ta_in; 86 enum power_supply_type old_type; 87 88 /* 89 * This means the line is asserted. 90 * 91 * The signal is active low, but the inversion is handled in the GPIO 92 * library as the line should be flagged GPIO_ACTIVE_LOW in the device 93 * tree. 94 */ 95 ta_in = gpiod_get_value(data->dok); 96 97 if (ta_in == data->ta_in) 98 return IRQ_HANDLED; 99 100 data->ta_in = ta_in; 101 102 /* Set Current-Limit-Mode 1:DC 0:USB */ 103 if (data->dcm) 104 gpiod_set_value(data->dcm, ta_in); 105 106 /* Charger Enable / Disable */ 107 if (data->cen) { 108 int val; 109 110 if (ta_in) 111 /* Certainly enable if DOK is asserted */ 112 val = 1; 113 else if (data->usb_in) 114 /* Enable if the USB charger is enabled */ 115 val = 1; 116 else 117 /* Else default-disable */ 118 val = 0; 119 120 gpiod_set_value(data->cen, val); 121 } 122 123 dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? 124 "Connected" : "Disconnected"); 125 126 old_type = data->psy_desc.type; 127 128 if (data->ta_in) 129 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 130 else if (data->usb_in) 131 data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 132 else 133 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 134 135 if (old_type != data->psy_desc.type) 136 power_supply_changed(data->psy); 137 138 return IRQ_HANDLED; 139} 140 141static irqreturn_t max8903_usbin(int irq, void *_data) 142{ 143 struct max8903_data *data = _data; 144 bool usb_in; 145 enum power_supply_type old_type; 146 147 /* 148 * This means the line is asserted. 149 * 150 * The signal is active low, but the inversion is handled in the GPIO 151 * library as the line should be flagged GPIO_ACTIVE_LOW in the device 152 * tree. 153 */ 154 usb_in = gpiod_get_value(data->uok); 155 156 if (usb_in == data->usb_in) 157 return IRQ_HANDLED; 158 159 data->usb_in = usb_in; 160 161 /* Do not touch Current-Limit-Mode */ 162 163 /* Charger Enable / Disable */ 164 if (data->cen) { 165 int val; 166 167 if (usb_in) 168 /* Certainly enable if UOK is asserted */ 169 val = 1; 170 else if (data->ta_in) 171 /* Enable if the DC charger is enabled */ 172 val = 1; 173 else 174 /* Else default-disable */ 175 val = 0; 176 177 gpiod_set_value(data->cen, val); 178 } 179 180 dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? 181 "Connected" : "Disconnected"); 182 183 old_type = data->psy_desc.type; 184 185 if (data->ta_in) 186 data->psy_desc.type = POWER_SUPPLY_TYPE_MAINS; 187 else if (data->usb_in) 188 data->psy_desc.type = POWER_SUPPLY_TYPE_USB; 189 else 190 data->psy_desc.type = POWER_SUPPLY_TYPE_BATTERY; 191 192 if (old_type != data->psy_desc.type) 193 power_supply_changed(data->psy); 194 195 return IRQ_HANDLED; 196} 197 198static irqreturn_t max8903_fault(int irq, void *_data) 199{ 200 struct max8903_data *data = _data; 201 bool fault; 202 203 /* 204 * This means the line is asserted. 205 * 206 * The signal is active low, but the inversion is handled in the GPIO 207 * library as the line should be flagged GPIO_ACTIVE_LOW in the device 208 * tree. 209 */ 210 fault = gpiod_get_value(data->flt); 211 212 if (fault == data->fault) 213 return IRQ_HANDLED; 214 215 data->fault = fault; 216 217 if (fault) 218 dev_err(data->dev, "Charger suffers a fault and stops.\n"); 219 else 220 dev_err(data->dev, "Charger recovered from a fault.\n"); 221 222 return IRQ_HANDLED; 223} 224 225static int max8903_setup_gpios(struct platform_device *pdev) 226{ 227 struct max8903_data *data = platform_get_drvdata(pdev); 228 struct device *dev = &pdev->dev; 229 bool ta_in = false; 230 bool usb_in = false; 231 enum gpiod_flags flags; 232 233 data->dok = devm_gpiod_get_optional(dev, "dok", GPIOD_IN); 234 if (IS_ERR(data->dok)) 235 return dev_err_probe(dev, PTR_ERR(data->dok), 236 "failed to get DOK GPIO"); 237 if (data->dok) { 238 gpiod_set_consumer_name(data->dok, data->psy_desc.name); 239 /* 240 * The DC OK is pulled up to 1 and goes low when a charger 241 * is plugged in (active low) but in the device tree the 242 * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted) 243 * here if the DC charger is plugged in. 244 */ 245 ta_in = gpiod_get_value(data->dok); 246 } 247 248 data->uok = devm_gpiod_get_optional(dev, "uok", GPIOD_IN); 249 if (IS_ERR(data->uok)) 250 return dev_err_probe(dev, PTR_ERR(data->uok), 251 "failed to get UOK GPIO"); 252 if (data->uok) { 253 gpiod_set_consumer_name(data->uok, data->psy_desc.name); 254 /* 255 * The USB OK is pulled up to 1 and goes low when a USB charger 256 * is plugged in (active low) but in the device tree the 257 * line is marked as GPIO_ACTIVE_LOW so we get a 1 (asserted) 258 * here if the USB charger is plugged in. 259 */ 260 usb_in = gpiod_get_value(data->uok); 261 } 262 263 /* Either DC OK or USB OK must be provided */ 264 if (!data->dok && !data->uok) { 265 dev_err(dev, "no valid power source\n"); 266 return -EINVAL; 267 } 268 269 /* 270 * If either charger is already connected at this point, 271 * assert the CEN line and enable charging from the start. 272 * 273 * The line is active low but also marked with GPIO_ACTIVE_LOW 274 * in the device tree, so when we assert the line with 275 * GPIOD_OUT_HIGH the line will be driven low. 276 */ 277 flags = (ta_in || usb_in) ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; 278 /* 279 * If DC OK is provided, Charger Enable CEN is compulsory 280 * so this is not optional here. 281 */ 282 data->cen = devm_gpiod_get(dev, "cen", flags); 283 if (IS_ERR(data->cen)) 284 return dev_err_probe(dev, PTR_ERR(data->cen), 285 "failed to get CEN GPIO"); 286 gpiod_set_consumer_name(data->cen, data->psy_desc.name); 287 288 /* 289 * If the DC charger is connected, then select it. 290 * 291 * The DCM line should be marked GPIO_ACTIVE_HIGH in the 292 * device tree. Driving it high will enable the DC charger 293 * input over the USB charger input. 294 */ 295 flags = ta_in ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; 296 data->dcm = devm_gpiod_get_optional(dev, "dcm", flags); 297 if (IS_ERR(data->dcm)) 298 return dev_err_probe(dev, PTR_ERR(data->dcm), 299 "failed to get DCM GPIO"); 300 gpiod_set_consumer_name(data->dcm, data->psy_desc.name); 301 302 data->chg = devm_gpiod_get_optional(dev, "chg", GPIOD_IN); 303 if (IS_ERR(data->chg)) 304 return dev_err_probe(dev, PTR_ERR(data->chg), 305 "failed to get CHG GPIO"); 306 gpiod_set_consumer_name(data->chg, data->psy_desc.name); 307 308 data->flt = devm_gpiod_get_optional(dev, "flt", GPIOD_IN); 309 if (IS_ERR(data->flt)) 310 return dev_err_probe(dev, PTR_ERR(data->flt), 311 "failed to get FLT GPIO"); 312 gpiod_set_consumer_name(data->flt, data->psy_desc.name); 313 314 data->usus = devm_gpiod_get_optional(dev, "usus", GPIOD_IN); 315 if (IS_ERR(data->usus)) 316 return dev_err_probe(dev, PTR_ERR(data->usus), 317 "failed to get USUS GPIO"); 318 gpiod_set_consumer_name(data->usus, data->psy_desc.name); 319 320 data->fault = false; 321 data->ta_in = ta_in; 322 data->usb_in = usb_in; 323 324 return 0; 325} 326 327static int max8903_probe(struct platform_device *pdev) 328{ 329 struct max8903_data *data; 330 struct device *dev = &pdev->dev; 331 struct power_supply_config psy_cfg = {}; 332 int ret = 0; 333 334 data = devm_kzalloc(dev, sizeof(struct max8903_data), GFP_KERNEL); 335 if (!data) 336 return -ENOMEM; 337 338 data->dev = dev; 339 platform_set_drvdata(pdev, data); 340 341 ret = max8903_setup_gpios(pdev); 342 if (ret) 343 return ret; 344 345 data->psy_desc.name = "max8903_charger"; 346 data->psy_desc.type = (data->ta_in) ? POWER_SUPPLY_TYPE_MAINS : 347 ((data->usb_in) ? POWER_SUPPLY_TYPE_USB : 348 POWER_SUPPLY_TYPE_BATTERY); 349 data->psy_desc.get_property = max8903_get_property; 350 data->psy_desc.properties = max8903_charger_props; 351 data->psy_desc.num_properties = ARRAY_SIZE(max8903_charger_props); 352 353 psy_cfg.of_node = dev->of_node; 354 psy_cfg.drv_data = data; 355 356 data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg); 357 if (IS_ERR(data->psy)) { 358 dev_err(dev, "failed: power supply register.\n"); 359 return PTR_ERR(data->psy); 360 } 361 362 if (data->dok) { 363 ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->dok), 364 NULL, max8903_dcin, 365 IRQF_TRIGGER_FALLING | 366 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 367 "MAX8903 DC IN", data); 368 if (ret) { 369 dev_err(dev, "Cannot request irq %d for DC (%d)\n", 370 gpiod_to_irq(data->dok), ret); 371 return ret; 372 } 373 } 374 375 if (data->uok) { 376 ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->uok), 377 NULL, max8903_usbin, 378 IRQF_TRIGGER_FALLING | 379 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 380 "MAX8903 USB IN", data); 381 if (ret) { 382 dev_err(dev, "Cannot request irq %d for USB (%d)\n", 383 gpiod_to_irq(data->uok), ret); 384 return ret; 385 } 386 } 387 388 if (data->flt) { 389 ret = devm_request_threaded_irq(dev, gpiod_to_irq(data->flt), 390 NULL, max8903_fault, 391 IRQF_TRIGGER_FALLING | 392 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 393 "MAX8903 Fault", data); 394 if (ret) { 395 dev_err(dev, "Cannot request irq %d for Fault (%d)\n", 396 gpiod_to_irq(data->flt), ret); 397 return ret; 398 } 399 } 400 401 return 0; 402} 403 404static const struct of_device_id max8903_match_ids[] = { 405 { .compatible = "maxim,max8903", }, 406 { /* sentinel */ } 407}; 408MODULE_DEVICE_TABLE(of, max8903_match_ids); 409 410static struct platform_driver max8903_driver = { 411 .probe = max8903_probe, 412 .driver = { 413 .name = "max8903-charger", 414 .of_match_table = max8903_match_ids 415 }, 416}; 417 418module_platform_driver(max8903_driver); 419 420MODULE_LICENSE("GPL"); 421MODULE_DESCRIPTION("MAX8903 Charger Driver"); 422MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 423MODULE_ALIAS("platform:max8903-charger");