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