cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spear-keyboard.c (9475B)


      1/*
      2 * SPEAr Keyboard Driver
      3 * Based on omap-keypad driver
      4 *
      5 * Copyright (C) 2010 ST Microelectronics
      6 * Rajeev Kumar <rajeevkumar.linux@gmail.com>
      7 *
      8 * This file is licensed under the terms of the GNU General Public
      9 * License version 2. This program is licensed "as is" without any
     10 * warranty of any kind, whether express or implied.
     11 */
     12
     13#include <linux/clk.h>
     14#include <linux/errno.h>
     15#include <linux/interrupt.h>
     16#include <linux/input.h>
     17#include <linux/io.h>
     18#include <linux/irq.h>
     19#include <linux/kernel.h>
     20#include <linux/module.h>
     21#include <linux/of.h>
     22#include <linux/platform_device.h>
     23#include <linux/pm_wakeup.h>
     24#include <linux/slab.h>
     25#include <linux/types.h>
     26#include <linux/platform_data/keyboard-spear.h>
     27
     28/* Keyboard Registers */
     29#define MODE_CTL_REG	0x00
     30#define STATUS_REG	0x0C
     31#define DATA_REG	0x10
     32#define INTR_MASK	0x54
     33
     34/* Register Values */
     35#define NUM_ROWS	16
     36#define NUM_COLS	16
     37#define MODE_CTL_PCLK_FREQ_SHIFT	9
     38#define MODE_CTL_PCLK_FREQ_MSK		0x7F
     39
     40#define MODE_CTL_KEYBOARD	(0x2 << 0)
     41#define MODE_CTL_SCAN_RATE_10	(0x0 << 2)
     42#define MODE_CTL_SCAN_RATE_20	(0x1 << 2)
     43#define MODE_CTL_SCAN_RATE_40	(0x2 << 2)
     44#define MODE_CTL_SCAN_RATE_80	(0x3 << 2)
     45#define MODE_CTL_KEYNUM_SHIFT	6
     46#define MODE_CTL_START_SCAN	(0x1 << 8)
     47
     48#define STATUS_DATA_AVAIL	(0x1 << 1)
     49
     50#define DATA_ROW_MASK		0xF0
     51#define DATA_COLUMN_MASK	0x0F
     52
     53#define ROW_SHIFT		4
     54
     55struct spear_kbd {
     56	struct input_dev *input;
     57	void __iomem *io_base;
     58	struct clk *clk;
     59	unsigned int irq;
     60	unsigned int mode;
     61	unsigned int suspended_rate;
     62	unsigned short last_key;
     63	unsigned short keycodes[NUM_ROWS * NUM_COLS];
     64	bool rep;
     65	bool irq_wake_enabled;
     66	u32 mode_ctl_reg;
     67};
     68
     69static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
     70{
     71	struct spear_kbd *kbd = dev_id;
     72	struct input_dev *input = kbd->input;
     73	unsigned int key;
     74	u32 sts, val;
     75
     76	sts = readl_relaxed(kbd->io_base + STATUS_REG);
     77	if (!(sts & STATUS_DATA_AVAIL))
     78		return IRQ_NONE;
     79
     80	if (kbd->last_key != KEY_RESERVED) {
     81		input_report_key(input, kbd->last_key, 0);
     82		kbd->last_key = KEY_RESERVED;
     83	}
     84
     85	/* following reads active (row, col) pair */
     86	val = readl_relaxed(kbd->io_base + DATA_REG) &
     87		(DATA_ROW_MASK | DATA_COLUMN_MASK);
     88	key = kbd->keycodes[val];
     89
     90	input_event(input, EV_MSC, MSC_SCAN, val);
     91	input_report_key(input, key, 1);
     92	input_sync(input);
     93
     94	kbd->last_key = key;
     95
     96	/* clear interrupt */
     97	writel_relaxed(0, kbd->io_base + STATUS_REG);
     98
     99	return IRQ_HANDLED;
    100}
    101
    102static int spear_kbd_open(struct input_dev *dev)
    103{
    104	struct spear_kbd *kbd = input_get_drvdata(dev);
    105	int error;
    106	u32 val;
    107
    108	kbd->last_key = KEY_RESERVED;
    109
    110	error = clk_enable(kbd->clk);
    111	if (error)
    112		return error;
    113
    114	/* keyboard rate to be programmed is input clock (in MHz) - 1 */
    115	val = clk_get_rate(kbd->clk) / 1000000 - 1;
    116	val = (val & MODE_CTL_PCLK_FREQ_MSK) << MODE_CTL_PCLK_FREQ_SHIFT;
    117
    118	/* program keyboard */
    119	val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val |
    120		(kbd->mode << MODE_CTL_KEYNUM_SHIFT);
    121	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
    122	writel_relaxed(1, kbd->io_base + STATUS_REG);
    123
    124	/* start key scan */
    125	val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
    126	val |= MODE_CTL_START_SCAN;
    127	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
    128
    129	return 0;
    130}
    131
    132static void spear_kbd_close(struct input_dev *dev)
    133{
    134	struct spear_kbd *kbd = input_get_drvdata(dev);
    135	u32 val;
    136
    137	/* stop key scan */
    138	val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
    139	val &= ~MODE_CTL_START_SCAN;
    140	writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
    141
    142	clk_disable(kbd->clk);
    143
    144	kbd->last_key = KEY_RESERVED;
    145}
    146
    147#ifdef CONFIG_OF
    148static int spear_kbd_parse_dt(struct platform_device *pdev,
    149                                        struct spear_kbd *kbd)
    150{
    151	struct device_node *np = pdev->dev.of_node;
    152	int error;
    153	u32 val, suspended_rate;
    154
    155	if (!np) {
    156		dev_err(&pdev->dev, "Missing DT data\n");
    157		return -EINVAL;
    158	}
    159
    160	if (of_property_read_bool(np, "autorepeat"))
    161		kbd->rep = true;
    162
    163	if (of_property_read_u32(np, "suspended_rate", &suspended_rate))
    164		kbd->suspended_rate = suspended_rate;
    165
    166	error = of_property_read_u32(np, "st,mode", &val);
    167	if (error) {
    168		dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
    169		return error;
    170	}
    171
    172	kbd->mode = val;
    173	return 0;
    174}
    175#else
    176static inline int spear_kbd_parse_dt(struct platform_device *pdev,
    177				     struct spear_kbd *kbd)
    178{
    179	return -ENOSYS;
    180}
    181#endif
    182
    183static int spear_kbd_probe(struct platform_device *pdev)
    184{
    185	struct kbd_platform_data *pdata = dev_get_platdata(&pdev->dev);
    186	const struct matrix_keymap_data *keymap = pdata ? pdata->keymap : NULL;
    187	struct spear_kbd *kbd;
    188	struct input_dev *input_dev;
    189	struct resource *res;
    190	int irq;
    191	int error;
    192
    193	irq = platform_get_irq(pdev, 0);
    194	if (irq < 0)
    195		return irq;
    196
    197	kbd = devm_kzalloc(&pdev->dev, sizeof(*kbd), GFP_KERNEL);
    198	if (!kbd) {
    199		dev_err(&pdev->dev, "not enough memory for driver data\n");
    200		return -ENOMEM;
    201	}
    202
    203	input_dev = devm_input_allocate_device(&pdev->dev);
    204	if (!input_dev) {
    205		dev_err(&pdev->dev, "unable to allocate input device\n");
    206		return -ENOMEM;
    207	}
    208
    209	kbd->input = input_dev;
    210	kbd->irq = irq;
    211
    212	if (!pdata) {
    213		error = spear_kbd_parse_dt(pdev, kbd);
    214		if (error)
    215			return error;
    216	} else {
    217		kbd->mode = pdata->mode;
    218		kbd->rep = pdata->rep;
    219		kbd->suspended_rate = pdata->suspended_rate;
    220	}
    221
    222	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    223	kbd->io_base = devm_ioremap_resource(&pdev->dev, res);
    224	if (IS_ERR(kbd->io_base))
    225		return PTR_ERR(kbd->io_base);
    226
    227	kbd->clk = devm_clk_get(&pdev->dev, NULL);
    228	if (IS_ERR(kbd->clk))
    229		return PTR_ERR(kbd->clk);
    230
    231	input_dev->name = "Spear Keyboard";
    232	input_dev->phys = "keyboard/input0";
    233	input_dev->id.bustype = BUS_HOST;
    234	input_dev->id.vendor = 0x0001;
    235	input_dev->id.product = 0x0001;
    236	input_dev->id.version = 0x0100;
    237	input_dev->open = spear_kbd_open;
    238	input_dev->close = spear_kbd_close;
    239
    240	error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS,
    241					   kbd->keycodes, input_dev);
    242	if (error) {
    243		dev_err(&pdev->dev, "Failed to build keymap\n");
    244		return error;
    245	}
    246
    247	if (kbd->rep)
    248		__set_bit(EV_REP, input_dev->evbit);
    249	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
    250
    251	input_set_drvdata(input_dev, kbd);
    252
    253	error = devm_request_irq(&pdev->dev, irq, spear_kbd_interrupt, 0,
    254			"keyboard", kbd);
    255	if (error) {
    256		dev_err(&pdev->dev, "request_irq failed\n");
    257		return error;
    258	}
    259
    260	error = clk_prepare(kbd->clk);
    261	if (error)
    262		return error;
    263
    264	error = input_register_device(input_dev);
    265	if (error) {
    266		dev_err(&pdev->dev, "Unable to register keyboard device\n");
    267		clk_unprepare(kbd->clk);
    268		return error;
    269	}
    270
    271	device_init_wakeup(&pdev->dev, 1);
    272	platform_set_drvdata(pdev, kbd);
    273
    274	return 0;
    275}
    276
    277static int spear_kbd_remove(struct platform_device *pdev)
    278{
    279	struct spear_kbd *kbd = platform_get_drvdata(pdev);
    280
    281	input_unregister_device(kbd->input);
    282	clk_unprepare(kbd->clk);
    283
    284	return 0;
    285}
    286
    287static int __maybe_unused spear_kbd_suspend(struct device *dev)
    288{
    289	struct platform_device *pdev = to_platform_device(dev);
    290	struct spear_kbd *kbd = platform_get_drvdata(pdev);
    291	struct input_dev *input_dev = kbd->input;
    292	unsigned int rate = 0, mode_ctl_reg, val;
    293
    294	mutex_lock(&input_dev->mutex);
    295
    296	/* explicitly enable clock as we may program device */
    297	clk_enable(kbd->clk);
    298
    299	mode_ctl_reg = readl_relaxed(kbd->io_base + MODE_CTL_REG);
    300
    301	if (device_may_wakeup(&pdev->dev)) {
    302		if (!enable_irq_wake(kbd->irq))
    303			kbd->irq_wake_enabled = true;
    304
    305		/*
    306		 * reprogram the keyboard operating frequency as on some
    307		 * platform it may change during system suspended
    308		 */
    309		if (kbd->suspended_rate)
    310			rate = kbd->suspended_rate / 1000000 - 1;
    311		else
    312			rate = clk_get_rate(kbd->clk) / 1000000 - 1;
    313
    314		val = mode_ctl_reg &
    315			~(MODE_CTL_PCLK_FREQ_MSK << MODE_CTL_PCLK_FREQ_SHIFT);
    316		val |= (rate & MODE_CTL_PCLK_FREQ_MSK)
    317			<< MODE_CTL_PCLK_FREQ_SHIFT;
    318		writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
    319
    320	} else {
    321		if (input_device_enabled(input_dev)) {
    322			writel_relaxed(mode_ctl_reg & ~MODE_CTL_START_SCAN,
    323					kbd->io_base + MODE_CTL_REG);
    324			clk_disable(kbd->clk);
    325		}
    326	}
    327
    328	/* store current configuration */
    329	if (input_device_enabled(input_dev))
    330		kbd->mode_ctl_reg = mode_ctl_reg;
    331
    332	/* restore previous clk state */
    333	clk_disable(kbd->clk);
    334
    335	mutex_unlock(&input_dev->mutex);
    336
    337	return 0;
    338}
    339
    340static int __maybe_unused spear_kbd_resume(struct device *dev)
    341{
    342	struct platform_device *pdev = to_platform_device(dev);
    343	struct spear_kbd *kbd = platform_get_drvdata(pdev);
    344	struct input_dev *input_dev = kbd->input;
    345
    346	mutex_lock(&input_dev->mutex);
    347
    348	if (device_may_wakeup(&pdev->dev)) {
    349		if (kbd->irq_wake_enabled) {
    350			kbd->irq_wake_enabled = false;
    351			disable_irq_wake(kbd->irq);
    352		}
    353	} else {
    354		if (input_device_enabled(input_dev))
    355			clk_enable(kbd->clk);
    356	}
    357
    358	/* restore current configuration */
    359	if (input_device_enabled(input_dev))
    360		writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG);
    361
    362	mutex_unlock(&input_dev->mutex);
    363
    364	return 0;
    365}
    366
    367static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume);
    368
    369#ifdef CONFIG_OF
    370static const struct of_device_id spear_kbd_id_table[] = {
    371	{ .compatible = "st,spear300-kbd" },
    372	{}
    373};
    374MODULE_DEVICE_TABLE(of, spear_kbd_id_table);
    375#endif
    376
    377static struct platform_driver spear_kbd_driver = {
    378	.probe		= spear_kbd_probe,
    379	.remove		= spear_kbd_remove,
    380	.driver		= {
    381		.name	= "keyboard",
    382		.pm	= &spear_kbd_pm_ops,
    383		.of_match_table = of_match_ptr(spear_kbd_id_table),
    384	},
    385};
    386module_platform_driver(spear_kbd_driver);
    387
    388MODULE_AUTHOR("Rajeev Kumar");
    389MODULE_DESCRIPTION("SPEAr Keyboard Driver");
    390MODULE_LICENSE("GPL");