blob: ef568ed3871cd7cc60e108e9372d47b5b55b2901 [file] [log] [blame]
/*
* s2mps20.c
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd
* http://www.samsung.com
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/mfd/samsung/s2mps20.h>
#include <linux/mfd/samsung/s2mps20-regulator.h>
#include <linux/platform_device.h>
#define CURRENT_METER 1
#define POWER_METER 2
#define SYNC_MODE 1
#define ASYNC_MODE 2
static struct adc_info *adc_meter;
struct device *s2mps20_adc_dev;
struct class *s2mps20_adc_class;
struct adc_info {
struct i2c_client *i2c;
u8 adc_mode;
u8 adc_sync_mode;
u8 *adc_reg;
u16 *adc_val;
u16 *current_val;
u16 *power_val;
u8 adc_ctrl1;
u8 ptr_base;
struct mutex adc_lock;
};
static const unsigned int current_buck_coeffs[S2MPS20_BUCK_CNT] =
{CURRENT_BD, CURRENT_BS, CURRENT_BS};
static const unsigned int current_ldo_coeffs[S2MPS20_LDO_CNT] =
{CURRENT_L300, CURRENT_L450, CURRENT_L300, CURRENT_L150, CURRENT_L150, CURRENT_L150,
CURRENT_L300, CURRENT_L450, CURRENT_L600, CURRENT_L300, CURRENT_L150, CURRENT_L150};
static const unsigned int power_buck_coeffs[S2MPS20_BUCK_CNT] =
{POWER_BD, POWER_BS, POWER_BS};
static const unsigned int power_ldo_coeffs[S2MPS20_LDO_CNT] =
{POWER_D300, POWER_P450, POWER_N300, POWER_N150, POWER_N150, POWER_P150, POWER_P300,
POWER_N450, POWER_N600, POWER_P300, POWER_P150, POWER_P150};
static unsigned int get_coeff_c(struct device *dev, u8 adc_reg_num)
{
unsigned int coeff;
/* if the regulator is LDO */
if (adc_reg_num >= S2MPS20_LDO_START && adc_reg_num <= S2MPS20_LDO_END)
coeff = current_ldo_coeffs[adc_reg_num - S2MPS20_LDO_START];
/* if the regulator is BUCK */
else if (adc_reg_num >= S2MPS20_BUCK_START && adc_reg_num <= S2MPS20_BUCK_END)
coeff = current_buck_coeffs[adc_reg_num - S2MPS20_BUCK_START];
else {
dev_err(dev, "%s: invalid adc regulator number(%d)\n", __func__, adc_reg_num);
coeff = 0;
}
return coeff;
}
static unsigned int get_coeff_p(struct device *dev, u8 adc_reg_num)
{
unsigned int coeff;
/* if the regulator is LDO */
if (adc_reg_num >= S2MPS20_LDO_START && adc_reg_num <= S2MPS20_LDO_END)
coeff = power_ldo_coeffs[adc_reg_num - S2MPS20_LDO_START];
/* if the regulator is BUCK */
else if (adc_reg_num >= S2MPS20_BUCK_START && adc_reg_num <= S2MPS20_BUCK_END)
coeff = power_buck_coeffs[adc_reg_num - S2MPS20_BUCK_START];
else {
dev_err(dev, "%s: invalid adc regulator number(%d)\n", __func__, adc_reg_num);
coeff = 0;
}
return coeff;
}
static void s2m_adc_read_data(struct device *dev, int channel)
{
int i;
u8 data_l, data_h;
mutex_lock(&adc_meter->adc_lock);
/* ASYNCRD bit '1' --> 2ms delay --> read in case of ADC Async mode */
if (adc_meter->adc_sync_mode == ASYNC_MODE) {
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL2, ADC_ASYNCRD_MASK, ADC_ASYNCRD_MASK);
usleep_range(2000, 2100);
}
if (channel < 0) {
/* current */
for (i = 0; i < S2MPS20_MAX_ADC_CHANNEL; i++) {
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(i + CURRENT_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_l);
adc_meter->current_val[i] = data_l;
}
/* power */
for (i = 0; i < S2MPS20_MAX_ADC_CHANNEL; i++) {
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(2*i + POWER_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_l);
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(2*i+1 + POWER_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_h);
adc_meter->power_val[i] = ((data_h & 0xff) << 8) | (data_l & 0xff);
}
} else {
/* current */
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(channel + CURRENT_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_l);
adc_meter->current_val[channel] = data_l;
/* power */
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(2*channel + POWER_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_l);
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
(2*channel+1 + POWER_PTR_BASE) | ADC_EN_MASK, ADC_PTR_MASK | ADC_EN_MASK);
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, &data_h);
adc_meter->power_val[channel] = ((data_h & 0xff) << 8) | (data_l & 0xff);
}
mutex_unlock(&adc_meter->adc_lock);
}
static ssize_t adc_val_power_show(struct device *dev, struct device_attribute *attr, char *buf)
{
s2m_adc_read_data(dev, -1);
return snprintf(buf, PAGE_SIZE, "CH0[%x]:%d uW (%d), CH1[%x]:%d uW (%d), CH2[%x]:%d uW (%d)\n",
adc_meter->adc_reg[0], ((adc_meter->power_val[0] >> 8) * get_coeff_p(dev, adc_meter->adc_reg[0])), adc_meter->power_val[0],
adc_meter->adc_reg[1], ((adc_meter->power_val[1] >> 8) * get_coeff_p(dev, adc_meter->adc_reg[1])), adc_meter->power_val[1],
adc_meter->adc_reg[2], ((adc_meter->power_val[2] >> 8) * get_coeff_p(dev, adc_meter->adc_reg[2])), adc_meter->power_val[2]);
}
static ssize_t adc_val_current_show(struct device *dev, struct device_attribute *attr, char *buf)
{
s2m_adc_read_data(dev, -1);
return snprintf(buf, PAGE_SIZE, "CH0[%x]:%d uA (%d), CH1[%x]:%d uA (%d), CH2[%x]:%d uA (%d)\n",
adc_meter->adc_reg[0], adc_meter->current_val[0] * get_coeff_c(dev, adc_meter->adc_reg[0])/1000, adc_meter->current_val[0],
adc_meter->adc_reg[1], adc_meter->current_val[1] * get_coeff_c(dev, adc_meter->adc_reg[1])/1000, adc_meter->current_val[1],
adc_meter->adc_reg[2], adc_meter->current_val[2] * get_coeff_c(dev, adc_meter->adc_reg[2])/1000, adc_meter->current_val[2]);
}
static ssize_t adc_en_show(struct device *dev, struct device_attribute *attr, char *buf)
{
u8 adc_ctrl3;
s2mps20_read_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3, &adc_ctrl3);
if (adc_ctrl3 & ADC_EN_MASK)
return snprintf(buf, PAGE_SIZE, "ADC enable (%x)\n", adc_ctrl3);
else
return snprintf(buf, PAGE_SIZE, "ADC disable (%x)\n", adc_ctrl3);
}
static ssize_t adc_en_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
u8 temp, val;
ret = kstrtou8(buf, 16, &temp);
if (ret)
return -EINVAL;
else {
switch (temp) {
case 0 :
val = 0x00;
break;
case 1 :
val = 0x80;
break;
default :
val = 0x00;
break;
}
}
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3, val, ADC_EN_MASK);
return count;
}
static ssize_t adc_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
switch (adc_meter->adc_mode) {
case CURRENT_METER :
return snprintf(buf, PAGE_SIZE, "CURRENT MODE (%d)\n", CURRENT_METER);
case POWER_METER :
return snprintf(buf, PAGE_SIZE, "POWER MODE (%d)\n", POWER_METER);
default :
return snprintf(buf, PAGE_SIZE, "error\n");
}
}
static ssize_t adc_mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
u8 temp;
ret = kstrtou8(buf, 16, &temp);
if (ret)
return -EINVAL;
else {
switch (temp) {
case CURRENT_METER :
adc_meter->adc_mode = CURRENT_METER;
adc_meter->ptr_base = CURRENT_PTR_BASE;
break;
case POWER_METER :
adc_meter->adc_mode = POWER_METER;
adc_meter->ptr_base = POWER_PTR_BASE;
break;
default :
adc_meter->adc_mode = CURRENT_METER;
adc_meter->ptr_base = CURRENT_PTR_BASE;
break;
}
return count;
}
}
static ssize_t adc_sync_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
{
switch (adc_meter->adc_sync_mode) {
case SYNC_MODE:
return snprintf(buf, PAGE_SIZE, "SYNC_MODE (%d)\n", adc_meter->adc_sync_mode);
case ASYNC_MODE:
return snprintf(buf, PAGE_SIZE, "ASYNC_MODE (%d)\n", adc_meter->adc_sync_mode);
default:
return snprintf(buf, PAGE_SIZE, "error (%x)\n", adc_meter->adc_sync_mode);
}
}
static ssize_t adc_sync_mode_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
u8 temp;
ret = kstrtou8(buf, 16, &temp);
if (ret)
return -EINVAL;
switch (temp) {
case SYNC_MODE:
adc_meter->adc_sync_mode = SYNC_MODE;
break;
case ASYNC_MODE:
adc_meter->adc_sync_mode = ASYNC_MODE;
break;
default:
adc_meter->adc_sync_mode = SYNC_MODE;
break;
}
return count;
}
static ssize_t adc_val_0_show(struct device *dev, struct device_attribute *attr, char *buf)
{
unsigned int coeff_p = get_coeff_p(dev, adc_meter->adc_reg[0]);
unsigned int coeff_c = get_coeff_c(dev, adc_meter->adc_reg[0]);
s2m_adc_read_data(dev, 0);
return snprintf(buf, PAGE_SIZE, "[CH0] %d(0x%x)uA, %d(0x%x) uW\n",
adc_meter->current_val[0] * coeff_c / 1000, adc_meter->current_val[0],
(adc_meter->power_val[0] >> 8) * coeff_p, adc_meter->power_val[0]);
}
static ssize_t adc_val_1_show(struct device *dev, struct device_attribute *attr, char *buf)
{
unsigned int coeff_p = get_coeff_p(dev, adc_meter->adc_reg[1]);
unsigned int coeff_c = get_coeff_c(dev, adc_meter->adc_reg[1]);
s2m_adc_read_data(dev, 1);
return snprintf(buf, PAGE_SIZE, "[CH0] %d(0x%x)uA, %d(0x%x) uW\n",
adc_meter->current_val[1] * coeff_c / 1000, adc_meter->current_val[1],
(adc_meter->power_val[1] >> 8) * coeff_p, adc_meter->power_val[1]);
}
static ssize_t adc_val_2_show(struct device *dev, struct device_attribute *attr, char *buf)
{
unsigned int coeff_p = get_coeff_p(dev, adc_meter->adc_reg[2]);
unsigned int coeff_c = get_coeff_c(dev, adc_meter->adc_reg[2]);
s2m_adc_read_data(dev, 2);
return snprintf(buf, PAGE_SIZE, "[CH0] %d(0x%x)uA, %d(0x%x) uW\n",
adc_meter->current_val[2] * coeff_c / 1000, adc_meter->current_val[2],
(adc_meter->power_val[2] >> 8) * coeff_p, adc_meter->power_val[2]);
}
static ssize_t adc_reg_0_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%2x\n", adc_meter->adc_reg[0]);
}
static ssize_t adc_reg_1_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%2x\n", adc_meter->adc_reg[1]);
}
static ssize_t adc_reg_2_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%2x\n", adc_meter->adc_reg[2]);
}
static void adc_reg_update(struct device *dev)
{
int i = 0;
/* ADC OFF */
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
0x00, ADC_EN_MASK);
/* CHANNEL setting */
for (i = 0; i < S2MPS20_MAX_ADC_CHANNEL; i++) {
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3,
i + MUX_PTR_BASE, ADC_PTR_MASK);
s2mps20_write_reg(adc_meter->i2c, S2MPS20_REG_ADC_DATA, adc_meter->adc_reg[i]);
}
/* ADC EN */
switch (adc_meter->adc_mode) {
case CURRENT_METER :
adc_meter->adc_mode = CURRENT_METER;
adc_meter->ptr_base = CURRENT_PTR_BASE;
pr_info("%s: current mode enable\n", __func__);
break;
case POWER_METER:
adc_meter->adc_mode = POWER_METER;
adc_meter->ptr_base = POWER_PTR_BASE;
pr_info("%s: power mode enable\n", __func__);
break;
default :
adc_meter->adc_mode = CURRENT_METER;
adc_meter->ptr_base = CURRENT_PTR_BASE;
pr_info("%s: current mode enable\n", __func__);
break;
}
}
static u8 buf_to_adc_reg(const char *buf)
{
u8 adc_reg_num;
if (kstrtou8(buf, 16, &adc_reg_num))
return 0;
if ((adc_reg_num >= S2MPS20_BUCK_START && adc_reg_num <= S2MPS20_BUCK_END) ||
(adc_reg_num >= S2MPS20_LDO_START && adc_reg_num <= S2MPS20_LDO_END))
return adc_reg_num;
else
return 0;
}
static ssize_t adc_reg_0_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u8 adc_reg_num = buf_to_adc_reg(buf);
if (!adc_reg_num)
return -EINVAL;
else {
adc_meter->adc_reg[0] = adc_reg_num;
adc_reg_update(dev);
return count;
}
}
static ssize_t adc_reg_1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u8 adc_reg_num = buf_to_adc_reg(buf);
if (!adc_reg_num)
return -EINVAL;
else {
adc_meter->adc_reg[1] = adc_reg_num;
adc_reg_update(dev);
return count;
}
}
static ssize_t adc_reg_2_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
u8 adc_reg_num = buf_to_adc_reg(buf);
if (!adc_reg_num)
return -EINVAL;
else {
adc_meter->adc_reg[2] = adc_reg_num;
adc_reg_update(dev);
return count;
}
}
static void adc_ctrl1_update(struct device *dev)
{
/* ADC temporarily off */
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3, 0x00, ADC_EN_MASK);
/* update ADC_CTRL1 register */
s2mps20_write_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL1, adc_meter->adc_ctrl1);
/* ADC Continuous ON */
s2mps20_update_reg(adc_meter->i2c, S2MPS20_REG_ADC_CTRL3, ADC_EN_MASK, ADC_EN_MASK);
}
static ssize_t adc_ctrl1_show(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, PAGE_SIZE, "0x%2x\n", adc_meter->adc_ctrl1);
}
static ssize_t adc_ctrl1_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int ret;
u8 temp;
ret = kstrtou8(buf, 16, &temp);
if (ret)
return -EINVAL;
else {
temp &= 0x0f;
adc_meter->adc_ctrl1 &= 0xf0;
adc_meter->adc_ctrl1 |= temp;
adc_ctrl1_update(dev);
return count;
}
}
static DEVICE_ATTR(power_val_all, 0444, adc_val_power_show, NULL);
static DEVICE_ATTR(current_val_all, 0444, adc_val_current_show, NULL);
static DEVICE_ATTR(adc_en, 0644, adc_en_show, adc_en_store);
static DEVICE_ATTR(adc_mode, 0644, adc_mode_show, adc_mode_store);
static DEVICE_ATTR(adc_sync_mode, 0644, adc_sync_mode_show, adc_sync_mode_store);
static DEVICE_ATTR(adc_val_0, 0444, adc_val_0_show, NULL);
static DEVICE_ATTR(adc_val_1, 0444, adc_val_1_show, NULL);
static DEVICE_ATTR(adc_val_2, 0444, adc_val_2_show, NULL);
static DEVICE_ATTR(adc_reg_0, 0644, adc_reg_0_show, adc_reg_0_store);
static DEVICE_ATTR(adc_reg_1, 0644, adc_reg_1_show, adc_reg_1_store);
static DEVICE_ATTR(adc_reg_2, 0644, adc_reg_2_show, adc_reg_2_store);
static DEVICE_ATTR(adc_ctrl1, 0644, adc_ctrl1_show, adc_ctrl1_store);
void s2mps20_powermeter_init(struct s2mps20_dev *s2mps20)
{
int i, ret;
adc_meter = kzalloc(sizeof(struct adc_info), GFP_KERNEL);
if (!adc_meter) {
pr_err("%s: adc_meter alloc fail.\n", __func__);
return;
}
adc_meter->current_val = kzalloc(sizeof(u16)*S2MPS20_MAX_ADC_CHANNEL, GFP_KERNEL);
adc_meter->power_val = kzalloc(sizeof(u16)*S2MPS20_MAX_ADC_CHANNEL, GFP_KERNEL);
adc_meter->adc_reg = kzalloc(sizeof(u8)*S2MPS20_MAX_ADC_CHANNEL, GFP_KERNEL);
pr_info("%s: s2mps20 power meter init start\n", __func__);
/* initial regulators : BUCK 1,2,3,4,5,6,7,8 */
adc_meter->adc_reg[0] = 0x1;
adc_meter->adc_reg[1] = 0x2;
adc_meter->adc_reg[2] = 0x3;
adc_meter->adc_mode = s2mps20->adc_mode;
adc_meter->adc_sync_mode = s2mps20->adc_sync_mode;
mutex_init(&adc_meter->adc_lock);
/* SMP_NUM=1011(16384), RATIO=10(125khz), (8us x 16384 x 16 x 8ch)=~16s in case of async mode */
if (adc_meter->adc_sync_mode == ASYNC_MODE) {
adc_meter->adc_ctrl1 = 0x2B;
s2mps20_write_reg(s2mps20->pmic, S2MPS20_REG_ADC_CTRL1, adc_meter->adc_ctrl1);
}
/* enable DC offset calibration */
s2mps20_update_reg(s2mps20->pmic, S2MPS20_REG_ADC_CTRL2, ADC_CAL_EN_MASK, ADC_CAL_EN_MASK);
/* CHANNEL setting */
for (i = 0; i < S2MPS20_MAX_ADC_CHANNEL; i++) {
s2mps20_update_reg(s2mps20->pmic, S2MPS20_REG_ADC_CTRL3, i + MUX_PTR_BASE, ADC_PTR_MASK);
s2mps20_write_reg(s2mps20->pmic, S2MPS20_REG_ADC_DATA, adc_meter->adc_reg[i]);
}
/* set ptr_base according to adc_mode */
switch (adc_meter->adc_mode) {
case CURRENT_METER :
adc_meter->ptr_base = CURRENT_PTR_BASE;
break;
case POWER_METER :
adc_meter->ptr_base = POWER_PTR_BASE;
break;
default :
adc_meter->adc_mode = CURRENT_METER;
adc_meter->ptr_base = CURRENT_PTR_BASE;
break;
}
/* turn on ADC */
s2mps20_update_reg(s2mps20->pmic, S2MPS20_REG_ADC_CTRL3, ADC_EN_MASK, ADC_EN_MASK);
adc_meter->i2c = s2mps20->pmic;
s2mps20_adc_class = class_create(THIS_MODULE, "s2mps20_adc_meter");
s2mps20_adc_dev = device_create(s2mps20_adc_class, NULL, 0, NULL, "s2mps20_adc");
/* create sysfs entries */
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_en);
if (ret)
goto err_free;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_mode);
if (ret)
goto remove_adc_en;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_sync_mode);
if (ret)
goto remove_adc_mode;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_power_val_all);
if (ret)
goto remove_adc_sync_mode;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_current_val_all);
if (ret)
goto remove_power_val_all;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_val_0);
if (ret)
goto remove_current_val_all;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_val_1);
if (ret)
goto remove_adc_val_0;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_val_2);
if (ret)
goto remove_adc_val_1;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_reg_0);
if (ret)
goto remove_adc_val_2;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_reg_1);
if (ret)
goto remove_adc_reg_0;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_reg_2);
if (ret)
goto remove_adc_reg_1;
ret = device_create_file(s2mps20_adc_dev, &dev_attr_adc_ctrl1);
if (ret)
goto remove_adc_reg_2;
#ifdef CONFIG_SEC_PM
if (!IS_ERR_OR_NULL(ap_sub_pmic_dev)) {
ret = sysfs_create_link(&ap_sub_pmic_dev->kobj,
&s2mps20_adc_dev->kobj, "power_meter");
if (ret)
pr_err("%s: fail to create link for power_meter(%d)\n",
__func__, ret);
}
#endif /* CONFIG_SEC_PM */
pr_info("%s: s2mps20 power meter init end\n", __func__);
return ;
remove_adc_reg_2:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_2);
remove_adc_reg_1:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_1);
remove_adc_reg_0:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_0);
remove_adc_val_2:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_2);
remove_adc_val_1:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_1);
remove_adc_val_0:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_0);
remove_current_val_all:
device_remove_file(s2mps20_adc_dev, &dev_attr_current_val_all);
remove_power_val_all:
device_remove_file(s2mps20_adc_dev, &dev_attr_power_val_all);
remove_adc_sync_mode:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_sync_mode);
remove_adc_mode:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_mode);
remove_adc_en:
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_en);
err_free:
kfree(adc_meter->adc_val);
kfree(adc_meter->adc_reg);
dev_info(s2mps20->dev, "%s : fail to create sysfs\n",__func__);
return ;
}
void s2mps20_powermeter_deinit(struct s2mps20_dev *s2mps20)
{
/* remove sysfs entries */
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_en);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_mode);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_sync_mode);
device_remove_file(s2mps20_adc_dev, &dev_attr_power_val_all);
device_remove_file(s2mps20_adc_dev, &dev_attr_current_val_all);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_0);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_1);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_val_2);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_0);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_1);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_reg_2);
device_remove_file(s2mps20_adc_dev, &dev_attr_adc_ctrl1);
/* ADC turned off */
s2mps20_update_reg(s2mps20->pmic, S2MPS20_REG_ADC_CTRL3, 0, ADC_EN_MASK);
kfree(adc_meter->adc_val);
kfree(adc_meter->adc_reg);
}