18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix 48c2ecf20Sopenharmony_ci * keyboard controller 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright (c) 2009-2011, NVIDIA Corporation. 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/input.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_device.h> 188c2ecf20Sopenharmony_ci#include <linux/clk.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/input/matrix_keypad.h> 218c2ecf20Sopenharmony_ci#include <linux/reset.h> 228c2ecf20Sopenharmony_ci#include <linux/err.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define KBC_MAX_KPENT 8 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Maximum row/column supported by Tegra KBC yet is 16x8 */ 278c2ecf20Sopenharmony_ci#define KBC_MAX_GPIO 24 288c2ecf20Sopenharmony_ci/* Maximum keys supported by Tegra KBC yet is 16 x 8*/ 298c2ecf20Sopenharmony_ci#define KBC_MAX_KEY (16 * 8) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define KBC_MAX_DEBOUNCE_CNT 0x3ffu 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci/* KBC row scan time and delay for beginning the row scan. */ 348c2ecf20Sopenharmony_ci#define KBC_ROW_SCAN_TIME 16 358c2ecf20Sopenharmony_ci#define KBC_ROW_SCAN_DLY 5 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* KBC uses a 32KHz clock so a cycle = 1/32Khz */ 388c2ecf20Sopenharmony_ci#define KBC_CYCLE_MS 32 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* KBC Registers */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* KBC Control Register */ 438c2ecf20Sopenharmony_ci#define KBC_CONTROL_0 0x0 448c2ecf20Sopenharmony_ci#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) 458c2ecf20Sopenharmony_ci#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) 468c2ecf20Sopenharmony_ci#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) 478c2ecf20Sopenharmony_ci#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) 488c2ecf20Sopenharmony_ci#define KBC_CONTROL_KBC_EN (1 << 0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* KBC Interrupt Register */ 518c2ecf20Sopenharmony_ci#define KBC_INT_0 0x4 528c2ecf20Sopenharmony_ci#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) 538c2ecf20Sopenharmony_ci#define KBC_INT_KEYPRESS_INT_STATUS (1 << 0) 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define KBC_ROW_CFG0_0 0x8 568c2ecf20Sopenharmony_ci#define KBC_COL_CFG0_0 0x18 578c2ecf20Sopenharmony_ci#define KBC_TO_CNT_0 0x24 588c2ecf20Sopenharmony_ci#define KBC_INIT_DLY_0 0x28 598c2ecf20Sopenharmony_ci#define KBC_RPT_DLY_0 0x2c 608c2ecf20Sopenharmony_ci#define KBC_KP_ENT0_0 0x30 618c2ecf20Sopenharmony_ci#define KBC_KP_ENT1_0 0x34 628c2ecf20Sopenharmony_ci#define KBC_ROW0_MASK_0 0x38 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define KBC_ROW_SHIFT 3 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cienum tegra_pin_type { 678c2ecf20Sopenharmony_ci PIN_CFG_IGNORE, 688c2ecf20Sopenharmony_ci PIN_CFG_COL, 698c2ecf20Sopenharmony_ci PIN_CFG_ROW, 708c2ecf20Sopenharmony_ci}; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci/* Tegra KBC hw support */ 738c2ecf20Sopenharmony_cistruct tegra_kbc_hw_support { 748c2ecf20Sopenharmony_ci int max_rows; 758c2ecf20Sopenharmony_ci int max_columns; 768c2ecf20Sopenharmony_ci}; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistruct tegra_kbc_pin_cfg { 798c2ecf20Sopenharmony_ci enum tegra_pin_type type; 808c2ecf20Sopenharmony_ci unsigned char num; 818c2ecf20Sopenharmony_ci}; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistruct tegra_kbc { 848c2ecf20Sopenharmony_ci struct device *dev; 858c2ecf20Sopenharmony_ci unsigned int debounce_cnt; 868c2ecf20Sopenharmony_ci unsigned int repeat_cnt; 878c2ecf20Sopenharmony_ci struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; 888c2ecf20Sopenharmony_ci const struct matrix_keymap_data *keymap_data; 898c2ecf20Sopenharmony_ci bool wakeup; 908c2ecf20Sopenharmony_ci void __iomem *mmio; 918c2ecf20Sopenharmony_ci struct input_dev *idev; 928c2ecf20Sopenharmony_ci int irq; 938c2ecf20Sopenharmony_ci spinlock_t lock; 948c2ecf20Sopenharmony_ci unsigned int repoll_dly; 958c2ecf20Sopenharmony_ci unsigned long cp_dly_jiffies; 968c2ecf20Sopenharmony_ci unsigned int cp_to_wkup_dly; 978c2ecf20Sopenharmony_ci bool use_fn_map; 988c2ecf20Sopenharmony_ci bool use_ghost_filter; 998c2ecf20Sopenharmony_ci bool keypress_caused_wake; 1008c2ecf20Sopenharmony_ci unsigned short keycode[KBC_MAX_KEY * 2]; 1018c2ecf20Sopenharmony_ci unsigned short current_keys[KBC_MAX_KPENT]; 1028c2ecf20Sopenharmony_ci unsigned int num_pressed_keys; 1038c2ecf20Sopenharmony_ci u32 wakeup_key; 1048c2ecf20Sopenharmony_ci struct timer_list timer; 1058c2ecf20Sopenharmony_ci struct clk *clk; 1068c2ecf20Sopenharmony_ci struct reset_control *rst; 1078c2ecf20Sopenharmony_ci const struct tegra_kbc_hw_support *hw_support; 1088c2ecf20Sopenharmony_ci int max_keys; 1098c2ecf20Sopenharmony_ci int num_rows_and_columns; 1108c2ecf20Sopenharmony_ci}; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void tegra_kbc_report_released_keys(struct input_dev *input, 1138c2ecf20Sopenharmony_ci unsigned short old_keycodes[], 1148c2ecf20Sopenharmony_ci unsigned int old_num_keys, 1158c2ecf20Sopenharmony_ci unsigned short new_keycodes[], 1168c2ecf20Sopenharmony_ci unsigned int new_num_keys) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci unsigned int i, j; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci for (i = 0; i < old_num_keys; i++) { 1218c2ecf20Sopenharmony_ci for (j = 0; j < new_num_keys; j++) 1228c2ecf20Sopenharmony_ci if (old_keycodes[i] == new_keycodes[j]) 1238c2ecf20Sopenharmony_ci break; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (j == new_num_keys) 1268c2ecf20Sopenharmony_ci input_report_key(input, old_keycodes[i], 0); 1278c2ecf20Sopenharmony_ci } 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic void tegra_kbc_report_pressed_keys(struct input_dev *input, 1318c2ecf20Sopenharmony_ci unsigned char scancodes[], 1328c2ecf20Sopenharmony_ci unsigned short keycodes[], 1338c2ecf20Sopenharmony_ci unsigned int num_pressed_keys) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci unsigned int i; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci for (i = 0; i < num_pressed_keys; i++) { 1388c2ecf20Sopenharmony_ci input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); 1398c2ecf20Sopenharmony_ci input_report_key(input, keycodes[i], 1); 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic void tegra_kbc_report_keys(struct tegra_kbc *kbc) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci unsigned char scancodes[KBC_MAX_KPENT]; 1468c2ecf20Sopenharmony_ci unsigned short keycodes[KBC_MAX_KPENT]; 1478c2ecf20Sopenharmony_ci u32 val = 0; 1488c2ecf20Sopenharmony_ci unsigned int i; 1498c2ecf20Sopenharmony_ci unsigned int num_down = 0; 1508c2ecf20Sopenharmony_ci bool fn_keypress = false; 1518c2ecf20Sopenharmony_ci bool key_in_same_row = false; 1528c2ecf20Sopenharmony_ci bool key_in_same_col = false; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci for (i = 0; i < KBC_MAX_KPENT; i++) { 1558c2ecf20Sopenharmony_ci if ((i % 4) == 0) 1568c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT0_0 + i); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci if (val & 0x80) { 1598c2ecf20Sopenharmony_ci unsigned int col = val & 0x07; 1608c2ecf20Sopenharmony_ci unsigned int row = (val >> 3) & 0x0f; 1618c2ecf20Sopenharmony_ci unsigned char scancode = 1628c2ecf20Sopenharmony_ci MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci scancodes[num_down] = scancode; 1658c2ecf20Sopenharmony_ci keycodes[num_down] = kbc->keycode[scancode]; 1668c2ecf20Sopenharmony_ci /* If driver uses Fn map, do not report the Fn key. */ 1678c2ecf20Sopenharmony_ci if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map) 1688c2ecf20Sopenharmony_ci fn_keypress = true; 1698c2ecf20Sopenharmony_ci else 1708c2ecf20Sopenharmony_ci num_down++; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci val >>= 8; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * Matrix keyboard designs are prone to keyboard ghosting. 1788c2ecf20Sopenharmony_ci * Ghosting occurs if there are 3 keys such that - 1798c2ecf20Sopenharmony_ci * any 2 of the 3 keys share a row, and any 2 of them share a column. 1808c2ecf20Sopenharmony_ci * If so ignore the key presses for this iteration. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci if (kbc->use_ghost_filter && num_down >= 3) { 1838c2ecf20Sopenharmony_ci for (i = 0; i < num_down; i++) { 1848c2ecf20Sopenharmony_ci unsigned int j; 1858c2ecf20Sopenharmony_ci u8 curr_col = scancodes[i] & 0x07; 1868c2ecf20Sopenharmony_ci u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* 1898c2ecf20Sopenharmony_ci * Find 2 keys such that one key is in the same row 1908c2ecf20Sopenharmony_ci * and the other is in the same column as the i-th key. 1918c2ecf20Sopenharmony_ci */ 1928c2ecf20Sopenharmony_ci for (j = i + 1; j < num_down; j++) { 1938c2ecf20Sopenharmony_ci u8 col = scancodes[j] & 0x07; 1948c2ecf20Sopenharmony_ci u8 row = scancodes[j] >> KBC_ROW_SHIFT; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci if (col == curr_col) 1978c2ecf20Sopenharmony_ci key_in_same_col = true; 1988c2ecf20Sopenharmony_ci if (row == curr_row) 1998c2ecf20Sopenharmony_ci key_in_same_row = true; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* 2058c2ecf20Sopenharmony_ci * If the platform uses Fn keymaps, translate keys on a Fn keypress. 2068c2ecf20Sopenharmony_ci * Function keycodes are max_keys apart from the plain keycodes. 2078c2ecf20Sopenharmony_ci */ 2088c2ecf20Sopenharmony_ci if (fn_keypress) { 2098c2ecf20Sopenharmony_ci for (i = 0; i < num_down; i++) { 2108c2ecf20Sopenharmony_ci scancodes[i] += kbc->max_keys; 2118c2ecf20Sopenharmony_ci keycodes[i] = kbc->keycode[scancodes[i]]; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci /* Ignore the key presses for this iteration? */ 2168c2ecf20Sopenharmony_ci if (key_in_same_col && key_in_same_row) 2178c2ecf20Sopenharmony_ci return; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci tegra_kbc_report_released_keys(kbc->idev, 2208c2ecf20Sopenharmony_ci kbc->current_keys, kbc->num_pressed_keys, 2218c2ecf20Sopenharmony_ci keycodes, num_down); 2228c2ecf20Sopenharmony_ci tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); 2238c2ecf20Sopenharmony_ci input_sync(kbc->idev); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); 2268c2ecf20Sopenharmony_ci kbc->num_pressed_keys = num_down; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci u32 val; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 2348c2ecf20Sopenharmony_ci if (enable) 2358c2ecf20Sopenharmony_ci val |= KBC_CONTROL_FIFO_CNT_INT_EN; 2368c2ecf20Sopenharmony_ci else 2378c2ecf20Sopenharmony_ci val &= ~KBC_CONTROL_FIFO_CNT_INT_EN; 2388c2ecf20Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 2398c2ecf20Sopenharmony_ci} 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cistatic void tegra_kbc_keypress_timer(struct timer_list *t) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = from_timer(kbc, t, timer); 2448c2ecf20Sopenharmony_ci unsigned long flags; 2458c2ecf20Sopenharmony_ci u32 val; 2468c2ecf20Sopenharmony_ci unsigned int i; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; 2518c2ecf20Sopenharmony_ci if (val) { 2528c2ecf20Sopenharmony_ci unsigned long dly; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci tegra_kbc_report_keys(kbc); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * If more than one keys are pressed we need not wait 2588c2ecf20Sopenharmony_ci * for the repoll delay. 2598c2ecf20Sopenharmony_ci */ 2608c2ecf20Sopenharmony_ci dly = (val == 1) ? kbc->repoll_dly : 1; 2618c2ecf20Sopenharmony_ci mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci /* Release any pressed keys and exit the polling loop */ 2648c2ecf20Sopenharmony_ci for (i = 0; i < kbc->num_pressed_keys; i++) 2658c2ecf20Sopenharmony_ci input_report_key(kbc->idev, kbc->current_keys[i], 0); 2668c2ecf20Sopenharmony_ci input_sync(kbc->idev); 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci kbc->num_pressed_keys = 0; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci /* All keys are released so enable the keypress interrupt */ 2718c2ecf20Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, true); 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic irqreturn_t tegra_kbc_isr(int irq, void *args) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = args; 2808c2ecf20Sopenharmony_ci unsigned long flags; 2818c2ecf20Sopenharmony_ci u32 val; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * Quickly bail out & reenable interrupts if the fifo threshold 2878c2ecf20Sopenharmony_ci * count interrupt wasn't the interrupt source 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_INT_0); 2908c2ecf20Sopenharmony_ci writel(val, kbc->mmio + KBC_INT_0); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (val & KBC_INT_FIFO_CNT_INT_STATUS) { 2938c2ecf20Sopenharmony_ci /* 2948c2ecf20Sopenharmony_ci * Until all keys are released, defer further processing to 2958c2ecf20Sopenharmony_ci * the polling loop in tegra_kbc_keypress_timer. 2968c2ecf20Sopenharmony_ci */ 2978c2ecf20Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, false); 2988c2ecf20Sopenharmony_ci mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); 2998c2ecf20Sopenharmony_ci } else if (val & KBC_INT_KEYPRESS_INT_STATUS) { 3008c2ecf20Sopenharmony_ci /* We can be here only through system resume path */ 3018c2ecf20Sopenharmony_ci kbc->keypress_caused_wake = true; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_cistatic void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int i; 3128c2ecf20Sopenharmony_ci unsigned int rst_val; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Either mask all keys or none. */ 3158c2ecf20Sopenharmony_ci rst_val = (filter && !kbc->wakeup) ? ~0 : 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci for (i = 0; i < kbc->hw_support->max_rows; i++) 3188c2ecf20Sopenharmony_ci writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_cistatic void tegra_kbc_config_pins(struct tegra_kbc *kbc) 3228c2ecf20Sopenharmony_ci{ 3238c2ecf20Sopenharmony_ci int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci for (i = 0; i < KBC_MAX_GPIO; i++) { 3268c2ecf20Sopenharmony_ci u32 r_shft = 5 * (i % 6); 3278c2ecf20Sopenharmony_ci u32 c_shft = 4 * (i % 8); 3288c2ecf20Sopenharmony_ci u32 r_mask = 0x1f << r_shft; 3298c2ecf20Sopenharmony_ci u32 c_mask = 0x0f << c_shft; 3308c2ecf20Sopenharmony_ci u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; 3318c2ecf20Sopenharmony_ci u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; 3328c2ecf20Sopenharmony_ci u32 row_cfg = readl(kbc->mmio + r_offs); 3338c2ecf20Sopenharmony_ci u32 col_cfg = readl(kbc->mmio + c_offs); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci row_cfg &= ~r_mask; 3368c2ecf20Sopenharmony_ci col_cfg &= ~c_mask; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci switch (kbc->pin_cfg[i].type) { 3398c2ecf20Sopenharmony_ci case PIN_CFG_ROW: 3408c2ecf20Sopenharmony_ci row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft; 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci case PIN_CFG_COL: 3448c2ecf20Sopenharmony_ci col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci case PIN_CFG_IGNORE: 3488c2ecf20Sopenharmony_ci break; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci writel(row_cfg, kbc->mmio + r_offs); 3528c2ecf20Sopenharmony_ci writel(col_cfg, kbc->mmio + c_offs); 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci} 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cistatic int tegra_kbc_start(struct tegra_kbc *kbc) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci unsigned int debounce_cnt; 3598c2ecf20Sopenharmony_ci u32 val = 0; 3608c2ecf20Sopenharmony_ci int ret; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci ret = clk_prepare_enable(kbc->clk); 3638c2ecf20Sopenharmony_ci if (ret) 3648c2ecf20Sopenharmony_ci return ret; 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Reset the KBC controller to clear all previous status.*/ 3678c2ecf20Sopenharmony_ci reset_control_assert(kbc->rst); 3688c2ecf20Sopenharmony_ci udelay(100); 3698c2ecf20Sopenharmony_ci reset_control_deassert(kbc->rst); 3708c2ecf20Sopenharmony_ci udelay(100); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci tegra_kbc_config_pins(kbc); 3738c2ecf20Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, false); 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci /* Keyboard debounce count is maximum of 12 bits. */ 3788c2ecf20Sopenharmony_ci debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); 3798c2ecf20Sopenharmony_ci val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); 3808c2ecf20Sopenharmony_ci val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ 3818c2ecf20Sopenharmony_ci val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ 3828c2ecf20Sopenharmony_ci val |= KBC_CONTROL_KBC_EN; /* enable */ 3838c2ecf20Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Compute the delay(ns) from interrupt mode to continuous polling 3878c2ecf20Sopenharmony_ci * mode so the timer routine is scheduled appropriately. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_INIT_DLY_0); 3908c2ecf20Sopenharmony_ci kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci kbc->num_pressed_keys = 0; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* 3958c2ecf20Sopenharmony_ci * Atomically clear out any remaining entries in the key FIFO 3968c2ecf20Sopenharmony_ci * and enable keyboard interrupts. 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci while (1) { 3998c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_INT_0); 4008c2ecf20Sopenharmony_ci val >>= 4; 4018c2ecf20Sopenharmony_ci if (!val) 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT0_0); 4058c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT1_0); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci writel(0x7, kbc->mmio + KBC_INT_0); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci enable_irq(kbc->irq); 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci return 0; 4128c2ecf20Sopenharmony_ci} 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_cistatic void tegra_kbc_stop(struct tegra_kbc *kbc) 4158c2ecf20Sopenharmony_ci{ 4168c2ecf20Sopenharmony_ci unsigned long flags; 4178c2ecf20Sopenharmony_ci u32 val; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 4208c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 4218c2ecf20Sopenharmony_ci val &= ~1; 4228c2ecf20Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 4238c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci disable_irq(kbc->irq); 4268c2ecf20Sopenharmony_ci del_timer_sync(&kbc->timer); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci clk_disable_unprepare(kbc->clk); 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic int tegra_kbc_open(struct input_dev *dev) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = input_get_drvdata(dev); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return tegra_kbc_start(kbc); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic void tegra_kbc_close(struct input_dev *dev) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = input_get_drvdata(dev); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return tegra_kbc_stop(kbc); 4438c2ecf20Sopenharmony_ci} 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cistatic bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, 4468c2ecf20Sopenharmony_ci unsigned int *num_rows) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci int i; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci *num_rows = 0; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci for (i = 0; i < KBC_MAX_GPIO; i++) { 4538c2ecf20Sopenharmony_ci const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i]; 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_ci switch (pin_cfg->type) { 4568c2ecf20Sopenharmony_ci case PIN_CFG_ROW: 4578c2ecf20Sopenharmony_ci if (pin_cfg->num >= kbc->hw_support->max_rows) { 4588c2ecf20Sopenharmony_ci dev_err(kbc->dev, 4598c2ecf20Sopenharmony_ci "pin_cfg[%d]: invalid row number %d\n", 4608c2ecf20Sopenharmony_ci i, pin_cfg->num); 4618c2ecf20Sopenharmony_ci return false; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci (*num_rows)++; 4648c2ecf20Sopenharmony_ci break; 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci case PIN_CFG_COL: 4678c2ecf20Sopenharmony_ci if (pin_cfg->num >= kbc->hw_support->max_columns) { 4688c2ecf20Sopenharmony_ci dev_err(kbc->dev, 4698c2ecf20Sopenharmony_ci "pin_cfg[%d]: invalid column number %d\n", 4708c2ecf20Sopenharmony_ci i, pin_cfg->num); 4718c2ecf20Sopenharmony_ci return false; 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci break; 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_ci case PIN_CFG_IGNORE: 4768c2ecf20Sopenharmony_ci break; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci default: 4798c2ecf20Sopenharmony_ci dev_err(kbc->dev, 4808c2ecf20Sopenharmony_ci "pin_cfg[%d]: invalid entry type %d\n", 4818c2ecf20Sopenharmony_ci pin_cfg->type, pin_cfg->num); 4828c2ecf20Sopenharmony_ci return false; 4838c2ecf20Sopenharmony_ci } 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci return true; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int tegra_kbc_parse_dt(struct tegra_kbc *kbc) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci struct device_node *np = kbc->dev->of_node; 4928c2ecf20Sopenharmony_ci u32 prop; 4938c2ecf20Sopenharmony_ci int i; 4948c2ecf20Sopenharmony_ci u32 num_rows = 0; 4958c2ecf20Sopenharmony_ci u32 num_cols = 0; 4968c2ecf20Sopenharmony_ci u32 cols_cfg[KBC_MAX_GPIO]; 4978c2ecf20Sopenharmony_ci u32 rows_cfg[KBC_MAX_GPIO]; 4988c2ecf20Sopenharmony_ci int proplen; 4998c2ecf20Sopenharmony_ci int ret; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) 5028c2ecf20Sopenharmony_ci kbc->debounce_cnt = prop; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) 5058c2ecf20Sopenharmony_ci kbc->repeat_cnt = prop; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) 5088c2ecf20Sopenharmony_ci kbc->use_ghost_filter = true; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source") || 5118c2ecf20Sopenharmony_ci of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */ 5128c2ecf20Sopenharmony_ci kbc->wakeup = true; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { 5158c2ecf20Sopenharmony_ci dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n"); 5168c2ecf20Sopenharmony_ci return -ENOENT; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci num_rows = proplen / sizeof(u32); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { 5218c2ecf20Sopenharmony_ci dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n"); 5228c2ecf20Sopenharmony_ci return -ENOENT; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci num_cols = proplen / sizeof(u32); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci if (num_rows > kbc->hw_support->max_rows) { 5278c2ecf20Sopenharmony_ci dev_err(kbc->dev, 5288c2ecf20Sopenharmony_ci "Number of rows is more than supported by hardware\n"); 5298c2ecf20Sopenharmony_ci return -EINVAL; 5308c2ecf20Sopenharmony_ci } 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci if (num_cols > kbc->hw_support->max_columns) { 5338c2ecf20Sopenharmony_ci dev_err(kbc->dev, 5348c2ecf20Sopenharmony_ci "Number of cols is more than supported by hardware\n"); 5358c2ecf20Sopenharmony_ci return -EINVAL; 5368c2ecf20Sopenharmony_ci } 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci if (!of_get_property(np, "linux,keymap", &proplen)) { 5398c2ecf20Sopenharmony_ci dev_err(kbc->dev, "property linux,keymap not found\n"); 5408c2ecf20Sopenharmony_ci return -ENOENT; 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { 5448c2ecf20Sopenharmony_ci dev_err(kbc->dev, 5458c2ecf20Sopenharmony_ci "keypad rows/columns not properly specified\n"); 5468c2ecf20Sopenharmony_ci return -EINVAL; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Set all pins as non-configured */ 5508c2ecf20Sopenharmony_ci for (i = 0; i < kbc->num_rows_and_columns; i++) 5518c2ecf20Sopenharmony_ci kbc->pin_cfg[i].type = PIN_CFG_IGNORE; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", 5548c2ecf20Sopenharmony_ci rows_cfg, num_rows); 5558c2ecf20Sopenharmony_ci if (ret < 0) { 5568c2ecf20Sopenharmony_ci dev_err(kbc->dev, "Rows configurations are not proper\n"); 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", 5618c2ecf20Sopenharmony_ci cols_cfg, num_cols); 5628c2ecf20Sopenharmony_ci if (ret < 0) { 5638c2ecf20Sopenharmony_ci dev_err(kbc->dev, "Cols configurations are not proper\n"); 5648c2ecf20Sopenharmony_ci return -EINVAL; 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci for (i = 0; i < num_rows; i++) { 5688c2ecf20Sopenharmony_ci kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; 5698c2ecf20Sopenharmony_ci kbc->pin_cfg[rows_cfg[i]].num = i; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci for (i = 0; i < num_cols; i++) { 5738c2ecf20Sopenharmony_ci kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; 5748c2ecf20Sopenharmony_ci kbc->pin_cfg[cols_cfg[i]].num = i; 5758c2ecf20Sopenharmony_ci } 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci return 0; 5788c2ecf20Sopenharmony_ci} 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_cistatic const struct tegra_kbc_hw_support tegra20_kbc_hw_support = { 5818c2ecf20Sopenharmony_ci .max_rows = 16, 5828c2ecf20Sopenharmony_ci .max_columns = 8, 5838c2ecf20Sopenharmony_ci}; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic const struct tegra_kbc_hw_support tegra11_kbc_hw_support = { 5868c2ecf20Sopenharmony_ci .max_rows = 11, 5878c2ecf20Sopenharmony_ci .max_columns = 8, 5888c2ecf20Sopenharmony_ci}; 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic const struct of_device_id tegra_kbc_of_match[] = { 5918c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support}, 5928c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support}, 5938c2ecf20Sopenharmony_ci { .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support}, 5948c2ecf20Sopenharmony_ci { }, 5958c2ecf20Sopenharmony_ci}; 5968c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_kbc_of_match); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_cistatic int tegra_kbc_probe(struct platform_device *pdev) 5998c2ecf20Sopenharmony_ci{ 6008c2ecf20Sopenharmony_ci struct tegra_kbc *kbc; 6018c2ecf20Sopenharmony_ci struct resource *res; 6028c2ecf20Sopenharmony_ci int err; 6038c2ecf20Sopenharmony_ci int num_rows = 0; 6048c2ecf20Sopenharmony_ci unsigned int debounce_cnt; 6058c2ecf20Sopenharmony_ci unsigned int scan_time_rows; 6068c2ecf20Sopenharmony_ci unsigned int keymap_rows; 6078c2ecf20Sopenharmony_ci const struct of_device_id *match; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci match = of_match_device(tegra_kbc_of_match, &pdev->dev); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); 6128c2ecf20Sopenharmony_ci if (!kbc) { 6138c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); 6148c2ecf20Sopenharmony_ci return -ENOMEM; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci kbc->dev = &pdev->dev; 6188c2ecf20Sopenharmony_ci kbc->hw_support = match->data; 6198c2ecf20Sopenharmony_ci kbc->max_keys = kbc->hw_support->max_rows * 6208c2ecf20Sopenharmony_ci kbc->hw_support->max_columns; 6218c2ecf20Sopenharmony_ci kbc->num_rows_and_columns = kbc->hw_support->max_rows + 6228c2ecf20Sopenharmony_ci kbc->hw_support->max_columns; 6238c2ecf20Sopenharmony_ci keymap_rows = kbc->max_keys; 6248c2ecf20Sopenharmony_ci spin_lock_init(&kbc->lock); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci err = tegra_kbc_parse_dt(kbc); 6278c2ecf20Sopenharmony_ci if (err) 6288c2ecf20Sopenharmony_ci return err; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci if (!tegra_kbc_check_pin_cfg(kbc, &num_rows)) 6318c2ecf20Sopenharmony_ci return -EINVAL; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci kbc->irq = platform_get_irq(pdev, 0); 6348c2ecf20Sopenharmony_ci if (kbc->irq < 0) 6358c2ecf20Sopenharmony_ci return -ENXIO; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci kbc->idev = devm_input_allocate_device(&pdev->dev); 6388c2ecf20Sopenharmony_ci if (!kbc->idev) { 6398c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate input device\n"); 6408c2ecf20Sopenharmony_ci return -ENOMEM; 6418c2ecf20Sopenharmony_ci } 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci timer_setup(&kbc->timer, tegra_kbc_keypress_timer, 0); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 6468c2ecf20Sopenharmony_ci kbc->mmio = devm_ioremap_resource(&pdev->dev, res); 6478c2ecf20Sopenharmony_ci if (IS_ERR(kbc->mmio)) 6488c2ecf20Sopenharmony_ci return PTR_ERR(kbc->mmio); 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ci kbc->clk = devm_clk_get(&pdev->dev, NULL); 6518c2ecf20Sopenharmony_ci if (IS_ERR(kbc->clk)) { 6528c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get keyboard clock\n"); 6538c2ecf20Sopenharmony_ci return PTR_ERR(kbc->clk); 6548c2ecf20Sopenharmony_ci } 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci kbc->rst = devm_reset_control_get(&pdev->dev, "kbc"); 6578c2ecf20Sopenharmony_ci if (IS_ERR(kbc->rst)) { 6588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get keyboard reset\n"); 6598c2ecf20Sopenharmony_ci return PTR_ERR(kbc->rst); 6608c2ecf20Sopenharmony_ci } 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci /* 6638c2ecf20Sopenharmony_ci * The time delay between two consecutive reads of the FIFO is 6648c2ecf20Sopenharmony_ci * the sum of the repeat time and the time taken for scanning 6658c2ecf20Sopenharmony_ci * the rows. There is an additional delay before the row scanning 6668c2ecf20Sopenharmony_ci * starts. The repoll delay is computed in milliseconds. 6678c2ecf20Sopenharmony_ci */ 6688c2ecf20Sopenharmony_ci debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); 6698c2ecf20Sopenharmony_ci scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; 6708c2ecf20Sopenharmony_ci kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt; 6718c2ecf20Sopenharmony_ci kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci kbc->idev->name = pdev->name; 6748c2ecf20Sopenharmony_ci kbc->idev->id.bustype = BUS_HOST; 6758c2ecf20Sopenharmony_ci kbc->idev->dev.parent = &pdev->dev; 6768c2ecf20Sopenharmony_ci kbc->idev->open = tegra_kbc_open; 6778c2ecf20Sopenharmony_ci kbc->idev->close = tegra_kbc_close; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (kbc->keymap_data && kbc->use_fn_map) 6808c2ecf20Sopenharmony_ci keymap_rows *= 2; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, 6838c2ecf20Sopenharmony_ci keymap_rows, 6848c2ecf20Sopenharmony_ci kbc->hw_support->max_columns, 6858c2ecf20Sopenharmony_ci kbc->keycode, kbc->idev); 6868c2ecf20Sopenharmony_ci if (err) { 6878c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to setup keymap\n"); 6888c2ecf20Sopenharmony_ci return err; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci __set_bit(EV_REP, kbc->idev->evbit); 6928c2ecf20Sopenharmony_ci input_set_capability(kbc->idev, EV_MSC, MSC_SCAN); 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci input_set_drvdata(kbc->idev, kbc); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, 6978c2ecf20Sopenharmony_ci IRQF_TRIGGER_HIGH, pdev->name, kbc); 6988c2ecf20Sopenharmony_ci if (err) { 6998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); 7008c2ecf20Sopenharmony_ci return err; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci disable_irq(kbc->irq); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci err = input_register_device(kbc->idev); 7068c2ecf20Sopenharmony_ci if (err) { 7078c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register input device\n"); 7088c2ecf20Sopenharmony_ci return err; 7098c2ecf20Sopenharmony_ci } 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, kbc); 7128c2ecf20Sopenharmony_ci device_init_wakeup(&pdev->dev, kbc->wakeup); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci return 0; 7158c2ecf20Sopenharmony_ci} 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 7188c2ecf20Sopenharmony_cistatic void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) 7198c2ecf20Sopenharmony_ci{ 7208c2ecf20Sopenharmony_ci u32 val; 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 7238c2ecf20Sopenharmony_ci if (enable) 7248c2ecf20Sopenharmony_ci val |= KBC_CONTROL_KEYPRESS_INT_EN; 7258c2ecf20Sopenharmony_ci else 7268c2ecf20Sopenharmony_ci val &= ~KBC_CONTROL_KEYPRESS_INT_EN; 7278c2ecf20Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int tegra_kbc_suspend(struct device *dev) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7338c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = platform_get_drvdata(pdev); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci mutex_lock(&kbc->idev->mutex); 7368c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 7378c2ecf20Sopenharmony_ci disable_irq(kbc->irq); 7388c2ecf20Sopenharmony_ci del_timer_sync(&kbc->timer); 7398c2ecf20Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, false); 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Forcefully clear the interrupt status */ 7428c2ecf20Sopenharmony_ci writel(0x7, kbc->mmio + KBC_INT_0); 7438c2ecf20Sopenharmony_ci /* 7448c2ecf20Sopenharmony_ci * Store the previous resident time of continuous polling mode. 7458c2ecf20Sopenharmony_ci * Force the keyboard into interrupt mode. 7468c2ecf20Sopenharmony_ci */ 7478c2ecf20Sopenharmony_ci kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0); 7488c2ecf20Sopenharmony_ci writel(0, kbc->mmio + KBC_TO_CNT_0); 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, true); 7518c2ecf20Sopenharmony_ci msleep(30); 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci kbc->keypress_caused_wake = false; 7548c2ecf20Sopenharmony_ci /* Enable keypress interrupt before going into suspend. */ 7558c2ecf20Sopenharmony_ci tegra_kbc_set_keypress_interrupt(kbc, true); 7568c2ecf20Sopenharmony_ci enable_irq(kbc->irq); 7578c2ecf20Sopenharmony_ci enable_irq_wake(kbc->irq); 7588c2ecf20Sopenharmony_ci } else { 7598c2ecf20Sopenharmony_ci if (kbc->idev->users) 7608c2ecf20Sopenharmony_ci tegra_kbc_stop(kbc); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci mutex_unlock(&kbc->idev->mutex); 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci return 0; 7658c2ecf20Sopenharmony_ci} 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cistatic int tegra_kbc_resume(struct device *dev) 7688c2ecf20Sopenharmony_ci{ 7698c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7708c2ecf20Sopenharmony_ci struct tegra_kbc *kbc = platform_get_drvdata(pdev); 7718c2ecf20Sopenharmony_ci int err = 0; 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci mutex_lock(&kbc->idev->mutex); 7748c2ecf20Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 7758c2ecf20Sopenharmony_ci disable_irq_wake(kbc->irq); 7768c2ecf20Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, false); 7778c2ecf20Sopenharmony_ci /* We will use fifo interrupts for key detection. */ 7788c2ecf20Sopenharmony_ci tegra_kbc_set_keypress_interrupt(kbc, false); 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci /* Restore the resident time of continuous polling mode. */ 7818c2ecf20Sopenharmony_ci writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, true); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (kbc->keypress_caused_wake && kbc->wakeup_key) { 7868c2ecf20Sopenharmony_ci /* 7878c2ecf20Sopenharmony_ci * We can't report events directly from the ISR 7888c2ecf20Sopenharmony_ci * because timekeeping is stopped when processing 7898c2ecf20Sopenharmony_ci * wakeup request and we get a nasty warning when 7908c2ecf20Sopenharmony_ci * we try to call do_gettimeofday() in evdev 7918c2ecf20Sopenharmony_ci * handler. 7928c2ecf20Sopenharmony_ci */ 7938c2ecf20Sopenharmony_ci input_report_key(kbc->idev, kbc->wakeup_key, 1); 7948c2ecf20Sopenharmony_ci input_sync(kbc->idev); 7958c2ecf20Sopenharmony_ci input_report_key(kbc->idev, kbc->wakeup_key, 0); 7968c2ecf20Sopenharmony_ci input_sync(kbc->idev); 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci } else { 7998c2ecf20Sopenharmony_ci if (kbc->idev->users) 8008c2ecf20Sopenharmony_ci err = tegra_kbc_start(kbc); 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci mutex_unlock(&kbc->idev->mutex); 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return err; 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci#endif 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic struct platform_driver tegra_kbc_driver = { 8118c2ecf20Sopenharmony_ci .probe = tegra_kbc_probe, 8128c2ecf20Sopenharmony_ci .driver = { 8138c2ecf20Sopenharmony_ci .name = "tegra-kbc", 8148c2ecf20Sopenharmony_ci .pm = &tegra_kbc_pm_ops, 8158c2ecf20Sopenharmony_ci .of_match_table = tegra_kbc_of_match, 8168c2ecf20Sopenharmony_ci }, 8178c2ecf20Sopenharmony_ci}; 8188c2ecf20Sopenharmony_cimodule_platform_driver(tegra_kbc_driver); 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 8218c2ecf20Sopenharmony_ciMODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>"); 8228c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); 8238c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:tegra-kbc"); 824