Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 73482346 authored by Lee Jones's avatar Lee Jones
Browse files

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
Loading
Loading
Loading
Loading
+266 −26
Original line number Diff line number Diff line
@@ -101,6 +101,11 @@ static int num_irqs;
static struct device_attribute **dev_attr;
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
 * @first: the first address of the range
@@ -808,7 +813,8 @@ static int ab8500_gpadc_bat_ctrl_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, bat_ctrl_raw);

@@ -836,7 +842,8 @@ static int ab8500_gpadc_btemp_ball_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -865,7 +872,8 @@ static int ab8500_gpadc_main_charger_v_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, main_charger_v_raw);

@@ -895,7 +903,8 @@ static int ab8500_gpadc_acc_detect1_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -925,7 +934,8 @@ static int ab8500_gpadc_acc_detect2_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, acc_detect2_raw);

@@ -955,7 +965,8 @@ static int ab8500_gpadc_aux1_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -983,7 +994,8 @@ static int ab8500_gpadc_aux2_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -1011,7 +1023,8 @@ static int ab8500_gpadc_main_bat_v_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -1040,7 +1053,8 @@ static int ab8500_gpadc_vbus_v_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -1068,7 +1082,8 @@ static int ab8500_gpadc_main_charger_c_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, main_charger_c_raw);

@@ -1098,7 +1113,8 @@ static int ab8500_gpadc_usb_charger_c_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, usb_charger_c_raw);

@@ -1128,7 +1144,8 @@ static int ab8500_gpadc_bk_bat_v_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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, bk_bat_v_raw);

@@ -1156,7 +1173,8 @@ static int ab8500_gpadc_die_temp_print(struct seq_file *s, void *p)
	struct ab8500_gpadc *gpadc;

	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_raw);

@@ -1177,6 +1195,208 @@ static const struct file_operations ab8500_gpadc_die_temp_fops = {
	.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
 * numerical value.
@@ -1722,6 +1942,26 @@ static int ab8500_debug_probe(struct platform_device *plf)
	if (!file)
		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;

err:
+207 −75
Original line number Diff line number Diff line
@@ -55,13 +55,18 @@
#define EN_VTVOUT			0x02
#define EN_GPADC			0x01
#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 EN_ICHAR			0x80
#define BTEMP_PULL_UP			0x08
#define EN_BUF				0x40
#define DIS_ZERO			0x00
#define GPADC_BUSY			0x01
#define EN_FALLING			0x10
#define EN_TRIG_EDGE			0x02

/* GPADC constants from AB8500 spec, UM0836 */
#define ADC_RESOLUTION			1024
@@ -116,7 +121,10 @@ struct adc_cal_data {
 *				the completion of gpadc conversion
 * @ab8500_gpadc_lock:		structure of type mutex
 * @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
 */
struct ab8500_gpadc {
@@ -126,7 +134,8 @@ struct ab8500_gpadc {
	struct completion ab8500_gpadc_complete;
	struct mutex ab8500_gpadc_lock;
	struct regulator *regu;
	int irq;
	int irq_sw;
	int irq_hw;
	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,
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
 * @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
 * 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 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 */
	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) {
		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;
	}

	voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value);

	if (voltage < 0)
		dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:"
			" %d AD: 0x%x\n", channel, ad_value);
@@ -279,11 +293,16 @@ EXPORT_SYMBOL(ab8500_gpadc_convert);
/**
 * ab8500_gpadc_read_raw() - gpadc 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
 * to be converted by calling ab8500_gpadc_ad_to_voltage()
 * This function obtains the raw ADC value for an hardware conversion,
 * 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 looplimit = 0;
@@ -293,7 +312,6 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
		return -ENODEV;

	mutex_lock(&gpadc->ab8500_gpadc_lock);

	/* Enable VTVout LDO this is required for GPADC */
	pm_runtime_get_sync(gpadc->dev);

@@ -321,9 +339,29 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
		goto out;
	}

	/* Select the channel source and set average samples to 16 */
	ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
		AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16));
	/* Select the channel source and set average samples */
	switch (avg_sample) {
	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) {
		dev_err(gpadc->dev,
			"gpadc_conversion: set avg samples failed\n");
@@ -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
	 * 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) {
	case MAIN_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,
				EN_BUF | EN_ICHAR,
				EN_BUF | EN_ICHAR);
		break;
	case BTEMP_BALL:
		if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
			if (conv_type == ADC_HW)
				/* Turn on btemp pull-up on ABB 3.0 */
			ret = abx500_mask_and_set_register_interruptible(
				gpadc->dev,
				ret = abx500_mask_and_set_register_interruptible
					(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,
					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)
		}
		/* Intentional fallthrough */
	default:
		ret = abx500_mask_and_set_register_interruptible(gpadc->dev,
			AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF);
		if (conv_type == ADC_HW)
			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;
	}
	if (ret < 0) {
@@ -371,37 +439,84 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
		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,
		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) {
			dev_err(gpadc->dev,
			"gpadc_conversion: start s/w conversion failed\n");
				"gpadc_conversion: start s/w conv failed\n");
			goto out;
		}
	}

	/* 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,
			msecs_to_jiffies(CONVERSION_TIME))) {
				dev_err(gpadc->dev,
			"timeout: didn't receive GPADC conversion interrupt\n");
					"timeout didn't receive"
					" sw GPADC conv interrupt\n");
				ret = -EINVAL;
				goto out;
		}
	}

	/* Read the converted RAW data */
	ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
		AB8500_GPADC_MANDATAL_REG, &low_data);
	if (conv_type == ADC_HW) {
		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) {
		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;
		}

	ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC,
		AB8500_GPADC_MANDATAH_REG, &high_data);
		ret = abx500_get_register_interruptible(gpadc->dev,
			AB8500_GPADC, AB8500_GPADC_MANDATAH_REG, &high_data);
		if (ret < 0) {
			dev_err(gpadc->dev,
			"gpadc_conversion: read high data failed\n");
				"gpadc_conversion: read sw high data failed\n");
			goto out;
		}
	}

	/* Disable 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)
		goto out;
	}

	/* Disable VTVout LDO this is required for GPADC */
	pm_runtime_mark_last_busy(gpadc->dev);
	pm_runtime_put_autosuspend(gpadc->dev);

@@ -427,9 +543,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
	 */
	(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
		AB8500_GPADC_CTRL1_REG, DIS_GPADC);

	pm_runtime_put(gpadc->dev);

	mutex_unlock(&gpadc->ab8500_gpadc_lock);
	dev_err(gpadc->dev,
		"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)
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
 * @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
 * can be read from the registers.
 * 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;

@@ -646,11 +760,19 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
		return -ENOMEM;
	}

	gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END");
	if (gpadc->irq < 0) {
		dev_err(&pdev->dev, "failed to get platform irq-%d\n",
			gpadc->irq);
		ret = gpadc->irq;
	gpadc->irq_sw = platform_get_irq_byname(pdev, "SW_CONV_END");
	if (gpadc->irq_sw < 0) {
		dev_err(gpadc->dev, "failed to get platform irq-%d\n",
			gpadc->irq_sw);
		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;
	}

@@ -661,14 +783,21 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
	/* Initialize completion used to notify completion of conversion */
	init_completion(&gpadc->ab8500_gpadc_complete);

	/* Register interrupt  - SwAdcComplete */
	ret = request_threaded_irq(gpadc->irq, NULL,
		ab8500_bm_gpswadcconvend_handler,
		IRQF_ONESHOT | IRQF_NO_SUSPEND | IRQF_SHARED,
				"ab8500-gpadc", gpadc);
	/* Register interrupts */
	ret = request_threaded_irq(gpadc->irq_sw, NULL,
		ab8500_bm_gpadcconvend_handler,
		IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc-sw", 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) {
		dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n",
			gpadc->irq);
			gpadc->irq_hw);
		goto fail;
	}

@@ -694,7 +823,8 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
	dev_dbg(gpadc->dev, "probe success\n");
	return 0;
fail_irq:
	free_irq(gpadc->irq, gpadc);
	free_irq(gpadc->irq_sw, gpadc);
	free_irq(gpadc->irq_hw, gpadc);
fail:
	kfree(gpadc);
	gpadc = NULL;
@@ -708,7 +838,8 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
	/* remove this gpadc entry from the list */
	list_del(&gpadc->node);
	/* 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_disable(gpadc->dev);
@@ -757,6 +888,7 @@ subsys_initcall_sync(ab8500_gpadc_init);
module_exit(ab8500_gpadc_exit);

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_DESCRIPTION("AB8500 GPADC driver");
+26 −4
Original line number Diff line number Diff line
@@ -4,12 +4,14 @@
 *
 * Author: Arun R Murthy <arun.murthy@stericsson.com>
 * Author: Daniel Willerud <daniel.willerud@stericsson.com>
 * Author: M'boumba Cedric Madianga <cedric.madianga@stericsson.com>
 */

#ifndef	_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 BTEMP_BALL	0x02
#define MAIN_CHARGER_V	0x03
@@ -24,11 +26,31 @@
#define BK_BAT_V	0x0C
#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 *ab8500_gpadc_get(char *name);
int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel);
int ab8500_gpadc_read_raw(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);
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,
		u8 channel, int ad_value);