summaryrefslogtreecommitdiff
path: root/arch/arm/plat-mxs/lradc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-mxs/lradc.c')
-rw-r--r--arch/arm/plat-mxs/lradc.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/arch/arm/plat-mxs/lradc.c b/arch/arm/plat-mxs/lradc.c
new file mode 100644
index 000000000000..3d9089e3ef25
--- /dev/null
+++ b/arch/arm/plat-mxs/lradc.c
@@ -0,0 +1,385 @@
+/*
+ * Freescale STMP37XX/STMP378X LRADC helper routines
+ *
+ * Embedded Alley Solutions, Inc <source@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sysdev.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+
+#include <mach/hardware.h>
+#include <mach/device.h>
+#include <mach/regs-lradc.h>
+#include <mach/lradc.h>
+
+struct lradc_device {
+ struct sys_device sys;
+ unsigned int base;
+ unsigned int vddio_voltage;
+ unsigned int battery_voltage;
+};
+
+static int channels[8];
+
+static __refdata struct lradc_device mxs_lradc;
+
+int hw_lradc_use_channel(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return -EINVAL;
+ channels[channel]++;
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_use_channel);
+
+int hw_lradc_unuse_channel(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return -EINVAL;
+ channels[channel]--;
+ return 0;
+}
+EXPORT_SYMBOL(hw_lradc_unuse_channel);
+
+void hw_lradc_reinit(int enable_ground_ref, unsigned freq)
+{
+ __raw_writel(BM_LRADC_CTRL0_SFTRST,
+ mxs_lradc.base + HW_LRADC_CTRL0_SET);
+ udelay(1);
+ __raw_writel(BM_LRADC_CTRL0_SFTRST,
+ mxs_lradc.base + HW_LRADC_CTRL0_CLR);
+
+ /* Clear the Clock Gate for normal operation */
+ __raw_writel(BM_LRADC_CTRL0_CLKGATE,
+ mxs_lradc.base + HW_LRADC_CTRL0_CLR);
+
+ if (enable_ground_ref)
+ __raw_writel(BM_LRADC_CTRL0_ONCHIP_GROUNDREF,
+ mxs_lradc.base + HW_LRADC_CTRL0_SET);
+ else
+ __raw_writel(BM_LRADC_CTRL0_ONCHIP_GROUNDREF,
+ mxs_lradc.base + HW_LRADC_CTRL0_CLR);
+
+ __raw_writel(BM_LRADC_CTRL3_CYCLE_TIME,
+ mxs_lradc.base + HW_LRADC_CTRL3_CLR);
+ __raw_writel(BF_LRADC_CTRL3_CYCLE_TIME(freq),
+ mxs_lradc.base + HW_LRADC_CTRL3_SET);
+
+ __raw_writel(BM_LRADC_CTRL4_LRADC6SELECT | BM_LRADC_CTRL4_LRADC7SELECT,
+ mxs_lradc.base + HW_LRADC_CTRL4_CLR);
+ __raw_writel(BF_LRADC_CTRL4_LRADC6SELECT(mxs_lradc.vddio_voltage),
+ mxs_lradc.base + HW_LRADC_CTRL4_SET);
+ __raw_writel(BF_LRADC_CTRL4_LRADC7SELECT(mxs_lradc.battery_voltage),
+ mxs_lradc.base + HW_LRADC_CTRL4_SET);
+}
+
+int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling)
+{
+ /*
+ * check if the lradc channel is present in this product
+ */
+ if (!hw_lradc_present(channel))
+ return -ENODEV;
+
+ hw_lradc_configure_channel(channel, !0 /* div2 */ ,
+ 0 /* acc */ ,
+ 0 /* num_samples */);
+
+ /* Setup the trigger loop forever */
+ hw_lradc_set_delay_trigger(trigger, 1 << channel,
+ 1 << trigger, 0, sampling);
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ __raw_writel(0xFFFFFFFF, mxs_lradc.base + HW_LRADC_CHn_CLR(channel));
+ return 0;
+}
+
+EXPORT_SYMBOL(hw_lradc_init_ladder);
+
+int hw_lradc_stop_ladder(int channel, int trigger)
+{
+ /*
+ * check if the lradc channel is present in this product
+ */
+ if (!hw_lradc_present(channel))
+ return -ENODEV;
+ hw_lradc_clear_delay_trigger(trigger, 1 << channel, 1 << trigger);
+ return 0;
+}
+
+EXPORT_SYMBOL(hw_lradc_stop_ladder);
+
+int hw_lradc_present(int channel)
+{
+ if (channel < 0 || channel > 7)
+ return 0;
+ return __raw_readl(mxs_lradc.base + HW_LRADC_STATUS)
+ & (1 << (16 + channel));
+}
+
+EXPORT_SYMBOL(hw_lradc_present);
+
+void hw_lradc_configure_channel(int channel, int enable_div2,
+ int enable_acc, int samples)
+{
+ if (enable_div2)
+ __raw_writel(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << channel),
+ mxs_lradc.base + HW_LRADC_CTRL2_SET);
+ else
+ __raw_writel(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << channel),
+ mxs_lradc.base + HW_LRADC_CTRL2_CLR);
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ __raw_writel(0xFFFFFFFF, mxs_lradc.base + HW_LRADC_CHn_CLR(channel));
+
+ /* Sets NUM_SAMPLES bitfield of HW_LRADC_CHn register. */
+ __raw_writel(BM_LRADC_CHn_NUM_SAMPLES,
+ mxs_lradc.base + HW_LRADC_CHn_CLR(channel));
+ __raw_writel(BF_LRADC_CHn_NUM_SAMPLES(samples),
+ mxs_lradc.base + HW_LRADC_CHn_SET(channel));
+
+ if (enable_acc)
+ __raw_writel(BM_LRADC_CHn_ACCUMULATE,
+ mxs_lradc.base + HW_LRADC_CHn_SET(channel));
+ else
+ __raw_writel(BM_LRADC_CHn_ACCUMULATE,
+ mxs_lradc.base + HW_LRADC_CHn_CLR(channel));
+}
+
+EXPORT_SYMBOL(hw_lradc_configure_channel);
+
+void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers, u32 loops, u32 delays)
+{
+ /* set TRIGGER_LRADCS in HW_LRADC_DELAYn */
+ __raw_writel(BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc),
+ mxs_lradc.base + HW_LRADC_DELAYn_SET(trigger));
+ __raw_writel(BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers),
+ mxs_lradc.base + HW_LRADC_DELAYn_SET(trigger));
+
+ __raw_writel(BM_LRADC_DELAYn_LOOP_COUNT | BM_LRADC_DELAYn_DELAY,
+ mxs_lradc.base + HW_LRADC_DELAYn_CLR(trigger));
+ __raw_writel(BF_LRADC_DELAYn_LOOP_COUNT(loops),
+ mxs_lradc.base + HW_LRADC_DELAYn_SET(trigger));
+ __raw_writel(BF_LRADC_DELAYn_DELAY(delays),
+ mxs_lradc.base + HW_LRADC_DELAYn_SET(trigger));
+}
+
+EXPORT_SYMBOL(hw_lradc_set_delay_trigger);
+
+void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc,
+ u32 delay_triggers)
+{
+ __raw_writel(BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc),
+ mxs_lradc.base + HW_LRADC_DELAYn_CLR(trigger));
+ __raw_writel(BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers),
+ mxs_lradc.base + HW_LRADC_DELAYn_CLR(trigger));
+}
+
+EXPORT_SYMBOL(hw_lradc_clear_delay_trigger);
+
+void hw_lradc_set_delay_trigger_kick(int trigger, int value)
+{
+ if (value)
+ __raw_writel(BM_LRADC_DELAYn_KICK,
+ mxs_lradc.base + HW_LRADC_DELAYn_SET(trigger));
+ else
+ __raw_writel(BM_LRADC_DELAYn_KICK,
+ mxs_lradc.base + HW_LRADC_DELAYn_CLR(trigger));
+}
+
+EXPORT_SYMBOL(hw_lradc_set_delay_trigger_kick);
+
+u32 hw_lradc_vddio(void)
+{
+ /* Clear the Soft Reset and Clock Gate for normal operation */
+ __raw_writel(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE,
+ mxs_lradc.base + HW_LRADC_CTRL0_CLR);
+
+ /*
+ * Clear the divide by two for channel 6 since
+ * it has a HW divide-by-two built in.
+ */
+ __raw_writel(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1 << VDDIO_VOLTAGE_CH),
+ mxs_lradc.base + HW_LRADC_CTRL2_CLR);
+
+ /* Clear the accumulator & NUM_SAMPLES */
+ __raw_writel(0xFFFFFFFF,
+ mxs_lradc.base + HW_LRADC_CHn_CLR(VDDIO_VOLTAGE_CH));
+
+ /* Clear the interrupt flag */
+ __raw_writel(BM_LRADC_CTRL1_LRADC6_IRQ,
+ mxs_lradc.base + HW_LRADC_CTRL1_CLR);
+
+ /*
+ * Get VddIO; this is the max scale value for the button resistor
+ * ladder.
+ * schedule ch 6:
+ */
+ __raw_writel(BF_LRADC_CTRL0_SCHEDULE(1 << VDDIO_VOLTAGE_CH),
+ mxs_lradc.base + HW_LRADC_CTRL0_SET);
+
+ /* wait for completion */
+ while ((__raw_readl(mxs_lradc.base + HW_LRADC_CTRL1)
+ & BM_LRADC_CTRL1_LRADC6_IRQ) != BM_LRADC_CTRL1_LRADC6_IRQ)
+ cpu_relax();
+
+ /* Clear the interrupt flag */
+ __raw_writel(BM_LRADC_CTRL1_LRADC6_IRQ,
+ mxs_lradc.base + HW_LRADC_CTRL1_CLR);
+
+ /* read ch 6 value. */
+ return __raw_readl(mxs_lradc.base + HW_LRADC_CHn(VDDIO_VOLTAGE_CH)) &
+ BM_LRADC_CHn_VALUE;
+}
+
+EXPORT_SYMBOL(hw_lradc_vddio);
+
+#ifdef CONFIG_PM
+static u32 lradc_registers[0x16];
+static int do_gate;
+
+static int hw_lradc_suspend(struct sys_device *dev, pm_message_t state)
+{
+ int i;
+
+ do_gate = 1;
+ for (i = 0; i < ARRAY_SIZE(channels); i++)
+ if (channels[i] > 0) {
+ do_gate = 0;
+ break;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(lradc_registers); i++)
+ lradc_registers[i] = __raw_readl(mxs_lradc.base + (i << 4));
+
+ if (do_gate)
+ __raw_writel(BM_LRADC_CTRL0_CLKGATE,
+ mxs_lradc.base + HW_LRADC_CTRL0_SET);
+ return 0;
+}
+
+static int hw_lradc_resume(struct sys_device *dev)
+{
+ int i;
+
+ if (do_gate) {
+ __raw_writel(BM_LRADC_CTRL0_SFTRST,
+ mxs_lradc.base + HW_LRADC_CTRL0_SET);
+ udelay(10);
+ __raw_writel(BM_LRADC_CTRL0_SFTRST |
+ BM_LRADC_CTRL0_CLKGATE,
+ mxs_lradc.base + HW_LRADC_CTRL0_CLR);
+ }
+ for (i = 0; i < ARRAY_SIZE(lradc_registers); i++)
+ __raw_writel(lradc_registers[i], mxs_lradc.base + (i << 4));
+ return 0;
+}
+
+#endif
+
+static struct sysdev_class mxs_lradc_sysclass = {
+ .name = "mxs-lradc",
+#ifdef CONFIG_PM
+ .suspend = hw_lradc_suspend,
+ .resume = hw_lradc_resume,
+#endif
+};
+
+static int lradc_freq = LRADC_CLOCK_6MHZ;
+
+static int __init lradc_freq_setup(char *str)
+{
+ long freq;
+
+ if (strict_strtol(str, 0, &freq) < 0)
+ return 0;
+
+ if (freq < 0)
+ return 0;
+ if (freq >= 6)
+ lradc_freq = LRADC_CLOCK_6MHZ;
+ else if (freq >= 4)
+ lradc_freq = LRADC_CLOCK_4MHZ;
+ else if (freq >= 3)
+ lradc_freq = LRADC_CLOCK_3MHZ;
+ else if (freq >= 2)
+ lradc_freq = LRADC_CLOCK_2MHZ;
+ else
+ return 0;
+ return 1;
+}
+
+__setup("lradc_freq=", lradc_freq_setup);
+
+static int __devinit mxs_lradc_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct mxs_lradc_plat_data *plat_data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL)
+ return -ENODEV;
+
+ plat_data = (struct mxs_lradc_plat_data *)(pdev->dev.platform_data);
+ if (plat_data == NULL)
+ return -EFAULT;
+
+ mxs_lradc.base = (unsigned int)IO_ADDRESS(res->start);
+ mxs_lradc.sys.id = -1;
+ mxs_lradc.sys.cls = &mxs_lradc_sysclass;
+ mxs_lradc.vddio_voltage = plat_data->vddio_voltage;
+ mxs_lradc.battery_voltage = plat_data->battery_voltage;
+ hw_lradc_reinit(0, lradc_freq);
+ return sysdev_register(&mxs_lradc.sys);
+}
+
+static int __devexit mxs_lradc_remove(struct platform_device *pdev)
+{
+ sysdev_unregister(&mxs_lradc.sys);
+ return 0;
+}
+
+static __refdata struct platform_driver mxs_lradc_drv = {
+ .probe = mxs_lradc_probe,
+ .remove = __devexit_p(mxs_lradc_remove),
+ .driver = {
+ .name = "mxs-lradc",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init hw_lradc_init(void)
+{
+ sysdev_class_register(&mxs_lradc_sysclass);
+ platform_driver_register(&mxs_lradc_drv);
+ return 0;
+}
+
+static void __exit hw_lradc_exit(void)
+{
+ platform_driver_unregister(&mxs_lradc_drv);
+ sysdev_class_unregister(&mxs_lradc_sysclass);
+}
+
+subsys_initcall(hw_lradc_init);
+module_exit(hw_lradc_exit);