@@ -22,6 +22,9 @@
#define SKE_MIS 0x18
#define SKE_ICR 0x1C
+#define SKE_KPD_MAX_ROWS 8
+#define SKE_KPD_MAX_COLS 8
+
/*
* Keypad module
*/
@@ -12,6 +12,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/workqueue.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -50,6 +51,10 @@
#define SKE_NUM_ASRX_REGISTERS (4)
#define KEY_PRESSED_DELAY 10
+
+#define KEY_REPORTED 1
+#define KEY_PRESSED 2
+
/**
* struct ske_keypad - data structure used by keypad driver
* @dev: Pointer to the structure device
@@ -60,6 +65,9 @@
* @keymap: matrix scan code table for keycodes
* @clk: clock structure pointer
* @ske_keypad_lock: lock used while writting into registers
+ * @key_pressed: hold the key state
+ * @keys: matrix holding key status
+ * @scan_work: delayed work for scaning new key actions
*/
struct ske_keypad {
struct device *dev;
@@ -70,6 +78,9 @@ struct ske_keypad {
unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
struct clk *clk;
spinlock_t ske_keypad_lock;
+ int key_pressed;
+ u8 keys[SKE_KPD_MAX_ROWS][SKE_KPD_MAX_COLS];
+ struct delayed_work scan_work;
};
static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
@@ -141,9 +152,7 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
{
int row = 0, code, pos;
- struct input_dev *input = keypad->input;
u32 ske_ris;
- int key_pressed;
int num_of_rows;
/* find out the row */
@@ -155,11 +164,15 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
ske_ris = readl(keypad->reg_base + SKE_RIS);
- key_pressed = ske_ris & SKE_KPRISA;
+ keypad->key_pressed = ske_ris & SKE_KPRISA;
+
+ dev_dbg(keypad->dev,
+ "%s key_pressed:%d code:%d row:%d col:%d\n",
+ __func__, keypad->key_pressed, code, row, col);
+
+ if (keypad->key_pressed)
+ keypad->keys[row][col] |= KEY_PRESSED;
- input_event(input, EV_MSC, MSC_SCAN, code);
- input_report_key(input, keypad->keymap[code], key_pressed);
- input_sync(input);
num_of_rows--;
} while (num_of_rows);
}
@@ -196,27 +209,101 @@ static void ske_keypad_read_data(struct ske_keypad *keypad)
}
}
-static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+static void ske_keypad_scan_work(struct work_struct *work)
{
- struct ske_keypad *keypad = dev_id;
- int timeout = keypad->board->debounce_ms;
-
- /* disable auto scan interrupt; mask the interrupt generated */
- ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
- ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+ int timeout = 10;
+ int i, j, code;
+ struct ske_keypad *keypad = container_of(work,
+ struct ske_keypad, scan_work.work);
+ struct input_dev *input = keypad->input;
- while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
+ /* Wait for autoscan to complete */
+ while (readl(keypad->reg_base + SKE_CR) & SKE_KPASON)
cpu_relax();
/* SKEx registers are stable and can be read */
ske_keypad_read_data(keypad);
- /* wait until raw interrupt is clear */
- while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
- msleep(KEY_PRESSED_DELAY);
+ /* Check for key actions */
+ for (i = 0; i < SKE_KPD_MAX_ROWS; i++) {
+ for (j = 0; j < SKE_KPD_MAX_COLS; j++) {
+ switch (keypad->keys[i][j]) {
+ case KEY_REPORTED:
+ /**
+ * Key was reported but is no longer pressed,
+ * report it as released.
+ */
+ code = MATRIX_SCAN_CODE(i, j,
+ SKE_KEYPAD_ROW_SHIFT);
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code],
+ 0);
+ input_sync(input);
+ keypad->keys[i][j] = 0;
+ dev_dbg(keypad->dev,
+ "%s Key release reported, code:%d\n",
+ __func__, code);
+ break;
+ case KEY_PRESSED:
+ /* Key pressed but not yet reported, report */
+ code = MATRIX_SCAN_CODE(i, j,
+ SKE_KEYPAD_ROW_SHIFT);
+ input_event(input, EV_MSC, MSC_SCAN, code);
+ input_report_key(input, keypad->keymap[code],
+ 1);
+ input_sync(input);
+ dev_dbg(keypad->dev,
+ "%s Key press reported, code:%d\n",
+ __func__, code);
+ /* Intentional fall though */
+ case (KEY_REPORTED | KEY_PRESSED):
+ /**
+ * Key pressed and reported, just reset
+ * KEY_PRESSED for next scan
+ */
+ keypad->keys[i][j] = KEY_REPORTED;
+ break;
+ }
+ }
+ }
+
+ if (keypad->key_pressed) {
+ /*
+ * Key still pressed, schedule work to poll changes in 100 ms
+ * After increasing the delay from 50 to 100 it is taking
+ * 2% to 3% load on average.
+ */
+ schedule_delayed_work(&keypad->scan_work,
+ msecs_to_jiffies(100));
+ } else {
+ /* For safety measures, clear interrupt once more */
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+ /* Wait for raw interrupt to clear */
+ while ((readl(keypad->reg_base + SKE_RIS) & SKE_KPRISA) &&
+ --timeout) {
+ udelay(10);
+ }
+
+ if (!timeout)
+ dev_err(keypad->dev,
+ "%s Timeed out waiting on irq to clear\n",
+ __func__);
- /* enable auto scan interrupts */
- ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+ /* Enable auto scan interrupts */
+ ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+ }
+}
+
+static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
+{
+ struct ske_keypad *keypad = dev_id;
+
+ /* disable auto scan interrupt; mask the interrupt generated */
+ ske_keypad_set_bits(keypad, SKE_IMSC, SKE_KPIMA, 0x0);
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
+
+ schedule_delayed_work(&keypad->scan_work, 0);
return IRQ_HANDLED;
}
@@ -314,6 +401,7 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
keypad->input = input;
keypad->reg_base = reg_base;
keypad->clk = clk;
+ INIT_DELAYED_WORK(&keypad->scan_work, ske_keypad_scan_work);
/* allocations are sane, we begin HW initialization */
clk_enable(keypad->clk);
@@ -324,8 +412,8 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
goto out_unregisterinput;
}
- ret = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
- IRQF_ONESHOT, "ske-keypad", keypad);
+ ret = request_irq(keypad->irq, ske_keypad_irq, 0,
+ "ske-keypad", keypad);
if (ret) {
dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
goto out_unregisterinput;
@@ -360,8 +448,9 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev)
struct ske_keypad *keypad = platform_get_drvdata(pdev);
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- input_unregister_device(keypad->input);
+ cancel_delayed_work_sync(&keypad->scan_work);
+ input_unregister_device(keypad->input);
clk_disable(keypad->clk);
clk_put(keypad->clk);
@@ -385,6 +474,7 @@ static int ske_keypad_suspend(struct device *dev)
if (device_may_wakeup(dev))
enable_irq_wake(irq);
else {
+ cancel_delayed_work_sync(&keypad->scan_work);
disable_irq(irq);
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
clk_disable(keypad->clk);