diff options
author | Geert Uytterhoeven <geert+renesas@glider.be> | 2017-05-22 09:11:45 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-05-26 08:12:19 -0400 |
commit | ce70e06c093a9609377e93ee20e7c528e156af14 (patch) | |
tree | 1d848c5d6a61ce975bd201c24622e2779c62309b | |
parent | 29f9ffa0e1f9a17c866c04a01acfc9976d78f29a (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/Kconfig | 6 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-slave-system-control.c | 154 |
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 | ||
805 | config 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 | |||
805 | endif # SPI_SLAVE | 811 | endif # SPI_SLAVE |
806 | 812 | ||
807 | endif # SPI | 813 | endif # 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 |
110 | obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o | 110 | obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o |
111 | obj-$(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 | |||
38 | struct 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 | |||
46 | static | ||
47 | int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv); | ||
48 | |||
49 | static 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 | |||
90 | terminate: | ||
91 | dev_info(&priv->spi->dev, "Terminating\n"); | ||
92 | complete(&priv->finished); | ||
93 | } | ||
94 | |||
95 | static | ||
96 | int 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 | |||
112 | static 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 | |||
134 | static 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 | |||
143 | static 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 | }; | ||
150 | module_spi_driver(spi_slave_system_control_driver); | ||
151 | |||
152 | MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); | ||
153 | MODULE_DESCRIPTION("SPI slave handler controlling system state"); | ||
154 | MODULE_LICENSE("GPL v2"); | ||