13d82a4d7SLubomir Rintel // SPDX-License-Identifier: BSD-2-Clause OR GPL-2.0-or-later
23d82a4d7SLubomir Rintel /*
33d82a4d7SLubomir Rintel  * Dell Wyse 3020 a.k.a. "Ariel" Power Button Driver
43d82a4d7SLubomir Rintel  *
53d82a4d7SLubomir Rintel  * Copyright (C) 2020 Lubomir Rintel
63d82a4d7SLubomir Rintel  */
73d82a4d7SLubomir Rintel 
83d82a4d7SLubomir Rintel #include <linux/device.h>
93d82a4d7SLubomir Rintel #include <linux/gfp.h>
103d82a4d7SLubomir Rintel #include <linux/input.h>
113d82a4d7SLubomir Rintel #include <linux/interrupt.h>
123d82a4d7SLubomir Rintel #include <linux/mod_devicetable.h>
133d82a4d7SLubomir Rintel #include <linux/module.h>
143d82a4d7SLubomir Rintel #include <linux/spi/spi.h>
153d82a4d7SLubomir Rintel 
163d82a4d7SLubomir Rintel #define RESP_COUNTER(response)	(response.header & 0x3)
173d82a4d7SLubomir Rintel #define RESP_SIZE(response)	((response.header >> 2) & 0x3)
183d82a4d7SLubomir Rintel #define RESP_TYPE(response)	((response.header >> 4) & 0xf)
193d82a4d7SLubomir Rintel 
203d82a4d7SLubomir Rintel struct ec_input_response {
213d82a4d7SLubomir Rintel 	u8 reserved;
223d82a4d7SLubomir Rintel 	u8 header;
233d82a4d7SLubomir Rintel 	u8 data[3];
243d82a4d7SLubomir Rintel } __packed;
253d82a4d7SLubomir Rintel 
263d82a4d7SLubomir Rintel struct ariel_pwrbutton {
273d82a4d7SLubomir Rintel 	struct spi_device *client;
283d82a4d7SLubomir Rintel 	struct input_dev *input;
293d82a4d7SLubomir Rintel 	u8 msg_counter;
303d82a4d7SLubomir Rintel };
313d82a4d7SLubomir Rintel 
ec_input_read(struct ariel_pwrbutton * priv,struct ec_input_response * response)323d82a4d7SLubomir Rintel static int ec_input_read(struct ariel_pwrbutton *priv,
333d82a4d7SLubomir Rintel 			 struct ec_input_response *response)
343d82a4d7SLubomir Rintel {
353d82a4d7SLubomir Rintel 	u8 read_request[] = { 0x00, 0x5a, 0xa5, 0x00, 0x00 };
363d82a4d7SLubomir Rintel 	struct spi_device *spi = priv->client;
373d82a4d7SLubomir Rintel 	struct spi_transfer t = {
383d82a4d7SLubomir Rintel 		.tx_buf = read_request,
393d82a4d7SLubomir Rintel 		.rx_buf = response,
403d82a4d7SLubomir Rintel 		.len = sizeof(read_request),
413d82a4d7SLubomir Rintel 	};
423d82a4d7SLubomir Rintel 
433d82a4d7SLubomir Rintel 	compiletime_assert(sizeof(read_request) == sizeof(*response),
443d82a4d7SLubomir Rintel 			   "SPI xfer request/response size mismatch");
453d82a4d7SLubomir Rintel 
463d82a4d7SLubomir Rintel 	return spi_sync_transfer(spi, &t, 1);
473d82a4d7SLubomir Rintel }
483d82a4d7SLubomir Rintel 
ec_input_interrupt(int irq,void * dev_id)493d82a4d7SLubomir Rintel static irqreturn_t ec_input_interrupt(int irq, void *dev_id)
503d82a4d7SLubomir Rintel {
513d82a4d7SLubomir Rintel 	struct ariel_pwrbutton *priv = dev_id;
523d82a4d7SLubomir Rintel 	struct spi_device *spi = priv->client;
533d82a4d7SLubomir Rintel 	struct ec_input_response response;
543d82a4d7SLubomir Rintel 	int error;
553d82a4d7SLubomir Rintel 	int i;
563d82a4d7SLubomir Rintel 
573d82a4d7SLubomir Rintel 	error = ec_input_read(priv, &response);
583d82a4d7SLubomir Rintel 	if (error < 0) {
593d82a4d7SLubomir Rintel 		dev_err(&spi->dev, "EC read failed: %d\n", error);
603d82a4d7SLubomir Rintel 		goto out;
613d82a4d7SLubomir Rintel 	}
623d82a4d7SLubomir Rintel 
633d82a4d7SLubomir Rintel 	if (priv->msg_counter == RESP_COUNTER(response)) {
643d82a4d7SLubomir Rintel 		dev_warn(&spi->dev, "No new data to read?\n");
653d82a4d7SLubomir Rintel 		goto out;
663d82a4d7SLubomir Rintel 	}
673d82a4d7SLubomir Rintel 
683d82a4d7SLubomir Rintel 	priv->msg_counter = RESP_COUNTER(response);
693d82a4d7SLubomir Rintel 
703d82a4d7SLubomir Rintel 	if (RESP_TYPE(response) != 0x3 && RESP_TYPE(response) != 0xc) {
713d82a4d7SLubomir Rintel 		dev_dbg(&spi->dev, "Ignoring message that's not kbd data\n");
723d82a4d7SLubomir Rintel 		goto out;
733d82a4d7SLubomir Rintel 	}
743d82a4d7SLubomir Rintel 
753d82a4d7SLubomir Rintel 	for (i = 0; i < RESP_SIZE(response); i++) {
763d82a4d7SLubomir Rintel 		switch (response.data[i]) {
773d82a4d7SLubomir Rintel 		case 0x74:
783d82a4d7SLubomir Rintel 			input_report_key(priv->input, KEY_POWER, 1);
793d82a4d7SLubomir Rintel 			input_sync(priv->input);
803d82a4d7SLubomir Rintel 			break;
813d82a4d7SLubomir Rintel 		case 0xf4:
823d82a4d7SLubomir Rintel 			input_report_key(priv->input, KEY_POWER, 0);
833d82a4d7SLubomir Rintel 			input_sync(priv->input);
843d82a4d7SLubomir Rintel 			break;
853d82a4d7SLubomir Rintel 		default:
863d82a4d7SLubomir Rintel 			dev_dbg(&spi->dev, "Unknown scan code: %02x\n",
873d82a4d7SLubomir Rintel 				response.data[i]);
883d82a4d7SLubomir Rintel 		}
893d82a4d7SLubomir Rintel 	}
903d82a4d7SLubomir Rintel 
913d82a4d7SLubomir Rintel out:
923d82a4d7SLubomir Rintel 	return IRQ_HANDLED;
933d82a4d7SLubomir Rintel }
943d82a4d7SLubomir Rintel 
ariel_pwrbutton_probe(struct spi_device * spi)953d82a4d7SLubomir Rintel static int ariel_pwrbutton_probe(struct spi_device *spi)
963d82a4d7SLubomir Rintel {
973d82a4d7SLubomir Rintel 	struct ec_input_response response;
983d82a4d7SLubomir Rintel 	struct ariel_pwrbutton *priv;
993d82a4d7SLubomir Rintel 	int error;
1003d82a4d7SLubomir Rintel 
1013d82a4d7SLubomir Rintel 	if (!spi->irq) {
1023d82a4d7SLubomir Rintel 		dev_err(&spi->dev, "Missing IRQ.\n");
1033d82a4d7SLubomir Rintel 		return -EINVAL;
1043d82a4d7SLubomir Rintel 	}
1053d82a4d7SLubomir Rintel 
1063d82a4d7SLubomir Rintel 	priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
1073d82a4d7SLubomir Rintel 	if (!priv)
1083d82a4d7SLubomir Rintel 		return -ENOMEM;
1093d82a4d7SLubomir Rintel 
1103d82a4d7SLubomir Rintel 	priv->client = spi;
1113d82a4d7SLubomir Rintel 	spi_set_drvdata(spi, priv);
1123d82a4d7SLubomir Rintel 
1133d82a4d7SLubomir Rintel 	priv->input = devm_input_allocate_device(&spi->dev);
1143d82a4d7SLubomir Rintel 	if (!priv->input)
1153d82a4d7SLubomir Rintel 		return -ENOMEM;
1163d82a4d7SLubomir Rintel 	priv->input->name = "Power Button";
1173d82a4d7SLubomir Rintel 	priv->input->dev.parent = &spi->dev;
1183d82a4d7SLubomir Rintel 	input_set_capability(priv->input, EV_KEY, KEY_POWER);
1193d82a4d7SLubomir Rintel 	error = input_register_device(priv->input);
1203d82a4d7SLubomir Rintel 	if (error) {
1213d82a4d7SLubomir Rintel 		dev_err(&spi->dev, "error registering input device: %d\n", error);
1223d82a4d7SLubomir Rintel 		return error;
1233d82a4d7SLubomir Rintel 	}
1243d82a4d7SLubomir Rintel 
1253d82a4d7SLubomir Rintel 	error = ec_input_read(priv, &response);
1263d82a4d7SLubomir Rintel 	if (error < 0) {
1273d82a4d7SLubomir Rintel 		dev_err(&spi->dev, "EC read failed: %d\n", error);
1283d82a4d7SLubomir Rintel 		return error;
1293d82a4d7SLubomir Rintel 	}
1303d82a4d7SLubomir Rintel 	priv->msg_counter = RESP_COUNTER(response);
1313d82a4d7SLubomir Rintel 
1323d82a4d7SLubomir Rintel 	error = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
1333d82a4d7SLubomir Rintel 					  ec_input_interrupt,
1343d82a4d7SLubomir Rintel 					  IRQF_ONESHOT,
1353d82a4d7SLubomir Rintel 					  "Ariel EC Input", priv);
1363d82a4d7SLubomir Rintel 
1373d82a4d7SLubomir Rintel 	if (error) {
1383d82a4d7SLubomir Rintel 		dev_err(&spi->dev, "Failed to request IRQ %d: %d\n",
1393d82a4d7SLubomir Rintel 			spi->irq, error);
1403d82a4d7SLubomir Rintel 		return error;
1413d82a4d7SLubomir Rintel 	}
1423d82a4d7SLubomir Rintel 
1433d82a4d7SLubomir Rintel 	return 0;
1443d82a4d7SLubomir Rintel }
1453d82a4d7SLubomir Rintel 
1463d82a4d7SLubomir Rintel static const struct of_device_id ariel_pwrbutton_of_match[] = {
1473d82a4d7SLubomir Rintel 	{ .compatible = "dell,wyse-ariel-ec-input" },
1483d82a4d7SLubomir Rintel 	{ }
1493d82a4d7SLubomir Rintel };
1503d82a4d7SLubomir Rintel MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match);
1513d82a4d7SLubomir Rintel 
152*17216499SMark Brown static const struct spi_device_id ariel_pwrbutton_spi_ids[] = {
153*17216499SMark Brown 	{ .name = "wyse-ariel-ec-input" },
154*17216499SMark Brown 	{ }
155*17216499SMark Brown };
156*17216499SMark Brown MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids);
157*17216499SMark Brown 
1583d82a4d7SLubomir Rintel static struct spi_driver ariel_pwrbutton_driver = {
1593d82a4d7SLubomir Rintel 	.driver = {
1603d82a4d7SLubomir Rintel 		.name = "dell-wyse-ariel-ec-input",
1613d82a4d7SLubomir Rintel 		.of_match_table = ariel_pwrbutton_of_match,
1623d82a4d7SLubomir Rintel 	},
1633d82a4d7SLubomir Rintel 	.probe = ariel_pwrbutton_probe,
164*17216499SMark Brown 	.id_table = ariel_pwrbutton_spi_ids,
1653d82a4d7SLubomir Rintel };
1663d82a4d7SLubomir Rintel module_spi_driver(ariel_pwrbutton_driver);
1673d82a4d7SLubomir Rintel 
1683d82a4d7SLubomir Rintel MODULE_AUTHOR("Lubomir Rintel <lkundrak@v3.sk>");
1693d82a4d7SLubomir Rintel MODULE_DESCRIPTION("Dell Wyse 3020 Power Button Input Driver");
1703d82a4d7SLubomir Rintel MODULE_LICENSE("Dual BSD/GPL");
171