0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
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
0031
0032
0033 #define CMD_REBOOT 0x7c50
0034 #define CMD_POWEROFF 0x713f
0035 #define CMD_HALT 0x3876
0036 #define CMD_SUSPEND 0x1b1b
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");