diff mbox

[U-Boot] Add support for i.MX6SL EVK board keypad

Message ID 1409520692-30412-2-git-send-email-nitin.garg@freescale.com
State Changes Requested
Delegated to: Stefano Babic
Headers show

Commit Message

Nitin Garg Aug. 31, 2014, 9:31 p.m. UTC
From: Nitin Garg <nitin.garg@freescale.com>

i.MX6sl evk has a keyboard on the board, so add mxc_keyb driver to
support keypad.

Signed-off-by: Nitin Garg <nitin.garg@freescale.com>
---
 drivers/input/Makefile   |    1 +
 drivers/input/mxc_keyb.c |  588 ++++++++++++++++++++++++++++++++++++++++++++++
 include/mxc_keyb.h       |  240 +++++++++++++++++++
 3 files changed, 829 insertions(+)
 create mode 100644 drivers/input/mxc_keyb.c
 create mode 100644 include/mxc_keyb.h

Comments

Otavio Salvador Sept. 1, 2014, 1:11 a.m. UTC | #1
On Sun, Aug 31, 2014 at 6:31 PM,  <nitin.garg@freescale.com> wrote:
> From: Nitin Garg <nitin.garg@freescale.com>
>
> i.MX6sl evk has a keyboard on the board, so add mxc_keyb driver to
> support keypad.
>
> Signed-off-by: Nitin Garg <nitin.garg@freescale.com>

This driver seems to be generic and this can be used with the other
reference designs which has the keyboard, doesn't it? So please rework
the commit log.

Another thing, please add the support for the board so it has a board using it.
Wolfgang Denk Sept. 1, 2014, 5:48 a.m. UTC | #2
Dear nitin.garg@freescale.com,

In message <1409520692-30412-2-git-send-email-nitin.garg@freescale.com> you wrote:

> +static void mxc_kpp_dump_regs()
> +{
> +	unsigned short t1, t2, t3;
> +
> +	t1 = __raw_readw(KPCR);
> +	t2 = __raw_readw(KPSR);
> +	t3 = __raw_readw(KDDR);
> +	/*
> +	KPP_PRINTF("KPCR=0x%04x, KPSR=0x%04x, KDDR=0x%04x\n",
> +		t1, t2, t3);
> +		*/

Please do not add dead code.


> +	/*
> +	 * wmb() linux kernel function which guarantees orderings in write
> +	 * operations
> +	 */
> +	/* wmb(); */

Please remove dead code.  Please fix globally.[

> +	for (col = 0; col < kpp_dev.kpp_cols; col++) {	/* Col */
> +		/* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
> +		reg_val = __raw_readw(KPDR);
> +		reg_val |= 0xff00;
> +		__raw_writew(reg_val, KPDR);

setbits?

Is there any specific reason for performing all these device accesses
using the __raw_*() functions, i. e. without memory barriers?

That looks awfully suspicious to me...

> +		reg_val = __raw_readw(KPCR);
> +		reg_val &= 0x00ff;
> +		__raw_writew(reg_val, KPCR);

clrbits?

> +		udelay(2);

Why exactly is this udelay() needed?  Maybe just because you are
lacking memory barriers, i. e. because you use the wrong I/O
accessors?

> +		reg_val = __raw_readw(KPCR);
> +		reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
> +		__raw_writew(reg_val, KPCR);

setbits() ... etc.  Please fix globally.

> +		/* Delay added to avoid propagating the 0 from column to row
> +		 * when scanning. */

Incorrect multiline comment style.  Is this delay really needed when
using non-__raw_ I/O accessors?

> +		udelay(5);
> +

> +					KPress = 1;
> +					kpp_dev.iKeyState = KStateUp;

Please fix variable names globally - they are all lower case.

Please fix everywhere, including the header file, sturct names, etc.

> +
> +					KPP_PRINTF("Press   (%d, %d) scan=%d "
> +						 "Kpress=%d\n",
> +						 row, col, scancode, KPress);

Any specific reason for not using standard debug() facility?


> +	/*
> +	* This switch case statement is the
> +	* implementation of state machine of debounc
> +	* logic for key press/release.
> +	* The explaination of state machine is as
> +	* follows:

There is no switch anywhere in the rest of this function?


> +	/* Enable number of rows in keypad (KPCR[7:0])
> +	 * Configure keypad columns as open-drain (KPCR[15:8])
> +	 *
> +	 * Configure the rows/cols in KPP
> +	 * LSB nibble in KPP is for 8 rows
> +	 * MSB nibble in KPP is for 8 cols
> +	 */

Incorrect multiline comment style. Please fix globally.


> +	press_scancode   = (short **)malloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]));
> +	release_scancode = (short **)malloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]));

Line too long.  Please fix globally.

Also, make sure to run your patches through checkpatch and fix such
warnings!!


> +/*
> + * _reg_KPP_KPCR   _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR
> + * Keypad Control Register Address
> + */
> +#define KPCR    (KPP_BASE_ADDR + 0x00)
> +
> +/*
> + * Keypad Status Register Address
> + */
> +#define KPSR    (KPP_BASE_ADDR + 0x02)
> +
> +/*
> + * Keypad Data Direction Address
> + */
> +#define KDDR    (KPP_BASE_ADDR + 0x04)
> +
> +/*
> + * Keypad Data Register
> + */
> +#define KPDR    (KPP_BASE_ADDR + 0x06)

Please use a C struct to describe the register map.  We deprecate the
notation of base register + offset.


Also consider to providing the comments at the end of the line - this
would reduce your line count to 20% here and make the code much better
to read.


> +enum KeyEvent {
> +	KDepress,
> +	KRelease
> +};
> +
> +/*!
> + * This enum represents the keypad state machine to maintain debounce logic
> + * for key press/release.
> + */
> +enum KeyState {
> +
> +	/*!
> +	 * Key press state.
> +	 */
> +	KStateUp,
> +
> +	/*!
> +	 * Key press debounce state.
> +	 */
> +	KStateFirstDown,
> +
> +	/*!
> +	 * Key release state.
> +	 */
> +	KStateDown,
> +
> +	/*!
> +	 * Key release debounce state.
> +	 */
> +	KStateFirstUp
> +};

See before - please use all lower case variable names.  We do not
allow CamelCase in U-Boot.

Please fix globally!

Thanks.

Best regards,

Wolfgang Denk
diff mbox

Patch

diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index a8e9be2..8b1b86c 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -14,3 +14,4 @@  obj-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o
 endif
 obj-y += input.o
 obj-$(CONFIG_OF_CONTROL) += key_matrix.o
+obj-$(CONFIG_MXC_KPD) += mxc_keyb.o
diff --git a/drivers/input/mxc_keyb.c b/drivers/input/mxc_keyb.c
new file mode 100644
index 0000000..cc39208
--- /dev/null
+++ b/drivers/input/mxc_keyb.c
@@ -0,0 +1,588 @@ 
+/*
+ * Copyright (C) 2009-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*!
+ * @file mxc_keyb.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC keypad port.
+ *
+ * The keypad driver is designed as a standard Input driver which interacts
+ * with low level keypad port hardware. Upon opening, the Keypad driver
+ * initializes the keypad port. When the keypad interrupt happens the driver
+ * calles keypad polling timer and scans the keypad matrix for key
+ * press/release. If all key press/release happened it comes out of timer and
+ * waits for key press interrupt. The scancode for key press and release events
+ * are passed to Input subsytem.
+ *
+ * @ingroup keypad
+ */
+
+#include <asm/io.h>
+#include <common.h>
+#include <asm/errno.h>
+#include <linux/types.h>
+#include <malloc.h>
+
+/*
+ *  * Module header file
+ *   */
+#include <mxc_keyb.h>
+
+/*!
+ * Comment KPP_DEBUG to disable debug messages
+ */
+
+#undef	KPP_DEBUG
+
+#ifdef	KPP_DEBUG
+#define	KPP_PRINTF(fmt, args...)	printf(fmt , ##args)
+
+static void mxc_kpp_dump_regs()
+{
+	unsigned short t1, t2, t3;
+
+	t1 = __raw_readw(KPCR);
+	t2 = __raw_readw(KPSR);
+	t3 = __raw_readw(KDDR);
+	/*
+	KPP_PRINTF("KPCR=0x%04x, KPSR=0x%04x, KDDR=0x%04x\n",
+		t1, t2, t3);
+		*/
+}
+#else
+#define KPP_PRINTF(fmt, args...)
+#endif
+
+static u16 mxc_key_mapping[] = CONFIG_MXC_KEYMAPPING;
+
+/*!
+ * This structure holds the keypad private data structure.
+ */
+static struct keypad_priv kpp_dev;
+
+/*! Indicates if the key pad device is enabled. */
+
+/*! This static variable indicates whether a key event is pressed/released. */
+static unsigned short KPress;
+
+/*! cur_rcmap and prev_rcmap array is used to detect key press and release. */
+static unsigned short *cur_rcmap;	/* max 64 bits (8x8 matrix) */
+static unsigned short *prev_rcmap;
+
+/*!
+ * Debounce polling period(10ms) in system ticks.
+ */
+/*static unsigned short KScanRate = (10 * CONFIG_SYS_HZ) / 1000;*/
+
+/*!
+ * These arrays are used to store press and release scancodes.
+ */
+static short **press_scancode;
+static short **release_scancode;
+
+static const unsigned short *mxckpd_keycodes;
+static unsigned short mxckpd_keycodes_size;
+
+/*!
+ * This function is called to scan the keypad matrix to find out the key press
+ * and key release events. Make scancode and break scancode are generated for
+ * key press and key release events.
+ *
+ * The following scanning sequence are done for
+ * keypad row and column scanning,
+ * -# Write 1's to KPDR[15:8], setting column data to 1's
+ * -# Configure columns as totem pole outputs(for quick discharging of keypad
+ * capacitance)
+ * -# Configure columns as open-drain
+ * -# Write a single column to 0, others to 1.
+ * -# Sample row inputs and save data. Multiple key presses can be detected on
+ * a single column.
+ * -# Repeat steps the above steps for remaining columns.
+ * -# Return all columns to 0 in preparation for standby mode.
+ * -# Clear KPKD and KPKR status bit(s) by writing to a 1,
+ *    Set the KPKR synchronizer chain by writing "1" to KRSS register,
+ *    Clear the KPKD synchronizer chain by writing "1" to KDSC register
+ *
+ * @result    Number of key pressed/released.
+ */
+static int mxc_kpp_scan_matrix(void)
+{
+	unsigned short reg_val;
+	int col, row;
+	short scancode = 0;
+	int keycnt = 0;		/* How many keys are still pressed */
+
+	/*
+	 * wmb() linux kernel function which guarantees orderings in write
+	 * operations
+	 */
+	/* wmb(); */
+
+	/* save cur keypad matrix to prev */
+	memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+	memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+
+	/*1. Disable both (depress and release) keypad interrupts.*/
+
+	/* KDIE has been disabled in mxc_kpp_getc before calling scan matrix.
+	  * KRIE is always disabled in this driver.
+	  */
+
+	for (col = 0; col < kpp_dev.kpp_cols; col++) {	/* Col */
+		/* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
+		reg_val = __raw_readw(KPDR);
+		reg_val |= 0xff00;
+		__raw_writew(reg_val, KPDR);
+
+		/*
+		 * 3. Configure columns as totem pole outputs(for quick
+		 * discharging of keypad capacitance)
+		 */
+		reg_val = __raw_readw(KPCR);
+		reg_val &= 0x00ff;
+		__raw_writew(reg_val, KPCR);
+
+		udelay(2);
+
+#ifdef KPP_DEBUG
+		mxc_kpp_dump_regs();
+#endif
+
+		/*
+		 * 4. Configure columns as open-drain
+		 */
+		reg_val = __raw_readw(KPCR);
+		reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
+		__raw_writew(reg_val, KPCR);
+
+		/*
+		 * 5. Write a single column to 0, others to 1.
+		 * 6. Sample row inputs and save data. Multiple key presses
+		 * can be detected on a single column.
+		 * 7. Repeat steps 2 - 6 for remaining columns.
+		 */
+
+		/* Col bit starts at 8th bit in KPDR */
+		reg_val = __raw_readw(KPDR);
+		reg_val &= ~(1 << (8 + col));
+		__raw_writew(reg_val, KPDR);
+
+		/* Delay added to avoid propagating the 0 from column to row
+		 * when scanning. */
+
+		udelay(5);
+
+#ifdef KPP_DEBUG
+		mxc_kpp_dump_regs();
+#endif
+
+		/* Read row input */
+		reg_val = __raw_readw(KPDR);
+		for (row = 0; row < kpp_dev.kpp_rows; row++) {	/* sample row */
+			if (TEST_BIT(reg_val, row) == 0) {
+				cur_rcmap[row] = BITSET(cur_rcmap[row], col);
+				keycnt++;
+			}
+		}
+	}
+
+	/*
+	 * 8. Return all columns to 0 in preparation for standby mode.
+	 * 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
+	 * set the KPKR synchronizer chain by writing "1" to KRSS register,
+	 * clear the KPKD synchronizer chain by writing "1" to KDSC register
+	 */
+	reg_val = 0x00;
+	__raw_writew(reg_val, KPDR);
+	reg_val = __raw_readw(KPDR);
+	reg_val = __raw_readw(KPSR);
+	reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
+	    KBD_STAT_KDSC;
+	__raw_writew(reg_val, KPSR);
+
+#ifdef KPP_DEBUG
+	mxc_kpp_dump_regs();
+#endif
+
+	/* Check key press status change */
+
+	/*
+	 * prev_rcmap array will contain the previous status of the keypad
+	 * matrix.  cur_rcmap array will contains the present status of the
+	 * keypad matrix. If a bit is set in the array, that (row, col) bit is
+	 * pressed, else it is not pressed.
+	 *
+	 * XORing these two variables will give us the change in bit for
+	 * particular row and column.  If a bit is set in XOR output, then that
+	 * (row, col) has a change of status from the previous state.  From
+	 * the diff variable the key press and key release of row and column
+	 * are found out.
+	 *
+	 * If the key press is determined then scancode for key pressed
+	 * can be generated using the following statement:
+	 *    scancode = ((row * 8) + col);
+	 *
+	 * If the key release is determined then scancode for key release
+	 * can be generated using the following statement:
+	 *    scancode = ((row * 8) + col) + MXC_KEYRELEASE;
+	 */
+	for (row = 0; row < kpp_dev.kpp_rows; row++) {
+		unsigned char diff;
+
+		/*
+		 * Calculate the change in the keypad row status
+		 */
+		diff = prev_rcmap[row] ^ cur_rcmap[row];
+
+		for (col = 0; col < kpp_dev.kpp_cols; col++) {
+			if ((diff >> col) & 0x1) {
+				/* There is a status change on col */
+				if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
+					/*
+					 * Previous state is 0, so now
+					 * a key is pressed
+					 */
+					scancode =
+					    ((row * kpp_dev.kpp_cols) +
+					     col);
+					KPress = 1;
+					kpp_dev.iKeyState = KStateUp;
+
+					KPP_PRINTF("Press   (%d, %d) scan=%d "
+						 "Kpress=%d\n",
+						 row, col, scancode, KPress);
+					press_scancode[row][col] =
+					    (short)scancode;
+				} else {
+					/*
+					 * Previous state is not 0, so
+					 * now a key is released
+					 */
+					scancode =
+					    (row * kpp_dev.kpp_cols) +
+					    col + MXC_KEYRELEASE;
+					KPress = 0;
+					kpp_dev.iKeyState = KStateDown;
+
+					KPP_PRINTF
+					    ("Release (%d, %d) scan=%d Kpress=%d\n",
+					     row, col, scancode, KPress);
+					release_scancode[row][col] =
+					    (short)scancode;
+					keycnt++;
+				}
+			}
+		}
+	}
+
+	return keycnt;
+}
+
+static int mxc_kpp_reset(void)
+{
+	unsigned short reg_val;
+	int i;
+
+	/*
+	* Stop scanning and wait for interrupt.
+	* Enable press interrupt and disable release interrupt.
+	*/
+	__raw_writew(0x00FF, KPDR);
+	reg_val = __raw_readw(KPSR);
+	reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
+	reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+	__raw_writew(reg_val, KPSR);
+	reg_val |= KBD_STAT_KDIE;
+	reg_val &= ~KBD_STAT_KRIE;
+	__raw_writew(reg_val, KPSR);
+
+#ifdef KPP_DEBUG
+	mxc_kpp_dump_regs();
+#endif
+
+	/*
+	* No more keys pressed... make sure unwanted key codes are
+	* not given upstairs
+	*/
+	for (i = 0; i < kpp_dev.kpp_rows; i++) {
+		memset(press_scancode[i], -1,
+			sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+		memset(release_scancode[i], -1,
+			sizeof(release_scancode[0][0]) *
+			kpp_dev.kpp_cols);
+	}
+
+	return 0;
+}
+
+int mxc_kpp_getc(struct kpp_key_info **key_info)
+{
+	int col, row;
+	int key_cnt;
+	unsigned short reg_val;
+	short scancode = 0;
+	int index = 0;
+	struct kpp_key_info *keyi;
+
+	reg_val = __raw_readw(KPSR);
+
+	if (reg_val & KBD_STAT_KPKD) {
+		/*
+		* Disable key press(KDIE status bit) interrupt
+		*/
+		reg_val &= ~KBD_STAT_KDIE;
+		__raw_writew(reg_val, KPSR);
+
+#ifdef KPP_DEBUG
+		mxc_kpp_dump_regs();
+#endif
+
+		key_cnt = mxc_kpp_scan_matrix();
+	} else {
+		return 0;
+	}
+
+	if (key_cnt <= 0)
+		return 0;
+
+	*key_info = keyi =
+		(struct kpp_key_info *)malloc
+		(sizeof(struct kpp_key_info) * key_cnt);
+
+	/*
+	* This switch case statement is the
+	* implementation of state machine of debounc
+	* logic for key press/release.
+	* The explaination of state machine is as
+	* follows:
+	*
+	* KStateUp State:
+	* This is in intial state of the state machine
+	* this state it checks for any key presses.
+	* The key press can be checked using the
+	* variable KPress. If KPress is set, then key
+	* press is identified and switches the to
+	* KStateFirstDown state for key press to
+	* debounce.
+	*
+	* KStateFirstDown:
+	* After debounce delay(10ms), if the KPress is
+	* still set then pass scancode generated to
+	* input device and change the state to
+	* KStateDown, else key press debounce is not
+	* satisfied so change the state to KStateUp.
+	*
+	* KStateDown:
+	* In this state it checks for any key release.
+	* If KPress variable is cleared, then key
+	* release is indicated and so, switch the
+	* state to KStateFirstUp else to state
+	* KStateDown.
+	*
+	* KStateFirstUp:
+	* After debounce delay(10ms), if the KPress is
+	* still reset then pass the key release
+	* scancode to input device and change
+	* the state to KStateUp else key release is
+	* not satisfied so change the state to
+	* KStateDown.
+	*/
+
+	for (row = 0; row < kpp_dev.kpp_rows; row++) {
+		for (col = 0; col < kpp_dev.kpp_cols; col++) {
+			if ((press_scancode[row][col] != -1)) {
+				/* Still Down, so add scancode */
+				scancode =
+				    press_scancode[row][col];
+
+				keyi[index].val = mxckpd_keycodes[scancode];
+				keyi[index++].evt = KDepress;
+
+				KPP_PRINTF("KStateFirstDown: scan=%d val=%d\n",
+					scancode, mxckpd_keycodes[scancode]);
+				if (index >= key_cnt)
+					goto key_detect;
+
+				kpp_dev.iKeyState = KStateDown;
+				press_scancode[row][col] = -1;
+			}
+		}
+	}
+
+	for (row = 0; row < kpp_dev.kpp_rows; row++) {
+		for (col = 0; col < kpp_dev.kpp_cols; col++) {
+			if ((release_scancode[row][col] != -1)) {
+				scancode =
+				    release_scancode[row][col];
+				scancode =
+					scancode - MXC_KEYRELEASE;
+
+				keyi[index].val = mxckpd_keycodes[scancode];
+				keyi[index++].evt = KRelease;
+
+				KPP_PRINTF("KStateFirstUp: scan=%d val=%d\n",
+					scancode, mxckpd_keycodes[scancode]);
+				if (index >= key_cnt)
+					goto key_detect;
+
+				kpp_dev.iKeyState = KStateUp;
+				release_scancode[row][col] = -1;
+			}
+		}
+	}
+
+key_detect:
+	mxc_kpp_reset();
+	return key_cnt;
+}
+
+/*!
+ * This function is called to free the allocated memory for local arrays
+ */
+static void mxc_kpp_free_allocated(void)
+{
+	int i;
+
+	if (press_scancode) {
+		for (i = 0; i < kpp_dev.kpp_rows; i++) {
+			if (press_scancode[i])
+				free(press_scancode[i]);
+		}
+		free(press_scancode);
+	}
+
+	if (release_scancode) {
+		for (i = 0; i < kpp_dev.kpp_rows; i++) {
+			if (release_scancode[i])
+				free(release_scancode[i]);
+		}
+		free(release_scancode);
+	}
+
+	if (cur_rcmap)
+		free(cur_rcmap);
+
+	if (prev_rcmap)
+		free(prev_rcmap);
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param   pdev  the device structure used to store device specific
+ *                information that is used by the suspend, resume and remove
+ *                functions.
+ *
+ * @return  The function returns 0 on successful registration. Otherwise returns
+ *          specific error code.
+ */
+int mxc_kpp_init(void)
+{
+	int i;
+	int retval;
+	unsigned int reg_val;
+
+	kpp_dev.kpp_cols = CONFIG_MXC_KPD_COLMAX;
+	kpp_dev.kpp_rows = CONFIG_MXC_KPD_ROWMAX;
+
+	/* Configure keypad */
+
+	/* Enable number of rows in keypad (KPCR[7:0])
+	 * Configure keypad columns as open-drain (KPCR[15:8])
+	 *
+	 * Configure the rows/cols in KPP
+	 * LSB nibble in KPP is for 8 rows
+	 * MSB nibble in KPP is for 8 cols
+	 */
+	reg_val = __raw_readw(KPCR);
+	reg_val |= (1  << kpp_dev.kpp_rows) - 1;	/* LSB */
+	reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;	/* MSB */
+	__raw_writew(reg_val, KPCR);
+
+	/* Write 0's to KPDR[15:8] */
+	reg_val = __raw_readw(KPDR);
+	reg_val &= 0x00ff;
+	__raw_writew(reg_val, KPDR);
+
+	/* Configure columns as output,
+	 * rows as input (KDDR[15:0]) */
+	reg_val = __raw_readw(KDDR);
+	reg_val |= 0xff00;
+	reg_val &= 0xff00;
+	__raw_writew(reg_val, KDDR);
+
+	/* Clear the KPKD Status Flag
+	 * and Synchronizer chain. */
+	reg_val = __raw_readw(KPSR);
+	reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
+	reg_val |= KBD_STAT_KPKD;
+	reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+	__raw_writew(reg_val, KPSR);
+	/* Set the KDIE control bit, and clear the KRIE
+	 * control bit (avoid false release events). */
+	reg_val |= KBD_STAT_KDIE;
+	reg_val &= ~KBD_STAT_KRIE;
+	__raw_writew(reg_val, KPSR);
+
+#ifdef KPP_DEBUG
+	mxc_kpp_dump_regs();
+#endif
+
+	mxckpd_keycodes = mxc_key_mapping;
+	mxckpd_keycodes_size = kpp_dev.kpp_cols * kpp_dev.kpp_rows;
+
+	if ((mxckpd_keycodes == (void *)0)
+	    || (mxckpd_keycodes_size == 0)) {
+		retval = -ENODEV;
+		goto err;
+	}
+
+	/* allocate required memory */
+	press_scancode   = (short **)malloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]));
+	release_scancode = (short **)malloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]));
+
+	if (!press_scancode || !release_scancode) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < kpp_dev.kpp_rows; i++) {
+		press_scancode[i] = (short *)malloc(kpp_dev.kpp_cols
+					    * sizeof(press_scancode[0][0]));
+		release_scancode[i] =
+		    (short *)malloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]));
+
+		if (!press_scancode[i] || !release_scancode[i]) {
+			retval = -ENOMEM;
+			goto err;
+		}
+	}
+
+	cur_rcmap =
+	    (unsigned short *)malloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+	prev_rcmap =
+	    (unsigned short *)malloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+
+	if (!cur_rcmap || !prev_rcmap) {
+		retval = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < kpp_dev.kpp_rows; i++) {
+		memset(press_scancode[i], -1,
+		       sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+		memset(release_scancode[i], -1,
+		       sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
+	}
+	memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+	memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+
+	return 0;
+
+err:
+	mxc_kpp_free_allocated();
+	return retval;
+}
diff --git a/include/mxc_keyb.h b/include/mxc_keyb.h
new file mode 100644
index 0000000..882fba2
--- /dev/null
+++ b/include/mxc_keyb.h
@@ -0,0 +1,240 @@ 
+/*
+ * Copyright (C) 2009-2014 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*!
+ * @defgroup keypad Keypad Driver
+ */
+
+/*!
+ * @file mxc_keyb.h
+ *
+ * @brief MXC keypad header file.
+ *
+ * @ingroup keypad
+ */
+#ifndef __MXC_KEYB_H__
+#define __MXC_KEYB_H__
+
+#define KEY_1                   2
+#define KEY_2                   3
+#define KEY_3                   4
+#define KEY_F1                  59
+#define KEY_UP                  103
+#define KEY_F2                  60
+
+#define KEY_4                   5
+#define KEY_5                   6
+#define KEY_6                   7
+#define KEY_LEFT                105
+#define KEY_SELECT              0x161
+#define KEY_RIGHT               106
+
+#define KEY_7                   8
+#define KEY_8                   9
+#define KEY_9                   10
+#define KEY_F3                  61
+#define KEY_DOWN                108
+#define KEY_F4                  62
+
+#define KEY_0                   11
+#define KEY_OK                  0x160
+#define KEY_ESC                 1
+#define KEY_ENTER               28
+#define KEY_MENU                139     /* Menu (show menu) */
+#define KEY_BACK                158     /* AC Back */
+
+/*!
+ * Keypad Module Name
+ */
+#define MOD_NAME  "mxckpd"
+
+/*!
+ * Keypad irq number
+ */
+#define KPP_IRQ  MXC_INT_KPP
+
+/*!
+ * XLATE mode selection
+ */
+#define KEYPAD_XLATE        0
+
+/*!
+ * RAW mode selection
+ */
+#define KEYPAD_RAW          1
+
+/*!
+ * Maximum number of keys.
+ */
+#define MAXROW			8
+#define MAXCOL			8
+#define MXC_MAXKEY		(MAXROW * MAXCOL)
+
+/*!
+ * This define indicates break scancode for every key release. A constant
+ * of 128 is added to the key press scancode.
+ */
+#define  MXC_KEYRELEASE   128
+
+/*
+ * _reg_KPP_KPCR   _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR
+ * Keypad Control Register Address
+ */
+#define KPCR    (KPP_BASE_ADDR + 0x00)
+
+/*
+ * Keypad Status Register Address
+ */
+#define KPSR    (KPP_BASE_ADDR + 0x02)
+
+/*
+ * Keypad Data Direction Address
+ */
+#define KDDR    (KPP_BASE_ADDR + 0x04)
+
+/*
+ * Keypad Data Register
+ */
+#define KPDR    (KPP_BASE_ADDR + 0x06)
+
+/*
+ * Key Press Interrupt Status bit
+ */
+#define KBD_STAT_KPKD        0x01
+
+/*
+ * Key Release Interrupt Status bit
+ */
+#define KBD_STAT_KPKR        0x02
+
+/*
+ * Key Depress Synchronizer Chain Status bit
+ */
+#define KBD_STAT_KDSC        0x04
+
+/*
+ * Key Release Synchronizer Status bit
+ */
+#define KBD_STAT_KRSS        0x08
+
+/*
+ * Key Depress Interrupt Enable Status bit
+ */
+#define KBD_STAT_KDIE        0x100
+
+/*
+ * Key Release Interrupt Enable
+ */
+#define KBD_STAT_KRIE        0x200
+
+/*
+ * Keypad Clock Enable
+ */
+#define KBD_STAT_KPPEN       0x400
+
+/*!
+ * Buffer size of keypad queue. Should be a power of 2.
+ */
+#define KPP_BUF_SIZE    128
+
+/*!
+ * Test whether bit is set for integer c
+ */
+#define TEST_BIT(c, n) ((c) & (0x1 << (n)))
+
+/*!
+ * Set nth bit in the integer c
+ */
+#define BITSET(c, n)   ((c) | (1 << (n)))
+
+/*!
+ * Reset nth bit in the integer c
+ */
+#define BITRESET(c, n) ((c) & ~(1 << (n)))
+
+#ifndef CONFIG_MXC_KEYMAPPING
+#define CONFIG_MXC_KEYMAPPING \
+	{       \
+		KEY_SELECT, KEY_BACK, KEY_1,     KEY_2, \
+		KEY_3,      KEY_4,    KEY_5,     KEY_MENU, \
+		KEY_6,      KEY_7,    KEY_8,     KEY_9, \
+		KEY_UP,     KEY_LEFT, KEY_RIGHT, KEY_DOWN, \
+	}
+#endif
+
+#ifndef CONFIG_MXC_KPD_COLMAX
+#define CONFIG_MXC_KPD_COLMAX 4
+#endif
+
+#ifndef CONFIG_MXC_KPD_ROWMAX
+#define CONFIG_MXC_KPD_ROWMAX 4
+#endif
+
+enum KeyEvent {
+	KDepress,
+	KRelease
+};
+
+/*!
+ * This enum represents the keypad state machine to maintain debounce logic
+ * for key press/release.
+ */
+enum KeyState {
+
+	/*!
+	 * Key press state.
+	 */
+	KStateUp,
+
+	/*!
+	 * Key press debounce state.
+	 */
+	KStateFirstDown,
+
+	/*!
+	 * Key release state.
+	 */
+	KStateDown,
+
+	/*!
+	 * Key release debounce state.
+	 */
+	KStateFirstUp
+};
+
+/*!
+ * Keypad Private Data Structure
+ */
+struct keypad_priv {
+
+	/*!
+	 * Keypad state machine.
+	 */
+	enum KeyState iKeyState;
+
+	/*!
+	 * Number of rows configured in the keypad matrix
+	 */
+	unsigned long kpp_rows;
+
+	/*!
+	 * Number of Columns configured in the keypad matrix
+	 */
+	unsigned long kpp_cols;
+};
+
+/*!
+ * Keypad Data Structure
+ * */
+struct kpp_key_info {
+	enum KeyEvent evt;
+	unsigned short val;
+};
+
+int mxc_kpp_init(void);
+int mxc_kpp_getc(struct kpp_key_info **);
+
+#endif				/* __MXC_KEYB_H__ */