|
@@ -12,6 +12,7 @@
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/spinlock.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/pm_runtime.h>
|
|
|
#include <linux/platform_device.h>
|
|
|
#include <linux/completion.h>
|
|
|
#include <linux/regulator/consumer.h>
|
|
@@ -82,6 +83,11 @@
|
|
|
/* This is used to not lose precision when dividing to get gain and offset */
|
|
|
#define CALIB_SCALE 1000
|
|
|
|
|
|
+/* Time in ms before disabling regulator */
|
|
|
+#define GPADC_AUDOSUSPEND_DELAY 1
|
|
|
+
|
|
|
+#define CONVERSION_TIME 500 /* ms */
|
|
|
+
|
|
|
enum cal_channels {
|
|
|
ADC_INPUT_VMAIN = 0,
|
|
|
ADC_INPUT_BTEMP,
|
|
@@ -102,10 +108,10 @@ struct adc_cal_data {
|
|
|
|
|
|
/**
|
|
|
* struct ab8500_gpadc - AB8500 GPADC device information
|
|
|
- * @chip_id ABB chip id
|
|
|
* @dev: pointer to the struct device
|
|
|
* @node: a list of AB8500 GPADCs, hence prepared for
|
|
|
reentrance
|
|
|
+ * @parent: pointer to the struct ab8500
|
|
|
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
|
|
|
* the completion of gpadc conversion
|
|
|
* @ab8500_gpadc_lock: structure of type mutex
|
|
@@ -114,9 +120,9 @@ struct adc_cal_data {
|
|
|
* @cal_data array of ADC calibration data structs
|
|
|
*/
|
|
|
struct ab8500_gpadc {
|
|
|
- u8 chip_id;
|
|
|
struct device *dev;
|
|
|
struct list_head node;
|
|
|
+ struct ab8500 *parent;
|
|
|
struct completion ab8500_gpadc_complete;
|
|
|
struct mutex ab8500_gpadc_lock;
|
|
|
struct regulator *regu;
|
|
@@ -282,8 +288,9 @@ 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 */
|
|
|
- regulator_enable(gpadc->regu);
|
|
|
+ pm_runtime_get_sync(gpadc->dev);
|
|
|
|
|
|
/* Check if ADC is not busy, lock and proceed */
|
|
|
do {
|
|
@@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|
|
EN_BUF | EN_ICHAR);
|
|
|
break;
|
|
|
case BTEMP_BALL:
|
|
|
- if (gpadc->chip_id >= AB8500_CUT3P0) {
|
|
|
+ if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
|
|
|
/* Turn on btemp pull-up on ABB 3.0 */
|
|
|
ret = abx500_mask_and_set_register_interruptible(
|
|
|
gpadc->dev,
|
|
@@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|
|
* Delay might be needed for ABB8500 cut 3.0, if not, remove
|
|
|
* when hardware will be available
|
|
|
*/
|
|
|
- msleep(1);
|
|
|
+ usleep_range(1000, 1000);
|
|
|
break;
|
|
|
}
|
|
|
/* Intentional fallthrough */
|
|
@@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|
|
goto out;
|
|
|
}
|
|
|
/* wait for completion of conversion */
|
|
|
- if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
|
|
|
+ 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");
|
|
|
ret = -EINVAL;
|
|
@@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|
|
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
|
|
|
goto out;
|
|
|
}
|
|
|
- /* Disable VTVout LDO this is required for GPADC */
|
|
|
- regulator_disable(gpadc->regu);
|
|
|
+
|
|
|
+ pm_runtime_mark_last_busy(gpadc->dev);
|
|
|
+ pm_runtime_put_autosuspend(gpadc->dev);
|
|
|
+
|
|
|
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
|
|
|
|
|
return (high_data << 8) | low_data;
|
|
@@ -412,7 +422,9 @@ out:
|
|
|
*/
|
|
|
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
|
|
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
|
|
|
- regulator_disable(gpadc->regu);
|
|
|
+
|
|
|
+ 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);
|
|
@@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|
|
gpadc->cal_data[ADC_INPUT_VBAT].offset);
|
|
|
}
|
|
|
|
|
|
+static int ab8500_gpadc_runtime_suspend(struct device *dev)
|
|
|
+{
|
|
|
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ regulator_disable(gpadc->regu);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ab8500_gpadc_runtime_resume(struct device *dev)
|
|
|
+{
|
|
|
+ struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
|
|
+
|
|
|
+ regulator_enable(gpadc->regu);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ab8500_gpadc_runtime_idle(struct device *dev)
|
|
|
+{
|
|
|
+ pm_runtime_suspend(dev);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
int ret = 0;
|
|
@@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|
|
}
|
|
|
|
|
|
gpadc->dev = &pdev->dev;
|
|
|
+ gpadc->parent = dev_get_drvdata(pdev->dev.parent);
|
|
|
mutex_init(&gpadc->ab8500_gpadc_lock);
|
|
|
|
|
|
/* Initialize completion used to notify completion of conversion */
|
|
@@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
- /* Get Chip ID of the ABB ASIC */
|
|
|
- ret = abx500_get_chip_id(gpadc->dev);
|
|
|
- if (ret < 0) {
|
|
|
- dev_err(gpadc->dev, "failed to get chip ID\n");
|
|
|
- goto fail_irq;
|
|
|
- }
|
|
|
- gpadc->chip_id = (u8) ret;
|
|
|
-
|
|
|
/* VTVout LDO used to power up ab8500-GPADC */
|
|
|
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
|
|
|
if (IS_ERR(gpadc->regu)) {
|
|
@@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|
|
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
|
|
|
goto fail_irq;
|
|
|
}
|
|
|
+
|
|
|
+ platform_set_drvdata(pdev, gpadc);
|
|
|
+
|
|
|
+ regulator_enable(gpadc->regu);
|
|
|
+
|
|
|
+ pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
|
|
|
+ pm_runtime_use_autosuspend(gpadc->dev);
|
|
|
+ pm_runtime_set_active(gpadc->dev);
|
|
|
+ pm_runtime_enable(gpadc->dev);
|
|
|
+
|
|
|
ab8500_gpadc_read_calibration_data(gpadc);
|
|
|
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
|
|
|
dev_dbg(gpadc->dev, "probe success\n");
|
|
@@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
|
|
|
list_del(&gpadc->node);
|
|
|
/* remove interrupt - completion of Sw ADC conversion */
|
|
|
free_irq(gpadc->irq, gpadc);
|
|
|
- /* disable VTVout LDO that is being used by GPADC */
|
|
|
- regulator_put(gpadc->regu);
|
|
|
+
|
|
|
+ pm_runtime_get_sync(gpadc->dev);
|
|
|
+ pm_runtime_disable(gpadc->dev);
|
|
|
+
|
|
|
+ regulator_disable(gpadc->regu);
|
|
|
+
|
|
|
+ pm_runtime_set_suspended(gpadc->dev);
|
|
|
+
|
|
|
+ pm_runtime_put_noidle(gpadc->dev);
|
|
|
+
|
|
|
kfree(gpadc);
|
|
|
gpadc = NULL;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
|
|
|
+ SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
|
|
|
+ ab8500_gpadc_runtime_resume,
|
|
|
+ ab8500_gpadc_runtime_idle)
|
|
|
+};
|
|
|
+
|
|
|
static struct platform_driver ab8500_gpadc_driver = {
|
|
|
.probe = ab8500_gpadc_probe,
|
|
|
.remove = ab8500_gpadc_remove,
|
|
|
.driver = {
|
|
|
.name = "ab8500-gpadc",
|
|
|
.owner = THIS_MODULE,
|
|
|
+ .pm = &ab8500_gpadc_pm_ops,
|
|
|
},
|
|
|
};
|
|
|
|