blob: 435024da4522ca5784246265d11db678ae546e62 [file] [log] [blame]
/* Copyright (c) 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
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "FG: %s: " fmt, __func__
#include <linux/atomic.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/init.h>
#include <linux/spmi.h>
#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/power_supply.h>
#include <linux/string_helpers.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 OTP_CFG1 0xE2
#define SOC_BOOT_MOD 0x50
#define SOC_RESTART 0x51
#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
#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
#define FG_BATT 0xA
#define FG_ADC 0xB
#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;
};
struct fg_mem_data {
u16 address;
u8 offset;
unsigned int len;
int value;
};
/* FG_MEMIF setting index */
enum fg_mem_setting_index {
FG_MEM_SOFT_COLD = 0,
FG_MEM_SOFT_HOT,
FG_MEM_HARD_COLD,
FG_MEM_HARD_HOT,
FG_MEM_SETTING_MAX,
};
/* FG_MEMIF data index */
enum fg_mem_data_index {
FG_DATA_BATT_TEMP = 0,
FG_DATA_OCV,
FG_DATA_VOLTAGE,
FG_DATA_CURRENT,
FG_DATA_BATT_ESR,
FG_DATA_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(SOFT_COLD, 0x454, 0, 100),
SETTING(SOFT_HOT, 0x454, 1, 400),
SETTING(HARD_COLD, 0x454, 2, 50),
SETTING(HARD_HOT, 0x454, 3, 450),
};
#define DATA(_idx, _address, _offset, _length, _value) \
[FG_DATA_##_idx] = { \
.address = _address, \
.offset = _offset, \
.len = _length, \
.value = _value, \
} \
static struct fg_mem_data fg_data[FG_DATA_MAX] = {
/* ID Address, Offset, Length, Value*/
DATA(BATT_TEMP, 0x550, 2, 2, -EINVAL),
DATA(OCV, 0x588, 3, 2, -EINVAL),
DATA(VOLTAGE, 0x5CC, 1, 2, -EINVAL),
DATA(CURRENT, 0x5CC, 3, 2, -EINVAL),
DATA(BATT_ESR, 0x554, 2, 2, -EINVAL),
};
static int fg_debug_mask;
module_param_named(
debug_mask, fg_debug_mask, int, S_IRUSR | S_IWUSR
);
struct fg_irq {
int irq;
unsigned long disabled;
};
enum fg_soc_irq {
HIGH_SOC,
LOW_SOC,
FULL_SOC,
EMPTY_SOC,
DELTA_SOC,
FIRST_EST_DONE,
SW_FALLBK_OCV,
SW_FALLBK_NEW_BATTRT_STS,
FG_SOC_IRQ_COUNT,
};
enum fg_batt_irq {
JEITA_SOFT_COLD,
JEITA_SOFT_HOT,
VBATT_LOW,
BATT_IDENTIFIED,
BATT_ID_REQ,
BATT_UNKNOWN,
BATT_MISSING,
BATT_MATCH,
FG_BATT_IRQ_COUNT,
};
enum fg_mem_if_irq {
FG_MEM_AVAIL,
TA_RCVRY_SUG,
FG_MEM_IF_IRQ_COUNT,
};
struct fg_chip {
struct device *dev;
struct spmi_device *spmi;
u16 soc_base;
u16 batt_base;
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;
struct delayed_work update_sram_data;
char *batt_profile;
unsigned int batt_profile_len;
};
/* FG_MEMIF DEBUGFS structures */
#define ADDR_LEN 4 /* 3 byte address + 1 space character */
#define CHARS_PER_ITEM 3 /* Format is 'XX ' */
#define ITEMS_PER_LINE 4 /* 4 data items per line */
#define MAX_LINE_LENGTH (ADDR_LEN + (ITEMS_PER_LINE * CHARS_PER_ITEM) + 1)
#define MAX_REG_PER_TRANSACTION (8)
static const char *DFS_ROOT_NAME = "fg_memif";
static const mode_t DFS_MODE = S_IRUSR | S_IWUSR;
/* Log buffer */
struct fg_log_buffer {
size_t rpos; /* Current 'read' position in buffer */
size_t wpos; /* Current 'write' position in buffer */
size_t len; /* Length of the buffer */
char data[0]; /* Log buffer */
};
/* transaction parameters */
struct fg_trans {
u32 cnt; /* Number of bytes to read */
u16 addr; /* 12-bit address in SRAM */
u32 offset; /* Offset of last read data + byte offset */
struct fg_chip *chip;
struct fg_log_buffer *log; /* log buffer */
};
struct fg_dbgfs {
u32 cnt;
u32 addr;
struct fg_chip *chip;
struct dentry *root;
struct mutex lock;
struct debugfs_blob_wrapper help_msg;
};
static struct fg_dbgfs dbgfs_data = {
.lock = __MUTEX_INITIALIZER(dbgfs_data.lock),
.help_msg = {
.data =
"FG Debug-FS support\n"
"\n"
"Hierarchy schema:\n"
"/sys/kernel/debug/fg_memif\n"
" /help -- Static help text\n"
" /address -- Starting register address for reads or writes\n"
" /count -- Number of registers to read (only used for reads)\n"
" /data -- Initiates the SRAM read (formatted output)\n"
"\n",
},
};
static const struct of_device_id fg_match_table[] = {
{ .compatible = QPNP_FG_DEV_NAME, },
{}
};
static char *fg_supplicants[] = {
"battery"
};
#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 ((addr & 0xff00) == 0) {
pr_err("base cannot be zero base=0x%02x sid=0x%02x rc=%d\n",
addr, spmi->sid, rc);
return -EINVAL;
}
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", 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, &reg, 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, &reg, 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, bool otp)
{
int rc;
u8 intf_ctl = 0;
if (otp) {
/* Configure OTP access */
rc = fg_masked_write(chip, chip->mem_base + OTP_CFG1,
0xFF, 0x00, 1);
if (rc) {
pr_err("failed to set OTP cfg\n");
return -EIO;
}
}
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;
}
#define BUF_LEN 4
static int fg_mem_read(struct fg_chip *chip, u8 *val, u16 address, int len,
int offset, bool keep_access)
{
int rc = 0, user_cnt = 0, total_len = 0;
u8 *rd_data = val;
bool otp;
char str[DEBUG_PRINT_BUFFER_SIZE];
if (address < RAM_OFFSET)
otp = 1;
if (offset > 3) {
pr_err("offset too large %d\n", offset);
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)
goto out;
}
rc = fg_config_access(chip, 0, (len > 4), otp);
if (rc)
goto out;
rc = fg_set_ram_addr(chip, &address);
if (rc)
goto out;
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("length %d addr=%02X keep_access %d\n",
len, address, keep_access);
total_len = len;
while (len > 0) {
if (!offset) {
rc = fg_read(chip, rd_data,
chip->mem_base + MEM_INTF_RD_DATA0,
(len > BUF_LEN) ? BUF_LEN : len);
} else {
rc = fg_read(chip, rd_data,
chip->mem_base + MEM_INTF_RD_DATA0 + offset,
BUF_LEN - offset);
/* manually set address to allow continous reads */
address += BUF_LEN;
rc = fg_set_ram_addr(chip, &address);
if (rc)
goto out;
}
if (rc) {
pr_err("spmi read failed: addr=%03x, rc=%d\n",
chip->mem_base + MEM_INTF_RD_DATA0, rc);
goto out;
}
rd_data += (BUF_LEN - offset);
len -= (BUF_LEN - offset);
offset = 0;
}
if (fg_debug_mask & FG_MEM_DEBUG_READS) {
fill_string(str, DEBUG_PRINT_BUFFER_SIZE, val, total_len);
pr_info("data: %s\n", str);
}
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");
}
mutex_unlock(&chip->rw_lock);
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, user_cnt = 0;
u8 *wr_data = val;
if (address < RAM_OFFSET)
return -EINVAL;
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)
goto out;
}
rc = fg_config_access(chip, 1, (len > 4), 0);
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;
}
}
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");
rc = -EIO;
}
}
mutex_unlock(&chip->rw_lock);
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, 0, 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);
if (rc) {
pr_err("spmi read failed: addr=%03x, rc=%d\n",
chip->soc_base + SOC_MONOTONIC_SOC, rc);
return rc;
}
if (cap[0] == cap[1])
break;
tries++;
}
if (tries == MAX_TRIES_SOC) {
pr_err("shadow registers do not match\n");
return -EINVAL;
}
if (cap[0] > 0)
capacity = (cap[0] * 100 / FULL_PERCENT);
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("capacity: %d, raw: 0x%02x\n", capacity, cap[0]);
return capacity;
}
#define DEFAULT_TEMP_DEGC 250
#define SRAM_DATA_DELAY_MS 1000
static int get_sram_prop_now(struct fg_chip *chip, unsigned int type)
{
if (fg_debug_mask & FG_POWER_SUPPLY)
pr_info("addr 0x%02X, offset %d value %d\n",
fg_data[type].address, fg_data[type].offset,
fg_data[type].value);
cancel_delayed_work_sync(
&chip->update_sram_data);
schedule_delayed_work(
&chip->update_sram_data, msecs_to_jiffies(SRAM_DATA_DELAY_MS));
return fg_data[type].value;
}
#define MIN_TEMP_DEGC -300
#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;
}
static int set_prop_jeita_temp(struct fg_chip *chip,
unsigned int type, int decidegc)
{
int rc = 0;
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;
cancel_delayed_work_sync(
&chip->update_jeita_setting);
schedule_delayed_work(
&chip->update_jeita_setting, 0);
return rc;
}
#define EXPONENT_MASK 0xF800
#define MANTISSA_MASK 0x3FF
#define SIGN BIT(10)
#define EXPONENT_SHIFT 11
#define MICRO_UNIT 1000000ULL
static int64_t float_decode(u16 reg)
{
int64_t final_val, exponent_val, mantissa_val;
int exponent, mantissa, n;
bool sign;
exponent = (reg & EXPONENT_MASK) >> EXPONENT_SHIFT;
mantissa = (reg & MANTISSA_MASK);
sign = !!(reg & SIGN);
pr_debug("exponent=%d mantissa=%d sign=%d\n", exponent, mantissa, sign);
mantissa_val = mantissa * MICRO_UNIT;
n = exponent - 15;
if (n < 0)
exponent_val = MICRO_UNIT >> -n;
else
exponent_val = MICRO_UNIT << n;
n = n - 10;
if (n < 0)
mantissa_val >>= -n;
else
mantissa_val <<= n;
final_val = exponent_val + mantissa_val;
if (sign)
final_val *= -1;
return final_val;
}
#define LSB_16B 153
#define TEMP_LSB_16B 625
#define DECIKELVIN 2730
#define SRAM_PERIOD_UPDATE_MS 30000
static void update_sram_data(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip,
update_sram_data.work);
int i, rc = 0;
u8 reg[2];
s16 temp;
for (i = 0; i < FG_DATA_MAX; i++) {
rc = fg_mem_read(chip, reg, fg_data[i].address,
fg_data[i].len, fg_data[i].offset,
(i+1 == FG_DATA_MAX) ? 0 : 1);
if (rc) {
pr_err("Failed ro update sram data\n");
break;
}
temp = reg[0] | (reg[1] << 8);
switch (i) {
case FG_DATA_BATT_TEMP:
fg_data[i].value = (temp * TEMP_LSB_16B / 1000)
- DECIKELVIN;
break;
case FG_DATA_OCV:
case FG_DATA_VOLTAGE:
fg_data[i].value = ((u16) temp) * LSB_16B;
break;
case FG_DATA_CURRENT:
fg_data[i].value = temp * LSB_16B;
break;
case FG_DATA_BATT_ESR:
fg_data[i].value = float_decode((u16) temp);
};
if (fg_debug_mask & FG_MEM_DEBUG_READS)
pr_info("%d %d %d\n", i, temp, fg_data[i].value);
}
schedule_delayed_work(
&chip->update_sram_data,
msecs_to_jiffies(SRAM_PERIOD_UPDATE_MS));
}
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[FG_MEM_SOFT_COLD + i].value / 10) + 30;
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);
}
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_VOLTAGE_OCV,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_COOL_TEMP,
POWER_SUPPLY_PROP_WARM_TEMP,
POWER_SUPPLY_PROP_RESISTANCE,
};
static int fg_power_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct fg_chip *chip = container_of(psy, struct fg_chip, bms_psy);
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_capacity(chip);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
break;
case POWER_SUPPLY_PROP_VOLTAGE_OCV:
val->intval = get_sram_prop_now(chip, FG_DATA_OCV);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_TEMP);
break;
case POWER_SUPPLY_PROP_COOL_TEMP:
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_SOFT_HOT);
break;
case POWER_SUPPLY_PROP_RESISTANCE:
val->intval = get_sram_prop_now(chip, FG_DATA_BATT_ESR);
break;
default:
return -EINVAL;
}
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_SOFT_COLD, val->intval);
break;
case POWER_SUPPLY_PROP_WARM_TEMP:
rc = set_prop_jeita_temp(chip,
FG_MEM_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, reg;
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");
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)
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;
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;
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)
#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, &reg, 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, &reg, 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, &reg, 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, &reg, 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_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;
}
static int fg_init_irqs(struct fg_chip *chip)
{
int rc = 0;
struct resource *resource;
struct spmi_resource *spmi_resource;
u8 subtype;
struct spmi_device *spmi = chip->spmi;
spmi_for_each_container_dev(spmi_resource, spmi) {
if (!spmi_resource) {
pr_err("fg: spmi resource absent\n");
return rc;
}
resource = spmi_get_resource(spmi, spmi_resource,
IORESOURCE_MEM, 0);
if (!(resource && resource->start)) {
pr_err("node %s IO resource absent!\n",
spmi->dev.of_node->full_name);
return rc;
}
if ((resource->start == chip->vbat_adc_addr) ||
(resource->start == chip->ibat_adc_addr))
continue;
rc = fg_read(chip, &subtype,
resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
if (rc) {
pr_err("Peripheral subtype read failed rc=%d\n", rc);
return rc;
}
switch (subtype) {
case FG_SOC:
chip->soc_irq[FULL_SOC].irq = spmi_get_irq_byname(
chip->spmi, spmi_resource, "full-soc");
if (chip->soc_irq[FULL_SOC].irq < 0) {
pr_err("Unable to get full-soc irq\n");
return rc;
}
chip->soc_irq[EMPTY_SOC].irq = spmi_get_irq_byname(
chip->spmi, spmi_resource, "empty-soc");
if (chip->soc_irq[EMPTY_SOC].irq < 0) {
pr_err("Unable to get low-soc irq\n");
return rc;
}
chip->soc_irq[DELTA_SOC].irq = spmi_get_irq_byname(
chip->spmi, spmi_resource, "delta-soc");
if (chip->soc_irq[DELTA_SOC].irq < 0) {
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,
fg_soc_irq_handler, IRQF_TRIGGER_RISING,
"full-soc", chip);
if (rc < 0) {
pr_err("Can't request %d full-soc: %d\n",
chip->soc_irq[FULL_SOC].irq, rc);
return rc;
}
rc |= devm_request_irq(chip->dev,
chip->soc_irq[EMPTY_SOC].irq,
fg_soc_irq_handler, IRQF_TRIGGER_RISING,
"empty-soc", chip);
if (rc < 0) {
pr_err("Can't request %d empty-soc: %d\n",
chip->soc_irq[EMPTY_SOC].irq, rc);
return rc;
}
rc |= devm_request_irq(chip->dev,
chip->soc_irq[DELTA_SOC].irq,
fg_soc_irq_handler, IRQF_TRIGGER_RISING,
"delta-soc", chip);
if (rc < 0) {
pr_err("Can't request %d delta-soc: %d\n",
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);
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;
default:
pr_err("subtype %d\n", subtype);
return -EINVAL;
}
}
return rc;
}
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);
cancel_work_sync(&chip->batt_profile_init);
power_supply_unregister(&chip->bms_psy);
dev_set_drvdata(&spmi->dev, NULL);
return 0;
}
static int fg_memif_data_open(struct inode *inode, struct file *file)
{
struct fg_log_buffer *log;
struct fg_trans *trans;
size_t logbufsize = SZ_4K;
if (!dbgfs_data.chip) {
pr_err("Not initialized data\n");
return -EINVAL;
}
/* Per file "transaction" data */
trans = kzalloc(sizeof(*trans), GFP_KERNEL);
if (!trans) {
pr_err("Unable to allocate memory for transaction data\n");
return -ENOMEM;
}
/* Allocate log buffer */
log = kzalloc(logbufsize, GFP_KERNEL);
if (!log) {
kfree(trans);
pr_err("Unable to allocate memory for log buffer\n");
return -ENOMEM;
}
log->rpos = 0;
log->wpos = 0;
log->len = logbufsize - sizeof(*log);
trans->log = log;
trans->cnt = dbgfs_data.cnt;
trans->addr = dbgfs_data.addr;
trans->chip = dbgfs_data.chip;
trans->offset = trans->addr;
file->private_data = trans;
return 0;
}
static int fg_memif_dfs_close(struct inode *inode, struct file *file)
{
struct fg_trans *trans = file->private_data;
if (trans && trans->log) {
file->private_data = NULL;
kfree(trans->log);
kfree(trans);
}
return 0;
}
/**
* print_to_log: format a string and place into the log buffer
* @log: The log buffer to place the result into.
* @fmt: The format string to use.
* @...: The arguments for the format string.
*
* The return value is the number of characters written to @log buffer
* not including the trailing '\0'.
*/
static int print_to_log(struct fg_log_buffer *log, const char *fmt, ...)
{
va_list args;
int cnt;
char *buf = &log->data[log->wpos];
size_t size = log->len - log->wpos;
va_start(args, fmt);
cnt = vscnprintf(buf, size, fmt, args);
va_end(args);
log->wpos += cnt;
return cnt;
}
/**
* write_next_line_to_log: Writes a single "line" of data into the log buffer
* @trans: Pointer to SRAM transaction data.
* @offset: SRAM address offset to start reading from.
* @pcnt: Pointer to 'cnt' variable. Indicates the number of bytes to read.
*
* The 'offset' is a 12-bit SRAM address.
*
* On a successful read, the pcnt is decremented by the number of data
* bytes read from the SRAM. When the cnt reaches 0, all requested bytes have
* been read.
*/
static int
write_next_line_to_log(struct fg_trans *trans, int offset, size_t *pcnt)
{
int i, j, rc = 0;
u8 data[ITEMS_PER_LINE];
struct fg_log_buffer *log = trans->log;
int cnt = 0;
int padding = offset % ITEMS_PER_LINE;
int items_to_read = min(ARRAY_SIZE(data) - padding, *pcnt);
int items_to_log = min(ITEMS_PER_LINE, padding + items_to_read);
/* Buffer needs enough space for an entire line */
if ((log->len - log->wpos) < MAX_LINE_LENGTH)
goto done;
/* Read the desired number of "items" */
rc = fg_mem_read(trans->chip, data, offset, items_to_read, 0,
*pcnt > items_to_read ? 1 : 0);
if (rc)
return -EINVAL;
*pcnt -= items_to_read;
/* Each line starts with the aligned offset (12-bit address) */
cnt = print_to_log(log, "%3.3X ", offset & 0xfff);
if (cnt == 0)
goto done;
/* If the offset is unaligned, add padding to right justify items */
for (i = 0; i < padding; ++i) {
cnt = print_to_log(log, "-- ");
if (cnt == 0)
goto done;
}
/* Log the data items */
for (j = 0; i < items_to_log; ++i, ++j) {
cnt = print_to_log(log, "%2.2X ", data[j]);
if (cnt == 0)
goto done;
}
/* If the last character was a space, then replace it with a newline */
if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
log->data[log->wpos - 1] = '\n';
done:
return cnt;
}
/**
* get_log_data - reads data from SRAM and saves to the log buffer
* @trans: Pointer to SRAM transaction data.
*
* Returns the number of "items" read or SPMI error code for read failures.
*/
static int get_log_data(struct fg_trans *trans)
{
int cnt;
int last_cnt;
int items_read;
int total_items_read = 0;
u32 offset = trans->offset;
size_t item_cnt = trans->cnt;
struct fg_log_buffer *log = trans->log;
if (item_cnt == 0)
return 0;
/* Reset the log buffer 'pointers' */
log->wpos = log->rpos = 0;
/* Keep reading data until the log is full */
do {
last_cnt = item_cnt;
cnt = write_next_line_to_log(trans, offset, &item_cnt);
items_read = last_cnt - item_cnt;
offset += items_read;
total_items_read += items_read;
} while (cnt && item_cnt > 0);
/* Adjust the transaction offset and count */
trans->cnt = item_cnt;
trans->offset += total_items_read;
return total_items_read;
}
/**
* fg_memif_dfs_reg_read: reads value(s) from SRAM and fills user's buffer a
* byte array (coded as string)
* @file: file pointer
* @buf: where to put the result
* @count: maximum space available in @buf
* @ppos: starting position
* @return number of user bytes read, or negative error value
*/
static ssize_t fg_memif_dfs_reg_read(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
struct fg_trans *trans = file->private_data;
struct fg_log_buffer *log = trans->log;
size_t ret;
size_t len;
/* Is the the log buffer empty */
if (log->rpos >= log->wpos) {
if (get_log_data(trans) <= 0)
return 0;
}
len = min(count, log->wpos - log->rpos);
ret = copy_to_user(buf, &log->data[log->rpos], len);
if (ret == len) {
pr_err("error copy SPMI register values to user\n");
return -EFAULT;
}
/* 'ret' is the number of bytes not copied */
len -= ret;
*ppos += len;
log->rpos += len;
return len;
}
/**
* fg_memif_dfs_reg_write: write user's byte array (coded as string) to SRAM.
* @file: file pointer
* @buf: user data to be written.
* @count: maximum space available in @buf
* @ppos: starting position
* @return number of user byte written, or negative error value
*/
static ssize_t fg_memif_dfs_reg_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
int bytes_read;
int data;
int pos = 0;
int cnt = 0;
u8 *values;
size_t ret = 0;
struct fg_trans *trans = file->private_data;
u32 offset = trans->offset;
/* Make a copy of the user data */
char *kbuf = kmalloc(count + 1, GFP_KERNEL);
if (!kbuf)
return -ENOMEM;
ret = copy_from_user(kbuf, buf, count);
if (ret == count) {
pr_err("failed to copy data from user\n");
ret = -EFAULT;
goto free_buf;
}
count -= ret;
*ppos += count;
kbuf[count] = '\0';
/* Override the text buffer with the raw data */
values = kbuf;
/* Parse the data in the buffer. It should be a string of numbers */
while (sscanf(kbuf + pos, "%i%n", &data, &bytes_read) == 1) {
pos += bytes_read;
values[cnt++] = data & 0xff;
}
if (!cnt)
goto free_buf;
pr_info("address %x, count %d\n", offset, cnt);
/* Perform the write(s) */
ret = fg_mem_write(trans->chip, values, offset,
cnt, 0, 0);
if (ret) {
pr_err("SPMI write failed, err = %zu\n", ret);
} else {
ret = count;
trans->offset += cnt > 4 ? 4 : cnt;
}
free_buf:
kfree(kbuf);
return ret;
}
static const struct file_operations fg_memif_dfs_reg_fops = {
.open = fg_memif_data_open,
.release = fg_memif_dfs_close,
.read = fg_memif_dfs_reg_read,
.write = fg_memif_dfs_reg_write,
};
/**
* fg_dfs_create_fs: create debugfs file system.
* @return pointer to root directory or NULL if failed to create fs
*/
static struct dentry *fg_dfs_create_fs(void)
{
struct dentry *root, *file;
pr_debug("Creating FG_MEM debugfs file-system\n");
root = debugfs_create_dir(DFS_ROOT_NAME, NULL);
if (IS_ERR_OR_NULL(root)) {
pr_err("Error creating top level directory err:%ld",
(long)root);
if (PTR_ERR(root) == -ENODEV)
pr_err("debugfs is not enabled in the kernel");
return NULL;
}
dbgfs_data.help_msg.size = strlen(dbgfs_data.help_msg.data);
file = debugfs_create_blob("help", S_IRUGO, root, &dbgfs_data.help_msg);
if (!file) {
pr_err("error creating help entry\n");
goto err_remove_fs;
}
return root;
err_remove_fs:
debugfs_remove_recursive(root);
return NULL;
}
/**
* fg_dfs_get_root: return a pointer to FG debugfs root directory.
* @return a pointer to the existing directory, or if no root
* directory exists then create one. Directory is created with file that
* configures SRAM transaction, namely: address, and count.
* @returns valid pointer on success or NULL
*/
struct dentry *fg_dfs_get_root(void)
{
if (dbgfs_data.root)
return dbgfs_data.root;
if (mutex_lock_interruptible(&dbgfs_data.lock) < 0)
return NULL;
/* critical section */
if (!dbgfs_data.root) { /* double checking idiom */
dbgfs_data.root = fg_dfs_create_fs();
}
mutex_unlock(&dbgfs_data.lock);
return dbgfs_data.root;
}
/*
* fg_dfs_create: adds new fg_mem if debugfs entry
* @return zero on success
*/
int fg_dfs_create(struct fg_chip *chip)
{
struct dentry *root;
struct dentry *file;
root = fg_dfs_get_root();
if (!root)
return -ENOENT;
dbgfs_data.chip = chip;
file = debugfs_create_u32("count", DFS_MODE, root, &(dbgfs_data.cnt));
if (!file) {
pr_err("error creating 'count' entry\n");
goto err_remove_fs;
}
file = debugfs_create_x32("address", DFS_MODE,
root, &(dbgfs_data.addr));
if (!file) {
pr_err("error creating 'address' entry\n");
goto err_remove_fs;
}
file = debugfs_create_file("data", DFS_MODE, root, &dbgfs_data,
&fg_memif_dfs_reg_fops);
if (!file) {
pr_err("error creating 'data' entry\n");
goto err_remove_fs;
}
return 0;
err_remove_fs:
debugfs_remove_recursive(root);
return -ENOMEM;
}
#define INIT_JEITA_DELAY_MS 1000
static int fg_probe(struct spmi_device *spmi)
{
struct device *dev = &(spmi->dev);
struct fg_chip *chip;
struct spmi_resource *spmi_resource;
struct resource *resource;
u8 subtype;
int rc = 0;
if (!spmi) {
pr_err("no valid spmi pointer\n");
return -ENODEV;
}
if (!spmi->dev.of_node) {
pr_err("device node missing\n");
return -ENODEV;
}
chip = devm_kzalloc(dev, sizeof(struct fg_chip), GFP_KERNEL);
if (chip == NULL) {
pr_err("Can't allocate fg_chip\n");
return -ENOMEM;
}
chip->spmi = spmi;
chip->dev = &(spmi->dev);
mutex_init(&chip->rw_lock);
INIT_DELAYED_WORK(&chip->update_jeita_setting,
update_jeita_setting);
INIT_DELAYED_WORK(&chip->update_sram_data, update_sram_data);
INIT_WORK(&chip->batt_profile_init,
batt_profile_init);
init_completion(&chip->sram_access);
spmi_for_each_container_dev(spmi_resource, spmi) {
if (!spmi_resource) {
pr_err("qpnp_chg: spmi resource absent\n");
rc = -ENXIO;
goto of_init_fail;
}
resource = spmi_get_resource(spmi, spmi_resource,
IORESOURCE_MEM, 0);
if (!(resource && resource->start)) {
pr_err("node %s IO resource absent!\n",
spmi->dev.of_node->full_name);
rc = -ENXIO;
goto of_init_fail;
}
if (strcmp("qcom,fg-adc-vbat",
spmi_resource->of_node->name) == 0) {
chip->vbat_adc_addr = resource->start;
continue;
} else if (strcmp("qcom,fg-adc-ibat",
spmi_resource->of_node->name) == 0) {
chip->ibat_adc_addr = resource->start;
continue;
}
rc = fg_read(chip, &subtype,
resource->start + REG_OFFSET_PERP_SUBTYPE, 1);
if (rc) {
pr_err("Peripheral subtype read failed rc=%d\n", 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;
}
}
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;
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);
goto of_init_fail;
}
rc = fg_init_irqs(chip);
if (rc) {
pr_err("failed to request interrupts %d\n", rc);
goto power_supply_unregister;
}
if (chip->mem_base) {
rc = fg_dfs_create(chip);
if (rc < 0) {
pr_err("failed to create debugfs rc = %d\n", rc);
goto power_supply_unregister;
}
}
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;
power_supply_unregister:
power_supply_unregister(&chip->bms_psy);
of_init_fail:
mutex_destroy(&chip->rw_lock);
return rc;
}
static struct spmi_driver fg_driver = {
.driver = {
.name = QPNP_FG_DEV_NAME,
.of_match_table = fg_match_table,
},
.probe = fg_probe,
.remove = fg_remove,
};
static int __init fg_init(void)
{
return spmi_driver_register(&fg_driver);
}
static void __exit fg_exit(void)
{
return spmi_driver_unregister(&fg_driver);
}
module_init(fg_init);
module_exit(fg_exit);
MODULE_DESCRIPTION("QPNP Fuel Gauge Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" QPNP_FG_DEV_NAME);