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 | break; |
64 | |
65 | case CMD_POWEROFF: |
66 | dev_info(&priv->spi->dev, "Powering off system...\n" ); |
67 | kernel_power_off(); |
68 | break; |
69 | |
70 | case CMD_HALT: |
71 | dev_info(&priv->spi->dev, "Halting system...\n" ); |
72 | kernel_halt(); |
73 | break; |
74 | |
75 | case CMD_SUSPEND: |
76 | dev_info(&priv->spi->dev, "Suspending system...\n" ); |
77 | pm_suspend(PM_SUSPEND_MEM); |
78 | break; |
79 | |
80 | default: |
81 | dev_warn(&priv->spi->dev, "Unknown command 0x%x\n" , cmd); |
82 | break; |
83 | } |
84 | |
85 | ret = spi_slave_system_control_submit(priv); |
86 | if (ret) |
87 | goto terminate; |
88 | |
89 | return; |
90 | |
91 | terminate: |
92 | dev_info(&priv->spi->dev, "Terminating\n" ); |
93 | complete(&priv->finished); |
94 | } |
95 | |
96 | static |
97 | int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv) |
98 | { |
99 | int ret; |
100 | |
101 | spi_message_init_with_transfers(m: &priv->msg, xfers: &priv->xfer, num_xfers: 1); |
102 | |
103 | priv->msg.complete = spi_slave_system_control_complete; |
104 | priv->msg.context = priv; |
105 | |
106 | ret = spi_async(spi: priv->spi, message: &priv->msg); |
107 | if (ret) |
108 | dev_err(&priv->spi->dev, "spi_async() failed %d\n" , ret); |
109 | |
110 | return ret; |
111 | } |
112 | |
113 | static int spi_slave_system_control_probe(struct spi_device *spi) |
114 | { |
115 | struct spi_slave_system_control_priv *priv; |
116 | int ret; |
117 | |
118 | priv = devm_kzalloc(dev: &spi->dev, size: sizeof(*priv), GFP_KERNEL); |
119 | if (!priv) |
120 | return -ENOMEM; |
121 | |
122 | priv->spi = spi; |
123 | init_completion(x: &priv->finished); |
124 | priv->xfer.rx_buf = &priv->cmd; |
125 | priv->xfer.len = sizeof(priv->cmd); |
126 | |
127 | ret = spi_slave_system_control_submit(priv); |
128 | if (ret) |
129 | return ret; |
130 | |
131 | spi_set_drvdata(spi, data: priv); |
132 | return 0; |
133 | } |
134 | |
135 | static void spi_slave_system_control_remove(struct spi_device *spi) |
136 | { |
137 | struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi); |
138 | |
139 | spi_slave_abort(spi); |
140 | wait_for_completion(&priv->finished); |
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" ); |
155 | |