From f462904ef1508c0a2cc22c65478a7be0cd89f47a Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 20 Jul 2011 21:08:17 +0900 Subject: ARM: SAMSUNG: use regulator VDD for ADC This patch allows the Samsung ADC driver to enable VDD regulator at probe and resume and to disable at exit and suspend. In a platform where ADC's VDD regulator is not "always-on", this control is required although this patch does not provide fine-grained power control (turning on the regulator only when being accessed). However, if VDD regulator ("vdd" for the adc device) is not provided, the regulator control will not be activated because there are platforms that do not provide regulator for ADC device. arch_initcall has been modified to module_init in order to allow regulators to be available at probe. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-samsung/adc.c | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'arch/arm/plat-samsung/adc.c') diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index e8f2be2d67f..2224128cc60 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -71,6 +72,7 @@ struct adc_device { unsigned int prescale; int irq; + struct regulator *vdd; }; static struct adc_device *adc_dev; @@ -338,17 +340,24 @@ static int s3c_adc_probe(struct platform_device *pdev) adc->pdev = pdev; adc->prescale = S3C2410_ADCCON_PRSCVL(49); + adc->vdd = regulator_get(dev, "vdd"); + if (IS_ERR(adc->vdd)) { + dev_err(dev, "operating without regulator \"vdd\" .\n"); + ret = PTR_ERR(adc->vdd); + goto err_alloc; + } + adc->irq = platform_get_irq(pdev, 1); if (adc->irq <= 0) { dev_err(dev, "failed to get adc irq\n"); ret = -ENOENT; - goto err_alloc; + goto err_reg; } ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); if (ret < 0) { dev_err(dev, "failed to attach adc irq\n"); - goto err_alloc; + goto err_reg; } adc->clk = clk_get(dev, "adc"); @@ -372,6 +381,10 @@ static int s3c_adc_probe(struct platform_device *pdev) goto err_clk; } + ret = regulator_enable(adc->vdd); + if (ret) + goto err_ioremap; + clk_enable(adc->clk); tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; @@ -388,12 +401,15 @@ static int s3c_adc_probe(struct platform_device *pdev) return 0; + err_ioremap: + iounmap(adc->regs); err_clk: clk_put(adc->clk); err_irq: free_irq(adc->irq, adc); - + err_reg: + regulator_put(adc->vdd); err_alloc: kfree(adc); return ret; @@ -406,6 +422,8 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev) iounmap(adc->regs); free_irq(adc->irq, adc); clk_disable(adc->clk); + regulator_disable(adc->vdd); + regulator_put(adc->vdd); clk_put(adc->clk); kfree(adc); @@ -428,6 +446,7 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) disable_irq(adc->irq); spin_unlock_irqrestore(&adc->lock, flags); clk_disable(adc->clk); + regulator_disable(adc->vdd); return 0; } @@ -435,7 +454,11 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) static int s3c_adc_resume(struct platform_device *pdev) { struct adc_device *adc = platform_get_drvdata(pdev); + int ret; + ret = regulator_enable(adc->vdd); + if (ret) + return ret; clk_enable(adc->clk); enable_irq(adc->irq); @@ -485,4 +508,4 @@ static int __init adc_init(void) return ret; } -arch_initcall(adc_init); +module_init(adc_init); -- cgit v1.2.3-70-g09d2 From 64df92ea7893d1cfd714c2f6acfd2eb15fbe3279 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 20 Jul 2011 21:08:18 +0900 Subject: ARM: SAMSUNG: ADC Channel selection In S5PV210/S5PC110/EXYNOS4, ADCMUX channel selection uses ADCMUX register, not ADCCON register. This patch corrects the behavior of SAMSUNG-ADC for such CPUs. Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-samsung/adc.c | 29 ++++++++++++++++++--------- arch/arm/plat-samsung/include/plat/regs-adc.h | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) (limited to 'arch/arm/plat-samsung/adc.c') diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index 2224128cc60..45cc7e6982c 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -40,8 +40,9 @@ */ enum s3c_cpu_type { - TYPE_S3C24XX, - TYPE_S3C64XX + TYPE_ADCV1, /* S3C24XX */ + TYPE_ADCV2, /* S3C64XX, S5P64X0, S5PC100 */ + TYPE_ADCV3, /* S5PV210, S5PC110, EXYNOS4210 */ }; struct s3c_adc_client { @@ -93,6 +94,7 @@ static inline void s3c_adc_select(struct adc_device *adc, struct s3c_adc_client *client) { unsigned con = readl(adc->regs + S3C2410_ADCCON); + enum s3c_cpu_type cpu = platform_get_device_id(adc->pdev)->driver_data; client->select_cb(client, 1); @@ -100,8 +102,12 @@ static inline void s3c_adc_select(struct adc_device *adc, con &= ~S3C2410_ADCCON_STDBM; con &= ~S3C2410_ADCCON_STARTMASK; - if (!client->is_ts) - con |= S3C2410_ADCCON_SELMUX(client->channel); + if (!client->is_ts) { + if (cpu == TYPE_ADCV3) + writel(client->channel & 0xf, adc->regs + S5P_ADCMUX); + else + con |= S3C2410_ADCCON_SELMUX(client->channel); + } writel(con, adc->regs + S3C2410_ADCCON); } @@ -287,8 +293,8 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) client->nr_samples--; - if (cpu == TYPE_S3C64XX) { - /* S3C64XX ADC resolution is 12-bit */ + if (cpu != TYPE_ADCV1) { + /* S3C64XX/S5P ADC resolution is 12-bit */ data0 &= 0xfff; data1 &= 0xfff; } else { @@ -314,7 +320,7 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw) } exit: - if (cpu == TYPE_S3C64XX) { + if (cpu != TYPE_ADCV1) { /* Clear ADC interrupt */ writel(0, adc->regs + S3C64XX_ADCCLRINT); } @@ -388,7 +394,7 @@ static int s3c_adc_probe(struct platform_device *pdev) clk_enable(adc->clk); tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; - if (platform_get_device_id(pdev)->driver_data == TYPE_S3C64XX) { + if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1) { /* Enable 12-bit ADC resolution */ tmp |= S3C64XX_ADCCON_RESSEL; } @@ -476,10 +482,13 @@ static int s3c_adc_resume(struct platform_device *pdev) static struct platform_device_id s3c_adc_driver_ids[] = { { .name = "s3c24xx-adc", - .driver_data = TYPE_S3C24XX, + .driver_data = TYPE_ADCV1, }, { .name = "s3c64xx-adc", - .driver_data = TYPE_S3C64XX, + .driver_data = TYPE_ADCV2, + }, { + .name = "samsung-adc-v3", + .driver_data = TYPE_ADCV3, }, { } }; diff --git a/arch/arm/plat-samsung/include/plat/regs-adc.h b/arch/arm/plat-samsung/include/plat/regs-adc.h index 7554c4fcddb..035e8c38d69 100644 --- a/arch/arm/plat-samsung/include/plat/regs-adc.h +++ b/arch/arm/plat-samsung/include/plat/regs-adc.h @@ -21,6 +21,7 @@ #define S3C2410_ADCDAT1 S3C2410_ADCREG(0x10) #define S3C64XX_ADCUPDN S3C2410_ADCREG(0x14) #define S3C64XX_ADCCLRINT S3C2410_ADCREG(0x18) +#define S5P_ADCMUX S3C2410_ADCREG(0x1C) #define S3C64XX_ADCCLRINTPNDNUP S3C2410_ADCREG(0x20) -- cgit v1.2.3-70-g09d2 From 67dcaec8d62ab751dbd1d5f9b3f3c1ce4205edf5 Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Wed, 20 Jul 2011 21:08:18 +0900 Subject: ARM: SAMSUNG: Revise PM for 12-bit ADC operations - Fixed: 12bit precision is lost at suspend/resume - Updated: use pm_dev_ops Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Kukjin Kim --- arch/arm/plat-samsung/adc.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'arch/arm/plat-samsung/adc.c') diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index 45cc7e6982c..ee8deef1948 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -437,8 +437,10 @@ static int __devexit s3c_adc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) +static int s3c_adc_suspend(struct device *dev) { + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); struct adc_device *adc = platform_get_drvdata(pdev); unsigned long flags; u32 con; @@ -457,10 +459,13 @@ static int s3c_adc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -static int s3c_adc_resume(struct platform_device *pdev) +static int s3c_adc_resume(struct device *dev) { + struct platform_device *pdev = container_of(dev, + struct platform_device, dev); struct adc_device *adc = platform_get_drvdata(pdev); int ret; + unsigned long tmp; ret = regulator_enable(adc->vdd); if (ret) @@ -468,8 +473,11 @@ static int s3c_adc_resume(struct platform_device *pdev) clk_enable(adc->clk); enable_irq(adc->irq); - writel(adc->prescale | S3C2410_ADCCON_PRSCEN, - adc->regs + S3C2410_ADCCON); + tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; + /* Enable 12-bit ADC resolution */ + if (platform_get_device_id(pdev)->driver_data != TYPE_ADCV1) + tmp |= S3C64XX_ADCCON_RESSEL; + writel(tmp, adc->regs + S3C2410_ADCCON); return 0; } @@ -494,16 +502,20 @@ static struct platform_device_id s3c_adc_driver_ids[] = { }; MODULE_DEVICE_TABLE(platform, s3c_adc_driver_ids); +static const struct dev_pm_ops adc_pm_ops = { + .suspend = s3c_adc_suspend, + .resume = s3c_adc_resume, +}; + static struct platform_driver s3c_adc_driver = { .id_table = s3c_adc_driver_ids, .driver = { .name = "s3c-adc", .owner = THIS_MODULE, + .pm = &adc_pm_ops, }, .probe = s3c_adc_probe, .remove = __devexit_p(s3c_adc_remove), - .suspend = s3c_adc_suspend, - .resume = s3c_adc_resume, }; static int __init adc_init(void) -- cgit v1.2.3-70-g09d2