linux/drivers/spi/spi-slave-system-control.c
<<
>>
Prefs
   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                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
  91terminate:
  92        dev_info(&priv->spi->dev, "Terminating\n");
  93        complete(&priv->finished);
  94}
  95
  96static
  97int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv)
  98{
  99        int ret;
 100
 101        spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
 102
 103        priv->msg.complete = spi_slave_system_control_complete;
 104        priv->msg.context = priv;
 105
 106        ret = spi_async(priv->spi, &priv->msg);
 107        if (ret)
 108                dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
 109
 110        return ret;
 111}
 112
 113static 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(&spi->dev, sizeof(*priv), GFP_KERNEL);
 119        if (!priv)
 120                return -ENOMEM;
 121
 122        priv->spi = spi;
 123        init_completion(&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, priv);
 132        return 0;
 133}
 134
 135static int 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        return 0;
 142}
 143
 144static struct spi_driver spi_slave_system_control_driver = {
 145        .driver = {
 146                .name   = "spi-slave-system-control",
 147        },
 148        .probe          = spi_slave_system_control_probe,
 149        .remove         = spi_slave_system_control_remove,
 150};
 151module_spi_driver(spi_slave_system_control_driver);
 152
 153MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
 154MODULE_DESCRIPTION("SPI slave handler controlling system state");
 155MODULE_LICENSE("GPL v2");
 156