162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix 462306a36Sopenharmony_ci * keyboard controller 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2009-2011, NVIDIA Corporation. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/input.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/io.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/of_device.h> 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/input/matrix_keypad.h> 2162306a36Sopenharmony_ci#include <linux/reset.h> 2262306a36Sopenharmony_ci#include <linux/err.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define KBC_MAX_KPENT 8 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* Maximum row/column supported by Tegra KBC yet is 16x8 */ 2762306a36Sopenharmony_ci#define KBC_MAX_GPIO 24 2862306a36Sopenharmony_ci/* Maximum keys supported by Tegra KBC yet is 16 x 8*/ 2962306a36Sopenharmony_ci#define KBC_MAX_KEY (16 * 8) 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define KBC_MAX_DEBOUNCE_CNT 0x3ffu 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* KBC row scan time and delay for beginning the row scan. */ 3462306a36Sopenharmony_ci#define KBC_ROW_SCAN_TIME 16 3562306a36Sopenharmony_ci#define KBC_ROW_SCAN_DLY 5 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* KBC uses a 32KHz clock so a cycle = 1/32Khz */ 3862306a36Sopenharmony_ci#define KBC_CYCLE_MS 32 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* KBC Registers */ 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* KBC Control Register */ 4362306a36Sopenharmony_ci#define KBC_CONTROL_0 0x0 4462306a36Sopenharmony_ci#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) 4562306a36Sopenharmony_ci#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) 4662306a36Sopenharmony_ci#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) 4762306a36Sopenharmony_ci#define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) 4862306a36Sopenharmony_ci#define KBC_CONTROL_KBC_EN (1 << 0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* KBC Interrupt Register */ 5162306a36Sopenharmony_ci#define KBC_INT_0 0x4 5262306a36Sopenharmony_ci#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) 5362306a36Sopenharmony_ci#define KBC_INT_KEYPRESS_INT_STATUS (1 << 0) 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define KBC_ROW_CFG0_0 0x8 5662306a36Sopenharmony_ci#define KBC_COL_CFG0_0 0x18 5762306a36Sopenharmony_ci#define KBC_TO_CNT_0 0x24 5862306a36Sopenharmony_ci#define KBC_INIT_DLY_0 0x28 5962306a36Sopenharmony_ci#define KBC_RPT_DLY_0 0x2c 6062306a36Sopenharmony_ci#define KBC_KP_ENT0_0 0x30 6162306a36Sopenharmony_ci#define KBC_KP_ENT1_0 0x34 6262306a36Sopenharmony_ci#define KBC_ROW0_MASK_0 0x38 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define KBC_ROW_SHIFT 3 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cienum tegra_pin_type { 6762306a36Sopenharmony_ci PIN_CFG_IGNORE, 6862306a36Sopenharmony_ci PIN_CFG_COL, 6962306a36Sopenharmony_ci PIN_CFG_ROW, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci/* Tegra KBC hw support */ 7362306a36Sopenharmony_cistruct tegra_kbc_hw_support { 7462306a36Sopenharmony_ci int max_rows; 7562306a36Sopenharmony_ci int max_columns; 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistruct tegra_kbc_pin_cfg { 7962306a36Sopenharmony_ci enum tegra_pin_type type; 8062306a36Sopenharmony_ci unsigned char num; 8162306a36Sopenharmony_ci}; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistruct tegra_kbc { 8462306a36Sopenharmony_ci struct device *dev; 8562306a36Sopenharmony_ci unsigned int debounce_cnt; 8662306a36Sopenharmony_ci unsigned int repeat_cnt; 8762306a36Sopenharmony_ci struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; 8862306a36Sopenharmony_ci const struct matrix_keymap_data *keymap_data; 8962306a36Sopenharmony_ci bool wakeup; 9062306a36Sopenharmony_ci void __iomem *mmio; 9162306a36Sopenharmony_ci struct input_dev *idev; 9262306a36Sopenharmony_ci int irq; 9362306a36Sopenharmony_ci spinlock_t lock; 9462306a36Sopenharmony_ci unsigned int repoll_dly; 9562306a36Sopenharmony_ci unsigned long cp_dly_jiffies; 9662306a36Sopenharmony_ci unsigned int cp_to_wkup_dly; 9762306a36Sopenharmony_ci bool use_fn_map; 9862306a36Sopenharmony_ci bool use_ghost_filter; 9962306a36Sopenharmony_ci bool keypress_caused_wake; 10062306a36Sopenharmony_ci unsigned short keycode[KBC_MAX_KEY * 2]; 10162306a36Sopenharmony_ci unsigned short current_keys[KBC_MAX_KPENT]; 10262306a36Sopenharmony_ci unsigned int num_pressed_keys; 10362306a36Sopenharmony_ci u32 wakeup_key; 10462306a36Sopenharmony_ci struct timer_list timer; 10562306a36Sopenharmony_ci struct clk *clk; 10662306a36Sopenharmony_ci struct reset_control *rst; 10762306a36Sopenharmony_ci const struct tegra_kbc_hw_support *hw_support; 10862306a36Sopenharmony_ci int max_keys; 10962306a36Sopenharmony_ci int num_rows_and_columns; 11062306a36Sopenharmony_ci}; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void tegra_kbc_report_released_keys(struct input_dev *input, 11362306a36Sopenharmony_ci unsigned short old_keycodes[], 11462306a36Sopenharmony_ci unsigned int old_num_keys, 11562306a36Sopenharmony_ci unsigned short new_keycodes[], 11662306a36Sopenharmony_ci unsigned int new_num_keys) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci unsigned int i, j; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci for (i = 0; i < old_num_keys; i++) { 12162306a36Sopenharmony_ci for (j = 0; j < new_num_keys; j++) 12262306a36Sopenharmony_ci if (old_keycodes[i] == new_keycodes[j]) 12362306a36Sopenharmony_ci break; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (j == new_num_keys) 12662306a36Sopenharmony_ci input_report_key(input, old_keycodes[i], 0); 12762306a36Sopenharmony_ci } 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic void tegra_kbc_report_pressed_keys(struct input_dev *input, 13162306a36Sopenharmony_ci unsigned char scancodes[], 13262306a36Sopenharmony_ci unsigned short keycodes[], 13362306a36Sopenharmony_ci unsigned int num_pressed_keys) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci unsigned int i; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < num_pressed_keys; i++) { 13862306a36Sopenharmony_ci input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); 13962306a36Sopenharmony_ci input_report_key(input, keycodes[i], 1); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void tegra_kbc_report_keys(struct tegra_kbc *kbc) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci unsigned char scancodes[KBC_MAX_KPENT]; 14662306a36Sopenharmony_ci unsigned short keycodes[KBC_MAX_KPENT]; 14762306a36Sopenharmony_ci u32 val = 0; 14862306a36Sopenharmony_ci unsigned int i; 14962306a36Sopenharmony_ci unsigned int num_down = 0; 15062306a36Sopenharmony_ci bool fn_keypress = false; 15162306a36Sopenharmony_ci bool key_in_same_row = false; 15262306a36Sopenharmony_ci bool key_in_same_col = false; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci for (i = 0; i < KBC_MAX_KPENT; i++) { 15562306a36Sopenharmony_ci if ((i % 4) == 0) 15662306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT0_0 + i); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (val & 0x80) { 15962306a36Sopenharmony_ci unsigned int col = val & 0x07; 16062306a36Sopenharmony_ci unsigned int row = (val >> 3) & 0x0f; 16162306a36Sopenharmony_ci unsigned char scancode = 16262306a36Sopenharmony_ci MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci scancodes[num_down] = scancode; 16562306a36Sopenharmony_ci keycodes[num_down] = kbc->keycode[scancode]; 16662306a36Sopenharmony_ci /* If driver uses Fn map, do not report the Fn key. */ 16762306a36Sopenharmony_ci if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map) 16862306a36Sopenharmony_ci fn_keypress = true; 16962306a36Sopenharmony_ci else 17062306a36Sopenharmony_ci num_down++; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci val >>= 8; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * Matrix keyboard designs are prone to keyboard ghosting. 17862306a36Sopenharmony_ci * Ghosting occurs if there are 3 keys such that - 17962306a36Sopenharmony_ci * any 2 of the 3 keys share a row, and any 2 of them share a column. 18062306a36Sopenharmony_ci * If so ignore the key presses for this iteration. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci if (kbc->use_ghost_filter && num_down >= 3) { 18362306a36Sopenharmony_ci for (i = 0; i < num_down; i++) { 18462306a36Sopenharmony_ci unsigned int j; 18562306a36Sopenharmony_ci u8 curr_col = scancodes[i] & 0x07; 18662306a36Sopenharmony_ci u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci /* 18962306a36Sopenharmony_ci * Find 2 keys such that one key is in the same row 19062306a36Sopenharmony_ci * and the other is in the same column as the i-th key. 19162306a36Sopenharmony_ci */ 19262306a36Sopenharmony_ci for (j = i + 1; j < num_down; j++) { 19362306a36Sopenharmony_ci u8 col = scancodes[j] & 0x07; 19462306a36Sopenharmony_ci u8 row = scancodes[j] >> KBC_ROW_SHIFT; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (col == curr_col) 19762306a36Sopenharmony_ci key_in_same_col = true; 19862306a36Sopenharmony_ci if (row == curr_row) 19962306a36Sopenharmony_ci key_in_same_row = true; 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci } 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* 20562306a36Sopenharmony_ci * If the platform uses Fn keymaps, translate keys on a Fn keypress. 20662306a36Sopenharmony_ci * Function keycodes are max_keys apart from the plain keycodes. 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci if (fn_keypress) { 20962306a36Sopenharmony_ci for (i = 0; i < num_down; i++) { 21062306a36Sopenharmony_ci scancodes[i] += kbc->max_keys; 21162306a36Sopenharmony_ci keycodes[i] = kbc->keycode[scancodes[i]]; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci /* Ignore the key presses for this iteration? */ 21662306a36Sopenharmony_ci if (key_in_same_col && key_in_same_row) 21762306a36Sopenharmony_ci return; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci tegra_kbc_report_released_keys(kbc->idev, 22062306a36Sopenharmony_ci kbc->current_keys, kbc->num_pressed_keys, 22162306a36Sopenharmony_ci keycodes, num_down); 22262306a36Sopenharmony_ci tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); 22362306a36Sopenharmony_ci input_sync(kbc->idev); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); 22662306a36Sopenharmony_ci kbc->num_pressed_keys = num_down; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci u32 val; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 23462306a36Sopenharmony_ci if (enable) 23562306a36Sopenharmony_ci val |= KBC_CONTROL_FIFO_CNT_INT_EN; 23662306a36Sopenharmony_ci else 23762306a36Sopenharmony_ci val &= ~KBC_CONTROL_FIFO_CNT_INT_EN; 23862306a36Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic void tegra_kbc_keypress_timer(struct timer_list *t) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct tegra_kbc *kbc = from_timer(kbc, t, timer); 24462306a36Sopenharmony_ci unsigned long flags; 24562306a36Sopenharmony_ci u32 val; 24662306a36Sopenharmony_ci unsigned int i; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; 25162306a36Sopenharmony_ci if (val) { 25262306a36Sopenharmony_ci unsigned long dly; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci tegra_kbc_report_keys(kbc); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci /* 25762306a36Sopenharmony_ci * If more than one keys are pressed we need not wait 25862306a36Sopenharmony_ci * for the repoll delay. 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci dly = (val == 1) ? kbc->repoll_dly : 1; 26162306a36Sopenharmony_ci mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci /* Release any pressed keys and exit the polling loop */ 26462306a36Sopenharmony_ci for (i = 0; i < kbc->num_pressed_keys; i++) 26562306a36Sopenharmony_ci input_report_key(kbc->idev, kbc->current_keys[i], 0); 26662306a36Sopenharmony_ci input_sync(kbc->idev); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci kbc->num_pressed_keys = 0; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci /* All keys are released so enable the keypress interrupt */ 27162306a36Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, true); 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic irqreturn_t tegra_kbc_isr(int irq, void *args) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct tegra_kbc *kbc = args; 28062306a36Sopenharmony_ci unsigned long flags; 28162306a36Sopenharmony_ci u32 val; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci /* 28662306a36Sopenharmony_ci * Quickly bail out & reenable interrupts if the fifo threshold 28762306a36Sopenharmony_ci * count interrupt wasn't the interrupt source 28862306a36Sopenharmony_ci */ 28962306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_INT_0); 29062306a36Sopenharmony_ci writel(val, kbc->mmio + KBC_INT_0); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (val & KBC_INT_FIFO_CNT_INT_STATUS) { 29362306a36Sopenharmony_ci /* 29462306a36Sopenharmony_ci * Until all keys are released, defer further processing to 29562306a36Sopenharmony_ci * the polling loop in tegra_kbc_keypress_timer. 29662306a36Sopenharmony_ci */ 29762306a36Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, false); 29862306a36Sopenharmony_ci mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); 29962306a36Sopenharmony_ci } else if (val & KBC_INT_KEYPRESS_INT_STATUS) { 30062306a36Sopenharmony_ci /* We can be here only through system resume path */ 30162306a36Sopenharmony_ci kbc->keypress_caused_wake = true; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci return IRQ_HANDLED; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci int i; 31262306a36Sopenharmony_ci unsigned int rst_val; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* Either mask all keys or none. */ 31562306a36Sopenharmony_ci rst_val = (filter && !kbc->wakeup) ? ~0 : 0; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci for (i = 0; i < kbc->hw_support->max_rows; i++) 31862306a36Sopenharmony_ci writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void tegra_kbc_config_pins(struct tegra_kbc *kbc) 32262306a36Sopenharmony_ci{ 32362306a36Sopenharmony_ci int i; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci for (i = 0; i < KBC_MAX_GPIO; i++) { 32662306a36Sopenharmony_ci u32 r_shft = 5 * (i % 6); 32762306a36Sopenharmony_ci u32 c_shft = 4 * (i % 8); 32862306a36Sopenharmony_ci u32 r_mask = 0x1f << r_shft; 32962306a36Sopenharmony_ci u32 c_mask = 0x0f << c_shft; 33062306a36Sopenharmony_ci u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; 33162306a36Sopenharmony_ci u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; 33262306a36Sopenharmony_ci u32 row_cfg = readl(kbc->mmio + r_offs); 33362306a36Sopenharmony_ci u32 col_cfg = readl(kbc->mmio + c_offs); 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci row_cfg &= ~r_mask; 33662306a36Sopenharmony_ci col_cfg &= ~c_mask; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci switch (kbc->pin_cfg[i].type) { 33962306a36Sopenharmony_ci case PIN_CFG_ROW: 34062306a36Sopenharmony_ci row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft; 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci case PIN_CFG_COL: 34462306a36Sopenharmony_ci col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft; 34562306a36Sopenharmony_ci break; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci case PIN_CFG_IGNORE: 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci writel(row_cfg, kbc->mmio + r_offs); 35262306a36Sopenharmony_ci writel(col_cfg, kbc->mmio + c_offs); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cistatic int tegra_kbc_start(struct tegra_kbc *kbc) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci unsigned int debounce_cnt; 35962306a36Sopenharmony_ci u32 val = 0; 36062306a36Sopenharmony_ci int ret; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci ret = clk_prepare_enable(kbc->clk); 36362306a36Sopenharmony_ci if (ret) 36462306a36Sopenharmony_ci return ret; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* Reset the KBC controller to clear all previous status.*/ 36762306a36Sopenharmony_ci reset_control_assert(kbc->rst); 36862306a36Sopenharmony_ci udelay(100); 36962306a36Sopenharmony_ci reset_control_deassert(kbc->rst); 37062306a36Sopenharmony_ci udelay(100); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci tegra_kbc_config_pins(kbc); 37362306a36Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, false); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci /* Keyboard debounce count is maximum of 12 bits. */ 37862306a36Sopenharmony_ci debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); 37962306a36Sopenharmony_ci val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); 38062306a36Sopenharmony_ci val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ 38162306a36Sopenharmony_ci val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ 38262306a36Sopenharmony_ci val |= KBC_CONTROL_KBC_EN; /* enable */ 38362306a36Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Compute the delay(ns) from interrupt mode to continuous polling 38762306a36Sopenharmony_ci * mode so the timer routine is scheduled appropriately. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_INIT_DLY_0); 39062306a36Sopenharmony_ci kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci kbc->num_pressed_keys = 0; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* 39562306a36Sopenharmony_ci * Atomically clear out any remaining entries in the key FIFO 39662306a36Sopenharmony_ci * and enable keyboard interrupts. 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci while (1) { 39962306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_INT_0); 40062306a36Sopenharmony_ci val >>= 4; 40162306a36Sopenharmony_ci if (!val) 40262306a36Sopenharmony_ci break; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT0_0); 40562306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_KP_ENT1_0); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci writel(0x7, kbc->mmio + KBC_INT_0); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci enable_irq(kbc->irq); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return 0; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void tegra_kbc_stop(struct tegra_kbc *kbc) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci unsigned long flags; 41762306a36Sopenharmony_ci u32 val; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci spin_lock_irqsave(&kbc->lock, flags); 42062306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 42162306a36Sopenharmony_ci val &= ~1; 42262306a36Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 42362306a36Sopenharmony_ci spin_unlock_irqrestore(&kbc->lock, flags); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci disable_irq(kbc->irq); 42662306a36Sopenharmony_ci del_timer_sync(&kbc->timer); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci clk_disable_unprepare(kbc->clk); 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic int tegra_kbc_open(struct input_dev *dev) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct tegra_kbc *kbc = input_get_drvdata(dev); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return tegra_kbc_start(kbc); 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic void tegra_kbc_close(struct input_dev *dev) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct tegra_kbc *kbc = input_get_drvdata(dev); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci return tegra_kbc_stop(kbc); 44362306a36Sopenharmony_ci} 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_cistatic bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, 44662306a36Sopenharmony_ci unsigned int *num_rows) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci int i; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci *num_rows = 0; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci for (i = 0; i < KBC_MAX_GPIO; i++) { 45362306a36Sopenharmony_ci const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i]; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci switch (pin_cfg->type) { 45662306a36Sopenharmony_ci case PIN_CFG_ROW: 45762306a36Sopenharmony_ci if (pin_cfg->num >= kbc->hw_support->max_rows) { 45862306a36Sopenharmony_ci dev_err(kbc->dev, 45962306a36Sopenharmony_ci "pin_cfg[%d]: invalid row number %d\n", 46062306a36Sopenharmony_ci i, pin_cfg->num); 46162306a36Sopenharmony_ci return false; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci (*num_rows)++; 46462306a36Sopenharmony_ci break; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci case PIN_CFG_COL: 46762306a36Sopenharmony_ci if (pin_cfg->num >= kbc->hw_support->max_columns) { 46862306a36Sopenharmony_ci dev_err(kbc->dev, 46962306a36Sopenharmony_ci "pin_cfg[%d]: invalid column number %d\n", 47062306a36Sopenharmony_ci i, pin_cfg->num); 47162306a36Sopenharmony_ci return false; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci break; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci case PIN_CFG_IGNORE: 47662306a36Sopenharmony_ci break; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci default: 47962306a36Sopenharmony_ci dev_err(kbc->dev, 48062306a36Sopenharmony_ci "pin_cfg[%d]: invalid entry type %d\n", 48162306a36Sopenharmony_ci pin_cfg->type, pin_cfg->num); 48262306a36Sopenharmony_ci return false; 48362306a36Sopenharmony_ci } 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci return true; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int tegra_kbc_parse_dt(struct tegra_kbc *kbc) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci struct device_node *np = kbc->dev->of_node; 49262306a36Sopenharmony_ci u32 prop; 49362306a36Sopenharmony_ci int i; 49462306a36Sopenharmony_ci u32 num_rows = 0; 49562306a36Sopenharmony_ci u32 num_cols = 0; 49662306a36Sopenharmony_ci u32 cols_cfg[KBC_MAX_GPIO]; 49762306a36Sopenharmony_ci u32 rows_cfg[KBC_MAX_GPIO]; 49862306a36Sopenharmony_ci int proplen; 49962306a36Sopenharmony_ci int ret; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) 50262306a36Sopenharmony_ci kbc->debounce_cnt = prop; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) 50562306a36Sopenharmony_ci kbc->repeat_cnt = prop; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci kbc->use_ghost_filter = of_property_present(np, "nvidia,needs-ghost-filter"); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (of_property_read_bool(np, "wakeup-source") || 51062306a36Sopenharmony_ci of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */ 51162306a36Sopenharmony_ci kbc->wakeup = true; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { 51462306a36Sopenharmony_ci dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n"); 51562306a36Sopenharmony_ci return -ENOENT; 51662306a36Sopenharmony_ci } 51762306a36Sopenharmony_ci num_rows = proplen / sizeof(u32); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { 52062306a36Sopenharmony_ci dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n"); 52162306a36Sopenharmony_ci return -ENOENT; 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci num_cols = proplen / sizeof(u32); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci if (num_rows > kbc->hw_support->max_rows) { 52662306a36Sopenharmony_ci dev_err(kbc->dev, 52762306a36Sopenharmony_ci "Number of rows is more than supported by hardware\n"); 52862306a36Sopenharmony_ci return -EINVAL; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (num_cols > kbc->hw_support->max_columns) { 53262306a36Sopenharmony_ci dev_err(kbc->dev, 53362306a36Sopenharmony_ci "Number of cols is more than supported by hardware\n"); 53462306a36Sopenharmony_ci return -EINVAL; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (!of_get_property(np, "linux,keymap", &proplen)) { 53862306a36Sopenharmony_ci dev_err(kbc->dev, "property linux,keymap not found\n"); 53962306a36Sopenharmony_ci return -ENOENT; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { 54362306a36Sopenharmony_ci dev_err(kbc->dev, 54462306a36Sopenharmony_ci "keypad rows/columns not properly specified\n"); 54562306a36Sopenharmony_ci return -EINVAL; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Set all pins as non-configured */ 54962306a36Sopenharmony_ci for (i = 0; i < kbc->num_rows_and_columns; i++) 55062306a36Sopenharmony_ci kbc->pin_cfg[i].type = PIN_CFG_IGNORE; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", 55362306a36Sopenharmony_ci rows_cfg, num_rows); 55462306a36Sopenharmony_ci if (ret < 0) { 55562306a36Sopenharmony_ci dev_err(kbc->dev, "Rows configurations are not proper\n"); 55662306a36Sopenharmony_ci return -EINVAL; 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", 56062306a36Sopenharmony_ci cols_cfg, num_cols); 56162306a36Sopenharmony_ci if (ret < 0) { 56262306a36Sopenharmony_ci dev_err(kbc->dev, "Cols configurations are not proper\n"); 56362306a36Sopenharmony_ci return -EINVAL; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci for (i = 0; i < num_rows; i++) { 56762306a36Sopenharmony_ci kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; 56862306a36Sopenharmony_ci kbc->pin_cfg[rows_cfg[i]].num = i; 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci for (i = 0; i < num_cols; i++) { 57262306a36Sopenharmony_ci kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; 57362306a36Sopenharmony_ci kbc->pin_cfg[cols_cfg[i]].num = i; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic const struct tegra_kbc_hw_support tegra20_kbc_hw_support = { 58062306a36Sopenharmony_ci .max_rows = 16, 58162306a36Sopenharmony_ci .max_columns = 8, 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic const struct tegra_kbc_hw_support tegra11_kbc_hw_support = { 58562306a36Sopenharmony_ci .max_rows = 11, 58662306a36Sopenharmony_ci .max_columns = 8, 58762306a36Sopenharmony_ci}; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_cistatic const struct of_device_id tegra_kbc_of_match[] = { 59062306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support}, 59162306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support}, 59262306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support}, 59362306a36Sopenharmony_ci { }, 59462306a36Sopenharmony_ci}; 59562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_kbc_of_match); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int tegra_kbc_probe(struct platform_device *pdev) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct tegra_kbc *kbc; 60062306a36Sopenharmony_ci int err; 60162306a36Sopenharmony_ci int num_rows = 0; 60262306a36Sopenharmony_ci unsigned int debounce_cnt; 60362306a36Sopenharmony_ci unsigned int scan_time_rows; 60462306a36Sopenharmony_ci unsigned int keymap_rows; 60562306a36Sopenharmony_ci const struct of_device_id *match; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci match = of_match_device(tegra_kbc_of_match, &pdev->dev); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); 61062306a36Sopenharmony_ci if (!kbc) { 61162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); 61262306a36Sopenharmony_ci return -ENOMEM; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci kbc->dev = &pdev->dev; 61662306a36Sopenharmony_ci kbc->hw_support = match->data; 61762306a36Sopenharmony_ci kbc->max_keys = kbc->hw_support->max_rows * 61862306a36Sopenharmony_ci kbc->hw_support->max_columns; 61962306a36Sopenharmony_ci kbc->num_rows_and_columns = kbc->hw_support->max_rows + 62062306a36Sopenharmony_ci kbc->hw_support->max_columns; 62162306a36Sopenharmony_ci keymap_rows = kbc->max_keys; 62262306a36Sopenharmony_ci spin_lock_init(&kbc->lock); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci err = tegra_kbc_parse_dt(kbc); 62562306a36Sopenharmony_ci if (err) 62662306a36Sopenharmony_ci return err; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (!tegra_kbc_check_pin_cfg(kbc, &num_rows)) 62962306a36Sopenharmony_ci return -EINVAL; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci kbc->irq = platform_get_irq(pdev, 0); 63262306a36Sopenharmony_ci if (kbc->irq < 0) 63362306a36Sopenharmony_ci return -ENXIO; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci kbc->idev = devm_input_allocate_device(&pdev->dev); 63662306a36Sopenharmony_ci if (!kbc->idev) { 63762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate input device\n"); 63862306a36Sopenharmony_ci return -ENOMEM; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci timer_setup(&kbc->timer, tegra_kbc_keypress_timer, 0); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci kbc->mmio = devm_platform_ioremap_resource(pdev, 0); 64462306a36Sopenharmony_ci if (IS_ERR(kbc->mmio)) 64562306a36Sopenharmony_ci return PTR_ERR(kbc->mmio); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci kbc->clk = devm_clk_get(&pdev->dev, NULL); 64862306a36Sopenharmony_ci if (IS_ERR(kbc->clk)) { 64962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get keyboard clock\n"); 65062306a36Sopenharmony_ci return PTR_ERR(kbc->clk); 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci kbc->rst = devm_reset_control_get(&pdev->dev, "kbc"); 65462306a36Sopenharmony_ci if (IS_ERR(kbc->rst)) { 65562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get keyboard reset\n"); 65662306a36Sopenharmony_ci return PTR_ERR(kbc->rst); 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * The time delay between two consecutive reads of the FIFO is 66162306a36Sopenharmony_ci * the sum of the repeat time and the time taken for scanning 66262306a36Sopenharmony_ci * the rows. There is an additional delay before the row scanning 66362306a36Sopenharmony_ci * starts. The repoll delay is computed in milliseconds. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); 66662306a36Sopenharmony_ci scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; 66762306a36Sopenharmony_ci kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt; 66862306a36Sopenharmony_ci kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci kbc->idev->name = pdev->name; 67162306a36Sopenharmony_ci kbc->idev->id.bustype = BUS_HOST; 67262306a36Sopenharmony_ci kbc->idev->dev.parent = &pdev->dev; 67362306a36Sopenharmony_ci kbc->idev->open = tegra_kbc_open; 67462306a36Sopenharmony_ci kbc->idev->close = tegra_kbc_close; 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci if (kbc->keymap_data && kbc->use_fn_map) 67762306a36Sopenharmony_ci keymap_rows *= 2; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, 68062306a36Sopenharmony_ci keymap_rows, 68162306a36Sopenharmony_ci kbc->hw_support->max_columns, 68262306a36Sopenharmony_ci kbc->keycode, kbc->idev); 68362306a36Sopenharmony_ci if (err) { 68462306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to setup keymap\n"); 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci } 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci __set_bit(EV_REP, kbc->idev->evbit); 68962306a36Sopenharmony_ci input_set_capability(kbc->idev, EV_MSC, MSC_SCAN); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci input_set_drvdata(kbc->idev, kbc); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, 69462306a36Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, 69562306a36Sopenharmony_ci pdev->name, kbc); 69662306a36Sopenharmony_ci if (err) { 69762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); 69862306a36Sopenharmony_ci return err; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci err = input_register_device(kbc->idev); 70262306a36Sopenharmony_ci if (err) { 70362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register input device\n"); 70462306a36Sopenharmony_ci return err; 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci platform_set_drvdata(pdev, kbc); 70862306a36Sopenharmony_ci device_init_wakeup(&pdev->dev, kbc->wakeup); 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) 71462306a36Sopenharmony_ci{ 71562306a36Sopenharmony_ci u32 val; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci val = readl(kbc->mmio + KBC_CONTROL_0); 71862306a36Sopenharmony_ci if (enable) 71962306a36Sopenharmony_ci val |= KBC_CONTROL_KEYPRESS_INT_EN; 72062306a36Sopenharmony_ci else 72162306a36Sopenharmony_ci val &= ~KBC_CONTROL_KEYPRESS_INT_EN; 72262306a36Sopenharmony_ci writel(val, kbc->mmio + KBC_CONTROL_0); 72362306a36Sopenharmony_ci} 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_cistatic int tegra_kbc_suspend(struct device *dev) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 72862306a36Sopenharmony_ci struct tegra_kbc *kbc = platform_get_drvdata(pdev); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci mutex_lock(&kbc->idev->mutex); 73162306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 73262306a36Sopenharmony_ci disable_irq(kbc->irq); 73362306a36Sopenharmony_ci del_timer_sync(&kbc->timer); 73462306a36Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, false); 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* Forcefully clear the interrupt status */ 73762306a36Sopenharmony_ci writel(0x7, kbc->mmio + KBC_INT_0); 73862306a36Sopenharmony_ci /* 73962306a36Sopenharmony_ci * Store the previous resident time of continuous polling mode. 74062306a36Sopenharmony_ci * Force the keyboard into interrupt mode. 74162306a36Sopenharmony_ci */ 74262306a36Sopenharmony_ci kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0); 74362306a36Sopenharmony_ci writel(0, kbc->mmio + KBC_TO_CNT_0); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, true); 74662306a36Sopenharmony_ci msleep(30); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci kbc->keypress_caused_wake = false; 74962306a36Sopenharmony_ci /* Enable keypress interrupt before going into suspend. */ 75062306a36Sopenharmony_ci tegra_kbc_set_keypress_interrupt(kbc, true); 75162306a36Sopenharmony_ci enable_irq(kbc->irq); 75262306a36Sopenharmony_ci enable_irq_wake(kbc->irq); 75362306a36Sopenharmony_ci } else { 75462306a36Sopenharmony_ci if (input_device_enabled(kbc->idev)) 75562306a36Sopenharmony_ci tegra_kbc_stop(kbc); 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci mutex_unlock(&kbc->idev->mutex); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return 0; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cistatic int tegra_kbc_resume(struct device *dev) 76362306a36Sopenharmony_ci{ 76462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 76562306a36Sopenharmony_ci struct tegra_kbc *kbc = platform_get_drvdata(pdev); 76662306a36Sopenharmony_ci int err = 0; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci mutex_lock(&kbc->idev->mutex); 76962306a36Sopenharmony_ci if (device_may_wakeup(&pdev->dev)) { 77062306a36Sopenharmony_ci disable_irq_wake(kbc->irq); 77162306a36Sopenharmony_ci tegra_kbc_setup_wakekeys(kbc, false); 77262306a36Sopenharmony_ci /* We will use fifo interrupts for key detection. */ 77362306a36Sopenharmony_ci tegra_kbc_set_keypress_interrupt(kbc, false); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci /* Restore the resident time of continuous polling mode. */ 77662306a36Sopenharmony_ci writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci tegra_kbc_set_fifo_interrupt(kbc, true); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci if (kbc->keypress_caused_wake && kbc->wakeup_key) { 78162306a36Sopenharmony_ci /* 78262306a36Sopenharmony_ci * We can't report events directly from the ISR 78362306a36Sopenharmony_ci * because timekeeping is stopped when processing 78462306a36Sopenharmony_ci * wakeup request and we get a nasty warning when 78562306a36Sopenharmony_ci * we try to call do_gettimeofday() in evdev 78662306a36Sopenharmony_ci * handler. 78762306a36Sopenharmony_ci */ 78862306a36Sopenharmony_ci input_report_key(kbc->idev, kbc->wakeup_key, 1); 78962306a36Sopenharmony_ci input_sync(kbc->idev); 79062306a36Sopenharmony_ci input_report_key(kbc->idev, kbc->wakeup_key, 0); 79162306a36Sopenharmony_ci input_sync(kbc->idev); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci } else { 79462306a36Sopenharmony_ci if (input_device_enabled(kbc->idev)) 79562306a36Sopenharmony_ci err = tegra_kbc_start(kbc); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci mutex_unlock(&kbc->idev->mutex); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return err; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, 80362306a36Sopenharmony_ci tegra_kbc_suspend, tegra_kbc_resume); 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_cistatic struct platform_driver tegra_kbc_driver = { 80662306a36Sopenharmony_ci .probe = tegra_kbc_probe, 80762306a36Sopenharmony_ci .driver = { 80862306a36Sopenharmony_ci .name = "tegra-kbc", 80962306a36Sopenharmony_ci .pm = pm_sleep_ptr(&tegra_kbc_pm_ops), 81062306a36Sopenharmony_ci .of_match_table = tegra_kbc_of_match, 81162306a36Sopenharmony_ci }, 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_cimodule_platform_driver(tegra_kbc_driver); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 81662306a36Sopenharmony_ciMODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>"); 81762306a36Sopenharmony_ciMODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); 81862306a36Sopenharmony_ciMODULE_ALIAS("platform:tegra-kbc"); 819