aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xDocumentation/DocBook/ske_keypad.tmpl89
-rwxr-xr-xarch/arm/mach-ux500/board-mop500.c6
-rwxr-xr-xarch/arm/mach-ux500/clock.c2
-rwxr-xr-xarch/arm/mach-ux500/devices.c232
-rwxr-xr-xarch/arm/mach-ux500/include/mach/devices.h1
-rwxr-xr-xarch/arm/mach-ux500/include/mach/irqs.h1
-rwxr-xr-xarch/arm/mach-ux500/include/mach/kpd.h60
-rw-r--r--drivers/input/keyboard/Kconfig7
-rw-r--r--drivers/input/keyboard/Makefile1
-rwxr-xr-xdrivers/input/keyboard/ske_keypad.c533
10 files changed, 904 insertions, 28 deletions
diff --git a/Documentation/DocBook/ske_keypad.tmpl b/Documentation/DocBook/ske_keypad.tmpl
new file mode 100755
index 00000000000..7e02b72e450
--- /dev/null
+++ b/Documentation/DocBook/ske_keypad.tmpl
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
+
+<book id="keypad-API-Guide">
+ <bookinfo>
+ <title>Keypad Driver API guide</title>
+
+ <authorgroup>
+ <author>
+ <firstname>NaveenKumar</firstname>
+ <surname>Gaddipati</surname>
+ <affiliation>
+ <address>
+ <email>naveen.gaddipati@stericsson.com</email>
+ </address>
+ </affiliation>
+ </author>
+ </authorgroup>
+
+ <copyright>
+ <year>2010</year>
+ <holder>ST-Ericsson</holder>
+ </copyright>
+
+ <subjectset>
+ <subject>
+ <subjectterm>Linux standard functions</subjectterm>
+ </subject>
+ </subjectset>
+
+ <legalnotice>
+ <para>
+ License terms: GNU General Public License (GPL) version 2.
+ </para>
+
+ </legalnotice>
+ </bookinfo>
+
+<toc></toc>
+
+ <chapter id="intro">
+ <title>Introduction</title>
+ <para>
+ This documentation describes the API provided by the keypad driver for internal keypad.
+ </para>
+ </chapter>
+
+ <chapter id="bugs">
+ <title>Known Bugs And Assumptions</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>None</term>
+ <listitem>
+ <para>
+ None.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </chapter>
+
+ <chapter id="pubfunctions">
+ <title>Public Functions Provided</title>
+ <para>
+ This ske keypad driver doesn't export any functions.
+ </para>
+ </chapter>
+
+ <chapter id="structs">
+ <title>Structures</title>
+ <para>
+ This chapter contains the autogenerated documentation of the structures which are
+ used in the keypad driver.
+ </para>
+!Iarch/arm/mach-ux500/include/mach/kpd.h
+ </chapter>
+
+ <chapter id="intfunctions">
+ <title>Internal Functions Provided</title>
+ <para>
+ This chapter contains the autogenerated documentation of the internal functions.
+ </para>
+!Idrivers/input/keyboard/ske_keypad.c
+ </chapter>
+
+ </book>
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 782a9801aa2..a52bc7e6231 100755
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -124,6 +124,9 @@ static struct gpio_altfun_data gpio_altfun_table[] = {
__GPIO_ALT(GPIO_ALT_TP_SET_EXT_CLK, 228, 228, 0, NMK_GPIO_ALT_A,
"u8500_tp"),
#endif
+#ifdef CONFIG_KEYPAD_SKE
+ __GPIO_ALT(GPIO_ALT_KEYPAD, 153, 168, 0, NMK_GPIO_ALT_A, "ske-kp"),
+#endif
};
@@ -1309,6 +1312,9 @@ static struct platform_device *platform_board_devs[] __initdata = {
&ux500_hash1_device,
#endif
&u8500_leds_controller,
+#ifdef CONFIG_KEYPAD_SKE
+ &ske_keypad_device,
+#endif
};
static void __init mop500_platdata_init(void)
diff --git a/arch/arm/mach-ux500/clock.c b/arch/arm/mach-ux500/clock.c
index 056ecc26140..5ab9027368b 100755
--- a/arch/arm/mach-ux500/clock.c
+++ b/arch/arm/mach-ux500/clock.c
@@ -433,7 +433,7 @@ static struct clk_lookup u8500_common_clkregs[] = {
CLK(gpio2, "gpioblock2", NULL),
CLK(sdi5, "sdi5", NULL),
CLK(uart2, "uart2", NULL),
- CLK(ske, "ske", NULL),
+ CLK(ske, "ske-kp", NULL),
CLK(sdi2, "sdi2", NULL),
CLK(i2c0, "nmk-i2c.0", NULL),
CLK(fsmc, "fsmc", NULL),
diff --git a/arch/arm/mach-ux500/devices.c b/arch/arm/mach-ux500/devices.c
index 711b02b45be..aab9f3e6501 100755
--- a/arch/arm/mach-ux500/devices.c
+++ b/arch/arm/mach-ux500/devices.c
@@ -17,6 +17,7 @@
#include <linux/gpio.h>
#include <linux/usb/musb.h>
#include <linux/dma-mapping.h>
+#include <linux/input.h>
#include <asm/irq.h>
@@ -43,6 +44,7 @@
#include <mach/tc35892.h>
#include <mach/uart.h>
#include <mach/setup.h>
+#include <mach/kpd.h>
void __init u8500_register_device(struct platform_device *dev, void *data)
{
@@ -726,7 +728,237 @@ struct amba_device ux500_uart2_device = {
};
#endif
+#ifdef CONFIG_KEYPAD_SKE
+
+#define KEYPAD_DEBOUNCE_PERIOD_SKE 64
+#define ROW_PIN_I0 164
+#define ROW_PIN_I3 161
+#define ROW_PIN_I4 156
+#define ROW_PIN_I7 153
+#define COL_PIN_O0 168
+#define COL_PIN_O3 165
+#define COL_PIN_O4 160
+#define COL_PIN_O7 157
+
+/* ske_set_gpio_column - Disables Pull up
+ * @start: start column pin
+ * @end: end column pin
+ * This function disables the pull up for output pins
+*/
+int ske_set_gpio_column(int start, int end)
+{
+ int i;
+ int status = 0;
+
+ for (i = start; i <= end; i++) {
+ status = gpio_request(i, "ske");
+ if (status < 0) {
+ printk(KERN_ERR "%s: gpio request failed \n", __func__);
+ return status;
+ }
+ status = nmk_gpio_set_pull(i, NMK_GPIO_PULL_UP);
+ if (status < 0) {
+ printk(KERN_ERR "%s: gpio set pull failed \n", __func__);
+ return status;
+ }
+ gpio_free(i);
+ }
+ return status;
+}
+/* ske_set_gpio_row - enable the input pins
+ * @start: start row pin
+ * @end: end row pin
+ * This function enable the input pins
+*/
+int ske_set_gpio_row(int start, int end)
+{
+ int i = 0;
+ int status = 0;
+
+ for (i = start; i <= end; i++) {
+ status = gpio_request(i, "ske");
+ if (status < 0) {
+ printk(KERN_ERR "%s: gpio request failed \n", __func__);
+ return status;
+ }
+ status = gpio_direction_output(i, 1);
+ if (status < 0) {
+ printk(KERN_ERR "%s: gpio direction failed \n", __func__);
+ gpio_free(i);
+ return status;
+ }
+ gpio_set_value(i, 1);
+ gpio_free(i);
+ }
+ return status;
+}
+/**
+ * ske_kp_init - enable the gpio configuration
+ * @kp: keypad device data pointer
+ *
+ * This function is used to enable the gpio configuration for keypad
+ *
+ */
+static int ske_kp_init(struct keypad_t *kp)
+{
+ int ret;
+ ret = ske_set_gpio_row(ROW_PIN_I3, ROW_PIN_I0);
+ if (ret < 0)
+ goto err;
+ ret = ske_set_gpio_row(ROW_PIN_I7, ROW_PIN_I4);
+ if (ret < 0)
+ goto err;
+ ret = ske_set_gpio_column(COL_PIN_O3, COL_PIN_O0);
+ if (ret < 0)
+ goto err;
+ ret = ske_set_gpio_column(COL_PIN_O7, COL_PIN_O4);
+ if (ret < 0)
+ goto err;
+ ret = stm_gpio_altfuncenable(GPIO_ALT_KEYPAD);
+ if (ret)
+ goto err;
+ return 0;
+err:
+ printk(KERN_ERR "%s: failed \n", __func__);
+ return ret;
+}
+/**
+ * ske_kp_exit - disable the gpio configuration
+ * @kp: keypad device data pointer
+ *
+ * This function is used to disable the gpio configuration for keypad
+ *
+ */
+static int ske_kp_exit(struct keypad_t *kp)
+{
+ stm_gpio_altfuncdisable(GPIO_ALT_KEYPAD);
+ return 0;
+}
+
+/*
+ * Initializes the key scan table (lookup table) as per pre-defined the scan
+ * codes to be passed to upper layer with respective key codes
+ */
+u8 const kpd_lookup_tbl[MAX_KPROW][MAX_KPROW] = {
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_MENU,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_3,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_END,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_7,
+ KEY_DOWN,
+ KEY_UP,
+ KEY_VOLUMEDOWN,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_RESERVED,
+ KEY_4,
+ KEY_VOLUMEUP,
+ KEY_LEFT,
+ KEY_RESERVED,
+ KEY_0,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_9,
+ KEY_RESERVED,
+ KEY_RIGHT,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_1,
+ KEY_RESERVED,
+ KEY_RESERVED
+ },
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_BACK,
+ KEY_RESERVED,
+ KEY_SEND,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_2
+ },
+ {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_ENTER,
+ KEY_RESERVED
+ }
+};
+
+static struct keypad_device keypad_board = {
+ .init = ske_kp_init,
+ .exit = ske_kp_exit,
+ .kcode_tbl = (u8 *) kpd_lookup_tbl,
+ .krow = MAX_KPROW,
+ .kcol = MAX_KPCOL,
+ .debounce_period = KEYPAD_DEBOUNCE_PERIOD_SKE,
+ .irqtype = 0,
+ .int_status = KP_INT_DISABLED,
+ .int_line_behaviour = INT_LINE_NOTSET,
+};
+
+struct resource keypad_resources[] = {
+ [0] = {
+ .start = U8500_SKE_BASE,
+ .end = U8500_SKE_BASE + SZ_4K - 1,
+ .name = "ux500_ske_base",
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_SKE_KP,
+ .end = IRQ_SKE_KP,
+ .name = "ux500_ske_irq",
+ .flags = IORESOURCE_IRQ,
+ },
+};
+struct platform_device ske_keypad_device = {
+ .name = "ske-kp",
+ .id = -1,
+ .dev = {
+ .platform_data = &keypad_board,
+ },
+ .num_resources = ARRAY_SIZE(keypad_resources),
+ .resource = keypad_resources,
+};
+#endif
#if defined(CONFIG_U5500_MLOADER_HELPER)
struct platform_device mloader_helper_device = {
.name = "mloader_helper",
diff --git a/arch/arm/mach-ux500/include/mach/devices.h b/arch/arm/mach-ux500/include/mach/devices.h
index 73ddb146626..5acb60460f3 100755
--- a/arch/arm/mach-ux500/include/mach/devices.h
+++ b/arch/arm/mach-ux500/include/mach/devices.h
@@ -50,6 +50,7 @@ extern struct platform_device ux500_musb_device;
extern struct amba_device ux500_uart0_device;
extern struct amba_device ux500_uart1_device;
extern struct amba_device ux500_uart2_device;
+extern struct platform_device ske_keypad_device;
#ifdef CONFIG_U5500_MLOADER_HELPER
extern struct platform_device mloader_helper_device;
diff --git a/arch/arm/mach-ux500/include/mach/irqs.h b/arch/arm/mach-ux500/include/mach/irqs.h
index b883d013773..88e51d1c4da 100755
--- a/arch/arm/mach-ux500/include/mach/irqs.h
+++ b/arch/arm/mach-ux500/include/mach/irqs.h
@@ -51,6 +51,7 @@
#define IRQ_SDMMC1 (IRQ_SPI_START + 50)
#define IRQ_I2C4 (IRQ_SPI_START + 51)
#define IRQ_SSP1 (IRQ_SPI_START + 52)
+#define IRQ_SKE_KP (IRQ_SPI_START + 54)
#define IRQ_I2C2 (IRQ_SPI_START + 55)
#define IRQ_SDMMC3 (IRQ_SPI_START + 59)
#define IRQ_SDMMC0 (IRQ_SPI_START + 60)
diff --git a/arch/arm/mach-ux500/include/mach/kpd.h b/arch/arm/mach-ux500/include/mach/kpd.h
index 46177466078..ffff8eea193 100755
--- a/arch/arm/mach-ux500/include/mach/kpd.h
+++ b/arch/arm/mach-ux500/include/mach/kpd.h
@@ -14,12 +14,14 @@
#define CONFIG_AUTOSCAN_ENABLED 1
#endif
#include <mach/hardware.h>
+#include <linux/clk.h>
#define MAX_KPROW 8
#define MAX_KPCOL 8
+#define MAX_KEYS (MAX_KPROW * MAX_KPCOL)
/*keypad related Constants*/
-#define KEYPAD_RELEASE_PERIOD 11 /*110 Msec, repeate key scan time */
-#define KEYPAD_SCAN_PERIOD 4 /*40Msec for new keypress */
+#define KEYPAD_RELEASE_PERIOD 11 /*11 msec, repeate key scan time */
+#define KEYPAD_SCAN_PERIOD 4 /*4msec for new keypress */
#define KEYPAD_STATE_DEFAULT 0
#define KEYPAD_STATE_PRESSED 1
#define KEYPAD_STATE_PRESSACK 2
@@ -33,18 +35,18 @@ struct keypad_t;
* struct keypad_device - Device data structure for platform specific data
* @init: pointer to keypad init function
* @exit: pointer to keypad deinitialisation function
- * @autoscan_check: pointer to read autoscan status function, not used
+ * @autoscan_check: pointer to read autoscan status function
* currently
* @autoscan_disable: pointer to autoscan feature disable function,
* not used currently
* @autoscan_results: pointer to read autoscan results function
- * @autoscan_en: pointer to enable autoscan feature function, not used
+ * @autoscan_en: pointer to enable autoscan feature function
* currently
* @irqen: pointer to enable irq function
* @irqdis: pointer to disable irq function
* @kcode_tbl: lookup table for keycodes
* @krow: mask for available rows, value is 0xFF
- * @kcol: mask for available columns, value is 0xFF
+ * @kcol: mask for available columns, value is 0xFF
* @irqdis_int: pointer to disable irq function, to be called from ISR
* @debounce_period: platform specific debounce time, can be fine tuned later
* @irqtype: type of interrupt
@@ -54,47 +56,51 @@ struct keypad_t;
* @enable_wakeup: specifies if keypad event can wake up system from sleep
*/
struct keypad_device {
- int (*init)(struct keypad_t *kp);
- int (*exit)(struct keypad_t *kp);
- int (*autoscan_check)(void);
- void (*autoscan_disable)(void);
- int (*autoscan_results)(struct keypad_t *kp);
- void (*autoscan_en)(void);
- int (*irqen)(struct keypad_t *kp);
- int (*irqdis)(struct keypad_t *kp); /* normal disable */
- u8 *kcode_tbl;
- u8 krow;
- u8 kcol;
- int (*irqdis_int)(struct keypad_t *kp);
- /* func used wen disable in interrupt handler */
- u8 debounce_period;
- unsigned long irqtype;
- u8 irq; /*IRQ no*/
- u8 int_status;
- u8 int_line_behaviour;
- bool enable_wakeup;
+ int (*init)(struct keypad_t *kp);
+ int (*exit)(struct keypad_t *kp);
+ int (*autoscan_check)(void);
+ void (*autoscan_disable)(void);
+ int (*autoscan_results)(struct keypad_t *kp);
+ void (*autoscan_en)(void);
+ int (*irqen)(struct keypad_t *kp);
+ int (*irqdis)(struct keypad_t *kp);
+ u8 *kcode_tbl;
+ u8 krow;
+ u8 kcol;
+ int (*irqdis_int)(struct keypad_t *kp);
+ u8 debounce_period;
+ unsigned long irqtype;
+ u8 irq;
+ u8 int_status;
+ u8 int_line_behaviour;
+ bool enable_wakeup;
};
-
/**
* struct keypad_t - keypad data structure used internally by keypad driver
* @irq: irq no
* @mode: 0 for interrupt mode, 1 for polling mode
+ * @ske_regs: ske regsiters base address
+ * @cr_lock: spinlock variable
* @key_state: array for saving keystates
* @lockbits: used for synchronisation in ISR
* @inp_dev: pointer to input device object
- * @address_for_data: not used
* @kscan_work: work queue
* @board: keypad platform device
+ * @key_cnt: count the keys pressed
+ * @clk: clock structure pointer
*/
struct keypad_t {
int irq;
int mode;
+ void __iomem *ske_regs;
int key_state[MAX_KPROW][MAX_KPCOL];
unsigned long lockbits;
+ spinlock_t cr_lock;
struct input_dev *inp_dev;
- void *address_for_data;
struct delayed_work kscan_work;
struct keypad_device *board;
+ int key_cnt;
+ struct clk *clk;
};
/**
* enum kp_int_status - enum for INTR status
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 922f72715ca..478c6487ffa 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -444,6 +444,13 @@ config KEYPAD_U8500
Say Y here if you want to use a keypad provided on UIB board
which is to be plugged on top of 8500 core platform.
+config KEYPAD_SKE
+ tristate "SKE keypad support"
+ depends on ARCH_U8500 && !KEYPAD_U8500 && !TC35893_KEYPAD
+ default y
+ help
+ Say Y here if you want to use a keypad provided on Ux500 platform
+
config TC35893_KEYPAD
tristate "TC35893 keypad controller"
depends on I2C
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index cc4ef677cea..f233a9ada5f 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -42,3 +42,4 @@ obj-$(CONFIG_KEYPAD_U8500) += u8500-kpd.o
obj-$(CONFIG_TC35893_KEYPAD) += tc35893-keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
+obj-$(CONFIG_KEYPAD_SKE) += ske_keypad.o
diff --git a/drivers/input/keyboard/ske_keypad.c b/drivers/input/keyboard/ske_keypad.c
new file mode 100755
index 00000000000..4e00e12a890
--- /dev/null
+++ b/drivers/input/keyboard/ske_keypad.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2009
+ * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
+ * License terms:GNU General Public License (GPL) version 2
+ *
+ * This driver is used for SKE(Scroll Key Encoder) controller in
+ * all Ux500 platform.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <mach/kpd.h>
+
+#define SKE_CR 0x00
+#define SKE_VAL0 0x04
+#define SKE_VAL1 0x08
+#define SKE_DBCR 0x0C
+#define SKE_IMSC 0x10
+#define SKE_RIS 0x14
+#define SKE_MIS 0x18
+#define SKE_ICR 0x1C
+#define SKE_ASR0 0x20
+#define SKE_ASR1 0x24
+#define SKE_ASR2 0x28
+#define SKE_ASR3 0x2C
+
+#define SKE_KEYPAD_VER_MAJOR 1
+#define SKE_KEYPAD_VER_MINOR 0
+#define SKE_KEYPAD_VER_RELEASE 0
+#define KP_ASDIS 0x00000000
+#define KP_ASEN 0x00000004
+#define KP_SOFTSCAN_INTR_EN 0x00000008
+#define KP_SOFTSCAN_INTR_CLR 0x00000008
+#define KP_HI_ALLCOLS 0x0000FF00
+#define KP_LOW_ALLCOLS 0x00000000
+#define KP_AS_INTR_EN 0x04
+
+#define KP_MULT 0x40
+#define KP_MULT_SCAN_ALL 0x38
+#define KP_ASCANON 0x80
+#define KP_DBCR_PERIOD 0x1000
+
+#define CLR_KP_AUTOSCAN_INTR_MASK 0x00
+#define CLR_ALL_INTR 0xF
+#define KP_AUTOSCAN_INTR_CLR 0x04
+#define KP_AUTOSCAN_INTR_RIS 0x04
+#define DISABLE_AUTOSCAN 0x00000000
+#define DEFAULT_ASCAN_VALUE 0x00000000
+
+#define LOWER_BYTE 0xFF
+#define UPPER_BYTE 0xFF00
+#define SHIFT_8 8
+#define REG_OFFSET 0x04
+
+static void ske_kp_wq_kscan(struct work_struct *work);
+
+/**
+ * ske_kp_autoscan_en - enables the autoscan mode
+ * @kp: keypad device data pointer
+ * This function enables the autoscan mode
+*/
+static void ske_kp_autoscan_en(struct keypad_t *kp)
+{
+ int value;
+ unsigned long flags;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ writel(KP_MULT, (kp->ske_regs + SKE_CR));
+ value = readl(kp->ske_regs + SKE_CR);
+ value = (value | KP_ASEN | KP_LOW_ALLCOLS | KP_MULT_SCAN_ALL);
+ writel(value, (kp->ske_regs + SKE_CR));
+ writel(KP_DBCR_PERIOD, (kp->ske_regs + SKE_DBCR));
+ writel(CLR_ALL_INTR, (kp->ske_regs + SKE_ICR));
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+}
+
+/**
+ * ske_kp_autoscan_disable - Disables the autoscan mode
+ * @kp: keypad device data pointer
+ * This function disables the autoscan mode
+*/
+static void ske_kp_autoscan_disable(struct keypad_t *kp)
+{
+ unsigned long ske_cr_reg_value = KP_ASCANON;
+ unsigned long flags;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ while (ske_cr_reg_value & KP_ASCANON)
+ ske_cr_reg_value = readl(kp->ske_regs + SKE_CR);
+ writel(DISABLE_AUTOSCAN, (kp->ske_regs + SKE_CR));
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+}
+
+/**
+ * ske_kp_autoscan_intr_enable - Enables the autoscan mode
+ * @kp: keypad device data pointer
+ * This function enables the interrupt in autoscan mode
+*/
+static void ske_kp_autoscan_intr_enable(struct keypad_t *kp)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ writel(KP_AS_INTR_EN, (kp->ske_regs + SKE_IMSC));
+ kp->board->int_status = KP_INT_ENABLED;
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+}
+
+/**
+ * ske_kp_autoscan_intr_disable - Disables the autoscan mode
+ * @kp: keypad device data pointer
+ * This function disables the interrupt in autoscan mode
+*/
+static void ske_kp_autoscan_intr_disable(struct keypad_t *kp)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ writel(CLR_KP_AUTOSCAN_INTR_MASK, (kp->ske_regs + SKE_IMSC));
+ writel(KP_AUTOSCAN_INTR_CLR, (kp->ske_regs + SKE_ICR));
+ kp->board->int_status = KP_INT_DISABLED;
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+}
+
+/**
+ * ske_kp_autoscan_check - check the autoscan mode
+ * @kp: keypad device data pointer
+ * This function used to check the autoscan mode
+*/
+static bool ske_kp_autoscan_check(struct keypad_t *kp)
+{
+ unsigned long ske_cr_reg_value = 0;
+ unsigned long flags = 0;
+ bool ret;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ ske_cr_reg_value = readl(kp->ske_regs + SKE_CR);
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+ ret = (ske_cr_reg_value & KP_ASCANON);
+ return ret;
+}
+/**
+ * ske_kp_key_pressed - report key pressed
+ * @kp: keypad device data pointer
+ * @row: row number
+ * @col: col number
+ * @scancode: scane code for the key
+ * This function is used to report key pressed
+*/
+static void ske_kp_key_pressed(struct keypad_t *kp, int row, int col,
+ u8 *scancode)
+{
+ if (kp->key_state[row][col] == KEYPAD_STATE_DEFAULT) {
+ kp->key_cnt++;
+ input_report_key(kp->inp_dev, *scancode, true);
+ kp->key_state[row][col] = KEYPAD_STATE_PRESSACK;
+ }
+}
+
+/**
+ * ske_kp_key_released - report key released
+ * @kp: keypad device data pointer
+ * @row: row number
+ * @col: col number
+ * @scancode: scane code for the key
+ * This function is used to report key released
+*/
+static void ske_kp_key_released(struct keypad_t *kp, int row, int col,
+ u8 *scancode)
+{
+ int value;
+ if (kp->key_state[row][col] == KEYPAD_STATE_PRESSACK) {
+ value = KP_AUTOSCAN_INTR_RIS;
+ while (value && KP_AUTOSCAN_INTR_RIS)
+ value = readl(kp->ske_regs + SKE_RIS);
+ kp->key_cnt--;
+ input_report_key(kp->inp_dev, *scancode, false);
+ kp->key_state[row][col] = KEYPAD_STATE_DEFAULT;
+ }
+}
+
+/**
+ * ske_kp_autoscan_results - check the results of autoscan
+ * @kp: keypad device data pointer
+ * This function is used to check the results of autoscan
+*/
+static void ske_kp_autoscan_results(struct keypad_t *kp)
+{
+ unsigned int col_val[MAX_KPCOL];
+ int col = 0, row = 0, mask;
+ unsigned long add = 0;
+ u8 *p_kcode;
+ int key_pressed;
+ unsigned long flags;
+ spin_lock_irqsave(&kp->cr_lock, flags);
+ while (col < MAX_KPCOL) {
+ col_val[col] =
+ readl(kp->ske_regs + SKE_ASR0 + add) & LOWER_BYTE;
+ col_val[col + 1] =
+ (readl(kp->ske_regs + SKE_ASR0 + add) & UPPER_BYTE)
+ >> SHIFT_8;
+ col += 2;
+ add = add + REG_OFFSET;
+ }
+ spin_unlock_irqrestore(&kp->cr_lock, flags);
+ for (col = 0; col < MAX_KPCOL; col++) {
+ if (col_val[col] != 0) {
+ p_kcode = kp->board->kcode_tbl + col;
+ for (row = 0; row < MAX_KPROW; row++) {
+ mask = (1 << row);
+ key_pressed = 0;
+ key_pressed = (col_val[col] & mask);
+ if (key_pressed != 0) {
+ ske_kp_key_pressed(kp, row, col,
+ p_kcode);
+ ske_kp_key_released(kp, row, col,
+ p_kcode);
+ }
+ p_kcode += MAX_KPROW;
+ mask = 0;
+ }
+ }
+ }
+ input_sync(kp->inp_dev);
+}
+/**
+ * ske_kp_key_irqen- enables keypad interrupt
+ * @kp: keypad device data pointer
+ *
+ * enables keypad interrupt
+ */
+static int ske_kp_key_irqen(struct keypad_t *kp)
+{
+ if (kp->board->int_status != KP_INT_ENABLED)
+ ske_kp_autoscan_intr_enable(kp);
+ return 0;
+}
+
+/**
+ * ske_kp_key_irqdis- disables keypad interrupt
+ * @kp: keypad device data pointer
+ *
+ * disables keypad interrupt
+ */
+static int ske_kp_key_irqdis(struct keypad_t *kp)
+{
+ if (kp->board->int_status != KP_INT_DISABLED)
+ ske_kp_autoscan_intr_disable(kp);
+ return 0;
+}
+/**
+ * ske_kp_intrhandler - keypad interrupt handler
+ * @irq: irq number for keypad
+ * @dev_id: pointer to keypad device id
+ *
+ * checks for valid interrupt, disables interrupt to avoid any nested interrupt
+ * starts work queue for further key processing with debouncing logic
+ */
+static irqreturn_t ske_kp_intrhandler(int irq, void *dev_id)
+{
+ struct keypad_t *kp = (struct keypad_t *)dev_id;
+ if (!(test_bit(KPINTR_LKBIT, &kp->lockbits))) {
+ set_bit(KPINTR_LKBIT, &kp->lockbits);
+ ske_kp_key_irqdis(kp);
+ schedule_delayed_work(&kp->kscan_work,
+ kp->board->debounce_period);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * ske_kp_wq_kscan - work queue for keypad scanning
+ * @work: pointer to keypad data
+ *
+ * Executes at each scan tick, execute the key press/release function,
+ * Generates key press/release event message for input subsystem for valid key
+ * events, enables keypad interrupts (for int mode)
+ */
+
+static void ske_kp_wq_kscan(struct work_struct *work)
+{
+ bool ret = true;
+ struct keypad_t *kp =
+ container_of((struct delayed_work *)work,
+ struct keypad_t, kscan_work);
+
+ while (ret) {
+ udelay(100);
+ ret = ske_kp_autoscan_check(kp);
+ }
+ kp->key_cnt = 0;
+ ske_kp_autoscan_results(kp);
+ clear_bit(KPINTR_LKBIT, &kp->lockbits);
+ ske_kp_key_irqen(kp);
+}
+
+/**
+ * ske_kp_init_keypad - keypad parameter initialization
+ * @kp: keypad device data pointer
+ *
+ * Initializes Keybits to enable keyevents
+ * Initializes Initial keypress status to default
+ * Calls the keypad platform specific init function.
+ */
+int __init ske_kp_init_keypad(struct keypad_t *kp)
+{
+ int row, column, err;
+ u8 *p_kcode = kp->board->kcode_tbl;
+ if (kp->board->init) {
+ err = kp->board->init(kp);
+ if (err)
+ return err;
+ }
+ for (row = 0; row < MAX_KPROW; row++) {
+ for (column = 0; column < MAX_KPCOL; column++) {
+ set_bit(*p_kcode, kp->inp_dev->keybit);
+ kp->key_state[row][column] =
+ KEYPAD_STATE_DEFAULT;
+ p_kcode++;
+ }
+ }
+ return err;
+}
+
+#ifdef CONFIG_PM
+/**
+ * ske_kp_suspend - suspend keypad
+ * @pdev: platform data
+ * @state: power down level
+ * This function is used to suspend the keypad
+ */
+static int ske_kp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct keypad_t *kp = platform_get_drvdata(pdev);
+ dev_dbg(&pdev->dev, "suspend start\n");
+ if (!device_may_wakeup(&pdev->dev))
+ ske_kp_key_irqdis(kp);
+ dev_dbg(&pdev->dev, "suspend done\n");
+ return 0;
+}
+
+/**
+ * ske_kp_resume - resumes keypad
+ * @pdev: platform data
+ * This function is used to resume the keypad
+ */
+
+static int ske_kp_resume(struct platform_device *pdev)
+{
+ struct keypad_t *kp = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "resume start\n");
+ if (!device_may_wakeup(&pdev->dev))
+ ske_kp_key_irqen(kp);
+ dev_dbg(&pdev->dev, "resume done\n");
+ return 0;
+}
+
+#endif
+
+/**
+ * ske_kp_probe - keypad module probe function
+ * @pdev: driver platform data
+ *
+ * Allocates data memory, registers the module with input subsystem,
+ * initializes keypad default condition, initializes keypad interrupt handler
+ * for interrupt mode operation.
+ */
+static int __init ske_kp_probe(struct platform_device *pdev)
+{
+ struct keypad_t *kp;
+ struct resource *res = NULL;
+ int err = 0;
+ struct keypad_device *keypad_board;
+
+ if (!pdev)
+ return -EINVAL;
+ dev_dbg(&pdev->dev, "probe start\n");
+
+ keypad_board = pdev->dev.platform_data;
+ kp = kzalloc(sizeof(struct keypad_t), GFP_KERNEL);
+ if (!kp) {
+ err = -ENOMEM;
+ goto err_kzalloc;
+ }
+ platform_set_drvdata(pdev, kp);
+ kp = platform_get_drvdata(pdev);
+ if (!keypad_board) {
+ dev_err(&pdev->dev, "platform data not defined\n");
+ err = -1;
+ goto err_board;
+ }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "memory resources not defined\n");
+ err = -EINVAL;
+ goto err_board;
+ } else {
+ kp->ske_regs = ioremap(res->start, resource_size(res));
+ if (kp->ske_regs == NULL) {
+ dev_err(&pdev->dev, "alloc reg space failed\n");
+ err = -ENOMEM;
+ goto err_board;
+ }
+ }
+ kp->irq = platform_get_irq(pdev, 0);
+ if (!kp->irq) {
+ dev_err(&pdev->dev, "irq not defined\n");
+ err = -EINVAL;
+ goto err_board;
+ }
+ kp->clk = clk_get(&pdev->dev, NULL);
+ if (IS_ERR(kp->clk)) {
+ dev_err(&pdev->dev, "clk error\n");
+ err = PTR_ERR(kp->clk);
+ goto err_board;
+ } else {
+ clk_enable(kp->clk);
+ }
+ kp->board = keypad_board;
+ kp->inp_dev = input_allocate_device();
+ if (!kp->inp_dev) {
+ dev_err(&pdev->dev, "alloc memory failed\n");
+ err = -EINVAL;
+ goto err_inp_devalloc;
+ }
+ clear_bit(KPINTR_LKBIT, &kp->lockbits);
+ spin_lock_init(&kp->cr_lock);
+ err = ske_kp_init_keypad(kp);
+ if (err) {
+ dev_err(&pdev->dev, "init hardware failed\n");
+ goto err_init_kpd;
+ }
+ INIT_DELAYED_WORK(&kp->kscan_work, ske_kp_wq_kscan);
+ ske_kp_autoscan_en(kp);
+ err = request_irq(kp->irq, ske_kp_intrhandler,
+ kp->board->irqtype, "ske", kp);
+ if (err) {
+ dev_err(&pdev->dev, "allocate irq %d failed\n", kp->irq);
+ goto err_req_irq;
+ }
+ ske_kp_key_irqen(kp);
+
+ set_bit(EV_KEY, kp->inp_dev->evbit);
+ set_bit(EV_REP, kp->inp_dev->evbit);
+ kp->inp_dev->id.bustype = BUS_HOST;
+ kp->inp_dev->name = "ske-kp";
+ kp->inp_dev->phys = "ske-kp/input0";
+ kp->inp_dev->id.product = SKE_KEYPAD_VER_MAJOR;
+ kp->inp_dev->id.version = SKE_KEYPAD_VER_MINOR * 0x0ff + SKE_KEYPAD_VER_RELEASE;
+ kp->inp_dev->dev.parent = &pdev->dev;
+ kp->inp_dev->keycode = kp->board->kcode_tbl;
+ kp->inp_dev->keycodesize = sizeof(unsigned char);
+ kp->inp_dev->keycodemax = MAX_KEYS;
+ clear_bit(0, kp->inp_dev->keybit);
+ err = input_register_device(kp->inp_dev);
+ if (err) {
+ dev_err(&pdev->dev, "could not register input device\n");
+ err = -EINVAL;
+ goto err_inp_reg;
+ }
+ dev_dbg(&pdev->dev, "Module initialized Ver(%d.%d.%d)\n",
+ SKE_KEYPAD_VER_MAJOR, SKE_KEYPAD_VER_MINOR, SKE_KEYPAD_VER_RELEASE);
+ return 0;
+
+err_req_irq:
+ free_irq(kp->irq, kp);
+err_inp_reg:
+ input_unregister_device(kp->inp_dev);
+err_inp_devalloc:
+err_init_kpd:
+ input_free_device(kp->inp_dev);
+err_board:
+ kfree(kp);
+err_kzalloc:
+ return err;
+}
+
+/**
+ * ske_kp_remove - keypad module remove function
+ * @pdev: driver platform data
+ *
+ * Disables Keypad interrupt if any, frees allocated keypad interrupt if any,
+ * cancels keypad work queues if any, deallocate used GPIO pin, unregisters the
+ * module, frees used memory
+ */
+static int ske_kp_remove(struct platform_device *pdev)
+{
+ struct keypad_t *kp = platform_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "keypad remove start\n");
+ ske_kp_autoscan_disable(kp);
+ if (kp->board->exit)
+ kp->board->exit(kp);
+ free_irq(kp->irq, kp);
+ cancel_delayed_work(&kp->kscan_work);
+ cancel_delayed_work_sync(&kp->kscan_work);
+ input_unregister_device(kp->inp_dev);
+ input_free_device(kp->inp_dev);
+ kp->clk = 0;
+ kfree(kp);
+ dev_dbg(&pdev->dev, "keypad removed\n");
+ return 0;
+}
+
+struct platform_driver ske_driver = {
+ .probe = ske_kp_probe,
+ .remove = ske_kp_remove,
+ .driver = {
+ .name = "ske-kp",
+ },
+#ifdef CONFIG_PM
+ .suspend = ske_kp_suspend,
+ .resume = ske_kp_resume,
+#endif
+};
+
+static int __devinit ske_kp_init(void)
+{
+ return platform_driver_register(&ske_driver);
+}
+
+static void __exit ske_kp_exit(void)
+{
+ platform_driver_unregister(&ske_driver);
+}
+
+module_init(ske_kp_init);
+module_exit(ske_kp_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("NAVEEN KUMAR G");
+MODULE_DESCRIPTION("SKE keyboard driver");