Loading Documentation/devicetree/bindings/batterydata/batterydata.txt +5 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,11 @@ Profile data node required properties: module when the ID resistance of some battery modules goes across several ranges. - qcom,battery-type : A string indicating the type of battery. - qcom,fg-profile-data : An array of hexadecimal values used to configure more complex fuel gauge peripherals which have a large amount of coefficients used in hardware state machines and thus influencing the final output of the state of charge read by software. Profile data node required subnodes: - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes Loading Documentation/devicetree/bindings/power/qpnp-fg.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ Parent node optional properties: - qcom,cool-bat-decidegc: Cool battery temperature in decidegC. - qcom,hot-bat-decidegc: Hot battery temperature in decidegC. - qcom,cold-bat-decidegc: Cold battery temperature in decidegC. - qcom,use-otp-profile: Specify this flag to avoid RAM loading any battery profile. qcom,fg-soc node required properties: - reg : offset and length of the PMIC peripheral register map. Loading arch/arm/boot/dts/qcom/batterydata-mtp-3000mah.dtsi +34 −1 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -19,6 +19,39 @@ qcom,mtp-3000mah { qcom,chg-term-ua = <200000>; qcom,batt-id-kohm = <300>; qcom,battery-type = "mtp_3000mah"; qcom,fg-profile-data = [ 07 88 73 7E 31 82 CB 7C 6D 83 E6 76 74 89 E1 8E F3 81 7A 93 72 AE 1A B3 67 1B 07 88 03 7E DB 81 7B 7C 6F 83 D7 75 DE 89 4C 94 34 82 0F 99 41 B7 F7 C2 60 14 07 0B 2C 5B 28 6E 5C FF 9A 3E 49 4E 78 49 18 28 82 46 19 34 C5 4F 18 28 66 72 66 62 66 72 66 62 2D 6B 8F 69 2F 88 65 91 F5 6E 0C 48 51 75 67 83 DF 6D 01 3E 66 4E 00 82 0A 32 58 8A 5C A0 71 0C 28 00 FF 37 F0 51 30 03 01 00 00 00]; qcom,fcc-temp-lut { qcom,lut-col-legend = <(-20) 0 25 40 60>; Loading arch/arm/boot/dts/qcom/msm8994-mtp.dtsi +10 −0 Original line number Diff line number Diff line Loading @@ -555,3 +555,13 @@ interrupt-names = "silabs_fm_int"; }; }; /{ mtp_batterydata: qcom,battery-data { #include "batterydata-mtp-3000mah.dtsi" }; }; &pmi8994_fg { qcom,battery-data = <&mtp_batterydata>; }; drivers/power/qpnp-fg.c +268 −45 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "FG: %s: " fmt, __func__ #include <linux/atomic.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/of.h> Loading Loading @@ -49,6 +50,8 @@ #define MEM_INTF_RD_DATA2 0x4E #define MEM_INTF_RD_DATA3 0x4F #define OTP_CFG1 0xE2 #define SOC_BOOT_MOD 0x50 #define SOC_RESTART 0x51 #define REG_OFFSET_PERP_SUBTYPE 0x05 Loading @@ -61,6 +64,9 @@ #define MA_MV_BIT_RES 39 #define MSB_SIGN BIT(7) #define IBAT_VBAT_MASK 0x7F #define NO_OTP_PROF_RELOAD BIT(6) #define REDO_FIRST_ESTIMATE BIT(3) #define RESTART_GO BIT(0) /* SUBTYPE definitions */ #define FG_SOC 0x9 Loading Loading @@ -89,10 +95,10 @@ struct fg_mem_setting { /* FG_MEMIF setting index */ enum fg_mem_setting_index { FG_MEM_JEITA_SOFT_COLD, FG_MEM_JEITA_SOFT_HOT, FG_MEM_JEITA_HARD_COLD, FG_MEM_JEITA_HARD_HOT, FG_MEM_SOFT_COLD, FG_MEM_SOFT_HOT, FG_MEM_HARD_COLD, FG_MEM_HARD_HOT, FG_MEM_SETTING_MAX, }; Loading @@ -105,10 +111,10 @@ enum fg_mem_setting_index { static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = { /* ID Address, Offset, Value*/ SETTING(JEITA_SOFT_COLD, 0x454, 0, 100), SETTING(JEITA_SOFT_HOT, 0x454, 1, 400), SETTING(JEITA_HARD_COLD, 0x454, 2, 50), SETTING(JEITA_HARD_HOT, 0x454, 3, 450), SETTING(SOFT_COLD, 0x454, 0, 100), SETTING(SOFT_HOT, 0x454, 1, 400), SETTING(HARD_COLD, 0x454, 2, 50), SETTING(HARD_HOT, 0x454, 3, 450), }; static int fg_debug_mask; Loading Loading @@ -159,13 +165,20 @@ struct fg_chip { u16 mem_base; u16 vbat_adc_addr; u16 ibat_adc_addr; atomic_t memif_user_cnt; bool fast_access; struct fg_irq soc_irq[FG_SOC_IRQ_COUNT]; struct fg_irq batt_irq[FG_BATT_IRQ_COUNT]; struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT]; struct completion sram_access; struct power_supply bms_psy; struct mutex rw_lock; struct work_struct batt_profile_init; bool profile_loaded; bool use_otp_profile; struct delayed_work update_jeita_setting; char *batt_profile; unsigned int batt_profile_len; }; /* FG_MEMIF DEBUGFS structures */ Loading Loading @@ -420,20 +433,23 @@ static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, bool keep_access) { int rc = 0; int rc = 0, user_cnt = 0; u8 *rd_data = val; bool otp; if (address < RAM_OFFSET) otp = 1; user_cnt = atomic_add_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); mutex_lock(&chip->rw_lock); if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; goto out; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 0, (len > 4), otp); if (rc) goto out; Loading Loading @@ -461,14 +477,18 @@ static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, len = 0; } if (!keep_access) { out: user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); if (!keep_access && (user_cnt == 0) && !rc) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, 0, 1); if (rc) pr_err("failed to set mem access bit\n"); } out: mutex_unlock(&chip->rw_lock); return rc; } Loading @@ -476,7 +496,7 @@ out: static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, unsigned int len, unsigned int offset, bool keep_access) { int rc = 0; int rc = 0, user_cnt = 0; u8 *wr_data = val; if (address < RAM_OFFSET) Loading @@ -485,13 +505,16 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, if (offset > 3) return -EINVAL; user_cnt = atomic_add_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); mutex_lock(&chip->rw_lock); if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; goto out; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 1, (len > 4), 0); if (rc) goto out; Loading Loading @@ -532,7 +555,11 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, } } if (!keep_access) { out: user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); if (!keep_access && (user_cnt == 0) && !rc) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, 0, 1); if (rc) { Loading @@ -541,16 +568,48 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, } } out: mutex_unlock(&chip->rw_lock); return 0; return rc; } static int fg_mem_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val, u8 offset) { int rc = 0; u8 reg[4]; char str[DEBUG_PRINT_BUFFER_SIZE]; rc = fg_mem_read(chip, reg, addr, 4, 1); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); return rc; } reg[offset] &= ~mask; reg[offset] |= val & mask; str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4); pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset); rc = fg_mem_write(chip, reg, addr, 4, 0, 0); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); return rc; } return rc; } #define DEFAULT_CAPACITY 50 static int get_prop_capacity(struct fg_chip *chip) { u8 cap[2]; int rc, capacity = 0, tries = 0; if (!chip->profile_loaded && !chip->use_otp_profile) return DEFAULT_CAPACITY; while (tries < MAX_TRIES_SOC) { rc = fg_read(chip, cap, chip->soc_base + SOC_MONOTONIC_SOC, 2); Loading Loading @@ -633,6 +692,10 @@ static int get_prop_voltage_now(struct fg_chip *chip) #define MAX_TEMP_DEGC 970 static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) { if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("addr 0x%02X, offset %d\n", settings[type].address, settings[type].offset); return settings[type].value; } Loading @@ -641,7 +704,9 @@ static int set_prop_jeita_temp(struct fg_chip *chip, { int rc = 0; pr_debug("addr 0x%02X, offset %d temp%d\n", settings[type].address, if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("addr 0x%02X, offset %d temp%d\n", settings[type].address, settings[type].offset, decidegc); settings[type].value = decidegc; Loading @@ -663,10 +728,10 @@ static void update_jeita_setting(struct work_struct *work) int i, rc; for (i = 0; i < 4; i++) reg[i] = (settings[JEITA_SOFT_COLD + i].value / 10) + 30; reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30; rc = fg_mem_write(chip, reg, settings[JEITA_SOFT_COLD].address, 4, settings[JEITA_SOFT_COLD].offset, 0); rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address, 4, settings[FG_MEM_SOFT_COLD].offset, 0); if (rc) pr_err("failed to update JEITA setting rc=%d\n", rc); } Loading Loading @@ -696,10 +761,10 @@ static int fg_power_get_property(struct power_supply *psy, val->intval = get_prop_voltage_now(chip); break; case POWER_SUPPLY_PROP_COOL_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD); val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD); break; case POWER_SUPPLY_PROP_WARM_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT); val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT); break; default: return -EINVAL; Loading @@ -718,11 +783,11 @@ static int fg_power_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD, val->intval); FG_MEM_SOFT_COLD, val->intval); break; case POWER_SUPPLY_PROP_WARM_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT, val->intval); FG_MEM_SOFT_HOT, val->intval); break; default: return -EINVAL; Loading @@ -748,7 +813,7 @@ static int fg_property_is_writeable(struct power_supply *psy, static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 mem_if_sts; u8 mem_if_sts, reg; int rc; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); Loading @@ -760,6 +825,13 @@ static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) if (fg_check_sram_access(chip)) { if (fg_debug_mask & FG_IRQS) pr_info("sram access granted\n"); if (chip->fast_access) { reg = REDO_FIRST_ESTIMATE | RESTART_GO; rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, reg, reg, 1); if (rc) pr_err("failed to set low latency bit\n"); } complete_all(&chip->sram_access); } else { if (fg_debug_mask & FG_IRQS) Loading @@ -774,6 +846,25 @@ static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) } static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 soc_rt_sts; int rc; rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", INT_RT_STS(chip->soc_base), rc); } if (fg_debug_mask & FG_IRQS) pr_info("triggered 0x%x\n", soc_rt_sts); power_supply_changed(&chip->bms_psy); return IRQ_HANDLED; } static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; Loading @@ -800,14 +891,121 @@ do { \ " property rc = %d\n", rc); \ } while (0) #define LOW_LATENCY BIT(6) static int fg_batt_profile_init(struct fg_chip *chip) { int rc = 0; int len; struct device_node *node = chip->spmi->dev.of_node; struct device_node *batt_node; const char *data; u8 reg = 0; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { pr_warn("No available batterydata, using OTP defaults\n"); return 0; } for_each_child_of_node(batt_node, node) { data = of_get_property(node, "qcom,fg-profile-data", &len); if (!data) { pr_err("no battery profile loaded\n"); return 0; } else { break; } } chip->batt_profile = devm_kzalloc(chip->dev, sizeof(char) * len, GFP_KERNEL); if (!chip->batt_profile) return -ENOMEM; memcpy(chip->batt_profile, data, len); chip->batt_profile_len = len; if (fg_debug_mask & FG_MEM_DEBUG_WRITES) print_hex_dump(KERN_ERR, "profile: ", DUMP_PREFIX_NONE, 16, 1, chip->batt_profile, chip->batt_profile_len, false); reg = NO_OTP_PROF_RELOAD; rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, reg, reg, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } reg = LOW_LATENCY; rc = fg_write(chip, ®, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->fast_access = true; rc = fg_mem_write(chip, chip->batt_profile, 0x4C0, chip->batt_profile_len, 0, 1); if (rc) pr_err("failed to write profile rc=%d\n", rc); rc = fg_mem_masked_write(chip, 0x53C, 0x1, 0x1, 0); if (rc) pr_err("failed to write profile rc=%d\n", rc); reg = 0; rc = fg_write(chip, ®, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } reg = 0; rc = fg_write(chip, ®, chip->soc_base + SOC_RESTART, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->fast_access = false; /* wait for 2 seconds prior to restart the fuel gauge */ msleep(2000); reg = 0x19; rc = fg_write(chip, ®, chip->soc_base + SOC_RESTART, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->profile_loaded = true; return rc; } static void batt_profile_init(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, batt_profile_init); if (fg_batt_profile_init(chip)) pr_err("failed to update JEITA setting\n"); } static int fg_of_init(struct fg_chip *chip) { int rc = 0; OF_READ_SETTING(FG_MEM_JEITA_SOFT_HOT, "warm-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_SOFT_COLD, "cool-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_HARD_HOT, "hot-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_HARD_COLD, "cold-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1); /* Get the use-otp-profile property */ chip->use_otp_profile = of_property_read_bool( chip->spmi->dev.of_node, "qcom,use-otp-profile"); return rc; } Loading Loading @@ -865,6 +1063,12 @@ static int fg_init_irqs(struct fg_chip *chip) pr_err("Unable to get delta-soc irq\n"); return rc; } chip->soc_irq[FIRST_EST_DONE].irq = spmi_get_irq_byname( chip->spmi, spmi_resource, "first-est-done"); if (chip->soc_irq[FIRST_EST_DONE].irq < 0) { pr_err("Unable to get first-est-done irq\n"); return rc; } rc |= devm_request_irq(chip->dev, chip->soc_irq[FULL_SOC].irq, Loading Loading @@ -893,6 +1097,15 @@ static int fg_init_irqs(struct fg_chip *chip) chip->soc_irq[DELTA_SOC].irq, rc); return rc; } rc |= devm_request_irq(chip->dev, chip->soc_irq[FIRST_EST_DONE].irq, fg_first_soc_irq_handler, IRQF_TRIGGER_RISING, "first-est-done", chip); if (rc < 0) { pr_err("Can't request %d delta-soc: %d\n", chip->soc_irq[FIRST_EST_DONE].irq, rc); return rc; } enable_irq_wake(chip->soc_irq[FULL_SOC].irq); enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); Loading Loading @@ -934,6 +1147,7 @@ static int fg_remove(struct spmi_device *spmi) mutex_destroy(&chip->rw_lock); cancel_delayed_work_sync(&chip->update_jeita_setting); cancel_work_sync(&chip->batt_profile_init); power_supply_unregister(&chip->bms_psy); dev_set_drvdata(&spmi->dev, NULL); return 0; Loading Loading @@ -1327,6 +1541,7 @@ err_remove_fs: return -ENOMEM; } #define INIT_JEITA_DELAY_MS 1000 static int fg_probe(struct spmi_device *spmi) { struct device *dev = &(spmi->dev); Loading Loading @@ -1358,14 +1573,10 @@ static int fg_probe(struct spmi_device *spmi) mutex_init(&chip->rw_lock); INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); INIT_WORK(&chip->batt_profile_init, batt_profile_init); init_completion(&chip->sram_access); rc = fg_of_init(chip); if (rc) { pr_err("failed to parse devicetree rc%d\n", rc); goto of_init_fail; } spmi_for_each_container_dev(spmi_resource, spmi) { if (!spmi_resource) { pr_err("qpnp_chg: spmi resource absent\n"); Loading Loading @@ -1412,6 +1623,12 @@ static int fg_probe(struct spmi_device *spmi) } } rc = fg_of_init(chip); if (rc) { pr_err("failed to parse devicetree rc%d\n", rc); goto of_init_fail; } chip->bms_psy.name = "bms"; chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS; chip->bms_psy.properties = fg_power_props; Loading Loading @@ -1442,7 +1659,13 @@ static int fg_probe(struct spmi_device *spmi) } } pr_info("probe success SOC %d\n", get_prop_capacity(chip)); schedule_delayed_work( &chip->update_jeita_setting, msecs_to_jiffies(INIT_JEITA_DELAY_MS)); if (!chip->use_otp_profile) schedule_work(&chip->batt_profile_init); pr_info("probe success\n"); return rc; Loading Loading
Documentation/devicetree/bindings/batterydata/batterydata.txt +5 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,11 @@ Profile data node required properties: module when the ID resistance of some battery modules goes across several ranges. - qcom,battery-type : A string indicating the type of battery. - qcom,fg-profile-data : An array of hexadecimal values used to configure more complex fuel gauge peripherals which have a large amount of coefficients used in hardware state machines and thus influencing the final output of the state of charge read by software. Profile data node required subnodes: - qcom,fcc-temp-lut : An 1-dimensional lookup table node that encodes Loading
Documentation/devicetree/bindings/power/qpnp-fg.txt +2 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ Parent node optional properties: - qcom,cool-bat-decidegc: Cool battery temperature in decidegC. - qcom,hot-bat-decidegc: Hot battery temperature in decidegC. - qcom,cold-bat-decidegc: Cold battery temperature in decidegC. - qcom,use-otp-profile: Specify this flag to avoid RAM loading any battery profile. qcom,fg-soc node required properties: - reg : offset and length of the PMIC peripheral register map. Loading
arch/arm/boot/dts/qcom/batterydata-mtp-3000mah.dtsi +34 −1 Original line number Diff line number Diff line /* Copyright (c) 2013, The Linux Foundation. All rights reserved. /* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading @@ -19,6 +19,39 @@ qcom,mtp-3000mah { qcom,chg-term-ua = <200000>; qcom,batt-id-kohm = <300>; qcom,battery-type = "mtp_3000mah"; qcom,fg-profile-data = [ 07 88 73 7E 31 82 CB 7C 6D 83 E6 76 74 89 E1 8E F3 81 7A 93 72 AE 1A B3 67 1B 07 88 03 7E DB 81 7B 7C 6F 83 D7 75 DE 89 4C 94 34 82 0F 99 41 B7 F7 C2 60 14 07 0B 2C 5B 28 6E 5C FF 9A 3E 49 4E 78 49 18 28 82 46 19 34 C5 4F 18 28 66 72 66 62 66 72 66 62 2D 6B 8F 69 2F 88 65 91 F5 6E 0C 48 51 75 67 83 DF 6D 01 3E 66 4E 00 82 0A 32 58 8A 5C A0 71 0C 28 00 FF 37 F0 51 30 03 01 00 00 00]; qcom,fcc-temp-lut { qcom,lut-col-legend = <(-20) 0 25 40 60>; Loading
arch/arm/boot/dts/qcom/msm8994-mtp.dtsi +10 −0 Original line number Diff line number Diff line Loading @@ -555,3 +555,13 @@ interrupt-names = "silabs_fm_int"; }; }; /{ mtp_batterydata: qcom,battery-data { #include "batterydata-mtp-3000mah.dtsi" }; }; &pmi8994_fg { qcom,battery-data = <&mtp_batterydata>; };
drivers/power/qpnp-fg.c +268 −45 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "FG: %s: " fmt, __func__ #include <linux/atomic.h> #include <linux/delay.h> #include <linux/kernel.h> #include <linux/of.h> Loading Loading @@ -49,6 +50,8 @@ #define MEM_INTF_RD_DATA2 0x4E #define MEM_INTF_RD_DATA3 0x4F #define OTP_CFG1 0xE2 #define SOC_BOOT_MOD 0x50 #define SOC_RESTART 0x51 #define REG_OFFSET_PERP_SUBTYPE 0x05 Loading @@ -61,6 +64,9 @@ #define MA_MV_BIT_RES 39 #define MSB_SIGN BIT(7) #define IBAT_VBAT_MASK 0x7F #define NO_OTP_PROF_RELOAD BIT(6) #define REDO_FIRST_ESTIMATE BIT(3) #define RESTART_GO BIT(0) /* SUBTYPE definitions */ #define FG_SOC 0x9 Loading Loading @@ -89,10 +95,10 @@ struct fg_mem_setting { /* FG_MEMIF setting index */ enum fg_mem_setting_index { FG_MEM_JEITA_SOFT_COLD, FG_MEM_JEITA_SOFT_HOT, FG_MEM_JEITA_HARD_COLD, FG_MEM_JEITA_HARD_HOT, FG_MEM_SOFT_COLD, FG_MEM_SOFT_HOT, FG_MEM_HARD_COLD, FG_MEM_HARD_HOT, FG_MEM_SETTING_MAX, }; Loading @@ -105,10 +111,10 @@ enum fg_mem_setting_index { static struct fg_mem_setting settings[FG_MEM_SETTING_MAX] = { /* ID Address, Offset, Value*/ SETTING(JEITA_SOFT_COLD, 0x454, 0, 100), SETTING(JEITA_SOFT_HOT, 0x454, 1, 400), SETTING(JEITA_HARD_COLD, 0x454, 2, 50), SETTING(JEITA_HARD_HOT, 0x454, 3, 450), SETTING(SOFT_COLD, 0x454, 0, 100), SETTING(SOFT_HOT, 0x454, 1, 400), SETTING(HARD_COLD, 0x454, 2, 50), SETTING(HARD_HOT, 0x454, 3, 450), }; static int fg_debug_mask; Loading Loading @@ -159,13 +165,20 @@ struct fg_chip { u16 mem_base; u16 vbat_adc_addr; u16 ibat_adc_addr; atomic_t memif_user_cnt; bool fast_access; struct fg_irq soc_irq[FG_SOC_IRQ_COUNT]; struct fg_irq batt_irq[FG_BATT_IRQ_COUNT]; struct fg_irq mem_irq[FG_MEM_IF_IRQ_COUNT]; struct completion sram_access; struct power_supply bms_psy; struct mutex rw_lock; struct work_struct batt_profile_init; bool profile_loaded; bool use_otp_profile; struct delayed_work update_jeita_setting; char *batt_profile; unsigned int batt_profile_len; }; /* FG_MEMIF DEBUGFS structures */ Loading Loading @@ -420,20 +433,23 @@ static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, bool keep_access) { int rc = 0; int rc = 0, user_cnt = 0; u8 *rd_data = val; bool otp; if (address < RAM_OFFSET) otp = 1; user_cnt = atomic_add_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); mutex_lock(&chip->rw_lock); if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; goto out; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 0, (len > 4), otp); if (rc) goto out; Loading Loading @@ -461,14 +477,18 @@ static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len, len = 0; } if (!keep_access) { out: user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); if (!keep_access && (user_cnt == 0) && !rc) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, 0, 1); if (rc) pr_err("failed to set mem access bit\n"); } out: mutex_unlock(&chip->rw_lock); return rc; } Loading @@ -476,7 +496,7 @@ out: static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, unsigned int len, unsigned int offset, bool keep_access) { int rc = 0; int rc = 0, user_cnt = 0; u8 *wr_data = val; if (address < RAM_OFFSET) Loading @@ -485,13 +505,16 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, if (offset > 3) return -EINVAL; user_cnt = atomic_add_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); mutex_lock(&chip->rw_lock); if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; goto out; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 1, (len > 4), 0); if (rc) goto out; Loading Loading @@ -532,7 +555,11 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, } } if (!keep_access) { out: user_cnt = atomic_sub_return(1, &chip->memif_user_cnt); if (fg_debug_mask & FG_MEM_DEBUG_READS) pr_info("user_cnt %d\n", user_cnt); if (!keep_access && (user_cnt == 0) && !rc) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, 0, 1); if (rc) { Loading @@ -541,16 +568,48 @@ static int fg_mem_write(struct fg_chip *chip, u8 *val, u16 address, } } out: mutex_unlock(&chip->rw_lock); return 0; return rc; } static int fg_mem_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val, u8 offset) { int rc = 0; u8 reg[4]; char str[DEBUG_PRINT_BUFFER_SIZE]; rc = fg_mem_read(chip, reg, addr, 4, 1); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); return rc; } reg[offset] &= ~mask; reg[offset] |= val & mask; str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, reg, 4); pr_debug("Writing %s address %03x, offset %d\n", str, addr, offset); rc = fg_mem_write(chip, reg, addr, 4, 0, 0); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); return rc; } return rc; } #define DEFAULT_CAPACITY 50 static int get_prop_capacity(struct fg_chip *chip) { u8 cap[2]; int rc, capacity = 0, tries = 0; if (!chip->profile_loaded && !chip->use_otp_profile) return DEFAULT_CAPACITY; while (tries < MAX_TRIES_SOC) { rc = fg_read(chip, cap, chip->soc_base + SOC_MONOTONIC_SOC, 2); Loading Loading @@ -633,6 +692,10 @@ static int get_prop_voltage_now(struct fg_chip *chip) #define MAX_TEMP_DEGC 970 static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) { if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("addr 0x%02X, offset %d\n", settings[type].address, settings[type].offset); return settings[type].value; } Loading @@ -641,7 +704,9 @@ static int set_prop_jeita_temp(struct fg_chip *chip, { int rc = 0; pr_debug("addr 0x%02X, offset %d temp%d\n", settings[type].address, if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("addr 0x%02X, offset %d temp%d\n", settings[type].address, settings[type].offset, decidegc); settings[type].value = decidegc; Loading @@ -663,10 +728,10 @@ static void update_jeita_setting(struct work_struct *work) int i, rc; for (i = 0; i < 4; i++) reg[i] = (settings[JEITA_SOFT_COLD + i].value / 10) + 30; reg[i] = (settings[FG_MEM_SOFT_COLD + i].value / 10) + 30; rc = fg_mem_write(chip, reg, settings[JEITA_SOFT_COLD].address, 4, settings[JEITA_SOFT_COLD].offset, 0); rc = fg_mem_write(chip, reg, settings[FG_MEM_SOFT_COLD].address, 4, settings[FG_MEM_SOFT_COLD].offset, 0); if (rc) pr_err("failed to update JEITA setting rc=%d\n", rc); } Loading Loading @@ -696,10 +761,10 @@ static int fg_power_get_property(struct power_supply *psy, val->intval = get_prop_voltage_now(chip); break; case POWER_SUPPLY_PROP_COOL_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD); val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_COLD); break; case POWER_SUPPLY_PROP_WARM_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT); val->intval = get_prop_jeita_temp(chip, FG_MEM_SOFT_HOT); break; default: return -EINVAL; Loading @@ -718,11 +783,11 @@ static int fg_power_set_property(struct power_supply *psy, switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD, val->intval); FG_MEM_SOFT_COLD, val->intval); break; case POWER_SUPPLY_PROP_WARM_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT, val->intval); FG_MEM_SOFT_HOT, val->intval); break; default: return -EINVAL; Loading @@ -748,7 +813,7 @@ static int fg_property_is_writeable(struct power_supply *psy, static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 mem_if_sts; u8 mem_if_sts, reg; int rc; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); Loading @@ -760,6 +825,13 @@ static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) if (fg_check_sram_access(chip)) { if (fg_debug_mask & FG_IRQS) pr_info("sram access granted\n"); if (chip->fast_access) { reg = REDO_FIRST_ESTIMATE | RESTART_GO; rc = fg_masked_write(chip, chip->soc_base + SOC_RESTART, reg, reg, 1); if (rc) pr_err("failed to set low latency bit\n"); } complete_all(&chip->sram_access); } else { if (fg_debug_mask & FG_IRQS) Loading @@ -774,6 +846,25 @@ static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) } static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 soc_rt_sts; int rc; rc = fg_read(chip, &soc_rt_sts, INT_RT_STS(chip->soc_base), 1); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", INT_RT_STS(chip->soc_base), rc); } if (fg_debug_mask & FG_IRQS) pr_info("triggered 0x%x\n", soc_rt_sts); power_supply_changed(&chip->bms_psy); return IRQ_HANDLED; } static irqreturn_t fg_first_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; Loading @@ -800,14 +891,121 @@ do { \ " property rc = %d\n", rc); \ } while (0) #define LOW_LATENCY BIT(6) static int fg_batt_profile_init(struct fg_chip *chip) { int rc = 0; int len; struct device_node *node = chip->spmi->dev.of_node; struct device_node *batt_node; const char *data; u8 reg = 0; batt_node = of_find_node_by_name(node, "qcom,battery-data"); if (!batt_node) { pr_warn("No available batterydata, using OTP defaults\n"); return 0; } for_each_child_of_node(batt_node, node) { data = of_get_property(node, "qcom,fg-profile-data", &len); if (!data) { pr_err("no battery profile loaded\n"); return 0; } else { break; } } chip->batt_profile = devm_kzalloc(chip->dev, sizeof(char) * len, GFP_KERNEL); if (!chip->batt_profile) return -ENOMEM; memcpy(chip->batt_profile, data, len); chip->batt_profile_len = len; if (fg_debug_mask & FG_MEM_DEBUG_WRITES) print_hex_dump(KERN_ERR, "profile: ", DUMP_PREFIX_NONE, 16, 1, chip->batt_profile, chip->batt_profile_len, false); reg = NO_OTP_PROF_RELOAD; rc = fg_masked_write(chip, chip->soc_base + SOC_BOOT_MOD, reg, reg, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } reg = LOW_LATENCY; rc = fg_write(chip, ®, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->fast_access = true; rc = fg_mem_write(chip, chip->batt_profile, 0x4C0, chip->batt_profile_len, 0, 1); if (rc) pr_err("failed to write profile rc=%d\n", rc); rc = fg_mem_masked_write(chip, 0x53C, 0x1, 0x1, 0); if (rc) pr_err("failed to write profile rc=%d\n", rc); reg = 0; rc = fg_write(chip, ®, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } reg = 0; rc = fg_write(chip, ®, chip->soc_base + SOC_RESTART, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->fast_access = false; /* wait for 2 seconds prior to restart the fuel gauge */ msleep(2000); reg = 0x19; rc = fg_write(chip, ®, chip->soc_base + SOC_RESTART, 1); if (rc) { pr_err("failed to set low latency access bit\n"); return -EIO; } chip->profile_loaded = true; return rc; } static void batt_profile_init(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, batt_profile_init); if (fg_batt_profile_init(chip)) pr_err("failed to update JEITA setting\n"); } static int fg_of_init(struct fg_chip *chip) { int rc = 0; OF_READ_SETTING(FG_MEM_JEITA_SOFT_HOT, "warm-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_SOFT_COLD, "cool-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_HARD_HOT, "hot-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_JEITA_HARD_COLD, "cold-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_SOFT_HOT, "warm-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_SOFT_COLD, "cool-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_HARD_HOT, "hot-bat-decidegc", rc, 1); OF_READ_SETTING(FG_MEM_HARD_COLD, "cold-bat-decidegc", rc, 1); /* Get the use-otp-profile property */ chip->use_otp_profile = of_property_read_bool( chip->spmi->dev.of_node, "qcom,use-otp-profile"); return rc; } Loading Loading @@ -865,6 +1063,12 @@ static int fg_init_irqs(struct fg_chip *chip) pr_err("Unable to get delta-soc irq\n"); return rc; } chip->soc_irq[FIRST_EST_DONE].irq = spmi_get_irq_byname( chip->spmi, spmi_resource, "first-est-done"); if (chip->soc_irq[FIRST_EST_DONE].irq < 0) { pr_err("Unable to get first-est-done irq\n"); return rc; } rc |= devm_request_irq(chip->dev, chip->soc_irq[FULL_SOC].irq, Loading Loading @@ -893,6 +1097,15 @@ static int fg_init_irqs(struct fg_chip *chip) chip->soc_irq[DELTA_SOC].irq, rc); return rc; } rc |= devm_request_irq(chip->dev, chip->soc_irq[FIRST_EST_DONE].irq, fg_first_soc_irq_handler, IRQF_TRIGGER_RISING, "first-est-done", chip); if (rc < 0) { pr_err("Can't request %d delta-soc: %d\n", chip->soc_irq[FIRST_EST_DONE].irq, rc); return rc; } enable_irq_wake(chip->soc_irq[FULL_SOC].irq); enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); Loading Loading @@ -934,6 +1147,7 @@ static int fg_remove(struct spmi_device *spmi) mutex_destroy(&chip->rw_lock); cancel_delayed_work_sync(&chip->update_jeita_setting); cancel_work_sync(&chip->batt_profile_init); power_supply_unregister(&chip->bms_psy); dev_set_drvdata(&spmi->dev, NULL); return 0; Loading Loading @@ -1327,6 +1541,7 @@ err_remove_fs: return -ENOMEM; } #define INIT_JEITA_DELAY_MS 1000 static int fg_probe(struct spmi_device *spmi) { struct device *dev = &(spmi->dev); Loading Loading @@ -1358,14 +1573,10 @@ static int fg_probe(struct spmi_device *spmi) mutex_init(&chip->rw_lock); INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); INIT_WORK(&chip->batt_profile_init, batt_profile_init); init_completion(&chip->sram_access); rc = fg_of_init(chip); if (rc) { pr_err("failed to parse devicetree rc%d\n", rc); goto of_init_fail; } spmi_for_each_container_dev(spmi_resource, spmi) { if (!spmi_resource) { pr_err("qpnp_chg: spmi resource absent\n"); Loading Loading @@ -1412,6 +1623,12 @@ static int fg_probe(struct spmi_device *spmi) } } rc = fg_of_init(chip); if (rc) { pr_err("failed to parse devicetree rc%d\n", rc); goto of_init_fail; } chip->bms_psy.name = "bms"; chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS; chip->bms_psy.properties = fg_power_props; Loading Loading @@ -1442,7 +1659,13 @@ static int fg_probe(struct spmi_device *spmi) } } pr_info("probe success SOC %d\n", get_prop_capacity(chip)); schedule_delayed_work( &chip->update_jeita_setting, msecs_to_jiffies(INIT_JEITA_DELAY_MS)); if (!chip->use_otp_profile) schedule_work(&chip->batt_profile_init); pr_info("probe success\n"); return rc; Loading