diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index 8ad785d54a4a..f2e743ff29f5 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -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. diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index dfa2b0382a46..c0e39673363f 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -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 diff --git a/drivers/power/supply/pmi8998_fg.c b/drivers/power/supply/qcom_fg.c similarity index 50% rename from drivers/power/supply/pmi8998_fg.c rename to drivers/power/supply/qcom_fg.c index aa848e5577fe..dc41f6072828 100644 --- a/drivers/power/supply/pmi8998_fg.c +++ b/drivers/power/supply/qcom_fg.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -14,14 +15,14 @@ #include #include -#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, ®, addr, 1); + error = qcom_fg_read(map, ®, addr, 1); if (error) return error; reg &= ~mask; reg |= val & mask; - error = pmi8998_write(map, ®, addr, 1); + error = qcom_fg_write(map, ®, 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 "); MODULE_AUTHOR("Joel Selvaraj "); -MODULE_DESCRIPTION("Qualcomm PMI8998 Fuel Guage Driver"); +MODULE_AUTHOR("Yassine Oudjana "); +MODULE_DESCRIPTION("Qualcomm PMIC Fuel Gauge Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/supply/pmi8998_fg.h b/drivers/power/supply/qcom_fg.h similarity index 60% rename from drivers/power/supply/pmi8998_fg.h rename to drivers/power/supply/qcom_fg.h index b44f88bc9395..2bc6df2fdd9d 100644 --- a/drivers/power/supply/pmi8998_fg.h +++ b/drivers/power/supply/qcom_fg.h @@ -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;