root/drivers/input/keyboard/tegra-kbc.c
// 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/property.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 = timer_container_of(kbc, t, timer);
        u32 val;
        unsigned int i;

        guard(spinlock_irqsave)(&kbc->lock);

        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);
        }
}

static irqreturn_t tegra_kbc_isr(int irq, void *args)
{
        struct tegra_kbc *kbc = args;
        u32 val;

        guard(spinlock_irqsave)(&kbc->lock);

        /*
         * 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;
        }

        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)
{
        u32 val;

        scoped_guard(spinlock_irqsave, &kbc->lock) {
                val = readl(kbc->mmio + KBC_CONTROL_0);
                val &= ~1;
                writel(val, kbc->mmio + KBC_CONTROL_0);
        }

        disable_irq(kbc->irq);
        timer_delete_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;
        int num_rows;
        int num_cols;
        u32 cols_cfg[KBC_MAX_GPIO];
        u32 rows_cfg[KBC_MAX_GPIO];

        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;

        kbc->use_ghost_filter = of_property_present(np, "nvidia,needs-ghost-filter");

        if (of_property_read_bool(np, "wakeup-source") ||
            of_property_read_bool(np, "nvidia,wakeup-source")) /* legacy */
                kbc->wakeup = true;

        if (!of_property_present(np, "linux,keymap")) {
                dev_err(kbc->dev, "property linux,keymap not found\n");
                return -ENOENT;
        }

        /* Set all pins as non-configured */
        for (i = 0; i < kbc->num_rows_and_columns; i++)
                kbc->pin_cfg[i].type = PIN_CFG_IGNORE;

        num_rows = of_property_read_variable_u32_array(np, "nvidia,kbc-row-pins",
                                rows_cfg, 1, KBC_MAX_GPIO);
        if (num_rows < 0) {
                dev_err(kbc->dev, "Rows configurations are not proper\n");
                return num_rows;
        } else if (num_rows > kbc->hw_support->max_rows) {
                dev_err(kbc->dev,
                        "Number of rows is more than supported by hardware\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;
        }

        num_cols = of_property_read_variable_u32_array(np, "nvidia,kbc-col-pins",
                                cols_cfg, 1, KBC_MAX_GPIO);
        if (num_cols < 0) {
                dev_err(kbc->dev, "Cols configurations are not proper\n");
                return num_cols;
        } else if (num_cols > kbc->hw_support->max_columns) {
                dev_err(kbc->dev,
                        "Number of cols is more than supported by hardware\n");
                return -EINVAL;
        }

        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;
        }

        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;
        }

        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;
        int err;
        int num_rows = 0;
        unsigned int debounce_cnt;
        unsigned int scan_time_rows;
        unsigned int keymap_rows;

        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 = device_get_match_data(&pdev->dev);
        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)
                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);

        kbc->mmio = devm_platform_ioremap_resource(pdev, 0);
        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 | IRQF_NO_AUTOEN,
                               pdev->name, kbc);
        if (err) {
                dev_err(&pdev->dev, "failed to request keyboard IRQ\n");
                return err;
        }

        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;
}

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);

        guard(mutex)(&kbc->idev->mutex);

        if (device_may_wakeup(&pdev->dev)) {
                disable_irq(kbc->irq);
                timer_delete_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 (input_device_enabled(kbc->idev)) {
                tegra_kbc_stop(kbc);
        }

        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;

        guard(mutex)(&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 (input_device_enabled(kbc->idev)) {
                err = tegra_kbc_start(kbc);
                if (err)
                        return err;
        }

        return 0;
}

static DEFINE_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     = pm_sleep_ptr(&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");