Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * SPI slave handler controlling system state
0003  *
0004  * This SPI slave handler allows remote control of system reboot, power off,
0005  * halt, and suspend.
0006  *
0007  * Copyright (C) 2016-2017 Glider bvba
0008  *
0009  * This file is subject to the terms and conditions of the GNU General Public
0010  * License.  See the file "COPYING" in the main directory of this archive
0011  * for more details.
0012  *
0013  * Usage (assuming /dev/spidev2.0 corresponds to the SPI master on the remote
0014  * system):
0015  *
0016  *   # reboot='\x7c\x50'
0017  *   # poweroff='\x71\x3f'
0018  *   # halt='\x38\x76'
0019  *   # suspend='\x1b\x1b'
0020  *   # spidev_test -D /dev/spidev2.0 -p $suspend # or $reboot, $poweroff, $halt
0021  */
0022 
0023 #include <linux/completion.h>
0024 #include <linux/module.h>
0025 #include <linux/reboot.h>
0026 #include <linux/suspend.h>
0027 #include <linux/spi/spi.h>
0028 
0029 /*
0030  * The numbers are chosen to display something human-readable on two 7-segment
0031  * displays connected to two 74HC595 shift registers
0032  */
0033 #define CMD_REBOOT  0x7c50  /* rb */
0034 #define CMD_POWEROFF    0x713f  /* OF */
0035 #define CMD_HALT    0x3876  /* HL */
0036 #define CMD_SUSPEND 0x1b1b  /* ZZ */
0037 
0038 struct spi_slave_system_control_priv {
0039     struct spi_device *spi;
0040     struct completion finished;
0041     struct spi_transfer xfer;
0042     struct spi_message msg;
0043     __be16 cmd;
0044 };
0045 
0046 static
0047 int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv);
0048 
0049 static void spi_slave_system_control_complete(void *arg)
0050 {
0051     struct spi_slave_system_control_priv *priv = arg;
0052     u16 cmd;
0053     int ret;
0054 
0055     if (priv->msg.status)
0056         goto terminate;
0057 
0058     cmd = be16_to_cpu(priv->cmd);
0059     switch (cmd) {
0060     case CMD_REBOOT:
0061         dev_info(&priv->spi->dev, "Rebooting system...\n");
0062         kernel_restart(NULL);
0063         break;
0064 
0065     case CMD_POWEROFF:
0066         dev_info(&priv->spi->dev, "Powering off system...\n");
0067         kernel_power_off();
0068         break;
0069 
0070     case CMD_HALT:
0071         dev_info(&priv->spi->dev, "Halting system...\n");
0072         kernel_halt();
0073         break;
0074 
0075     case CMD_SUSPEND:
0076         dev_info(&priv->spi->dev, "Suspending system...\n");
0077         pm_suspend(PM_SUSPEND_MEM);
0078         break;
0079 
0080     default:
0081         dev_warn(&priv->spi->dev, "Unknown command 0x%x\n", cmd);
0082         break;
0083     }
0084 
0085     ret = spi_slave_system_control_submit(priv);
0086     if (ret)
0087         goto terminate;
0088 
0089     return;
0090 
0091 terminate:
0092     dev_info(&priv->spi->dev, "Terminating\n");
0093     complete(&priv->finished);
0094 }
0095 
0096 static
0097 int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv)
0098 {
0099     int ret;
0100 
0101     spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
0102 
0103     priv->msg.complete = spi_slave_system_control_complete;
0104     priv->msg.context = priv;
0105 
0106     ret = spi_async(priv->spi, &priv->msg);
0107     if (ret)
0108         dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
0109 
0110     return ret;
0111 }
0112 
0113 static int spi_slave_system_control_probe(struct spi_device *spi)
0114 {
0115     struct spi_slave_system_control_priv *priv;
0116     int ret;
0117 
0118     priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
0119     if (!priv)
0120         return -ENOMEM;
0121 
0122     priv->spi = spi;
0123     init_completion(&priv->finished);
0124     priv->xfer.rx_buf = &priv->cmd;
0125     priv->xfer.len = sizeof(priv->cmd);
0126 
0127     ret = spi_slave_system_control_submit(priv);
0128     if (ret)
0129         return ret;
0130 
0131     spi_set_drvdata(spi, priv);
0132     return 0;
0133 }
0134 
0135 static void spi_slave_system_control_remove(struct spi_device *spi)
0136 {
0137     struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi);
0138 
0139     spi_slave_abort(spi);
0140     wait_for_completion(&priv->finished);
0141 }
0142 
0143 static struct spi_driver spi_slave_system_control_driver = {
0144     .driver = {
0145         .name   = "spi-slave-system-control",
0146     },
0147     .probe      = spi_slave_system_control_probe,
0148     .remove     = spi_slave_system_control_remove,
0149 };
0150 module_spi_driver(spi_slave_system_control_driver);
0151 
0152 MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
0153 MODULE_DESCRIPTION("SPI slave handler controlling system state");
0154 MODULE_LICENSE("GPL v2");