Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1635936 > unrolled thread
| Started by | Geert Uytterhoeven <geert+renesas@glider.be> |
|---|---|
| First post | 2017-05-04 19:50 +0200 |
| Last post | 2017-05-04 19:50 +0200 |
| Articles | 5 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/6] spi: Add slave mode support Geert Uytterhoeven <geert+renesas@glider.be> - 2017-05-04 19:50 +0200
[PATCH v3 4/6] spi: sh-msiof: Add slave mode support Geert Uytterhoeven <geert+renesas@glider.be> - 2017-05-04 19:50 +0200
Re: [PATCH v3 4/6] spi: sh-msiof: Add slave mode support Geert Uytterhoeven <geert@linux-m68k.org> - 2017-05-05 11:50 +0200
[PATCH v3 6/6] spi: slave: Add SPI slave handler controlling system state Geert Uytterhoeven <geert+renesas@glider.be> - 2017-05-04 19:50 +0200
[PATCH v3 5/6] spi: slave: Add SPI slave handler reporting uptime at previous message Geert Uytterhoeven <geert+renesas@glider.be> - 2017-05-04 19:50 +0200
| From | Geert Uytterhoeven <geert+renesas@glider.be> |
|---|---|
| Date | 2017-05-04 19:50 +0200 |
| Subject | [PATCH v3 0/6] spi: Add slave mode support |
| Message-ID | <tDt33-6JH-5@gated-at.bofh.it> |
Hi all,
This patch series adds support for SPI slave controllers to the Linux
SPI subsystem, including:
- DT binding updates for SPI slave support,
- Core support for SPI slave controllers,
- SPI slave support for the Renesas MSIOF device driver (thanks to
Nakamura-san for the initial implementation in the R-Car BSP!),
- Sample SPI slave handlers.
Due to the nature of SPI slave (simultaneous transmit and receive, while
everything runs at the pace of the master), it has hard real-time
requirements: once an SPI transfer is started by the SPI master, a
software SPI slave must have prepared all data to be sent back to the
SPI master. Hence without additional hardware support, an SPI slave
response can never be a reply to a command being simultaneously
transmitted, and SPI slave replies must be received by the SPI master in
a subsequent SPI transfer.
Examples of possible use cases:
- Receiving streams of data in fixed-size messages (e.g. from a
tuner),
- Receiving and transmitting fixed-size messages of data (e.g. network
frames),
- Sending commands, and querying for responses,
- ...
Binding an SPI slave handler to the SPI slave device represented by an
SPI slave controller can either be done from DT, or through sysfs.
The latter, which also allows unregistering, is done through a sysfs
virtual file named "slave", cfr. Documentation/spi/spi-summary.
Originally I wanted to implement a simple SPI slave handler that could
interface with an existing Linux SPI slave driver, cfr. Wolfram Sang's
I2C slave mode EEPROM simulator for the i2c subsystem.
Unfortunately I couldn't find any existing driver using an SPI slave
protocol that fulfills the above requirements. The Nordic Semiconductor
nRF8001 BLE controller seems to use a suitable protocol, but I couldn't
find a Linux driver for it. Hence I created two sample SPI slave
protocols and drivers myself:
1. "spi-slave-time" responds with the system uptime at the time of
reception of the last SPI message, which can be used by an external
microcontroller as a dead man's switch.
2. "spi-slave-system-control" allows remote control of system reboot,
power off, halt, and suspend.
For some use cases, using spidev from user space may be a more appropriate
solution than an in-kernel SPI protocol handler, and this is fully
supported.
From the point of view of an SPI slave protocol handler, an SPI slave
controller looks almost like an ordinary SPI master controller. The only
exception is that a transfer request will block on the remote SPI
master, and may be cancelled using spi_slave_abort().
Hence "struct spi_master" has become a misnomer. I'll send an RFC
follow-up patch to fix that.
For now, the MSIOF SPI slave driver only supports the transmission of
messages with a size that is known in advance (the hardware can provide
an interrupt when CS is deasserted before, though).
I.e. when the SPI master sends a shorter message, the slave won't
receive it. When the SPI master sends a longer message, the slave will
receive the first part, and the rest will remain in the FIFO.
Handshaking (5-pin SPI, RDY-signal) is optional, and not yet
implemented. An RDY-signal may be used for one or both of:
1. The SPI slave asserts RDY when it has data available, and wants to
be queried by the SPI master.
-> This can be handled on top, in the SPI slave protocol handler,
using a GPIO.
2. After the SPI master has asserted CS, the SPI slave asserts RDY
when it is ready to accept the transfer.
-> This may need hardware support in the SPI slave controller,
or dynamic GPIO vs. CS pinmuxing.
Changes compared to v2 (highlights only, see individual patches for
more details):
- In SPI slave mode, represent the (single) slave device again as a
child of the controller node, which is now optional, and must be
named "slave" if present,
- Introduce a separate spi_alloc_slave() function,
- Replace the SPI_CONTROLLER_IS_SLAVE flag in spi_master.flags by a
bool in spi_master,
- Fix cancellation in the spi-sh-msiof driver,
- Drop "spi: core: Extract of_spi_parse_dt()", which was applied,
Changes compared to v1 (highlights only, see individual patches for
more details):
- Do not create a child node in SPI slave mode. Instead, add an
"spi-slave" property, and put the mode properties in the controller
node.
- Attach SPI slave controllers to a new "spi_slave" device class,
- Use a "slave" virtual file in sysfs to (un)register the (single)
slave device for an SPI slave controller, incl. specifying the slave
protocol handler,
- Add cancellation support using spi_master.slave_abort() and
spi_slave_abort(),
- Please see the individual patches for more detailed changelog
information.
Dependencies:
- Today's spi/for-next,
- "[PATCH] spi: core: Fix devm_spi_register_master() function name in
kerneldoc",
- "[PATCH] spi: core: Replace S_IRUGO permissions by 0444".
For your convenience, I've pushed this series and its dependencies to
the topic/spi-slave-v3 branch of the git repository at
https://git.kernel.org/cgit/linux/kernel/git/geert/renesas-drivers.git
Full test information is also available on the eLinux wiki
(http://elinux.org/Tests:MSIOF-SPI-Slave).
For testing, device tree overlays enabling SPI master and slave
controllers on an expansion I/O connector on r8a7791/koelsch are
available in the topic/renesas-overlays branch of my renesas-drivers git
repository. Please see http://elinux.org/R-Car/DT-Overlays for more
information about using these overlays.
Test wiring on r8a7791/koelsch, between MSIOF1 and MSIOF2 on EXIO
connector A:
- Connect pin 48 (MSIOF1 CS#) to pin 63 (MSIOF2 CS#),
- Connect pin 46 (MSIOF1 SCK) to pin 61 (MSIOF2 SCK),
- Connect pin 54 (MSIOF1 TX/MOSI) to pin 70 (MSIOF2 RX/MOSI),
- Connect pin 56 (MSIOF1 RX/MISO) to pin 68 (MSIOF2 TX/MISO).
Preparation for all examples below:
# overlay add a-msiof1-spidev # buggy DT: spidev listed directly in DT
# overlay add a-msiof2-slave
Example 1:
# echo spi-slave-time > /sys/class/spi_slave/spi3/slave
# spidev_test -D /dev/spidev2.0 -p dummy-8B
spi mode: 0x0
bits per word: 8
max speed: 500000 Hz (500 KHz)
RX | 00 00 04 6D 00 09 5B BB __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ | ...m..[�
^^^^^ ^^^^^^^^
seconds microseconds
Example 2:
# echo spi-slave-system-control > /sys/class/spi_slave/spi3/slave
# reboot='\x7c\x50'
# poweroff='\x71\x3f'
# halt='\x38\x76'
# suspend='\x1b\x1b'
# spidev_test -D /dev/spidev2.0 -p $suspend # or $reboot, $poweroff, $halt
Example 3:
# echo spidev > /sys/class/spi_slave/spi3/slave
# spidev_test -D /dev/spidev3.0 -p slave-hello-to-master &
# spidev_test -D /dev/spidev2.0 -p master-hello-to-slave
Thanks!
Geert Uytterhoeven (5):
spi: Document DT bindings for SPI controllers in slave mode
spi: core: Add support for registering SPI slave controllers
spi: Document SPI slave controller support
spi: slave: Add SPI slave handler reporting uptime at previous message
spi: slave: Add SPI slave handler controlling system state
Hisashi Nakamura (1):
spi: sh-msiof: Add slave mode support
Documentation/devicetree/bindings/spi/sh-msiof.txt | 2 +
Documentation/devicetree/bindings/spi/spi-bus.txt | 76 +++++----
Documentation/spi/spi-summary | 27 +++-
drivers/spi/Kconfig | 26 ++-
drivers/spi/Makefile | 4 +
drivers/spi/spi-sh-msiof.c | 121 ++++++++++----
drivers/spi/spi-slave-system-control.c | 154 ++++++++++++++++++
drivers/spi/spi-slave-time.c | 127 +++++++++++++++
drivers/spi/spi.c | 179 ++++++++++++++++++---
include/linux/spi/sh_msiof.h | 6 +
include/linux/spi/spi.h | 33 +++-
11 files changed, 655 insertions(+), 100 deletions(-)
create mode 100644 drivers/spi/spi-slave-system-control.c
create mode 100644 drivers/spi/spi-slave-time.c
--
2.7.4
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
[toc] | [next] | [standalone]
| From | Geert Uytterhoeven <geert+renesas@glider.be> |
|---|---|
| Date | 2017-05-04 19:50 +0200 |
| Subject | [PATCH v3 4/6] spi: sh-msiof: Add slave mode support |
| Message-ID | <tDt34-6JH-27@gated-at.bofh.it> |
| In reply to | #1635936 |
From: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Add slave mode support to the MSIOF driver, in both PIO and DMA mode.
For now this only supports the transmission of messages with a size
that is known in advance.
Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
Signed-off-by: Hiromitsu Yamasaki <hiromitsu.yamasaki.ym@renesas.com>
[geert: Timeout handling cleanup, spi core integration, cancellation,
rewording]
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v3:
- Clear TIF_SIGPENDING when interrupted to fix cancellation,
- Extract sh_msiof_wait_for_completion(),
- Add #include <linux/sched/signal.h>,
- Convert to use spi_alloc_slave(),
v2:
- Document "spi-slave" property in DT bindings,
- Use spi_controller_is_slave() helper,
- Check for "spi-slave" property instead of "slave" child node,
- Replace SPI_MASTER_IS_SLAVE by SPI_CONTROLLER_IS_SLAVE,
- Implement cancellation.
---
Documentation/devicetree/bindings/spi/sh-msiof.txt | 2 +
drivers/spi/spi-sh-msiof.c | 121 +++++++++++++++------
include/linux/spi/sh_msiof.h | 6 +
3 files changed, 96 insertions(+), 33 deletions(-)
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt
index dc975064fa273c36..64ee489571c42f88 100644
--- a/Documentation/devicetree/bindings/spi/sh-msiof.txt
+++ b/Documentation/devicetree/bindings/spi/sh-msiof.txt
@@ -38,6 +38,8 @@ Optional properties:
specifiers, one for transmission, and one for
reception.
- dma-names : Must contain a list of two DMA names, "tx" and "rx".
+- spi-slave : Empty property indicating the SPI controller is used
+ in slave mode.
- renesas,dtdl : delay sync signal (setup) in transmit mode.
Must contain one of the following values:
0 (no bit delay)
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c
index 2ce15ca977828668..7c4e8c4f3a9bddfd 100644
--- a/drivers/spi/spi-sh-msiof.c
+++ b/drivers/spi/spi-sh-msiof.c
@@ -2,7 +2,8 @@
* SuperH MSIOF SPI Master Interface
*
* Copyright (c) 2009 Magnus Damm
- * Copyright (C) 2014 Glider bvba
+ * Copyright (C) 2014 Renesas Electronics Corporation
+ * Copyright (C) 2014-2017 Glider bvba
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -26,6 +27,7 @@
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/sched/signal.h>
#include <linux/sh_dma.h>
#include <linux/spi/sh_msiof.h>
@@ -33,7 +35,6 @@
#include <asm/unaligned.h>
-
struct sh_msiof_chipdata {
u16 tx_fifo_size;
u16 rx_fifo_size;
@@ -337,7 +338,10 @@ static void sh_msiof_spi_set_pin_regs(struct sh_msiof_spi_priv *p,
tmp |= !cs_high << MDR1_SYNCAC_SHIFT;
tmp |= lsb_first << MDR1_BITLSB_SHIFT;
tmp |= sh_msiof_spi_get_dtdl_and_syncdl(p);
- sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
+ if (spi_controller_is_slave(p->master))
+ sh_msiof_write(p, TMDR1, tmp | TMDR1_PCON);
+ else
+ sh_msiof_write(p, TMDR1, tmp | MDR1_TRMD | TMDR1_PCON);
if (p->master->flags & SPI_MASTER_MUST_TX) {
/* These bits are reserved if RX needs TX */
tmp &= ~0x0000ffff;
@@ -564,17 +568,19 @@ static int sh_msiof_prepare_message(struct spi_master *master,
static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
{
- int ret;
+ bool slave = spi_controller_is_slave(p->master);
+ int ret = 0;
/* setup clock and rx/tx signals */
- ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
+ if (!slave)
+ ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TSCKE);
if (rx_buf && !ret)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_RXE);
if (!ret)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TXE);
/* start by setting frame bit */
- if (!ret)
+ if (!ret && !slave)
ret = sh_msiof_modify_ctr_wait(p, 0, CTR_TFSE);
return ret;
@@ -582,20 +588,61 @@ static int sh_msiof_spi_start(struct sh_msiof_spi_priv *p, void *rx_buf)
static int sh_msiof_spi_stop(struct sh_msiof_spi_priv *p, void *rx_buf)
{
- int ret;
+ bool slave = spi_controller_is_slave(p->master);
+ int ret = 0;
/* shut down frame, rx/tx and clock signals */
- ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
+ if (!slave)
+ ret = sh_msiof_modify_ctr_wait(p, CTR_TFSE, 0);
if (!ret)
ret = sh_msiof_modify_ctr_wait(p, CTR_TXE, 0);
if (rx_buf && !ret)
ret = sh_msiof_modify_ctr_wait(p, CTR_RXE, 0);
- if (!ret)
+ if (!ret && !slave)
ret = sh_msiof_modify_ctr_wait(p, CTR_TSCKE, 0);
return ret;
}
+static int sh_msiof_slave_abort(struct spi_master *master)
+{
+ struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
+ unsigned long flags;
+
+ spin_lock_irqsave(&p->done.wait.lock, flags);
+ if (!p->done.done) {
+ wait_queue_t *curr, *next;
+
+ list_for_each_entry_safe(curr, next, &p->done.wait.task_list,
+ task_list) {
+ signal_wake_up(curr->private, 1);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&p->done.wait.lock, flags);
+ return 0;
+}
+
+static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p)
+{
+ if (spi_controller_is_slave(p->master)) {
+ int ret = wait_for_completion_interruptible(&p->done);
+
+ if (ret) {
+ dev_dbg(&p->pdev->dev, "interrupted\n");
+ clear_thread_flag(TIF_SIGPENDING);
+ return ret;
+ }
+ } else {
+ if (!wait_for_completion_timeout(&p->done, HZ)) {
+ dev_err(&p->pdev->dev, "timeout\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
void (*tx_fifo)(struct sh_msiof_spi_priv *,
const void *, int, int),
@@ -636,11 +683,9 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p,
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
- if (!wait_for_completion_timeout(&p->done, HZ)) {
- dev_err(&p->pdev->dev, "PIO timeout\n");
- ret = -ETIMEDOUT;
+ ret = sh_msiof_wait_for_completion(p);
+ if (ret)
goto stop_reset;
- }
/* read rx fifo */
if (rx_buf)
@@ -746,11 +791,9 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx,
}
/* wait for tx fifo to be emptied / rx fifo to be filled */
- if (!wait_for_completion_timeout(&p->done, HZ)) {
- dev_err(&p->pdev->dev, "DMA timeout\n");
- ret = -ETIMEDOUT;
+ ret = sh_msiof_wait_for_completion(p);
+ if (ret)
goto stop_reset;
- }
/* clear status bits */
sh_msiof_reset_str(p);
@@ -843,7 +886,8 @@ static int sh_msiof_transfer_one(struct spi_master *master,
int ret;
/* setup clocks (clock already enabled in chipselect()) */
- sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
+ if (!spi_controller_is_slave(p->master))
+ sh_msiof_spi_set_clk_regs(p, clk_get_rate(p->clk), t->speed_hz);
while (master->dma_tx && len > 15) {
/*
@@ -998,8 +1042,12 @@ static struct sh_msiof_spi_info *sh_msiof_spi_parse_dt(struct device *dev)
if (!info)
return NULL;
+ info->mode = of_property_read_bool(np, "spi-slave") ? MSIOF_SPI_SLAVE
+ : MSIOF_SPI_MASTER;
+
/* Parse the MSIOF properties */
- of_property_read_u32(np, "num-cs", &num_cs);
+ if (info->mode == MSIOF_SPI_MASTER)
+ of_property_read_u32(np, "num-cs", &num_cs);
of_property_read_u32(np, "renesas,tx-fifo-size",
&info->tx_fifo_override);
of_property_read_u32(np, "renesas,rx-fifo-size",
@@ -1159,34 +1207,40 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
struct spi_master *master;
const struct sh_msiof_chipdata *chipdata;
const struct of_device_id *of_id;
+ struct sh_msiof_spi_info *info;
struct sh_msiof_spi_priv *p;
int i;
int ret;
- master = spi_alloc_master(&pdev->dev, sizeof(struct sh_msiof_spi_priv));
- if (master == NULL)
- return -ENOMEM;
-
- p = spi_master_get_devdata(master);
-
- platform_set_drvdata(pdev, p);
- p->master = master;
-
of_id = of_match_device(sh_msiof_match, &pdev->dev);
if (of_id) {
chipdata = of_id->data;
- p->info = sh_msiof_spi_parse_dt(&pdev->dev);
+ info = sh_msiof_spi_parse_dt(&pdev->dev);
} else {
chipdata = (const void *)pdev->id_entry->driver_data;
- p->info = dev_get_platdata(&pdev->dev);
+ info = dev_get_platdata(&pdev->dev);
}
- if (!p->info) {
+ if (!info) {
dev_err(&pdev->dev, "failed to obtain device info\n");
- ret = -ENXIO;
- goto err1;
+ return -ENXIO;
}
+ if (info->mode == MSIOF_SPI_SLAVE)
+ master = spi_alloc_slave(&pdev->dev,
+ sizeof(struct sh_msiof_spi_priv));
+ else
+ master = spi_alloc_master(&pdev->dev,
+ sizeof(struct sh_msiof_spi_priv));
+ if (master == NULL)
+ return -ENOMEM;
+
+ p = spi_master_get_devdata(master);
+
+ platform_set_drvdata(pdev, p);
+ p->master = master;
+ p->info = info;
+
init_completion(&p->done);
p->clk = devm_clk_get(&pdev->dev, NULL);
@@ -1237,6 +1291,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
master->num_chipselect = p->info->num_chipselect;
master->setup = sh_msiof_spi_setup;
master->prepare_message = sh_msiof_prepare_message;
+ master->slave_abort = sh_msiof_slave_abort;
master->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
master->auto_runtime_pm = true;
master->transfer_one = sh_msiof_transfer_one;
diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h
index b087a85f5f72a351..f74b581f242f8c43 100644
--- a/include/linux/spi/sh_msiof.h
+++ b/include/linux/spi/sh_msiof.h
@@ -1,10 +1,16 @@
#ifndef __SPI_SH_MSIOF_H__
#define __SPI_SH_MSIOF_H__
+enum {
+ MSIOF_SPI_MASTER,
+ MSIOF_SPI_SLAVE,
+};
+
struct sh_msiof_spi_info {
int tx_fifo_override;
int rx_fifo_override;
u16 num_chipselect;
+ int mode;
unsigned int dma_tx_id;
unsigned int dma_rx_id;
u32 dtdl;
--
2.7.4
[toc] | [prev] | [next] | [standalone]
| From | Geert Uytterhoeven <geert@linux-m68k.org> |
|---|---|
| Date | 2017-05-05 11:50 +0200 |
| Subject | Re: [PATCH v3 4/6] spi: sh-msiof: Add slave mode support |
| Message-ID | <tDI26-8l5-19@gated-at.bofh.it> |
| In reply to | #1635937 |
On Thu, May 4, 2017 at 7:45 PM, Geert Uytterhoeven
<geert+renesas@glider.be> wrote:
> From: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
>
> Add slave mode support to the MSIOF driver, in both PIO and DMA mode.
>
> For now this only supports the transmission of messages with a size
> that is known in advance.
>
> Signed-off-by: Hisashi Nakamura <hisashi.nakamura.ak@renesas.com>
> Signed-off-by: Hiromitsu Yamasaki <hiromitsu.yamasaki.ym@renesas.com>
> [geert: Timeout handling cleanup, spi core integration, cancellation,
> rewording]
> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
> index 2ce15ca977828668..7c4e8c4f3a9bddfd 100644
> --- a/drivers/spi/spi-sh-msiof.c
> +++ b/drivers/spi/spi-sh-msiof.c
> +static int sh_msiof_slave_abort(struct spi_master *master)
> +{
> + struct sh_msiof_spi_priv *p = spi_master_get_devdata(master);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&p->done.wait.lock, flags);
> + if (!p->done.done) {
> + wait_queue_t *curr, *next;
> +
> + list_for_each_entry_safe(curr, next, &p->done.wait.task_list,
> + task_list) {
> + signal_wake_up(curr->private, 1);
0day reported a build failure in the modular case (thanks!):
ERROR: "signal_wake_up_state" [drivers/spi/spi-sh-msiof.ko] undefined!
signal_wake_up() is a static inline function calling signal_wake_up_state(),
but the latter is not exported to modules.
I'll bring it up with the scheduler people...
> + break;
> + }
> + }
> + spin_unlock_irqrestore(&p->done.wait.lock, flags);
> + return 0;
> +}
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
[toc] | [prev] | [next] | [standalone]
| From | Geert Uytterhoeven <geert+renesas@glider.be> |
|---|---|
| Date | 2017-05-04 19:50 +0200 |
| Subject | [PATCH v3 6/6] spi: slave: Add SPI slave handler controlling system state |
| Message-ID | <tDt34-6JH-23@gated-at.bofh.it> |
| In reply to | #1635936 |
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>
---
v3:
- 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 ade542c5bfd87e37..e6d9e329a3801d6d 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -802,6 +802,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..736dd59928cb3bc3
--- /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 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.
+ */
+
+#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 0x507c /* rb */
+#define CMD_POWEROFF 0x3f71 /* OF */
+#define CMD_HALT 0x7638 /* 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;
+ __le16 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 = le16_to_cpu(priv->cmd);
+ switch (cmd) {
+ case CMD_REBOOT:
+ pr_info("Rebooting system...\n");
+ kernel_restart(NULL);
+
+ case CMD_POWEROFF:
+ pr_info("Powering off system...\n");
+ kernel_power_off();
+ break;
+
+ case CMD_HALT:
+ pr_info("Halting system...\n");
+ kernel_halt();
+ break;
+
+ case CMD_SUSPEND:
+ pr_info("Suspending system...\n");
+ pm_suspend(PM_SUSPEND_MEM);
+ break;
+
+ default:
+ pr_warn("%s: Unknown command 0x%x\n", __func__, cmd);
+ break;
+ }
+
+ ret = spi_slave_system_control_submit(priv);
+ if (ret)
+ goto terminate;
+
+ return;
+
+terminate:
+ pr_info("%s: Terminating\n", __func__);
+ 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)
+ pr_err("%s: spi_async() failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int spi_slave_system_control_probe(struct spi_device *spi)
+{
+ struct spi_slave_system_control_priv *priv;
+ int ret;
+
+ /*
+ * bits_per_word cannot be configured in platform data
+ */
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return 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
[toc] | [prev] | [next] | [standalone]
| From | Geert Uytterhoeven <geert+renesas@glider.be> |
|---|---|
| Date | 2017-05-04 19:50 +0200 |
| Subject | [PATCH v3 5/6] spi: slave: Add SPI slave handler reporting uptime at previous message |
| Message-ID | <tDt34-6JH-25@gated-at.bofh.it> |
| In reply to | #1635936 |
Add an example SPI slave handler responding with the uptime at the time
of reception of the last SPI message.
This can be used by an external microcontroller as a dead man's switch.
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v3:
- Add #include <linux/sched/clock.h>,
v2:
- Resolve semantic differences in patch description, file header, and
module description,
- 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().
---
drivers/spi/Kconfig | 6 ++
drivers/spi/Makefile | 1 +
drivers/spi/spi-slave-time.c | 127 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 134 insertions(+)
create mode 100644 drivers/spi/spi-slave-time.c
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index df8ddec24b5d7e88..ade542c5bfd87e37 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -796,6 +796,12 @@ config SPI_SLAVE
if SPI_SLAVE
+config SPI_SLAVE_TIME
+ tristate "SPI slave handler reporting boot up time"
+ help
+ SPI slave handler responding with the time of reception of the last
+ SPI message.
+
endif # SPI_SLAVE
endif # SPI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index e50852c6fcb87d8b..fb078693dbe40da4 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -107,3 +107,4 @@ obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
# SPI slave protocol handlers
+obj-$(CONFIG_SPI_SLAVE_TIME) += spi-slave-time.o
diff --git a/drivers/spi/spi-slave-time.c b/drivers/spi/spi-slave-time.c
new file mode 100644
index 0000000000000000..c2940f3f18ecd22e
--- /dev/null
+++ b/drivers/spi/spi-slave-time.c
@@ -0,0 +1,127 @@
+/*
+ * SPI slave handler reporting uptime at reception of previous SPI message
+ *
+ * This SPI slave handler sends the time of reception of the last SPI message
+ * as two 32-bit unsigned integers in binary format and in network byte order,
+ * representing the number of seconds and fractional seconds (in microseconds)
+ * since boot up.
+ *
+ * Copyright (C) 2016 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.
+ */
+
+#include <linux/completion.h>
+#include <linux/module.h>
+#include <linux/sched/clock.h>
+#include <linux/spi/spi.h>
+
+
+struct spi_slave_time_priv {
+ struct spi_device *spi;
+ struct completion finished;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ __be32 buf[2];
+};
+
+static int spi_slave_time_submit(struct spi_slave_time_priv *priv);
+
+static void spi_slave_time_complete(void *arg)
+{
+ struct spi_slave_time_priv *priv = arg;
+ int ret;
+
+ ret = priv->msg.status;
+ if (ret)
+ goto terminate;
+
+ ret = spi_slave_time_submit(priv);
+ if (ret)
+ goto terminate;
+
+ return;
+
+terminate:
+ pr_info("%s: Terminating\n", __func__);
+ complete(&priv->finished);
+}
+
+static int spi_slave_time_submit(struct spi_slave_time_priv *priv)
+{
+ u32 rem_ns;
+ int ret;
+ u64 ts;
+
+ ts = local_clock();
+ rem_ns = do_div(ts, 1000000000) / 1000;
+
+ priv->buf[0] = cpu_to_be32(ts);
+ priv->buf[1] = cpu_to_be32(rem_ns);
+
+ spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1);
+
+ priv->msg.complete = spi_slave_time_complete;
+ priv->msg.context = priv;
+
+ ret = spi_async(priv->spi, &priv->msg);
+ if (ret)
+ pr_err("%s: spi_async() failed %d\n", __func__, ret);
+
+ return ret;
+}
+
+static int spi_slave_time_probe(struct spi_device *spi)
+{
+ struct spi_slave_time_priv *priv;
+ int ret;
+
+ /*
+ * bits_per_word cannot be configured in platform data
+ */
+ spi->bits_per_word = 8;
+
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->spi = spi;
+ init_completion(&priv->finished);
+ priv->xfer.tx_buf = priv->buf;
+ priv->xfer.len = sizeof(priv->buf);
+
+ ret = spi_slave_time_submit(priv);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, priv);
+ return 0;
+}
+
+static int spi_slave_time_remove(struct spi_device *spi)
+{
+ struct spi_slave_time_priv *priv = spi_get_drvdata(spi);
+
+ spi_slave_abort(spi);
+ wait_for_completion(&priv->finished);
+ return 0;
+}
+
+static struct spi_driver spi_slave_time_driver = {
+ .driver = {
+ .name = "spi-slave-time",
+ },
+ .probe = spi_slave_time_probe,
+ .remove = spi_slave_time_remove,
+};
+module_spi_driver(spi_slave_time_driver);
+
+MODULE_AUTHOR("Geert Uytterhoeven <geert+renesas@glider.be>");
+MODULE_DESCRIPTION("SPI slave reporting uptime at previous SPI message");
+MODULE_LICENSE("GPL v2");
--
2.7.4
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web