Thread (3 messages) 3 messages, 3 authors, 2012-02-02

[PATCH] ARM: LPC32xx: ADC support

From: Kevin Wells <hidden>
Date: 2012-02-02 22:53:19
Also in: linux-iio, lkml

On Thu, Feb 2, 2012 at 7:02 AM,  [off-list ref] wrote:
quoted hunk ↗ jump to hunk
This patch adds a 3-channel ADC driver for the LPC32xx ARM SoC

Signed-off-by: Roland Stigge <redacted>
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.
quoted hunk ↗ jump to hunk
+
?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 [off-list ref]");
+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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help