[PATCH] ARM: LPC32xx: ADC support
Kevin Wells
wellsk40 at gmail.com
Thu Feb 2 17:53:15 EST 2012
On Thu, Feb 2, 2012 at 7:02 AM, <stigge at antcom.de> wrote:
> This patch adds a 3-channel ADC driver for the LPC32xx ARM SoC
>
> Signed-off-by: Roland Stigge <stigge at antcom.de>
>
> diff --git a/arch/arm/mach-lpc32xx/clock.c b/arch/arm/mach-lpc32xx/clock.c
> index c269ef5..9cda6fa 100644
> --- a/arch/arm/mach-lpc32xx/clock.c
> +++ b/arch/arm/mach-lpc32xx/clock.c
> @@ -760,6 +760,36 @@ static struct clk clk_tsc = {
> .get_rate = local_return_parent_rate,
> };
>
> +static int adc_onoff_enable(struct clk *clk, int enable)
> +{
> + u32 tmp;
> +
> + /* Use PERIPH_CLOCK */
> + tmp = __raw_readl(LPC32XX_CLKPWR_ADC_CLK_CTRL_1);
> + tmp |= LPC32XX_CLKPWR_ADCCTRL1_PCLK_SEL;
> + /*
> + * Set clock divider so that we have equal to or less than
> + * 4.5MHz clock at ADC
> + */
> + tmp |= clk->get_rate(clk) / 4500000 + 1;
> + __raw_writel(tmp, LPC32XX_CLKPWR_ADC_CLK_CTRL_1);
For this clock, also set clk->rate = (actual rate of the ADC input clock).
Something like clk->rate = clk->get_rate(clk->parent) / (computed divider)
If the clk->rate stays at 0, the clk_get_rate() function for the ADC will
return 13Mhz instead of around 4.5MHz.
(I know the driver doesn't use clk_get_rate(), but this might save some
debugging later if it ever does).
> +
> + if (enable == 0)
> + __raw_writel(0, clk->enable_reg);
> + else
> + __raw_writel(clk->enable_mask, clk->enable_reg);
> +
> + return 0;
> +}
> +
...
...
> +config LPC32XX_ADC
> + tristate "NXP LPC32XX ADC"
> + depends on ARCH_LPC32XX && !TOUCHSCREEN_LPC32XX
> + help
> + Say yes here to build support for the integrated ADC inside the
> + LPC32XX SoC. Note that this feature uses the same hardware as the
> + touchscreen driver, so you can only select one of the two drivers
> + (lpc32xx_adc or lpc32xx_ts). Provides direct access via sysfs.
I like how clean this driver is. Thanks for taking the time to write and submit
this.
> +
> endmenu
> diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
> index ceee7f3..f83ab95 100644
> --- a/drivers/staging/iio/adc/Makefile
> +++ b/drivers/staging/iio/adc/Makefile
> @@ -37,3 +37,4 @@ obj-$(CONFIG_AD7192) += ad7192.o
...
...
> + platform_set_drvdata(pdev, info);
> +
> + device_init_wakeup(&pdev->dev, 1);
I don't think you need this for this driver.
> + init_completion(&info->completion);
> +
> + printk(KERN_INFO "LPC32XX ADC driver loaded, IRQ %d\n", info->irq);
dev_info instead of printk
> +
> + return 0;
> +
> +errout6:
> + clk_disable(info->clk);
> + clk_put(info->clk);
> +errout5:
> + iio_device_unregister(info->dev);
> +errout4:
> + iounmap(info->adc_base);
> +errout3:
> + iio_free_device(info->dev);
> +errout1:
> + return retval;
> +}
> +
> +static int __devexit lpc32xx_adc_remove(struct platform_device *pdev)
> +{
> + struct lpc32xx_adc_info *info = platform_get_drvdata(pdev);
> +
> + free_irq(info->irq, info->dev);
> + platform_set_drvdata(pdev, NULL);
> + clk_disable(info->clk);
> + clk_put(info->clk);
> + iio_device_unregister(info->dev);
> + iio_free_device(info->dev);
> + iounmap(info->adc_base);
> + kfree(info);
This may be ok. Needs a sanity check only.
kfree() is used in remove() but not used in probe() failure path. Might be
missing in probe() or not needed here.
> +
> + printk(KERN_INFO "LPC32XX ADC driver unloaded\n");
dev_info()
> +
> + return 0;
> +}
> +
> +#if defined(CONFIG_SUSPEND)
> +static int lpc32xx_adc_suspend(struct device *dev)
> +{
> + struct lpc32xx_adc_info *info = dev_get_drvdata(dev);
> +
> + clk_disable(info->clk);
The ADC block doesn't have to remain clocked when it's not being used.
You might just encapsulate the main ADC read code with clock enable/disable
and remove the suspend code and enable/disable code in probe/remove.
This should save a little power when the ADC is idle.
> + return 0;
> +}
> +
> +static int lpc32xx_adc_resume(struct device *dev)
> +{
> + struct lpc32xx_adc_info *info = dev_get_drvdata(dev);
> +
> + clk_enable(info->clk);
> + return 0;
> +}
> +
> +static const struct dev_pm_ops lpc32xx_adc_pm_ops = {
> + .suspend = lpc32xx_adc_suspend,
> + .resume = lpc32xx_adc_resume,
> +};
> +#define LPC32XX_ADC_PM_OPS (&lpc32xx_adc_pm_ops)
> +#else
> +#define LPC32XX_ADC_PM_OPS NULL
> +#endif
> +
> +static struct platform_driver lpc32xx_adc_driver = {
> + .probe = lpc32xx_adc_probe,
> + .remove = __devexit_p(lpc32xx_adc_remove),
> + .driver = {
> + .name = MOD_NAME,
> + .owner = THIS_MODULE,
> + .pm = LPC32XX_ADC_PM_OPS,
> + },
> +};
> +
> +static int __init lpc32xx_adc_init(void)
> +{
> + return platform_driver_register(&lpc32xx_adc_driver);
> +}
> +
> +static void __exit lpc32xx_adc_exit(void)
> +{
> + platform_driver_unregister(&lpc32xx_adc_driver);
> +}
> +
> +module_init(lpc32xx_adc_init);
> +module_exit(lpc32xx_adc_exit);
> +
> +MODULE_AUTHOR("Roland Stigge <stigge at antcom.de>");
> +MODULE_DESCRIPTION("LPC32XX ADC driver");
> +MODULE_LICENSE("GPL");
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
More information about the linux-arm-kernel
mailing list