diff options
Diffstat (limited to 'drivers/mfd/ab8500-gpadc.c')
| -rw-r--r-- | drivers/mfd/ab8500-gpadc.c | 90 | 
1 files changed, 71 insertions, 19 deletions
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index 3fb1f40d638..b1f3561b023 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -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,  	},  };  |