summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJinyoung Park <jinyoungp@nvidia.com>2013-12-05 02:56:07 +0900
committerHarry Hong <hhong@nvidia.com>2014-02-04 20:49:41 -0800
commit18b2fd8d3be8f40be3e50a3587395a0673c22121 (patch)
tree68299da37156a1334ffe7393c042f24d45ab60e1 /drivers
parentba5b84c8f24f6175c88cec19f9cf3de848ceaf6d (diff)
iio: staging: adc: palmas: Robustify in GPADC lock issue
Robustify in GPADC lock issue. - Check GPADC status to unlock GPADC, if GPADC is in the lock status. - The palmas_update_bits() updates a register value only if cached value and new value are different. So use palmas_write() instead palmas_update_bits() in palmas_disable_auto_conversion() to update AUTO_CTRL register always. - Remove ADC S/W conversion disable. It is unnecessary and can be cause of GPADC lock issue. - Clear AUTO_SELECT register after disable auto conversion. - Add shutdown callback to disable auto conversion before shutdown. - Print INT3_LINE_STATE register in palmas_gpadc_irq_auto. Bug 1398960 Bug 1415280 Change-Id: Ia63e56d6119680fb6c761647718e7467c28bab6d Signed-off-by: Jinyoung Park <jinyoungp@nvidia.com> Reviewed-on: http://git-master/r/360670 Reviewed-by: Harry Hong <hhong@nvidia.com> Tested-by: Harry Hong <hhong@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/staging/iio/adc/palmas_gpadc.c135
1 files changed, 102 insertions, 33 deletions
diff --git a/drivers/staging/iio/adc/palmas_gpadc.c b/drivers/staging/iio/adc/palmas_gpadc.c
index fb4f59236957..50880fbcfd55 100644
--- a/drivers/staging/iio/adc/palmas_gpadc.c
+++ b/drivers/staging/iio/adc/palmas_gpadc.c
@@ -109,22 +109,22 @@ struct palmas_gpadc {
* mode feature.
* Details:
* When the AUTO mode is the only conversion mode enabled, if the AUTO
- * mode feature is disabled with bit GPADC_AUTO_CTRL. AUTO_CONV1_EN = 0
- * or bit GPADC_AUTO_CTRL. AUTO_CONV0_EN = 0 during a conversion, the
+ * mode feature is disabled with bit GPADC_AUTO_CTRL.AUTO_CONV1_EN = 0
+ * or bit GPADC_AUTO_CTRL.AUTO_CONV0_EN = 0 during a conversion, the
* conversion mechanism can be seen as locked meaning that all following
- * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE
- * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock
+ * conversion will give 0 as a result. Bit GPADC_STATUS.GPADC_AVAILABLE
+ * will stay at 0 meaning that GPADC is busy. An RT conversion can unlock
* the GPADC.
*
* Workaround(s):
* To avoid the lock mechanism, the workaround to follow before any stop
* conversion request is:
- * Force the GPADC state machine to be ON by using the GPADC_CTRL1.
- * GPADC_FORCE bit = 1
+ * Force the GPADC state machine to be ON by using the
+ * GPADC_CTRL1.GPADC_FORCE bit = 1
* Shutdown the GPADC AUTO conversion using
* GPADC_AUTO_CTRL.SHUTDOWN_CONV[01] = 0.
* After 100us, force the GPADC state machine to be OFF by using the
- * GPADC_CTRL1. GPADC_FORCE bit = 0
+ * GPADC_CTRL1.GPADC_FORCE bit = 0
*/
static int palmas_disable_auto_conversion(struct palmas_gpadc *adc)
{
@@ -139,13 +139,10 @@ static int palmas_disable_auto_conversion(struct palmas_gpadc *adc)
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,
- 0);
+ ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_AUTO_CTRL, 0);
if (ret < 0) {
- dev_err(adc->dev, "AUTO_CTRL update failed: %d\n", ret);
+ dev_err(adc->dev, "AUTO_CTRL write failed: %d\n", ret);
return ret;
}
@@ -172,9 +169,20 @@ static irqreturn_t palmas_gpadc_irq(int irq, void *data)
static irqreturn_t palmas_gpadc_irq_auto(int irq, void *data)
{
struct palmas_gpadc *adc = data;
+ unsigned int val = 0;
+ int ret;
+
+ ret = palmas_read(adc->palmas, PALMAS_INTERRUPT_BASE,
+ PALMAS_INT3_LINE_STATE, &val);
+ if (ret < 0)
+ dev_err(adc->dev, "%s: Failed to read INT3_LINE_STATE, %d\n",
+ __func__, ret);
+
+ if (val & PALMAS_INT3_LINE_STATE_GPADC_AUTO_0)
+ dev_info(adc->dev, "Auto0 threshold interrupt occurred\n");
+ if (val & PALMAS_INT3_LINE_STATE_GPADC_AUTO_1)
+ dev_info(adc->dev, "Auto1 threshold interrupt occurred\n");
- dev_info(adc->dev, "Threshold interrupt %d occurs\n", irq);
- palmas_disable_auto_conversion(adc);
return IRQ_HANDLED;
}
@@ -301,6 +309,12 @@ static int palmas_gpadc_auto_conv_reset(struct palmas_gpadc *adc)
if (!adc->auto_conv0_enable && !adc->auto_conv1_enable)
return 0;
+ ret = palmas_disable_auto_conversion(adc);
+ if (ret < 0) {
+ dev_err(adc->dev, "Disable auto conversion failed: %d\n", ret);
+ return ret;
+ }
+
ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
PALMAS_GPADC_AUTO_SELECT, 0);
if (ret < 0) {
@@ -308,10 +322,58 @@ static int palmas_gpadc_auto_conv_reset(struct palmas_gpadc *adc)
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_check_status(struct palmas_gpadc *adc)
+{
+ int retry_cnt = 3;
+ int check_cnt = 3;
+ int loop_cnt = 3;
+ unsigned int val = 0;
+ int ret;
+
+retry:
+ do {
+ ret = palmas_read(adc->palmas, PALMAS_GPADC_BASE,
+ PALMAS_GPADC_STATUS, &val);
+ if (ret < 0) {
+ dev_err(adc->dev, "%s: Failed to read STATUS, %d\n",
+ __func__, ret);
+ return ret;
+ } else if (val & PALMAS_GPADC_STATUS_GPADC_AVAILABLE) {
+ if (--check_cnt == 0)
+ break;
+ } else {
+ dev_warn(adc->dev, "%s: GPADC is busy, STATUS 0x%02x\n",
+ __func__, val);
+ }
+ udelay(100);
+ } while (loop_cnt-- > 0);
+
+ if (check_cnt == 0) {
+ if (retry_cnt < 3)
+ dev_warn(adc->dev, "%s: GPADC is unlocked.\n",
+ __func__);
+ return 0;
+ }
+
+ dev_warn(adc->dev, "%s: GPADC is locked.\n", __func__);
+ dev_warn(adc->dev, "%s: Perform RT conversion to unlock GPADC.\n",
+ __func__);
+ palmas_disable_auto_conversion(adc);
+ palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_RT_SELECT,
+ PALMAS_GPADC_RT_SELECT_RT_CONV_EN);
+ palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_RT_CTRL,
+ PALMAS_GPADC_RT_CTRL_START_POLARITY);
+ udelay(100);
+ palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_RT_CTRL, 0);
+ palmas_write(adc->palmas, PALMAS_GPADC_BASE, PALMAS_GPADC_RT_SELECT, 0);
+ if (retry_cnt-- > 0) {
+ goto retry;
+ } else {
+ dev_err(adc->dev, "%s: Failed to unlock GPADC.\n", __func__);
+ return -EDEADLK;
}
return 0;
@@ -413,20 +475,6 @@ static int palmas_gpadc_enable(struct palmas_gpadc *adc, int adc_chan,
return ret;
}
} else {
- ret = palmas_write(adc->palmas, PALMAS_GPADC_BASE,
- PALMAS_GPADC_SW_SELECT, 0);
- if (ret < 0) {
- dev_err(adc->dev, "SW_SELECT write failed: %d\n", ret);
- return ret;
- }
-
- /* 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);
- }
-
mask = val = 0;
mask |= PALMAS_GPADC_CTRL1_GPADC_FORCE;
@@ -499,6 +547,12 @@ static int palmas_gpadc_start_convertion(struct palmas_gpadc *adc, int adc_chan)
}
ret = (val & 0xFFF);
+ if (ret == 0) {
+ ret = palmas_gpadc_check_status(adc);
+ if (ret == 0)
+ ret = -EAGAIN;
+ }
+
return ret;
}
@@ -932,6 +986,11 @@ static int __devinit palmas_gpadc_probe(struct platform_device *pdev)
if (adc->auto_conv0_enable || adc->auto_conv1_enable)
device_wakeup_enable(&pdev->dev);
+ ret = palmas_gpadc_check_status(adc);
+ if (ret < 0)
+ goto out_irq_auto1_free;
+
+ palmas_gpadc_auto_conv_reset(adc);
ret = palmas_gpadc_auto_conv_configure(adc);
if (ret < 0) {
dev_err(adc->dev, "auto_conv_configure() failed: %d\n", ret);
@@ -976,6 +1035,15 @@ static int __devexit palmas_gpadc_remove(struct platform_device *pdev)
return 0;
}
+static void palmas_gpadc_shutdown(struct platform_device *pdev)
+{
+ struct iio_dev *iodev = dev_get_drvdata(&pdev->dev);
+ struct palmas_gpadc *adc = iio_priv(iodev);
+
+ if (adc->auto_conv0_enable || adc->auto_conv1_enable)
+ palmas_gpadc_auto_conv_reset(adc);
+}
+
#ifdef CONFIG_PM_SLEEP
static int palmas_gpadc_suspend(struct device *dev)
{
@@ -1022,6 +1090,7 @@ static const struct dev_pm_ops palmas_pm_ops = {
static struct platform_driver palmas_gpadc_driver = {
.probe = palmas_gpadc_probe,
.remove = __devexit_p(palmas_gpadc_remove),
+ .shutdown = palmas_gpadc_shutdown,
.driver = {
.name = MOD_NAME,
.owner = THIS_MODULE,