Re: [PATCH v4 01/10] input: keyboard: adp5588-keys: support gpi key events as 'gpio keys'
From: Andy Shevchenko
Date: Mon Aug 29 2022 - 10:19:28 EST
On Mon, Aug 29, 2022 at 4:15 PM Nuno Sá <nuno.sa@xxxxxxxxxx> wrote:
>
> This change replaces the support for GPIs as key event generators.
> Instead of reporting the events directly, we add a gpio based irqchip
> so that these events can be consumed by keys defined in the gpio-keys
> driver (as it's goal is indeed for keys on GPIOs capable of generating
> interrupts). With this, the gpio-adp5588 driver can also be dropped.
>
> The basic idea is that all the pins that are not being used as part of
> the keymap matrix can be possibly requested as GPIOs by gpio-keys
> (it's also fine to use these pins as plain interrupts though that's not
> really the point).
>
> Since the gpiochip now also has irqchip capabilities, we should only
> remove it after we free the device interrupt (otherwise we could, in
> theory, be handling GPIs interrupts while the gpiochip is concurrently
> removed). Thus the call 'adp5588_gpio_add()' is moved and since the
> setup phase also needs to come before making the gpios visible, we also
> need to move 'adp5588_setup()'.
>
> While at it, always select GPIOLIB so that we don't need to use #ifdef
> guards.
Fine with me from GPIO chip (incl. IRQ one) perspective,
Reviewed-by: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>
> Signed-off-by: Nuno Sá <nuno.sa@xxxxxxxxxx>
> ---
> drivers/input/keyboard/Kconfig | 2 +
> drivers/input/keyboard/adp5588-keys.c | 274 +++++++++++++-------------
> include/linux/platform_data/adp5588.h | 2 -
> 3 files changed, 144 insertions(+), 134 deletions(-)
>
> diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
> index a20ee693b22b..ca5cd5e520a7 100644
> --- a/drivers/input/keyboard/Kconfig
> +++ b/drivers/input/keyboard/Kconfig
> @@ -40,6 +40,8 @@ config KEYBOARD_ADP5520
> config KEYBOARD_ADP5588
> tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander"
> depends on I2C
> + select GPIOLIB
> + select GPIOLIB_IRQCHIP
> help
> Say Y here if you want to use a ADP5588/87 attached to your
> system I2C bus.
> diff --git a/drivers/input/keyboard/adp5588-keys.c b/drivers/input/keyboard/adp5588-keys.c
> index e2719737360a..f5f7ddfe68be 100644
> --- a/drivers/input/keyboard/adp5588-keys.c
> +++ b/drivers/input/keyboard/adp5588-keys.c
> @@ -40,21 +40,21 @@
> #define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
> #define WA_DELAYED_READOUT_TIME 25
>
> +#define ADP5588_INVALID_HWIRQ (~0UL)
> +
> struct adp5588_kpad {
> struct i2c_client *client;
> struct input_dev *input;
> ktime_t irq_time;
> unsigned long delay;
> unsigned short keycode[ADP5588_KEYMAPSIZE];
> - const struct adp5588_gpi_map *gpimap;
> - unsigned short gpimapsize;
> -#ifdef CONFIG_GPIOLIB
> unsigned char gpiomap[ADP5588_MAXGPIO];
> struct gpio_chip gc;
> struct mutex gpio_lock; /* Protect cached dir, dat_out */
> u8 dat_out[3];
> u8 dir[3];
> -#endif
> + u8 int_en[3];
> + u8 irq_mask[3];
> };
>
> static int adp5588_read(struct i2c_client *client, u8 reg)
> @@ -72,7 +72,6 @@ static int adp5588_write(struct i2c_client *client, u8 reg, u8 val)
> return i2c_smbus_write_byte_data(client, reg, val);
> }
>
> -#ifdef CONFIG_GPIOLIB
> static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
> {
> struct adp5588_kpad *kpad = gpiochip_get_data(chip);
> @@ -171,9 +170,6 @@ static int adp5588_build_gpiomap(struct adp5588_kpad *kpad,
> for (i = 0; i < pdata->cols; i++)
> pin_used[i + GPI_PIN_COL_BASE - GPI_PIN_BASE] = true;
>
> - for (i = 0; i < kpad->gpimapsize; i++)
> - pin_used[kpad->gpimap[i].pin - GPI_PIN_BASE] = true;
> -
> for (i = 0; i < ADP5588_MAXGPIO; i++)
> if (!pin_used[i])
> kpad->gpiomap[n_unused++] = i;
> @@ -196,11 +192,79 @@ static void adp5588_gpio_do_teardown(void *_kpad)
> dev_warn(&kpad->client->dev, "teardown failed %d\n", error);
> }
>
> +static void adp5588_irq_bus_lock(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + struct adp5588_kpad *kpad = gpiochip_get_data(gc);
> +
> + mutex_lock(&kpad->gpio_lock);
> +}
> +
> +static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + struct adp5588_kpad *kpad = gpiochip_get_data(gc);
> + int i;
> +
> + for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
> + if (kpad->int_en[i] ^ kpad->irq_mask[i]) {
> + kpad->int_en[i] = kpad->irq_mask[i];
> + adp5588_write(kpad->client, GPI_EM1 + i, kpad->int_en[i]);
> + }
> + }
> +
> + mutex_unlock(&kpad->gpio_lock);
> +}
> +
> +static void adp5588_irq_mask(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + struct adp5588_kpad *kpad = gpiochip_get_data(gc);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> + unsigned long real_irq = kpad->gpiomap[hwirq];
> +
> + kpad->irq_mask[ADP5588_BANK(real_irq)] &= ~ADP5588_BIT(real_irq);
> + gpiochip_disable_irq(gc, hwirq);
> +}
> +
> +static void adp5588_irq_unmask(struct irq_data *d)
> +{
> + struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
> + struct adp5588_kpad *kpad = gpiochip_get_data(gc);
> + irq_hw_number_t hwirq = irqd_to_hwirq(d);
> + unsigned long real_irq = kpad->gpiomap[hwirq];
> +
> + gpiochip_enable_irq(gc, hwirq);
> + kpad->irq_mask[ADP5588_BANK(real_irq)] |= ADP5588_BIT(real_irq);
> +}
> +
> +static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
> +{
> + if (!(type & IRQ_TYPE_EDGE_BOTH))
> + return -EINVAL;
> +
> + irq_set_handler_locked(d, handle_edge_irq);
> +
> + return 0;
> +}
> +
> +static const struct irq_chip adp5588_irq_chip = {
> + .name = "adp5588",
> + .irq_mask = adp5588_irq_mask,
> + .irq_unmask = adp5588_irq_unmask,
> + .irq_bus_lock = adp5588_irq_bus_lock,
> + .irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
> + .irq_set_type = adp5588_irq_set_type,
> + .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_IMMUTABLE,
> + GPIOCHIP_IRQ_RESOURCE_HELPERS,
> +};
> +
> static int adp5588_gpio_add(struct adp5588_kpad *kpad)
> {
> struct device *dev = &kpad->client->dev;
> const struct adp5588_kpad_platform_data *pdata = dev_get_platdata(dev);
> const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
> + struct gpio_irq_chip *girq;
> int i, error;
>
> if (!gpio_data)
> @@ -212,6 +276,7 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
> return 0;
> }
>
> + kpad->gc.parent = &kpad->client->dev;
> kpad->gc.direction_input = adp5588_gpio_direction_input;
> kpad->gc.direction_output = adp5588_gpio_direction_output;
> kpad->gc.get = adp5588_gpio_get_value;
> @@ -223,6 +288,11 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
> kpad->gc.owner = THIS_MODULE;
> kpad->gc.names = gpio_data->names;
>
> + girq = &kpad->gc.irq;
> + gpio_irq_chip_set_chip(girq, &adp5588_irq_chip);
> + girq->handler = handle_bad_irq;
> + girq->threaded = true;
> +
> mutex_init(&kpad->gpio_lock);
>
> error = devm_gpiochip_add_data(dev, &kpad->gc, kpad);
> @@ -255,35 +325,73 @@ static int adp5588_gpio_add(struct adp5588_kpad *kpad)
> return 0;
> }
>
> -#else
> -static inline int adp5588_gpio_add(struct adp5588_kpad *kpad)
> +static unsigned long adp5588_gpiomap_get_hwirq(struct device *dev,
> + const u8 *map, unsigned int gpio,
> + unsigned int ngpios)
> {
> - return 0;
> + unsigned int hwirq;
> +
> + for (hwirq = 0; hwirq < ngpios; hwirq++)
> + if (map[hwirq] == gpio)
> + return hwirq;
> +
> + /* should never happen */
> + dev_warn_ratelimited(dev, "could not find the hwirq for gpio(%u)\n", gpio);
> +
> + return ADP5588_INVALID_HWIRQ;
> +}
> +
> +static void adp5588_gpio_irq_handle(struct adp5588_kpad *kpad, int key_val,
> + int key_press)
> +{
> + unsigned int irq, gpio = key_val - GPI_PIN_BASE, irq_type;
> + struct i2c_client *client = kpad->client;
> + struct irq_data *irqd;
> + unsigned long hwirq;
> +
> + hwirq = adp5588_gpiomap_get_hwirq(&client->dev, kpad->gpiomap,
> + gpio, kpad->gc.ngpio);
> + if (hwirq == ADP5588_INVALID_HWIRQ) {
> + dev_err(&client->dev, "Could not get hwirq for key(%u)\n", key_val);
> + return;
> + }
> +
> + irq = irq_find_mapping(kpad->gc.irq.domain, hwirq);
> + if (!irq)
> + return;
> +
> + irqd = irq_get_irq_data(irq);
> + if (!irqd) {
> + dev_err(&client->dev, "Could not get irq(%u) data\n", irq);
> + return;
> + }
> +
> + irq_type = irqd_get_trigger_type(irqd);
> +
> + /*
> + * Default is active low which means key_press is asserted on
> + * the falling edge.
> + */
> + if ((irq_type & IRQ_TYPE_EDGE_RISING && !key_press) ||
> + (irq_type & IRQ_TYPE_EDGE_FALLING && key_press))
> + handle_nested_irq(irq);
> }
> -#endif
>
> static void adp5588_report_events(struct adp5588_kpad *kpad, int ev_cnt)
> {
> - int i, j;
> + int i;
>
> for (i = 0; i < ev_cnt; i++) {
> int key = adp5588_read(kpad->client, Key_EVENTA + i);
> int key_val = key & KEY_EV_MASK;
> + int key_press = key & KEY_EV_PRESSED;
>
> - if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END) {
> - for (j = 0; j < kpad->gpimapsize; j++) {
> - if (key_val == kpad->gpimap[j].pin) {
> - input_report_switch(kpad->input,
> - kpad->gpimap[j].sw_evt,
> - key & KEY_EV_PRESSED);
> - break;
> - }
> - }
> - } else {
> + if (key_val >= GPI_PIN_BASE && key_val <= GPI_PIN_END)
> + /* gpio line used as IRQ source */
> + adp5588_gpio_irq_handle(kpad, key_val, key_press);
> + else
> input_report_key(kpad->input,
> - kpad->keycode[key_val - 1],
> - key & KEY_EV_PRESSED);
> - }
> + kpad->keycode[key_val - 1], key_press);
> }
> }
>
> @@ -341,7 +449,6 @@ static int adp5588_setup(struct i2c_client *client)
> dev_get_platdata(&client->dev);
> const struct adp5588_gpio_platform_data *gpio_data = pdata->gpio_data;
> int i, ret;
> - unsigned char evt_mode1 = 0, evt_mode2 = 0, evt_mode3 = 0;
>
> ret = adp5588_write(client, KP_GPIO1, KP_SEL(pdata->rows));
> ret |= adp5588_write(client, KP_GPIO2, KP_SEL(pdata->cols) & 0xFF);
> @@ -356,23 +463,6 @@ static int adp5588_setup(struct i2c_client *client)
> for (i = 0; i < KEYP_MAX_EVENT; i++)
> ret |= adp5588_read(client, Key_EVENTA);
>
> - for (i = 0; i < pdata->gpimapsize; i++) {
> - unsigned short pin = pdata->gpimap[i].pin;
> -
> - if (pin <= GPI_PIN_ROW_END) {
> - evt_mode1 |= (1 << (pin - GPI_PIN_ROW_BASE));
> - } else {
> - evt_mode2 |= ((1 << (pin - GPI_PIN_COL_BASE)) & 0xFF);
> - evt_mode3 |= ((1 << (pin - GPI_PIN_COL_BASE)) >> 8);
> - }
> - }
> -
> - if (pdata->gpimapsize) {
> - ret |= adp5588_write(client, GPI_EM1, evt_mode1);
> - ret |= adp5588_write(client, GPI_EM2, evt_mode2);
> - ret |= adp5588_write(client, GPI_EM3, evt_mode3);
> - }
> -
> if (gpio_data) {
> for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
> int pull_mask = gpio_data->pullup_dis_mask;
> @@ -399,44 +489,6 @@ static int adp5588_setup(struct i2c_client *client)
> return 0;
> }
>
> -static void adp5588_report_switch_state(struct adp5588_kpad *kpad)
> -{
> - int gpi_stat1 = adp5588_read(kpad->client, GPIO_DAT_STAT1);
> - int gpi_stat2 = adp5588_read(kpad->client, GPIO_DAT_STAT2);
> - int gpi_stat3 = adp5588_read(kpad->client, GPIO_DAT_STAT3);
> - int gpi_stat_tmp, pin_loc;
> - int i;
> -
> - for (i = 0; i < kpad->gpimapsize; i++) {
> - unsigned short pin = kpad->gpimap[i].pin;
> -
> - if (pin <= GPI_PIN_ROW_END) {
> - gpi_stat_tmp = gpi_stat1;
> - pin_loc = pin - GPI_PIN_ROW_BASE;
> - } else if ((pin - GPI_PIN_COL_BASE) < 8) {
> - gpi_stat_tmp = gpi_stat2;
> - pin_loc = pin - GPI_PIN_COL_BASE;
> - } else {
> - gpi_stat_tmp = gpi_stat3;
> - pin_loc = pin - GPI_PIN_COL_BASE - 8;
> - }
> -
> - if (gpi_stat_tmp < 0) {
> - dev_err(&kpad->client->dev,
> - "Can't read GPIO_DAT_STAT switch %d default to OFF\n",
> - pin);
> - gpi_stat_tmp = 0;
> - }
> -
> - input_report_switch(kpad->input,
> - kpad->gpimap[i].sw_evt,
> - !(gpi_stat_tmp & (1 << pin_loc)));
> - }
> -
> - input_sync(kpad->input);
> -}
> -
> -
> static int adp5588_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> @@ -469,37 +521,6 @@ static int adp5588_probe(struct i2c_client *client,
> return -EINVAL;
> }
>
> - if (!pdata->gpimap && pdata->gpimapsize) {
> - dev_err(&client->dev, "invalid gpimap from pdata\n");
> - return -EINVAL;
> - }
> -
> - if (pdata->gpimapsize > ADP5588_GPIMAPSIZE_MAX) {
> - dev_err(&client->dev, "invalid gpimapsize\n");
> - return -EINVAL;
> - }
> -
> - for (i = 0; i < pdata->gpimapsize; i++) {
> - unsigned short pin = pdata->gpimap[i].pin;
> -
> - if (pin < GPI_PIN_BASE || pin > GPI_PIN_END) {
> - dev_err(&client->dev, "invalid gpi pin data\n");
> - return -EINVAL;
> - }
> -
> - if (pin <= GPI_PIN_ROW_END) {
> - if (pin - GPI_PIN_ROW_BASE + 1 <= pdata->rows) {
> - dev_err(&client->dev, "invalid gpi row data\n");
> - return -EINVAL;
> - }
> - } else {
> - if (pin - GPI_PIN_COL_BASE + 1 <= pdata->cols) {
> - dev_err(&client->dev, "invalid gpi col data\n");
> - return -EINVAL;
> - }
> - }
> - }
> -
> if (!client->irq) {
> dev_err(&client->dev, "no IRQ?\n");
> return -EINVAL;
> @@ -541,9 +562,6 @@ static int adp5588_probe(struct i2c_client *client,
> memcpy(kpad->keycode, pdata->keymap,
> pdata->keymapsize * input->keycodesize);
>
> - kpad->gpimap = pdata->gpimap;
> - kpad->gpimapsize = pdata->gpimapsize;
> -
> /* setup input device */
> __set_bit(EV_KEY, input->evbit);
>
> @@ -555,11 +573,6 @@ static int adp5588_probe(struct i2c_client *client,
> __set_bit(kpad->keycode[i], input->keybit);
> __clear_bit(KEY_RESERVED, input->keybit);
>
> - if (kpad->gpimapsize)
> - __set_bit(EV_SW, input->evbit);
> - for (i = 0; i < kpad->gpimapsize; i++)
> - __set_bit(kpad->gpimap[i].sw_evt, input->swbit);
> -
> error = input_register_device(input);
> if (error) {
> dev_err(&client->dev, "unable to register input device: %d\n",
> @@ -567,6 +580,14 @@ static int adp5588_probe(struct i2c_client *client,
> return error;
> }
>
> + error = adp5588_setup(client);
> + if (error)
> + return error;
> +
> + error = adp5588_gpio_add(kpad);
> + if (error)
> + return error;
> +
> error = devm_request_threaded_irq(&client->dev, client->irq,
> adp5588_hard_irq, adp5588_thread_irq,
> IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
> @@ -577,17 +598,6 @@ static int adp5588_probe(struct i2c_client *client,
> return error;
> }
>
> - error = adp5588_setup(client);
> - if (error)
> - return error;
> -
> - if (kpad->gpimapsize)
> - adp5588_report_switch_state(kpad);
> -
> - error = adp5588_gpio_add(kpad);
> - if (error)
> - return error;
> -
> dev_info(&client->dev, "Rev.%d keypad, irq %d\n", revid, client->irq);
> return 0;
> }
> diff --git a/include/linux/platform_data/adp5588.h b/include/linux/platform_data/adp5588.h
> index 6d3f7d911a92..82170ec8c266 100644
> --- a/include/linux/platform_data/adp5588.h
> +++ b/include/linux/platform_data/adp5588.h
> @@ -147,8 +147,6 @@ struct adp5588_kpad_platform_data {
> unsigned en_keylock:1; /* Enable Key Lock feature */
> unsigned short unlock_key1; /* Unlock Key 1 */
> unsigned short unlock_key2; /* Unlock Key 2 */
> - const struct adp5588_gpi_map *gpimap;
> - unsigned short gpimapsize;
> const struct adp5588_gpio_platform_data *gpio_data;
> };
>
> --
> 2.37.2
>
--
With Best Regards,
Andy Shevchenko