Merge tag 'spdx-6.0-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[linux-2.6-microblaze.git] / drivers / input / misc / ariel-pwrbutton.c
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
20 struct ec_input_response {
21         u8 reserved;
22         u8 header;
23         u8 data[3];
24 } __packed;
25
26 struct ariel_pwrbutton {
27         struct spi_device *client;
28         struct input_dev *input;
29         u8 msg_counter;
30 };
31
32 static 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
49 static 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
91 out:
92         return IRQ_HANDLED;
93 }
94
95 static 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
146 static const struct of_device_id ariel_pwrbutton_of_match[] = {
147         { .compatible = "dell,wyse-ariel-ec-input" },
148         { }
149 };
150 MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match);
151
152 static const struct spi_device_id ariel_pwrbutton_spi_ids[] = {
153         { .name = "wyse-ariel-ec-input" },
154         { }
155 };
156 MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids);
157
158 static 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 };
166 module_spi_driver(ariel_pwrbutton_driver);
167
168 MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
169 MODULE_DESCRIPTION("Dell Wyse 3020 Power Button Input Driver");
170 MODULE_LICENSE("Dual BSD/GPL");