blob: b055ff61bd62646ed1613eab2407ec41583fa387 [file] [log] [blame]
/*
* Copyright (c) 2016-2017, 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) "RRADC: %s: " fmt, __func__
#include <linux/iio/iio.h>
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/delay.h>
#include <linux/qpnp/qpnp-revid.h>
#define FG_ADC_RR_EN_CTL 0x46
#define FG_ADC_RR_SKIN_TEMP_LSB 0x50
#define FG_ADC_RR_SKIN_TEMP_MSB 0x51
#define FG_ADC_RR_RR_ADC_CTL 0x52
#define FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK 0x8
#define FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL BIT(3)
#define FG_ADC_RR_ADC_LOG 0x53
#define FG_ADC_RR_ADC_LOG_CLR_CTRL BIT(0)
#define FG_ADC_RR_FAKE_BATT_LOW_LSB 0x58
#define FG_ADC_RR_FAKE_BATT_LOW_MSB 0x59
#define FG_ADC_RR_FAKE_BATT_HIGH_LSB 0x5A
#define FG_ADC_RR_FAKE_BATT_HIGH_MSB 0x5B
#define FG_ADC_RR_BATT_ID_CTRL 0x60
#define FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV BIT(0)
#define FG_ADC_RR_BATT_ID_TRIGGER 0x61
#define FG_ADC_RR_BATT_ID_TRIGGER_CTL BIT(0)
#define FG_ADC_RR_BATT_ID_STS 0x62
#define FG_ADC_RR_BATT_ID_CFG 0x63
#define FG_ADC_RR_BATT_ID_5_LSB 0x66
#define FG_ADC_RR_BATT_ID_5_MSB 0x67
#define FG_ADC_RR_BATT_ID_15_LSB 0x68
#define FG_ADC_RR_BATT_ID_15_MSB 0x69
#define FG_ADC_RR_BATT_ID_150_LSB 0x6A
#define FG_ADC_RR_BATT_ID_150_MSB 0x6B
#define FG_ADC_RR_BATT_THERM_CTRL 0x70
#define FG_ADC_RR_BATT_THERM_TRIGGER 0x71
#define FG_ADC_RR_BATT_THERM_STS 0x72
#define FG_ADC_RR_BATT_THERM_CFG 0x73
#define FG_ADC_RR_BATT_THERM_LSB 0x74
#define FG_ADC_RR_BATT_THERM_MSB 0x75
#define FG_ADC_RR_BATT_THERM_FREQ 0x76
#define FG_ADC_RR_AUX_THERM_CTRL 0x80
#define FG_ADC_RR_AUX_THERM_TRIGGER 0x81
#define FG_ADC_RR_AUX_THERM_STS 0x82
#define FG_ADC_RR_AUX_THERM_CFG 0x83
#define FG_ADC_RR_AUX_THERM_LSB 0x84
#define FG_ADC_RR_AUX_THERM_MSB 0x85
#define FG_ADC_RR_SKIN_HOT 0x86
#define FG_ADC_RR_SKIN_TOO_HOT 0x87
#define FG_ADC_RR_AUX_THERM_C1 0x88
#define FG_ADC_RR_AUX_THERM_C2 0x89
#define FG_ADC_RR_AUX_THERM_C3 0x8A
#define FG_ADC_RR_AUX_THERM_HALF_RANGE 0x8B
#define FG_ADC_RR_USB_IN_V_CTRL 0x90
#define FG_ADC_RR_USB_IN_V_TRIGGER 0x91
#define FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK 0x80
#define FG_ADC_RR_USB_IN_V_EVERY_CYCLE BIT(7)
#define FG_ADC_RR_USB_IN_V_STS 0x92
#define FG_ADC_RR_USB_IN_V_LSB 0x94
#define FG_ADC_RR_USB_IN_V_MSB 0x95
#define FG_ADC_RR_USB_IN_I_CTRL 0x98
#define FG_ADC_RR_USB_IN_I_TRIGGER 0x99
#define FG_ADC_RR_USB_IN_I_STS 0x9A
#define FG_ADC_RR_USB_IN_I_LSB 0x9C
#define FG_ADC_RR_USB_IN_I_MSB 0x9D
#define FG_ADC_RR_DC_IN_V_CTRL 0xA0
#define FG_ADC_RR_DC_IN_V_TRIGGER 0xA1
#define FG_ADC_RR_DC_IN_V_STS 0xA2
#define FG_ADC_RR_DC_IN_V_LSB 0xA4
#define FG_ADC_RR_DC_IN_V_MSB 0xA5
#define FG_ADC_RR_DC_IN_I_CTRL 0xA8
#define FG_ADC_RR_DC_IN_I_TRIGGER 0xA9
#define FG_ADC_RR_DC_IN_I_STS 0xAA
#define FG_ADC_RR_DC_IN_I_LSB 0xAC
#define FG_ADC_RR_DC_IN_I_MSB 0xAD
#define FG_ADC_RR_PMI_DIE_TEMP_CTRL 0xB0
#define FG_ADC_RR_PMI_DIE_TEMP_TRIGGER 0xB1
#define FG_ADC_RR_PMI_DIE_TEMP_STS 0xB2
#define FG_ADC_RR_PMI_DIE_TEMP_CFG 0xB3
#define FG_ADC_RR_PMI_DIE_TEMP_LSB 0xB4
#define FG_ADC_RR_PMI_DIE_TEMP_MSB 0xB5
#define FG_ADC_RR_CHARGER_TEMP_CTRL 0xB8
#define FG_ADC_RR_CHARGER_TEMP_TRIGGER 0xB9
#define FG_ADC_RR_CHARGER_TEMP_STS 0xBA
#define FG_ADC_RR_CHARGER_TEMP_CFG 0xBB
#define FG_ADC_RR_CHARGER_TEMP_LSB 0xBC
#define FG_ADC_RR_CHARGER_TEMP_MSB 0xBD
#define FG_ADC_RR_CHARGER_HOT 0xBE
#define FG_ADC_RR_CHARGER_TOO_HOT 0xBF
#define FG_ADC_RR_GPIO_CTRL 0xC0
#define FG_ADC_RR_GPIO_TRIGGER 0xC1
#define FG_ADC_RR_GPIO_STS 0xC2
#define FG_ADC_RR_GPIO_LSB 0xC4
#define FG_ADC_RR_GPIO_MSB 0xC5
#define FG_ADC_RR_ATEST_CTRL 0xC8
#define FG_ADC_RR_ATEST_TRIGGER 0xC9
#define FG_ADC_RR_ATEST_STS 0xCA
#define FG_ADC_RR_ATEST_LSB 0xCC
#define FG_ADC_RR_ATEST_MSB 0xCD
#define FG_ADC_RR_SEC_ACCESS 0xD0
#define FG_ADC_RR_PERPH_RESET_CTL2 0xD9
#define FG_ADC_RR_PERPH_RESET_CTL3 0xDA
#define FG_ADC_RR_PERPH_RESET_CTL4 0xDB
#define FG_ADC_RR_INT_TEST1 0xE0
#define FG_ADC_RR_INT_TEST_VAL 0xE1
#define FG_ADC_RR_TM_TRIGGER_CTRLS 0xE2
#define FG_ADC_RR_TM_ADC_CTRLS 0xE3
#define FG_ADC_RR_TM_CNL_CTRL 0xE4
#define FG_ADC_RR_TM_BATT_ID_CTRL 0xE5
#define FG_ADC_RR_TM_THERM_CTRL 0xE6
#define FG_ADC_RR_TM_CONV_STS 0xE7
#define FG_ADC_RR_TM_ADC_READ_LSB 0xE8
#define FG_ADC_RR_TM_ADC_READ_MSB 0xE9
#define FG_ADC_RR_TM_ATEST_MUX_1 0xEA
#define FG_ADC_RR_TM_ATEST_MUX_2 0xEB
#define FG_ADC_RR_TM_REFERENCES 0xED
#define FG_ADC_RR_TM_MISC_CTL 0xEE
#define FG_ADC_RR_TM_RR_CTRL 0xEF
#define FG_ADC_RR_BATT_ID_5_MA 5
#define FG_ADC_RR_BATT_ID_15_MA 15
#define FG_ADC_RR_BATT_ID_150_MA 150
#define FG_ADC_RR_BATT_ID_RANGE 820
#define FG_ADC_BITS 10
#define FG_MAX_ADC_READINGS (1 << FG_ADC_BITS)
#define FG_ADC_RR_FS_VOLTAGE_MV 2500
/* BATT_THERM 0.25K/LSB */
#define FG_ADC_RR_BATT_THERM_LSB_K 4
#define FG_ADC_RR_TEMP_FS_VOLTAGE_NUM 5000000
#define FG_ADC_RR_TEMP_FS_VOLTAGE_DEN 3
#define FG_ADC_RR_DIE_TEMP_OFFSET 601400
#define FG_ADC_RR_DIE_TEMP_SLOPE 2
#define FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC 25000
#define FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV 1303168
#define FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C 3784
#define FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV 1338433
#define FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C 3655
#define FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV 1309001
#define FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C 3403
#define FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV 1295898
#define FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C 3596
#define FG_ADC_RR_CHG_TEMP_660_MGNA_OFFSET_UV 1314779
#define FG_RR_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C 3496
#define FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC 25000
#define FG_ADC_RR_CHG_THRESHOLD_SCALE 4
#define FG_ADC_RR_VOLT_INPUT_FACTOR 8
#define FG_ADC_RR_CURR_INPUT_FACTOR 2000
#define FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL 1886
#define FG_ADC_SCALE_MILLI_FACTOR 1000
#define FG_ADC_KELVINMIL_CELSIUSMIL 273150
#define FG_ADC_RR_GPIO_FS_RANGE 5000
#define FG_RR_ADC_COHERENT_CHECK_RETRY 5
#define FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN 16
#define FG_RR_ADC_STS_CHANNEL_READING_MASK 0x3
#define FG_RR_ADC_STS_CHANNEL_STS 0x2
#define FG_RR_CONV_CONTINUOUS_TIME_MIN_US 50000
#define FG_RR_CONV_CONTINUOUS_TIME_MAX_US 51000
#define FG_RR_CONV_MAX_RETRY_CNT 50
/*
* The channel number is not a physical index in hardware,
* rather it's a list of supported channels and an index to
* select the respective channel properties such as scaling
* the result. Add any new additional channels supported by
* the RR ADC before RR_ADC_MAX.
*/
enum rradc_channel_id {
RR_ADC_BATT_ID = 0,
RR_ADC_BATT_THERM,
RR_ADC_SKIN_TEMP,
RR_ADC_USBIN_I,
RR_ADC_USBIN_V,
RR_ADC_DCIN_I,
RR_ADC_DCIN_V,
RR_ADC_DIE_TEMP,
RR_ADC_CHG_TEMP,
RR_ADC_GPIO,
RR_ADC_CHG_HOT_TEMP,
RR_ADC_CHG_TOO_HOT_TEMP,
RR_ADC_SKIN_HOT_TEMP,
RR_ADC_SKIN_TOO_HOT_TEMP,
RR_ADC_MAX
};
struct rradc_chip {
struct device *dev;
struct mutex lock;
struct regmap *regmap;
u16 base;
struct iio_chan_spec *iio_chans;
unsigned int nchannels;
struct rradc_chan_prop *chan_props;
struct device_node *revid_dev_node;
struct pmic_revid_data *pmic_fab_id;
};
struct rradc_channels {
const char *datasheet_name;
enum iio_chan_type type;
long info_mask;
u8 lsb;
u8 msb;
u8 sts;
int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
u16 adc_code, int *result);
};
struct rradc_chan_prop {
enum rradc_channel_id channel;
uint32_t channel_data;
int (*scale)(struct rradc_chip *chip, struct rradc_chan_prop *prop,
u16 adc_code, int *result);
};
static int rradc_masked_write(struct rradc_chip *rr_adc, u16 offset, u8 mask,
u8 val)
{
int rc;
rc = regmap_update_bits(rr_adc->regmap, rr_adc->base + offset,
mask, val);
if (rc) {
pr_err("spmi write failed: addr=%03X, rc=%d\n", offset, rc);
return rc;
}
return rc;
}
static int rradc_read(struct rradc_chip *rr_adc, u16 offset, u8 *data, int len)
{
int rc = 0, retry_cnt = 0, i = 0;
u8 data_check[FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN];
bool coherent_err = false;
if (len > FG_RR_ADC_MAX_CONTINUOUS_BUFFER_LEN) {
pr_err("Increase the buffer length\n");
return -EINVAL;
}
while (retry_cnt < FG_RR_ADC_COHERENT_CHECK_RETRY) {
rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
data, len);
if (rc < 0) {
pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
return rc;
}
rc = regmap_bulk_read(rr_adc->regmap, rr_adc->base + offset,
data_check, len);
if (rc < 0) {
pr_err("rr_adc reg 0x%x failed :%d\n", offset, rc);
return rc;
}
for (i = 0; i < len; i++) {
if (data[i] != data_check[i])
coherent_err = true;
}
if (coherent_err) {
retry_cnt++;
coherent_err = false;
pr_debug("retry_cnt:%d\n", retry_cnt);
} else {
break;
}
}
if (retry_cnt == FG_RR_ADC_COHERENT_CHECK_RETRY)
pr_err("Retry exceeded for coherrency check\n");
return rc;
}
static int rradc_post_process_batt_id(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_ohms)
{
uint32_t current_value;
int64_t r_id;
current_value = prop->channel_data;
r_id = ((int64_t)adc_code * FG_ADC_RR_FS_VOLTAGE_MV);
r_id = div64_s64(r_id, (FG_MAX_ADC_READINGS * current_value));
*result_ohms = (r_id * FG_ADC_SCALE_MILLI_FACTOR);
return 0;
}
static int rradc_post_process_therm(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
{
int64_t temp;
/* K = code/4 */
temp = ((int64_t)adc_code * FG_ADC_SCALE_MILLI_FACTOR);
temp = div64_s64(temp, FG_ADC_RR_BATT_THERM_LSB_K);
*result_millidegc = temp - FG_ADC_KELVINMIL_CELSIUSMIL;
return 0;
}
static int rradc_post_process_volt(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_uv)
{
int64_t uv = 0;
/* 8x input attenuation; 2.5V ADC full scale */
uv = ((int64_t)adc_code * FG_ADC_RR_VOLT_INPUT_FACTOR);
uv *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
uv = div64_s64(uv, FG_MAX_ADC_READINGS);
*result_uv = uv;
return 0;
}
static int rradc_post_process_curr(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_ua)
{
int64_t ua = 0, scale = 0;
if (!prop)
return -EINVAL;
if (prop->channel == RR_ADC_USBIN_I)
scale = FG_ADC_RR_CURR_USBIN_INPUT_FACTOR_MIL;
else
scale = FG_ADC_RR_CURR_INPUT_FACTOR;
/* scale * V/A; 2.5V ADC full scale */
ua = ((int64_t)adc_code * scale);
ua *= (FG_ADC_RR_FS_VOLTAGE_MV * FG_ADC_SCALE_MILLI_FACTOR);
ua = div64_s64(ua, (FG_MAX_ADC_READINGS * 1000));
*result_ua = ua;
return 0;
}
static int rradc_post_process_die_temp(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
{
int64_t temp = 0;
temp = ((int64_t)adc_code * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM);
temp = div64_s64(temp, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
FG_MAX_ADC_READINGS));
temp -= FG_ADC_RR_DIE_TEMP_OFFSET;
temp = div64_s64(temp, FG_ADC_RR_DIE_TEMP_SLOPE);
temp += FG_ADC_RR_DIE_TEMP_OFFSET_MILLI_DEGC;
*result_millidegc = temp;
return 0;
}
static int rradc_get_660_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
case PM660_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_660_GF_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_GF_SLOPE_UV_PER_C;
break;
case PM660_FAB_ID_TSMC:
*offset = FG_ADC_RR_CHG_TEMP_660_SMIC_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_SMIC_SLOPE_UV_PER_C;
break;
default:
*offset = FG_ADC_RR_CHG_TEMP_660_MGNA_OFFSET_UV;
*slope = FG_RR_CHG_TEMP_660_MGNA_SLOPE_UV_PER_C;
}
return 0;
}
static int rradc_get_8998_fab_coeff(struct rradc_chip *chip,
int64_t *offset, int64_t *slope)
{
switch (chip->pmic_fab_id->fab_id) {
case PMI8998_FAB_ID_GF:
*offset = FG_ADC_RR_CHG_TEMP_GF_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_GF_SLOPE_UV_PER_C;
break;
case PMI8998_FAB_ID_SMIC:
*offset = FG_ADC_RR_CHG_TEMP_SMIC_OFFSET_UV;
*slope = FG_ADC_RR_CHG_TEMP_SMIC_SLOPE_UV_PER_C;
break;
default:
return -EINVAL;
}
return 0;
}
static int rradc_post_process_chg_temp_hot(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
{
int64_t uv = 0, offset = 0, slope = 0;
int rc = 0;
if (chip->revid_dev_node) {
switch (chip->pmic_fab_id->pmic_subtype) {
case PM660_SUBTYPE:
rc = rradc_get_660_fab_coeff(chip, &offset, &slope);
if (rc < 0) {
pr_err("Unable to get fab id coefficients\n");
return -EINVAL;
}
break;
case PMI8998_SUBTYPE:
rc = rradc_get_8998_fab_coeff(chip, &offset, &slope);
if (rc < 0) {
pr_err("Unable to get fab id coefficients\n");
return -EINVAL;
}
break;
default:
pr_err("No PMIC subtype found\n");
return -EINVAL;
}
} else {
pr_err("No temperature scaling coefficients\n");
return -EINVAL;
}
uv = (int64_t) adc_code * FG_ADC_RR_CHG_THRESHOLD_SCALE;
uv = uv * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM;
uv = div64_s64(uv, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
FG_MAX_ADC_READINGS));
uv = offset - uv;
uv = div64_s64((uv * FG_ADC_SCALE_MILLI_FACTOR), slope);
uv = uv + FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
*result_millidegc = uv;
return 0;
}
static int rradc_post_process_skin_temp_hot(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
{
int64_t temp = 0;
temp = (int64_t) adc_code;
temp = div64_s64(temp, 2);
temp = temp - 30;
temp *= FG_ADC_SCALE_MILLI_FACTOR;
*result_millidegc = temp;
return 0;
}
static int rradc_post_process_chg_temp(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_millidegc)
{
int64_t uv = 0, offset = 0, slope = 0;
int rc = 0;
if (chip->revid_dev_node) {
switch (chip->pmic_fab_id->pmic_subtype) {
case PM660_SUBTYPE:
rc = rradc_get_660_fab_coeff(chip, &offset, &slope);
if (rc < 0) {
pr_err("Unable to get fab id coefficients\n");
return -EINVAL;
}
break;
case PMI8998_SUBTYPE:
rc = rradc_get_8998_fab_coeff(chip, &offset, &slope);
if (rc < 0) {
pr_err("Unable to get fab id coefficients\n");
return -EINVAL;
}
break;
default:
pr_err("No PMIC subtype found\n");
return -EINVAL;
}
} else {
pr_err("No temperature scaling coefficients\n");
return -EINVAL;
}
uv = ((int64_t) adc_code * FG_ADC_RR_TEMP_FS_VOLTAGE_NUM);
uv = div64_s64(uv, (FG_ADC_RR_TEMP_FS_VOLTAGE_DEN *
FG_MAX_ADC_READINGS));
uv = offset - uv;
uv = div64_s64((uv * FG_ADC_SCALE_MILLI_FACTOR), slope);
uv += FG_ADC_RR_CHG_TEMP_OFFSET_MILLI_DEGC;
*result_millidegc = uv;
return 0;
}
static int rradc_post_process_gpio(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 adc_code,
int *result_mv)
{
int64_t mv = 0;
/* 5V ADC full scale, 10 bit */
mv = ((int64_t)adc_code * FG_ADC_RR_GPIO_FS_RANGE);
mv = div64_s64(mv, FG_MAX_ADC_READINGS);
*result_mv = mv;
return 0;
}
#define RR_ADC_CHAN(_dname, _type, _mask, _scale, _lsb, _msb, _sts) \
{ \
.datasheet_name = (_dname), \
.type = _type, \
.info_mask = _mask, \
.scale = _scale, \
.lsb = _lsb, \
.msb = _msb, \
.sts = _sts, \
}, \
#define RR_ADC_CHAN_TEMP(_dname, _scale, mask, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_TEMP, \
mask, \
_scale, _lsb, _msb, _sts) \
#define RR_ADC_CHAN_VOLT(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_VOLTAGE, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
_scale, _lsb, _msb, _sts) \
#define RR_ADC_CHAN_CURRENT(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_CURRENT, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
_scale, _lsb, _msb, _sts) \
#define RR_ADC_CHAN_RESISTANCE(_dname, _scale, _lsb, _msb, _sts) \
RR_ADC_CHAN(_dname, IIO_RESISTANCE, \
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),\
_scale, _lsb, _msb, _sts) \
static const struct rradc_channels rradc_chans[] = {
RR_ADC_CHAN_RESISTANCE("batt_id", rradc_post_process_batt_id,
FG_ADC_RR_BATT_ID_5_LSB, FG_ADC_RR_BATT_ID_5_MSB,
FG_ADC_RR_BATT_ID_STS)
RR_ADC_CHAN_TEMP("batt_therm", &rradc_post_process_therm,
BIT(IIO_CHAN_INFO_RAW),
FG_ADC_RR_BATT_THERM_LSB, FG_ADC_RR_BATT_THERM_MSB,
FG_ADC_RR_BATT_THERM_STS)
RR_ADC_CHAN_TEMP("skin_temp", &rradc_post_process_therm,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_TEMP_LSB, FG_ADC_RR_SKIN_TEMP_MSB,
FG_ADC_RR_AUX_THERM_STS)
RR_ADC_CHAN_CURRENT("usbin_i", &rradc_post_process_curr,
FG_ADC_RR_USB_IN_I_LSB, FG_ADC_RR_USB_IN_I_MSB,
FG_ADC_RR_USB_IN_I_STS)
RR_ADC_CHAN_VOLT("usbin_v", &rradc_post_process_volt,
FG_ADC_RR_USB_IN_V_LSB, FG_ADC_RR_USB_IN_V_MSB,
FG_ADC_RR_USB_IN_V_STS)
RR_ADC_CHAN_CURRENT("dcin_i", &rradc_post_process_curr,
FG_ADC_RR_DC_IN_I_LSB, FG_ADC_RR_DC_IN_I_MSB,
FG_ADC_RR_DC_IN_I_STS)
RR_ADC_CHAN_VOLT("dcin_v", &rradc_post_process_volt,
FG_ADC_RR_DC_IN_V_LSB, FG_ADC_RR_DC_IN_V_MSB,
FG_ADC_RR_DC_IN_V_STS)
RR_ADC_CHAN_TEMP("die_temp", &rradc_post_process_die_temp,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_PMI_DIE_TEMP_LSB, FG_ADC_RR_PMI_DIE_TEMP_MSB,
FG_ADC_RR_PMI_DIE_TEMP_STS)
RR_ADC_CHAN_TEMP("chg_temp", &rradc_post_process_chg_temp,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_TEMP_LSB, FG_ADC_RR_CHARGER_TEMP_MSB,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_VOLT("gpio", &rradc_post_process_gpio,
FG_ADC_RR_GPIO_LSB, FG_ADC_RR_GPIO_MSB,
FG_ADC_RR_GPIO_STS)
RR_ADC_CHAN_TEMP("chg_temp_hot", &rradc_post_process_chg_temp_hot,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_HOT, FG_ADC_RR_CHARGER_HOT,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_TEMP("chg_temp_too_hot", &rradc_post_process_chg_temp_hot,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_CHARGER_TOO_HOT, FG_ADC_RR_CHARGER_TOO_HOT,
FG_ADC_RR_CHARGER_TEMP_STS)
RR_ADC_CHAN_TEMP("skin_temp_hot", &rradc_post_process_skin_temp_hot,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_HOT, FG_ADC_RR_SKIN_HOT,
FG_ADC_RR_AUX_THERM_STS)
RR_ADC_CHAN_TEMP("skin_temp_too_hot", &rradc_post_process_skin_temp_hot,
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED),
FG_ADC_RR_SKIN_TOO_HOT, FG_ADC_RR_SKIN_TOO_HOT,
FG_ADC_RR_AUX_THERM_STS)
};
static int rradc_enable_continuous_mode(struct rradc_chip *chip)
{
int rc = 0;
/* Clear channel log */
rc = rradc_masked_write(chip, FG_ADC_RR_ADC_LOG,
FG_ADC_RR_ADC_LOG_CLR_CTRL,
FG_ADC_RR_ADC_LOG_CLR_CTRL);
if (rc < 0) {
pr_err("log ctrl update to clear failed:%d\n", rc);
return rc;
}
rc = rradc_masked_write(chip, FG_ADC_RR_ADC_LOG,
FG_ADC_RR_ADC_LOG_CLR_CTRL, 0);
if (rc < 0) {
pr_err("log ctrl update to not clear failed:%d\n", rc);
return rc;
}
/* Switch to continuous mode */
rc = rradc_masked_write(chip, FG_ADC_RR_RR_ADC_CTL,
FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK,
FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL);
if (rc < 0) {
pr_err("Update to continuous mode failed:%d\n", rc);
return rc;
}
return rc;
}
static int rradc_disable_continuous_mode(struct rradc_chip *chip)
{
int rc = 0;
/* Switch to non continuous mode */
rc = rradc_masked_write(chip, FG_ADC_RR_RR_ADC_CTL,
FG_ADC_RR_ADC_CTL_CONTINUOUS_SEL_MASK, 0);
if (rc < 0) {
pr_err("Update to non-continuous mode failed:%d\n", rc);
return rc;
}
return rc;
}
static int rradc_check_status_ready_with_retry(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u8 *buf, u16 status)
{
int rc = 0, retry_cnt = 0, mask = 0;
switch (prop->channel) {
case RR_ADC_BATT_ID:
/* BATT_ID STS bit does not get set initially */
mask = FG_RR_ADC_STS_CHANNEL_STS;
break;
default:
mask = FG_RR_ADC_STS_CHANNEL_READING_MASK;
break;
}
while (((buf[0] & mask) != mask) &&
(retry_cnt < FG_RR_CONV_MAX_RETRY_CNT)) {
pr_debug("%s is not ready; nothing to read:0x%x\n",
rradc_chans[prop->channel].datasheet_name, buf[0]);
usleep_range(FG_RR_CONV_CONTINUOUS_TIME_MIN_US,
FG_RR_CONV_CONTINUOUS_TIME_MAX_US);
retry_cnt++;
rc = rradc_read(chip, status, buf, 1);
if (rc < 0) {
pr_err("status read failed:%d\n", rc);
return rc;
}
}
if (retry_cnt >= FG_RR_CONV_MAX_RETRY_CNT)
rc = -ENODATA;
return rc;
}
static int rradc_read_channel_with_continuous_mode(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u8 *buf)
{
int rc = 0;
u16 status = 0;
rc = rradc_enable_continuous_mode(chip);
if (rc < 0) {
pr_err("Failed to switch to continuous mode\n");
return rc;
}
status = rradc_chans[prop->channel].sts;
rc = rradc_read(chip, status, buf, 1);
if (rc < 0) {
pr_err("status read failed:%d\n", rc);
return rc;
}
rc = rradc_check_status_ready_with_retry(chip, prop,
buf, status);
if (rc < 0) {
pr_err("Status read failed:%d\n", rc);
return rc;
}
rc = rradc_disable_continuous_mode(chip);
if (rc < 0) {
pr_err("Failed to switch to non continuous mode\n");
return rc;
}
return rc;
}
static int rradc_enable_batt_id_channel(struct rradc_chip *chip, bool enable)
{
int rc = 0;
if (enable) {
rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV,
FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV);
if (rc < 0) {
pr_err("Enabling BATT ID channel failed:%d\n", rc);
return rc;
}
} else {
rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_CTRL,
FG_ADC_RR_BATT_ID_CTRL_CHANNEL_CONV, 0);
if (rc < 0) {
pr_err("Disabling BATT ID channel failed:%d\n", rc);
return rc;
}
}
return rc;
}
static int rradc_do_batt_id_conversion(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 *data, u8 *buf)
{
int rc = 0, ret = 0;
rc = rradc_enable_batt_id_channel(chip, true);
if (rc < 0) {
pr_err("Enabling BATT ID channel failed:%d\n", rc);
return rc;
}
rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
FG_ADC_RR_BATT_ID_TRIGGER_CTL,
FG_ADC_RR_BATT_ID_TRIGGER_CTL);
if (rc < 0) {
pr_err("BATT_ID trigger set failed:%d\n", rc);
ret = rc;
rc = rradc_enable_batt_id_channel(chip, false);
if (rc < 0)
pr_err("Disabling BATT ID channel failed:%d\n", rc);
return ret;
}
rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
if (rc < 0) {
pr_err("Error reading in continuous mode:%d\n", rc);
ret = rc;
}
rc = rradc_masked_write(chip, FG_ADC_RR_BATT_ID_TRIGGER,
FG_ADC_RR_BATT_ID_TRIGGER_CTL, 0);
if (rc < 0) {
pr_err("BATT_ID trigger re-set failed:%d\n", rc);
ret = rc;
}
rc = rradc_enable_batt_id_channel(chip, false);
if (rc < 0) {
pr_err("Disabling BATT ID channel failed:%d\n", rc);
ret = rc;
}
return ret;
}
static int rradc_do_conversion(struct rradc_chip *chip,
struct rradc_chan_prop *prop, u16 *data)
{
int rc = 0, bytes_to_read = 0;
u8 buf[6];
u16 offset = 0, batt_id_5 = 0, batt_id_15 = 0, batt_id_150 = 0;
u16 status = 0;
mutex_lock(&chip->lock);
switch (prop->channel) {
case RR_ADC_BATT_ID:
rc = rradc_do_batt_id_conversion(chip, prop, data, buf);
if (rc < 0) {
pr_err("Battery ID conversion failed:%d\n", rc);
goto fail;
}
break;
case RR_ADC_USBIN_V:
/* Force conversion every cycle */
rc = rradc_masked_write(chip, FG_ADC_RR_USB_IN_V_TRIGGER,
FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK,
FG_ADC_RR_USB_IN_V_EVERY_CYCLE);
if (rc < 0) {
pr_err("Force every cycle update failed:%d\n", rc);
goto fail;
}
rc = rradc_read_channel_with_continuous_mode(chip, prop, buf);
if (rc < 0) {
pr_err("Error reading in continuous mode:%d\n", rc);
goto fail;
}
/* Restore usb_in trigger */
rc = rradc_masked_write(chip, FG_ADC_RR_USB_IN_V_TRIGGER,
FG_ADC_RR_USB_IN_V_EVERY_CYCLE_MASK, 0);
if (rc < 0) {
pr_err("Restore every cycle update failed:%d\n", rc);
goto fail;
}
break;
case RR_ADC_CHG_HOT_TEMP:
case RR_ADC_CHG_TOO_HOT_TEMP:
case RR_ADC_SKIN_HOT_TEMP:
case RR_ADC_SKIN_TOO_HOT_TEMP:
pr_debug("Read only the data registers\n");
break;
default:
status = rradc_chans[prop->channel].sts;
rc = rradc_read(chip, status, buf, 1);
if (rc < 0) {
pr_err("status read failed:%d\n", rc);
goto fail;
}
rc = rradc_check_status_ready_with_retry(chip, prop,
buf, status);
if (rc < 0) {
pr_debug("Status read failed:%d\n", rc);
rc = -ENODATA;
goto fail;
}
break;
}
offset = rradc_chans[prop->channel].lsb;
if (prop->channel == RR_ADC_BATT_ID)
bytes_to_read = 6;
else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
(prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
(prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
(prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP))
bytes_to_read = 1;
else
bytes_to_read = 2;
buf[0] = 0;
rc = rradc_read(chip, offset, buf, bytes_to_read);
if (rc) {
pr_err("read data failed\n");
goto fail;
}
if (prop->channel == RR_ADC_BATT_ID) {
batt_id_150 = (buf[5] << 8) | buf[4];
batt_id_15 = (buf[3] << 8) | buf[2];
batt_id_5 = (buf[1] << 8) | buf[0];
if ((!batt_id_150) && (!batt_id_15) && (!batt_id_5)) {
pr_err("Invalid batt_id values with all zeros\n");
rc = -EINVAL;
goto fail;
}
if (batt_id_150 <= FG_ADC_RR_BATT_ID_RANGE) {
pr_debug("Batt_id_150 is chosen\n");
*data = batt_id_150;
prop->channel_data = FG_ADC_RR_BATT_ID_150_MA;
} else if (batt_id_15 <= FG_ADC_RR_BATT_ID_RANGE) {
pr_debug("Batt_id_15 is chosen\n");
*data = batt_id_15;
prop->channel_data = FG_ADC_RR_BATT_ID_15_MA;
} else {
pr_debug("Batt_id_5 is chosen\n");
*data = batt_id_5;
prop->channel_data = FG_ADC_RR_BATT_ID_5_MA;
}
} else if ((prop->channel == RR_ADC_CHG_HOT_TEMP) ||
(prop->channel == RR_ADC_CHG_TOO_HOT_TEMP) ||
(prop->channel == RR_ADC_SKIN_HOT_TEMP) ||
(prop->channel == RR_ADC_SKIN_TOO_HOT_TEMP)) {
*data = buf[0];
} else {
*data = (buf[1] << 8) | buf[0];
}
fail:
mutex_unlock(&chip->lock);
return rc;
}
static int rradc_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2,
long mask)
{
struct rradc_chip *chip = iio_priv(indio_dev);
struct rradc_chan_prop *prop;
u16 adc_code;
int rc = 0;
if (chan->address >= RR_ADC_MAX) {
pr_err("Invalid channel index:%ld\n", chan->address);
return -EINVAL;
}
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
prop = &chip->chan_props[chan->address];
rc = rradc_do_conversion(chip, prop, &adc_code);
if (rc)
break;
prop->scale(chip, prop, adc_code, val);
return IIO_VAL_INT;
case IIO_CHAN_INFO_RAW:
prop = &chip->chan_props[chan->address];
rc = rradc_do_conversion(chip, prop, &adc_code);
if (rc)
break;
*val = (int) adc_code;
return IIO_VAL_INT;
default:
rc = -EINVAL;
break;
}
return rc;
}
static const struct iio_info rradc_info = {
.read_raw = &rradc_read_raw,
.driver_module = THIS_MODULE,
};
static int rradc_get_dt_data(struct rradc_chip *chip, struct device_node *node)
{
const struct rradc_channels *rradc_chan;
struct iio_chan_spec *iio_chan;
unsigned int i = 0, base;
int rc = 0;
struct rradc_chan_prop prop;
chip->nchannels = RR_ADC_MAX;
chip->iio_chans = devm_kcalloc(chip->dev, chip->nchannels,
sizeof(*chip->iio_chans), GFP_KERNEL);
if (!chip->iio_chans)
return -ENOMEM;
chip->chan_props = devm_kcalloc(chip->dev, chip->nchannels,
sizeof(*chip->chan_props), GFP_KERNEL);
if (!chip->chan_props)
return -ENOMEM;
/* Get the peripheral address */
rc = of_property_read_u32(node, "reg", &base);
if (rc < 0) {
dev_err(chip->dev,
"Couldn't find reg in node = %s rc = %d\n",
node->name, rc);
return rc;
}
chip->base = base;
chip->revid_dev_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
if (chip->revid_dev_node) {
chip->pmic_fab_id = get_revid_data(chip->revid_dev_node);
if (IS_ERR(chip->pmic_fab_id)) {
rc = PTR_ERR(chip->pmic_fab_id);
if (rc != -EPROBE_DEFER)
pr_err("Unable to get pmic_revid rc=%d\n", rc);
return rc;
}
if (!chip->pmic_fab_id)
return -EINVAL;
if (chip->pmic_fab_id->fab_id == -EINVAL) {
rc = chip->pmic_fab_id->fab_id;
pr_debug("Unable to read fabid rc=%d\n", rc);
}
}
iio_chan = chip->iio_chans;
for (i = 0; i < RR_ADC_MAX; i++) {
prop.channel = i;
prop.scale = rradc_chans[i].scale;
/* Private channel data used for selecting batt_id */
prop.channel_data = 0;
chip->chan_props[i] = prop;
rradc_chan = &rradc_chans[i];
iio_chan->channel = prop.channel;
iio_chan->datasheet_name = rradc_chan->datasheet_name;
iio_chan->extend_name = rradc_chan->datasheet_name;
iio_chan->info_mask_separate = rradc_chan->info_mask;
iio_chan->type = rradc_chan->type;
iio_chan->address = i;
iio_chan++;
}
return 0;
}
static int rradc_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct rradc_chip *chip;
int rc = 0;
indio_dev = devm_iio_device_alloc(dev, sizeof(*chip));
if (!indio_dev)
return -ENOMEM;
chip = iio_priv(indio_dev);
chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!chip->regmap) {
dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
return -EINVAL;
}
chip->dev = dev;
mutex_init(&chip->lock);
rc = rradc_get_dt_data(chip, node);
if (rc)
return rc;
indio_dev->dev.parent = dev;
indio_dev->dev.of_node = node;
indio_dev->name = pdev->name;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &rradc_info;
indio_dev->channels = chip->iio_chans;
indio_dev->num_channels = chip->nchannels;
return devm_iio_device_register(dev, indio_dev);
}
static const struct of_device_id rradc_match_table[] = {
{ .compatible = "qcom,rradc" },
{ }
};
MODULE_DEVICE_TABLE(of, rradc_match_table);
static struct platform_driver rradc_driver = {
.driver = {
.name = "qcom-rradc",
.of_match_table = rradc_match_table,
},
.probe = rradc_probe,
};
module_platform_driver(rradc_driver);
MODULE_DESCRIPTION("QPNP PMIC RR ADC driver");
MODULE_LICENSE("GPL v2");