Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Rakesh Iyer | 2572 | 67.03% | 9 | 28.12% |
Laxman Dewangan | 666 | 17.36% | 5 | 15.62% |
Stephen Warren | 248 | 6.46% | 2 | 6.25% |
Olof Johansson | 163 | 4.25% | 2 | 6.25% |
Dmitry Torokhov | 73 | 1.90% | 3 | 9.38% |
Shridhar Rasal | 51 | 1.33% | 1 | 3.12% |
Kees Cook | 14 | 0.36% | 1 | 3.12% |
Arvind Yadav | 12 | 0.31% | 1 | 3.12% |
Julia Lawall | 11 | 0.29% | 1 | 3.12% |
Sachin Kamat | 11 | 0.29% | 1 | 3.12% |
Sudeep Holla | 9 | 0.23% | 1 | 3.12% |
Prashant Gaikwad | 2 | 0.05% | 1 | 3.12% |
Thomas Gleixner | 2 | 0.05% | 1 | 3.12% |
Masahiro Yamada | 1 | 0.03% | 1 | 3.12% |
JJ Ding | 1 | 0.03% | 1 | 3.12% |
Colin Ian King | 1 | 0.03% | 1 | 3.12% |
Total | 3837 | 32 |
// SPDX-License-Identifier: GPL-2.0-or-later /* * Keyboard class input driver for the NVIDIA Tegra SoC internal matrix * keyboard controller * * Copyright (c) 2009-2011, NVIDIA Corporation. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/clk.h> #include <linux/slab.h> #include <linux/input/matrix_keypad.h> #include <linux/reset.h> #include <linux/err.h> #define KBC_MAX_KPENT 8 /* Maximum row/column supported by Tegra KBC yet is 16x8 */ #define KBC_MAX_GPIO 24 /* Maximum keys supported by Tegra KBC yet is 16 x 8*/ #define KBC_MAX_KEY (16 * 8) #define KBC_MAX_DEBOUNCE_CNT 0x3ffu /* KBC row scan time and delay for beginning the row scan. */ #define KBC_ROW_SCAN_TIME 16 #define KBC_ROW_SCAN_DLY 5 /* KBC uses a 32KHz clock so a cycle = 1/32Khz */ #define KBC_CYCLE_MS 32 /* KBC Registers */ /* KBC Control Register */ #define KBC_CONTROL_0 0x0 #define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14) #define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 4) #define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) #define KBC_CONTROL_KEYPRESS_INT_EN (1 << 1) #define KBC_CONTROL_KBC_EN (1 << 0) /* KBC Interrupt Register */ #define KBC_INT_0 0x4 #define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) #define KBC_INT_KEYPRESS_INT_STATUS (1 << 0) #define KBC_ROW_CFG0_0 0x8 #define KBC_COL_CFG0_0 0x18 #define KBC_TO_CNT_0 0x24 #define KBC_INIT_DLY_0 0x28 #define KBC_RPT_DLY_0 0x2c #define KBC_KP_ENT0_0 0x30 #define KBC_KP_ENT1_0 0x34 #define KBC_ROW0_MASK_0 0x38 #define KBC_ROW_SHIFT 3 enum tegra_pin_type { PIN_CFG_IGNORE, PIN_CFG_COL, PIN_CFG_ROW, }; /* Tegra KBC hw support */ struct tegra_kbc_hw_support { int max_rows; int max_columns; }; struct tegra_kbc_pin_cfg { enum tegra_pin_type type; unsigned char num; }; struct tegra_kbc { struct device *dev; unsigned int debounce_cnt; unsigned int repeat_cnt; struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; const struct matrix_keymap_data *keymap_data; bool wakeup; void __iomem *mmio; struct input_dev *idev; int irq; spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; unsigned int cp_to_wkup_dly; bool use_fn_map; bool use_ghost_filter; bool keypress_caused_wake; unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; unsigned int num_pressed_keys; u32 wakeup_key; struct timer_list timer; struct clk *clk; struct reset_control *rst; const struct tegra_kbc_hw_support *hw_support; int max_keys; int num_rows_and_columns; }; static void tegra_kbc_report_released_keys(struct input_dev *input, unsigned short old_keycodes[], unsigned int old_num_keys, unsigned short new_keycodes[], unsigned int new_num_keys) { unsigned int i, j; for (i = 0; i < old_num_keys; i++) { for (j = 0; j < new_num_keys; j++) if (old_keycodes[i] == new_keycodes[j]) break; if (j == new_num_keys) input_report_key(input, old_keycodes[i], 0); } } static void tegra_kbc_report_pressed_keys(struct input_dev *input, unsigned char scancodes[], unsigned short keycodes[], unsigned int num_pressed_keys) { unsigned int i; for (i = 0; i < num_pressed_keys; i++) { input_event(input, EV_MSC, MSC_SCAN, scancodes[i]); input_report_key(input, keycodes[i], 1); } } static void tegra_kbc_report_keys(struct tegra_kbc *kbc) { unsigned char scancodes[KBC_MAX_KPENT]; unsigned short keycodes[KBC_MAX_KPENT]; u32 val = 0; unsigned int i; unsigned int num_down = 0; bool fn_keypress = false; bool key_in_same_row = false; bool key_in_same_col = false; for (i = 0; i < KBC_MAX_KPENT; i++) { if ((i % 4) == 0) val = readl(kbc->mmio + KBC_KP_ENT0_0 + i); if (val & 0x80) { unsigned int col = val & 0x07; unsigned int row = (val >> 3) & 0x0f; unsigned char scancode = MATRIX_SCAN_CODE(row, col, KBC_ROW_SHIFT); scancodes[num_down] = scancode; keycodes[num_down] = kbc->keycode[scancode]; /* If driver uses Fn map, do not report the Fn key. */ if ((keycodes[num_down] == KEY_FN) && kbc->use_fn_map) fn_keypress = true; else num_down++; } val >>= 8; } /* * Matrix keyboard designs are prone to keyboard ghosting. * Ghosting occurs if there are 3 keys such that - * any 2 of the 3 keys share a row, and any 2 of them share a column. * If so ignore the key presses for this iteration. */ if (kbc->use_ghost_filter && num_down >= 3) { for (i = 0; i < num_down; i++) { unsigned int j; u8 curr_col = scancodes[i] & 0x07; u8 curr_row = scancodes[i] >> KBC_ROW_SHIFT; /* * Find 2 keys such that one key is in the same row * and the other is in the same column as the i-th key. */ for (j = i + 1; j < num_down; j++) { u8 col = scancodes[j] & 0x07; u8 row = scancodes[j] >> KBC_ROW_SHIFT; if (col == curr_col) key_in_same_col = true; if (row == curr_row) key_in_same_row = true; } } } /* * If the platform uses Fn keymaps, translate keys on a Fn keypress. * Function keycodes are max_keys apart from the plain keycodes. */ if (fn_keypress) { for (i = 0; i < num_down; i++) { scancodes[i] += kbc->max_keys; keycodes[i] = kbc->keycode[scancodes[i]]; } } /* Ignore the key presses for this iteration? */ if (key_in_same_col && key_in_same_row) return; tegra_kbc_report_released_keys(kbc->idev, kbc->current_keys, kbc->num_pressed_keys, keycodes, num_down); tegra_kbc_report_pressed_keys(kbc->idev, scancodes, keycodes, num_down); input_sync(kbc->idev); memcpy(kbc->current_keys, keycodes, sizeof(kbc->current_keys)); kbc->num_pressed_keys = num_down; } static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) { u32 val; val = readl(kbc->mmio + KBC_CONTROL_0); if (enable) val |= KBC_CONTROL_FIFO_CNT_INT_EN; else val &= ~KBC_CONTROL_FIFO_CNT_INT_EN; writel(val, kbc->mmio + KBC_CONTROL_0); } static void tegra_kbc_keypress_timer(struct timer_list *t) { struct tegra_kbc *kbc = from_timer(kbc, t, timer); unsigned long flags; u32 val; unsigned int i; spin_lock_irqsave(&kbc->lock, flags); val = (readl(kbc->mmio + KBC_INT_0) >> 4) & 0xf; if (val) { unsigned long dly; tegra_kbc_report_keys(kbc); /* * If more than one keys are pressed we need not wait * for the repoll delay. */ dly = (val == 1) ? kbc->repoll_dly : 1; mod_timer(&kbc->timer, jiffies + msecs_to_jiffies(dly)); } else { /* Release any pressed keys and exit the polling loop */ for (i = 0; i < kbc->num_pressed_keys; i++) input_report_key(kbc->idev, kbc->current_keys[i], 0); input_sync(kbc->idev); kbc->num_pressed_keys = 0; /* All keys are released so enable the keypress interrupt */ tegra_kbc_set_fifo_interrupt(kbc, true); } spin_unlock_irqrestore(&kbc->lock, flags); } static irqreturn_t tegra_kbc_isr(int irq, void *args) { struct tegra_kbc *kbc = args; unsigned long flags; u32 val; spin_lock_irqsave(&kbc->lock, flags); /* * Quickly bail out & reenable interrupts if the fifo threshold * count interrupt wasn't the interrupt source */ val = readl(kbc->mmio + KBC_INT_0); writel(val, kbc->mmio + KBC_INT_0); if (val & KBC_INT_FIFO_CNT_INT_STATUS) { /* * Until all keys are released, defer further processing to * the polling loop in tegra_kbc_keypress_timer. */ tegra_kbc_set_fifo_interrupt(kbc, false); mod_timer(&kbc->timer, jiffies + kbc->cp_dly_jiffies); } else if (val & KBC_INT_KEYPRESS_INT_STATUS) { /* We can be here only through system resume path */ kbc->keypress_caused_wake = true; } spin_unlock_irqrestore(&kbc->lock, flags); return IRQ_HANDLED; } static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) { int i; unsigned int rst_val; /* Either mask all keys or none. */ rst_val = (filter && !kbc->wakeup) ? ~0 : 0; for (i = 0; i < kbc->hw_support->max_rows; i++) writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); } static void tegra_kbc_config_pins(struct tegra_kbc *kbc) { int i; for (i = 0; i < KBC_MAX_GPIO; i++) { u32 r_shft = 5 * (i % 6); u32 c_shft = 4 * (i % 8); u32 r_mask = 0x1f << r_shft; u32 c_mask = 0x0f << c_shft; u32 r_offs = (i / 6) * 4 + KBC_ROW_CFG0_0; u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0; u32 row_cfg = readl(kbc->mmio + r_offs); u32 col_cfg = readl(kbc->mmio + c_offs); row_cfg &= ~r_mask; col_cfg &= ~c_mask; switch (kbc->pin_cfg[i].type) { case PIN_CFG_ROW: row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft; break; case PIN_CFG_COL: col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft; break; case PIN_CFG_IGNORE: break; } writel(row_cfg, kbc->mmio + r_offs); writel(col_cfg, kbc->mmio + c_offs); } } static int tegra_kbc_start(struct tegra_kbc *kbc) { unsigned int debounce_cnt; u32 val = 0; int ret; ret = clk_prepare_enable(kbc->clk); if (ret) return ret; /* Reset the KBC controller to clear all previous status.*/ reset_control_assert(kbc->rst); udelay(100); reset_control_deassert(kbc->rst); udelay(100); tegra_kbc_config_pins(kbc); tegra_kbc_setup_wakekeys(kbc, false); writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); /* Keyboard debounce count is maximum of 12 bits. */ debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ val |= KBC_CONTROL_KBC_EN; /* enable */ writel(val, kbc->mmio + KBC_CONTROL_0); /* * Compute the delay(ns) from interrupt mode to continuous polling * mode so the timer routine is scheduled appropriately. */ val = readl(kbc->mmio + KBC_INIT_DLY_0); kbc->cp_dly_jiffies = usecs_to_jiffies((val & 0xfffff) * 32); kbc->num_pressed_keys = 0; /* * Atomically clear out any remaining entries in the key FIFO * and enable keyboard interrupts. */ while (1) { val = readl(kbc->mmio + KBC_INT_0); val >>= 4; if (!val) break; val = readl(kbc->mmio + KBC_KP_ENT0_0); val = readl(kbc->mmio + KBC_KP_ENT1_0); } writel(0x7, kbc->mmio + KBC_INT_0); enable_irq(kbc->irq); return 0; } static void tegra_kbc_stop(struct tegra_kbc *kbc) { unsigned long flags; u32 val; spin_lock_irqsave(&kbc->lock, flags); val = readl(kbc->mmio + KBC_CONTROL_0); val &= ~1; writel(val, kbc->mmio + KBC_CONTROL_0); spin_unlock_irqrestore(&kbc->lock, flags); disable_irq(kbc->irq); del_timer_sync(&kbc->timer); clk_disable_unprepare(kbc->clk); } static int tegra_kbc_open(struct input_dev *dev) { struct tegra_kbc *kbc = input_get_drvdata(dev); return tegra_kbc_start(kbc); } static void tegra_kbc_close(struct input_dev *dev) { struct tegra_kbc *kbc = input_get_drvdata(dev); return tegra_kbc_stop(kbc); } static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, unsigned int *num_rows) { int i; *num_rows = 0; for (i = 0; i < KBC_MAX_GPIO; i++) { const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i]; switch (pin_cfg->type) { case PIN_CFG_ROW: if (pin_cfg->num >= kbc->hw_support->max_rows) { dev_err(kbc->dev, "pin_cfg[%d]: invalid row number %d\n", i, pin_cfg->num); return false; } (*num_rows)++; break; case PIN_CFG_COL: if (pin_cfg->num >= kbc->hw_support->max_columns) { dev_err(kbc->dev, "pin_cfg[%d]: invalid column number %d\n", i, pin_cfg->num); return false; } break; case PIN_CFG_IGNORE: break; default: dev_err(kbc->dev, "pin_cfg[%d]: invalid entry type %d\n", pin_cfg->type, pin_cfg->num); return false; } } return true; } static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) { struct device_node *np = kbc->dev->of_node; u32 prop; int i; u32 num_rows = 0; u32 num_cols = 0; u32 cols_cfg[KBC_MAX_GPIO]; u32 rows_cfg[KBC_MAX_GPIO]; int proplen; int ret; if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) kbc->debounce_cnt = prop; if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) kbc->repeat_cnt = prop; if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) kbc->use_ghost_filter = true; if (of_property_read_bool(np, "wakeup-source") || of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */ kbc->wakeup = true; if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n"); return -ENOENT; } num_rows = proplen / sizeof(u32); if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n"); return -ENOENT; } num_cols = proplen / sizeof(u32); if (num_rows > kbc->hw_support->max_rows) { dev_err(kbc->dev, "Number of rows is more than supported by hardware\n"); return -EINVAL; } if (num_cols > kbc->hw_support->max_columns) { dev_err(kbc->dev, "Number of cols is more than supported by hardware\n"); return -EINVAL; } if (!of_get_property(np, "linux,keymap", &proplen)) { dev_err(kbc->dev, "property linux,keymap not found\n"); return -ENOENT; } if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { dev_err(kbc->dev, "keypad rows/columns not properly specified\n"); return -EINVAL; } /* Set all pins as non-configured */ for (i = 0; i < kbc->num_rows_and_columns; i++) kbc->pin_cfg[i].type = PIN_CFG_IGNORE; ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", rows_cfg, num_rows); if (ret < 0) { dev_err(kbc->dev, "Rows configurations are not proper\n"); return -EINVAL; } ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", cols_cfg, num_cols); if (ret < 0) { dev_err(kbc->dev, "Cols configurations are not proper\n"); return -EINVAL; } for (i = 0; i < num_rows; i++) { kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; kbc->pin_cfg[rows_cfg[i]].num = i; } for (i = 0; i < num_cols; i++) { kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; kbc->pin_cfg[cols_cfg[i]].num = i; } return 0; } static const struct tegra_kbc_hw_support tegra20_kbc_hw_support = { .max_rows = 16, .max_columns = 8, }; static const struct tegra_kbc_hw_support tegra11_kbc_hw_support = { .max_rows = 11, .max_columns = 8, }; static const struct of_device_id tegra_kbc_of_match[] = { { .compatible = "nvidia,tegra114-kbc", .data = &tegra11_kbc_hw_support}, { .compatible = "nvidia,tegra30-kbc", .data = &tegra20_kbc_hw_support}, { .compatible = "nvidia,tegra20-kbc", .data = &tegra20_kbc_hw_support}, { }, }; MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); static int tegra_kbc_probe(struct platform_device *pdev) { struct tegra_kbc *kbc; struct resource *res; int err; int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; unsigned int keymap_rows; const struct of_device_id *match; match = of_match_device(tegra_kbc_of_match, &pdev->dev); kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); if (!kbc) { dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); return -ENOMEM; } kbc->dev = &pdev->dev; kbc->hw_support = match->data; kbc->max_keys = kbc->hw_support->max_rows * kbc->hw_support->max_columns; kbc->num_rows_and_columns = kbc->hw_support->max_rows + kbc->hw_support->max_columns; keymap_rows = kbc->max_keys; spin_lock_init(&kbc->lock); err = tegra_kbc_parse_dt(kbc); if (err) return err; if (!tegra_kbc_check_pin_cfg(kbc, &num_rows)) return -EINVAL; kbc->irq = platform_get_irq(pdev, 0); if (kbc->irq < 0) { dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); return -ENXIO; } kbc->idev = devm_input_allocate_device(&pdev->dev); if (!kbc->idev) { dev_err(&pdev->dev, "failed to allocate input device\n"); return -ENOMEM; } timer_setup(&kbc->timer, tegra_kbc_keypress_timer, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); kbc->mmio = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(kbc->mmio)) return PTR_ERR(kbc->mmio); kbc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(kbc->clk)) { dev_err(&pdev->dev, "failed to get keyboard clock\n"); return PTR_ERR(kbc->clk); } kbc->rst = devm_reset_control_get(&pdev->dev, "kbc"); if (IS_ERR(kbc->rst)) { dev_err(&pdev->dev, "failed to get keyboard reset\n"); return PTR_ERR(kbc->rst); } /* * The time delay between two consecutive reads of the FIFO is * the sum of the repeat time and the time taken for scanning * the rows. There is an additional delay before the row scanning * starts. The repoll delay is computed in milliseconds. */ debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt; kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); kbc->idev->name = pdev->name; kbc->idev->id.bustype = BUS_HOST; kbc->idev->dev.parent = &pdev->dev; kbc->idev->open = tegra_kbc_open; kbc->idev->close = tegra_kbc_close; if (kbc->keymap_data && kbc->use_fn_map) keymap_rows *= 2; err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, keymap_rows, kbc->hw_support->max_columns, kbc->keycode, kbc->idev); if (err) { dev_err(&pdev->dev, "failed to setup keymap\n"); return err; } __set_bit(EV_REP, kbc->idev->evbit); input_set_capability(kbc->idev, EV_MSC, MSC_SCAN); input_set_drvdata(kbc->idev, kbc); err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, IRQF_TRIGGER_HIGH, pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); return err; } disable_irq(kbc->irq); err = input_register_device(kbc->idev); if (err) { dev_err(&pdev->dev, "failed to register input device\n"); return err; } platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, kbc->wakeup); return 0; } #ifdef CONFIG_PM_SLEEP static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) { u32 val; val = readl(kbc->mmio + KBC_CONTROL_0); if (enable) val |= KBC_CONTROL_KEYPRESS_INT_EN; else val &= ~KBC_CONTROL_KEYPRESS_INT_EN; writel(val, kbc->mmio + KBC_CONTROL_0); } static int tegra_kbc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_kbc *kbc = platform_get_drvdata(pdev); mutex_lock(&kbc->idev->mutex); if (device_may_wakeup(&pdev->dev)) { disable_irq(kbc->irq); del_timer_sync(&kbc->timer); tegra_kbc_set_fifo_interrupt(kbc, false); /* Forcefully clear the interrupt status */ writel(0x7, kbc->mmio + KBC_INT_0); /* * Store the previous resident time of continuous polling mode. * Force the keyboard into interrupt mode. */ kbc->cp_to_wkup_dly = readl(kbc->mmio + KBC_TO_CNT_0); writel(0, kbc->mmio + KBC_TO_CNT_0); tegra_kbc_setup_wakekeys(kbc, true); msleep(30); kbc->keypress_caused_wake = false; /* Enable keypress interrupt before going into suspend. */ tegra_kbc_set_keypress_interrupt(kbc, true); enable_irq(kbc->irq); enable_irq_wake(kbc->irq); } else { if (kbc->idev->users) tegra_kbc_stop(kbc); } mutex_unlock(&kbc->idev->mutex); return 0; } static int tegra_kbc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_kbc *kbc = platform_get_drvdata(pdev); int err = 0; mutex_lock(&kbc->idev->mutex); if (device_may_wakeup(&pdev->dev)) { disable_irq_wake(kbc->irq); tegra_kbc_setup_wakekeys(kbc, false); /* We will use fifo interrupts for key detection. */ tegra_kbc_set_keypress_interrupt(kbc, false); /* Restore the resident time of continuous polling mode. */ writel(kbc->cp_to_wkup_dly, kbc->mmio + KBC_TO_CNT_0); tegra_kbc_set_fifo_interrupt(kbc, true); if (kbc->keypress_caused_wake && kbc->wakeup_key) { /* * We can't report events directly from the ISR * because timekeeping is stopped when processing * wakeup request and we get a nasty warning when * we try to call do_gettimeofday() in evdev * handler. */ input_report_key(kbc->idev, kbc->wakeup_key, 1); input_sync(kbc->idev); input_report_key(kbc->idev, kbc->wakeup_key, 0); input_sync(kbc->idev); } } else { if (kbc->idev->users) err = tegra_kbc_start(kbc); } mutex_unlock(&kbc->idev->mutex); return err; } #endif static SIMPLE_DEV_PM_OPS(tegra_kbc_pm_ops, tegra_kbc_suspend, tegra_kbc_resume); static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, .driver = { .name = "tegra-kbc", .pm = &tegra_kbc_pm_ops, .of_match_table = tegra_kbc_of_match, }, }; module_platform_driver(tegra_kbc_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Rakesh Iyer <riyer@nvidia.com>"); MODULE_DESCRIPTION("Tegra matrix keyboard controller driver"); MODULE_ALIAS("platform:tegra-kbc");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1