18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * GPIO driven matrix keyboard driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Marek Vasut <marek.vasut@gmail.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Based on corgikbd.c 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/input.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio.h> 198c2ecf20Sopenharmony_ci#include <linux/input/matrix_keypad.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <linux/of.h> 228c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 238c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_cistruct matrix_keypad { 268c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata; 278c2ecf20Sopenharmony_ci struct input_dev *input_dev; 288c2ecf20Sopenharmony_ci unsigned int row_shift; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci DECLARE_BITMAP(disabled_gpios, MATRIX_MAX_ROWS); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci uint32_t last_key_state[MATRIX_MAX_COLS]; 338c2ecf20Sopenharmony_ci struct delayed_work work; 348c2ecf20Sopenharmony_ci spinlock_t lock; 358c2ecf20Sopenharmony_ci bool scan_pending; 368c2ecf20Sopenharmony_ci bool stopped; 378c2ecf20Sopenharmony_ci bool gpio_all_disabled; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* 418c2ecf20Sopenharmony_ci * NOTE: If drive_inactive_cols is false, then the GPIO has to be put into 428c2ecf20Sopenharmony_ci * HiZ when de-activated to cause minmal side effect when scanning other 438c2ecf20Sopenharmony_ci * columns. In that case it is configured here to be input, otherwise it is 448c2ecf20Sopenharmony_ci * driven with the inactive value. 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_cistatic void __activate_col(const struct matrix_keypad_platform_data *pdata, 478c2ecf20Sopenharmony_ci int col, bool on) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci bool level_on = !pdata->active_low; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (on) { 528c2ecf20Sopenharmony_ci gpio_direction_output(pdata->col_gpios[col], level_on); 538c2ecf20Sopenharmony_ci } else { 548c2ecf20Sopenharmony_ci gpio_set_value_cansleep(pdata->col_gpios[col], !level_on); 558c2ecf20Sopenharmony_ci if (!pdata->drive_inactive_cols) 568c2ecf20Sopenharmony_ci gpio_direction_input(pdata->col_gpios[col]); 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void activate_col(const struct matrix_keypad_platform_data *pdata, 618c2ecf20Sopenharmony_ci int col, bool on) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci __activate_col(pdata, col, on); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (on && pdata->col_scan_delay_us) 668c2ecf20Sopenharmony_ci udelay(pdata->col_scan_delay_us); 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic void activate_all_cols(const struct matrix_keypad_platform_data *pdata, 708c2ecf20Sopenharmony_ci bool on) 718c2ecf20Sopenharmony_ci{ 728c2ecf20Sopenharmony_ci int col; 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci for (col = 0; col < pdata->num_col_gpios; col++) 758c2ecf20Sopenharmony_ci __activate_col(pdata, col, on); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic bool row_asserted(const struct matrix_keypad_platform_data *pdata, 798c2ecf20Sopenharmony_ci int row) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci return gpio_get_value_cansleep(pdata->row_gpios[row]) ? 828c2ecf20Sopenharmony_ci !pdata->active_low : pdata->active_low; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void enable_row_irqs(struct matrix_keypad *keypad) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 888c2ecf20Sopenharmony_ci int i; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) 918c2ecf20Sopenharmony_ci enable_irq(pdata->clustered_irq); 928c2ecf20Sopenharmony_ci else { 938c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) 948c2ecf20Sopenharmony_ci enable_irq(gpio_to_irq(pdata->row_gpios[i])); 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic void disable_row_irqs(struct matrix_keypad *keypad) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 1018c2ecf20Sopenharmony_ci int i; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) 1048c2ecf20Sopenharmony_ci disable_irq_nosync(pdata->clustered_irq); 1058c2ecf20Sopenharmony_ci else { 1068c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) 1078c2ecf20Sopenharmony_ci disable_irq_nosync(gpio_to_irq(pdata->row_gpios[i])); 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* 1128c2ecf20Sopenharmony_ci * This gets the keys from keyboard and reports it to input subsystem 1138c2ecf20Sopenharmony_ci */ 1148c2ecf20Sopenharmony_cistatic void matrix_keypad_scan(struct work_struct *work) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = 1178c2ecf20Sopenharmony_ci container_of(work, struct matrix_keypad, work.work); 1188c2ecf20Sopenharmony_ci struct input_dev *input_dev = keypad->input_dev; 1198c2ecf20Sopenharmony_ci const unsigned short *keycodes = input_dev->keycode; 1208c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 1218c2ecf20Sopenharmony_ci uint32_t new_state[MATRIX_MAX_COLS]; 1228c2ecf20Sopenharmony_ci int row, col, code; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* de-activate all columns for scanning */ 1258c2ecf20Sopenharmony_ci activate_all_cols(pdata, false); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci memset(new_state, 0, sizeof(new_state)); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* assert each column and read the row status out */ 1308c2ecf20Sopenharmony_ci for (col = 0; col < pdata->num_col_gpios; col++) { 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci activate_col(pdata, col, true); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci for (row = 0; row < pdata->num_row_gpios; row++) 1358c2ecf20Sopenharmony_ci new_state[col] |= 1368c2ecf20Sopenharmony_ci row_asserted(pdata, row) ? (1 << row) : 0; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci activate_col(pdata, col, false); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci for (col = 0; col < pdata->num_col_gpios; col++) { 1428c2ecf20Sopenharmony_ci uint32_t bits_changed; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci bits_changed = keypad->last_key_state[col] ^ new_state[col]; 1458c2ecf20Sopenharmony_ci if (bits_changed == 0) 1468c2ecf20Sopenharmony_ci continue; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci for (row = 0; row < pdata->num_row_gpios; row++) { 1498c2ecf20Sopenharmony_ci if ((bits_changed & (1 << row)) == 0) 1508c2ecf20Sopenharmony_ci continue; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci code = MATRIX_SCAN_CODE(row, col, keypad->row_shift); 1538c2ecf20Sopenharmony_ci input_event(input_dev, EV_MSC, MSC_SCAN, code); 1548c2ecf20Sopenharmony_ci input_report_key(input_dev, 1558c2ecf20Sopenharmony_ci keycodes[code], 1568c2ecf20Sopenharmony_ci new_state[col] & (1 << row)); 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci input_sync(input_dev); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci memcpy(keypad->last_key_state, new_state, sizeof(new_state)); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci activate_all_cols(pdata, true); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* Enable IRQs again */ 1668c2ecf20Sopenharmony_ci spin_lock_irq(&keypad->lock); 1678c2ecf20Sopenharmony_ci keypad->scan_pending = false; 1688c2ecf20Sopenharmony_ci enable_row_irqs(keypad); 1698c2ecf20Sopenharmony_ci spin_unlock_irq(&keypad->lock); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic irqreturn_t matrix_keypad_interrupt(int irq, void *id) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = id; 1758c2ecf20Sopenharmony_ci unsigned long flags; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci spin_lock_irqsave(&keypad->lock, flags); 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci /* 1808c2ecf20Sopenharmony_ci * See if another IRQ beaten us to it and scheduled the 1818c2ecf20Sopenharmony_ci * scan already. In that case we should not try to 1828c2ecf20Sopenharmony_ci * disable IRQs again. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_ci if (unlikely(keypad->scan_pending || keypad->stopped)) 1858c2ecf20Sopenharmony_ci goto out; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci disable_row_irqs(keypad); 1888c2ecf20Sopenharmony_ci keypad->scan_pending = true; 1898c2ecf20Sopenharmony_ci schedule_delayed_work(&keypad->work, 1908c2ecf20Sopenharmony_ci msecs_to_jiffies(keypad->pdata->debounce_ms)); 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciout: 1938c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&keypad->lock, flags); 1948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int matrix_keypad_start(struct input_dev *dev) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = input_get_drvdata(dev); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci keypad->stopped = false; 2028c2ecf20Sopenharmony_ci mb(); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * Schedule an immediate key scan to capture current key state; 2068c2ecf20Sopenharmony_ci * columns will be activated and IRQs be enabled after the scan. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci schedule_delayed_work(&keypad->work, 0); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void matrix_keypad_stop(struct input_dev *dev) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = input_get_drvdata(dev); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spin_lock_irq(&keypad->lock); 2188c2ecf20Sopenharmony_ci keypad->stopped = true; 2198c2ecf20Sopenharmony_ci spin_unlock_irq(&keypad->lock); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci flush_delayed_work(&keypad->work); 2228c2ecf20Sopenharmony_ci /* 2238c2ecf20Sopenharmony_ci * matrix_keypad_scan() will leave IRQs enabled; 2248c2ecf20Sopenharmony_ci * we should disable them now. 2258c2ecf20Sopenharmony_ci */ 2268c2ecf20Sopenharmony_ci disable_row_irqs(keypad); 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 2308c2ecf20Sopenharmony_cistatic void matrix_keypad_enable_wakeup(struct matrix_keypad *keypad) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 2338c2ecf20Sopenharmony_ci unsigned int gpio; 2348c2ecf20Sopenharmony_ci int i; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) { 2378c2ecf20Sopenharmony_ci if (enable_irq_wake(pdata->clustered_irq) == 0) 2388c2ecf20Sopenharmony_ci keypad->gpio_all_disabled = true; 2398c2ecf20Sopenharmony_ci } else { 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) { 2428c2ecf20Sopenharmony_ci if (!test_bit(i, keypad->disabled_gpios)) { 2438c2ecf20Sopenharmony_ci gpio = pdata->row_gpios[i]; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (enable_irq_wake(gpio_to_irq(gpio)) == 0) 2468c2ecf20Sopenharmony_ci __set_bit(i, keypad->disabled_gpios); 2478c2ecf20Sopenharmony_ci } 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic void matrix_keypad_disable_wakeup(struct matrix_keypad *keypad) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 2558c2ecf20Sopenharmony_ci unsigned int gpio; 2568c2ecf20Sopenharmony_ci int i; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) { 2598c2ecf20Sopenharmony_ci if (keypad->gpio_all_disabled) { 2608c2ecf20Sopenharmony_ci disable_irq_wake(pdata->clustered_irq); 2618c2ecf20Sopenharmony_ci keypad->gpio_all_disabled = false; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) { 2658c2ecf20Sopenharmony_ci if (test_and_clear_bit(i, keypad->disabled_gpios)) { 2668c2ecf20Sopenharmony_ci gpio = pdata->row_gpios[i]; 2678c2ecf20Sopenharmony_ci disable_irq_wake(gpio_to_irq(gpio)); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic int matrix_keypad_suspend(struct device *dev) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 2768c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = platform_get_drvdata(pdev); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci matrix_keypad_stop(keypad->input_dev); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 2818c2ecf20Sopenharmony_ci matrix_keypad_enable_wakeup(keypad); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic int matrix_keypad_resume(struct device *dev) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 2898c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = platform_get_drvdata(pdev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) 2928c2ecf20Sopenharmony_ci matrix_keypad_disable_wakeup(keypad); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci matrix_keypad_start(keypad->input_dev); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci return 0; 2978c2ecf20Sopenharmony_ci} 2988c2ecf20Sopenharmony_ci#endif 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(matrix_keypad_pm_ops, 3018c2ecf20Sopenharmony_ci matrix_keypad_suspend, matrix_keypad_resume); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_cistatic int matrix_keypad_init_gpio(struct platform_device *pdev, 3048c2ecf20Sopenharmony_ci struct matrix_keypad *keypad) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 3078c2ecf20Sopenharmony_ci int i, err; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci /* initialized strobe lines as outputs, activated */ 3108c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_col_gpios; i++) { 3118c2ecf20Sopenharmony_ci err = gpio_request(pdata->col_gpios[i], "matrix_kbd_col"); 3128c2ecf20Sopenharmony_ci if (err) { 3138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3148c2ecf20Sopenharmony_ci "failed to request GPIO%d for COL%d\n", 3158c2ecf20Sopenharmony_ci pdata->col_gpios[i], i); 3168c2ecf20Sopenharmony_ci goto err_free_cols; 3178c2ecf20Sopenharmony_ci } 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci gpio_direction_output(pdata->col_gpios[i], !pdata->active_low); 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) { 3238c2ecf20Sopenharmony_ci err = gpio_request(pdata->row_gpios[i], "matrix_kbd_row"); 3248c2ecf20Sopenharmony_ci if (err) { 3258c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3268c2ecf20Sopenharmony_ci "failed to request GPIO%d for ROW%d\n", 3278c2ecf20Sopenharmony_ci pdata->row_gpios[i], i); 3288c2ecf20Sopenharmony_ci goto err_free_rows; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci gpio_direction_input(pdata->row_gpios[i]); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) { 3358c2ecf20Sopenharmony_ci err = request_any_context_irq(pdata->clustered_irq, 3368c2ecf20Sopenharmony_ci matrix_keypad_interrupt, 3378c2ecf20Sopenharmony_ci pdata->clustered_irq_flags, 3388c2ecf20Sopenharmony_ci "matrix-keypad", keypad); 3398c2ecf20Sopenharmony_ci if (err < 0) { 3408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3418c2ecf20Sopenharmony_ci "Unable to acquire clustered interrupt\n"); 3428c2ecf20Sopenharmony_ci goto err_free_rows; 3438c2ecf20Sopenharmony_ci } 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) { 3468c2ecf20Sopenharmony_ci err = request_any_context_irq( 3478c2ecf20Sopenharmony_ci gpio_to_irq(pdata->row_gpios[i]), 3488c2ecf20Sopenharmony_ci matrix_keypad_interrupt, 3498c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | 3508c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, 3518c2ecf20Sopenharmony_ci "matrix-keypad", keypad); 3528c2ecf20Sopenharmony_ci if (err < 0) { 3538c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 3548c2ecf20Sopenharmony_ci "Unable to acquire interrupt for GPIO line %i\n", 3558c2ecf20Sopenharmony_ci pdata->row_gpios[i]); 3568c2ecf20Sopenharmony_ci goto err_free_irqs; 3578c2ecf20Sopenharmony_ci } 3588c2ecf20Sopenharmony_ci } 3598c2ecf20Sopenharmony_ci } 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* initialized as disabled - enabled by input->open */ 3628c2ecf20Sopenharmony_ci disable_row_irqs(keypad); 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cierr_free_irqs: 3668c2ecf20Sopenharmony_ci while (--i >= 0) 3678c2ecf20Sopenharmony_ci free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); 3688c2ecf20Sopenharmony_ci i = pdata->num_row_gpios; 3698c2ecf20Sopenharmony_cierr_free_rows: 3708c2ecf20Sopenharmony_ci while (--i >= 0) 3718c2ecf20Sopenharmony_ci gpio_free(pdata->row_gpios[i]); 3728c2ecf20Sopenharmony_ci i = pdata->num_col_gpios; 3738c2ecf20Sopenharmony_cierr_free_cols: 3748c2ecf20Sopenharmony_ci while (--i >= 0) 3758c2ecf20Sopenharmony_ci gpio_free(pdata->col_gpios[i]); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci return err; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_cistatic void matrix_keypad_free_gpio(struct matrix_keypad *keypad) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata = keypad->pdata; 3838c2ecf20Sopenharmony_ci int i; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (pdata->clustered_irq > 0) { 3868c2ecf20Sopenharmony_ci free_irq(pdata->clustered_irq, keypad); 3878c2ecf20Sopenharmony_ci } else { 3888c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) 3898c2ecf20Sopenharmony_ci free_irq(gpio_to_irq(pdata->row_gpios[i]), keypad); 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_row_gpios; i++) 3938c2ecf20Sopenharmony_ci gpio_free(pdata->row_gpios[i]); 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci for (i = 0; i < pdata->num_col_gpios; i++) 3968c2ecf20Sopenharmony_ci gpio_free(pdata->col_gpios[i]); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 4008c2ecf20Sopenharmony_cistatic struct matrix_keypad_platform_data * 4018c2ecf20Sopenharmony_cimatrix_keypad_parse_dt(struct device *dev) 4028c2ecf20Sopenharmony_ci{ 4038c2ecf20Sopenharmony_ci struct matrix_keypad_platform_data *pdata; 4048c2ecf20Sopenharmony_ci struct device_node *np = dev->of_node; 4058c2ecf20Sopenharmony_ci unsigned int *gpios; 4068c2ecf20Sopenharmony_ci int ret, i, nrow, ncol; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (!np) { 4098c2ecf20Sopenharmony_ci dev_err(dev, "device lacks DT data\n"); 4108c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 4118c2ecf20Sopenharmony_ci } 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); 4148c2ecf20Sopenharmony_ci if (!pdata) { 4158c2ecf20Sopenharmony_ci dev_err(dev, "could not allocate memory for platform data\n"); 4168c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci pdata->num_row_gpios = nrow = of_gpio_named_count(np, "row-gpios"); 4208c2ecf20Sopenharmony_ci pdata->num_col_gpios = ncol = of_gpio_named_count(np, "col-gpios"); 4218c2ecf20Sopenharmony_ci if (nrow <= 0 || ncol <= 0) { 4228c2ecf20Sopenharmony_ci dev_err(dev, "number of keypad rows/columns not specified\n"); 4238c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4248c2ecf20Sopenharmony_ci } 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci if (of_get_property(np, "linux,no-autorepeat", NULL)) 4278c2ecf20Sopenharmony_ci pdata->no_autorepeat = true; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci pdata->wakeup = of_property_read_bool(np, "wakeup-source") || 4308c2ecf20Sopenharmony_ci of_property_read_bool(np, "linux,wakeup"); /* legacy */ 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci if (of_get_property(np, "gpio-activelow", NULL)) 4338c2ecf20Sopenharmony_ci pdata->active_low = true; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci pdata->drive_inactive_cols = 4368c2ecf20Sopenharmony_ci of_property_read_bool(np, "drive-inactive-cols"); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci of_property_read_u32(np, "debounce-delay-ms", &pdata->debounce_ms); 4398c2ecf20Sopenharmony_ci of_property_read_u32(np, "col-scan-delay-us", 4408c2ecf20Sopenharmony_ci &pdata->col_scan_delay_us); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci gpios = devm_kcalloc(dev, 4438c2ecf20Sopenharmony_ci pdata->num_row_gpios + pdata->num_col_gpios, 4448c2ecf20Sopenharmony_ci sizeof(unsigned int), 4458c2ecf20Sopenharmony_ci GFP_KERNEL); 4468c2ecf20Sopenharmony_ci if (!gpios) { 4478c2ecf20Sopenharmony_ci dev_err(dev, "could not allocate memory for gpios\n"); 4488c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci for (i = 0; i < nrow; i++) { 4528c2ecf20Sopenharmony_ci ret = of_get_named_gpio(np, "row-gpios", i); 4538c2ecf20Sopenharmony_ci if (ret < 0) 4548c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4558c2ecf20Sopenharmony_ci gpios[i] = ret; 4568c2ecf20Sopenharmony_ci } 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci for (i = 0; i < ncol; i++) { 4598c2ecf20Sopenharmony_ci ret = of_get_named_gpio(np, "col-gpios", i); 4608c2ecf20Sopenharmony_ci if (ret < 0) 4618c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4628c2ecf20Sopenharmony_ci gpios[nrow + i] = ret; 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci pdata->row_gpios = gpios; 4668c2ecf20Sopenharmony_ci pdata->col_gpios = &gpios[pdata->num_row_gpios]; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci return pdata; 4698c2ecf20Sopenharmony_ci} 4708c2ecf20Sopenharmony_ci#else 4718c2ecf20Sopenharmony_cistatic inline struct matrix_keypad_platform_data * 4728c2ecf20Sopenharmony_cimatrix_keypad_parse_dt(struct device *dev) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci dev_err(dev, "no platform data defined\n"); 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci#endif 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic int matrix_keypad_probe(struct platform_device *pdev) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci const struct matrix_keypad_platform_data *pdata; 4838c2ecf20Sopenharmony_ci struct matrix_keypad *keypad; 4848c2ecf20Sopenharmony_ci struct input_dev *input_dev; 4858c2ecf20Sopenharmony_ci int err; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&pdev->dev); 4888c2ecf20Sopenharmony_ci if (!pdata) { 4898c2ecf20Sopenharmony_ci pdata = matrix_keypad_parse_dt(&pdev->dev); 4908c2ecf20Sopenharmony_ci if (IS_ERR(pdata)) 4918c2ecf20Sopenharmony_ci return PTR_ERR(pdata); 4928c2ecf20Sopenharmony_ci } else if (!pdata->keymap_data) { 4938c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no keymap data defined\n"); 4948c2ecf20Sopenharmony_ci return -EINVAL; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci keypad = kzalloc(sizeof(struct matrix_keypad), GFP_KERNEL); 4988c2ecf20Sopenharmony_ci input_dev = input_allocate_device(); 4998c2ecf20Sopenharmony_ci if (!keypad || !input_dev) { 5008c2ecf20Sopenharmony_ci err = -ENOMEM; 5018c2ecf20Sopenharmony_ci goto err_free_mem; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci keypad->input_dev = input_dev; 5058c2ecf20Sopenharmony_ci keypad->pdata = pdata; 5068c2ecf20Sopenharmony_ci keypad->row_shift = get_count_order(pdata->num_col_gpios); 5078c2ecf20Sopenharmony_ci keypad->stopped = true; 5088c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&keypad->work, matrix_keypad_scan); 5098c2ecf20Sopenharmony_ci spin_lock_init(&keypad->lock); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci input_dev->name = pdev->name; 5128c2ecf20Sopenharmony_ci input_dev->id.bustype = BUS_HOST; 5138c2ecf20Sopenharmony_ci input_dev->dev.parent = &pdev->dev; 5148c2ecf20Sopenharmony_ci input_dev->open = matrix_keypad_start; 5158c2ecf20Sopenharmony_ci input_dev->close = matrix_keypad_stop; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, 5188c2ecf20Sopenharmony_ci pdata->num_row_gpios, 5198c2ecf20Sopenharmony_ci pdata->num_col_gpios, 5208c2ecf20Sopenharmony_ci NULL, input_dev); 5218c2ecf20Sopenharmony_ci if (err) { 5228c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to build keymap\n"); 5238c2ecf20Sopenharmony_ci goto err_free_mem; 5248c2ecf20Sopenharmony_ci } 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (!pdata->no_autorepeat) 5278c2ecf20Sopenharmony_ci __set_bit(EV_REP, input_dev->evbit); 5288c2ecf20Sopenharmony_ci input_set_capability(input_dev, EV_MSC, MSC_SCAN); 5298c2ecf20Sopenharmony_ci input_set_drvdata(input_dev, keypad); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci err = matrix_keypad_init_gpio(pdev, keypad); 5328c2ecf20Sopenharmony_ci if (err) 5338c2ecf20Sopenharmony_ci goto err_free_mem; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci err = input_register_device(keypad->input_dev); 5368c2ecf20Sopenharmony_ci if (err) 5378c2ecf20Sopenharmony_ci goto err_free_gpio; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, pdata->wakeup); 5408c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, keypad); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_cierr_free_gpio: 5458c2ecf20Sopenharmony_ci matrix_keypad_free_gpio(keypad); 5468c2ecf20Sopenharmony_cierr_free_mem: 5478c2ecf20Sopenharmony_ci input_free_device(input_dev); 5488c2ecf20Sopenharmony_ci kfree(keypad); 5498c2ecf20Sopenharmony_ci return err; 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int matrix_keypad_remove(struct platform_device *pdev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci struct matrix_keypad *keypad = platform_get_drvdata(pdev); 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci matrix_keypad_free_gpio(keypad); 5578c2ecf20Sopenharmony_ci input_unregister_device(keypad->input_dev); 5588c2ecf20Sopenharmony_ci kfree(keypad); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci return 0; 5618c2ecf20Sopenharmony_ci} 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 5648c2ecf20Sopenharmony_cistatic const struct of_device_id matrix_keypad_dt_match[] = { 5658c2ecf20Sopenharmony_ci { .compatible = "gpio-matrix-keypad" }, 5668c2ecf20Sopenharmony_ci { } 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, matrix_keypad_dt_match); 5698c2ecf20Sopenharmony_ci#endif 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_cistatic struct platform_driver matrix_keypad_driver = { 5728c2ecf20Sopenharmony_ci .probe = matrix_keypad_probe, 5738c2ecf20Sopenharmony_ci .remove = matrix_keypad_remove, 5748c2ecf20Sopenharmony_ci .driver = { 5758c2ecf20Sopenharmony_ci .name = "matrix-keypad", 5768c2ecf20Sopenharmony_ci .pm = &matrix_keypad_pm_ops, 5778c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(matrix_keypad_dt_match), 5788c2ecf20Sopenharmony_ci }, 5798c2ecf20Sopenharmony_ci}; 5808c2ecf20Sopenharmony_cimodule_platform_driver(matrix_keypad_driver); 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); 5838c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO Driven Matrix Keypad Driver"); 5848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 5858c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:matrix-keypad"); 586