Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Geert Uytterhoeven and committed by
Mark Brown
ce70e06c 29f9ffa0

+161
+6
drivers/spi/Kconfig
··· 802 802 SPI slave handler responding with the time of reception of the last 803 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 811 endif # SPI_SLAVE 806 812 807 813 endif # SPI
+1
drivers/spi/Makefile
··· 108 108 109 109 # SPI slave protocol handlers 110 110 obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o 111 + obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o
+154
drivers/spi/spi-slave-system-control.c
··· 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");