Commit 73482346 authored by Lee Jones's avatar Lee Jones

mfd: ab8500-gpadc: Add gpadc hw conversion

Add the support of gpacd hw conversion and make the number of
sample configurable.
Signed-off-by: default avatarM'boumba Cedric Madianga <cedric.madianga@stericsson.com>
Signed-off-by: default avatarLee Jones <lee.jones@linaro.org>
Reviewed-by: default avatarMattias WALLIN <mattias.wallin@stericsson.com>
Tested-by: default avatarMichel JAOUEN <michel.jaouen@stericsson.com>
Acked-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent d89cc5aa
...@@ -101,6 +101,11 @@ static int num_irqs; ...@@ -101,6 +101,11 @@ static int num_irqs;
static struct device_attribute **dev_attr; static struct device_attribute **dev_attr;
static char **event_name; static char **event_name;
static u8 avg_sample = SAMPLE_16;
static u8 trig_edge = RISING_EDGE;
static u8 conv_type = ADC_SW;
static u8 trig_timer;
/** /**
* struct ab8500_reg_range * struct ab8500_reg_range
* @first: the first address of the range * @first: the first address of the range
...@@ -808,7 +813,8 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p) ...@@ -808,7 +813,8 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL); bat_ctrl_raw = ab8500_gpadc_read_raw(gpadc, BAT_CTRL,
avg_sample, trig_edge, trig_timer, conv_type);
bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc, bat_ctrl_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BAT_CTRL, bat_ctrl_raw); BAT_CTRL, bat_ctrl_raw);
...@@ -836,7 +842,8 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p) ...@@ -836,7 +842,8 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL); btemp_ball_raw = ab8500_gpadc_read_raw(gpadc, BTEMP_BALL,
avg_sample, trig_edge, trig_timer, conv_type);
btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL, btemp_ball_convert = ab8500_gpadc_ad_to_voltage(gpadc, BTEMP_BALL,
btemp_ball_raw); btemp_ball_raw);
...@@ -865,7 +872,8 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p) ...@@ -865,7 +872,8 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V); main_charger_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_V,
avg_sample, trig_edge, trig_timer, conv_type);
main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, main_charger_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_V, main_charger_v_raw); MAIN_CHARGER_V, main_charger_v_raw);
...@@ -895,7 +903,8 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p) ...@@ -895,7 +903,8 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1); acc_detect1_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT1,
avg_sample, trig_edge, trig_timer, conv_type);
acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1, acc_detect1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ACC_DETECT1,
acc_detect1_raw); acc_detect1_raw);
...@@ -925,7 +934,8 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p) ...@@ -925,7 +934,8 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2); acc_detect2_raw = ab8500_gpadc_read_raw(gpadc, ACC_DETECT2,
avg_sample, trig_edge, trig_timer, conv_type);
acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc, acc_detect2_convert = ab8500_gpadc_ad_to_voltage(gpadc,
ACC_DETECT2, acc_detect2_raw); ACC_DETECT2, acc_detect2_raw);
...@@ -955,7 +965,8 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p) ...@@ -955,7 +965,8 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1); aux1_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX1,
avg_sample, trig_edge, trig_timer, conv_type);
aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1, aux1_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX1,
aux1_raw); aux1_raw);
...@@ -983,7 +994,8 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p) ...@@ -983,7 +994,8 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2); aux2_raw = ab8500_gpadc_read_raw(gpadc, ADC_AUX2,
avg_sample, trig_edge, trig_timer, conv_type);
aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2, aux2_convert = ab8500_gpadc_ad_to_voltage(gpadc, ADC_AUX2,
aux2_raw); aux2_raw);
...@@ -1011,7 +1023,8 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p) ...@@ -1011,7 +1023,8 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V); main_bat_v_raw = ab8500_gpadc_read_raw(gpadc, MAIN_BAT_V,
avg_sample, trig_edge, trig_timer, conv_type);
main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V, main_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, MAIN_BAT_V,
main_bat_v_raw); main_bat_v_raw);
...@@ -1040,7 +1053,8 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p) ...@@ -1040,7 +1053,8 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V); vbus_v_raw = ab8500_gpadc_read_raw(gpadc, VBUS_V,
avg_sample, trig_edge, trig_timer, conv_type);
vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V, vbus_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, VBUS_V,
vbus_v_raw); vbus_v_raw);
...@@ -1068,7 +1082,8 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p) ...@@ -1068,7 +1082,8 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C); main_charger_c_raw = ab8500_gpadc_read_raw(gpadc, MAIN_CHARGER_C,
avg_sample, trig_edge, trig_timer, conv_type);
main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, main_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
MAIN_CHARGER_C, main_charger_c_raw); MAIN_CHARGER_C, main_charger_c_raw);
...@@ -1098,7 +1113,8 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p) ...@@ -1098,7 +1113,8 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C); usb_charger_c_raw = ab8500_gpadc_read_raw(gpadc, USB_CHARGER_C,
avg_sample, trig_edge, trig_timer, conv_type);
usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc, usb_charger_c_convert = ab8500_gpadc_ad_to_voltage(gpadc,
USB_CHARGER_C, usb_charger_c_raw); USB_CHARGER_C, usb_charger_c_raw);
...@@ -1128,7 +1144,8 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p) ...@@ -1128,7 +1144,8 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V); bk_bat_v_raw = ab8500_gpadc_read_raw(gpadc, BK_BAT_V,
avg_sample, trig_edge, trig_timer, conv_type);
bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc, bk_bat_v_convert = ab8500_gpadc_ad_to_voltage(gpadc,
BK_BAT_V, bk_bat_v_raw); BK_BAT_V, bk_bat_v_raw);
...@@ -1156,7 +1173,8 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p) ...@@ -1156,7 +1173,8 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
struct ab8500_gpadc *gpadc; struct ab8500_gpadc *gpadc;
gpadc = ab8500_gpadc_get("ab8500-gpadc.0"); gpadc = ab8500_gpadc_get("ab8500-gpadc.0");
die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP); die_temp_raw = ab8500_gpadc_read_raw(gpadc, DIE_TEMP,
avg_sample, trig_edge, trig_timer, conv_type);
die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP, die_temp_convert = ab8500_gpadc_ad_to_voltage(gpadc, DIE_TEMP,
die_temp_raw); die_temp_raw);
...@@ -1177,6 +1195,208 @@ static const struct file_operations ab8500_gpadc_die_temp_fops = { ...@@ -1177,6 +1195,208 @@ static const struct file_operations ab8500_gpadc_die_temp_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
static int ab8500_gpadc_avg_sample_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", avg_sample);
}
static int ab8500_gpadc_avg_sample_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_avg_sample_print,
inode->i_private);
}
static ssize_t ab8500_gpadc_avg_sample_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_avg_sample;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_avg_sample);
if (err)
return -EINVAL;
if ((user_avg_sample == SAMPLE_1) || (user_avg_sample == SAMPLE_4)
|| (user_avg_sample == SAMPLE_8)
|| (user_avg_sample == SAMPLE_16)) {
avg_sample = (u8) user_avg_sample;
} else {
dev_err(dev, "debugfs error input: "
"should be egal to 1, 4, 8 or 16\n");
return -EINVAL;
}
return buf_size;
}
static const struct file_operations ab8500_gpadc_avg_sample_fops = {
.open = ab8500_gpadc_avg_sample_open,
.read = seq_read,
.write = ab8500_gpadc_avg_sample_write,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_trig_edge_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", trig_edge);
}
static int ab8500_gpadc_trig_edge_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_trig_edge_print,
inode->i_private);
}
static ssize_t ab8500_gpadc_trig_edge_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_trig_edge;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_trig_edge);
if (err)
return -EINVAL;
if ((user_trig_edge == RISING_EDGE)
|| (user_trig_edge == FALLING_EDGE)) {
trig_edge = (u8) user_trig_edge;
} else {
dev_err(dev, "Wrong input:\n"
"Enter 0. Rising edge\n"
"Enter 1. Falling edge\n");
return -EINVAL;
}
return buf_size;
}
static const struct file_operations ab8500_gpadc_trig_edge_fops = {
.open = ab8500_gpadc_trig_edge_open,
.read = seq_read,
.write = ab8500_gpadc_trig_edge_write,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_trig_timer_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", trig_timer);
}
static int ab8500_gpadc_trig_timer_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_trig_timer_print,
inode->i_private);
}
static ssize_t ab8500_gpadc_trig_timer_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_trig_timer;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_trig_timer);
if (err)
return -EINVAL;
if ((user_trig_timer >= 0) && (user_trig_timer <= 255)) {
trig_timer = (u8) user_trig_timer;
} else {
dev_err(dev, "debugfs error input: "
"should be beetween 0 to 255\n");
return -EINVAL;
}
return buf_size;
}
static const struct file_operations ab8500_gpadc_trig_timer_fops = {
.open = ab8500_gpadc_trig_timer_open,
.read = seq_read,
.write = ab8500_gpadc_trig_timer_write,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int ab8500_gpadc_conv_type_print(struct seq_file *s, void *p)
{
return seq_printf(s, "%d\n", conv_type);
}
static int ab8500_gpadc_conv_type_open(struct inode *inode, struct file *file)
{
return single_open(file, ab8500_gpadc_conv_type_print,
inode->i_private);
}
static ssize_t ab8500_gpadc_conv_type_write(struct file *file,
const char __user *user_buf,
size_t count, loff_t *ppos)
{
struct device *dev = ((struct seq_file *)(file->private_data))->private;
char buf[32];
int buf_size;
unsigned long user_conv_type;
int err;
/* Get userspace string and assure termination */
buf_size = min(count, (sizeof(buf) - 1));
if (copy_from_user(buf, user_buf, buf_size))
return -EFAULT;
buf[buf_size] = 0;
err = strict_strtoul(buf, 0, &user_conv_type);
if (err)
return -EINVAL;
if ((user_conv_type == ADC_SW)
|| (user_conv_type == ADC_HW)) {
conv_type = (u8) user_conv_type;
} else {
dev_err(dev, "Wrong input:\n"
"Enter 0. ADC SW conversion\n"
"Enter 1. ADC HW conversion\n");
return -EINVAL;
}
return buf_size;
}
static const struct file_operations ab8500_gpadc_conv_type_fops = {
.open = ab8500_gpadc_conv_type_open,
.read = seq_read,
.write = ab8500_gpadc_conv_type_write,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
/* /*
* return length of an ASCII numerical value, 0 is string is not a * return length of an ASCII numerical value, 0 is string is not a
* numerical value. * numerical value.
...@@ -1722,6 +1942,26 @@ static int ab8500_debug_probe(struct platform_device *plf) ...@@ -1722,6 +1942,26 @@ static int ab8500_debug_probe(struct platform_device *plf)
if (!file) if (!file)
goto err; goto err;
file = debugfs_create_file("avg_sample", (S_IRUGO | S_IWUGO),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_avg_sample_fops);
if (!file)
goto err;
file = debugfs_create_file("trig_edge", (S_IRUGO | S_IWUGO),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_edge_fops);
if (!file)
goto err;
file = debugfs_create_file("trig_timer", (S_IRUGO | S_IWUGO),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_trig_timer_fops);
if (!file)
goto err;
file = debugfs_create_file("conv_type", (S_IRUGO | S_IWUGO),
ab8500_gpadc_dir, &plf->dev, &ab8500_gpadc_conv_type_fops);
if (!file)
goto err;
return 0; return 0;
err: err:
......
...@@ -55,13 +55,18 @@ ...@@ -55,13 +55,18 @@
#define EN_VTVOUT 0x02 #define EN_VTVOUT 0x02
#define EN_GPADC 0x01 #define EN_GPADC 0x01
#define DIS_GPADC 0x00 #define DIS_GPADC 0x00
#define SW_AVG_16 0x60 #define AVG_1 0x00
#define AVG_4 0x20
#define AVG_8 0x40
#define AVG_16 0x60
#define ADC_SW_CONV 0x04 #define ADC_SW_CONV 0x04
#define EN_ICHAR 0x80 #define EN_ICHAR 0x80
#define BTEMP_PULL_UP 0x08 #define BTEMP_PULL_UP 0x08
#define EN_BUF 0x40 #define EN_BUF 0x40
#define DIS_ZERO 0x00 #define DIS_ZERO 0x00
#define GPADC_BUSY 0x01 #define GPADC_BUSY 0x01
#define EN_FALLING 0x10
#define EN_TRIG_EDGE 0x02
/* GPADC constants from AB8500 spec, UM0836 */ /* GPADC constants from AB8500 spec, UM0836 */
#define ADC_RESOLUTION 1024 #define ADC_RESOLUTION 1024
...@@ -116,7 +121,10 @@ struct adc_cal_data { ...@@ -116,7 +121,10 @@ struct adc_cal_data {
* the completion of gpadc conversion * the completion of gpadc conversion
* @ab8500_gpadc_lock: structure of type mutex * @ab8500_gpadc_lock: structure of type mutex
* @regu: pointer to the struct regulator * @regu: pointer to the struct regulator
* @irq: interrupt number that is used by gpadc * @irq_sw: interrupt number that is used by gpadc for Sw
* conversion
* @irq_hw: interrupt number that is used by gpadc for Hw
* conversion
* @cal_data array of ADC calibration data structs * @cal_data array of ADC calibration data structs
*/ */
struct ab8500_gpadc { struct ab8500_gpadc {
...@@ -126,7 +134,8 @@ struct ab8500_gpadc { ...@@ -126,7 +134,8 @@ struct ab8500_gpadc {
struct completion ab8500_gpadc_complete; struct completion ab8500_gpadc_complete;
struct mutex ab8500_gpadc_lock; struct mutex ab8500_gpadc_lock;
struct regulator *regu; struct regulator *regu;
int irq; int irq_sw;
int irq_hw;
struct adc_cal_data cal_data[NBR_CAL_INPUTS]; struct adc_cal_data cal_data[NBR_CAL_INPUTS];
}; };
...@@ -244,30 +253,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, ...@@ -244,30 +253,35 @@ int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel,
EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage);
/** /**
* ab8500_gpadc_convert() - gpadc conversion * ab8500_gpadc_sw_hw_convert() - gpadc conversion
* @channel: analog channel to be converted to digital data * @channel: analog channel to be converted to digital data
* @avg_sample: number of ADC sample to average
* @trig_egde: selected ADC trig edge
* @trig_timer: selected ADC trigger delay timer
* @conv_type: selected conversion type (HW or SW conversion)
* *
* This function converts the selected analog i/p to digital * This function converts the selected analog i/p to digital
* data. * data.
*/ */
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{ {
int ad_value; int ad_value;
int voltage; int voltage;
ad_value = ab8500_gpadc_read_raw(gpadc, channel); ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
/* On failure retry a second time */ /* On failure retry a second time */
if (ad_value < 0) if (ad_value < 0)
ad_value = ab8500_gpadc_read_raw(gpadc, channel); ad_value = ab8500_gpadc_read_raw(gpadc, channel, avg_sample,
trig_edge, trig_timer, conv_type);
if (ad_value < 0) { if (ad_value < 0) {
dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel); dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n",
channel);
return ad_value; return ad_value;
} }
voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);
if (voltage < 0) if (voltage < 0)
dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:" dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
" %d AD: 0x%x\n", channel, ad_value); " %d AD: 0x%x\n", channel, ad_value);
...@@ -279,11 +293,16 @@ EXPORT_SYMBOL(ab8500_gpadc_convert); ...@@ -279,11 +293,16 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
/** /**
* ab8500_gpadc_read_raw() - gpadc read * ab8500_gpadc_read_raw() - gpadc read
* @channel: analog channel to be read * @channel: analog channel to be read
* @avg_sample: number of ADC sample to average
* @trig_edge: selected trig edge
* @trig_timer: selected ADC trigger delay timer
* @conv_type: selected conversion type (HW or SW conversion)
* *
* This function obtains the raw ADC value, this then needs * This function obtains the raw ADC value for an hardware conversion,
* to be converted by calling ab8500_gpadc_ad_to_voltage() * this then needs to be converted by calling ab8500_gpadc_ad_to_voltage()
*/ */
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type)
{ {
int ret; int ret;
int looplimit = 0; int looplimit = 0;
...@@ -293,7 +312,6 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -293,7 +312,6 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
return -ENODEV; return -ENODEV;
mutex_lock(&gpadc->ab8500_gpadc_lock); mutex_lock(&gpadc->ab8500_gpadc_lock);
/* Enable VTVout LDO this is required for GPADC */ /* Enable VTVout LDO this is required for GPADC */
pm_runtime_get_sync(gpadc->dev); pm_runtime_get_sync(gpadc->dev);
...@@ -321,9 +339,29 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -321,9 +339,29 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out; goto out;
} }
/* Select the channel source and set average samples to 16 */ /* Select the channel source and set average samples */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, switch (avg_sample) {
AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16)); case SAMPLE_1:
val = channel | AVG_1;
break;
case SAMPLE_4:
val = channel | AVG_4;
break;
case SAMPLE_8:
val = channel | AVG_8;
break;
default:
val = channel | AVG_16;
break;
}
if (conv_type == ADC_HW)
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL3_REG, val);
else
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL2_REG, val);
if (ret < 0) { if (ret < 0) {
dev_err(gpadc->dev, dev_err(gpadc->dev,
"gpadc_conversion: set avg samples failed\n"); "gpadc_conversion: set avg samples failed\n");
...@@ -335,19 +373,40 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -335,19 +373,40 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
* charging current sense if it needed, ABB 3.0 needs some special * charging current sense if it needed, ABB 3.0 needs some special
* treatment too. * treatment too.
*/ */
if ((conv_type == ADC_HW) && (trig_edge)) {
ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_FALLING, EN_FALLING);
}
switch (channel) { switch (channel) {
case MAIN_CHARGER_C: case MAIN_CHARGER_C:
case USB_CHARGER_C: case USB_CHARGER_C:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev, if (conv_type == ADC_HW)
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | EN_ICHAR | EN_TRIG_EDGE,
EN_BUF | EN_ICHAR | EN_TRIG_EDGE);
else
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | EN_ICHAR, EN_BUF | EN_ICHAR,
EN_BUF | EN_ICHAR); EN_BUF | EN_ICHAR);
break; break;
case BTEMP_BALL: case BTEMP_BALL:
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) { if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
if (conv_type == ADC_HW)
/* Turn on btemp pull-up on ABB 3.0 */ /* Turn on btemp pull-up on ABB 3.0 */
ret = abx500_mask_and_set_register_interruptible( ret = abx500_mask_and_set_register_interruptible
gpadc->dev, (gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | BTEMP_PULL_UP | EN_TRIG_EDGE,
EN_BUF | BTEMP_PULL_UP | EN_TRIG_EDGE);
else
ret = abx500_mask_and_set_register_interruptible
(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | BTEMP_PULL_UP, EN_BUF | BTEMP_PULL_UP,
EN_BUF | BTEMP_PULL_UP); EN_BUF | BTEMP_PULL_UP);
...@@ -361,8 +420,17 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -361,8 +420,17 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
} }
/* Intentional fallthrough */ /* Intentional fallthrough */
default: default:
ret = abx500_mask_and_set_register_interruptible(gpadc->dev, if (conv_type == ADC_HW)
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
EN_BUF | EN_TRIG_EDGE,
EN_BUF | EN_TRIG_EDGE);
else
ret = abx500_mask_and_set_register_interruptible(
gpadc->dev,
AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
break; break;
} }
if (ret < 0) { if (ret < 0) {
...@@ -371,37 +439,84 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -371,37 +439,84 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out; goto out;
} }
/* Set trigger delay timer */
if (conv_type == ADC_HW) {
ret = abx500_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_AUTO_TIMER_REG, trig_timer);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: trig timer failed\n");
goto out;
}
}
/* Start SW conversion */
if (conv_type == ADC_SW) {
ret = abx500_mask_and_set_register_interruptible(gpadc->dev, ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); AB8500_GPADC, AB8500_GPADC_CTRL1_REG,
ADC_SW_CONV, ADC_SW_CONV);
if (ret < 0) { if (ret < 0) {
dev_err(gpadc->dev, dev_err(gpadc->dev,
"gpadc_conversion: start s/w conversion failed\n"); "gpadc_conversion: start s/w conv failed\n");
goto out; goto out;
} }
}
/* wait for completion of conversion */ /* wait for completion of conversion */
if (conv_type == ADC_HW) {
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
2*HZ)) {
dev_err(gpadc->dev,
"timeout didn't receive"
" hw GPADC conv interrupt\n");
ret = -EINVAL;
goto out;
}
} else {
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
msecs_to_jiffies(CONVERSION_TIME))) { msecs_to_jiffies(CONVERSION_TIME))) {
dev_err(gpadc->dev, dev_err(gpadc->dev,
"timeout: didn't receive GPADC conversion interrupt\n"); "timeout didn't receive"
" sw GPADC conv interrupt\n");
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
}
/* Read the converted RAW data */ /* Read the converted RAW data */
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, if (conv_type == ADC_HW) {
AB8500_GPADC_MANDATAL_REG, &low_data); ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_AUTODATAL_REG, &low_data);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read hw low data failed\n");
goto out;
}
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_AUTODATAH_REG, &high_data);
if (ret < 0) { if (ret < 0) {
dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); dev_err(gpadc->dev,
"gpadc_conversion: read hw high data failed\n");
goto out;
}
} else {
ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC, AB8500_GPADC_MANDATAL_REG, &low_data);
if (ret < 0) {
dev_err(gpadc->dev,
"gpadc_conversion: read sw low data failed\n");
goto out; goto out;
} }
ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, ret = abx500_get_register_interruptible(gpadc->dev,
AB8500_GPADC_MANDATAH_REG, &high_data); AB8500_GPADC, AB8500_GPADC_MANDATAH_REG, &high_data);
if (ret < 0) { if (ret < 0) {
dev_err(gpadc->dev, dev_err(gpadc->dev,
"gpadc_conversion: read high data failed\n"); "gpadc_conversion: read sw high data failed\n");
goto out; goto out;
} }
}
/* Disable GPADC */ /* Disable GPADC */
ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
...@@ -411,6 +526,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -411,6 +526,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
goto out; goto out;
} }
/* Disable VTVout LDO this is required for GPADC */
pm_runtime_mark_last_busy(gpadc->dev); pm_runtime_mark_last_busy(gpadc->dev);
pm_runtime_put_autosuspend(gpadc->dev); pm_runtime_put_autosuspend(gpadc->dev);
...@@ -427,9 +543,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -427,9 +543,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
*/ */
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
AB8500_GPADC_CTRL1_REG, DIS_GPADC); AB8500_GPADC_CTRL1_REG, DIS_GPADC);
pm_runtime_put(gpadc->dev); pm_runtime_put(gpadc->dev);
mutex_unlock(&gpadc->ab8500_gpadc_lock); mutex_unlock(&gpadc->ab8500_gpadc_lock);
dev_err(gpadc->dev, dev_err(gpadc->dev,
"gpadc_conversion: Failed to AD convert channel %d\n", channel); "gpadc_conversion: Failed to AD convert channel %d\n", channel);
...@@ -438,16 +552,16 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) ...@@ -438,16 +552,16 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
EXPORT_SYMBOL(ab8500_gpadc_read_raw); EXPORT_SYMBOL(ab8500_gpadc_read_raw);
/** /**
* ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion * ab8500_bm_gpadcconvend_handler() - isr for gpadc conversion completion
* @irq: irq number * @irq: irq number
* @data: pointer to the data passed during request irq * @data: pointer to the data passed during request irq
* *
* This is a interrupt service routine for s/w gpadc conversion completion. * This is a interrupt service routine for gpadc conversion completion.
* Notifies the gpadc completion is completed and the converted raw value * Notifies the gpadc completion is completed and the converted raw value
* can be read from the registers. * can be read from the registers.
* Returns IRQ status(IRQ_HANDLED) * Returns IRQ status(IRQ_HANDLED)
*/ */
static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc) static irqreturn_t ab8500_bm_gpadcconvend_handler(int irq, void *_gpadc)
{ {
struct ab8500_gpadc *gpadc = _gpadc; struct ab8500_gpadc *gpadc = _gpadc;
...@@ -646,11 +760,19 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) ...@@ -646,11 +760,19 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
if (gpadc->irq < 0) { if (gpadc->irq_sw < 0) {
dev_err(&pdev->dev, "failed to get platform irq-%d\n", dev_err(gpadc->dev, "failed to get platform irq-%d\n",
gpadc->irq); gpadc->irq_sw);
ret = gpadc->irq; ret = gpadc->irq_sw;
goto fail;
}
gpadc->irq_hw = platform_get_irq_byname(pdev, "HW_CONV_END");
if (gpadc->irq_hw < 0) {
dev_err(gpadc->dev, "failed to get platform irq-%d\n",
gpadc->irq_hw);
ret = gpadc->irq_hw;
goto fail; goto fail;
} }
...@@ -661,14 +783,21 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) ...@@ -661,14 +783,21 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
/* Initialize completion used to notify completion of conversion */ /* Initialize completion used to notify completion of conversion */
init_completion(&gpadc->ab8500_gpadc_complete); init_completion(&gpadc->ab8500_gpadc_complete);
/* Register interrupt - SwAdcComplete */ /* Register interrupts */
ret = request_threaded_irq(gpadc->irq, NULL, ret = request_threaded_irq(gpadc->irq_sw, NULL,
ab8500_bm_gpswadcconvend_handler, ab8500_bm_gpadcconvend_handler,
IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED, IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw", gpadc);
"ab8500-gpadc", gpadc); if (ret < 0) {
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
gpadc->irq_sw);
goto fail;
}
ret = request_threaded_irq(gpadc->irq_hw, NULL,
ab8500_bm_gpadcconvend_handler,
IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-hw", gpadc);
if (ret < 0) { if (ret < 0) {
dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
gpadc->irq); gpadc->irq_hw);
goto fail; goto fail;
} }
...@@ -694,7 +823,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev) ...@@ -694,7 +823,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
dev_dbg(gpadc->dev, "probe success\n"); dev_dbg(gpadc->dev, "probe success\n");
return 0; return 0;
fail_irq: fail_irq:
free_irq(gpadc->irq, gpadc); free_irq(gpadc->irq_sw, gpadc);
free_irq(gpadc->irq_hw, gpadc);
fail: fail:
kfree(gpadc); kfree(gpadc);
gpadc = NULL; gpadc = NULL;
...@@ -708,7 +838,8 @@ static int ab8500_gpadc_remove(struct platform_device *pdev) ...@@ -708,7 +838,8 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
/* remove this gpadc entry from the list */ /* remove this gpadc entry from the list */
list_del(&gpadc->node); list_del(&gpadc->node);
/* remove interrupt - completion of Sw ADC conversion */ /* remove interrupt - completion of Sw ADC conversion */
free_irq(gpadc->irq, gpadc); free_irq(gpadc->irq_sw, gpadc);
free_irq(gpadc->irq_hw, gpadc);
pm_runtime_get_sync(gpadc->dev); pm_runtime_get_sync(gpadc->dev);
pm_runtime_disable(gpadc->dev); pm_runtime_disable(gpadc->dev);
...@@ -757,6 +888,7 @@ subsys_initcall_sync(ab8500_gpadc_init); ...@@ -757,6 +888,7 @@ subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit); module_exit(ab8500_gpadc_exit);
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson"); MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson,"
"M'boumba Cedric Madianga");
MODULE_ALIAS("platform:ab8500_gpadc"); MODULE_ALIAS("platform:ab8500_gpadc");
MODULE_DESCRIPTION("AB8500 GPADC driver"); MODULE_DESCRIPTION("AB8500 GPADC driver");
...@@ -4,12 +4,14 @@ ...@@ -4,12 +4,14 @@
* *
* Author: Arun R Murthy <arun.murthy@stericsson.com> * Author: Arun R Murthy <arun.murthy@stericsson.com>
* Author: Daniel Willerud <daniel.willerud@stericsson.com> * Author: Daniel Willerud <daniel.willerud@stericsson.com>
* Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com>
*/ */
#ifndef _AB8500_GPADC_H #ifndef _AB8500_GPADC_H
#define _AB8500_GPADC_H #define _AB8500_GPADC_H
/* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2) */ /* GPADC source: From datasheet(ADCSwSel[4:0] in GPADCCtrl2
* and ADCHwSel[4:0] in GPADCCtrl3 ) */
#define BAT_CTRL 0x01 #define BAT_CTRL 0x01
#define BTEMP_BALL 0x02 #define BTEMP_BALL 0x02
#define MAIN_CHARGER_V 0x03 #define MAIN_CHARGER_V 0x03
...@@ -24,11 +26,31 @@ ...@@ -24,11 +26,31 @@
#define BK_BAT_V 0x0C #define BK_BAT_V 0x0C
#define DIE_TEMP 0x0D #define DIE_TEMP 0x0D
#define SAMPLE_1 1
#define SAMPLE_4 4
#define SAMPLE_8 8
#define SAMPLE_16 16
#define RISING_EDGE 0
#define FALLING_EDGE 1
/* Arbitrary ADC conversion type constants */
#define ADC_SW 0
#define ADC_HW 1
struct ab8500_gpadc; struct ab8500_gpadc;
struct ab8500_gpadc *ab8500_gpadc_get(char *name); struct ab8500_gpadc *ab8500_gpadc_get(char *name);
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel); int ab8500_gpadc_sw_hw_convert(struct ab8500_gpadc *gpadc, u8 channel,
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel); u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
static inline int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel)
{
return ab8500_gpadc_sw_hw_convert(gpadc, channel,
SAMPLE_16, 0, 0, ADC_SW);
}
int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel,
u8 avg_sample, u8 trig_edge, u8 trig_timer, u8 conv_type);
int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc,
u8 channel, int ad_value); u8 channel, int ad_value);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment