562 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * DA9150 Fuel-Gauge Driver4 *5 * Copyright (c) 2015 Dialog Semiconductor6 *7 * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.com>8 */9 10#include <linux/kernel.h>11#include <linux/module.h>12#include <linux/platform_device.h>13#include <linux/of.h>14#include <linux/slab.h>15#include <linux/interrupt.h>16#include <linux/delay.h>17#include <linux/power_supply.h>18#include <linux/list.h>19#include <asm/div64.h>20#include <linux/mfd/da9150/core.h>21#include <linux/mfd/da9150/registers.h>22#include <linux/devm-helpers.h>23 24/* Core2Wire */25#define DA9150_QIF_READ (0x0 << 7)26#define DA9150_QIF_WRITE (0x1 << 7)27#define DA9150_QIF_CODE_MASK 0x7F28 29#define DA9150_QIF_BYTE_SIZE 830#define DA9150_QIF_BYTE_MASK 0xFF31#define DA9150_QIF_SHORT_SIZE 232#define DA9150_QIF_LONG_SIZE 433 34/* QIF Codes */35#define DA9150_QIF_UAVG 636#define DA9150_QIF_UAVG_SIZE DA9150_QIF_LONG_SIZE37#define DA9150_QIF_IAVG 838#define DA9150_QIF_IAVG_SIZE DA9150_QIF_LONG_SIZE39#define DA9150_QIF_NTCAVG 1240#define DA9150_QIF_NTCAVG_SIZE DA9150_QIF_LONG_SIZE41#define DA9150_QIF_SHUNT_VAL 3642#define DA9150_QIF_SHUNT_VAL_SIZE DA9150_QIF_SHORT_SIZE43#define DA9150_QIF_SD_GAIN 3844#define DA9150_QIF_SD_GAIN_SIZE DA9150_QIF_LONG_SIZE45#define DA9150_QIF_FCC_MAH 4046#define DA9150_QIF_FCC_MAH_SIZE DA9150_QIF_SHORT_SIZE47#define DA9150_QIF_SOC_PCT 4348#define DA9150_QIF_SOC_PCT_SIZE DA9150_QIF_SHORT_SIZE49#define DA9150_QIF_CHARGE_LIMIT 4450#define DA9150_QIF_CHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE51#define DA9150_QIF_DISCHARGE_LIMIT 4552#define DA9150_QIF_DISCHARGE_LIMIT_SIZE DA9150_QIF_SHORT_SIZE53#define DA9150_QIF_FW_MAIN_VER 11854#define DA9150_QIF_FW_MAIN_VER_SIZE DA9150_QIF_SHORT_SIZE55#define DA9150_QIF_E_FG_STATUS 12656#define DA9150_QIF_E_FG_STATUS_SIZE DA9150_QIF_SHORT_SIZE57#define DA9150_QIF_SYNC 12758#define DA9150_QIF_SYNC_SIZE DA9150_QIF_SHORT_SIZE59#define DA9150_QIF_MAX_CODES 12860 61/* QIF Sync Timeout */62#define DA9150_QIF_SYNC_TIMEOUT 100063#define DA9150_QIF_SYNC_RETRIES 1064 65/* QIF E_FG_STATUS */66#define DA9150_FG_IRQ_LOW_SOC_MASK (1 << 0)67#define DA9150_FG_IRQ_HIGH_SOC_MASK (1 << 1)68#define DA9150_FG_IRQ_SOC_MASK \69 (DA9150_FG_IRQ_LOW_SOC_MASK | DA9150_FG_IRQ_HIGH_SOC_MASK)70 71/* Private data */72struct da9150_fg {73 struct da9150 *da9150;74 struct device *dev;75 76 struct mutex io_lock;77 78 struct power_supply *battery;79 struct delayed_work work;80 u32 interval;81 82 int warn_soc;83 int crit_soc;84 int soc;85};86 87/* Battery Properties */88static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)89 90{91 u8 buf[DA9150_QIF_LONG_SIZE];92 u8 read_addr;93 u32 res = 0;94 int i;95 96 /* Set QIF code (READ mode) */97 read_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_READ;98 99 da9150_read_qif(fg->da9150, read_addr, size, buf);100 for (i = 0; i < size; ++i)101 res |= (buf[i] << (i * DA9150_QIF_BYTE_SIZE));102 103 return res;104}105 106static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,107 u32 val)108 109{110 u8 buf[DA9150_QIF_LONG_SIZE];111 u8 write_addr;112 int i;113 114 /* Set QIF code (WRITE mode) */115 write_addr = (code & DA9150_QIF_CODE_MASK) | DA9150_QIF_WRITE;116 117 for (i = 0; i < size; ++i) {118 buf[i] = (val >> (i * DA9150_QIF_BYTE_SIZE)) &119 DA9150_QIF_BYTE_MASK;120 }121 da9150_write_qif(fg->da9150, write_addr, size, buf);122}123 124/* Trigger QIF Sync to update QIF readable data */125static void da9150_fg_read_sync_start(struct da9150_fg *fg)126{127 int i = 0;128 u32 res = 0;129 130 mutex_lock(&fg->io_lock);131 132 /* Check if QIF sync already requested, and write to sync if not */133 res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,134 DA9150_QIF_SYNC_SIZE);135 if (res > 0)136 da9150_fg_write_attr(fg, DA9150_QIF_SYNC,137 DA9150_QIF_SYNC_SIZE, 0);138 139 /* Wait for sync to complete */140 res = 0;141 while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {142 usleep_range(DA9150_QIF_SYNC_TIMEOUT,143 DA9150_QIF_SYNC_TIMEOUT * 2);144 res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,145 DA9150_QIF_SYNC_SIZE);146 }147 148 /* Check if sync completed */149 if (res == 0)150 dev_err(fg->dev, "Failed to perform QIF read sync!\n");151}152 153/*154 * Should always be called after QIF sync read has been performed, and all155 * attributes required have been accessed.156 */157static inline void da9150_fg_read_sync_end(struct da9150_fg *fg)158{159 mutex_unlock(&fg->io_lock);160}161 162/* Sync read of single QIF attribute */163static u32 da9150_fg_read_attr_sync(struct da9150_fg *fg, u8 code, u8 size)164{165 u32 val;166 167 da9150_fg_read_sync_start(fg);168 val = da9150_fg_read_attr(fg, code, size);169 da9150_fg_read_sync_end(fg);170 171 return val;172}173 174/* Wait for QIF Sync, write QIF data and wait for ack */175static void da9150_fg_write_attr_sync(struct da9150_fg *fg, u8 code, u8 size,176 u32 val)177{178 int i = 0;179 u32 res = 0, sync_val;180 181 mutex_lock(&fg->io_lock);182 183 /* Check if QIF sync already requested */184 res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,185 DA9150_QIF_SYNC_SIZE);186 187 /* Wait for an existing sync to complete */188 while ((res == 0) && (i++ < DA9150_QIF_SYNC_RETRIES)) {189 usleep_range(DA9150_QIF_SYNC_TIMEOUT,190 DA9150_QIF_SYNC_TIMEOUT * 2);191 res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,192 DA9150_QIF_SYNC_SIZE);193 }194 195 if (res == 0) {196 dev_err(fg->dev, "Timeout waiting for existing QIF sync!\n");197 mutex_unlock(&fg->io_lock);198 return;199 }200 201 /* Write value for QIF code */202 da9150_fg_write_attr(fg, code, size, val);203 204 /* Wait for write acknowledgment */205 i = 0;206 sync_val = res;207 while ((res == sync_val) && (i++ < DA9150_QIF_SYNC_RETRIES)) {208 usleep_range(DA9150_QIF_SYNC_TIMEOUT,209 DA9150_QIF_SYNC_TIMEOUT * 2);210 res = da9150_fg_read_attr(fg, DA9150_QIF_SYNC,211 DA9150_QIF_SYNC_SIZE);212 }213 214 mutex_unlock(&fg->io_lock);215 216 /* Check write was actually successful */217 if (res != (sync_val + 1))218 dev_err(fg->dev, "Error performing QIF sync write for code %d\n",219 code);220}221 222/* Power Supply attributes */223static int da9150_fg_capacity(struct da9150_fg *fg,224 union power_supply_propval *val)225{226 val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,227 DA9150_QIF_SOC_PCT_SIZE);228 229 if (val->intval > 100)230 val->intval = 100;231 232 return 0;233}234 235static int da9150_fg_current_avg(struct da9150_fg *fg,236 union power_supply_propval *val)237{238 u32 iavg, sd_gain, shunt_val;239 u64 div, res;240 241 da9150_fg_read_sync_start(fg);242 iavg = da9150_fg_read_attr(fg, DA9150_QIF_IAVG,243 DA9150_QIF_IAVG_SIZE);244 shunt_val = da9150_fg_read_attr(fg, DA9150_QIF_SHUNT_VAL,245 DA9150_QIF_SHUNT_VAL_SIZE);246 sd_gain = da9150_fg_read_attr(fg, DA9150_QIF_SD_GAIN,247 DA9150_QIF_SD_GAIN_SIZE);248 da9150_fg_read_sync_end(fg);249 250 div = (u64) (sd_gain * shunt_val * 65536ULL);251 do_div(div, 1000000);252 res = (u64) (iavg * 1000000ULL);253 do_div(res, div);254 255 val->intval = (int) res;256 257 return 0;258}259 260static int da9150_fg_voltage_avg(struct da9150_fg *fg,261 union power_supply_propval *val)262{263 u64 res;264 265 val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_UAVG,266 DA9150_QIF_UAVG_SIZE);267 268 res = (u64) (val->intval * 186ULL);269 do_div(res, 10000);270 val->intval = (int) res;271 272 return 0;273}274 275static int da9150_fg_charge_full(struct da9150_fg *fg,276 union power_supply_propval *val)277{278 val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_FCC_MAH,279 DA9150_QIF_FCC_MAH_SIZE);280 281 val->intval = val->intval * 1000;282 283 return 0;284}285 286/*287 * Temperature reading from device is only valid if battery/system provides288 * valid NTC to associated pin of DA9150 chip.289 */290static int da9150_fg_temp(struct da9150_fg *fg,291 union power_supply_propval *val)292{293 val->intval = da9150_fg_read_attr_sync(fg, DA9150_QIF_NTCAVG,294 DA9150_QIF_NTCAVG_SIZE);295 296 val->intval = (val->intval * 10) / 1048576;297 298 return 0;299}300 301static enum power_supply_property da9150_fg_props[] = {302 POWER_SUPPLY_PROP_CAPACITY,303 POWER_SUPPLY_PROP_CURRENT_AVG,304 POWER_SUPPLY_PROP_VOLTAGE_AVG,305 POWER_SUPPLY_PROP_CHARGE_FULL,306 POWER_SUPPLY_PROP_TEMP,307};308 309static int da9150_fg_get_prop(struct power_supply *psy,310 enum power_supply_property psp,311 union power_supply_propval *val)312{313 struct da9150_fg *fg = dev_get_drvdata(psy->dev.parent);314 int ret;315 316 switch (psp) {317 case POWER_SUPPLY_PROP_CAPACITY:318 ret = da9150_fg_capacity(fg, val);319 break;320 case POWER_SUPPLY_PROP_CURRENT_AVG:321 ret = da9150_fg_current_avg(fg, val);322 break;323 case POWER_SUPPLY_PROP_VOLTAGE_AVG:324 ret = da9150_fg_voltage_avg(fg, val);325 break;326 case POWER_SUPPLY_PROP_CHARGE_FULL:327 ret = da9150_fg_charge_full(fg, val);328 break;329 case POWER_SUPPLY_PROP_TEMP:330 ret = da9150_fg_temp(fg, val);331 break;332 default:333 ret = -EINVAL;334 break;335 }336 337 return ret;338}339 340/* Repeated SOC check */341static bool da9150_fg_soc_changed(struct da9150_fg *fg)342{343 union power_supply_propval val;344 345 da9150_fg_capacity(fg, &val);346 if (val.intval != fg->soc) {347 fg->soc = val.intval;348 return true;349 }350 351 return false;352}353 354static void da9150_fg_work(struct work_struct *work)355{356 struct da9150_fg *fg = container_of(work, struct da9150_fg, work.work);357 358 /* Report if SOC has changed */359 if (da9150_fg_soc_changed(fg))360 power_supply_changed(fg->battery);361 362 schedule_delayed_work(&fg->work, msecs_to_jiffies(fg->interval));363}364 365/* SOC level event configuration */366static void da9150_fg_soc_event_config(struct da9150_fg *fg)367{368 int soc;369 370 soc = da9150_fg_read_attr_sync(fg, DA9150_QIF_SOC_PCT,371 DA9150_QIF_SOC_PCT_SIZE);372 373 if (soc > fg->warn_soc) {374 /* If SOC > warn level, set discharge warn level event */375 da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,376 DA9150_QIF_DISCHARGE_LIMIT_SIZE,377 fg->warn_soc + 1);378 } else if ((soc <= fg->warn_soc) && (soc > fg->crit_soc)) {379 /*380 * If SOC <= warn level, set discharge crit level event,381 * and set charge warn level event.382 */383 da9150_fg_write_attr_sync(fg, DA9150_QIF_DISCHARGE_LIMIT,384 DA9150_QIF_DISCHARGE_LIMIT_SIZE,385 fg->crit_soc + 1);386 387 da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,388 DA9150_QIF_CHARGE_LIMIT_SIZE,389 fg->warn_soc);390 } else if (soc <= fg->crit_soc) {391 /* If SOC <= crit level, set charge crit level event */392 da9150_fg_write_attr_sync(fg, DA9150_QIF_CHARGE_LIMIT,393 DA9150_QIF_CHARGE_LIMIT_SIZE,394 fg->crit_soc);395 }396}397 398static irqreturn_t da9150_fg_irq(int irq, void *data)399{400 struct da9150_fg *fg = data;401 u32 e_fg_status;402 403 /* Read FG IRQ status info */404 e_fg_status = da9150_fg_read_attr(fg, DA9150_QIF_E_FG_STATUS,405 DA9150_QIF_E_FG_STATUS_SIZE);406 407 /* Handle warning/critical threhold events */408 if (e_fg_status & DA9150_FG_IRQ_SOC_MASK)409 da9150_fg_soc_event_config(fg);410 411 /* Clear any FG IRQs */412 da9150_fg_write_attr(fg, DA9150_QIF_E_FG_STATUS,413 DA9150_QIF_E_FG_STATUS_SIZE, e_fg_status);414 415 return IRQ_HANDLED;416}417 418static struct da9150_fg_pdata *da9150_fg_dt_pdata(struct device *dev)419{420 struct device_node *fg_node = dev->of_node;421 struct da9150_fg_pdata *pdata;422 423 pdata = devm_kzalloc(dev, sizeof(struct da9150_fg_pdata), GFP_KERNEL);424 if (!pdata)425 return NULL;426 427 of_property_read_u32(fg_node, "dlg,update-interval",428 &pdata->update_interval);429 of_property_read_u8(fg_node, "dlg,warn-soc-level",430 &pdata->warn_soc_lvl);431 of_property_read_u8(fg_node, "dlg,crit-soc-level",432 &pdata->crit_soc_lvl);433 434 return pdata;435}436 437static const struct power_supply_desc fg_desc = {438 .name = "da9150-fg",439 .type = POWER_SUPPLY_TYPE_BATTERY,440 .properties = da9150_fg_props,441 .num_properties = ARRAY_SIZE(da9150_fg_props),442 .get_property = da9150_fg_get_prop,443};444 445static int da9150_fg_probe(struct platform_device *pdev)446{447 struct device *dev = &pdev->dev;448 struct da9150 *da9150 = dev_get_drvdata(dev->parent);449 struct da9150_fg_pdata *fg_pdata = dev_get_platdata(dev);450 struct da9150_fg *fg;451 int ver, irq, ret = 0;452 453 fg = devm_kzalloc(dev, sizeof(*fg), GFP_KERNEL);454 if (fg == NULL)455 return -ENOMEM;456 457 platform_set_drvdata(pdev, fg);458 fg->da9150 = da9150;459 fg->dev = dev;460 461 mutex_init(&fg->io_lock);462 463 /* Enable QIF */464 da9150_set_bits(da9150, DA9150_CORE2WIRE_CTRL_A, DA9150_FG_QIF_EN_MASK,465 DA9150_FG_QIF_EN_MASK);466 467 fg->battery = devm_power_supply_register(dev, &fg_desc, NULL);468 if (IS_ERR(fg->battery)) {469 ret = PTR_ERR(fg->battery);470 return ret;471 }472 473 ver = da9150_fg_read_attr(fg, DA9150_QIF_FW_MAIN_VER,474 DA9150_QIF_FW_MAIN_VER_SIZE);475 dev_info(dev, "Version: 0x%x\n", ver);476 477 /* Handle DT data if provided */478 if (dev->of_node) {479 fg_pdata = da9150_fg_dt_pdata(dev);480 dev->platform_data = fg_pdata;481 }482 483 /* Handle any pdata provided */484 if (fg_pdata) {485 fg->interval = fg_pdata->update_interval;486 487 if (fg_pdata->warn_soc_lvl > 100)488 dev_warn(dev, "Invalid SOC warning level provided, Ignoring");489 else490 fg->warn_soc = fg_pdata->warn_soc_lvl;491 492 if ((fg_pdata->crit_soc_lvl > 100) ||493 (fg_pdata->crit_soc_lvl >= fg_pdata->warn_soc_lvl))494 dev_warn(dev, "Invalid SOC critical level provided, Ignoring");495 else496 fg->crit_soc = fg_pdata->crit_soc_lvl;497 498 499 }500 501 /* Configure initial SOC level events */502 da9150_fg_soc_event_config(fg);503 504 /*505 * If an interval period has been provided then setup repeating506 * work for reporting data updates.507 */508 if (fg->interval) {509 ret = devm_delayed_work_autocancel(dev, &fg->work,510 da9150_fg_work);511 if (ret) {512 dev_err(dev, "Failed to init work\n");513 return ret;514 }515 516 schedule_delayed_work(&fg->work,517 msecs_to_jiffies(fg->interval));518 }519 520 /* Register IRQ */521 irq = platform_get_irq_byname(pdev, "FG");522 if (irq < 0)523 return irq;524 525 ret = devm_request_threaded_irq(dev, irq, NULL, da9150_fg_irq,526 IRQF_ONESHOT, "FG", fg);527 if (ret) {528 dev_err(dev, "Failed to request IRQ %d: %d\n", irq, ret);529 return ret;530 }531 532 return 0;533}534 535static int da9150_fg_resume(struct platform_device *pdev)536{537 struct da9150_fg *fg = platform_get_drvdata(pdev);538 539 /*540 * Trigger SOC check to happen now so as to indicate any value change541 * since last check before suspend.542 */543 if (fg->interval)544 flush_delayed_work(&fg->work);545 546 return 0;547}548 549static struct platform_driver da9150_fg_driver = {550 .driver = {551 .name = "da9150-fuel-gauge",552 },553 .probe = da9150_fg_probe,554 .resume = da9150_fg_resume,555};556 557module_platform_driver(da9150_fg_driver);558 559MODULE_DESCRIPTION("Fuel-Gauge Driver for DA9150");560MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");561MODULE_LICENSE("GPL");562