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