diff options
Diffstat (limited to 'arch/arm/plat-samsung/adc.c')
| -rw-r--r-- | arch/arm/plat-samsung/adc.c | 160 | 
1 files changed, 99 insertions, 61 deletions
diff --git a/arch/arm/plat-samsung/adc.c b/arch/arm/plat-samsung/adc.c index e8f2be2d67f..79690f2f6d3 100644 --- a/arch/arm/plat-samsung/adc.c +++ b/arch/arm/plat-samsung/adc.c @@ -21,6 +21,7 @@  #include <linux/clk.h>  #include <linux/interrupt.h>  #include <linux/io.h> +#include <linux/regulator/consumer.h>  #include <plat/regs-adc.h>  #include <plat/adc.h> @@ -39,8 +40,11 @@   */  enum s3c_cpu_type { -	TYPE_S3C24XX, -	TYPE_S3C64XX +	TYPE_ADCV1, /* S3C24XX */ +	TYPE_ADCV11, /* S3C2443 */ +	TYPE_ADCV12, /* S3C2416, S3C2450 */ +	TYPE_ADCV2, /* S3C64XX, S5P64X0, S5PC100 */ +	TYPE_ADCV3, /* S5PV210, S5PC110, EXYNOS4210 */  };  struct s3c_adc_client { @@ -71,6 +75,7 @@ struct adc_device {  	unsigned int		 prescale;  	int			 irq; +	struct regulator	*vdd;  };  static struct adc_device *adc_dev; @@ -91,15 +96,24 @@ 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); -	con &= ~S3C2410_ADCCON_MUXMASK; +	if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV2) +		con &= ~S3C2410_ADCCON_MUXMASK;  	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 if (cpu == TYPE_ADCV11 || cpu == TYPE_ADCV12) +			writel(client->channel & 0xf, +						adc->regs + S3C2443_ADCMUX); +		else +			con |= S3C2410_ADCCON_SELMUX(client->channel); +	}  	writel(con, adc->regs + S3C2410_ADCCON);  } @@ -143,11 +157,13 @@ int s3c_adc_start(struct s3c_adc_client *client,  		return -EINVAL;  	} -	if (client->is_ts && adc->ts_pend) -		return -EAGAIN; -  	spin_lock_irqsave(&adc->lock, flags); +	if (client->is_ts && adc->ts_pend) { +		spin_unlock_irqrestore(&adc->lock, flags); +		return -EAGAIN; +	} +  	client->channel = channel;  	client->nr_samples = nr_samples; @@ -285,13 +301,13 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)  	client->nr_samples--; -	if (cpu == TYPE_S3C64XX) { -		/* S3C64XX ADC resolution is 12-bit */ -		data0 &= 0xfff; -		data1 &= 0xfff; -	} else { +	if (cpu == TYPE_ADCV1 || cpu == TYPE_ADCV11) {  		data0 &= 0x3ff;  		data1 &= 0x3ff; +	} else { +		/* S3C2416/S3C64XX/S5P ADC resolution is 12-bit */ +		data0 &= 0xfff; +		data1 &= 0xfff;  	}  	if (client->convert_cb) @@ -312,7 +328,7 @@ static irqreturn_t s3c_adc_irq(int irq, void *pw)  	}  exit: -	if (cpu == TYPE_S3C64XX) { +	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) {  		/* Clear ADC interrupt */  		writel(0, adc->regs + S3C64XX_ADCCLRINT);  	} @@ -324,10 +340,11 @@ static int s3c_adc_probe(struct platform_device *pdev)  	struct device *dev = &pdev->dev;  	struct adc_device *adc;  	struct resource *regs; +	enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data;  	int ret;  	unsigned tmp; -	adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); +	adc = devm_kzalloc(dev, sizeof(struct adc_device), GFP_KERNEL);  	if (adc == NULL) {  		dev_err(dev, "failed to allocate adc_device\n");  		return -ENOMEM; @@ -338,47 +355,50 @@ static int s3c_adc_probe(struct platform_device *pdev)  	adc->pdev = pdev;  	adc->prescale = S3C2410_ADCCON_PRSCVL(49); +	adc->vdd = devm_regulator_get(dev, "vdd"); +	if (IS_ERR(adc->vdd)) { +		dev_err(dev, "operating without regulator \"vdd\" .\n"); +		return PTR_ERR(adc->vdd); +	} +  	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; +		return -ENOENT;  	} -	ret = request_irq(adc->irq, s3c_adc_irq, 0, dev_name(dev), adc); +	ret = devm_request_irq(dev, 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; +		return ret;  	} -	adc->clk = clk_get(dev, "adc"); +	adc->clk = devm_clk_get(dev, "adc");  	if (IS_ERR(adc->clk)) {  		dev_err(dev, "failed to get adc clock\n"); -		ret = PTR_ERR(adc->clk); -		goto err_irq; +		return PTR_ERR(adc->clk);  	}  	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!regs) { -		dev_err(dev, "failed to find registers\n"); -		ret = -ENXIO; -		goto err_clk; -	} +	adc->regs = devm_ioremap_resource(dev, regs); +	if (IS_ERR(adc->regs)) +		return PTR_ERR(adc->regs); -	adc->regs = ioremap(regs->start, resource_size(regs)); -	if (!adc->regs) { -		dev_err(dev, "failed to map registers\n"); -		ret = -ENXIO; -		goto err_clk; -	} +	ret = regulator_enable(adc->vdd); +	if (ret) +		return ret;  	clk_enable(adc->clk);  	tmp = adc->prescale | S3C2410_ADCCON_PRSCEN; -	if (platform_get_device_id(pdev)->driver_data == TYPE_S3C64XX) { -		/* Enable 12-bit ADC resolution */ + +	/* Enable 12-bit ADC resolution */ +	if (cpu == TYPE_ADCV12) +		tmp |= S3C2416_ADCCON_RESSEL; +	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3)  		tmp |= S3C64XX_ADCCON_RESSEL; -	} +  	writel(tmp, adc->regs + S3C2410_ADCCON);  	dev_info(dev, "attached adc driver\n"); @@ -387,34 +407,23 @@ static int s3c_adc_probe(struct platform_device *pdev)  	adc_dev = adc;  	return 0; - - err_clk: -	clk_put(adc->clk); - - err_irq: -	free_irq(adc->irq, adc); - - err_alloc: -	kfree(adc); -	return ret;  } -static int __devexit s3c_adc_remove(struct platform_device *pdev) +static int s3c_adc_remove(struct platform_device *pdev)  {  	struct adc_device *adc = platform_get_drvdata(pdev); -	iounmap(adc->regs); -	free_irq(adc->irq, adc);  	clk_disable(adc->clk); -	clk_put(adc->clk); -	kfree(adc); +	regulator_disable(adc->vdd);  	return 0;  }  #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; @@ -428,19 +437,35 @@ 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;  } -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); +	enum s3c_cpu_type cpu = platform_get_device_id(pdev)->driver_data; +	int ret; +	unsigned long tmp; +	ret = regulator_enable(adc->vdd); +	if (ret) +		return ret;  	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 (cpu == TYPE_ADCV12) +		tmp |= S3C2416_ADCCON_RESSEL; +	if (cpu == TYPE_ADCV2 || cpu == TYPE_ADCV3) +		tmp |= S3C64XX_ADCCON_RESSEL; + +	writel(tmp, adc->regs + S3C2410_ADCCON);  	return 0;  } @@ -453,25 +478,38 @@ 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		= "s3c2443-adc", +		.driver_data	= TYPE_ADCV11, +	}, { +		.name		= "s3c2416-adc", +		.driver_data	= TYPE_ADCV12,  	}, {  		.name           = "s3c64xx-adc", -		.driver_data    = TYPE_S3C64XX, +		.driver_data    = TYPE_ADCV2, +	}, { +		.name		= "samsung-adc-v3", +		.driver_data	= TYPE_ADCV3,  	},  	{ }  };  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, +	.remove		= s3c_adc_remove,  };  static int __init adc_init(void) @@ -485,4 +523,4 @@ static int __init adc_init(void)  	return ret;  } -arch_initcall(adc_init); +module_init(adc_init);  | 
