power: pmi8998_fg: Rename to qcom_fg and add support for PMI8994/6

This adds support for accessing SRAM. That includes requesting and releasing
access to SRAM by setting some registers and handling the mem-avail IRQ,
configuring access, and reading from/writing to it.

Pre-gen3 fuel gauges require reading from SRAM to get voltage, current
and temperature data. Getting capacity is identical to gen3.

Tested on Xiaomi Mi Note 2 with PMI8996.
This commit is contained in:
Yassine Oudjana 2021-03-05 21:37:13 +04:00 committed by Jami Kettunen
parent ea1d7a2cb6
commit f748dea23d
4 changed files with 599 additions and 100 deletions

View file

@ -934,13 +934,13 @@ 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"
config BATTERY_QCOM_FG
tristate "Qualcomm PMIC fuel gauge driver"
depends on MFD_SPMI_PMIC
help
Say Y here to enable the Qualcomm PMI8998 Fuel Gauge driver. This
Say Y here to enable the Qualcomm PMIC 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
battery connected to the fuel gauge. The state of charge is
reported through a BMS power supply property and also sends
uevents when the capacity is updated.

View file

@ -111,4 +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
obj-$(CONFIG_BATTERY_QCOM_FG) += qcom_fg.o

View file

@ -3,6 +3,7 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/device.h>
@ -14,14 +15,14 @@
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include "pmi8998_fg.h"
#include "qcom_fg.h"
/************************
* IO FUNCTIONS
* **********************/
/**
* pmi8998_read() - Read multiple registers with regmap_bulk_read
* qcom_fg_read() - Read multiple registers with regmap_bulk_read
*
* @param map The regmap to read
* @param val Pointer to read values into
@ -29,7 +30,7 @@
* @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)
static int qcom_fg_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);
@ -42,7 +43,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len)
}
/**
* @brief pmi8998_write() - Write multiple registers with regmap_bulk_write
* @brief qcom_fg_write() - Write multiple registers with regmap_bulk_write
*
* @param map The regmap to write
* @param val Pointer to write values into
@ -50,7 +51,7 @@ static int pmi8998_read(struct regmap *map, u8 *val, u16 addr, int len)
* @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)
static int qcom_fg_write(struct regmap *map, u8 *val, u16 addr, int len)
{
int rc;
bool sec_access = (addr & 0xff) > 0xd0;
@ -71,7 +72,7 @@ static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len)
}
/**
* @brief pmi8998_masked_write() - like pmi8998_write but applies
* @brief qcom_fg_masked_write() - like qcom_fg_write but applies
* a mask first.
*
* @param map The regmap to write
@ -80,19 +81,19 @@ static int pmi8998_write(struct regmap *map, u8 *val, u16 addr, int len)
* @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,
static int qcom_fg_masked_write(struct regmap *map, u16 addr,
u8 mask, u8 val)
{
int error;
u8 reg;
error = pmi8998_read(map, &reg, addr, 1);
error = qcom_fg_read(map, &reg, addr, 1);
if (error)
return error;
reg &= ~mask;
reg |= val & mask;
error = pmi8998_write(map, &reg, addr, 1);
error = qcom_fg_write(map, &reg, addr, 1);
return error;
}
@ -111,14 +112,243 @@ static int64_t twos_compliment_extend(int64_t val, int nbytes)
return val;
}
/************************
* SRAM FUNCTIONS
* **********************/
/**
* qcom_fg_sram_check_access() - Check if SRAM is accessible
*
* @param chip Pointer to chip
* @return bool true if accessible, false otherwise
*/
static bool qcom_fg_sram_check_access(struct qcom_fg_chip *chip)
{
int rc;
u8 mem_if_status;
rc = qcom_fg_read(chip->regmap, &mem_if_status,
REG_MEM + MEM_INTF_STS, 1);
if(!(mem_if_status & MEM_INTF_AVAIL))
return false;
rc = qcom_fg_read(chip->regmap, &mem_if_status,
REG_MEM + MEM_INTF_CFG, 1);
if(rc)
return false;
return !!(mem_if_status & RIF_MEM_ACCESS_REQ);
}
/**
* qcom_fg_sram_request_access() - Request access to SRAM and wait for it
*
* @param chip Pointer to chip
* @return int 0 on success, negative errno on error
*/
static int qcom_fg_sram_request_access(struct qcom_fg_chip *chip)
{
int rc;
if(!qcom_fg_sram_check_access(chip)) {
rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_CFG,
RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ);
if (rc) {
dev_err(chip->dev,
"Failed to set SRAM access request bit: %d\n", rc);
return rc;
}
}
/* Wait to get access to SRAM, and try again if interrupted */
do {
rc = wait_for_completion_interruptible_timeout(
&chip->sram_access_granted,
msecs_to_jiffies(MEM_IF_TIMEOUT_MS));
} while(rc == -ERESTARTSYS);
if(rc <= 0) {
rc = -ETIMEDOUT;
return rc;
}
return 0;
}
/**
* qcom_fg_sram_release_access() - Release access to SRAM
*
* @param chip Pointer to chip
* @return int 0 on success, negative errno on error
*/
static int qcom_fg_sram_release_access(struct qcom_fg_chip *chip)
{
int rc;
rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_CFG,
RIF_MEM_ACCESS_REQ, 0);
if (rc) {
dev_err(chip->dev,
"Failed to set SRAM access request bit: %d\n", rc);
return rc;
}
reinit_completion(&chip->sram_access_granted);
return rc;
}
/**
* qcom_fg_sram_config_access() - Configure access to SRAM
*
* @param chip Pointer to chip
* @param write 0 for read access, 1 for write access
* @param burst 1 to access mutliple addresses successively
* @return int 0 on success, negative errno on error
*/
static int qcom_fg_sram_config_access(struct qcom_fg_chip *chip,
bool write, bool burst)
{
int rc;
u8 intf_ctl = (write ? MEM_INTF_CTL_WR_EN : 0)
| (burst ? MEM_INTF_CTL_BURST : 0);
rc = qcom_fg_write(chip->regmap, &intf_ctl,
REG_MEM + MEM_INTF_CTL, 1);
if(rc)
dev_err(chip->dev,
"Failed to configure SRAM access: %d\n", rc);
return rc;
}
/**
* qcom_fg_sram_read() - Read data from SRAM
*
* @param chip Pointer to chip
* @param val Pointer to read values into
* @param addr Address to read from
* @param len Number of bytes to read
* @return int 0 on success, negative errno on error
*/
static int qcom_fg_sram_read(struct qcom_fg_chip *chip,
u8 *val, u16 addr, int len, int offset)
{
int rc;
u8 *rd_data = val;
rc = qcom_fg_sram_request_access(chip);
if(rc) {
dev_err(chip->dev, "Failed to request SRAM access: %d", rc);
return rc;
}
dev_dbg(chip->dev,
"Reading address 0x%x with offset %d of length %d from SRAM",
addr, len, offset);
rc = qcom_fg_sram_config_access(chip, 0, (len > 4));
if(rc) {
dev_err(chip->dev, "Failed to configure SRAM access: %d", rc);
return rc;
}
while(len > 0) {
/* Set SRAM address register */
rc = qcom_fg_write(chip->regmap, (u8 *) &addr,
REG_MEM + MEM_INTF_ADDR_LSB, 2);
if(rc) {
dev_err(chip->dev, "Failed to set SRAM address: %d", rc);
return rc;
}
rc = qcom_fg_read(chip->regmap, rd_data,
REG_MEM + MEM_INTF_RD_DATA0 + offset, len);
addr += 4;
if(rc)
return rc;
rd_data += 4 - offset;
len -= 4 - offset;
offset = 0;
}
rc = qcom_fg_sram_release_access(chip);
return rc;
}
/**
* qcom_fg_sram_write() - Write data to SRAM
*
* @param chip Pointer to chip
* @param val Pointer to write values into
* @param addr Address to write to
* @param len Number of bytes to write
* @return int 0 on success, negative errno on error
*/
static int qcom_fg_sram_write(struct qcom_fg_chip *chip,
u8 *val, u16 addr, int len, int offset)
{
int rc;
u8 *wr_data = val;
rc = qcom_fg_sram_request_access(chip);
if(rc) {
dev_err(chip->dev, "Failed to request SRAM access: %d", rc);
return rc;
}
dev_dbg(chip->dev,
"Reading address 0x%x with offset %d of length %d from SRAM",
addr, len, offset);
rc = qcom_fg_sram_config_access(chip, 1, (len > 4));
if(rc) {
dev_err(chip->dev, "Failed to configure SRAM access: %d", rc);
return rc;
}
while(len > 0) {
/* Set SRAM address register */
rc = qcom_fg_write(chip->regmap, (u8 *) &addr,
REG_MEM + MEM_INTF_ADDR_LSB, 2);
if(rc) {
dev_err(chip->dev, "Failed to set SRAM address: %d", rc);
return rc;
}
rc = qcom_fg_write(chip->regmap, wr_data,
REG_MEM + MEM_INTF_WR_DATA0 + offset, len);
addr += 4;
if(rc)
return rc;
wr_data += 4 - offset;
len -= 4 - offset;
offset = 0;
}
rc = qcom_fg_sram_release_access(chip);
return rc;
}
/*************************
* Battery Status RW
* ***********************/
static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val)
static int qcom_fg_get_capacity(struct qcom_fg_chip *chip, int *val)
{
u8 cap[2];
int error = pmi8998_read(chip->regmap, cap, REG_BASE + BATT_MONOTONIC_SOC, 2);
int error = qcom_fg_read(chip->regmap, cap, REG_BASE + BATT_MONOTONIC_SOC, 2);
if (error)
return error;
if (cap[0] != cap[1]) {
@ -128,12 +358,66 @@ static int pmi8998_fg_get_capacity(struct pmi8998_fg_chip *chip, int *val)
return 0;
}
static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val)
static int qcom_fg_get_temperature(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_TEMP, 2);
rc = qcom_fg_sram_read(chip, readval, 0x550, 2, 2);
if(rc) {
dev_err(chip->dev, "Failed to read temperature: %d", rc);
return rc;
}
temp = readval[1] << 8 | readval[0];
*val = temp * 625 / 1000 - 2730;
return 0;
}
static int qcom_fg_get_current(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = qcom_fg_sram_read(chip, readval, 0x5CC, 2, 3);
if(rc) {
dev_err(chip->dev, "Failed to read current: %d", rc);
return rc;
}
//handle rev 1 too
temp = readval[1] << 8 | readval[0];
temp = twos_compliment_extend(temp, 15);
*val = div_s64((s64)temp * 152587, 1000);
return 0;
}
static int qcom_fg_get_voltage(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = qcom_fg_sram_read(chip, readval, 0x5CC, 2, 1);
if(rc) {
dev_err(chip->dev, "Failed to read voltage: %d", rc);
return rc;
}
temp = readval[1] << 8 | readval[0];
*val = div_u64((u64)temp * 152587, 1000);
return 0;
}
/*************************
* Battery Status RW, Gen3
* ***********************/
static int qcom_fg_gen3_get_temperature(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_TEMP, 2);
if (rc) {
pr_err("Failed to read temperature\n");
return rc;
@ -146,12 +430,12 @@ static int pmi8998_fg_get_temperature(struct pmi8998_fg_chip *chip, int *val)
return 0;
}
static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val)
static int qcom_fg_gen3_get_current(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_CURRENT, 2);
rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_CURRENT, 2);
if (rc) {
pr_err("Failed to read current\n");
return rc;
@ -163,12 +447,12 @@ static int pmi8998_fg_get_current(struct pmi8998_fg_chip *chip, int *val)
return 0;
}
static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val)
static int qcom_fg_gen3_get_voltage(struct qcom_fg_chip *chip, int *val)
{
int rc, temp;
u8 readval[2];
rc = pmi8998_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_VOLTAGE, 2);
rc = qcom_fg_read(chip->regmap, readval, REG_BATT + PARAM_ADDR_BATT_VOLTAGE, 2);
if (rc) {
pr_err("Failed to read voltage\n");
return rc;
@ -183,13 +467,13 @@ static int pmi8998_fg_get_voltage(struct pmi8998_fg_chip *chip, int *val)
* Init stuff
* ******************/
static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip)
static int qcom_fg_iacs_clear_sequence(struct qcom_fg_chip *chip)
{
int rc = 0;
u8 temp;
/* clear the error */
rc = pmi8998_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG,
rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG,
BIT(2), BIT(2));
if (rc) {
pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
@ -197,26 +481,26 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip)
}
temp = 0x4;
rc = pmi8998_write(chip->regmap, &temp, REG_MEM + MEM_INTF_ADDR_LSB + 1, 1);
rc = qcom_fg_write(chip->regmap, &temp, REG_MEM + 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 + MEM_INTF_WR_DATA0 + 3, 1);
rc = qcom_fg_write(chip->regmap, &temp, REG_MEM + 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 + MEM_INTF_RD_DATA0 + 3, 1);
rc = qcom_fg_read(chip->regmap, &temp, REG_MEM + 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 + MEM_INTF_IMA_CFG,
rc = qcom_fg_masked_write(chip->regmap, REG_MEM + MEM_INTF_IMA_CFG,
BIT(2), 0);
if (rc) {
pr_err("Error writing to IMA_CFG, rc=%d\n", rc);
@ -225,21 +509,21 @@ static int pmi8998_iacs_clear_sequence(struct pmi8998_fg_chip *chip)
return rc;
}
static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip,
static int qcom_fg_clear_ima(struct qcom_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,
rc = qcom_fg_read(chip->regmap, &err_sts,
REG_MEM + 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,
rc = qcom_fg_read(chip->regmap, &exp_sts,
REG_MEM + MEM_INTF_IMA_EXP_STS, 1);
if (rc) {
dev_err(chip->dev, "Error in reading IMA_EXP_STS, rc=%d\n", rc);
@ -247,7 +531,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip,
}
if (check_hw_sts) {
rc = pmi8998_read(chip->regmap, &hw_sts,
rc = qcom_fg_read(chip->regmap, &hw_sts,
REG_MEM + MEM_INTF_IMA_HW_STS, 1);
if (rc) {
dev_err(chip->dev, "Error in reading IMA_HW_STS, rc=%d\n", rc);
@ -272,7 +556,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip,
}
if (run_err_clr_seq) {
ret = pmi8998_iacs_clear_sequence(chip);
ret = qcom_fg_iacs_clear_sequence(chip);
if (!ret)
return -EAGAIN;
else
@ -282,7 +566,7 @@ static int pmi8998_clear_ima(struct pmi8998_fg_chip *chip,
return rc;
}
int pmi8998_get_prop_usb_online(struct pmi8998_fg_chip *chip, int *val){
int qcom_fg_get_prop_usb_online(struct qcom_fg_chip *chip, int *val){
unsigned int stat;
int rc;
@ -297,13 +581,13 @@ int pmi8998_get_prop_usb_online(struct pmi8998_fg_chip *chip, int *val){
return rc;
}
int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){
int qcom_fg_get_prop_batt_status(struct qcom_fg_chip *chip, int *val){
int usb_online_val;
unsigned int stat;
int rc;
bool usb_online;
rc = pmi8998_get_prop_usb_online(chip, &usb_online_val);
rc = qcom_fg_get_prop_usb_online(chip, &usb_online_val);
if (rc < 0) {
dev_err(chip->dev, "Couldn't get usb online property rc=%d\n", rc);
return rc;
@ -346,7 +630,7 @@ int pmi8998_get_prop_batt_status(struct pmi8998_fg_chip *chip, int *val){
return rc;
}
int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){
int qcom_fg_get_prop_health_status(struct qcom_fg_chip *chip, int *val){
unsigned int stat;
int rc;
@ -370,7 +654,42 @@ int pmi8998_get_prop_health_status(struct pmi8998_fg_chip *chip, int *val){
return rc;
}
static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip,
static int qcom_fg_get_temp_threshold(struct qcom_fg_chip *chip,
enum power_supply_property psp, int *val)
{
int rc;
u8 temp;
int offset;
switch (psp) {
case POWER_SUPPLY_PROP_TEMP_MIN:
offset = 0;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
offset = 1;
break;
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
offset = 2;
break;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
offset = 3;
break;
default:
return -EINVAL;
}
rc = qcom_fg_sram_read(chip, &temp, 0x454, 1, offset);
if (rc < 0) {
dev_err(chip->dev, "Error in reading jeita level for psp:%d, rc=%d\n", psp, rc);
return rc;
}
*val = (temp - 30) * 10;
return 0;
}
static int qcom_fg_gen3_get_temp_threshold(struct qcom_fg_chip *chip,
enum power_supply_property psp, int *val)
{
int rc;
@ -394,7 +713,7 @@ static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip,
return -EINVAL;
}
rc = pmi8998_read(chip->regmap, &temp, reg, 1);
rc = qcom_fg_read(chip->regmap, &temp, reg, 1);
if (rc < 0) {
dev_err(chip->dev, "Error in reading jeita level for psp:%d, rc=%d\n", psp, rc);
return rc;
@ -405,17 +724,66 @@ static int pmi8998_get_temp_threshold(struct pmi8998_fg_chip *chip,
return 0;
}
static void fg_get_model_name(struct pmi8998_fg_chip *chip, union power_supply_propval *val)
static int qcom_fg_set_temp_threshold(struct qcom_fg_chip *chip,
enum power_supply_property psp, int val)
{
int rc;
u8 temp;
int offset;
switch (psp) {
case POWER_SUPPLY_PROP_TEMP_MIN:
offset = 0;
break;
case POWER_SUPPLY_PROP_TEMP_MAX:
offset = 1;
break;
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
offset = 2;
break;
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
offset = 3;
break;
default:
return -EINVAL;
}
temp = val / 10 + 30;
rc = qcom_fg_sram_write(chip, &temp, 0x454, 1, offset);
if (rc < 0) {
dev_err(chip->dev, "Error in writing jeita level for psp:%d, rc=%d\n", psp, rc);
return rc;
}
return 0;
}
static int qcom_fg_gen3_set_temp_threshold(struct qcom_fg_chip *chip,
enum power_supply_property psp, int val)
{
/* Not implemented yet */
return -ENOTSUPP;
}
static void qcom_fg_get_model_name(struct qcom_fg_chip *chip, union power_supply_propval *val)
{
switch (chip->subtype)
{
case PMI8994_SUBTYPE:
val->strval = "PMI8994 Battery";
break;
case PMI8996_SUBTYPE:
val->strval = "PMI8996 Battery";
break;
case PMI8998_SUBTYPE:
val->strval = "PMI8998 Battery";
break;
case PM8998_SUBTYPE:
val->strval = "PM8998 Battery";
break;
//handle pm660 and other socs that use fg3
/* Handle other PMICs */
default:
val->strval = "Unknown PMIC Battery";
}
@ -425,7 +793,7 @@ 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);
struct qcom_fg_chip *chip = power_supply_get_drvdata(psy);
int error = 0;
dev_dbg(chip->dev, "Getting property: %d", psp);
@ -435,19 +803,19 @@ static int fg_get_property(struct power_supply *psy,
val->strval = "Qualcomm";
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
fg_get_model_name(chip, val);
qcom_fg_get_model_name(chip, val);
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);
error = chip->ops->get_capacity(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
error = pmi8998_fg_get_current(chip, &val->intval);
error = chip->ops->get_current(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
error = pmi8998_fg_get_voltage(chip, &val->intval);
error = chip->ops->get_voltage(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
@ -458,23 +826,23 @@ static int fg_get_property(struct power_supply *psy,
val->intval = chip->batt_max_voltage_uv;
break;
case POWER_SUPPLY_PROP_STATUS:
error = pmi8998_get_prop_batt_status(chip, &val->intval);
error = chip->ops->get_batt_status(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
error = pmi8998_get_prop_health_status(chip, &val->intval);
error = chip->ops->get_health_status(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
case POWER_SUPPLY_PROP_CHARGE_FULL: /* TODO: Implement capacity learning */
val->intval = chip->batt_cap_uah;
break;
case POWER_SUPPLY_PROP_TEMP:
error = pmi8998_fg_get_temperature(chip, &val->intval);
error = chip->ops->get_temperature(chip, &val->intval);
break;
case POWER_SUPPLY_PROP_TEMP_MIN:
case POWER_SUPPLY_PROP_TEMP_MAX:
case POWER_SUPPLY_PROP_TEMP_ALERT_MIN:
case POWER_SUPPLY_PROP_TEMP_ALERT_MAX:
error = pmi8998_get_temp_threshold(chip, psp, &val->intval);
error = chip->ops->get_temp_threshold(chip, psp, &val->intval);
break;
//POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,POWER_SUPPLY_PROP_TIME_TO_FULL_AVG - calculate time remaining for full charge - implementable
//POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG - calculate time remaining when discharging - implementable
@ -500,8 +868,8 @@ static const struct power_supply_desc bms_psy_desc = {
.get_property = fg_get_property,
};
irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){
struct pmi8998_fg_chip *chip = data;
irqreturn_t qcom_fg_handle_usb_plugin(int irq, void *data){
struct qcom_fg_chip *chip = data;
int rc;
unsigned int stat;
bool vbus_rising;
@ -527,10 +895,47 @@ irqreturn_t pmi8998_handle_usb_plugin(int irq, void *data){
return IRQ_HANDLED;
}
static int pmi8998_fg_probe(struct platform_device *pdev)
irqreturn_t qcom_fg_handle_mem_avail(int irq, void *data){
struct qcom_fg_chip *chip = data;
if(qcom_fg_sram_check_access(chip)) {
complete_all(&chip->sram_access_granted);
dev_dbg(chip->dev, "SRAM access granted");
} else {
dev_dbg(chip->dev, "SRAM access revoked");
}
return IRQ_HANDLED;
}
/* Pre-Gen3 fuel gauge. PMI8996 and older */
static const struct qcom_fg_ops ops_fg = {
.get_capacity = qcom_fg_get_capacity,
.get_temperature = qcom_fg_get_temperature,
.get_current = qcom_fg_get_current,
.get_voltage = qcom_fg_get_voltage,
.get_batt_status = qcom_fg_get_prop_batt_status,
.get_health_status = qcom_fg_get_prop_health_status,
.get_temp_threshold = qcom_fg_get_temp_threshold,
.set_temp_threshold = qcom_fg_set_temp_threshold,
};
/* Gen3 fuel gauge. PMI8998 and newer */
static const struct qcom_fg_ops ops_fg_gen3 = {
.get_capacity = qcom_fg_get_capacity,
.get_temperature = qcom_fg_gen3_get_temperature,
.get_current = qcom_fg_gen3_get_current,
.get_voltage = qcom_fg_gen3_get_voltage,
.get_batt_status = qcom_fg_get_prop_batt_status,
.get_health_status = qcom_fg_get_prop_health_status,
.get_temp_threshold = qcom_fg_gen3_get_temp_threshold,
.set_temp_threshold = qcom_fg_gen3_set_temp_threshold,
};
static int qcom_fg_probe(struct platform_device *pdev)
{
struct power_supply_config supply_config = {};
struct pmi8998_fg_chip *chip;
struct qcom_fg_chip *chip;
const __be32 *prop_addr;
int rc = 0, irq;
u8 dma_status;
@ -542,6 +947,8 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
}
chip->dev = &pdev->dev;
chip->ops = of_device_get_match_data(&pdev->dev);
mutex_init(&chip->lock);
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
@ -587,7 +994,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
}
// Init memif fn inlined here (chip hardware info)
rc = pmi8998_read(chip->regmap, chip->revision, REG_MEM + DIG_MINOR, 4);
rc = qcom_fg_read(chip->regmap, chip->revision, REG_MEM + DIG_MINOR, 4);
if (rc) {
dev_err(chip->dev, "Unable to read FG revision rc=%d\n", rc);
return rc;
@ -599,7 +1006,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
return rc;
}
dev_dbg(chip->dev, "pmi8998 revision DIG:%d.%d ANA:%d.%d\n",
dev_dbg(chip->dev, "PMIC revision DIG:%d.%d ANA:%d.%d\n",
chip->revision[DIG_MAJOR], chip->revision[DIG_MINOR],
chip->revision[ANA_MAJOR], chip->revision[ANA_MINOR]);
@ -609,7 +1016,7 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
* that the next transaction starts only after the hw is ready.
* IACS_INTR_SRC_SLCT is BIT(3)
*/
rc = pmi8998_masked_write(chip->regmap,
rc = qcom_fg_masked_write(chip->regmap,
REG_MEM + MEM_INTF_IMA_CFG, BIT(3), BIT(3));
if (rc) {
dev_err(chip->dev,
@ -618,21 +1025,21 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
return rc;
}
rc = pmi8998_clear_ima(chip, true);
rc = qcom_fg_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 + 0x70, 1);
rc = qcom_fg_read(chip->regmap, &dma_status, REG_MEM + 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 + 0x71, BIT(0),
rc = qcom_fg_masked_write(chip->regmap, REG_MEM + 0x71, BIT(0),
error_present ? BIT(0) : 0);
if (rc < 0) {
pr_err("failed to write dma_ctl, rc=%d\n", rc);
@ -651,39 +1058,90 @@ static int pmi8998_fg_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, chip);
irq = of_irq_get_byname(pdev->dev.of_node, "usb-plugin");
if (irq < 0) {
dev_err(&pdev->dev, "Couldn't get irq usb-plugin byname\n");
return irq;
/* Initialize IRQs */
switch(chip->subtype)
{
case PMI8994_SUBTYPE:
case PMI8996_SUBTYPE:
irq = of_irq_get_byname(pdev->dev.of_node, "mem-avail");
if (irq < 0) {
dev_err(&pdev->dev, "Couldn't get irq mem-avail byname\n");
return irq;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL,
qcom_fg_handle_mem_avail,
IRQF_ONESHOT, "mem-avail", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d\n", irq);
return rc;
}
init_completion(&chip->sram_access_granted);
break;
case PM8998_SUBTYPE:
case PMI8998_SUBTYPE:
irq = of_irq_get_byname(pdev->dev.of_node, "usb-plugin");
if (irq < 0) {
dev_err(&pdev->dev, "Couldn't get irq usb-plugin byname\n");
return irq;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL,
qcom_fg_handle_usb_plugin,
IRQF_ONESHOT, "usb-plugin", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d\n", irq);
return rc;
}
break;
}
rc = devm_request_threaded_irq(chip->dev, irq, NULL,
pmi8998_handle_usb_plugin,
IRQF_ONESHOT, "usb-plugin", chip);
if (rc < 0) {
pr_err("Couldn't request irq %d\n", irq);
// Set default temperature thresholds
rc = chip->ops->set_temp_threshold(chip,
POWER_SUPPLY_PROP_TEMP_MIN,
BATT_TEMP_JEITA_COLD);
rc = chip->ops->set_temp_threshold(chip,
POWER_SUPPLY_PROP_TEMP_MAX,
BATT_TEMP_JEITA_WARM);
rc = chip->ops->set_temp_threshold(chip,
POWER_SUPPLY_PROP_TEMP_ALERT_MIN,
BATT_TEMP_JEITA_COOL);
rc = chip->ops->set_temp_threshold(chip,
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
BATT_TEMP_JEITA_HOT);
if(rc == -ENOTSUPP) {
dev_warn(chip->dev,
"Setting temperature thresholds not supported");
}
else if(rc < 0) {
dev_err(chip->dev,
"Setting temperature thresholds failed: %d\n", rc);
return rc;
}
return 0;
}
static int pmi8998_fg_remove(struct platform_device *pdev)
static int qcom_fg_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id fg_match_id_table[] = {
{ .compatible = "qcom,pmi8998-fg" },
{ .compatible = "qcom,pmi8994-fg", .data = &ops_fg },
{ .compatible = "qcom,pmi8998-fg", .data = &ops_fg_gen3 },
{ /* sentinal */ }
};
MODULE_DEVICE_TABLE(of, fg_match_id_table);
static struct platform_driver qcom_fg_driver = {
.probe = pmi8998_fg_probe,
.remove = pmi8998_fg_remove,
.probe = qcom_fg_probe,
.remove = qcom_fg_remove,
.driver = {
.name = "pmi8998-fg",
.name = "qcom-fg",
.of_match_table = fg_match_id_table,
},
};
@ -692,5 +1150,6 @@ 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_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
MODULE_DESCRIPTION("Qualcomm PMIC Fuel Gauge Driver");
MODULE_LICENSE("GPL v2");

View file

@ -3,19 +3,18 @@
/**** Registers *****/
// pmi8998 v2 specific
#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a
#define PMIC_SUBTYPE 0x105
#define PMI8994_SUBTYPE 0x0a
#define PMI8996_SUBTYPE 0x13
#define PM8998_SUBTYPE 0x14
#define PMI8998_SUBTYPE 0x15
/* SRAM */
#define MEM_INTF_STS 0x10
#define MEM_INTF_CFG 0x50
#define MEM_INTF_ADDR_LSB 0x61
#define MEM_INTF_RD_DATA0 0x67
#define MEM_INTF_WR_DATA0 0x63
#define PMIC_SUBTYPE 0x105
#define PM8998_SUBTYPE 0x14
#define PMI8998_SUBTYPE 0x15
// pm8950 / pm89988 common
#define MEM_INTF_CTL 0x51
#define MEM_INTF_IMA_CFG 0x52
#define MEM_INTF_IMA_OPR_STS 0x54
#define MEM_INTF_IMA_EXP_STS 0x55
@ -23,7 +22,18 @@
#define MEM_INTF_BEAT_COUNT 0x57
#define MEM_INTF_IMA_ERR_STS 0x5f
#define MEM_INTF_IMA_BYTE_EN 0x60
#define MEM_INTF_ADDR_LSB 0x61
#define MEM_INTF_RD_DATA0 0x67
#define MEM_INTF_WR_DATA0 0x63
#define MEM_INTF_AVAIL BIT(0)
#define MEM_INTF_CTL_BURST BIT(7)
#define MEM_INTF_CTL_WR_EN BIT(6)
#define RIF_MEM_ACCESS_REQ BIT(7)
/* Battery info */
#define BATT_INFO_CHARGE_MAX_DESIGN 0x4a
#define BATT_INFO_THERM_C1 0x5c
#define BATT_INFO_VBATT_LSB 0xa0
#define BATT_INFO_VBATT_MSB 0xa1
@ -36,45 +46,58 @@
#define BATT_TEMP_LSB_MASK GENMASK(7, 0)
#define BATT_TEMP_MSB_MASK GENMASK(2, 0)
#define REG_BASE 0x4000
#define REG_BATT 0x4100
#define REG_MEM 0x4400
/* Base addresses */
#define REG_BASE 0x4000
#define REG_BATT 0x4100
#define REG_MEM 0x4400
/* 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
/* Param addresses */
#define PARAM_ADDR_BATT_TEMP 0x50
#define PARAM_ADDR_BATT_VOLTAGE 0xa0
#define PARAM_ADDR_BATT_CURRENT 0xa2
#define MISC_BASE 0x1600
#define USBIN_BASE 0x1300
#define BATT_INFO_JEITA_COLD(chip) (REG_BATT + 0x62)
#define BATT_INFO_JEITA_COOL(chip) (REG_BATT + 0x63)
#define BATT_INFO_JEITA_WARM(chip) (REG_BATT + 0x64)
#define BATT_INFO_JEITA_HOT(chip) (REG_BATT + 0x65)
#define MISC_BASE 0x1600
#define USBIN_BASE 0x1300
#define BATT_TEMP_JEITA_COLD 100
#define BATT_TEMP_JEITA_COOL 50
#define BATT_TEMP_JEITA_WARM 400
#define BATT_TEMP_JEITA_HOT 450
#define BATTERY_CHARGER_STATUS_REG(chip) (chip->chg_base + 0x06)
#define BATTERY_HEALTH_STATUS_REG(chip) (chip->chg_base + 0x07)
#define BATTERY_HEALTH_STATUS_REG(chip) (chip->chg_base + 0x07)
#define BATTERY_CHARGER_STATUS_MASK GENMASK(2, 0)
#define POWER_PATH_STATUS_REG (MISC_BASE + 0x0B)
#define MEM_IF_TIMEOUT_MS 5000
enum wa_flags {
PMI8998_V1_REV_WA,
PMI8998_V2_REV_WA,
};
enum pmi8998_rev_offsets {
enum pmic_rev_offsets {
DIG_MINOR = 0x0,
DIG_MAJOR = 0x1,
ANA_MINOR = 0x2,
ANA_MAJOR = 0x3,
};
enum pmi8998_rev {
enum pmic_rev {
DIG_REV_1 = 0x1,
DIG_REV_2 = 0x2,
DIG_REV_3 = 0x3,
@ -112,18 +135,35 @@ static enum power_supply_property fg_properties[] = {
POWER_SUPPLY_PROP_TEMP_ALERT_MAX,
};
struct pmi8998_fg_chip {
struct qcom_fg_chip;
struct qcom_fg_ops {
int (*get_capacity)(struct qcom_fg_chip *chip, int *);
int (*get_temperature)(struct qcom_fg_chip *chip, int *);
int (*get_current)(struct qcom_fg_chip *chip, int *);
int (*get_voltage)(struct qcom_fg_chip *chip, int *);
int (*get_batt_status)(struct qcom_fg_chip *chip, int *);
int (*get_health_status)(struct qcom_fg_chip *chip, int *);
int (*get_temp_threshold)(struct qcom_fg_chip *chip,
enum power_supply_property psp, int *);
int (*set_temp_threshold)(struct qcom_fg_chip *chip,
enum power_supply_property psp, int);
};
struct qcom_fg_chip {
struct device *dev;
unsigned int base;
unsigned int chg_base;
struct regmap *regmap;
struct mutex lock;
unsigned int subtype;
const struct qcom_fg_ops *ops;
struct power_supply *bms_psy;
u8 revision[4];
bool ima_supported;
struct completion sram_access_granted;
int batt_cap_uah;
int batt_max_voltage_uv;