Commit fb76dd10 authored by Luotao Fu's avatar Luotao Fu Committed by Dmitry Torokhov

Input: matrix_keypad - add support for clustered irq

This one adds support of a combined irq source for the whole matrix keypad.
This can be useful if all rows and columns of the keypad are e.g. connected
to a GPIO expander, which only has one interrupt line for all events on
every single GPIO.
Signed-off-by: default avatarLuotao Fu <l.fu@pengutronix.de>
Acked-by: default avatarEric Miao <eric.y.miao@gmail.com>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent 1719ec41
...@@ -37,6 +37,7 @@ struct matrix_keypad { ...@@ -37,6 +37,7 @@ struct matrix_keypad {
spinlock_t lock; spinlock_t lock;
bool scan_pending; bool scan_pending;
bool stopped; bool stopped;
bool gpio_all_disabled;
}; };
/* /*
...@@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad) ...@@ -87,8 +88,12 @@ static void enable_row_irqs(struct matrix_keypad *keypad)
const struct matrix_keypad_platform_data *pdata = keypad->pdata; const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i; int i;
if (pdata->clustered_irq > 0)
enable_irq(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++) for (i = 0; i < pdata->num_row_gpios; i++)
enable_irq(gpio_to_irq(pdata->row_gpios[i])); enable_irq(gpio_to_irq(pdata->row_gpios[i]));
}
} }
static void disable_row_irqs(struct matrix_keypad *keypad) static void disable_row_irqs(struct matrix_keypad *keypad)
...@@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad) ...@@ -96,8 +101,12 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
const struct matrix_keypad_platform_data *pdata = keypad->pdata; const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i; int i;
if (pdata->clustered_irq > 0)
disable_irq_nosync(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++) for (i = 0; i < pdata->num_row_gpios; i++)
disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
}
} }
/* /*
...@@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev) ...@@ -216,45 +225,69 @@ static void matrix_keypad_stop(struct input_dev *dev)
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int matrix_keypad_suspend(struct device *dev) static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
{ {
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
const struct matrix_keypad_platform_data *pdata = keypad->pdata; const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i; int i;
matrix_keypad_stop(keypad->input_dev); if (pdata->clustered_irq > 0) {
if (enable_irq_wake(pdata->clustered_irq) == 0)
keypad->gpio_all_disabled = true;
} else {
if (device_may_wakeup(&pdev->dev)) {
for (i = 0; i < pdata->num_row_gpios; i++) { for (i = 0; i < pdata->num_row_gpios; i++) {
if (!test_bit(i, keypad->disabled_gpios)) { if (!test_bit(i, keypad->disabled_gpios)) {
unsigned int gpio = pdata->row_gpios[i]; gpio = pdata->row_gpios[i];
if (enable_irq_wake(gpio_to_irq(gpio)) == 0) if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
__set_bit(i, keypad->disabled_gpios); __set_bit(i, keypad->disabled_gpios);
} }
} }
} }
return 0;
} }
static int matrix_keypad_resume(struct device *dev) static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
{ {
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
const struct matrix_keypad_platform_data *pdata = keypad->pdata; const struct matrix_keypad_platform_data *pdata = keypad->pdata;
unsigned int gpio;
int i; int i;
if (device_may_wakeup(&pdev->dev)) { if (pdata->clustered_irq > 0) {
if (keypad->gpio_all_disabled) {
disable_irq_wake(pdata->clustered_irq);
keypad->gpio_all_disabled = false;
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) { for (i = 0; i < pdata->num_row_gpios; i++) {
if (test_and_clear_bit(i, keypad->disabled_gpios)) { if (test_and_clear_bit(i, keypad->disabled_gpios)) {
unsigned int gpio = pdata->row_gpios[i]; gpio = pdata->row_gpios[i];
disable_irq_wake(gpio_to_irq(gpio)); disable_irq_wake(gpio_to_irq(gpio));
} }
} }
} }
}
static int matrix_keypad_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
matrix_keypad_stop(keypad->input_dev);
if (device_may_wakeup(&pdev->dev))
matrix_keypad_enable_wakeup(keypad);
return 0;
}
static int matrix_keypad_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct matrix_keypad *keypad = platform_get_drvdata(pdev);
if (device_may_wakeup(&pdev->dev))
matrix_keypad_disable_wakeup(keypad);
matrix_keypad_start(keypad->input_dev); matrix_keypad_start(keypad->input_dev);
...@@ -296,19 +329,33 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev, ...@@ -296,19 +329,33 @@ static int __devinit init_matrix_gpio(struct platform_device *pdev,
gpio_direction_input(pdata->row_gpios[i]); gpio_direction_input(pdata->row_gpios[i]);
} }
if (pdata->clustered_irq > 0) {
err = request_irq(pdata->clustered_irq,
matrix_keypad_interrupt,
pdata->clustered_irq_flags,
"matrix-keypad", keypad);
if (err) {
dev_err(&pdev->dev,
"Unable to acquire clustered interrupt\n");
goto err_free_rows;
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) { for (i = 0; i < pdata->num_row_gpios; i++) {
err = request_irq(gpio_to_irq(pdata->row_gpios[i]), err = request_irq(gpio_to_irq(pdata->row_gpios[i]),
matrix_keypad_interrupt, matrix_keypad_interrupt,
IRQF_DISABLED | IRQF_DISABLED |
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad); "matrix-keypad", keypad);
if (err) { if (err) {
dev_err(&pdev->dev, dev_err(&pdev->dev,
"Unable to acquire interrupt for GPIO line %i\n", "Unable to acquire interrupt "
"for GPIO line %i\n",
pdata->row_gpios[i]); pdata->row_gpios[i]);
goto err_free_irqs; goto err_free_irqs;
} }
} }
}
/* initialized as disabled - enabled by input->open */ /* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad); disable_row_irqs(keypad);
...@@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev) ...@@ -418,11 +465,16 @@ static int __devexit matrix_keypad_remove(struct platform_device *pdev)
device_init_wakeup(&pdev->dev, 0); device_init_wakeup(&pdev->dev, 0);
for (i = 0; i < pdata->num_row_gpios; i++) { if (pdata->clustered_irq > 0) {
free_irq(pdata->clustered_irq, keypad);
} else {
for (i = 0; i < pdata->num_row_gpios; i++)
free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
gpio_free(pdata->row_gpios[i]);
} }
for (i = 0; i < pdata->num_row_gpios; i++)
gpio_free(pdata->row_gpios[i]);
for (i = 0; i < pdata->num_col_gpios; i++) for (i = 0; i < pdata->num_col_gpios; i++)
gpio_free(pdata->col_gpios[i]); gpio_free(pdata->col_gpios[i]);
......
...@@ -41,6 +41,9 @@ struct matrix_keymap_data { ...@@ -41,6 +41,9 @@ struct matrix_keymap_data {
* @col_scan_delay_us: delay, measured in microseconds, that is * @col_scan_delay_us: delay, measured in microseconds, that is
* needed before we can keypad after activating column gpio * needed before we can keypad after activating column gpio
* @debounce_ms: debounce interval in milliseconds * @debounce_ms: debounce interval in milliseconds
* @clustered_irq: may be specified if interrupts of all row/column GPIOs
* are bundled to one single irq
* @clustered_irq_flags: flags that are needed for the clustered irq
* @active_low: gpio polarity * @active_low: gpio polarity
* @wakeup: controls whether the device should be set up as wakeup * @wakeup: controls whether the device should be set up as wakeup
* source * source
...@@ -63,6 +66,9 @@ struct matrix_keypad_platform_data { ...@@ -63,6 +66,9 @@ struct matrix_keypad_platform_data {
/* key debounce interval in milli-second */ /* key debounce interval in milli-second */
unsigned int debounce_ms; unsigned int debounce_ms;
unsigned int clustered_irq;
unsigned int clustered_irq_flags;
bool active_low; bool active_low;
bool wakeup; bool wakeup;
bool no_autorepeat; bool no_autorepeat;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment