Commit 9d540b0d authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'spi/topic/master' into spi-next

parents 096bf6b7 8caab75f
SPI (Serial Peripheral Interface) busses SPI (Serial Peripheral Interface) busses
SPI busses can be described with a node for the SPI master device SPI busses can be described with a node for the SPI controller device
and a set of child nodes for each SPI slave on the bus. For this and a set of child nodes for each SPI slave on the bus. The system's SPI
discussion, it is assumed that the system's SPI controller is in controller may be described for use in SPI master mode or in SPI slave mode,
SPI master mode. This binding does not describe SPI controllers but not for both at the same time.
in slave mode.
The SPI master node requires the following properties: The SPI controller node requires the following properties:
- compatible - Name of SPI bus controller following generic names
recommended practice.
In master mode, the SPI controller node requires the following additional
properties:
- #address-cells - number of cells required to define a chip select - #address-cells - number of cells required to define a chip select
address on the SPI bus. address on the SPI bus.
- #size-cells - should be zero. - #size-cells - should be zero.
- compatible - name of SPI bus controller following generic names
recommended practice. In slave mode, the SPI controller node requires one additional property:
- spi-slave - Empty property.
No other properties are required in the SPI bus node. It is assumed No other properties are required in the SPI bus node. It is assumed
that a driver for an SPI bus device will understand that it is an SPI bus. that a driver for an SPI bus device will understand that it is an SPI bus.
However, the binding does not attempt to define the specific method for However, the binding does not attempt to define the specific method for
...@@ -21,7 +27,7 @@ assumption that board specific platform code will be used to manage ...@@ -21,7 +27,7 @@ assumption that board specific platform code will be used to manage
chip selects. Individual drivers can define additional properties to chip selects. Individual drivers can define additional properties to
support describing the chip select layout. support describing the chip select layout.
Optional properties: Optional properties (master mode only):
- cs-gpios - gpios chip select. - cs-gpios - gpios chip select.
- num-cs - total number of chipselects. - num-cs - total number of chipselects.
...@@ -41,28 +47,36 @@ cs1 : native ...@@ -41,28 +47,36 @@ cs1 : native
cs2 : &gpio1 1 0 cs2 : &gpio1 1 0
cs3 : &gpio1 2 0 cs3 : &gpio1 2 0
SPI slave nodes must be children of the SPI master node and can
contain the following properties. SPI slave nodes must be children of the SPI controller node.
- reg - (required) chip select address of device.
- compatible - (required) name of SPI device following generic names In master mode, one or more slave nodes (up to the number of chip selects) can
recommended practice. be present. Required properties are:
- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz. - compatible - Name of SPI device following generic names recommended
- spi-cpol - (optional) Empty property indicating device requires practice.
inverse clock polarity (CPOL) mode. - reg - Chip select address of device.
- spi-cpha - (optional) Empty property indicating device requires - spi-max-frequency - Maximum SPI clocking speed of device in Hz.
shifted clock phase (CPHA) mode.
- spi-cs-high - (optional) Empty property indicating device requires In slave mode, the (single) slave node is optional.
chip select active high. If present, it must be called "slave". Required properties are:
- spi-3wire - (optional) Empty property indicating device requires - compatible - Name of SPI device following generic names recommended
3-wire mode. practice.
- spi-lsb-first - (optional) Empty property indicating device requires
LSB first mode. All slave nodes can contain the following optional properties:
- spi-tx-bus-width - (optional) The bus width (number of data wires) that is - spi-cpol - Empty property indicating device requires inverse clock
used for MOSI. Defaults to 1 if not present. polarity (CPOL) mode.
- spi-rx-bus-width - (optional) The bus width (number of data wires) that is - spi-cpha - Empty property indicating device requires shifted clock
used for MISO. Defaults to 1 if not present. phase (CPHA) mode.
- spi-rx-delay-us - (optional) Microsecond delay after a read transfer. - spi-cs-high - Empty property indicating device requires chip select
- spi-tx-delay-us - (optional) Microsecond delay after a write transfer. active high.
- spi-3wire - Empty property indicating device requires 3-wire mode.
- spi-lsb-first - Empty property indicating device requires LSB first mode.
- spi-tx-bus-width - The bus width (number of data wires) that is used for MOSI.
Defaults to 1 if not present.
- spi-rx-bus-width - The bus width (number of data wires) that is used for MISO.
Defaults to 1 if not present.
- spi-rx-delay-us - Microsecond delay after a read transfer.
- spi-tx-delay-us - Microsecond delay after a write transfer.
Some SPI controllers and devices support Dual and Quad SPI transfer mode. Some SPI controllers and devices support Dual and Quad SPI transfer mode.
It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4 It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4
......
...@@ -62,8 +62,8 @@ chips described as using "three wire" signaling: SCK, data, nCSx. ...@@ -62,8 +62,8 @@ chips described as using "three wire" signaling: SCK, data, nCSx.
(That data line is sometimes called MOMI or SISO.) (That data line is sometimes called MOMI or SISO.)
Microcontrollers often support both master and slave sides of the SPI Microcontrollers often support both master and slave sides of the SPI
protocol. This document (and Linux) currently only supports the master protocol. This document (and Linux) supports both the master and slave
side of SPI interactions. sides of SPI interactions.
Who uses it? On what kinds of systems? Who uses it? On what kinds of systems?
...@@ -154,9 +154,8 @@ control audio interfaces, present touchscreen sensors as input interfaces, ...@@ -154,9 +154,8 @@ control audio interfaces, present touchscreen sensors as input interfaces,
or monitor temperature and voltage levels during industrial processing. or monitor temperature and voltage levels during industrial processing.
And those might all be sharing the same controller driver. And those might all be sharing the same controller driver.
A "struct spi_device" encapsulates the master-side interface between A "struct spi_device" encapsulates the controller-side interface between
those two types of driver. At this writing, Linux has no slave side those two types of drivers.
programming interface.
There is a minimal core of SPI programming interfaces, focussing on There is a minimal core of SPI programming interfaces, focussing on
using the driver model to connect controller and protocol drivers using using the driver model to connect controller and protocol drivers using
...@@ -177,10 +176,24 @@ shows up in sysfs in several locations: ...@@ -177,10 +176,24 @@ shows up in sysfs in several locations:
/sys/bus/spi/drivers/D ... driver for one or more spi*.* devices /sys/bus/spi/drivers/D ... driver for one or more spi*.* devices
/sys/class/spi_master/spiB ... symlink (or actual device node) to /sys/class/spi_master/spiB ... symlink (or actual device node) to
a logical node which could hold class related state for the a logical node which could hold class related state for the SPI
controller managing bus "B". All spiB.* devices share one master controller managing bus "B". All spiB.* devices share one
physical SPI bus segment, with SCLK, MOSI, and MISO. physical SPI bus segment, with SCLK, MOSI, and MISO.
/sys/devices/.../CTLR/slave ... virtual file for (un)registering the
slave device for an SPI slave controller.
Writing the driver name of an SPI slave handler to this file
registers the slave device; writing "(null)" unregisters the slave
device.
Reading from this file shows the name of the slave device ("(null)"
if not registered).
/sys/class/spi_slave/spiB ... symlink (or actual device node) to
a logical node which could hold class related state for the SPI
slave controller on bus "B". When registered, a single spiB.*
device is present here, possible sharing the physical SPI bus
segment with other SPI slave devices.
Note that the actual location of the controller's class state depends Note that the actual location of the controller's class state depends
on whether you enabled CONFIG_SYSFS_DEPRECATED or not. At this time, on whether you enabled CONFIG_SYSFS_DEPRECATED or not. At this time,
the only class-specific state is the bus number ("B" in "spiB"), so the only class-specific state is the bus number ("B" in "spiB"), so
......
...@@ -785,6 +785,30 @@ config SPI_TLE62X0 ...@@ -785,6 +785,30 @@ config SPI_TLE62X0
endif # SPI_MASTER endif # SPI_MASTER
# (slave support would go here) #
# SLAVE side ... listening to other SPI masters
#
config SPI_SLAVE
bool "SPI slave protocol handlers"
help
If your system has a slave-capable SPI controller, you can enable
slave protocol handlers.
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.
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 endif # SPI
...@@ -105,3 +105,7 @@ obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o ...@@ -105,3 +105,7 @@ obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_XLP) += spi-xlp.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o 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
/*
* 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");
/*
* 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-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):
*
* # 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 ...
* ^^^^^ ^^^^^^^^
* seconds microseconds
*/
#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:
dev_info(&priv->spi->dev, "Terminating\n");
complete(&priv->finished);
}
static int spi_slave_time_submit(struct spi_slave_time_priv *priv)
{
u32 rem_us;
int ret;
u64 ts;
ts = local_clock();
rem_us = do_div(ts, 1000000000) / 1000;
priv->buf[0] = cpu_to_be32(ts);
priv->buf[1] = cpu_to_be32(rem_us);
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)
dev_err(&priv->spi->dev, "spi_async() failed %d\n", ret);
return ret;
}
static int spi_slave_time_probe(struct spi_device *spi)
{
struct spi_slave_time_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.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");
This diff is collapsed.
This diff is collapsed.
...@@ -7,37 +7,37 @@ ...@@ -7,37 +7,37 @@
#include <linux/ktime.h> #include <linux/ktime.h>
#include <linux/tracepoint.h> #include <linux/tracepoint.h>
DECLARE_EVENT_CLASS(spi_master, DECLARE_EVENT_CLASS(spi_controller,
TP_PROTO(struct spi_master *master), TP_PROTO(struct spi_controller *controller),
TP_ARGS(master), TP_ARGS(controller),
TP_STRUCT__entry( TP_STRUCT__entry(
__field( int, bus_num ) __field( int, bus_num )
), ),
TP_fast_assign( TP_fast_assign(
__entry->bus_num = master->bus_num; __entry->bus_num = controller->bus_num;
), ),
TP_printk("spi%d", (int)__entry->bus_num) TP_printk("spi%d", (int)__entry->bus_num)
); );
DEFINE_EVENT(spi_master, spi_master_idle, DEFINE_EVENT(spi_controller, spi_controller_idle,
TP_PROTO(struct spi_master *master), TP_PROTO(struct spi_controller *controller),
TP_ARGS(master) TP_ARGS(controller)
); );
DEFINE_EVENT(spi_master, spi_master_busy, DEFINE_EVENT(spi_controller, spi_controller_busy,
TP_PROTO(struct spi_master *master), TP_PROTO(struct spi_controller *controller),
TP_ARGS(master) TP_ARGS(controller)
); );
...@@ -54,7 +54,7 @@ DECLARE_EVENT_CLASS(spi_message, ...@@ -54,7 +54,7 @@ DECLARE_EVENT_CLASS(spi_message,
), ),
TP_fast_assign( TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num; __entry->bus_num = msg->spi->controller->bus_num;
__entry->chip_select = msg->spi->chip_select; __entry->chip_select = msg->spi->chip_select;
__entry->msg = msg; __entry->msg = msg;
), ),
...@@ -95,7 +95,7 @@ TRACE_EVENT(spi_message_done, ...@@ -95,7 +95,7 @@ TRACE_EVENT(spi_message_done,
), ),
TP_fast_assign( TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num; __entry->bus_num = msg->spi->controller->bus_num;
__entry->chip_select = msg->spi->chip_select; __entry->chip_select = msg->spi->chip_select;
__entry->msg = msg; __entry->msg = msg;
__entry->frame = msg->frame_length; __entry->frame = msg->frame_length;
...@@ -122,7 +122,7 @@ DECLARE_EVENT_CLASS(spi_transfer, ...@@ -122,7 +122,7 @@ DECLARE_EVENT_CLASS(spi_transfer,
), ),
TP_fast_assign( TP_fast_assign(
__entry->bus_num = msg->spi->master->bus_num; __entry->bus_num = msg->spi->controller->bus_num;
__entry->chip_select = msg->spi->chip_select; __entry->chip_select = msg->spi->chip_select;
__entry->xfer = xfer; __entry->xfer = xfer;
__entry->len = xfer->len; __entry->len = xfer->len;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment