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

ariel-pwrbutton.c (4119B)


      1// SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
      2/*
      3 * Dell Wyse 3020 a.k.a. "Ariel" Power Button Driver
      4 *
      5 * Copyright (C) 2020 Lubomir Rintel
      6 */
      7
      8#include <linux/device.h>
      9#include <linux/gfp.h>
     10#include <linux/input.h>
     11#include <linux/interrupt.h>
     12#include <linux/mod_devicetable.h>
     13#include <linux/module.h>
     14#include <linux/spi/spi.h>
     15
     16#define RESP_COUNTER(response)	(response.header & 0x3)
     17#define RESP_SIZE(response)	((response.header >> 2) & 0x3)
     18#define RESP_TYPE(response)	((response.header >> 4) & 0xf)
     19
     20struct ec_input_response {
     21	u8 reserved;
     22	u8 header;
     23	u8 data[3];
     24} __packed;
     25
     26struct ariel_pwrbutton {
     27	struct spi_device *client;
     28	struct input_dev *input;
     29	u8 msg_counter;
     30};
     31
     32static int ec_input_read(struct ariel_pwrbutton *priv,
     33			 struct ec_input_response *response)
     34{
     35	u8 read_request[] = { 0x00, 0x5a, 0xa5, 0x00, 0x00 };
     36	struct spi_device *spi = priv->client;
     37	struct spi_transfer t = {
     38		.tx_buf = read_request,
     39		.rx_buf = response,
     40		.len = sizeof(read_request),
     41	};
     42
     43	compiletime_assert(sizeof(read_request) == sizeof(*response),
     44			   "SPI xfer request/response size mismatch");
     45
     46	return spi_sync_transfer(spi, &t, 1);
     47}
     48
     49static irqreturn_t ec_input_interrupt(int irq, void *dev_id)
     50{
     51	struct ariel_pwrbutton *priv = dev_id;
     52	struct spi_device *spi = priv->client;
     53	struct ec_input_response response;
     54	int error;
     55	int i;
     56
     57	error = ec_input_read(priv, &response);
     58	if (error < 0) {
     59		dev_err(&spi->dev, "EC read failed: %d\n", error);
     60		goto out;
     61	}
     62
     63	if (priv->msg_counter == RESP_COUNTER(response)) {
     64		dev_warn(&spi->dev, "No new data to read?\n");
     65		goto out;
     66	}
     67
     68	priv->msg_counter = RESP_COUNTER(response);
     69
     70	if (RESP_TYPE(response) != 0x3 && RESP_TYPE(response) != 0xc) {
     71		dev_dbg(&spi->dev, "Ignoring message that's not kbd data\n");
     72		goto out;
     73	}
     74
     75	for (i = 0; i < RESP_SIZE(response); i++) {
     76		switch (response.data[i]) {
     77		case 0x74:
     78			input_report_key(priv->input, KEY_POWER, 1);
     79			input_sync(priv->input);
     80			break;
     81		case 0xf4:
     82			input_report_key(priv->input, KEY_POWER, 0);
     83			input_sync(priv->input);
     84			break;
     85		default:
     86			dev_dbg(&spi->dev, "Unknown scan code: %02x\n",
     87				response.data[i]);
     88		}
     89	}
     90
     91out:
     92	return IRQ_HANDLED;
     93}
     94
     95static int ariel_pwrbutton_probe(struct spi_device *spi)
     96{
     97	struct ec_input_response response;
     98	struct ariel_pwrbutton *priv;
     99	int error;
    100
    101	if (!spi->irq) {
    102		dev_err(&spi->dev, "Missing IRQ.\n");
    103		return -EINVAL;
    104	}
    105
    106	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
    107	if (!priv)
    108		return -ENOMEM;
    109
    110	priv->client = spi;
    111	spi_set_drvdata(spi, priv);
    112
    113	priv->input = devm_input_allocate_device(&spi->dev);
    114	if (!priv->input)
    115		return -ENOMEM;
    116	priv->input->name = "Power Button";
    117	priv->input->dev.parent = &spi->dev;
    118	input_set_capability(priv->input, EV_KEY, KEY_POWER);
    119	error = input_register_device(priv->input);
    120	if (error) {
    121		dev_err(&spi->dev, "error registering input device: %d\n", error);
    122		return error;
    123	}
    124
    125	error = ec_input_read(priv, &response);
    126	if (error < 0) {
    127		dev_err(&spi->dev, "EC read failed: %d\n", error);
    128		return error;
    129	}
    130	priv->msg_counter = RESP_COUNTER(response);
    131
    132	error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
    133					  ec_input_interrupt,
    134					  IRQF_ONESHOT,
    135					  "Ariel EC Input", priv);
    136
    137	if (error) {
    138		dev_err(&spi->dev, "Failed to request IRQ %d: %d\n",
    139			spi->irq, error);
    140		return error;
    141	}
    142
    143	return 0;
    144}
    145
    146static const struct of_device_id ariel_pwrbutton_of_match[] = {
    147	{ .compatible = "dell,wyse-ariel-ec-input" },
    148	{ }
    149};
    150MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match);
    151
    152static const struct spi_device_id ariel_pwrbutton_spi_ids[] = {
    153	{ .name = "wyse-ariel-ec-input" },
    154	{ }
    155};
    156MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids);
    157
    158static struct spi_driver ariel_pwrbutton_driver = {
    159	.driver = {
    160		.name = "dell-wyse-ariel-ec-input",
    161		.of_match_table = ariel_pwrbutton_of_match,
    162	},
    163	.probe = ariel_pwrbutton_probe,
    164	.id_table = ariel_pwrbutton_spi_ids,
    165};
    166module_spi_driver(ariel_pwrbutton_driver);
    167
    168MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
    169MODULE_DESCRIPTION("Dell Wyse 3020 Power Button Input Driver");
    170MODULE_LICENSE("Dual BSD/GPL");