summaryrefslogtreecommitdiff
path: root/drivers/staging/iio
diff options
context:
space:
mode:
authorJinyoung Park <jinyoungp@nvidia.com>2013-11-29 16:13:15 +0900
committerLaxman Dewangan <ldewangan@nvidia.com>2014-03-04 09:56:03 -0800
commit463f9b17388e5e7bdcc3f90df25777f09b197a55 (patch)
treea4c931a9bc6ec72e35678665b2833f5bc9e0fc0c /drivers/staging/iio
parente2c64bcbe0d59e24a812d9dcce99ea9fffc26520 (diff)
iio: staging: adc: palmas: Support for auto conversion shutdown
Support for auto conversion shutdown when auto conversion result is crossed threshold. Corrected auto conversion period calculation. Restore CH3 current source if CH3 is dual current mode. Added debugfs for auto conversion. Bug 1398960 Bug 1415280 Change-Id: I148c51ab70dcca756f74a5cb8ab10112de3a9f03 Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com> (cherry picked from commit 067cbc4f23849b3e5c5108e1d8636d008ad88498) Reviewed-on: http://git-master/r/357299 Reviewed-by: Anshul Jain (SW) <anshulj@nvidia.com> Tested-by: Anshul Jain (SW) <anshulj@nvidia.com> GVS: Gerrit_Virtual_Submit Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Diffstat (limited to 'drivers/staging/iio')
-rw-r--r--drivers/staging/iio/adc/palmas_gpadc.c466
1 files changed, 329 insertions, 137 deletions
diff --git a/drivers/staging/iio/adc/palmas_gpadc.c b/drivers/staging/iio/adc/palmas_gpadc.c
index 2381deed68fa..26a94122c505 100644
--- a/drivers/staging/iio/adc/palmas_gpadc.c
+++ b/drivers/staging/iio/adc/palmas_gpadc.c
@@ -25,11 +25,13 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/pm.h>
#include <linux/mfd/palmas.h>
#include <linux/completion.h>
+#include <linux/debugfs.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/iio/iio.h>
@@ -99,6 +101,8 @@ struct palmas_gpadc {
bool auto_conv0_enable;
bool auto_conv1_enable;
int auto_conversion_period;
+
+ struct dentry *dentry;
};
/*
@@ -176,6 +180,145 @@ static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data)
return IRQ_HANDLED;
}
+static int palmas_gpadc_auto_conv_configure(struct palmas_gpadc *adc)
+{
+ int adc_period, conv;
+ int i;
+ int ch0 = 0, ch1 = 0;
+ int thres;
+ int ret;
+
+ if (!adc->auto_conv0_enable && !adc->auto_conv1_enable)
+ return 0;
+
+ adc_period = adc->auto_conversion_period;
+ for (i = 0; i < 16; ++i) {
+ if (((1000 * (1 << i))/32) > adc_period)
+ break;
+ }
+ if (i > 0)
+ i--;
+ adc_period = i;
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
+ adc_period);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+ return ret;
+ }
+
+ conv = 0;
+ if (adc->auto_conv0_enable) {
+ int is_high;
+
+ ch0 = adc->auto_conv0_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
+ conv |= (adc->auto_conv0_data.adc_shutdown ?
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0 : 0);
+ if (adc->auto_conv0_data.adc_high_threshold > 0) {
+ thres = adc->auto_conv0_data.adc_high_threshold;
+ is_high = 0;
+ } else {
+ thres = adc->auto_conv0_data.adc_low_threshold;
+ is_high = BIT(7);
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV0_MSB,
+ ((thres >> 8) & 0xF) | is_high);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV0_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ if (adc->auto_conv1_enable) {
+ int is_high;
+
+ ch1 = adc->auto_conv1_data.adc_channel_number;
+ conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
+ conv |= (adc->auto_conv1_data.adc_shutdown ?
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 : 0);
+ if (adc->auto_conv1_data.adc_high_threshold > 0) {
+ thres = adc->auto_conv1_data.adc_high_threshold;
+ is_high = 0;
+ } else {
+ thres = adc->auto_conv1_data.adc_low_threshold;
+ is_high = BIT(7);
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_LSB write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_THRES_CONV1_MSB,
+ ((thres >> 8) & 0xF) | is_high);
+ if (ret < 0) {
+ dev_err(adc->dev,
+ "THRES_CONV1_MSB write failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL,
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV1 |
+ PALMAS_GPADC_AUTO_CTRL_SHUTDOWN_CONV0 |
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN |
+ PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int palmas_gpadc_auto_conv_reset(struct palmas_gpadc *adc)
+{
+ int ret;
+
+ if (!adc->auto_conv0_enable && !adc->auto_conv1_enable)
+ return 0;
+
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_SELECT, 0);
+ if (ret < 0) {
+ dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = palmas_disable_auto_conversion(adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
static int palmas_gpadc_start_mask_interrupt(struct palmas_gpadc *adc, int mask)
{
int ret;
@@ -243,9 +386,18 @@ static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan,
if (ret < 0)
dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret);
+ mask = val = 0;
+ mask |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
+
+ /* Restore CH3 current source if CH3 is dual current mode. */
+ if ((adc_chan == PALMAS_ADC_CH_IN3) && adc->ch3_dual_current) {
+ mask |= PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_MASK;
+ val |= (adc->ch3_current
+ << PALMAS_GPADC_CTRL1_CURRENT_SRC_CH3_SHIFT);
+ }
+
ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_CTRL1,
- PALMAS_GPADC_CTRL1_GPADC_FORCE, 0);
+ PALMAS_GPADC_CTRL1, mask, val);
if (ret < 0) {
dev_err(adc->dev, "CTRL1 update failed: %d\n", ret);
return ret;
@@ -457,6 +609,171 @@ out:
return ret;
}
+#ifdef CONFIG_DEBUG_FS
+static int auto_conv_period_get(void *data, u64 *val)
+{
+ struct palmas_gpadc *adc = (struct palmas_gpadc *)data;
+
+ *val = adc->auto_conversion_period;
+ return 0;
+}
+
+static int auto_conv_period_set(void *data, u64 val)
+{
+ struct palmas_gpadc *adc = (struct palmas_gpadc *)data;
+ struct iio_dev *iodev = dev_get_drvdata(adc->dev);
+
+ adc->auto_conversion_period = val;
+
+ mutex_lock(&iodev->mlock);
+ palmas_gpadc_auto_conv_reset(adc);
+ palmas_gpadc_auto_conv_configure(adc);
+ mutex_unlock(&iodev->mlock);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_conv_period_fops, auto_conv_period_get,
+ auto_conv_period_set, "%llu\n");
+
+static ssize_t auto_conv_data_read(struct file *file,
+ char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct palmas_gpadc *adc = file->private_data;
+ unsigned char *d_iname;
+ char buf[64] = { 0, };
+ ssize_t ret = 0;
+
+ d_iname = file->f_path.dentry->d_iname;
+
+ if (!strcmp("auto_conv0_channel", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv0_data.adc_channel_number);
+ } else if (!strcmp("auto_conv1_channel", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv1_data.adc_channel_number);
+ } else if (!strcmp("auto_conv0_high_threshold", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv0_data.adc_high_threshold);
+ } else if (!strcmp("auto_conv1_high_threshold", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv1_data.adc_high_threshold);
+ } else if (!strcmp("auto_conv0_low_threshold", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv0_data.adc_low_threshold);
+ } else if (!strcmp("auto_conv1_low_threshold", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv1_data.adc_low_threshold);
+ } else if (!strcmp("auto_conv0_shutdown", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv0_data.adc_shutdown);
+ } else if (!strcmp("auto_conv1_shutdown", d_iname)) {
+ ret = snprintf(buf, sizeof(buf), "%d\n",
+ adc->auto_conv1_data.adc_shutdown);
+ }
+
+ return simple_read_from_buffer(user_buf, count, ppos, buf, ret);
+}
+
+static ssize_t auto_conv_data_write(struct file *file,
+ const char __user *user_buf, size_t count, loff_t *ppos)
+{
+ struct palmas_gpadc *adc = file->private_data;
+ struct iio_dev *iodev = dev_get_drvdata(adc->dev);
+ unsigned char *d_iname;
+ char buf[64] = { 0, };
+ int val;
+ ssize_t buf_size;
+
+ buf_size = min(count, (sizeof(buf)-1));
+ if (copy_from_user(buf, user_buf, buf_size))
+ return -EFAULT;
+
+ if (!sscanf(buf, "%d\n", &val))
+ return -EINVAL;
+
+ d_iname = file->f_path.dentry->d_iname;
+
+ if (!strcmp("auto_conv0_channel", d_iname)) {
+ adc->auto_conv0_data.adc_channel_number = val;
+ } else if (!strcmp("auto_conv1_channel", d_iname)) {
+ adc->auto_conv1_data.adc_channel_number = val;
+ } else if (!strcmp("auto_conv0_high_threshold", d_iname)) {
+ adc->auto_conv0_data.adc_high_threshold = val;
+ if (val > 0)
+ adc->auto_conv0_data.adc_low_threshold = 0;
+ } else if (!strcmp("auto_conv1_high_threshold", d_iname)) {
+ adc->auto_conv1_data.adc_high_threshold = val;
+ if (val > 0)
+ adc->auto_conv1_data.adc_low_threshold = 0;
+ } else if (!strcmp("auto_conv0_low_threshold", d_iname)) {
+ adc->auto_conv0_data.adc_low_threshold = val;
+ if (val > 0)
+ adc->auto_conv0_data.adc_high_threshold = 0;
+ } else if (!strcmp("auto_conv1_low_threshold", d_iname)) {
+ adc->auto_conv1_data.adc_low_threshold = val;
+ if (val > 0)
+ adc->auto_conv1_data.adc_high_threshold = 0;
+ } else if (!strcmp("auto_conv0_shutdown", d_iname)) {
+ adc->auto_conv0_data.adc_shutdown = val;
+ } else if (!strcmp("auto_conv1_shutdown", d_iname)) {
+ adc->auto_conv1_data.adc_shutdown = val;
+ }
+
+ mutex_lock(&iodev->mlock);
+ palmas_gpadc_auto_conv_reset(adc);
+ palmas_gpadc_auto_conv_configure(adc);
+ mutex_unlock(&iodev->mlock);
+ return buf_size;
+}
+
+static const struct file_operations auto_conv_data_fops = {
+ .open = simple_open,
+ .write = auto_conv_data_write,
+ .read = auto_conv_data_read,
+};
+
+static void palmas_gpadc_debugfs_init(struct palmas_gpadc *adc)
+{
+ adc->dentry = debugfs_create_dir(dev_name(adc->dev), NULL);
+ if (!adc->dentry) {
+ dev_err(adc->dev, "%s: failed to create debugfs dir\n",
+ __func__);
+ return;
+ }
+
+ if (adc->auto_conv0_enable || adc->auto_conv1_enable)
+ debugfs_create_file("auto_conv_period", 0644,
+ adc->dentry, adc,
+ &auto_conv_period_fops);
+
+ if (adc->auto_conv0_enable) {
+ debugfs_create_file("auto_conv0_channel", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv0_high_threshold", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv0_low_threshold", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv0_shutdown", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ }
+
+ if (adc->auto_conv1_enable) {
+ debugfs_create_file("auto_conv1_channel", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv1_high_threshold", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv1_low_threshold", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ debugfs_create_file("auto_conv1_shutdown", 0644,
+ adc->dentry, adc, &auto_conv_data_fops);
+ }
+}
+#else
+static void palmas_gpadc_debugfs_init(struct palmas_gpadc *adc)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
static const struct iio_info palmas_gpadc_iio_info = {
.read_raw = palmas_gpadc_read_raw,
.driver_module = THIS_MODULE,
@@ -719,6 +1036,13 @@ static int palmas_gpadc_probe(struct platform_device *pdev)
if (adc->auto_conv0_enable || adc->auto_conv1_enable)
device_wakeup_enable(&pdev->dev);
+ ret = palmas_gpadc_auto_conv_configure(adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "auto_conv_configure() failed: %d\n", ret);
+ goto out_irq_auto1_free;
+ }
+
+ palmas_gpadc_debugfs_init(adc);
return 0;
out_irq_auto1_free:
@@ -742,6 +1066,8 @@ static int palmas_gpadc_remove(struct platform_device *pdev)
struct iio_dev *iodev = dev_to_iio_dev(&pdev->dev);
struct palmas_gpadc *adc = iio_priv(iodev);
struct palmas_platform_data *pdata = dev_get_platdata(pdev->dev.parent);
+
+ debugfs_remove_recursive(adc->dentry);
if (pdata->gpadc_pdata->iio_maps)
iio_map_array_unregister(iodev);
iio_device_unregister(iodev);
@@ -755,150 +1081,21 @@ static int palmas_gpadc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
-static int palmas_adc_auto_conv_configure(struct palmas_gpadc *adc)
-{
- int adc_period, conv;
- int i;
- int ch0 = 0, ch1 = 0;
- int thres;
- int ret;
-
- adc_period = adc->auto_conversion_period;
- for (i = 0; i < 16; ++i) {
- if (((1000 * (1 << i))/32) < adc_period)
- continue;
- }
- if (i > 0)
- i--;
- adc_period = i;
- ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_AUTO_CTRL,
- PALMAS_GPADC_AUTO_CTRL_COUNTER_CONV_MASK,
- adc_period);
- if (ret < 0) {
- dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
- return ret;
- }
-
- conv = 0;
- if (adc->auto_conv0_enable) {
- int is_high;
-
- ch0 = adc->auto_conv0_data.adc_channel_number;
- conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN;
- if (adc->auto_conv0_data.adc_high_threshold > 0) {
- thres = adc->auto_conv0_data.adc_high_threshold;
- is_high = 0;
- } else {
- thres = adc->auto_conv0_data.adc_low_threshold;
- is_high = BIT(7);
- }
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_THRES_CONV0_LSB, thres & 0xFF);
- if (ret < 0) {
- dev_err(adc->dev,
- "THRES_CONV0_LSB write failed: %d\n", ret);
- return ret;
- }
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_THRES_CONV0_MSB,
- ((thres >> 8) & 0xF) | is_high);
- if (ret < 0) {
- dev_err(adc->dev,
- "THRES_CONV0_MSB write failed: %d\n", ret);
- return ret;
- }
- }
-
- if (adc->auto_conv1_enable) {
- int is_high;
-
- ch1 = adc->auto_conv1_data.adc_channel_number;
- conv |= PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN;
- if (adc->auto_conv1_data.adc_high_threshold > 0) {
- thres = adc->auto_conv1_data.adc_high_threshold;
- is_high = 0;
- } else {
- thres = adc->auto_conv1_data.adc_low_threshold;
- is_high = BIT(7);
- }
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_THRES_CONV1_LSB, thres & 0xFF);
- if (ret < 0) {
- dev_err(adc->dev,
- "THRES_CONV1_LSB write failed: %d\n", ret);
- return ret;
- }
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_THRES_CONV1_MSB,
- ((thres >> 8) & 0xF) | is_high);
- if (ret < 0) {
- dev_err(adc->dev,
- "THRES_CONV1_MSB write failed: %d\n", ret);
- return ret;
- }
- }
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_AUTO_SELECT, (ch1 << 4) | ch0);
- if (ret < 0) {
- dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
- return ret;
- }
-
- ret = palmas_update_bits(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_AUTO_CTRL,
- PALMAS_GPADC_AUTO_CTRL_AUTO_CONV1_EN |
- PALMAS_GPADC_AUTO_CTRL_AUTO_CONV0_EN, conv);
- if (ret < 0) {
- dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
-static int palmas_adc_auto_conv_reset(struct palmas_gpadc *adc)
-{
- int ret;
-
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_AUTO_SELECT, 0);
- if (ret < 0) {
- dev_err(adc->dev, "AUTO_SELECT write failed: %d\n", ret);
- return ret;
- }
-
- ret = palmas_disable_auto_conversion(adc);
- if (ret < 0) {
- dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
- return ret;
- }
- return 0;
-}
-
static int palmas_gpadc_suspend(struct device *dev)
{
struct iio_dev *iodev = dev_to_iio_dev(dev);
struct palmas_gpadc *adc = iio_priv(iodev);
int wakeup = adc->auto_conv0_enable || adc->auto_conv1_enable;
- int ret;
if (!device_may_wakeup(dev) || !wakeup)
return 0;
- ret = palmas_adc_auto_conv_configure(adc);
- if (ret < 0)
- return ret;
-
if (adc->auto_conv0_enable)
enable_irq_wake(adc->irq_auto_0);
if (adc->auto_conv1_enable)
enable_irq_wake(adc->irq_auto_1);
+
return 0;
}
@@ -907,15 +1104,10 @@ static int palmas_gpadc_resume(struct device *dev)
struct iio_dev *iodev = dev_to_iio_dev(dev);
struct palmas_gpadc *adc = iio_priv(iodev);
int wakeup = adc->auto_conv0_enable || adc->auto_conv1_enable;
- int ret;
if (!device_may_wakeup(dev) || !wakeup)
return 0;
- ret = palmas_adc_auto_conv_reset(adc);
- if (ret < 0)
- return ret;
-
if (adc->auto_conv0_enable)
disable_irq_wake(adc->irq_auto_0);