647 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * DA9150 Charger Driver4 *5 * Copyright (c) 2014 Dialog Semiconductor6 *7 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>8 */9 10#include <linux/kernel.h>11#include <linux/slab.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/interrupt.h>15#include <linux/power_supply.h>16#include <linux/notifier.h>17#include <linux/usb/phy.h>18#include <linux/iio/consumer.h>19#include <linux/mfd/da9150/core.h>20#include <linux/mfd/da9150/registers.h>21 22/* Private data */23struct da9150_charger {24 struct da9150 *da9150;25 struct device *dev;26 27 struct power_supply *usb;28 struct power_supply *battery;29 struct power_supply *supply_online;30 31 struct usb_phy *usb_phy;32 struct notifier_block otg_nb;33 struct work_struct otg_work;34 unsigned long usb_event;35 36 struct iio_channel *ibus_chan;37 struct iio_channel *vbus_chan;38 struct iio_channel *tjunc_chan;39 struct iio_channel *vbat_chan;40};41 42static inline int da9150_charger_supply_online(struct da9150_charger *charger,43 struct power_supply *psy,44 union power_supply_propval *val)45{46 val->intval = (psy == charger->supply_online) ? 1 : 0;47 48 return 0;49}50 51/* Charger Properties */52static int da9150_charger_vbus_voltage_now(struct da9150_charger *charger,53 union power_supply_propval *val)54{55 int v_val, ret;56 57 /* Read processed value - mV units */58 ret = iio_read_channel_processed(charger->vbus_chan, &v_val);59 if (ret < 0)60 return ret;61 62 /* Convert voltage to expected uV units */63 val->intval = v_val * 1000;64 65 return 0;66}67 68static int da9150_charger_ibus_current_avg(struct da9150_charger *charger,69 union power_supply_propval *val)70{71 int i_val, ret;72 73 /* Read processed value - mA units */74 ret = iio_read_channel_processed(charger->ibus_chan, &i_val);75 if (ret < 0)76 return ret;77 78 /* Convert current to expected uA units */79 val->intval = i_val * 1000;80 81 return 0;82}83 84static int da9150_charger_tjunc_temp(struct da9150_charger *charger,85 union power_supply_propval *val)86{87 int t_val, ret;88 89 /* Read processed value - 0.001 degrees C units */90 ret = iio_read_channel_processed(charger->tjunc_chan, &t_val);91 if (ret < 0)92 return ret;93 94 /* Convert temp to expect 0.1 degrees C units */95 val->intval = t_val / 100;96 97 return 0;98}99 100static enum power_supply_property da9150_charger_props[] = {101 POWER_SUPPLY_PROP_ONLINE,102 POWER_SUPPLY_PROP_VOLTAGE_NOW,103 POWER_SUPPLY_PROP_CURRENT_AVG,104 POWER_SUPPLY_PROP_TEMP,105};106 107static int da9150_charger_get_prop(struct power_supply *psy,108 enum power_supply_property psp,109 union power_supply_propval *val)110{111 struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);112 int ret;113 114 switch (psp) {115 case POWER_SUPPLY_PROP_ONLINE:116 ret = da9150_charger_supply_online(charger, psy, val);117 break;118 case POWER_SUPPLY_PROP_VOLTAGE_NOW:119 ret = da9150_charger_vbus_voltage_now(charger, val);120 break;121 case POWER_SUPPLY_PROP_CURRENT_AVG:122 ret = da9150_charger_ibus_current_avg(charger, val);123 break;124 case POWER_SUPPLY_PROP_TEMP:125 ret = da9150_charger_tjunc_temp(charger, val);126 break;127 default:128 ret = -EINVAL;129 break;130 }131 132 return ret;133}134 135/* Battery Properties */136static int da9150_charger_battery_status(struct da9150_charger *charger,137 union power_supply_propval *val)138{139 u8 reg;140 141 /* Check to see if battery is discharging */142 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);143 144 if (((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_OFF) ||145 ((reg & DA9150_VBUS_STAT_MASK) == DA9150_VBUS_STAT_WAIT)) {146 val->intval = POWER_SUPPLY_STATUS_DISCHARGING;147 148 return 0;149 }150 151 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);152 153 /* Now check for other states */154 switch (reg & DA9150_CHG_STAT_MASK) {155 case DA9150_CHG_STAT_ACT:156 case DA9150_CHG_STAT_PRE:157 case DA9150_CHG_STAT_CC:158 case DA9150_CHG_STAT_CV:159 val->intval = POWER_SUPPLY_STATUS_CHARGING;160 break;161 case DA9150_CHG_STAT_OFF:162 case DA9150_CHG_STAT_SUSP:163 case DA9150_CHG_STAT_TEMP:164 case DA9150_CHG_STAT_TIME:165 case DA9150_CHG_STAT_BAT:166 val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;167 break;168 case DA9150_CHG_STAT_FULL:169 val->intval = POWER_SUPPLY_STATUS_FULL;170 break;171 default:172 val->intval = POWER_SUPPLY_STATUS_UNKNOWN;173 break;174 }175 176 return 0;177}178 179static int da9150_charger_battery_health(struct da9150_charger *charger,180 union power_supply_propval *val)181{182 u8 reg;183 184 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);185 186 /* Check if temperature limit reached */187 switch (reg & DA9150_CHG_TEMP_MASK) {188 case DA9150_CHG_TEMP_UNDER:189 val->intval = POWER_SUPPLY_HEALTH_COLD;190 return 0;191 case DA9150_CHG_TEMP_OVER:192 val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;193 return 0;194 default:195 break;196 }197 198 /* Check for other health states */199 switch (reg & DA9150_CHG_STAT_MASK) {200 case DA9150_CHG_STAT_ACT:201 case DA9150_CHG_STAT_PRE:202 val->intval = POWER_SUPPLY_HEALTH_DEAD;203 break;204 case DA9150_CHG_STAT_TIME:205 val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;206 break;207 default:208 val->intval = POWER_SUPPLY_HEALTH_GOOD;209 break;210 }211 212 return 0;213}214 215static int da9150_charger_battery_present(struct da9150_charger *charger,216 union power_supply_propval *val)217{218 u8 reg;219 220 /* Check if battery present or removed */221 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);222 if ((reg & DA9150_CHG_STAT_MASK) == DA9150_CHG_STAT_BAT)223 val->intval = 0;224 else225 val->intval = 1;226 227 return 0;228}229 230static int da9150_charger_battery_charge_type(struct da9150_charger *charger,231 union power_supply_propval *val)232{233 u8 reg;234 235 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_J);236 237 switch (reg & DA9150_CHG_STAT_MASK) {238 case DA9150_CHG_STAT_CC:239 val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;240 break;241 case DA9150_CHG_STAT_ACT:242 case DA9150_CHG_STAT_PRE:243 case DA9150_CHG_STAT_CV:244 val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;245 break;246 default:247 val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;248 break;249 }250 251 return 0;252}253 254static int da9150_charger_battery_voltage_min(struct da9150_charger *charger,255 union power_supply_propval *val)256{257 u8 reg;258 259 reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_C);260 261 /* Value starts at 2500 mV, 50 mV increments, presented in uV */262 val->intval = ((reg & DA9150_CHG_VFAULT_MASK) * 50000) + 2500000;263 264 return 0;265}266 267static int da9150_charger_battery_voltage_now(struct da9150_charger *charger,268 union power_supply_propval *val)269{270 int v_val, ret;271 272 /* Read processed value - mV units */273 ret = iio_read_channel_processed(charger->vbat_chan, &v_val);274 if (ret < 0)275 return ret;276 277 val->intval = v_val * 1000;278 279 return 0;280}281 282static int da9150_charger_battery_current_max(struct da9150_charger *charger,283 union power_supply_propval *val)284{285 int reg;286 287 reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_D);288 289 /* 25mA increments */290 val->intval = reg * 25000;291 292 return 0;293}294 295static int da9150_charger_battery_voltage_max(struct da9150_charger *charger,296 union power_supply_propval *val)297{298 u8 reg;299 300 reg = da9150_reg_read(charger->da9150, DA9150_PPR_CHGCTRL_B);301 302 /* Value starts at 3650 mV, 25 mV increments, presented in uV */303 val->intval = ((reg & DA9150_CHG_VBAT_MASK) * 25000) + 3650000;304 return 0;305}306 307static enum power_supply_property da9150_charger_bat_props[] = {308 POWER_SUPPLY_PROP_STATUS,309 POWER_SUPPLY_PROP_ONLINE,310 POWER_SUPPLY_PROP_HEALTH,311 POWER_SUPPLY_PROP_PRESENT,312 POWER_SUPPLY_PROP_CHARGE_TYPE,313 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,314 POWER_SUPPLY_PROP_VOLTAGE_NOW,315 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,316 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,317};318 319static int da9150_charger_battery_get_prop(struct power_supply *psy,320 enum power_supply_property psp,321 union power_supply_propval *val)322{323 struct da9150_charger *charger = dev_get_drvdata(psy->dev.parent);324 int ret;325 326 switch (psp) {327 case POWER_SUPPLY_PROP_STATUS:328 ret = da9150_charger_battery_status(charger, val);329 break;330 case POWER_SUPPLY_PROP_ONLINE:331 ret = da9150_charger_supply_online(charger, psy, val);332 break;333 case POWER_SUPPLY_PROP_HEALTH:334 ret = da9150_charger_battery_health(charger, val);335 break;336 case POWER_SUPPLY_PROP_PRESENT:337 ret = da9150_charger_battery_present(charger, val);338 break;339 case POWER_SUPPLY_PROP_CHARGE_TYPE:340 ret = da9150_charger_battery_charge_type(charger, val);341 break;342 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:343 ret = da9150_charger_battery_voltage_min(charger, val);344 break;345 case POWER_SUPPLY_PROP_VOLTAGE_NOW:346 ret = da9150_charger_battery_voltage_now(charger, val);347 break;348 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:349 ret = da9150_charger_battery_current_max(charger, val);350 break;351 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:352 ret = da9150_charger_battery_voltage_max(charger, val);353 break;354 default:355 ret = -EINVAL;356 break;357 }358 359 return ret;360}361 362static irqreturn_t da9150_charger_chg_irq(int irq, void *data)363{364 struct da9150_charger *charger = data;365 366 power_supply_changed(charger->battery);367 368 return IRQ_HANDLED;369}370 371static irqreturn_t da9150_charger_tjunc_irq(int irq, void *data)372{373 struct da9150_charger *charger = data;374 375 /* Nothing we can really do except report this. */376 dev_crit(charger->dev, "TJunc over temperature!!!\n");377 power_supply_changed(charger->usb);378 379 return IRQ_HANDLED;380}381 382static irqreturn_t da9150_charger_vfault_irq(int irq, void *data)383{384 struct da9150_charger *charger = data;385 386 /* Nothing we can really do except report this. */387 dev_crit(charger->dev, "VSYS under voltage!!!\n");388 power_supply_changed(charger->usb);389 power_supply_changed(charger->battery);390 391 return IRQ_HANDLED;392}393 394static irqreturn_t da9150_charger_vbus_irq(int irq, void *data)395{396 struct da9150_charger *charger = data;397 u8 reg;398 399 reg = da9150_reg_read(charger->da9150, DA9150_STATUS_H);400 401 /* Charger plugged in or battery only */402 switch (reg & DA9150_VBUS_STAT_MASK) {403 case DA9150_VBUS_STAT_OFF:404 case DA9150_VBUS_STAT_WAIT:405 charger->supply_online = charger->battery;406 break;407 case DA9150_VBUS_STAT_CHG:408 charger->supply_online = charger->usb;409 break;410 default:411 dev_warn(charger->dev, "Unknown VBUS state - reg = 0x%x\n",412 reg);413 charger->supply_online = NULL;414 break;415 }416 417 power_supply_changed(charger->usb);418 power_supply_changed(charger->battery);419 420 return IRQ_HANDLED;421}422 423static void da9150_charger_otg_work(struct work_struct *data)424{425 struct da9150_charger *charger =426 container_of(data, struct da9150_charger, otg_work);427 428 switch (charger->usb_event) {429 case USB_EVENT_ID:430 /* Enable OTG Boost */431 da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,432 DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_OTG);433 break;434 case USB_EVENT_NONE:435 /* Revert to charge mode */436 power_supply_changed(charger->usb);437 power_supply_changed(charger->battery);438 da9150_set_bits(charger->da9150, DA9150_PPR_BKCTRL_A,439 DA9150_VBUS_MODE_MASK, DA9150_VBUS_MODE_CHG);440 break;441 }442}443 444static int da9150_charger_otg_ncb(struct notifier_block *nb, unsigned long val,445 void *priv)446{447 struct da9150_charger *charger =448 container_of(nb, struct da9150_charger, otg_nb);449 450 dev_dbg(charger->dev, "DA9150 OTG notify %lu\n", val);451 452 charger->usb_event = val;453 schedule_work(&charger->otg_work);454 455 return NOTIFY_OK;456}457 458static int da9150_charger_register_irq(struct platform_device *pdev,459 irq_handler_t handler,460 const char *irq_name)461{462 struct device *dev = &pdev->dev;463 struct da9150_charger *charger = platform_get_drvdata(pdev);464 int irq, ret;465 466 irq = platform_get_irq_byname(pdev, irq_name);467 if (irq < 0)468 return irq;469 470 ret = request_threaded_irq(irq, NULL, handler, IRQF_ONESHOT, irq_name,471 charger);472 if (ret)473 dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);474 475 return ret;476}477 478static void da9150_charger_unregister_irq(struct platform_device *pdev,479 const char *irq_name)480{481 struct da9150_charger *charger = platform_get_drvdata(pdev);482 int irq;483 484 irq = platform_get_irq_byname(pdev, irq_name);485 if (irq < 0)486 return;487 488 free_irq(irq, charger);489}490 491static const struct power_supply_desc usb_desc = {492 .name = "da9150-usb",493 .type = POWER_SUPPLY_TYPE_USB,494 .properties = da9150_charger_props,495 .num_properties = ARRAY_SIZE(da9150_charger_props),496 .get_property = da9150_charger_get_prop,497};498 499static const struct power_supply_desc battery_desc = {500 .name = "da9150-battery",501 .type = POWER_SUPPLY_TYPE_BATTERY,502 .properties = da9150_charger_bat_props,503 .num_properties = ARRAY_SIZE(da9150_charger_bat_props),504 .get_property = da9150_charger_battery_get_prop,505};506 507static int da9150_charger_probe(struct platform_device *pdev)508{509 struct device *dev = &pdev->dev;510 struct da9150 *da9150 = dev_get_drvdata(dev->parent);511 struct da9150_charger *charger;512 u8 reg;513 int ret;514 515 charger = devm_kzalloc(dev, sizeof(struct da9150_charger), GFP_KERNEL);516 if (!charger)517 return -ENOMEM;518 519 platform_set_drvdata(pdev, charger);520 charger->da9150 = da9150;521 charger->dev = dev;522 523 /* Acquire ADC channels */524 charger->ibus_chan = devm_iio_channel_get(dev, "CHAN_IBUS");525 if (IS_ERR(charger->ibus_chan))526 return PTR_ERR(charger->ibus_chan);527 528 charger->vbus_chan = devm_iio_channel_get(dev, "CHAN_VBUS");529 if (IS_ERR(charger->vbus_chan))530 return PTR_ERR(charger->vbus_chan);531 532 charger->tjunc_chan = devm_iio_channel_get(dev, "CHAN_TJUNC");533 if (IS_ERR(charger->tjunc_chan))534 return PTR_ERR(charger->tjunc_chan);535 536 charger->vbat_chan = devm_iio_channel_get(dev, "CHAN_VBAT");537 if (IS_ERR(charger->vbat_chan))538 return PTR_ERR(charger->vbat_chan);539 540 /* Register power supplies */541 charger->usb = devm_power_supply_register(dev, &usb_desc, NULL);542 if (IS_ERR(charger->usb))543 return PTR_ERR(charger->usb);544 545 charger->battery = devm_power_supply_register(dev, &battery_desc, NULL);546 if (IS_ERR(charger->battery))547 return PTR_ERR(charger->battery);548 549 /* Get initial online supply */550 reg = da9150_reg_read(da9150, DA9150_STATUS_H);551 552 switch (reg & DA9150_VBUS_STAT_MASK) {553 case DA9150_VBUS_STAT_OFF:554 case DA9150_VBUS_STAT_WAIT:555 charger->supply_online = charger->battery;556 break;557 case DA9150_VBUS_STAT_CHG:558 charger->supply_online = charger->usb;559 break;560 default:561 dev_warn(dev, "Unknown VBUS state - reg = 0x%x\n", reg);562 charger->supply_online = NULL;563 break;564 }565 566 /* Setup OTG reporting & configuration */567 charger->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);568 if (!IS_ERR_OR_NULL(charger->usb_phy)) {569 INIT_WORK(&charger->otg_work, da9150_charger_otg_work);570 charger->otg_nb.notifier_call = da9150_charger_otg_ncb;571 usb_register_notifier(charger->usb_phy, &charger->otg_nb);572 }573 574 /* Register IRQs */575 ret = da9150_charger_register_irq(pdev, da9150_charger_chg_irq,576 "CHG_STATUS");577 if (ret < 0)578 goto chg_irq_fail;579 580 ret = da9150_charger_register_irq(pdev, da9150_charger_tjunc_irq,581 "CHG_TJUNC");582 if (ret < 0)583 goto tjunc_irq_fail;584 585 ret = da9150_charger_register_irq(pdev, da9150_charger_vfault_irq,586 "CHG_VFAULT");587 if (ret < 0)588 goto vfault_irq_fail;589 590 ret = da9150_charger_register_irq(pdev, da9150_charger_vbus_irq,591 "CHG_VBUS");592 if (ret < 0)593 goto vbus_irq_fail;594 595 return 0;596 597 598vbus_irq_fail:599 da9150_charger_unregister_irq(pdev, "CHG_VFAULT");600vfault_irq_fail:601 da9150_charger_unregister_irq(pdev, "CHG_TJUNC");602tjunc_irq_fail:603 da9150_charger_unregister_irq(pdev, "CHG_STATUS");604chg_irq_fail:605 if (!IS_ERR_OR_NULL(charger->usb_phy))606 usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);607 608 return ret;609}610 611static void da9150_charger_remove(struct platform_device *pdev)612{613 struct da9150_charger *charger = platform_get_drvdata(pdev);614 int irq;615 616 /* Make sure IRQs are released before unregistering power supplies */617 irq = platform_get_irq_byname(pdev, "CHG_VBUS");618 free_irq(irq, charger);619 620 irq = platform_get_irq_byname(pdev, "CHG_VFAULT");621 free_irq(irq, charger);622 623 irq = platform_get_irq_byname(pdev, "CHG_TJUNC");624 free_irq(irq, charger);625 626 irq = platform_get_irq_byname(pdev, "CHG_STATUS");627 free_irq(irq, charger);628 629 if (!IS_ERR_OR_NULL(charger->usb_phy))630 usb_unregister_notifier(charger->usb_phy, &charger->otg_nb);631 cancel_work_sync(&charger->otg_work);632}633 634static struct platform_driver da9150_charger_driver = {635 .driver = {636 .name = "da9150-charger",637 },638 .probe = da9150_charger_probe,639 .remove_new = da9150_charger_remove,640};641 642module_platform_driver(da9150_charger_driver);643 644MODULE_DESCRIPTION("Charger Driver for DA9150");645MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");646MODULE_LICENSE("GPL");647