Message ID | 1495458705-11692-7-git-send-email-geert+renesas@glider.be (mailing list archive) |
---|---|
State | Accepted |
Commit | ce70e06c093a9609377e93ee20e7c528e156af14 |
Headers | show |
On Mon, May 22, 2017 at 4:11 PM, Geert Uytterhoeven <geert+renesas@glider.be> wrote: > Add an example SPI slave handler to allow remote control of system > reboot, power off, halt, and suspend. > FWIW: Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> > Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> > --- > v5: > - Add usage documentation to file header, > - Use network byte order for commands, to match the "-p" parameter of > spidev_test, > - Replace pr_*() by dev_*(), stop printing __func__, > - Remove spi_setup() call to configure 8 bits per word, as that's the > default, > > v3, v4: > - No changes, > > v2: > - Use spi_async() instead of spi_read(), > - Submit the next transfer from the previous transfer's completion > callback, removing the need for a thread, > - Let .remove() call spi_slave_abort() to cancel the current ongoing > transfer, and wait for the completion to terminate, > - Remove FIXME about hanging kthread_stop(), > - Fix copy-and-pasted module description. > --- > drivers/spi/Kconfig | 6 ++ > drivers/spi/Makefile | 1 + > drivers/spi/spi-slave-system-control.c | 154 +++++++++++++++++++++++++++++++++ > 3 files changed, 161 insertions(+) > create mode 100644 drivers/spi/spi-slave-system-control.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 9972ee2a8454a2fc..82cd818aa06293f5 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -803,6 +803,12 @@ config SPI_SLAVE_TIME > SPI slave handler responding with the time of reception of the last > SPI message. > > +config SPI_SLAVE_SYSTEM_CONTROL > + tristate "SPI slave handler controlling system state" > + help > + SPI slave handler to allow remote control of system reboot, power > + off, halt, and suspend. > + > endif # SPI_SLAVE > > endif # SPI > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index fb078693dbe40da4..1d7923e8c63bc22b 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -108,3 +108,4 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o > > # SPI slave protocol handlers > obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o > +obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o > diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c > new file mode 100644 > index 0000000000000000..c0257e937995ec53 > --- /dev/null > +++ b/drivers/spi/spi-slave-system-control.c > @@ -0,0 +1,154 @@ > +/* > + * SPI slave handler controlling system state > + * > + * This SPI slave handler allows remote control of system reboot, power off, > + * halt, and suspend. > + * > + * Copyright (C) 2016-2017 Glider bvba > + * > + * This file is subject to the terms and conditions of the GNU General Public > + * License. See the file "COPYING" in the main directory of this archive > + * for more details. > + * > + * Usage (assuming /dev/spidev2.0 corresponds to the SPI master on the remote > + * system): > + * > + * # reboot='\x7c\x50' > + * # poweroff='\x71\x3f' > + * # halt='\x38\x76' > + * # suspend='\x1b\x1b' > + * # spidev_test -D /dev/spidev2.0 -p $suspend # or $reboot, $poweroff, $halt > + */ > + > +#include <linux/completion.h> > +#include <linux/module.h> > +#include <linux/reboot.h> > +#include <linux/suspend.h> > +#include <linux/spi/spi.h> > + > +/* > + * The numbers are chosen to display something human-readable on two 7-segment > + * displays connected to two 74HC595 shift registers > + */ > +#define CMD_REBOOT 0x7c50 /* rb */ > +#define CMD_POWEROFF 0x713f /* OF */ > +#define CMD_HALT 0x3876 /* HL */ > +#define CMD_SUSPEND 0x1b1b /* ZZ */ > + > +struct spi_slave_system_control_priv { > + struct spi_device *spi; > + struct completion finished; > + struct spi_transfer xfer; > + struct spi_message msg; > + __be16 cmd; > +}; > + > +static > +int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv); > + > +static void spi_slave_system_control_complete(void *arg) > +{ > + struct spi_slave_system_control_priv *priv = arg; > + u16 cmd; > + int ret; > + > + if (priv->msg.status) > + goto terminate; > + > + cmd = be16_to_cpu(priv->cmd); > + switch (cmd) { > + case CMD_REBOOT: > + dev_info(&priv->spi->dev, "Rebooting system...\n"); > + kernel_restart(NULL); > + > + case CMD_POWEROFF: > + dev_info(&priv->spi->dev, "Powering off system...\n"); > + kernel_power_off(); > + break; > + > + case CMD_HALT: > + dev_info(&priv->spi->dev, "Halting system...\n"); > + kernel_halt(); > + break; > + > + case CMD_SUSPEND: > + dev_info(&priv->spi->dev, "Suspending system...\n"); > + pm_suspend(PM_SUSPEND_MEM); > + break; > + > + default: > + dev_warn(&priv->spi->dev, "Unknown command 0x%x\n", cmd); > + break; > + } > + > + ret = spi_slave_system_control_submit(priv); > + if (ret) > + goto terminate; > + > + return; > + > +terminate: > + dev_info(&priv->spi->dev, "Terminating\n"); > + complete(&priv->finished); > +} > + > +static > +int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv) > +{ > + int ret; > + > + spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1); > + > + priv->msg.complete = spi_slave_system_control_complete; > + priv->msg.context = priv; > + > + ret = spi_async(priv->spi, &priv->msg); > + if (ret) > + dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret); > + > + return ret; > +} > + > +static int spi_slave_system_control_probe(struct spi_device *spi) > +{ > + struct spi_slave_system_control_priv *priv; > + int ret; > + > + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->spi = spi; > + init_completion(&priv->finished); > + priv->xfer.rx_buf = &priv->cmd; > + priv->xfer.len = sizeof(priv->cmd); > + > + ret = spi_slave_system_control_submit(priv); > + if (ret) > + return ret; > + > + spi_set_drvdata(spi, priv); > + return 0; > +} > + > +static int spi_slave_system_control_remove(struct spi_device *spi) > +{ > + struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi); > + > + spi_slave_abort(spi); > + wait_for_completion(&priv->finished); > + return 0; > +} > + > +static struct spi_driver spi_slave_system_control_driver = { > + .driver = { > + .name = "spi-slave-system-control", > + }, > + .probe = spi_slave_system_control_probe, > + .remove = spi_slave_system_control_remove, > +}; > +module_spi_driver(spi_slave_system_control_driver); > + > +MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); > +MODULE_DESCRIPTION("SPI slave handler controlling system state"); > +MODULE_LICENSE("GPL v2"); > -- > 2.7.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-spi" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 9972ee2a8454a2fc..82cd818aa06293f5 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -803,6 +803,12 @@ config SPI_SLAVE_TIME SPI slave handler responding with the time of reception of the last SPI message. +config SPI_SLAVE_SYSTEM_CONTROL + tristate "SPI slave handler controlling system state" + help + SPI slave handler to allow remote control of system reboot, power + off, halt, and suspend. + endif # SPI_SLAVE endif # SPI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index fb078693dbe40da4..1d7923e8c63bc22b 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -108,3 +108,4 @@ obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o # SPI slave protocol handlers obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o +obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o diff --git a/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c new file mode 100644 index 0000000000000000..c0257e937995ec53 --- /dev/null +++ b/drivers/spi/spi-slave-system-control.c @@ -0,0 +1,154 @@ +/* + * SPI slave handler controlling system state + * + * This SPI slave handler allows remote control of system reboot, power off, + * halt, and suspend. + * + * Copyright (C) 2016-2017 Glider bvba + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Usage (assuming /dev/spidev2.0 corresponds to the SPI master on the remote + * system): + * + * # reboot='\x7c\x50' + * # poweroff='\x71\x3f' + * # halt='\x38\x76' + * # suspend='\x1b\x1b' + * # spidev_test -D /dev/spidev2.0 -p $suspend # or $reboot, $poweroff, $halt + */ + +#include <linux/completion.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/suspend.h> +#include <linux/spi/spi.h> + +/* + * The numbers are chosen to display something human-readable on two 7-segment + * displays connected to two 74HC595 shift registers + */ +#define CMD_REBOOT 0x7c50 /* rb */ +#define CMD_POWEROFF 0x713f /* OF */ +#define CMD_HALT 0x3876 /* HL */ +#define CMD_SUSPEND 0x1b1b /* ZZ */ + +struct spi_slave_system_control_priv { + struct spi_device *spi; + struct completion finished; + struct spi_transfer xfer; + struct spi_message msg; + __be16 cmd; +}; + +static +int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv); + +static void spi_slave_system_control_complete(void *arg) +{ + struct spi_slave_system_control_priv *priv = arg; + u16 cmd; + int ret; + + if (priv->msg.status) + goto terminate; + + cmd = be16_to_cpu(priv->cmd); + switch (cmd) { + case CMD_REBOOT: + dev_info(&priv->spi->dev, "Rebooting system...\n"); + kernel_restart(NULL); + + case CMD_POWEROFF: + dev_info(&priv->spi->dev, "Powering off system...\n"); + kernel_power_off(); + break; + + case CMD_HALT: + dev_info(&priv->spi->dev, "Halting system...\n"); + kernel_halt(); + break; + + case CMD_SUSPEND: + dev_info(&priv->spi->dev, "Suspending system...\n"); + pm_suspend(PM_SUSPEND_MEM); + break; + + default: + dev_warn(&priv->spi->dev, "Unknown command 0x%x\n", cmd); + break; + } + + ret = spi_slave_system_control_submit(priv); + if (ret) + goto terminate; + + return; + +terminate: + dev_info(&priv->spi->dev, "Terminating\n"); + complete(&priv->finished); +} + +static +int spi_slave_system_control_submit(struct spi_slave_system_control_priv *priv) +{ + int ret; + + spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1); + + priv->msg.complete = spi_slave_system_control_complete; + priv->msg.context = priv; + + ret = spi_async(priv->spi, &priv->msg); + if (ret) + dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret); + + return ret; +} + +static int spi_slave_system_control_probe(struct spi_device *spi) +{ + struct spi_slave_system_control_priv *priv; + int ret; + + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->spi = spi; + init_completion(&priv->finished); + priv->xfer.rx_buf = &priv->cmd; + priv->xfer.len = sizeof(priv->cmd); + + ret = spi_slave_system_control_submit(priv); + if (ret) + return ret; + + spi_set_drvdata(spi, priv); + return 0; +} + +static int spi_slave_system_control_remove(struct spi_device *spi) +{ + struct spi_slave_system_control_priv *priv = spi_get_drvdata(spi); + + spi_slave_abort(spi); + wait_for_completion(&priv->finished); + return 0; +} + +static struct spi_driver spi_slave_system_control_driver = { + .driver = { + .name = "spi-slave-system-control", + }, + .probe = spi_slave_system_control_probe, + .remove = spi_slave_system_control_remove, +}; +module_spi_driver(spi_slave_system_control_driver); + +MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>"); +MODULE_DESCRIPTION("SPI slave handler controlling system state"); +MODULE_LICENSE("GPL v2");
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> --- v5: - Add usage documentation to file header, - Use network byte order for commands, to match the "-p" parameter of spidev_test, - Replace pr_*() by dev_*(), stop printing __func__, - Remove spi_setup() call to configure 8 bits per word, as that's the default, v3, v4: - No changes, v2: - Use spi_async() instead of spi_read(), - Submit the next transfer from the previous transfer's completion callback, removing the need for a thread, - Let .remove() call spi_slave_abort() to cancel the current ongoing transfer, and wait for the completion to terminate, - Remove FIXME about hanging kthread_stop(), - Fix copy-and-pasted module description. --- drivers/spi/Kconfig | 6 ++ drivers/spi/Makefile | 1 + drivers/spi/spi-slave-system-control.c | 154 +++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 drivers/spi/spi-slave-system-control.c