summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl-Johan Perntz <karl-johan.perntz@stericsson.com>2011-09-29 13:22:20 +0530
committerPhilippe Langlais <philippe.langlais@stericsson.com>2012-05-22 11:03:36 +0200
commit44808732559fdd0e684be86c8f8004570a43b237 (patch)
tree7e6fb668df4cf85ddc3b71f482637dd7c0d96b57
parent84b09a06a77caebbf9e367cb02e4c5e53ca1638a (diff)
input: Report all key actions in SKE autoscan mode
Driver now use a delayed work to poll the autoscan registers after a key press interrupt. Polling is performed until all keys are released. Solution was chosen since the hardware does not clear the KPRISA interrupt bit while a key is pressed, making detection of a second key action impossible. ST-Ericsson ID: 361135 ST-Ericsson Linux next: Not tested ST-Ericsson FOSS-OUT ID: Trivial Change-Id:Ic258f380ed5a9df78bce68362d4d2ca3e18a9cf1 Signed-off-by: Karl-Johan Perntz <karl-johan.perntz@stericsson.com> Reviewed-on: http://gerrit.lud.stericsson.com/gerrit/32458 Reviewed-by: Naga RADHESH Y <naga.radheshy@stericsson.com> Tested-by: Naga RADHESH Y <naga.radheshy@stericsson.com> Reviewed-by: Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com>
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c195
1 files changed, 144 insertions, 51 deletions
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
index b86d44cd5ad..e791b16c6b1 100644
--- a/drivers/input/keyboard/nomadik-ske-keypad.c
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -1,4 +1,4 @@
-/*
+/**
* Copyright (C) ST-Ericsson SA 2010
*
* Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
@@ -53,26 +53,34 @@
#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
- * @irq: irq no
- * @reg_base: ske regsiters base address
- * @input: pointer to input device object
- * @board: keypad platform device
- * @keymap: matrix scan code table for keycodes
- * @clk: clock structure pointer
- * @enable: flag to enable the driver event
- * @regulator: pointer to the regulator used for ske kyepad
- * @gpio_input_irq: array for gpio irqs
- * @key_pressed: hold the key state
- * @work: delayed work variable for gpio switch
- * @ske_rows: rows gpio array for ske
- * @ske_cols: columns gpio array for ske
- * @gpio_row: gpio row
- * @gpio_col: gpio column
- * @gpio_work: delayed work variable for release gpio key
+ * @dev: Pointer to the structure device
+ * @irq: irq no
+ * @reg_base: ske regsiters base address
+ * @input: pointer to input device object
+ * @board: keypad platform device
+ * @keymap: matrix scan code table for keycodes
+ * @clk: clock structure pointer
+ * @enable: flag to enable the driver event
+ * @regulator: pointer to the regulator used for ske kyepad
+ * @gpio_input_irq: array for gpio irqs
+ * @key_pressed: hold the key state
+ * @work: delayed work variable for gpio switch
+ * @ske_rows: rows gpio array for ske
+ * @ske_cols: columns gpio array for ske
+ * @gpio_row: gpio row
+ * @gpio_col: gpio column
+ * @gpio_work: delayed work variable for release gpio key
+ * @keys: matrix holding key status
+ * @scan_work: delayed work for scaning new key actions
*/
struct ske_keypad {
+ struct device *dev;
int irq;
void __iomem *reg_base;
struct input_dev *input;
@@ -90,6 +98,8 @@ struct ske_keypad {
int gpio_row;
int gpio_col;
struct delayed_work gpio_work;
+ 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,
@@ -107,7 +117,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
spin_unlock(&keypad->ske_keypad_lock);
}
-/*
+/**
* ske_keypad_chip_init: init keypad controller configuration
*
* Enable Multi key press detection, auto scan mode
@@ -124,7 +134,7 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
if (!timeout)
return -EINVAL;
- /*
+ /**
* set debounce value
* keypad dbounce is configured in DBCR[15:8]
* dbounce value in steps of 32/32.768 ms
@@ -139,7 +149,7 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
/* enable multi key detection */
ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
- /*
+ /**
* set up the number of columns
* KPCN[5:3] defines no. of keypad columns to be auto scanned
*/
@@ -163,6 +173,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable)
int i;
if (!enable) {
+ dev_dbg(keypad->dev, "%s disable keypad\n", __func__);
writel(0, keypad->reg_base + SKE_CR);
if (keypad->board->exit)
keypad->board->exit();
@@ -173,6 +184,7 @@ static void ske_mode_enable(struct ske_keypad *keypad, bool enable)
clk_disable(keypad->clk);
regulator_disable(keypad->regulator);
} else {
+ dev_dbg(keypad->dev, "%s enable keypad\n", __func__);
regulator_enable(keypad->regulator);
clk_enable(keypad->clk);
for (i = 0; i < keypad->board->krow; i++) {
@@ -239,7 +251,6 @@ static struct attribute_group ske_attr_group = {
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 num_of_rows;
@@ -254,10 +265,13 @@ static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
ske_ris = readl(keypad->reg_base + SKE_RIS);
keypad->key_pressed = ske_ris & SKE_KPRISA;
- input_event(input, EV_MSC, MSC_SCAN, code);
- input_report_key(input, keypad->keymap[code],
- keypad->key_pressed);
- input_sync(input);
+ 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;
+
num_of_rows--;
} while (num_of_rows);
}
@@ -268,7 +282,7 @@ static void ske_keypad_read_data(struct ske_keypad *keypad)
int col = 0;
int ske_asr, i;
- /*
+ /**
* Read the auto scan registers
*
* Each SKE_ASRx (x=0 to x=3) contains two row values.
@@ -293,26 +307,95 @@ static void ske_keypad_read_data(struct ske_keypad *keypad)
}
}
}
-static void ske_keypad_scan(struct ske_keypad *keypad)
-{
- 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);
+static void ske_keypad_scan_work(struct work_struct *work)
+{
+ 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 50 ms */
+ schedule_delayed_work(&keypad->scan_work,
+ msecs_to_jiffies(50));
+ } else {
+ /* For safty measure, clear interrupt once more */
+ ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
- /* enable auto scan interrupts */
- ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
+ /* 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);
+
+ /**
+ * Schedule the work queue to change it to GPIO mode
+ * if there is no activity in SKE mode
+ */
+ if (!keypad->key_pressed && keypad->enable)
+ schedule_delayed_work(&keypad->work,
+ keypad->board->switch_delay);
+ }
}
static void ske_gpio_switch_work(struct work_struct *work)
@@ -333,6 +416,10 @@ static void ske_gpio_release_work(struct work_struct *work)
code = MATRIX_SCAN_CODE(keypad->gpio_row, keypad->gpio_col,
SKE_KEYPAD_ROW_SHIFT);
+
+ dev_dbg(keypad->dev, "%s Key press reported, code:%d\n",
+ __func__, code);
+
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], 1);
input_sync(input);
@@ -366,7 +453,7 @@ static void ske_set_cols(struct ske_keypad *keypad, int col)
int i ;
int value;
- /*
+ /**
* Set all columns except the requested column
* output pin as high
*/
@@ -419,10 +506,16 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id)
if (!keypad->enable) {
keypad->enable = true;
ske_mode_enable(keypad, true);
+ /**
+ * Schedule the work queue to change it back to GPIO
+ * mode if there is no activity in SKE mode
+ */
+ schedule_delayed_work(&keypad->work,
+ keypad->board->switch_delay);
}
- /*
- * Schedule the work queue to change it to
- * report the key pressed, if it is not detected in SKE mode.
+ /**
+ * Schedule delayed work to report key press if it is not
+ * detected in SKE mode.
*/
if (keypad->key_pressed)
schedule_delayed_work(&keypad->gpio_work,
@@ -434,18 +527,14 @@ static irqreturn_t ske_keypad_gpio_irq(int irq, void *dev_id)
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
{
struct ske_keypad *keypad = dev_id;
-
cancel_delayed_work_sync(&keypad->gpio_work);
cancel_delayed_work_sync(&keypad->work);
- ske_keypad_scan(keypad);
- /*
- * Schedule the work queue to change it to
- * GPIO mode, if there is no activity in SKE mode
- */
- if (!keypad->key_pressed && keypad->enable)
- schedule_delayed_work(&keypad->work,
- keypad->board->switch_delay);
+ /* 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;
}
@@ -505,6 +594,7 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
goto out_freeclk;
}
+ keypad->dev = &pdev->dev;
input = input_allocate_device();
if (!input) {
@@ -557,6 +647,7 @@ static int __init ske_keypad_probe(struct platform_device *pdev)
keypad->clk = clk;
INIT_DELAYED_WORK(&keypad->work, ske_gpio_switch_work);
INIT_DELAYED_WORK(&keypad->gpio_work, ske_gpio_release_work);
+ INIT_DELAYED_WORK(&keypad->scan_work, ske_keypad_scan_work);
/* allocations are sane, we begin HW initialization */
clk_enable(keypad->clk);
@@ -660,6 +751,7 @@ static int __devexit ske_keypad_remove(struct platform_device *pdev)
cancel_delayed_work_sync(&keypad->gpio_work);
cancel_delayed_work_sync(&keypad->work);
+ cancel_delayed_work_sync(&keypad->scan_work);
free_irq(keypad->irq, keypad);
input_unregister_device(keypad->input);
@@ -692,6 +784,7 @@ static int ske_keypad_suspend(struct device *dev)
else {
cancel_delayed_work_sync(&keypad->gpio_work);
cancel_delayed_work_sync(&keypad->work);
+ cancel_delayed_work_sync(&keypad->scan_work);
disable_irq(irq);
if (keypad->enable) {
ske_mode_enable(keypad, false);