Loading Documentation/devicetree/bindings/power/qpnp-fg.txt +24 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,12 @@ Optionally ADC nodes can be added Parent node required properties: - compatible : should be "qcom,qpnp-fg" for the FG driver. Parent node optional properties: - qcom,warm-bat-decidegc: Warm battery temperature in decidegC. - 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,fg-soc node required properties: - reg : offset and length of the PMIC peripheral register map. - interrupts : the interrupt mappings. Loading @@ -40,6 +46,15 @@ qcom,fg-soc node required properties: 6: sw-fallbk-ocv 7: sw-fallbk-new-battrt-sts qcom,fg-memif node required properties: - reg : offset and length of the PMIC peripheral register map. - interrupts : the interrupt mappings. The format should be <slave-id peripheral-id interrupt-number>. - interrupt-names : names for the mapped fg adc interrupts The following interrupts are required: 0: mem-avail Example: pmi8994_fg: qcom,fg { spmi-dev-container; Loading Loading @@ -77,4 +92,13 @@ pmi8994_fg: qcom,fg { qcom,fg-adc-ibat@4255 { reg = <0x4255 0x1>; }; qcom,fg-memif@4400 { reg = <0x4400 0x100>; interrupts = <0x2 0x44 0x0>, <0x2 0x44 0x1>; interrupt-names = "mem-avail", "data-rcvry-sug"; }; }; arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +10 −0 Original line number Diff line number Diff line Loading @@ -274,6 +274,16 @@ qcom,fg-adc-ibat@4255 { reg = <0x4255 0x1>; }; qcom,fg-memif@4400 { status = "okay"; reg = <0x4400 0x100>; interrupts = <0x2 0x44 0x0>, <0x2 0x44 0x1>; interrupt-names = "mem-avail", "data-rcvry-sug"; }; }; }; Loading drivers/power/qpnp-fg.c +510 −23 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "FG: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/err.h> Loading @@ -25,10 +26,30 @@ #include <linux/power_supply.h> /* Register offsets */ /* Interrupt offsets */ #define INT_RT_STS(base) (base + 0x10) /* SPMI Register offsets */ #define SOC_MONOTONIC_SOC 0x09 #define MEM_INTF_CFG 0x40 #define MEM_INTF_CTL 0x41 #define MEM_INTF_ADDR_LSB 0x42 #define MEM_INTF_ADDR_MSB 0x43 #define MEM_INTF_WR_DATA0 0x48 #define MEM_INTF_WR_DATA1 0x49 #define MEM_INTF_WR_DATA2 0x4A #define MEM_INTF_WR_DATA3 0x4B #define MEM_INTF_RD_DATA0 0x4C #define MEM_INTF_RD_DATA1 0x4D #define MEM_INTF_RD_DATA2 0x4E #define MEM_INTF_RD_DATA3 0x4F #define REG_OFFSET_PERP_SUBTYPE 0x05 /* RAM register offsets */ #define RAM_OFFSET 0x400 /* Bit/Mask definitions */ #define FULL_PERCENT 0xFF #define MAX_TRIES_SOC 5 Loading @@ -43,6 +64,52 @@ #define FG_MEMIF 0xC #define QPNP_FG_DEV_NAME "qcom,qpnp-fg" #define MEM_IF_TIMEOUT_MS 2200 /* Debug Flag Definitions */ enum { FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */ FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */ FG_IRQS = BIT(2), /* Show interrupts */ FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */ FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */ FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */ }; struct fg_mem_setting { u16 address; u8 offset; int value; }; /* 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_SETTING_MAX, }; #define SETTING(_idx, _address, _offset, _value) \ [FG_MEM_##_idx] = { \ .address = _address, \ .offset = _offset, \ .value = _value, \ } \ 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), }; static int fg_debug_mask; module_param_named( debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR ); struct fg_irq { int irq; Loading Loading @@ -81,7 +148,6 @@ enum fg_mem_if_irq { struct fg_chip { struct device *dev; struct dentry *dent; struct spmi_device *spmi; u16 soc_base; u16 batt_base; Loading @@ -91,7 +157,9 @@ struct fg_chip { 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 fg_mem_if_chip *memif; struct delayed_work update_jeita_setting; struct mutex rw_lock; struct completion sram_access; struct power_supply bms_psy; }; Loading @@ -104,24 +172,254 @@ static char *fg_supplicants[] = { "battery" }; static int fg_read(struct fg_chip *chip, u8 *val, u16 base, int count) #define DEBUG_PRINT_BUFFER_SIZE 64 static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) { int pos = 0; int i; for (i = 0; i < buf_len; i++) { pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]); if (i < buf_len - 1) pos += scnprintf(str + pos, str_len - pos, ", "); } } static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len) { int rc = 0; struct spmi_device *spmi = chip->spmi; char str[DEBUG_PRINT_BUFFER_SIZE]; if ((addr & 0xff00) == 0) { pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return -EINVAL; } rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, len); if (rc) { pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return rc; } if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) { str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); pr_info("write(0x%04X), sid=%d, len=%d; %s\n", addr, spmi->sid, len, str); } return rc; } static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len) { int rc = 0; struct spmi_device *spmi = chip->spmi; char str[DEBUG_PRINT_BUFFER_SIZE]; if (base == 0) { if ((addr & 0xff00) == 0) { pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n", base, spmi->sid, rc); addr, spmi->sid, rc); return -EINVAL; } rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count); rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, len); if (rc) { pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base, pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return rc; } if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) { str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); pr_info("read(0x%04x), sid=%d, len=%d; %s\n", addr, spmi->sid, len, str); } return rc; } static int fg_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val, int len) { int rc; u8 reg; rc = fg_read(chip, ®, addr, len); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); return rc; } pr_debug("addr = 0x%x read 0x%x\n", addr, reg); reg &= ~mask; reg |= val & mask; pr_debug("Writing 0x%x\n", reg); rc = fg_write(chip, ®, addr, len); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); return rc; } return rc; } static inline bool fg_check_sram_access(struct fg_chip *chip) { int rc; u8 mem_if_sts; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); if (rc) { pr_err("failed to read mem status rc=%d\n", rc); return 0; } return !!(mem_if_sts & BIT(FG_MEM_AVAIL)); } #define RIF_MEM_ACCESS_REQ BIT(7) #define INTF_CTL_BURST BIT(7) #define INTF_CTL_WR_EN BIT(6) static int fg_config_access(struct fg_chip *chip, bool write, bool burst) { int rc; u8 intf_ctl = 0; intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0); rc = fg_write(chip, &intf_ctl, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set mem access bit\n"); return -EIO; } return rc; } static int fg_req_and_wait_access(struct fg_chip *chip, int timeout) { int rc = 0, ret = 0; bool tried_again = false; if (!fg_check_sram_access(chip)) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1); if (rc) { pr_err("failed to set mem access bit\n"); return -EIO; } } wait: /* Wait for MEM_AVAIL IRQ. */ ret = wait_for_completion_interruptible_timeout(&chip->sram_access, msecs_to_jiffies(timeout)); /* If we were interrupted wait again one more time. */ if (ret == -ERESTARTSYS && !tried_again) { tried_again = true; goto wait; } else if (ret <= 0) { rc = -ETIMEDOUT; pr_err("transaction timed out rc=%d\n", rc); return rc; } return rc; } static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) { int rc; rc = fg_write(chip, (u8 *) address, chip->mem_base + MEM_INTF_ADDR_LSB, 2); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", chip->mem_base + MEM_INTF_ADDR_LSB, rc); return rc; } return rc; } 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; u8 *wr_data = val; if (address < RAM_OFFSET) return -EINVAL; if (offset > 3) return -EINVAL; if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 1, (len > 4)); if (rc) goto out; rc = fg_set_ram_addr(chip, &address); if (rc) goto out; if (fg_debug_mask & FG_MEM_DEBUG_WRITES) pr_info("length %d addr=%02X\n", len, address); while (len > 0) { if (offset) rc = fg_write(chip, wr_data, chip->mem_base + MEM_INTF_WR_DATA0 + offset, (len > 4) ? 4 : len); else rc = fg_write(chip, wr_data, chip->mem_base + MEM_INTF_WR_DATA0, (len > 4) ? 4 : len); if (rc) { pr_err("spmi read failed: addr=%03x, rc=%d\n", chip->mem_base + MEM_INTF_RD_DATA0, rc); goto out; } if (offset) { wr_data += 4-offset; if (len >= 4) len -= 4-offset; else len = 0; } else { wr_data += 4; if (len >= 4) len -= 4; else len = 0; } } if (!keep_access) { 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"); rc = -EIO; } } out: mutex_unlock(&chip->rw_lock); return 0; } Loading Loading @@ -153,13 +451,14 @@ static int get_prop_capacity(struct fg_chip *chip) if (cap[0] > 0) capacity = (cap[0] * 100 / FULL_PERCENT); pr_debug("capacity: %d, raw: 0x%02x\n", capacity, cap[0]); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("capacity: %d, raw: 0x%02x\n", capacity, cap[0]); return capacity; } static int get_prop_current_now(struct fg_chip *chip) { u8 ibat_ma; s8 ibat_ma; int rc, current_now_ua; if (!chip->ibat_adc_addr) Loading @@ -173,18 +472,16 @@ static int get_prop_current_now(struct fg_chip *chip) } /* convert to uA */ current_now_ua = MA_MV_BIT_RES * (ibat_ma & IBAT_VBAT_MASK) * 1000; if (ibat_ma & MSB_SIGN) current_now_ua *= -1; current_now_ua = MA_MV_BIT_RES * ibat_ma * 1000; pr_debug("current %d uA\n", current_now_ua); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("current %d uA\n", current_now_ua); return current_now_ua; } static int get_prop_voltage_now(struct fg_chip *chip) { u8 vbat_mv; s8 vbat_mv; int rc, voltage_now_uv; if (!chip->vbat_adc_addr) Loading @@ -198,20 +495,65 @@ static int get_prop_voltage_now(struct fg_chip *chip) } /* convert to uV */ voltage_now_uv = MA_MV_BIT_RES * (vbat_mv & IBAT_VBAT_MASK) * 1000; voltage_now_uv = MA_MV_BIT_RES * vbat_mv * 1000; if (vbat_mv & MSB_SIGN) voltage_now_uv *= -1; pr_debug("voltage %d uV\n", voltage_now_uv); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("voltage %d uV\n", voltage_now_uv); return voltage_now_uv; } #define MIN_TEMP_DEGC -300 #define MAX_TEMP_DEGC 970 static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) { return settings[type].value; } static int set_prop_jeita_temp(struct fg_chip *chip, unsigned int type, int decidegc) { int rc = 0; pr_debug("addr 0x%02X, offset %d temp%d\n", settings[type].address, settings[type].offset, decidegc); settings[type].value = decidegc; cancel_delayed_work_sync( &chip->update_jeita_setting); schedule_delayed_work( &chip->update_jeita_setting, 0); return rc; } static void update_jeita_setting(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, update_jeita_setting.work); u8 reg[4]; int i, rc; for (i = 0; i < 4; i++) reg[i] = (settings[JEITA_SOFT_COLD + i].value / 10) + 30; rc = fg_mem_write(chip, reg, settings[JEITA_SOFT_COLD].address, 4, settings[JEITA_SOFT_COLD].offset, 0); if (rc) pr_err("failed to update JEITA setting rc=%d\n", rc); } static enum power_supply_property fg_power_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, }; static int fg_power_get_property(struct power_supply *psy, Loading @@ -230,6 +572,12 @@ static int fg_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: 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); break; case POWER_SUPPLY_PROP_WARM_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT); break; default: return -EINVAL; } Loading @@ -237,14 +585,110 @@ static int fg_power_get_property(struct power_supply *psy, return 0; } static int fg_power_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy); int rc = 0; switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD, val->intval); break; case POWER_SUPPLY_PROP_WARM_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT, val->intval); break; default: return -EINVAL; }; return rc; }; static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: return 1; default: break; } return 0; } static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 mem_if_sts; int rc; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); if (rc) { pr_err("failed to read mem status rc=%d\n", rc); return 0; } if (fg_check_sram_access(chip)) { if (fg_debug_mask & FG_IRQS) pr_info("sram access granted\n"); complete_all(&chip->sram_access); } else { if (fg_debug_mask & FG_IRQS) pr_info("sram access revoked\n"); INIT_COMPLETION(chip->sram_access); } if (!rc && (fg_debug_mask & FG_IRQS)) pr_info("mem_if sts 0x%02x\n", mem_if_sts); return IRQ_HANDLED; } static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; if (fg_debug_mask & FG_IRQS) pr_info("triggered\n"); power_supply_changed(&chip->bms_psy); return IRQ_HANDLED; } #define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \ do { \ if (retval) \ break; \ \ retval = of_property_read_u32(chip->spmi->dev.of_node, \ "qcom," qpnp_dt_property, \ &settings[type].value); \ \ if ((retval == -EINVAL) && optional) \ retval = 0; \ else if (retval) \ pr_err("Error reading " #qpnp_dt_property \ " property rc = %d\n", rc); \ } while (0) 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); return rc; } static int fg_init_irqs(struct fg_chip *chip) { int rc = 0; Loading Loading @@ -331,6 +775,24 @@ static int fg_init_irqs(struct fg_chip *chip) enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); break; case FG_MEMIF: chip->mem_irq[FG_MEM_AVAIL].irq = spmi_get_irq_byname( chip->spmi, spmi_resource, "mem-avail"); if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) { pr_err("Unable to get mem-avail irq\n"); return rc; } rc |= devm_request_irq(chip->dev, chip->mem_irq[FG_MEM_AVAIL].irq, fg_mem_avail_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mem-avail", chip); if (rc < 0) { pr_err("Can't request %d mem-avail: %d\n", chip->mem_irq[FG_MEM_AVAIL].irq, rc); return rc; } break; case FG_BATT: case FG_ADC: break; Loading @@ -347,6 +809,8 @@ static int fg_remove(struct spmi_device *spmi) { struct fg_chip *chip = dev_get_drvdata(&spmi->dev); mutex_destroy(&chip->rw_lock); cancel_delayed_work_sync(&chip->update_jeita_setting); power_supply_unregister(&chip->bms_psy); dev_set_drvdata(&spmi->dev, NULL); return 0; Loading @@ -354,7 +818,7 @@ static int fg_remove(struct spmi_device *spmi) static int fg_probe(struct spmi_device *spmi) { struct device *dev = &spmi->dev; struct device *dev = &(spmi->dev); struct fg_chip *chip; struct spmi_resource *spmi_resource; struct resource *resource; Loading @@ -366,8 +830,13 @@ static int fg_probe(struct spmi_device *spmi) return -ENODEV; } if (!spmi) { pr_err("no valid spmi pointer\n"); return -ENODEV; } chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL); if (!chip) { if (chip == NULL) { pr_err("Can't allocate fg_chip\n"); return -ENOMEM; } Loading @@ -375,11 +844,22 @@ static int fg_probe(struct spmi_device *spmi) chip->spmi = spmi; chip->dev = &(spmi->dev); mutex_init(&chip->rw_lock); INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); 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"); rc = -ENXIO; return rc; goto of_init_fail; } resource = spmi_get_resource(spmi, spmi_resource, Loading @@ -388,7 +868,7 @@ static int fg_probe(struct spmi_device *spmi) pr_err("node %s IO resource absent!\n", spmi->dev.of_node->full_name); rc = -ENXIO; return rc; goto of_init_fail; } if (strcmp("qcom,fg-adc-vbat", Loading @@ -405,13 +885,16 @@ static int fg_probe(struct spmi_device *spmi) resource->start + REG_OFFSET_PERP_SUBTYPE, 1); if (rc) { pr_err("Peripheral subtype read failed rc=%d\n", rc); return rc; goto of_init_fail; } switch (subtype) { case FG_SOC: chip->soc_base = resource->start; break; case FG_MEMIF: chip->mem_base = resource->start; break; default: pr_err("Invalid peripheral subtype=0x%x\n", subtype); rc = -EINVAL; Loading @@ -423,13 +906,15 @@ static int fg_probe(struct spmi_device *spmi) chip->bms_psy.properties = fg_power_props; chip->bms_psy.num_properties = ARRAY_SIZE(fg_power_props); chip->bms_psy.get_property = fg_power_get_property; chip->bms_psy.set_property = fg_power_set_property; chip->bms_psy.supplied_to = fg_supplicants; chip->bms_psy.num_supplicants = ARRAY_SIZE(fg_supplicants); chip->bms_psy.property_is_writeable = fg_property_is_writeable; rc = power_supply_register(chip->dev, &chip->bms_psy); if (rc < 0) { pr_err("batt failed to register rc = %d\n", rc); return rc; goto of_init_fail; } rc = fg_init_irqs(chip); Loading @@ -444,6 +929,8 @@ static int fg_probe(struct spmi_device *spmi) power_supply_unregister: power_supply_unregister(&chip->bms_psy); of_init_fail: mutex_destroy(&chip->rw_lock); return rc; } Loading Loading
Documentation/devicetree/bindings/power/qpnp-fg.txt +24 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,12 @@ Optionally ADC nodes can be added Parent node required properties: - compatible : should be "qcom,qpnp-fg" for the FG driver. Parent node optional properties: - qcom,warm-bat-decidegc: Warm battery temperature in decidegC. - 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,fg-soc node required properties: - reg : offset and length of the PMIC peripheral register map. - interrupts : the interrupt mappings. Loading @@ -40,6 +46,15 @@ qcom,fg-soc node required properties: 6: sw-fallbk-ocv 7: sw-fallbk-new-battrt-sts qcom,fg-memif node required properties: - reg : offset and length of the PMIC peripheral register map. - interrupts : the interrupt mappings. The format should be <slave-id peripheral-id interrupt-number>. - interrupt-names : names for the mapped fg adc interrupts The following interrupts are required: 0: mem-avail Example: pmi8994_fg: qcom,fg { spmi-dev-container; Loading Loading @@ -77,4 +92,13 @@ pmi8994_fg: qcom,fg { qcom,fg-adc-ibat@4255 { reg = <0x4255 0x1>; }; qcom,fg-memif@4400 { reg = <0x4400 0x100>; interrupts = <0x2 0x44 0x0>, <0x2 0x44 0x1>; interrupt-names = "mem-avail", "data-rcvry-sug"; }; };
arch/arm/boot/dts/qcom/msm-pmi8994.dtsi +10 −0 Original line number Diff line number Diff line Loading @@ -274,6 +274,16 @@ qcom,fg-adc-ibat@4255 { reg = <0x4255 0x1>; }; qcom,fg-memif@4400 { status = "okay"; reg = <0x4400 0x100>; interrupts = <0x2 0x44 0x0>, <0x2 0x44 0x1>; interrupt-names = "mem-avail", "data-rcvry-sug"; }; }; }; Loading
drivers/power/qpnp-fg.c +510 −23 Original line number Diff line number Diff line Loading @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "FG: %s: " fmt, __func__ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/of.h> #include <linux/err.h> Loading @@ -25,10 +26,30 @@ #include <linux/power_supply.h> /* Register offsets */ /* Interrupt offsets */ #define INT_RT_STS(base) (base + 0x10) /* SPMI Register offsets */ #define SOC_MONOTONIC_SOC 0x09 #define MEM_INTF_CFG 0x40 #define MEM_INTF_CTL 0x41 #define MEM_INTF_ADDR_LSB 0x42 #define MEM_INTF_ADDR_MSB 0x43 #define MEM_INTF_WR_DATA0 0x48 #define MEM_INTF_WR_DATA1 0x49 #define MEM_INTF_WR_DATA2 0x4A #define MEM_INTF_WR_DATA3 0x4B #define MEM_INTF_RD_DATA0 0x4C #define MEM_INTF_RD_DATA1 0x4D #define MEM_INTF_RD_DATA2 0x4E #define MEM_INTF_RD_DATA3 0x4F #define REG_OFFSET_PERP_SUBTYPE 0x05 /* RAM register offsets */ #define RAM_OFFSET 0x400 /* Bit/Mask definitions */ #define FULL_PERCENT 0xFF #define MAX_TRIES_SOC 5 Loading @@ -43,6 +64,52 @@ #define FG_MEMIF 0xC #define QPNP_FG_DEV_NAME "qcom,qpnp-fg" #define MEM_IF_TIMEOUT_MS 2200 /* Debug Flag Definitions */ enum { FG_SPMI_DEBUG_WRITES = BIT(0), /* Show SPMI writes */ FG_SPMI_DEBUG_READS = BIT(1), /* Show SPMI reads */ FG_IRQS = BIT(2), /* Show interrupts */ FG_MEM_DEBUG_WRITES = BIT(3), /* Show SRAM writes */ FG_MEM_DEBUG_READS = BIT(4), /* Show SRAM reads */ FG_POWER_SUPPLY = BIT(5), /* Show POWER_SUPPLY */ }; struct fg_mem_setting { u16 address; u8 offset; int value; }; /* 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_SETTING_MAX, }; #define SETTING(_idx, _address, _offset, _value) \ [FG_MEM_##_idx] = { \ .address = _address, \ .offset = _offset, \ .value = _value, \ } \ 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), }; static int fg_debug_mask; module_param_named( debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR ); struct fg_irq { int irq; Loading Loading @@ -81,7 +148,6 @@ enum fg_mem_if_irq { struct fg_chip { struct device *dev; struct dentry *dent; struct spmi_device *spmi; u16 soc_base; u16 batt_base; Loading @@ -91,7 +157,9 @@ struct fg_chip { 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 fg_mem_if_chip *memif; struct delayed_work update_jeita_setting; struct mutex rw_lock; struct completion sram_access; struct power_supply bms_psy; }; Loading @@ -104,24 +172,254 @@ static char *fg_supplicants[] = { "battery" }; static int fg_read(struct fg_chip *chip, u8 *val, u16 base, int count) #define DEBUG_PRINT_BUFFER_SIZE 64 static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len) { int pos = 0; int i; for (i = 0; i < buf_len; i++) { pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]); if (i < buf_len - 1) pos += scnprintf(str + pos, str_len - pos, ", "); } } static int fg_write(struct fg_chip *chip, u8 *val, u16 addr, int len) { int rc = 0; struct spmi_device *spmi = chip->spmi; char str[DEBUG_PRINT_BUFFER_SIZE]; if ((addr & 0xff00) == 0) { pr_err("addr cannot be zero base=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return -EINVAL; } rc = spmi_ext_register_writel(spmi->ctrl, spmi->sid, addr, val, len); if (rc) { pr_err("write failed addr=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return rc; } if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_WRITES)) { str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); pr_info("write(0x%04X), sid=%d, len=%d; %s\n", addr, spmi->sid, len, str); } return rc; } static int fg_read(struct fg_chip *chip, u8 *val, u16 addr, int len) { int rc = 0; struct spmi_device *spmi = chip->spmi; char str[DEBUG_PRINT_BUFFER_SIZE]; if (base == 0) { if ((addr & 0xff00) == 0) { pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n", base, spmi->sid, rc); addr, spmi->sid, rc); return -EINVAL; } rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, base, val, count); rc = spmi_ext_register_readl(spmi->ctrl, spmi->sid, addr, val, len); if (rc) { pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", base, pr_err("SPMI read failed base=0x%02x sid=0x%02x rc=%d\n", addr, spmi->sid, rc); return rc; } if (!rc && (fg_debug_mask & FG_SPMI_DEBUG_READS)) { str[0] = '\0'; fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, len); pr_info("read(0x%04x), sid=%d, len=%d; %s\n", addr, spmi->sid, len, str); } return rc; } static int fg_masked_write(struct fg_chip *chip, u16 addr, u8 mask, u8 val, int len) { int rc; u8 reg; rc = fg_read(chip, ®, addr, len); if (rc) { pr_err("spmi read failed: addr=%03X, rc=%d\n", addr, rc); return rc; } pr_debug("addr = 0x%x read 0x%x\n", addr, reg); reg &= ~mask; reg |= val & mask; pr_debug("Writing 0x%x\n", reg); rc = fg_write(chip, ®, addr, len); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", addr, rc); return rc; } return rc; } static inline bool fg_check_sram_access(struct fg_chip *chip) { int rc; u8 mem_if_sts; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); if (rc) { pr_err("failed to read mem status rc=%d\n", rc); return 0; } return !!(mem_if_sts & BIT(FG_MEM_AVAIL)); } #define RIF_MEM_ACCESS_REQ BIT(7) #define INTF_CTL_BURST BIT(7) #define INTF_CTL_WR_EN BIT(6) static int fg_config_access(struct fg_chip *chip, bool write, bool burst) { int rc; u8 intf_ctl = 0; intf_ctl = (write ? INTF_CTL_WR_EN : 0) | (burst ? INTF_CTL_BURST : 0); rc = fg_write(chip, &intf_ctl, chip->mem_base + MEM_INTF_CTL, 1); if (rc) { pr_err("failed to set mem access bit\n"); return -EIO; } return rc; } static int fg_req_and_wait_access(struct fg_chip *chip, int timeout) { int rc = 0, ret = 0; bool tried_again = false; if (!fg_check_sram_access(chip)) { rc = fg_masked_write(chip, chip->mem_base + MEM_INTF_CFG, RIF_MEM_ACCESS_REQ, RIF_MEM_ACCESS_REQ, 1); if (rc) { pr_err("failed to set mem access bit\n"); return -EIO; } } wait: /* Wait for MEM_AVAIL IRQ. */ ret = wait_for_completion_interruptible_timeout(&chip->sram_access, msecs_to_jiffies(timeout)); /* If we were interrupted wait again one more time. */ if (ret == -ERESTARTSYS && !tried_again) { tried_again = true; goto wait; } else if (ret <= 0) { rc = -ETIMEDOUT; pr_err("transaction timed out rc=%d\n", rc); return rc; } return rc; } static int fg_set_ram_addr(struct fg_chip *chip, u16 *address) { int rc; rc = fg_write(chip, (u8 *) address, chip->mem_base + MEM_INTF_ADDR_LSB, 2); if (rc) { pr_err("spmi write failed: addr=%03X, rc=%d\n", chip->mem_base + MEM_INTF_ADDR_LSB, rc); return rc; } return rc; } 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; u8 *wr_data = val; if (address < RAM_OFFSET) return -EINVAL; if (offset > 3) return -EINVAL; if (!fg_check_sram_access(chip)) { rc = fg_req_and_wait_access(chip, MEM_IF_TIMEOUT_MS); if (rc) return rc; } mutex_lock(&chip->rw_lock); rc = fg_config_access(chip, 1, (len > 4)); if (rc) goto out; rc = fg_set_ram_addr(chip, &address); if (rc) goto out; if (fg_debug_mask & FG_MEM_DEBUG_WRITES) pr_info("length %d addr=%02X\n", len, address); while (len > 0) { if (offset) rc = fg_write(chip, wr_data, chip->mem_base + MEM_INTF_WR_DATA0 + offset, (len > 4) ? 4 : len); else rc = fg_write(chip, wr_data, chip->mem_base + MEM_INTF_WR_DATA0, (len > 4) ? 4 : len); if (rc) { pr_err("spmi read failed: addr=%03x, rc=%d\n", chip->mem_base + MEM_INTF_RD_DATA0, rc); goto out; } if (offset) { wr_data += 4-offset; if (len >= 4) len -= 4-offset; else len = 0; } else { wr_data += 4; if (len >= 4) len -= 4; else len = 0; } } if (!keep_access) { 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"); rc = -EIO; } } out: mutex_unlock(&chip->rw_lock); return 0; } Loading Loading @@ -153,13 +451,14 @@ static int get_prop_capacity(struct fg_chip *chip) if (cap[0] > 0) capacity = (cap[0] * 100 / FULL_PERCENT); pr_debug("capacity: %d, raw: 0x%02x\n", capacity, cap[0]); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("capacity: %d, raw: 0x%02x\n", capacity, cap[0]); return capacity; } static int get_prop_current_now(struct fg_chip *chip) { u8 ibat_ma; s8 ibat_ma; int rc, current_now_ua; if (!chip->ibat_adc_addr) Loading @@ -173,18 +472,16 @@ static int get_prop_current_now(struct fg_chip *chip) } /* convert to uA */ current_now_ua = MA_MV_BIT_RES * (ibat_ma & IBAT_VBAT_MASK) * 1000; if (ibat_ma & MSB_SIGN) current_now_ua *= -1; current_now_ua = MA_MV_BIT_RES * ibat_ma * 1000; pr_debug("current %d uA\n", current_now_ua); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("current %d uA\n", current_now_ua); return current_now_ua; } static int get_prop_voltage_now(struct fg_chip *chip) { u8 vbat_mv; s8 vbat_mv; int rc, voltage_now_uv; if (!chip->vbat_adc_addr) Loading @@ -198,20 +495,65 @@ static int get_prop_voltage_now(struct fg_chip *chip) } /* convert to uV */ voltage_now_uv = MA_MV_BIT_RES * (vbat_mv & IBAT_VBAT_MASK) * 1000; voltage_now_uv = MA_MV_BIT_RES * vbat_mv * 1000; if (vbat_mv & MSB_SIGN) voltage_now_uv *= -1; pr_debug("voltage %d uV\n", voltage_now_uv); if (fg_debug_mask & FG_POWER_SUPPLY) pr_info("voltage %d uV\n", voltage_now_uv); return voltage_now_uv; } #define MIN_TEMP_DEGC -300 #define MAX_TEMP_DEGC 970 static int get_prop_jeita_temp(struct fg_chip *chip, unsigned int type) { return settings[type].value; } static int set_prop_jeita_temp(struct fg_chip *chip, unsigned int type, int decidegc) { int rc = 0; pr_debug("addr 0x%02X, offset %d temp%d\n", settings[type].address, settings[type].offset, decidegc); settings[type].value = decidegc; cancel_delayed_work_sync( &chip->update_jeita_setting); schedule_delayed_work( &chip->update_jeita_setting, 0); return rc; } static void update_jeita_setting(struct work_struct *work) { struct fg_chip *chip = container_of(work, struct fg_chip, update_jeita_setting.work); u8 reg[4]; int i, rc; for (i = 0; i < 4; i++) reg[i] = (settings[JEITA_SOFT_COLD + i].value / 10) + 30; rc = fg_mem_write(chip, reg, settings[JEITA_SOFT_COLD].address, 4, settings[JEITA_SOFT_COLD].offset, 0); if (rc) pr_err("failed to update JEITA setting rc=%d\n", rc); } static enum power_supply_property fg_power_props[] = { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_COOL_TEMP, POWER_SUPPLY_PROP_WARM_TEMP, }; static int fg_power_get_property(struct power_supply *psy, Loading @@ -230,6 +572,12 @@ static int fg_power_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_NOW: 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); break; case POWER_SUPPLY_PROP_WARM_TEMP: val->intval = get_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT); break; default: return -EINVAL; } Loading @@ -237,14 +585,110 @@ static int fg_power_get_property(struct power_supply *psy, return 0; } static int fg_power_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy); int rc = 0; switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_COLD, val->intval); break; case POWER_SUPPLY_PROP_WARM_TEMP: rc = set_prop_jeita_temp(chip, FG_MEM_JEITA_SOFT_HOT, val->intval); break; default: return -EINVAL; }; return rc; }; static int fg_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_COOL_TEMP: case POWER_SUPPLY_PROP_WARM_TEMP: return 1; default: break; } return 0; } static irqreturn_t fg_mem_avail_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; u8 mem_if_sts; int rc; rc = fg_read(chip, &mem_if_sts, INT_RT_STS(chip->mem_base), 1); if (rc) { pr_err("failed to read mem status rc=%d\n", rc); return 0; } if (fg_check_sram_access(chip)) { if (fg_debug_mask & FG_IRQS) pr_info("sram access granted\n"); complete_all(&chip->sram_access); } else { if (fg_debug_mask & FG_IRQS) pr_info("sram access revoked\n"); INIT_COMPLETION(chip->sram_access); } if (!rc && (fg_debug_mask & FG_IRQS)) pr_info("mem_if sts 0x%02x\n", mem_if_sts); return IRQ_HANDLED; } static irqreturn_t fg_soc_irq_handler(int irq, void *_chip) { struct fg_chip *chip = _chip; if (fg_debug_mask & FG_IRQS) pr_info("triggered\n"); power_supply_changed(&chip->bms_psy); return IRQ_HANDLED; } #define OF_READ_SETTING(type, qpnp_dt_property, retval, optional) \ do { \ if (retval) \ break; \ \ retval = of_property_read_u32(chip->spmi->dev.of_node, \ "qcom," qpnp_dt_property, \ &settings[type].value); \ \ if ((retval == -EINVAL) && optional) \ retval = 0; \ else if (retval) \ pr_err("Error reading " #qpnp_dt_property \ " property rc = %d\n", rc); \ } while (0) 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); return rc; } static int fg_init_irqs(struct fg_chip *chip) { int rc = 0; Loading Loading @@ -331,6 +775,24 @@ static int fg_init_irqs(struct fg_chip *chip) enable_irq_wake(chip->soc_irq[EMPTY_SOC].irq); break; case FG_MEMIF: chip->mem_irq[FG_MEM_AVAIL].irq = spmi_get_irq_byname( chip->spmi, spmi_resource, "mem-avail"); if (chip->mem_irq[FG_MEM_AVAIL].irq < 0) { pr_err("Unable to get mem-avail irq\n"); return rc; } rc |= devm_request_irq(chip->dev, chip->mem_irq[FG_MEM_AVAIL].irq, fg_mem_avail_irq_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "mem-avail", chip); if (rc < 0) { pr_err("Can't request %d mem-avail: %d\n", chip->mem_irq[FG_MEM_AVAIL].irq, rc); return rc; } break; case FG_BATT: case FG_ADC: break; Loading @@ -347,6 +809,8 @@ static int fg_remove(struct spmi_device *spmi) { struct fg_chip *chip = dev_get_drvdata(&spmi->dev); mutex_destroy(&chip->rw_lock); cancel_delayed_work_sync(&chip->update_jeita_setting); power_supply_unregister(&chip->bms_psy); dev_set_drvdata(&spmi->dev, NULL); return 0; Loading @@ -354,7 +818,7 @@ static int fg_remove(struct spmi_device *spmi) static int fg_probe(struct spmi_device *spmi) { struct device *dev = &spmi->dev; struct device *dev = &(spmi->dev); struct fg_chip *chip; struct spmi_resource *spmi_resource; struct resource *resource; Loading @@ -366,8 +830,13 @@ static int fg_probe(struct spmi_device *spmi) return -ENODEV; } if (!spmi) { pr_err("no valid spmi pointer\n"); return -ENODEV; } chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL); if (!chip) { if (chip == NULL) { pr_err("Can't allocate fg_chip\n"); return -ENOMEM; } Loading @@ -375,11 +844,22 @@ static int fg_probe(struct spmi_device *spmi) chip->spmi = spmi; chip->dev = &(spmi->dev); mutex_init(&chip->rw_lock); INIT_DELAYED_WORK(&chip->update_jeita_setting, update_jeita_setting); 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"); rc = -ENXIO; return rc; goto of_init_fail; } resource = spmi_get_resource(spmi, spmi_resource, Loading @@ -388,7 +868,7 @@ static int fg_probe(struct spmi_device *spmi) pr_err("node %s IO resource absent!\n", spmi->dev.of_node->full_name); rc = -ENXIO; return rc; goto of_init_fail; } if (strcmp("qcom,fg-adc-vbat", Loading @@ -405,13 +885,16 @@ static int fg_probe(struct spmi_device *spmi) resource->start + REG_OFFSET_PERP_SUBTYPE, 1); if (rc) { pr_err("Peripheral subtype read failed rc=%d\n", rc); return rc; goto of_init_fail; } switch (subtype) { case FG_SOC: chip->soc_base = resource->start; break; case FG_MEMIF: chip->mem_base = resource->start; break; default: pr_err("Invalid peripheral subtype=0x%x\n", subtype); rc = -EINVAL; Loading @@ -423,13 +906,15 @@ static int fg_probe(struct spmi_device *spmi) chip->bms_psy.properties = fg_power_props; chip->bms_psy.num_properties = ARRAY_SIZE(fg_power_props); chip->bms_psy.get_property = fg_power_get_property; chip->bms_psy.set_property = fg_power_set_property; chip->bms_psy.supplied_to = fg_supplicants; chip->bms_psy.num_supplicants = ARRAY_SIZE(fg_supplicants); chip->bms_psy.property_is_writeable = fg_property_is_writeable; rc = power_supply_register(chip->dev, &chip->bms_psy); if (rc < 0) { pr_err("batt failed to register rc = %d\n", rc); return rc; goto of_init_fail; } rc = fg_init_irqs(chip); Loading @@ -444,6 +929,8 @@ static int fg_probe(struct spmi_device *spmi) power_supply_unregister: power_supply_unregister(&chip->bms_psy); of_init_fail: mutex_destroy(&chip->rw_lock); return rc; } Loading