[PATCH] mx28: added LRADC and touchscreen support

Lothar Waßmann LW at KARO-electronics.de
Thu Nov 24 08:56:36 EST 2011


Peter Rusko writes:
> This patch is based on the Freescale SDK 2010.08. It supports the on-chip
> Low Resolution ADC and a capacitive touchscreen connected to it.
> 
> Tested on Ka-Ro TX28 module
> 
> Signed-off-by: Peter Rusko <rusko.peter at prolan.hu>
> ---
>  arch/arm/mach-mxs/devices-mx28.h                |    6 +
>  arch/arm/mach-mxs/devices/Kconfig               |    3 +
>  arch/arm/mach-mxs/devices/Makefile              |    1 +
>  arch/arm/mach-mxs/devices/platform-mxs-lradc.c  |   25 +
>  arch/arm/mach-mxs/devices/platform-mxs-ts.c     |   34 +
>  arch/arm/mach-mxs/include/mach/devices-common.h |   22 +
>  arch/arm/mach-mxs/include/mach/lradc.h          |   61 ++
>  arch/arm/mach-mxs/include/mach/mx28.h           |    4 +-
>  arch/arm/mach-mxs/include/mach/regs-lradc.h     |  772 +++++++++++++++++++++++
>  drivers/input/touchscreen/Kconfig               |   11 +
>  drivers/input/touchscreen/Makefile              |    1 +
>  drivers/input/touchscreen/mxs_ts.c              |  470 ++++++++++++++
>  drivers/misc/Kconfig                            |    3 +
>  drivers/misc/Makefile                           |    1 +
>  drivers/misc/mxs_lradc.c                        |  381 +++++++++++
>  15 files changed, 1793 insertions(+), 2 deletions(-)
>  create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-lradc.c
>  create mode 100644 arch/arm/mach-mxs/devices/platform-mxs-ts.c
>  create mode 100644 arch/arm/mach-mxs/include/mach/lradc.h
>  create mode 100644 arch/arm/mach-mxs/include/mach/regs-lradc.h
>  create mode 100644 drivers/input/touchscreen/mxs_ts.c
>  create mode 100644 drivers/misc/mxs_lradc.c
> 
> diff --git a/arch/arm/mach-mxs/devices-mx28.h b/arch/arm/mach-mxs/devices-mx28.h
> index c888710..b0efa25 100644
> --- a/arch/arm/mach-mxs/devices-mx28.h
> +++ b/arch/arm/mach-mxs/devices-mx28.h
> @@ -46,6 +46,12 @@ extern const struct mxs_mxs_mmc_data mx28_mxs_mmc_data[] __initconst;
>  struct platform_device *__init mx28_add_mxsfb(
>  		const struct mxsfb_platform_data *pdata);
>  
> +struct platform_device * __init mx28_add_lradc(
> +		const struct mxs_lradc_plat_data *pdata);
> +
> +struct platform_device *__init mx28_add_ts(
> +		const struct mxs_touchscreen_plat_data *pdata);
> +
>  extern const struct mxs_saif_data mx28_saif_data[] __initconst;
>  #define mx28_add_saif(id)              mxs_add_saif(&mx28_saif_data[id])
>  
> diff --git a/arch/arm/mach-mxs/devices/Kconfig b/arch/arm/mach-mxs/devices/Kconfig
> index 18b6bf5..58d1c1e 100644
> --- a/arch/arm/mach-mxs/devices/Kconfig
> +++ b/arch/arm/mach-mxs/devices/Kconfig
> @@ -15,6 +15,9 @@ config MXS_HAVE_PLATFORM_FLEXCAN
>  config MXS_HAVE_PLATFORM_MXS_I2C
>  	bool
>  
> +config MXS_HAVE_PLATFORM_MXS_LRADC
> +	bool
> +
>  config MXS_HAVE_PLATFORM_MXS_MMC
>  	bool
>  
> diff --git a/arch/arm/mach-mxs/devices/Makefile b/arch/arm/mach-mxs/devices/Makefile
> index f52e3e5..9ba66ed 100644
> --- a/arch/arm/mach-mxs/devices/Makefile
> +++ b/arch/arm/mach-mxs/devices/Makefile
> @@ -4,6 +4,7 @@ obj-y += platform-dma.o
>  obj-$(CONFIG_MXS_HAVE_PLATFORM_FEC) += platform-fec.o
>  obj-$(CONFIG_MXS_HAVE_PLATFORM_FLEXCAN) += platform-flexcan.o
>  obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_I2C) += platform-mxs-i2c.o
> +obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_LRADC) += platform-mxs-lradc.o platform-mxs-ts.o
>  obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_MMC) += platform-mxs-mmc.o
>  obj-$(CONFIG_MXS_HAVE_PLATFORM_MXS_PWM) += platform-mxs-pwm.o
>  obj-y += platform-gpio-mxs.o
> diff --git a/arch/arm/mach-mxs/devices/platform-mxs-lradc.c b/arch/arm/mach-mxs/devices/platform-mxs-lradc.c
> new file mode 100644
> index 0000000..d1af2ec
> --- /dev/null
> +++ b/arch/arm/mach-mxs/devices/platform-mxs-lradc.c
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + */
> +
> +#include <mach/devices-common.h>
> +#include <mach/mx28.h>
> +
> +struct platform_device * __init mx28_add_lradc(
> +		const struct mxs_lradc_plat_data *pdata)
> +{
> +	struct resource res[] = {
> +		{
> +			.start = MX28_LRADC_BASE_ADDR,
> +			.end = MX28_LRADC_BASE_ADDR + SZ_16K - 1,
> +			.flags = IORESOURCE_MEM,
> +		},
> +	};
> +
> +	return mxs_add_platform_device("mxs-lradc", 0,
> +			res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
> +}
> diff --git a/arch/arm/mach-mxs/devices/platform-mxs-ts.c b/arch/arm/mach-mxs/devices/platform-mxs-ts.c
> new file mode 100644
> index 0000000..05343af
> --- /dev/null
> +++ b/arch/arm/mach-mxs/devices/platform-mxs-ts.c
> @@ -0,0 +1,34 @@
> +/*
> + * Copyright 2011 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it under
> + * the terms of the GNU General Public License version 2 as published by the
> + * Free Software Foundation.
> + */
> +
> +#include <mach/devices-common.h>
> +#include <mach/mx28.h>
> +
> +struct platform_device *__init mx28_add_ts(
> +		const struct mxs_touchscreen_plat_data *pdata)
> +{
> +	struct resource res[] = {
> +		{
> +		 .flags = IORESOURCE_MEM,
> +		 .start = MX28_LRADC_BASE_ADDR,
> +		 .end   = MX28_LRADC_BASE_ADDR + 0x2000 - 1,
>
'MX28_LRADC_BASE_ADDR + SZ_4K - 1' would be more than sufficient.
> +		}, {
> +		 .flags = IORESOURCE_IRQ,
> +		 .start = MX28_INT_LRADC_TOUCH,
> +		 .end   = MX28_INT_LRADC_TOUCH,
> +		}, {
> +		 .flags = IORESOURCE_IRQ,
> +		 .start = MX28_INT_LRADC_CH5,
> +		 .end   = MX28_INT_LRADC_CH5,
>
strange indentation.

> +
> +	return mxs_add_platform_device("mxs-ts", 0,
> +			res, ARRAY_SIZE(res), pdata, sizeof(*pdata));
> +
> +}
> diff --git a/arch/arm/mach-mxs/include/mach/devices-common.h b/arch/arm/mach-mxs/include/mach/devices-common.h
> index a8080f4..e86b193 100644
> --- a/arch/arm/mach-mxs/include/mach/devices-common.h
> +++ b/arch/arm/mach-mxs/include/mach/devices-common.h
> @@ -76,6 +76,12 @@ struct mxs_mxs_i2c_data {
>  struct platform_device * __init mxs_add_mxs_i2c(
>  		const struct mxs_mxs_i2c_data *data);
>  
> +/* lradc */
> +struct mxs_lradc_plat_data {
> +	unsigned int vddio_voltage;
> +	unsigned int battery_voltage;
> +};
> +
>  /* mmc */
>  #include <mach/mmc.h>
>  struct mxs_mxs_mmc_data {
> @@ -104,3 +110,19 @@ struct mxs_saif_data {
>  
>  struct platform_device *__init mxs_add_saif(
>  		const struct mxs_saif_data *data);
> +
> +/* touchscreen */
> +struct mxs_touchscreen_plat_data {
> +	u8 x_plus_chan;
> +	u8 x_minus_chan;
> +	u8 y_plus_chan;
> +	u8 y_minus_chan;
> +	unsigned int x_plus_val;
> +	unsigned int x_minus_val;
> +	unsigned int y_plus_val;
> +	unsigned int y_minus_val;
> +	unsigned int x_plus_mask;
> +	unsigned int x_minus_mask;
> +	unsigned int y_plus_mask;
> +	unsigned int y_minus_mask;
> +};
> diff --git a/arch/arm/mach-mxs/include/mach/lradc.h b/arch/arm/mach-mxs/include/mach/lradc.h
> new file mode 100644
> index 0000000..c2c0a7d
> --- /dev/null
> +++ b/arch/arm/mach-mxs/include/mach/lradc.h
> @@ -0,0 +1,61 @@
> +/*
> + * Freescale STMP37XX/STMP378X LRADC helper interface
> + *
> + * Embedded Alley Solutions, Inc <source at 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
> + */
> +#ifndef __ASM_PLAT_LRADC_H
> +#define __ASM_PLAT_LRADC_H
> +
> +int hw_lradc_use_channel(int);
> +int hw_lradc_unuse_channel(int);
> +extern u32 hw_lradc_vddio(void);
> +void hw_lradc_set_delay_trigger_kick(int trigger, int value);
> +void hw_lradc_configure_channel(int channel, int enable_div2,
> +		int enable_acc, int samples);
> +int hw_lradc_present(int channel);
> +int hw_lradc_init_ladder(int channel, int trigger, unsigned sampling);
> +int hw_lradc_stop_ladder(int channel, int trigger);
> +void hw_lradc_set_delay_trigger(int trigger, u32 trigger_lradc,
> +		u32 delay_triggers, u32 loops, u32 delays);
> +void hw_lradc_clear_delay_trigger(int trigger, u32 trigger_lradc,
> +		u32 delay_triggers);
> +
> +
> +#define LRADC_CH0		0
> +#define LRADC_CH1		1
> +#define LRADC_CH2		2
> +#define LRADC_CH3		3
> +#define LRADC_CH4		4
> +#define LRADC_CH5		5
> +#define LRADC_CH6		6
> +#define LRADC_CH7		7
> +#define LRADC_TOUCH_X_PLUS	LRADC_CH2
> +#define LRADC_TOUCH_Y_PLUS	LRADC_CH3
> +#define LRADC_TOUCH_X_MINUS	LRADC_CH4
> +#define LRADC_TOUCH_Y_MINUS	LRADC_CH5
> +#define VDDIO_VOLTAGE_CH	LRADC_CH6
> +#define BATTERY_VOLTAGE_CH	LRADC_CH7
> +
> +#define LRADC_CLOCK_6MHZ	0
> +#define LRADC_CLOCK_4MHZ	1
> +#define LRADC_CLOCK_3MHZ	2
> +#define LRADC_CLOCK_2MHZ	3
> +
> +#define LRADC_DELAY_TRIGGER_BUTTON	0
> +#define LRADC_DELAY_TRIGGER_BATTERY	1
> +#define LRADC_DELAY_TRIGGER_TOUCHSCREEN	2
> +#define LRADC_DELAY_TRIGGER_DIE		3
> +
> +#endif /* __ASM_PLAT_LRADC_H */
> diff --git a/arch/arm/mach-mxs/include/mach/mx28.h b/arch/arm/mach-mxs/include/mach/mx28.h
> index 75d8611..30c7990 100644
> --- a/arch/arm/mach-mxs/include/mach/mx28.h
> +++ b/arch/arm/mach-mxs/include/mach/mx28.h
> @@ -104,8 +104,8 @@
>  #define MX28_INT_CAN1			9
>  #define MX28_INT_LRADC_TOUCH		10
>  #define MX28_INT_HSADC			13
> -#define MX28_INT_IRADC_THRESH0		14
> -#define MX28_INT_IRADC_THRESH1		15
> +#define MX28_INT_LRADC_THRESH0		14
> +#define MX28_INT_LRADC_THRESH1		15
>  #define MX28_INT_LRADC_CH0		16
>  #define MX28_INT_LRADC_CH1		17
>  #define MX28_INT_LRADC_CH2		18
> diff --git a/arch/arm/mach-mxs/include/mach/regs-lradc.h b/arch/arm/mach-mxs/include/mach/regs-lradc.h
> new file mode 100644
> index 0000000..d7906b9
> --- /dev/null
> +++ b/arch/arm/mach-mxs/include/mach/regs-lradc.h
> @@ -0,0 +1,772 @@
> +/*
> + * Freescale LRADC Register Definitions
> + *
> + * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
> + *
> + * This file is created by xml file. Don't Edit it.
> + *
> + * Xml Revision: 1.50
> + * Template revision: 26195
> + */
> +
> +#ifndef __ARCH_ARM___LRADC_H
> +#define __ARCH_ARM___LRADC_H
> +
> +
> +#define HW_LRADC_CTRL0	(0x00000000)
> +#define HW_LRADC_CTRL0_SET	(0x00000004)
> +#define HW_LRADC_CTRL0_CLR	(0x00000008)
> +#define HW_LRADC_CTRL0_TOG	(0x0000000c)
> +
useless parens.
more of these follow...
[...]
> + *  multi-register-define name HW_LRADC_DELAYn
> + *              base 0x000000D0
> + *              count 4
> + *              offset 0x10
> + */
> +#define HW_LRADC_DELAYn(n)	(0x000000d0 + (n) * 0x10)
> +#define HW_LRADC_DELAYn_SET(n)	(0x000000d4 + (n) * 0x10)
> +#define HW_LRADC_DELAYn_CLR(n)	(0x000000d8 + (n) * 0x10)
> +#define HW_LRADC_DELAYn_TOG(n)	(0x000000dc + (n) * 0x10)
>
You should use MXS_SET_ADDR, MXS_CLR_ADDR, MXS_TOG_ADDR here.
Thus:
|#define HW_LRADC_DELAYn_SET(n)		(HW_LRADC_DELAYn(n) + MXS_SET_ADDR)
|#define HW_LRADC_DELAYn_CLR(n)		(HW_LRADC_DELAYn(n) + MXS_CLR_ADDR)
|#define HW_LRADC_DELAYn_TOG(n)		(HW_LRADC_DELAYn(n) + MXS_TOG_ADDR)
or use the __mxs_setl(), __mxs_clrl(), __mxs_togl() functions instead.

> diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
> index 3488ffe..36bd3cf 100644
> --- a/drivers/input/touchscreen/Kconfig
> +++ b/drivers/input/touchscreen/Kconfig
> @@ -339,6 +339,17 @@ config TOUCHSCREEN_HP7XX
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called jornada720_ts.
>  
> +config TOUCHSCREEN_MXS
> +	tristate "MXS LRADC-based touchscreen"
> +	depends on MXS_LRADC
> +	select SERIO
> +	help
> +	  Say Y here if you want to enable touchscreen, connected to MXS
> +	  Low-Resoulion ADC.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called mxs_ts.
> +
>  config TOUCHSCREEN_HTCPEN
>  	tristate "HTC Shift X9500 touchscreen"
>  	depends on ISA
> diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
> index f957676..6c3728c 100644
> --- a/drivers/input/touchscreen/Makefile
> +++ b/drivers/input/touchscreen/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH)	+= mtouch.o
>  obj-$(CONFIG_TOUCHSCREEN_MK712)		+= mk712.o
>  obj-$(CONFIG_TOUCHSCREEN_HP600)		+= hp680_ts_input.o
>  obj-$(CONFIG_TOUCHSCREEN_HP7XX)		+= jornada720_ts.o
> +obj-$(CONFIG_TOUCHSCREEN_MXS)		+= mxs_ts.o
>  obj-$(CONFIG_TOUCHSCREEN_HTCPEN)	+= htcpen.o
>  obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE)	+= usbtouchscreen.o
>  obj-$(CONFIG_TOUCHSCREEN_PCAP)		+= pcap_ts.o
> diff --git a/drivers/input/touchscreen/mxs_ts.c b/drivers/input/touchscreen/mxs_ts.c
> new file mode 100644
> index 0000000..ad05c3f
> --- /dev/null
> +++ b/drivers/input/touchscreen/mxs_ts.c
> @@ -0,0 +1,470 @@
> +/*
> + * Freesclae MXS Touchscreen driver
> + *
> + * Author: Vitaly Wool <vital at 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/slab.h>
> +#include <linux/kernel.h>
> +#include <linux/device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/input.h>
> +#include <linux/interrupt.h>
> +#include <linux/fsl_devices.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +
> +#include <mach/hardware.h>
> +#include <mach/lradc.h>
> +#include <mach/devices-common.h>
> +#include <mach/regs-lradc.h>
> +#include <mach/mxs.h>
> +
> +#define TOUCH_DEBOUNCE_TOLERANCE	100
> +
> +struct mxs_ts_info {
> +	int touch_irq;
> +	int device_irq;
> +	unsigned int base;
>
void __iomem *base;

> +	u8 x_plus_chan;
> +	u8 x_minus_chan;
> +	u8 y_plus_chan;
> +	u8 y_minus_chan;
> +
> +	unsigned int x_plus_val;
> +	unsigned int x_minus_val;
> +	unsigned int y_plus_val;
> +	unsigned int y_minus_val;
> +	unsigned int x_plus_mask;
> +	unsigned int x_minus_mask;
> +	unsigned int y_plus_mask;
> +	unsigned int y_minus_mask;
> +
> +	struct input_dev *idev;
> +	enum {
> +		TS_STATE_DISABLED,
> +		TS_STATE_TOUCH_DETECT,
> +		TS_STATE_TOUCH_VERIFY,
> +		TS_STATE_X_PLANE,
> +		TS_STATE_Y_PLANE,
> +	} state;
> +	u16 x;
> +	u16 y;
> +	int sample_count;
> +};
> +
> +static inline void enter_state_touch_detect(struct mxs_ts_info *info)
> +{
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
>
writel() instead of __raw_writel()?
Also: writing a '0' to the regular register address would be clearer
here:
	 writel(0, info->base + HW_LRADC_CHn(info->x_plus_chan));

> +
> +	__raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
> +		     info->base + HW_LRADC_CTRL1_CLR);
> +	__raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
> +		     info->base + HW_LRADC_CTRL1_CLR);
> +	/*
> +	 * turn off the yplus and yminus pullup and pulldown, and turn off touch
> +	 * detect (enables yminus, and xplus through a resistor.On a press,
> +	 * xplus is pulled down)
> +	 */
> +	__raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
> +		     info->base + HW_LRADC_CTRL0_SET);
> +	hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
> +	info->state = TS_STATE_TOUCH_DETECT;
> +	info->sample_count = 0;
> +}
> +
> +static inline void enter_state_disabled(struct mxs_ts_info *info)
> +{
> +	__raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
> +		     info->base + HW_LRADC_CTRL0_CLR);
> +	hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
> +	info->state = TS_STATE_DISABLED;
> +	info->sample_count = 0;
> +}
> +
> +
> +static inline void enter_state_x_plane(struct mxs_ts_info *info)
> +{
> +	__raw_writel(info->y_plus_val, info->base + HW_LRADC_CTRL0_SET);
> +	__raw_writel(info->y_minus_val, info->base + HW_LRADC_CTRL0_SET);
> +	__raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
> +		     info->base + HW_LRADC_CTRL0_CLR);
> +	hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +
> +	info->state = TS_STATE_X_PLANE;
> +	info->sample_count = 0;
> +}
> +
> +static inline void enter_state_y_plane(struct mxs_ts_info *info)
> +{
> +	__raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_plus_val, info->base + HW_LRADC_CTRL0_SET);
> +	__raw_writel(info->x_minus_val, info->base + HW_LRADC_CTRL0_SET);
> +	__raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
> +		     info->base + HW_LRADC_CTRL0_CLR);
> +	hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +	info->state = TS_STATE_Y_PLANE;
> +	info->sample_count = 0;
> +}
> +
> +static inline void enter_state_touch_verify(struct mxs_ts_info *info)
> +{
> +	__raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
> +	__raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
> +		     info->base + HW_LRADC_CTRL0_SET);
> +	info->state = TS_STATE_TOUCH_VERIFY;
> +	hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +	info->sample_count = 0;
> +}
> +
> +static void process_lradc(struct mxs_ts_info *info, u16 x, u16 y,
> +			int pressure)
> +{
> +	switch (info->state) {
> +	case TS_STATE_X_PLANE:
> +		pr_debug("%s: x plane state, sample_count %d\n", __func__,
> +				info->sample_count);
> +		if (info->sample_count < 2) {
> +			info->x = x;
> +			info->sample_count++;
> +		} else {
> +			if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
> +				info->sample_count = 1;
> +			else {
> +				u16 x_c = info->x * (info->sample_count - 1);
> +				info->x = (x_c + x) / info->sample_count;
> +				info->sample_count++;
> +			}
> +		}
> +		if (info->sample_count > 4)
> +			enter_state_y_plane(info);
> +		else
> +			hw_lradc_set_delay_trigger_kick(
> +					LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +		break;
> +
> +	case TS_STATE_Y_PLANE:
> +		pr_debug("%s: y plane state, sample_count %d\n", __func__,
> +				info->sample_count);
> +		if (info->sample_count < 2) {
> +			info->y = y;
> +			info->sample_count++;
> +		} else {
> +			if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
> +				info->sample_count = 1;
> +			else {
> +				u16 y_c = info->y * (info->sample_count - 1);
> +				info->y = (y_c + y) / info->sample_count;
> +				info->sample_count++;
> +			}
> +		}
> +		if (info->sample_count > 4)
> +			enter_state_touch_verify(info);
> +		else
> +			hw_lradc_set_delay_trigger_kick(
> +					LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +		break;
> +
> +	case TS_STATE_TOUCH_VERIFY:
> +		pr_debug("%s: touch verify state, sample_count %d\n", __func__,
> +				info->sample_count);
> +		pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
> +		input_report_abs(info->idev, ABS_X, info->x);
> +		input_report_abs(info->idev, ABS_Y, info->y);
> +		input_report_abs(info->idev, ABS_PRESSURE, pressure);
> +		input_sync(info->idev);
> +		/* fall through */
> +	case TS_STATE_TOUCH_DETECT:
> +		pr_debug("%s: touch detect state, sample_count %d\n", __func__,
> +				info->sample_count);
> +		if (pressure) {
> +			input_report_abs(info->idev, ABS_PRESSURE, pressure);
> +			enter_state_x_plane(info);
> +			hw_lradc_set_delay_trigger_kick(
> +					LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
> +		} else
> +			enter_state_touch_detect(info);
> +		break;
> +
> +	default:
> +		printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
> +				info->state);
> +	}
> +}
> +
> +static irqreturn_t ts_handler(int irq, void *dev_id)
> +{
> +	struct mxs_ts_info *info = dev_id;
> +	u16 x_plus, y_plus;
> +	int pressure = 0;
> +
> +	if (irq == info->touch_irq)
> +		__raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
> +			     info->base + HW_LRADC_CTRL1_CLR);
> +	else if (irq == info->device_irq)
> +		__raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
> +			     info->base + HW_LRADC_CTRL1_CLR);
> +
> +	/* get x, y values */
> +	x_plus = __raw_readl(info->base + HW_LRADC_CHn(info->x_plus_chan)) &
> +		BM_LRADC_CHn_VALUE;
> +	y_plus = __raw_readl(info->base + HW_LRADC_CHn(info->y_plus_chan)) &
> +		BM_LRADC_CHn_VALUE;
> +
> +	/* pressed? */
> +	if (__raw_readl(info->base + HW_LRADC_STATUS) &
> +	    BM_LRADC_STATUS_TOUCH_DETECT_RAW)
> +		pressure = 1;
> +
> +	pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
> +			__func__, irq, x_plus, y_plus, pressure);
> +
> +	process_lradc(info, x_plus, y_plus, pressure);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __devinit mxs_ts_probe(struct platform_device *pdev)
> +{
> +	struct input_dev *idev;
> +	struct mxs_ts_info *info;
> +	int ret = 0;
> +	struct resource *res;
> +	struct mxs_touchscreen_plat_data *plat_data;
> +
> +	plat_data = (struct mxs_touchscreen_plat_data *)pdev->dev.platform_data;
>
useless type cast.

> +	if (plat_data == NULL)
> +		return -ENODEV;
> +
> +	idev = input_allocate_device();
> +	if (idev == NULL)
> +		return -ENOMEM;
> +
> +	info = kzalloc(sizeof(struct mxs_ts_info), GFP_KERNEL);
> +	if (info == NULL) {
> +		ret = -ENOMEM;
> +		goto out_nomem_info;
> +	}
> +
> +	idev->name = "MXS touchscreen";
> +	idev->evbit[0] = BIT(EV_ABS);
> +	input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
> +	input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
> +	input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
> +
> +	ret = input_register_device(idev);
> +	if (ret)
> +		goto out_nomem;
> +
> +	info->idev = idev;
> +	info->x_plus_chan = plat_data->x_plus_chan;
> +	info->x_minus_chan = plat_data->x_minus_chan;
> +	info->y_plus_chan = plat_data->y_plus_chan;
> +	info->y_minus_chan = plat_data->y_minus_chan;
> +	info->x_plus_val = plat_data->x_plus_val;
> +	info->x_minus_val = plat_data->x_minus_val;
> +	info->y_plus_val = plat_data->y_plus_val;
> +	info->y_minus_val = plat_data->y_minus_val;
> +	info->x_plus_mask = plat_data->x_plus_mask;
> +	info->x_minus_mask = plat_data->x_minus_mask;
> +	info->y_plus_mask = plat_data->y_plus_mask;
> +	info->y_minus_mask = plat_data->y_minus_mask;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		printk(KERN_ERR "%s: couldn't get MEM resource\n", __func__);
> +		ret = -ENODEV;
> +		goto out_nodev;
> +	}
> +	info->base = (unsigned int)MXS_IO_ADDRESS(res->start);
>
ioremap() please.

> +	ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
>
IRQF_DISABLED is a NOP nowadays and will be removed sooner or later.

> +	/* Clear the accumulator & NUM_SAMPLES for the channels */
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
> +	__raw_writel(0xFFFFFFFF,
> +		     info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
> +
see above.

> +static int __devexit mxs_ts_remove(struct platform_device *pdev)
> +{
> +	struct mxs_ts_info *info = platform_get_drvdata(pdev);
> +
> +	platform_set_drvdata(pdev, NULL);
> +
> +	hw_lradc_unuse_channel(info->x_plus_chan);
> +	hw_lradc_unuse_channel(info->x_minus_chan);
> +	hw_lradc_unuse_channel(info->y_plus_chan);
> +	hw_lradc_unuse_channel(info->y_minus_chan);
> +
> +	__raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << info->y_minus_chan,
> +		     info->base + HW_LRADC_CTRL1_CLR);
> +	__raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
> +		     info->base + HW_LRADC_CTRL1_CLR);
> +
> +	free_irq(info->device_irq, info);
> +	free_irq(info->touch_irq, info);
> +	input_free_device(info->idev);
> +
> +	enter_state_disabled(info);
> +	kfree(info->idev);
> +	kfree(info);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_PM
> +static int mxs_ts_suspend(struct platform_device *pdev,
> +				pm_message_t state)
> +{
> +	struct mxs_ts_info *info = platform_get_drvdata(pdev);
> +
> +	if (!device_may_wakeup(&pdev->dev)) {
> +		hw_lradc_unuse_channel(info->x_plus_chan);
> +		hw_lradc_unuse_channel(info->x_minus_chan);
> +		hw_lradc_unuse_channel(info->y_plus_chan);
> +		hw_lradc_unuse_channel(info->y_minus_chan);
> +	}
> +	return 0;
> +}
> +
> +static int mxs_ts_resume(struct platform_device *pdev)
> +{
> +	struct mxs_ts_info *info = platform_get_drvdata(pdev);
> +
> +	if (!device_may_wakeup(&pdev->dev)) {
> +		hw_lradc_use_channel(info->x_plus_chan);
> +		hw_lradc_use_channel(info->x_minus_chan);
> +		hw_lradc_use_channel(info->y_plus_chan);
> +		hw_lradc_use_channel(info->y_minus_chan);
> +	}
> +	return 0;
> +}
> +#endif
> +
> +static struct platform_driver mxs_ts_driver = {
> +	.probe		= mxs_ts_probe,
> +	.remove		= __devexit_p(mxs_ts_remove),
> +#ifdef CONFIG_PM
> +	.suspend	= mxs_ts_suspend,
> +	.resume		= mxs_ts_resume,
> +#endif
> +	.driver		= {
> +		.name	= "mxs-ts",
> +	},
> +};
> +
> +static int __init mxs_ts_init(void)
> +{
> +	return platform_driver_register(&mxs_ts_driver);
> +}
> +
> +static void __exit mxs_ts_exit(void)
> +{
> +	platform_driver_unregister(&mxs_ts_driver);
> +}
> +
> +module_init(mxs_ts_init);
> +module_exit(mxs_ts_exit);
> +
> +MODULE_DESCRIPTION("MXS touchscreen driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
> index d593878..ab89b1b 100644
> --- a/drivers/misc/Kconfig
> +++ b/drivers/misc/Kconfig
> @@ -499,6 +499,9 @@ config USB_SWITCH_FSA9480
>  	  stereo and mono audio, video, microphone and UART data to use
>  	  a common connector port.
>  
> +config MXS_LRADC
> +	tristate "ARM MXS LRADC Support"
> +
>  source "drivers/misc/c2port/Kconfig"
>  source "drivers/misc/eeprom/Kconfig"
>  source "drivers/misc/cb710/Kconfig"
> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
> index b26495a..0ce0017 100644
> --- a/drivers/misc/Makefile
> +++ b/drivers/misc/Makefile
> @@ -47,4 +47,5 @@ obj-$(CONFIG_AB8500_PWM)	+= ab8500-pwm.o
>  obj-y				+= lis3lv02d/
>  obj-y				+= carma/
>  obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
> +obj-$(CONFIG_MXS_LRADC)		+= mxs_lradc.o
>  obj-$(CONFIG_ALTERA_STAPL)	+=altera-stapl/
> diff --git a/drivers/misc/mxs_lradc.c b/drivers/misc/mxs_lradc.c
> new file mode 100644
> index 0000000..cc32fac
> --- /dev/null
> +++ b/drivers/misc/mxs_lradc.c
> @@ -0,0 +1,381 @@
> +/*
> + * Freescale STMP37XX/STMP378X LRADC helper routines
> + *
> + * Embedded Alley Solutions, Inc <source at 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/devices-common.h>
> +#include <mach/mxs.h>
> +#include <mach/regs-lradc.h>
> +#include <mach/lradc.h>
> +
> +struct lradc_device {
> +	struct sys_device sys;
> +	unsigned int base;
>
void __iomem *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]++;
>
Missing locking to make the increment atomic.

> +	return 0;
> +}
> +EXPORT_SYMBOL(hw_lradc_use_channel);
> +
> +int hw_lradc_unuse_channel(int channel)
> +{
> +	if (channel < 0 || channel > 7)
> +		return -EINVAL;
> +	channels[channel]--;
>
Locking?

> +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));
>
see above.

> +	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));
>
>From my point it is more readable to have the operator at the end of
the preceding line instead of at the beginning of a continuation line
like:
	return __raw_readl(mxs_lradc.base + HW_LRADC_STATUS) &
		(1 << (16 + channel));
Also one of the following would be safer wrt the processing of the
result:
	return (__raw_readl(mxs_lradc.base + HW_LRADC_STATUS) >>
		(16 + channel)) & 1;
or:
	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));
>
see above.

> +	/* 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));
>
see above.

> +
> +	/* 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();
>
No endless loops depending solely on a HW bit please!

> +	/* 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];
                              ^^^^
You are missing to save/restore the HW_LRADC_THRESHOLD1 register at
offset 0x160.

> +static int do_gate;
> +
> +static int hw_lradc_suspend(struct 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 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",
> +};
> +
> +static int lradc_freq = LRADC_CLOCK_6MHZ;
> +
> +static int __init lradc_freq_setup(char *str)
> +{
> +	long freq;
> +
> +	if (kstrtol(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);
>
How do you set lradc_freq when the driver is compiled as a module?
module_param() would work for the driver compiled as a module as well
as built-in.

> +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);
>
useless type cast.

> +	if (plat_data == NULL)
> +		return -EFAULT;
>
-EFAULT indicates that a memory access from kernel to userspace memory
failed. -ENODEV would be more appropriate here.

> +	mxs_lradc.base = (unsigned int)MXS_IO_ADDRESS(res->start);
>
ioremap()

> +	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,
> +#ifdef CONFIG_PM
> +		.suspend = hw_lradc_suspend,
> +		.resume = hw_lradc_resume,
> +#endif
> +	}
> +};
> +
> +static int __init hw_lradc_init(void)
> +{
> +	sysdev_class_register(&mxs_lradc_sysclass);
>
Does it make sense to go ahead and register the driver, if this fails?

> +	platform_driver_register(&mxs_lradc_drv);
> +	return 0;
>
return platform_driver_register(&mxs_lradc_drv);



Lothar Waßmann
-- 
___________________________________________________________

Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996

www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________



More information about the linux-arm-kernel mailing list