2024-01-21 05:32:49

by Dmitry Torokhov

[permalink] [raw]
Subject: [PATCH 1/3] Input: matrix_keypad - avoid repeatedly converting GPIO to IRQ

gpio_to_irq() is getting more expensive and may require sleeping.
Convert row GPIOs to interrupt numbers once in probe() and use
this information when the driver needs to enable or disable given
interrupt line.

Signed-off-by: Dmitry Torokhov <[email protected]>
---
drivers/input/keyboard/matrix_keypad.c | 48 ++++++++++++++------------
1 file changed, 25 insertions(+), 23 deletions(-)

diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 50fa764c82d2..1cd1ffb80401 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -27,6 +27,7 @@ struct matrix_keypad {
const struct matrix_keypad_platform_data *pdata;
struct input_dev *input_dev;
unsigned int row_shift;
+ unsigned int row_irqs[MATRIX_MAX_ROWS];

DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);

@@ -92,7 +93,7 @@ static void enable_row_irqs(struct matrix_keypad *keypad)
enable_irq(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
- enable_irq(gpio_to_irq(pdata->row_gpios[i]));
+ enable_irq(keypad->row_irqs[i]);
}
}

@@ -105,7 +106,7 @@ static void disable_row_irqs(struct matrix_keypad *keypad)
disable_irq_nosync(pdata->clustered_irq);
else {
for (i = 0; i < pdata->num_row_gpios; i++)
- disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i]));
+ disable_irq_nosync(keypad->row_irqs[i]);
}
}

@@ -233,7 +234,6 @@ static void matrix_keypad_stop(struct input_dev *dev)
static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
- unsigned int gpio;
int i;

if (pdata->clustered_irq > 0) {
@@ -241,21 +241,16 @@ static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
keypad->gpio_all_disabled = true;
} else {

- for (i = 0; i < pdata->num_row_gpios; i++) {
- if (!test_bit(i, keypad->disabled_gpios)) {
- gpio = pdata->row_gpios[i];
-
- if (enable_irq_wake(gpio_to_irq(gpio)) == 0)
+ for (i = 0; i < pdata->num_row_gpios; i++)
+ if (!test_bit(i, keypad->disabled_gpios))
+ if (enable_irq_wake(keypad->row_irqs[i]) == 0)
__set_bit(i, keypad->disabled_gpios);
- }
- }
}
}

static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
- unsigned int gpio;
int i;

if (pdata->clustered_irq > 0) {
@@ -264,12 +259,9 @@ static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
keypad->gpio_all_disabled = false;
}
} else {
- for (i = 0; i < pdata->num_row_gpios; i++) {
- if (test_and_clear_bit(i, keypad->disabled_gpios)) {
- gpio = pdata->row_gpios[i];
- disable_irq_wake(gpio_to_irq(gpio));
- }
- }
+ for (i = 0; i < pdata->num_row_gpios; i++)
+ if (test_and_clear_bit(i, keypad->disabled_gpios))
+ disable_irq_wake(keypad->row_irqs[i]);
}
}

@@ -306,7 +298,7 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
struct matrix_keypad *keypad)
{
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
- int i, err;
+ int i, irq, err;

/* initialized strobe lines as outputs, activated */
for (i = 0; i < pdata->num_col_gpios; i++) {
@@ -345,11 +337,19 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
}
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
- err = request_any_context_irq(
- gpio_to_irq(pdata->row_gpios[i]),
+ irq = gpio_to_irq(pdata->row_gpios[i]);
+ if (irq < 0) {
+ err = irq;
+ dev_err(&pdev->dev,
+ "Unable to convert GPIO line %i to irq: %d\n",
+ pdata->row_gpios[i], err);
+ goto err_free_irqs;
+ }
+
+ err = request_any_context_irq(irq,
matrix_keypad_interrupt,
IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING,
+ IRQF_TRIGGER_FALLING,
"matrix-keypad", keypad);
if (err < 0) {
dev_err(&pdev->dev,
@@ -357,6 +357,8 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
pdata->row_gpios[i]);
goto err_free_irqs;
}
+
+ keypad->row_irqs[i] = irq;
}
}

@@ -366,7 +368,7 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,

err_free_irqs:
while (--i >= 0)
- free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad);
+ free_irq(keypad->row_irqs[i], keypad);
i = pdata->num_row_gpios;
err_free_rows:
while (--i >= 0)
@@ -388,7 +390,7 @@ static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
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(keypad->row_irqs[i], keypad);
}

for (i = 0; i < pdata->num_row_gpios; i++)
--
2.43.0.429.g432eaa2c6b-goog



2024-01-21 05:32:57

by Dmitry Torokhov

[permalink] [raw]
Subject: [PATCH 2/3] Input: matrix_keypad - consolidate handling of clustered interrupt

Now that the driver stores interrupt numbers corresponding to individual
GPIOs in non-clustered mode, it is possible to unify handling of both
modes by storing clustered interrupt at position 0 and setting the
number of interrupts in this case to 1.

Signed-off-by: Dmitry Torokhov <[email protected]>
---
drivers/input/keyboard/matrix_keypad.c | 63 ++++++++------------------
1 file changed, 20 insertions(+), 43 deletions(-)

diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 1cd1ffb80401..44ef600b8f19 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -27,9 +27,10 @@ struct matrix_keypad {
const struct matrix_keypad_platform_data *pdata;
struct input_dev *input_dev;
unsigned int row_shift;
- unsigned int row_irqs[MATRIX_MAX_ROWS];

- DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS);
+ unsigned int row_irqs[MATRIX_MAX_ROWS];
+ unsigned int num_row_irqs;
+ DECLARE_BITMAP(wakeup_enabled_irqs, MATRIX_MAX_ROWS);

uint32_t last_key_state[MATRIX_MAX_COLS];
struct delayed_work work;
@@ -86,28 +87,18 @@ static bool row_asserted(const struct matrix_keypad_platform_data *pdata,

static void enable_row_irqs(struct matrix_keypad *keypad)
{
- const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;

- if (pdata->clustered_irq > 0)
- enable_irq(pdata->clustered_irq);
- else {
- for (i = 0; i < pdata->num_row_gpios; i++)
- enable_irq(keypad->row_irqs[i]);
- }
+ for (i = 0; i < keypad->num_row_irqs; i++)
+ enable_irq(keypad->row_irqs[i]);
}

static void disable_row_irqs(struct matrix_keypad *keypad)
{
- const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;

- if (pdata->clustered_irq > 0)
- disable_irq_nosync(pdata->clustered_irq);
- else {
- for (i = 0; i < pdata->num_row_gpios; i++)
- disable_irq_nosync(keypad->row_irqs[i]);
- }
+ for (i = 0; i < keypad->num_row_irqs; i++)
+ disable_irq_nosync(keypad->row_irqs[i]);
}

/*
@@ -233,35 +224,20 @@ static void matrix_keypad_stop(struct input_dev *dev)

static void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad)
{
- const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;

- if (pdata->clustered_irq > 0) {
- if (enable_irq_wake(pdata->clustered_irq) == 0)
- keypad->gpio_all_disabled = true;
- } else {
-
- for (i = 0; i < pdata->num_row_gpios; i++)
- if (!test_bit(i, keypad->disabled_gpios))
- if (enable_irq_wake(keypad->row_irqs[i]) == 0)
- __set_bit(i, keypad->disabled_gpios);
- }
+ for_each_clear_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs)
+ if (enable_irq_wake(keypad->row_irqs[i]) == 0)
+ __set_bit(i, keypad->wakeup_enabled_irqs);
}

static void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad)
{
- const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;

- 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++)
- if (test_and_clear_bit(i, keypad->disabled_gpios))
- disable_irq_wake(keypad->row_irqs[i]);
+ for_each_set_bit(i, keypad->wakeup_enabled_irqs, keypad->num_row_irqs) {
+ disable_irq_wake(keypad->row_irqs[i]);
+ __clear_bit(i, keypad->wakeup_enabled_irqs);
}
}

@@ -335,6 +311,9 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
"Unable to acquire clustered interrupt\n");
goto err_free_rows;
}
+
+ keypad->row_irqs[0] = pdata->clustered_irq;
+ keypad->num_row_irqs = 1;
} else {
for (i = 0; i < pdata->num_row_gpios; i++) {
irq = gpio_to_irq(pdata->row_gpios[i]);
@@ -360,6 +339,8 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,

keypad->row_irqs[i] = irq;
}
+
+ keypad->num_row_irqs = pdata->num_row_gpios;
}

/* initialized as disabled - enabled by input->open */
@@ -386,12 +367,8 @@ static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
const struct matrix_keypad_platform_data *pdata = keypad->pdata;
int i;

- if (pdata->clustered_irq > 0) {
- free_irq(pdata->clustered_irq, keypad);
- } else {
- for (i = 0; i < pdata->num_row_gpios; i++)
- free_irq(keypad->row_irqs[i], keypad);
- }
+ for (i = 0; i < keypad->num_row_irqs; i++)
+ free_irq(keypad->row_irqs[i], keypad);

for (i = 0; i < pdata->num_row_gpios; i++)
gpio_free(pdata->row_gpios[i]);
--
2.43.0.429.g432eaa2c6b-goog


2024-01-21 05:33:13

by Dmitry Torokhov

[permalink] [raw]
Subject: [PATCH 3/3] Input: matrix_keypad - switch to using managed resources

Switch the drivers to use managed resources (devm) to simplify error
handling and remove the need to have remove() implementation.

Signed-off-by: Dmitry Torokhov <[email protected]>
---
drivers/input/keyboard/matrix_keypad.c | 89 +++++++-------------------
1 file changed, 24 insertions(+), 65 deletions(-)

diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c
index 44ef600b8f19..695c03e075b5 100644
--- a/drivers/input/keyboard/matrix_keypad.c
+++ b/drivers/input/keyboard/matrix_keypad.c
@@ -278,38 +278,41 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,

/* initialized strobe lines as outputs, activated */
for (i = 0; i < pdata->num_col_gpios; i++) {
- err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col");
+ err = devm_gpio_request(&pdev->dev,
+ pdata->col_gpios[i], "matrix_kbd_col");
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO%d for COL%d\n",
pdata->col_gpios[i], i);
- goto err_free_cols;
+ return err;
}

gpio_direction_output(pdata->col_gpios[i], !pdata->active_low);
}

for (i = 0; i < pdata->num_row_gpios; i++) {
- err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row");
+ err = devm_gpio_request(&pdev->dev,
+ pdata->row_gpios[i], "matrix_kbd_row");
if (err) {
dev_err(&pdev->dev,
"failed to request GPIO%d for ROW%d\n",
pdata->row_gpios[i], i);
- goto err_free_rows;
+ return err;
}

gpio_direction_input(pdata->row_gpios[i]);
}

if (pdata->clustered_irq > 0) {
- err = request_any_context_irq(pdata->clustered_irq,
+ err = devm_request_any_context_irq(&pdev->dev,
+ pdata->clustered_irq,
matrix_keypad_interrupt,
pdata->clustered_irq_flags,
"matrix-keypad", keypad);
if (err < 0) {
dev_err(&pdev->dev,
"Unable to acquire clustered interrupt\n");
- goto err_free_rows;
+ return err;
}

keypad->row_irqs[0] = pdata->clustered_irq;
@@ -322,10 +325,11 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
dev_err(&pdev->dev,
"Unable to convert GPIO line %i to irq: %d\n",
pdata->row_gpios[i], err);
- goto err_free_irqs;
+ return err;
}

- err = request_any_context_irq(irq,
+ err = devm_request_any_context_irq(&pdev->dev,
+ irq,
matrix_keypad_interrupt,
IRQF_TRIGGER_RISING |
IRQF_TRIGGER_FALLING,
@@ -334,7 +338,7 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,
dev_err(&pdev->dev,
"Unable to acquire interrupt for GPIO line %i\n",
pdata->row_gpios[i]);
- goto err_free_irqs;
+ return err;
}

keypad->row_irqs[i] = irq;
@@ -345,36 +349,8 @@ static int matrix_keypad_init_gpio(struct platform_device *pdev,

/* initialized as disabled - enabled by input->open */
disable_row_irqs(keypad);
- return 0;
-
-err_free_irqs:
- while (--i >= 0)
- free_irq(keypad->row_irqs[i], keypad);
- i = pdata->num_row_gpios;
-err_free_rows:
- while (--i >= 0)
- gpio_free(pdata->row_gpios[i]);
- i = pdata->num_col_gpios;
-err_free_cols:
- while (--i >= 0)
- gpio_free(pdata->col_gpios[i]);
-
- return err;
-}
-
-static void matrix_keypad_free_gpio(struct matrix_keypad *keypad)
-{
- const struct matrix_keypad_platform_data *pdata = keypad->pdata;
- int i;
-
- for (i = 0; i < keypad->num_row_irqs; i++)
- free_irq(keypad->row_irqs[i], keypad);
-
- for (i = 0; i < pdata->num_row_gpios; i++)
- gpio_free(pdata->row_gpios[i]);

- for (i = 0; i < pdata->num_col_gpios; i++)
- gpio_free(pdata->col_gpios[i]);
+ return 0;
}

#ifdef CONFIG_OF
@@ -473,12 +449,13 @@ static int matrix_keypad_probe(struct platform_device *pdev)
return -EINVAL;
}

- keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!keypad || !input_dev) {
- err = -ENOMEM;
- goto err_free_mem;
- }
+ keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
+ if (!keypad)
+ return -ENOMEM;
+
+ input_dev = devm_input_allocate_device(&pdev->dev);
+ if (!input_dev)
+ return -ENOMEM;

keypad->input_dev = input_dev;
keypad->pdata = pdata;
@@ -489,7 +466,6 @@ static int matrix_keypad_probe(struct platform_device *pdev)

input_dev->name = pdev->name;
input_dev->id.bustype = BUS_HOST;
- input_dev->dev.parent = &pdev->dev;
input_dev->open = matrix_keypad_start;
input_dev->close = matrix_keypad_stop;

@@ -499,7 +475,7 @@ static int matrix_keypad_probe(struct platform_device *pdev)
NULL, input_dev);
if (err) {
dev_err(&pdev->dev, "failed to build keymap\n");
- goto err_free_mem;
+ return -ENOMEM;
}

if (!pdata->no_autorepeat)
@@ -509,32 +485,16 @@ static int matrix_keypad_probe(struct platform_device *pdev)

err = matrix_keypad_init_gpio(pdev, keypad);
if (err)
- goto err_free_mem;
+ return err;

err = input_register_device(keypad->input_dev);
if (err)
- goto err_free_gpio;
+ return err;

device_init_wakeup(&pdev->dev, pdata->wakeup);
platform_set_drvdata(pdev, keypad);

return 0;
-
-err_free_gpio:
- matrix_keypad_free_gpio(keypad);
-err_free_mem:
- input_free_device(input_dev);
- kfree(keypad);
- return err;
-}
-
-static void matrix_keypad_remove(struct platform_device *pdev)
-{
- struct matrix_keypad *keypad = platform_get_drvdata(pdev);
-
- matrix_keypad_free_gpio(keypad);
- input_unregister_device(keypad->input_dev);
- kfree(keypad);
}

#ifdef CONFIG_OF
@@ -547,7 +507,6 @@ MODULE_DEVICE_TABLE(of, matrix_keypad_dt_match);

static struct platform_driver matrix_keypad_driver = {
.probe = matrix_keypad_probe,
- .remove_new = matrix_keypad_remove,
.driver = {
.name = "matrix-keypad",
.pm = pm_sleep_ptr(&matrix_keypad_pm_ops),
--
2.43.0.429.g432eaa2c6b-goog


2024-01-22 08:26:46

by Bartosz Golaszewski

[permalink] [raw]
Subject: Re: [PATCH 1/3] Input: matrix_keypad - avoid repeatedly converting GPIO to IRQ

On Sun, 21 Jan 2024 at 06:32, Dmitry Torokhov <[email protected]> wrote:
>
> gpio_to_irq() is getting more expensive and may require sleeping.

This is no longer true, the relevant commit was reverted and I have
now a working series where I'm using SRCU for lockless list traversal
so if anything it will actually become cheaper.

> Convert row GPIOs to interrupt numbers once in probe() and use
> this information when the driver needs to enable or disable given
> interrupt line.

That being said, I'm not against this change, though this may not be
very useful after all.

Bartosz

[snip!]

2024-01-31 18:49:24

by Dmitry Torokhov

[permalink] [raw]
Subject: Re: [PATCH 1/3] Input: matrix_keypad - avoid repeatedly converting GPIO to IRQ

On Mon, Jan 22, 2024 at 09:19:56AM +0100, Bartosz Golaszewski wrote:
> On Sun, 21 Jan 2024 at 06:32, Dmitry Torokhov <[email protected]> wrote:
> >
> > gpio_to_irq() is getting more expensive and may require sleeping.
>
> This is no longer true, the relevant commit was reverted and I have
> now a working series where I'm using SRCU for lockless list traversal
> so if anything it will actually become cheaper.
>
> > Convert row GPIOs to interrupt numbers once in probe() and use
> > this information when the driver needs to enable or disable given
> > interrupt line.
>
> That being said, I'm not against this change, though this may not be
> very useful after all.

OK, then I'll tone down the patch description and apply.

Thanks.

--
Dmitry