aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2017-05-22 09:11:45 -0400
committerMark Brown <broonie@kernel.org>2017-05-26 08:12:19 -0400
commitce70e06c093a9609377e93ee20e7c528e156af14 (patch)
tree1d848c5d6a61ce975bd201c24622e2779c62309b
parent29f9ffa0e1f9a17c866c04a01acfc9976d78f29a (diff)
spi: slave: Add SPI slave handler controlling system state
Add an example SPI slave handler to allow remote control of system reboot, power off, halt, and suspend. Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--drivers/spi/Kconfig6
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-slave-system-control.c154
3 files changed, 161 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index ade542c5bfd8..e6d9e329a380 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -802,6 +802,12 @@ config SPI_SLAVE_TIME
802 SPI slave handler responding with the time of reception of the last 802 SPI slave handler responding with the time of reception of the last
803 SPI message. 803 SPI message.
804 804
805config SPI_SLAVE_SYSTEM_CONTROL
806 tristate "SPI slave handler controlling system state"
807 help
808 SPI slave handler to allow remote control of system reboot, power
809 off, halt, and suspend.
810
805endif # SPI_SLAVE 811endif # SPI_SLAVE
806 812
807endif # SPI 813endif # SPI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index fb078693dbe4..1d7923e8c63b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -108,3 +108,4 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
108 108
109# SPI slave protocol handlers 109# SPI slave protocol handlers
110obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o 110obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
111obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c
new file mode 100644
index 000000000000..c0257e937995
--- /dev/null
+++ b/drivers/spi/spi-slave-system-control.c
@@ -0,0 +1,154 @@
1/*
2 * SPI slave handler controlling system state
3 *
4 * This SPI slave handler allows remote control of system reboot, power off,
5 * halt, and suspend.
6 *
7 * Copyright (C) 2016-2017 Glider bvba
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file "COPYING" in the main directory of this archive
11 * for more details.
12 *
13 * Usage (assuming /dev/spidev2.0 corresponds to the SPI master on the remote
14 * system):
15 *
16 * # reboot='\x7c\x50'
17 * # poweroff='\x71\x3f'
18 * # halt='\x38\x76'
19 * # suspend='\x1b\x1b'
20 * # spidev_test -D /dev/spidev2.0 -p $suspend # or $reboot, $poweroff, $halt
21 */
22
23#include <linux/completion.h>
24#include <linux/module.h>
25#include <linux/reboot.h>
26#include <linux/suspend.h>
27#include <linux/spi/spi.h>
28
29/*
30 * The numbers are chosen to display something human-readable on two 7-segment
31 * displays connected to two 74HC595 shift registers
32 */
33#define CMD_REBOOT 0x7c50 /* rb */
34#define CMD_POWEROFF 0x713f /* OF */
35#define CMD_HALT 0x3876 /* HL */
36#define CMD_SUSPEND 0x1b1b /* ZZ */
37
38struct spi_slave_system_control_priv {
39 struct spi_device *spi;
40 struct completion finished;
41 struct spi_transfer xfer;
42 struct spi_message msg;
43 __be16 cmd;
44};
45
46static
47int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv);
48
49static void spi_slave_system_control_complete(void *arg)
50{
51 struct spi_slave_system_control_priv *priv = arg;
52 u16 cmd;
53 int ret;
54
55 if (priv->msg.status)
56 goto terminate;
57
58 cmd = be16_to_cpu(priv->cmd);
59 switch (cmd) {
60 case CMD_REBOOT:
61 dev_info(&priv->spi->dev, "Rebooting system...\n");
62 kernel_restart(NULL);
63
64 case CMD_POWEROFF:
65 dev_info(&priv->spi->dev, "Powering off system...\n");
66 kernel_power_off();
67 break;
68
69 case CMD_HALT:
70 dev_info(&priv->spi->dev, "Halting system...\n");
71 kernel_halt();
72 break;
73
74 case CMD_SUSPEND:
75 dev_info(&priv->spi->dev, "Suspending system...\n");
76 pm_suspend(PM_SUSPEND_MEM);
77 break;
78
79 default:
80 dev_warn(&priv->spi->dev, "Unknown command 0x%x\n", cmd);
81 break;
82 }
83
84 ret = spi_slave_system_control_submit(priv);
85 if (ret)
86 goto terminate;
87
88 return;
89
90terminate:
91 dev_info(&priv->spi->dev, "Terminating\n");
92 complete(&priv->finished);
93}
94
95static
96int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv)
97{
98 int ret;
99
100 spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
101
102 priv->msg.complete = spi_slave_system_control_complete;
103 priv->msg.context = priv;
104
105 ret = spi_async(priv->spi, &priv->msg);
106 if (ret)
107 dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
108
109 return ret;
110}
111
112static int spi_slave_system_control_probe(struct spi_device *spi)
113{
114 struct spi_slave_system_control_priv *priv;
115 int ret;
116
117 priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
118 if (!priv)
119 return -ENOMEM;
120
121 priv->spi = spi;
122 init_completion(&priv->finished);
123 priv->xfer.rx_buf = &priv->cmd;
124 priv->xfer.len = sizeof(priv->cmd);
125
126 ret = spi_slave_system_control_submit(priv);
127 if (ret)
128 return ret;
129
130 spi_set_drvdata(spi, priv);
131 return 0;
132}
133
134static int spi_slave_system_control_remove(struct spi_device *spi)
135{
136 struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi);
137
138 spi_slave_abort(spi);
139 wait_for_completion(&priv->finished);
140 return 0;
141}
142
143static struct spi_driver spi_slave_system_control_driver = {
144 .driver = {
145 .name = "spi-slave-system-control",
146 },
147 .probe = spi_slave_system_control_probe,
148 .remove = spi_slave_system_control_remove,
149};
150module_spi_driver(spi_slave_system_control_driver);
151
152MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
153MODULE_DESCRIPTION("SPI slave handler controlling system state");
154MODULE_LICENSE("GPL v2");