diff options
Diffstat (limited to 'arch/arm/mach-stmp3xxx/lradc.c')
-rw-r--r-- | arch/arm/mach-stmp3xxx/lradc.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/arch/arm/mach-stmp3xxx/lradc.c b/arch/arm/mach-stmp3xxx/lradc.c new file mode 100644 index 000000000000..6f23022b41c6 --- /dev/null +++ b/arch/arm/mach-stmp3xxx/lradc.c @@ -0,0 +1,294 @@ +/* + * Freescale STMP37XX/STMP378X LRADC helper routines + * + * Embedded Alley Solutions, Inc <source@embeddedalley.com> + * + * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * 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/errno.h> +#include <linux/sysdev.h> +#include <linux/bitops.h> +#include <linux/irq.h> +#include <mach/hardware.h> +#include <linux/delay.h> +#include <mach/regs-lradc.h> +#include <mach/lradc.h> + +static int channels[8]; + +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) +{ + HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_SFTRST); + udelay(1); + HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST); + + /* Clear the Clock Gate for normal operation */ + HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_CLKGATE); + + if (enable_ground_ref) + HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_ONCHIP_GROUNDREF); + else + HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_ONCHIP_GROUNDREF); + + HW_LRADC_CTRL3_CLR(BM_LRADC_CTRL3_CYCLE_TIME); + HW_LRADC_CTRL3_SET(BF_LRADC_CTRL3_CYCLE_TIME(freq)); + + HW_LRADC_CTRL4_CLR(BM_LRADC_CTRL4_LRADC6SELECT | + BM_LRADC_CTRL4_LRADC7SELECT); + HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC6SELECT(VDDIO_VOLTAGE_CH)); + HW_LRADC_CTRL4_SET(BF_LRADC_CTRL4_LRADC7SELECT(BATTERY_VOLTAGE_CH)); +} + +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 */ + HW_LRADC_CHn_CLR(channel, 0xFFFFFFFF); + 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 HW_LRADC_STATUS_RD() & (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) + HW_LRADC_CTRL2_SET(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<channel)); + else + HW_LRADC_CTRL2_CLR(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<channel)); + + /* Clear the accumulator & NUM_SAMPLES */ + HW_LRADC_CHn_CLR(channel, 0xFFFFFFFF); + + /* Sets NUM_SAMPLES bitfield of HW_LRADC_CHn register. */ + HW_LRADC_CHn_CLR(channel, BM_LRADC_CHn_NUM_SAMPLES); + HW_LRADC_CHn_SET(channel, BF_LRADC_CHn_NUM_SAMPLES(samples)); + + if (enable_acc) + HW_LRADC_CHn_SET(channel, BM_LRADC_CHn_ACCUMULATE); + else + HW_LRADC_CHn_CLR(channel, BM_LRADC_CHn_ACCUMULATE); +} +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 */ + HW_LRADC_DELAYn_SET(trigger, + BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc)); + HW_LRADC_DELAYn_SET(trigger, + BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers)); + + HW_LRADC_DELAYn_CLR(trigger, + BM_LRADC_DELAYn_LOOP_COUNT | BM_LRADC_DELAYn_DELAY); + HW_LRADC_DELAYn_SET(trigger, + BF_LRADC_DELAYn_LOOP_COUNT(loops)); + HW_LRADC_DELAYn_SET(trigger, + BF_LRADC_DELAYn_DELAY(delays)); +} +EXPORT_SYMBOL(hw_lradc_set_delay_trigger); + +void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc, + u32 delay_triggers) +{ + HW_LRADC_DELAYn_CLR(trigger, + BF_LRADC_DELAYn_TRIGGER_LRADCS(trigger_lradc)); + HW_LRADC_DELAYn_CLR(trigger, + BF_LRADC_DELAYn_TRIGGER_DELAYS(delay_triggers)); +} +EXPORT_SYMBOL(hw_lradc_clear_delay_trigger); + +void hw_lradc_set_delay_trigger_kick(int trigger, int value) +{ + if (value) + HW_LRADC_DELAYn_SET(trigger, BM_LRADC_DELAYn_KICK); + else + HW_LRADC_DELAYn_CLR(trigger, BM_LRADC_DELAYn_KICK); +} +EXPORT_SYMBOL(hw_lradc_set_delay_trigger_kick); + +u32 hw_lradc_vddio(void) +{ + /* Clear the Soft Reset and Clock Gate for normal operation */ + HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST | BM_LRADC_CTRL0_CLKGATE); + + /* + * Clear the divide by two for channel 6 since + * it has a HW divide-by-two built in. + */ + HW_LRADC_CTRL2_CLR(BF_LRADC_CTRL2_DIVIDE_BY_TWO(1<<VDDIO_VOLTAGE_CH)); + + /* Clear the accumulator & NUM_SAMPLES */ + HW_LRADC_CHn_CLR(VDDIO_VOLTAGE_CH, 0xFFFFFFFF); + + /* Clear the interrupt flag */ + HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC6_IRQ); + + /* + * Get VddIO; this is the max scale value for the button resistor + * ladder. + * schedule ch 6: + */ + HW_LRADC_CTRL0_SET(BF_LRADC_CTRL0_SCHEDULE(1<<VDDIO_VOLTAGE_CH)); + + /* wait for completion */ + while ((HW_LRADC_CTRL1_RD() & BM_LRADC_CTRL1_LRADC6_IRQ) != + BM_LRADC_CTRL1_LRADC6_IRQ) + cpu_relax(); + + /* Clear the interrupt flag */ + HW_LRADC_CTRL1_CLR(BM_LRADC_CTRL1_LRADC6_IRQ); + + /* read ch 6 value. */ + return HW_LRADC_CHn_RD(6) & BM_LRADC_CHn_VALUE; +} +EXPORT_SYMBOL(hw_lradc_vddio); + +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(REGS_LRADC_BASE + (i << 4)); + + if (do_gate) + HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_CLKGATE); + return 0; +} + +static int hw_lradc_resume(struct sys_device *dev) +{ + int i; + + if (do_gate) { + HW_LRADC_CTRL0_SET(BM_LRADC_CTRL0_SFTRST); + udelay(10); + HW_LRADC_CTRL0_CLR(BM_LRADC_CTRL0_SFTRST | + BM_LRADC_CTRL0_CLKGATE); + } + for (i = 0; i < ARRAY_SIZE(lradc_registers); i++) + __raw_writel(lradc_registers[i], REGS_LRADC_BASE + (i << 4)); + return 0; +} + +static struct sysdev_class stmp3xxx_lradc_sysclass = { + .name = "stmp3xxx-lradc", +#ifdef CONFIG_PM + .suspend = hw_lradc_suspend, + .resume = hw_lradc_resume, +#endif +}; + +static struct sys_device stmp3xxx_lradc_device = { + .id = -1, + .cls = &stmp3xxx_lradc_sysclass, +}; + +static int __initdata 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 __init hw_lradc_init(void) +{ + hw_lradc_reinit(0, lradc_freq); + sysdev_class_register(&stmp3xxx_lradc_sysclass); + sysdev_register(&stmp3xxx_lradc_device); + return 0; +} +subsys_initcall(hw_lradc_init); |