Message ID | 20250512-dev-adp5589-fw-v3-17-092b14b79a88@analog.com |
---|---|
State | Changes Requested |
Headers | show |
Series | mfd: adp5585: support keymap events and drop legacy Input driver | expand |
Hi Nuno, On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote: > + > + for (pin = 0; pin < n_pins; pin++) { > + if (keypad_pins[pin] >= adp5585->info->n_pins) { > + error = dev_err_probe(dev, -EINVAL, > + "Invalid keypad pin(%u) defined\n", > + keypad_pins[pin]); > + goto out_free_map; > + } > + > + if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) { > + error = dev_err_probe(dev, -EBUSY, > + "Keypad pin(%u) already used\n", > + keypad_pins[pin]); > + goto out_free_map; This jump looked confusing, together with devm, etc. I wonder, can you move call to devm_add_action_or_reset() before the loop? It looks like it should handle completely unpopulated pin map just fine... > + } > + > + __set_bit(keypad_pins[pin], &kpad->keypad); > + } > + > + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad); > + if (error) > + return error; > + > + /* > + * Note that given that we get a mask (and the HW allows it), we > + * can have holes in our keypad (eg: row0, row1 and row7 enabled). > + * However, for the matrix parsing functions we need to pass the > + * number of rows/cols as the maximum row/col used plus 1. This > + * pretty much means we will also have holes in our SW keypad. > + */ > + > + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; > + if (rows == kpad->info->max_rows + 1) > + return dev_err_probe(dev, -EINVAL, > + "Now rows defined in the keypad!\n"); > + > + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad->info->max_rows); > + if (cols < kpad->info->max_rows) > + return dev_err_probe(dev, -EINVAL, > + "No columns defined in the keypad!\n"); > + > + cols = cols + 1 - kpad->info->max_rows; > + > + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, > + kpad->keycode, kpad->input); > + if (error) > + return error; > + > + kpad->row_shift = get_count_order(cols); > + > + if (device_property_read_bool(kpad->dev, "autorepeat")) > + __set_bit(EV_REP, kpad->input->evbit); > + > + return adp5585_keys_check_special_events(adp5585, kpad); error = adp5585_keys_check_special_events(...); if (error) return error; return 0; Thanks.
On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote: > Hi Nuno, > > On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote: > > + > > + for (pin = 0; pin < n_pins; pin++) { > > + if (keypad_pins[pin] >= adp5585->info->n_pins) { > > + error = dev_err_probe(dev, -EINVAL, > > + "Invalid keypad pin(%u) > > defined\n", > > + keypad_pins[pin]); > > + goto out_free_map; > > + } > > + > > + if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) > > { > > + error = dev_err_probe(dev, -EBUSY, > > + "Keypad pin(%u) already > > used\n", > > + keypad_pins[pin]); > > + goto out_free_map; > > This jump looked confusing, together with devm, etc. I wonder, can you > move call to devm_add_action_or_reset() before the loop? It looks like > it should handle completely unpopulated pin map just fine... Seemed the logical way but I agree that what you suggest makes it more simpler. > > > + } > > + > > + __set_bit(keypad_pins[pin], &kpad->keypad); > > + } > > + > > + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, > > kpad); > > + if (error) > > + return error; > > + > > + /* > > + * Note that given that we get a mask (and the HW allows it), we > > + * can have holes in our keypad (eg: row0, row1 and row7 enabled). > > + * However, for the matrix parsing functions we need to pass the > > + * number of rows/cols as the maximum row/col used plus 1. This > > + * pretty much means we will also have holes in our SW keypad. > > + */ > > + > > + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; > > + if (rows == kpad->info->max_rows + 1) > > + return dev_err_probe(dev, -EINVAL, > > + "Now rows defined in the keypad!\n"); > > + > > + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad- > > >info->max_rows); > > + if (cols < kpad->info->max_rows) > > + return dev_err_probe(dev, -EINVAL, > > + "No columns defined in the > > keypad!\n"); > > + > > + cols = cols + 1 - kpad->info->max_rows; > > + > > + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, > > + kpad->keycode, kpad->input); > > + if (error) > > + return error; > > + > > + kpad->row_shift = get_count_order(cols); > > + > > + if (device_property_read_bool(kpad->dev, "autorepeat")) > > + __set_bit(EV_REP, kpad->input->evbit); > > + > > + return adp5585_keys_check_special_events(adp5585, kpad); > > error = adp5585_keys_check_special_events(...); > if (error) > return error; Curious, any special reason for the above? Or is just personal preference? - Nuno Sá
On Tue, May 20, 2025 at 09:32:53AM +0100, Nuno Sá wrote: > On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote: > > Hi Nuno, > > > > On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote: > > > + > > > + for (pin = 0; pin < n_pins; pin++) { > > > + if (keypad_pins[pin] >= adp5585->info->n_pins) { > > > + error = dev_err_probe(dev, -EINVAL, > > > + "Invalid keypad pin(%u) > > > defined\n", > > > + keypad_pins[pin]); > > > + goto out_free_map; > > > + } > > > + > > > + if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) > > > { > > > + error = dev_err_probe(dev, -EBUSY, > > > + "Keypad pin(%u) already > > > used\n", > > > + keypad_pins[pin]); > > > + goto out_free_map; > > > > This jump looked confusing, together with devm, etc. I wonder, can you > > move call to devm_add_action_or_reset() before the loop? It looks like > > it should handle completely unpopulated pin map just fine... > > Seemed the logical way but I agree that what you suggest makes it more simpler. > > > > > > + } > > > + > > > + __set_bit(keypad_pins[pin], &kpad->keypad); > > > + } > > > + > > > + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, > > > kpad); > > > + if (error) > > > + return error; > > > + > > > + /* > > > + * Note that given that we get a mask (and the HW allows it), we > > > + * can have holes in our keypad (eg: row0, row1 and row7 enabled). > > > + * However, for the matrix parsing functions we need to pass the > > > + * number of rows/cols as the maximum row/col used plus 1. This > > > + * pretty much means we will also have holes in our SW keypad. > > > + */ > > > + > > > + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; > > > + if (rows == kpad->info->max_rows + 1) > > > + return dev_err_probe(dev, -EINVAL, > > > + "Now rows defined in the keypad!\n"); > > > + > > > + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad- > > > >info->max_rows); > > > + if (cols < kpad->info->max_rows) > > > + return dev_err_probe(dev, -EINVAL, > > > + "No columns defined in the > > > keypad!\n"); > > > + > > > + cols = cols + 1 - kpad->info->max_rows; > > > + > > > + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, > > > + kpad->keycode, kpad->input); > > > + if (error) > > > + return error; > > > + > > > + kpad->row_shift = get_count_order(cols); > > > + > > > + if (device_property_read_bool(kpad->dev, "autorepeat")) > > > + __set_bit(EV_REP, kpad->input->evbit); > > > + > > > + return adp5585_keys_check_special_events(adp5585, kpad); > > > > error = adp5585_keys_check_special_events(...); > > if (error) > > return error; > > Curious, any special reason for the above? Or is just personal preference? More of a personal preference, however there is some logic to it ;) - in a function with multiple failure/return points such form allows for easy addition and/or movement of the code. Thanks.
On Tue, 2025-05-20 at 11:33 -0700, Dmitry Torokhov wrote: > On Tue, May 20, 2025 at 09:32:53AM +0100, Nuno Sá wrote: > > On Mon, 2025-05-19 at 15:29 -0700, Dmitry Torokhov wrote: > > > Hi Nuno, > > > > > > On Mon, May 12, 2025 at 01:39:09PM +0100, Nuno Sá via B4 Relay wrote: > > > > + > > > > + for (pin = 0; pin < n_pins; pin++) { > > > > + if (keypad_pins[pin] >= adp5585->info->n_pins) { > > > > + error = dev_err_probe(dev, -EINVAL, > > > > + "Invalid keypad pin(%u) > > > > defined\n", > > > > + keypad_pins[pin]); > > > > + goto out_free_map; > > > > + } > > > > + > > > > + if (test_and_set_bit(keypad_pins[pin], adp5585- > > > > >pin_usage)) > > > > { > > > > + error = dev_err_probe(dev, -EBUSY, > > > > + "Keypad pin(%u) already > > > > used\n", > > > > + keypad_pins[pin]); > > > > + goto out_free_map; > > > > > > This jump looked confusing, together with devm, etc. I wonder, can you > > > move call to devm_add_action_or_reset() before the loop? It looks like > > > it should handle completely unpopulated pin map just fine... > > > > Seemed the logical way but I agree that what you suggest makes it more > > simpler. > > > > > > > > > + } > > > > + > > > > + __set_bit(keypad_pins[pin], &kpad->keypad); > > > > + } > > > > + > > > > + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, > > > > kpad); > > > > + if (error) > > > > + return error; > > > > + > > > > + /* > > > > + * Note that given that we get a mask (and the HW allows it), > > > > we > > > > + * can have holes in our keypad (eg: row0, row1 and row7 > > > > enabled). > > > > + * However, for the matrix parsing functions we need to pass > > > > the > > > > + * number of rows/cols as the maximum row/col used plus 1. This > > > > + * pretty much means we will also have holes in our SW keypad. > > > > + */ > > > > + > > > > + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; > > > > + if (rows == kpad->info->max_rows + 1) > > > > + return dev_err_probe(dev, -EINVAL, > > > > + "Now rows defined in the > > > > keypad!\n"); > > > > + > > > > + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + > > > > kpad- > > > > > info->max_rows); > > > > + if (cols < kpad->info->max_rows) > > > > + return dev_err_probe(dev, -EINVAL, > > > > + "No columns defined in the > > > > keypad!\n"); > > > > + > > > > + cols = cols + 1 - kpad->info->max_rows; > > > > + > > > > + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, > > > > + kpad->keycode, kpad->input); > > > > + if (error) > > > > + return error; > > > > + > > > > + kpad->row_shift = get_count_order(cols); > > > > + > > > > + if (device_property_read_bool(kpad->dev, "autorepeat")) > > > > + __set_bit(EV_REP, kpad->input->evbit); > > > > + > > > > + return adp5585_keys_check_special_events(adp5585, kpad); > > > > > > error = adp5585_keys_check_special_events(...); > > > if (error) > > > return error; > > > > Curious, any special reason for the above? Or is just personal preference? > > More of a personal preference, however there is some logic to it ;) - > in a function with multiple failure/return points such form allows for > easy addition and/or movement of the code. > > Thanks. I get your point. Honestly still prefer returning right away but no strong feelings anyways. I'll change it. - Nuno Sá
diff --git a/MAINTAINERS b/MAINTAINERS index 0737dcb2e41119426f1d8fbaec829cc90ed0bf64..18838ba19e5edbbe352a470c4e177c6d24136d83 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -550,6 +550,7 @@ L: linux-pwm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/*/adi,adp5585*.yaml F: drivers/gpio/gpio-adp5585.c +F: drivers/input/keyboard/adp5585-keys.c F: drivers/mfd/adp5585.c F: drivers/pwm/pwm-adp5585.c F: include/linux/mfd/adp5585.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 721ab69e84ac6586f4f19102890a15ca3fcf1910..322da0957067db77c7f66ab26a181d39c2c1d513 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -37,6 +37,17 @@ config KEYBOARD_ADP5520 To compile this driver as a module, choose M here: the module will be called adp5520-keys. +config KEYBOARD_ADP5585 + tristate "ADP5585 and similar I2C QWERTY Keypad and IO Expanders" + depends on MFD_ADP5585 + select INPUT_MATRIXKMAP + help + This option enables support for the KEYMAP function found in the Analog + Devices ADP5585 and similar devices. + + To compile this driver as a module, choose M here: the + module will be called adp5585-keys. + config KEYBOARD_ADP5588 tristate "ADP5588/87 I2C QWERTY Keypad and IO Expander" depends on I2C diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 1e0721c3070968a6339a42f65a95af48364f6897..f00ec003a59aa28577ae164c0539cc5aff9579fc 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_KEYBOARD_ADC) += adc-keys.o obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o +obj-$(CONFIG_KEYBOARD_ADP5585) += adp5585-keys.o obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o diff --git a/drivers/input/keyboard/adp5585-keys.c b/drivers/input/keyboard/adp5585-keys.c new file mode 100644 index 0000000000000000000000000000000000000000..54e8fefcfab8942507245aa27931ccaf8d195b8e --- /dev/null +++ b/drivers/input/keyboard/adp5585-keys.c @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices ADP5585 Keys driver + * + * Copyright (C) 2025 Analog Devices, Inc. + */ + +#include <linux/bitmap.h> +#include <linux/device.h> +#include <linux/find.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> +#include <linux/mfd/adp5585.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/types.h> + +/* As needed for the matrix parsing code */ +#define ADP5589_MAX_KEYMAPSIZE 123 + +struct adp5585_kpad_chip { + u8 key_ev_min; + u8 key_ev_max; + u8 max_rows; + u8 max_cols; +}; + +struct adp5585_kpad { + const struct adp5585_kpad_chip *info; + struct adp5585_ev_handler ev_handler; + struct input_dev *input; + unsigned short keycode[ADP5589_MAX_KEYMAPSIZE]; + struct device *dev; + unsigned long keypad; + int row_shift; +}; + +static int adp5585_keys_validate_events(const struct adp5585_kpad *kpad, + const u32 *events, u32 n_events) +{ + unsigned int ev; + u32 row, col; + + for (ev = 0; ev < n_events; ev++) { + if (events[ev] < kpad->info->key_ev_min || + events[ev] > kpad->info->key_ev_max) + continue; + + /* + * if the event is to be generated by the keymap, we need to make + * sure that the pins are part of it! + */ + row = (events[ev] - 1) / kpad->info->max_cols; + col = (events[ev] - 1) % kpad->info->max_cols; + + if (test_bit(row, &kpad->keypad) && + test_bit(col + kpad->info->max_rows, &kpad->keypad)) + continue; + + return dev_err_probe(kpad->dev, -EINVAL, + "Invalid unlock/reset event(%u) not used in the keypad\n", + events[ev]); + } + + return 0; +} + +static int adp5585_keys_check_special_events(const struct adp5585_dev *adp5585, + const struct adp5585_kpad *kpad) +{ + int error; + + error = adp5585_keys_validate_events(kpad, adp5585->unlock_keys, + adp5585->nkeys_unlock); + if (error) + return error; + + error = adp5585_keys_validate_events(kpad, adp5585->reset1_keys, + adp5585->nkeys_reset1); + if (error) + return error; + + return adp5585_keys_validate_events(kpad, adp5585->reset2_keys, + adp5585->nkeys_reset2); +} + +static void adp5585_keys_pins_free(void *data) +{ + struct adp5585_kpad *kpad = data; + struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent); + unsigned int pin; + + for_each_set_bit(pin, &kpad->keypad, adp5585->info->n_pins) + clear_bit(pin, adp5585->pin_usage); +} + +static int adp5585_keys_parse_fw(const struct adp5585_dev *adp5585, + struct adp5585_kpad *kpad) +{ + struct device *dev = kpad->dev; + u32 cols = 0, rows = 0, pin; + int error, n_pins; + + /* + * We do not check for errors (or no value) since the input device is + * only added if this property is present in the first place. + */ + n_pins = device_property_count_u32(dev, "adi,keypad-pins"); + if (n_pins > adp5585->info->n_pins) + return dev_err_probe(dev, -EINVAL, + "Too many keypad pins (%d) defined (max=%d)\n", + n_pins, adp5585->info->n_pins); + + unsigned int *keypad_pins __free(kfree) = kcalloc(n_pins, sizeof(*keypad_pins), + GFP_KERNEL); + if (!keypad_pins) + return -ENOMEM; + + error = device_property_read_u32_array(dev, "adi,keypad-pins", + keypad_pins, n_pins); + if (error) + return error; + + for (pin = 0; pin < n_pins; pin++) { + if (keypad_pins[pin] >= adp5585->info->n_pins) { + error = dev_err_probe(dev, -EINVAL, + "Invalid keypad pin(%u) defined\n", + keypad_pins[pin]); + goto out_free_map; + } + + if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) { + error = dev_err_probe(dev, -EBUSY, + "Keypad pin(%u) already used\n", + keypad_pins[pin]); + goto out_free_map; + } + + __set_bit(keypad_pins[pin], &kpad->keypad); + } + + error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad); + if (error) + return error; + + /* + * Note that given that we get a mask (and the HW allows it), we + * can have holes in our keypad (eg: row0, row1 and row7 enabled). + * However, for the matrix parsing functions we need to pass the + * number of rows/cols as the maximum row/col used plus 1. This + * pretty much means we will also have holes in our SW keypad. + */ + + rows = find_last_bit(&kpad->keypad, kpad->info->max_rows) + 1; + if (rows == kpad->info->max_rows + 1) + return dev_err_probe(dev, -EINVAL, + "Now rows defined in the keypad!\n"); + + cols = find_last_bit(&kpad->keypad, kpad->info->max_cols + kpad->info->max_rows); + if (cols < kpad->info->max_rows) + return dev_err_probe(dev, -EINVAL, + "No columns defined in the keypad!\n"); + + cols = cols + 1 - kpad->info->max_rows; + + error = matrix_keypad_build_keymap(NULL, NULL, rows, cols, + kpad->keycode, kpad->input); + if (error) + return error; + + kpad->row_shift = get_count_order(cols); + + if (device_property_read_bool(kpad->dev, "autorepeat")) + __set_bit(EV_REP, kpad->input->evbit); + + return adp5585_keys_check_special_events(adp5585, kpad); + +out_free_map: + adp5585_keys_pins_free(kpad); + return error; +} + +static int adp5585_keys_setup(const struct adp5585_dev *adp5585, + struct adp5585_kpad *kpad) +{ + unsigned long keys_bits, start = 0, nbits = kpad->info->max_rows; + const struct adp5585_regs *regs = adp5585->info->regs; + unsigned int i = 0, max_cols = kpad->info->max_cols; + int error; + + /* + * Take care as the below assumes max_rows is always less or equal than + * 8 which is true for the supported devices. If we happen to add + * another device we need to make sure this still holds true. Although + * adding a new device is very unlikely. + */ + do { + keys_bits = bitmap_read(&kpad->keypad, start, nbits); + if (keys_bits) { + error = regmap_write(adp5585->regmap, regs->pin_cfg_a + i, + keys_bits); + if (error) + return error; + } + + start += nbits; + if (max_cols > 8) { + nbits = 8; + max_cols -= nbits; + } else { + nbits = max_cols; + } + + i++; + } while (start < kpad->info->max_rows + kpad->info->max_cols); + + return 0; +} + +static int adp5585_keys_ev_handle(struct device *dev, unsigned int key, + unsigned int key_press) +{ + struct adp5585_kpad *kpad = dev_get_drvdata(dev); + unsigned int row, col, code; + + /* make sure the event is for us */ + if (key < kpad->info->key_ev_min || key > kpad->info->key_ev_max) + return -EINVAL; + + /* + * Unlikely but lets be on the safe side! We do not return any error + * because the event was indeed for us but with some weird value. So, + * we still want the caller know that the right handler was called. + */ + if (!key) + return 0; + + row = (key - 1) / (kpad->info->max_cols); + col = (key - 1) % (kpad->info->max_cols); + code = MATRIX_SCAN_CODE(row, col, kpad->row_shift); + + dev_dbg_ratelimited(kpad->dev, "report key(%d) r(%d) c(%d) code(%d)\n", + key, row, col, kpad->keycode[code]); + + input_report_key(kpad->input, kpad->keycode[code], key_press); + input_sync(kpad->input); + + return 0; +} + +static int adp5585_keys_probe(struct platform_device *pdev) +{ + const struct platform_device_id *id = platform_get_device_id(pdev); + struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct adp5585_kpad *kpad; + unsigned int revid; + const char *phys; + int error; + + kpad = devm_kzalloc(dev, sizeof(*kpad), GFP_KERNEL); + if (!kpad) + return -ENOMEM; + + if (!adp5585->irq) + return dev_err_probe(dev, -EINVAL, + "IRQ is mandatory for the keypad\n"); + + kpad->dev = dev; + + kpad->input = devm_input_allocate_device(dev); + if (!kpad->input) + return -ENOMEM; + + kpad->info = (const struct adp5585_kpad_chip *)id->driver_data; + if (!kpad->info) + return -ENODEV; + + error = regmap_read(adp5585->regmap, ADP5585_ID, &revid); + if (error) + return dev_err_probe(dev, error, "Failed to read device ID\n"); + + phys = devm_kasprintf(dev, GFP_KERNEL, "%s/input0", pdev->name); + if (!phys) + return -ENOMEM; + + kpad->input->name = pdev->name; + kpad->input->phys = phys; + + kpad->input->id.bustype = BUS_I2C; + kpad->input->id.vendor = 0x0001; + kpad->input->id.product = 0x0001; + kpad->input->id.version = revid & ADP5585_REV_ID_MASK; + + device_set_of_node_from_dev(dev, dev->parent); + + error = adp5585_keys_parse_fw(adp5585, kpad); + if (error) + return error; + + error = adp5585_keys_setup(adp5585, kpad); + if (error) + return error; + + platform_set_drvdata(pdev, kpad); + kpad->ev_handler.dev = dev; + kpad->ev_handler.handler = adp5585_keys_ev_handle; + + error = devm_adp5585_ev_handler_add(adp5585, &kpad->ev_handler); + if (error) + return error; + + error = input_register_device(kpad->input); + if (error) + return dev_err_probe(dev, error, + "Failed to register input device\n"); + + return 0; +} + +static const struct adp5585_kpad_chip adp5585_kpad_chip_info = { + .max_rows = 6, + .max_cols = 5, + .key_ev_min = ADP5585_ROW5_KEY_EVENT_START, + .key_ev_max = ADP5585_ROW5_KEY_EVENT_END, +}; + +static const struct adp5585_kpad_chip adp5589_kpad_chip_info = { + .max_rows = 8, + .max_cols = 11, + .key_ev_min = ADP5589_KEY_EVENT_START, + .key_ev_max = ADP5589_KEY_EVENT_END, +}; + +static const struct platform_device_id adp5585_keys_id_table[] = { + { "adp5585-keys", (kernel_ulong_t)&adp5585_kpad_chip_info }, + { "adp5589-keys", (kernel_ulong_t)&adp5589_kpad_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(platform, adp5585_keys_id_table); + +static struct platform_driver adp5585_keys_driver = { + .driver = { + .name = "adp5585-keys", + }, + .probe = adp5585_keys_probe, + .id_table = adp5585_keys_id_table, +}; +module_platform_driver(adp5585_keys_driver); + +MODULE_AUTHOR("Nuno Sá <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("ADP5585 Keys Driver"); +MODULE_LICENSE("GPL");