power: supply: introduce pmi8998 fuel guage driver
This commit is contained in:
parent
aadef46ad8
commit
f74131008a
4 changed files with 675 additions and 0 deletions
|
|
@ -934,4 +934,14 @@ config CHARGER_QCOM_SMB2
|
|||
Say Y or M here to enable reporting the charger status and rate on
|
||||
supported platforms such as Snapdragon 845 and 835 based phones.
|
||||
|
||||
config BATTERY_PMI8998_FG
|
||||
tristate "Qualcomm PMI8998 fuel gauge driver"
|
||||
depends on MFD_SPMI_PMIC
|
||||
help
|
||||
Say Y here to enable the Qualcomm PMI8998 Fuel Gauge driver. This
|
||||
adds support for battery fuel gauging and state of charge of
|
||||
battery connected tothe fuel gauge. The state of charge is
|
||||
reported through a BMS power supply property and also sends
|
||||
uevents when the capacity is updated.
|
||||
|
||||
endif # POWER_SUPPLY
|
||||
|
|
|
|||
|
|
@ -111,3 +111,4 @@ obj-$(CONFIG_BATTERY_ACER_A500) += acer_a500_battery.o
|
|||
obj-$(CONFIG_BATTERY_SURFACE) += surface_battery.o
|
||||
obj-$(CONFIG_CHARGER_SURFACE) += surface_charger.o
|
||||
obj-$(CONFIG_BATTERY_UG3105) += ug3105_battery.o
|
||||
obj-$(CONFIG_BATTERY_PMI8998_FG) += pmi8998_fg.o
|
||||
|
|
|
|||
526
drivers/power/supply/pmi8998_fg.c
Normal file
526
drivers/power/supply/pmi8998_fg.c
Normal file
|
|
@ -0,0 +1,526 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/math64.h>
|
||||
|
||||
#include "pmi8998_fg.h"
|
||||
|
||||
/************************
|
||||
* IO FUNCTIONS
|
||||
* **********************/
|
||||
|
||||
/**
|
||||
* pmi8998_read() - Read multiple registers with regmap_bulk_read
|
||||
*
|
||||
* @param map The regmap to read
|
||||
* @param val Pointer to read values into
|
||||
* @param addr Address to read from
|
||||
* @param len Number of registers (bytes) to read
|
||||
* @return int 0 on success, negative errno on error
|
||||
*/
|
||||
static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len)
|
||||
{
|
||||
if ((addr & 0xff00) == 0) {
|
||||
pr_err("base cannot be zero base=0x%02x\n", addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
pr_info("%s: reading 0x%x bytes from 0x%x", __func__, len, addr);
|
||||
|
||||
return regmap_bulk_read(map, addr, val, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pmi8998_write() - Write multiple registers with regmap_bulk_write
|
||||
*
|
||||
* @param map The regmap to write
|
||||
* @param val Pointer to write values into
|
||||
* @param addr Address to write from
|
||||
* @param len Number of registers (bytes) to write
|
||||
* @return int 0 on success, negative errno on error
|
||||
*/
|
||||
static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len)
|
||||
{
|
||||
int rc;
|
||||
bool sec_access = (addr & 0xff) > 0xd0;
|
||||
u8 sec_addr_val = 0xa5;
|
||||
|
||||
if (sec_access) {
|
||||
rc = regmap_bulk_write(map,
|
||||
(addr & 0xff00) | 0xd0,
|
||||
&sec_addr_val, 1);
|
||||
}
|
||||
|
||||
if ((addr & 0xff00) == 0) {
|
||||
pr_err("addr cannot be zero base=0x%02x\n", addr);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return regmap_bulk_write(map, addr, val, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief pmi8998_masked_write() - like pmi8998_write but applies
|
||||
* a mask first.
|
||||
*
|
||||
* @param map The regmap to write
|
||||
* @param val Pointer to write values into
|
||||
* @param addr Address to write from
|
||||
* @param len Number of registers (bytes) to write
|
||||
* @return int 0 on success, negative errno on error
|
||||
*/
|
||||
static int pmi8998_masked_write(struct regmap *map, u16 addr,
|
||||
u8 mask, u8 val)
|
||||
{
|
||||
int error;
|
||||
u8 reg;
|
||||
error = pmi8998_read(map, ®, addr, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
reg &= ~mask;
|
||||
reg |= val & mask;
|
||||
|
||||
error = pmi8998_write(map, ®, addr, 1);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int64_t twos_compliment_extend(int64_t val, int nbytes)
|
||||
{
|
||||
int i;
|
||||
int64_t mask;
|
||||
|
||||
mask = 0x80LL << ((nbytes - 1) * 8);
|
||||
if (val & mask) {
|
||||
for (i = 8; i > nbytes; i--) {
|
||||
mask = 0xFFLL << ((i - 1) * 8);
|
||||
val |= mask;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/*************************
|
||||
* Battery Status RW
|
||||
* ***********************/
|
||||
|
||||
static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val)
|
||||
{
|
||||
u8 cap[2];
|
||||
int error = pmi8998_read(chip->regmap, cap, REG_BASE(chip) + BATT_MONOTONIC_SOC, 2);
|
||||
if (error)
|
||||
return error;
|
||||
if (cap[0] != cap[1]) {
|
||||
cap[0] = cap[0] < cap[1] ? cap[0] : cap[1];
|
||||
}
|
||||
*val = DIV_ROUND_CLOSEST((cap[0] - 1) * 98, 0xff - 2) + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool pmi8998_battery_missing(struct pmi8998_fg_chip *chip)
|
||||
{
|
||||
int rc;
|
||||
u8 fg_batt_sts;
|
||||
|
||||
rc = pmi8998_read(chip->regmap, &fg_batt_sts,
|
||||
REG_BATT(chip) + INT_RT_STS, 1);
|
||||
if (rc) {
|
||||
pr_warn("read read failed: addr=%03X, rc=%d\n",
|
||||
REG_BATT(chip) + INT_RT_STS, rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Bit 6 is set if the battery is missing
|
||||
return (fg_batt_sts & BIT(6)) ? true : false;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val)
|
||||
{
|
||||
int rc, temp;
|
||||
u8 readval[2];
|
||||
|
||||
rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_TEMP, 2);
|
||||
if (rc) {
|
||||
pr_err("Failed to read temperature\n");
|
||||
return rc;
|
||||
}
|
||||
temp = ((readval[1] & BATT_TEMP_MSB_MASK) << 8) |
|
||||
(readval[0] & BATT_TEMP_LSB_MASK);
|
||||
temp = DIV_ROUND_CLOSEST(temp * 10, 4);
|
||||
|
||||
*val = temp -2730;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val)
|
||||
{
|
||||
int rc, temp;
|
||||
u8 readval[2];
|
||||
|
||||
rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_CURRENT, 2);
|
||||
if (rc) {
|
||||
pr_err("Failed to read current\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
temp = readval[1] << 8 | readval[0];
|
||||
temp = twos_compliment_extend(temp, 2);
|
||||
*val = div_s64((s64)temp * 488281,
|
||||
1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val)
|
||||
{
|
||||
int rc, temp;
|
||||
u8 readval[2];
|
||||
|
||||
rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + PARAM_ADDR_BATT_VOLTAGE, 2);
|
||||
if (rc) {
|
||||
pr_err("Failed to read voltage\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
temp = readval[1] << 8 | readval[0];
|
||||
temp = twos_compliment_extend(temp, 2);
|
||||
*val = div_s64((s64)temp * 122070,
|
||||
1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_get_max_charge_design(struct pmi8998_fg_chip *chip, int *val)
|
||||
{
|
||||
int rc, temp;
|
||||
u8 readval[2];
|
||||
|
||||
rc = pmi8998_read(chip->regmap, readval, REG_BATT(chip) + BATT_INFO_CHARGE_MAX_DESIGN, 2);
|
||||
if (rc) {
|
||||
pr_err("Failed to read voltage\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
temp = readval[1] << 8 | readval[0];
|
||||
temp = twos_compliment_extend(temp, 2);
|
||||
*val = div_s64((s64)temp * 122070,
|
||||
1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************
|
||||
* Init stuff
|
||||
* ******************/
|
||||
|
||||
static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip)
|
||||
{
|
||||
int rc = 0;
|
||||
u8 temp;
|
||||
|
||||
/* clear the error */
|
||||
rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG,
|
||||
BIT(2), BIT(2));
|
||||
if (rc) {
|
||||
pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
temp = 0x4;
|
||||
rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_ADDR_LSB + 1, 1);
|
||||
if (rc) {
|
||||
pr_err("Error writing to MEM_INTF_ADDR_MSB, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
temp = 0x0;
|
||||
rc = pmi8998_write(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_WR_DATA0 + 3, 1);
|
||||
if (rc) {
|
||||
pr_err("Error writing to WR_DATA3, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pmi8998_read(chip->regmap, &temp, REG_MEM(chip) + MEM_INTF_RD_DATA0 + 3, 1);
|
||||
if (rc) {
|
||||
pr_err("Error writing to RD_DATA3, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + MEM_INTF_IMA_CFG,
|
||||
BIT(2), 0);
|
||||
if (rc) {
|
||||
pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip,
|
||||
bool check_hw_sts)
|
||||
{
|
||||
int rc = 0, ret = 0;
|
||||
u8 err_sts = 0, exp_sts = 0, hw_sts = 0;
|
||||
bool run_err_clr_seq = false;
|
||||
|
||||
rc = pmi8998_read(chip->regmap, &err_sts,
|
||||
REG_MEM(chip) + MEM_INTF_IMA_ERR_STS, 1);
|
||||
if (rc) {
|
||||
dev_err(chip->dev, "failed to read IMA_ERR_STS, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pmi8998_read(chip->regmap, &exp_sts,
|
||||
REG_MEM(chip) + MEM_INTF_IMA_EXP_STS, 1);
|
||||
if (rc) {
|
||||
dev_err(chip->dev, "Error in reading IMA_EXP_STS, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (check_hw_sts) {
|
||||
rc = pmi8998_read(chip->regmap, &hw_sts,
|
||||
REG_MEM(chip) + MEM_INTF_IMA_HW_STS, 1);
|
||||
if (rc) {
|
||||
dev_err(chip->dev, "Error in reading IMA_HW_STS, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
/*
|
||||
* Lower nibble should be equal to upper nibble before SRAM
|
||||
* transactions begins from SW side.
|
||||
*/
|
||||
if ((hw_sts & 0x0f) != hw_sts >> 4) {
|
||||
dev_err(chip->dev, "IMA HW not in correct state, hw_sts=%x\n",
|
||||
hw_sts);
|
||||
run_err_clr_seq = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exp_sts & (BIT(0) | BIT(1) | BIT(3) |
|
||||
BIT(4) | BIT(5) | BIT(6) |
|
||||
BIT(7))) {
|
||||
dev_warn(chip->dev, "IMA exception bit set, exp_sts=%x\n", exp_sts);
|
||||
run_err_clr_seq = true;
|
||||
}
|
||||
|
||||
if (run_err_clr_seq) {
|
||||
ret = pmi8998_iacs_clear_sequence(chip);
|
||||
if (!ret)
|
||||
return -EAGAIN;
|
||||
else
|
||||
dev_err(chip->dev, "Error clearing IMA exception ret=%d\n", ret);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int fg_get_property(struct power_supply *psy,
|
||||
enum power_supply_property psp,
|
||||
union power_supply_propval *val)
|
||||
{
|
||||
struct pmi8998_fg_chip *chip = power_supply_get_drvdata(psy);
|
||||
int error = 0;
|
||||
|
||||
dev_info(chip->dev, "Getting property: %d", psp);
|
||||
|
||||
switch (psp) {
|
||||
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||
val->strval = "QCOM";
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||
val->strval = "PMI8998 Battery";
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CAPACITY:
|
||||
error = pmi8998_fg_get_capacity(chip, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||
error = pmi8998_fg_get_current(chip, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||
error = pmi8998_fg_get_voltage(chip, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
|
||||
val->intval = chip->batt_info.batt_max_voltage_uv;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_TEMP:
|
||||
error = pmi8998_fg_get_temperature(chip, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||
val->intval = 3370000;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
|
||||
case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement learning */
|
||||
val->intval = chip->batt_info.nom_cap_uah;
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_STATUS:
|
||||
val->intval = POWER_SUPPLY_STATUS_CHARGING;
|
||||
//error = smb2_chg_get_status(chip, &val->intval);
|
||||
break;
|
||||
case POWER_SUPPLY_PROP_HEALTH:
|
||||
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||
break;
|
||||
default:
|
||||
pr_err("invalid property: %d\n", psp);
|
||||
return -EINVAL;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
static const struct power_supply_desc bms_psy_desc = {
|
||||
.name = "pmi8998-bms",
|
||||
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||
.properties = fg_properties,
|
||||
.num_properties = ARRAY_SIZE(fg_properties),
|
||||
.get_property = fg_get_property,
|
||||
};
|
||||
|
||||
static int pmi8998_fg_of_battery_init(struct pmi8998_fg_chip *chip){
|
||||
struct device_node *batt_node;
|
||||
struct device_node *node = chip->dev->of_node;
|
||||
int rc = 0;
|
||||
|
||||
batt_node = of_find_node_by_name(node, "qcom,battery-data");
|
||||
if (!batt_node) {
|
||||
pr_err("No available batterydata\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
of_property_read_u32(batt_node, "qcom,max-voltage-uv",
|
||||
&chip->batt_info.batt_max_voltage_uv_design);
|
||||
|
||||
// Can be read from SRAM, hardcode in DTS for now as reading SRAM is HARD!
|
||||
of_property_read_u32(batt_node, "qcom,design-capacity",
|
||||
&chip->batt_info.nom_cap_uah);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct power_supply_config supply_config = {};
|
||||
struct pmi8998_fg_chip *chip;
|
||||
const __be32 *prop_addr;
|
||||
int rc = 0;
|
||||
u8 dma_status;
|
||||
bool error_present;
|
||||
|
||||
chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
|
||||
if (!chip) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
chip->dev = &pdev->dev;
|
||||
mutex_init(&chip->lock);
|
||||
|
||||
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!chip->regmap) {
|
||||
dev_err(chip->dev, "failed to locate the regmap\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
// Get base address
|
||||
prop_addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
|
||||
if (!prop_addr) {
|
||||
dev_err(chip->dev, "Couldn't read SOC base address from dt\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
chip->base = be32_to_cpu(*prop_addr);
|
||||
|
||||
// Init memif fn inlined here (chip hardware info)
|
||||
rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM(chip) + DIG_MINOR, 4);
|
||||
if (rc) {
|
||||
dev_err(chip->dev, "Unable to read FG revision rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n",
|
||||
chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
|
||||
chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
|
||||
|
||||
/*
|
||||
* Change the FG_MEM_INT interrupt to track IACS_READY
|
||||
* condition instead of end-of-transaction. This makes sure
|
||||
* that the next transaction starts only after the hw is ready.
|
||||
* IACS_INTR_SRC_SLCT is BIT(3)
|
||||
*/
|
||||
rc = pmi8998_masked_write(chip->regmap,
|
||||
REG_MEM(chip) + MEM_INTF_IMA_CFG, BIT(3), BIT(3));
|
||||
if (rc) {
|
||||
dev_err(chip->dev,
|
||||
"failed to configure interrupt source %d\n",
|
||||
rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pmi8998_clear_ima(chip, true);
|
||||
if (rc && rc != -EAGAIN) {
|
||||
dev_err(chip->dev, "Error clearing IMA, exception rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Check and clear DMA errors
|
||||
rc = pmi8998_read(chip->regmap, &dma_status, REG_MEM(chip) + 0x70, 1);
|
||||
if (rc < 0) {
|
||||
pr_err("failed to read dma_status, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
error_present = dma_status & (BIT(1) | BIT(2));
|
||||
rc = pmi8998_masked_write(chip->regmap, REG_MEM(chip) + 0x71, BIT(0),
|
||||
error_present ? BIT(0) : 0);
|
||||
if (rc < 0) {
|
||||
pr_err("failed to write dma_ctl, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_dbg(chip->dev, "probed revision DIG:%d.%d ANA:%d.%d\n",
|
||||
chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
|
||||
chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
|
||||
|
||||
supply_config.drv_data = chip;
|
||||
supply_config.of_node = pdev->dev.of_node;
|
||||
|
||||
chip->bms_psy = devm_power_supply_register(chip->dev,
|
||||
&bms_psy_desc, &supply_config);
|
||||
if (IS_ERR(chip->bms_psy)) {
|
||||
dev_err(&pdev->dev, "failed to register battery\n");
|
||||
return PTR_ERR(chip->bms_psy);
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pmi8998_fg_remove(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id fg_match_id_table[] = {
|
||||
{ .compatible = "qcom,pmi8998-fg" },
|
||||
{ /* sentinal */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, fg_match_id_table);
|
||||
|
||||
static struct platform_driver qcom_fg_driver = {
|
||||
.probe = pmi8998_fg_probe,
|
||||
.remove = pmi8998_fg_remove,
|
||||
.driver = {
|
||||
.name = "pmi8998-fg",
|
||||
.of_match_table = fg_match_id_table,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(qcom_fg_driver);
|
||||
|
||||
MODULE_AUTHOR("Caleb Connolly <caleb@connolly.tech>");
|
||||
MODULE_AUTHOR("Joel Selvaraj <jo@jsfamily.in>");
|
||||
MODULE_DESCRIPTION("Qualcomm PMI8998 Fuel Guage Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
138
drivers/power/supply/pmi8998_fg.h
Normal file
138
drivers/power/supply/pmi8998_fg.h
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
|
||||
// #define FG_PARAM_MAX 49
|
||||
|
||||
/**** Registers *****/
|
||||
|
||||
// pmi8998 v2 specific
|
||||
#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a
|
||||
#define MEM_INTF_CFG 0x50
|
||||
#define MEM_INTF_ADDR_LSB 0x61
|
||||
#define MEM_INTF_RD_DATA0 0x67
|
||||
#define MEM_INTF_WR_DATA0 0x63
|
||||
|
||||
#define SMB2_CABLE_CONNECTED 0x06
|
||||
|
||||
// pm8950 / pm89988 common
|
||||
#define MEM_INTF_IMA_CFG 0x52
|
||||
#define MEM_INTF_IMA_OPR_STS 0x54
|
||||
#define MEM_INTF_IMA_EXP_STS 0x55
|
||||
#define MEM_INTF_IMA_HW_STS 0x56
|
||||
#define MEM_INTF_BEAT_COUNT 0x57
|
||||
#define MEM_INTF_IMA_ERR_STS 0x5f
|
||||
#define MEM_INTF_IMA_BYTE_EN 0x60
|
||||
|
||||
#define BATT_INFO_THERM_C1 0x5c
|
||||
#define BATT_INFO_VBATT_LSB 0xa0
|
||||
#define BATT_INFO_VBATT_MSB 0xa1
|
||||
#define BATT_INFO_IBATT_LSB 0xa2
|
||||
#define BATT_INFO_IBATT_MSB 0xa3
|
||||
#define BATT_INFO_BATT_TEMP_LSB 0x50
|
||||
#define BATT_INFO_BATT_TEMP_MSB 0x51
|
||||
#define BATT_MONOTONIC_SOC 0x09
|
||||
|
||||
#define BATT_TEMP_LSB_MASK GENMASK(7, 0)
|
||||
#define BATT_TEMP_MSB_MASK GENMASK(2, 0)
|
||||
|
||||
#define REG_BASE(chip) (chip->base)
|
||||
#define REG_BATT(chip) (chip->base + 0x100)
|
||||
#define REG_MEM(chip) (chip->base + 0x400)
|
||||
|
||||
/* Interrupt offsets */
|
||||
#define INT_RT_STS 0x10
|
||||
#define INT_EN_CLR 0x16
|
||||
|
||||
// Param addresses
|
||||
#define PARAM_ADDR_BATT_TEMP 0x50
|
||||
#define PARAM_ADDR_BATT_VOLTAGE 0xa0
|
||||
#define PARAM_ADDR_BATT_CURRENT 0xa2
|
||||
|
||||
enum wa_flags {
|
||||
PMI8998_V1_REV_WA,
|
||||
PMI8998_V2_REV_WA,
|
||||
};
|
||||
|
||||
enum pmi8998_rev_offsets {
|
||||
DIG_MINOR = 0x0,
|
||||
DIG_MAJOR = 0x1,
|
||||
ANA_MINOR = 0x2,
|
||||
ANA_MAJOR = 0x3,
|
||||
};
|
||||
enum pmi8998_rev {
|
||||
DIG_REV_1 = 0x1,
|
||||
DIG_REV_2 = 0x2,
|
||||
DIG_REV_3 = 0x3,
|
||||
};
|
||||
|
||||
enum charger_status{
|
||||
TRICKLE_CHARGE = 0,
|
||||
PRE_CHARGE,
|
||||
FAST_CHARGE,
|
||||
FULLON_CHARGE,
|
||||
TAPER_CHARGE,
|
||||
TERMINATE_CHARGE,
|
||||
INHIBIT_CHARGE,
|
||||
DISABLE_CHARGE,
|
||||
};
|
||||
|
||||
static enum power_supply_property fg_properties[] = {
|
||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
POWER_SUPPLY_PROP_CAPACITY,
|
||||
POWER_SUPPLY_PROP_CURRENT_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
|
||||
POWER_SUPPLY_PROP_VOLTAGE_MIN,
|
||||
POWER_SUPPLY_PROP_TEMP,
|
||||
// POWER_SUPPLY_PROP_CHARGE_NOW,
|
||||
// POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||
POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
|
||||
POWER_SUPPLY_PROP_HEALTH,
|
||||
POWER_SUPPLY_PROP_STATUS,
|
||||
};
|
||||
|
||||
struct fg_learning_data {
|
||||
struct mutex learning_lock;
|
||||
bool active;
|
||||
int64_t cc_uah;
|
||||
int learned_cc_uah;
|
||||
int init_cc_pc_val;
|
||||
int max_start_soc;
|
||||
int max_increment;
|
||||
int max_decrement;
|
||||
int vbat_est_thr_uv;
|
||||
int max_cap_limit;
|
||||
int min_cap_limit;
|
||||
int min_temp;
|
||||
int max_temp;
|
||||
};
|
||||
|
||||
struct battery_info {
|
||||
const char *manufacturer;
|
||||
const char *model;
|
||||
const char *serial_num;
|
||||
|
||||
int nom_cap_uah;
|
||||
|
||||
int batt_max_voltage_uv_design;
|
||||
int batt_max_voltage_uv;
|
||||
};
|
||||
|
||||
struct pmi8998_fg_chip {
|
||||
struct device *dev;
|
||||
unsigned int base;
|
||||
struct regmap *regmap;
|
||||
struct mutex lock;
|
||||
|
||||
struct power_supply *bms_psy;
|
||||
|
||||
u8 revision[4];
|
||||
bool ima_supported;
|
||||
|
||||
struct battery_info batt_info;
|
||||
|
||||
struct fg_learning_data learning_data;
|
||||
|
||||
int health;
|
||||
int status;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue