| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Analog Devices ADP5585 Keys driver | |
| * | |
| * Copyright (C) 2025 Analog Devices, Inc. | |
| */ | |
| #include <linux/bitmap.h> | |
| #include <linux/container_of.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/notifier.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 notifier_block nb; | |
| 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->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->n_pins) | |
| return dev_err_probe(dev, -EINVAL, | |
| "Too many keypad pins (%d) defined (max=%d)\n", | |
| n_pins, adp5585->n_pins); | |
| unsigned int *keypad_pins __free(kfree) = kzalloc_objs(*keypad_pins, | |
| n_pins); | |
| if (!keypad_pins) | |
| return -ENOMEM; | |
| error = device_property_read_u32_array(dev, "adi,keypad-pins", | |
| keypad_pins, n_pins); | |
| if (error) | |
| return error; | |
| /* | |
| * We can add the action here since it makes the code easier and nothing | |
| * "bad" will happen out of it. Worst case, it will be a no-op and no | |
| * bit will set. | |
| */ | |
| error = devm_add_action_or_reset(dev, adp5585_keys_pins_free, kpad); | |
| if (error) | |
| return error; | |
| for (pin = 0; pin < n_pins; pin++) { | |
| if (keypad_pins[pin] >= adp5585->n_pins) | |
| return dev_err_probe(dev, -EINVAL, | |
| "Invalid keypad pin(%u) defined\n", | |
| keypad_pins[pin]); | |
| if (test_and_set_bit(keypad_pins[pin], adp5585->pin_usage)) | |
| return dev_err_probe(dev, -EBUSY, | |
| "Keypad pin(%u) already used\n", | |
| keypad_pins[pin]); | |
| __set_bit(keypad_pins[pin], &kpad->keypad); | |
| } | |
| /* | |
| * 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); | |
| error = adp5585_keys_check_special_events(adp5585, kpad); | |
| if (error) | |
| return error; | |
| return 0; | |
| } | |
| 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->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 notifier_block *nb, unsigned long key, | |
| void *data) | |
| { | |
| struct adp5585_kpad *kpad = container_of(nb, struct adp5585_kpad, nb); | |
| unsigned long key_press = (unsigned long)data; | |
| 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 NOTIFY_DONE; | |
| /* | |
| * 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 NOTIFY_BAD; | |
| 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(%lu) 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 NOTIFY_STOP; | |
| } | |
| static void adp5585_keys_unreg_notifier(void *data) | |
| { | |
| struct adp5585_kpad *kpad = data; | |
| struct adp5585_dev *adp5585 = dev_get_drvdata(kpad->dev->parent); | |
| blocking_notifier_chain_unregister(&adp5585->event_notifier, | |
| &kpad->nb); | |
| } | |
| 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; | |
| kpad->nb.notifier_call = adp5585_keys_ev_handle; | |
| error = blocking_notifier_chain_register(&adp5585->event_notifier, | |
| &kpad->nb); | |
| if (error) | |
| return error; | |
| error = devm_add_action_or_reset(dev, adp5585_keys_unreg_notifier, kpad); | |
| 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"); |