linux/drivers/input/misc/ariel-pwrbutton.c
<<
>>
Prefs
   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 struct spi_driver ariel_pwrbutton_driver = {
 153        .driver = {
 154                .name = "dell-wyse-ariel-ec-input",
 155                .of_match_table = ariel_pwrbutton_of_match,
 156        },
 157        .probe = ariel_pwrbutton_probe,
 158};
 159module_spi_driver(ariel_pwrbutton_driver);
 160
 161MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
 162MODULE_DESCRIPTION("Dell Wyse 3020 Power Button Input Driver");
 163MODULE_LICENSE("Dual BSD/GPL");
 164