diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2017-05-30 12:43:51 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2017-05-30 12:43:51 +1000 |
commit | fdabcf0c35152c82c06b09831b9b0812b7315146 (patch) | |
tree | 28c6a36880d2ed8711395981460c056ba28361ec | |
parent | 59c268fbd279a1126d6e80da06996d8919f89b38 (diff) | |
parent | b8076f1a1d44f61c9366e89bdef499140c7eccb3 (diff) |
Merge remote-tracking branch 'spi/for-next'
-rw-r--r-- | Documentation/devicetree/bindings/spi/sh-msiof.txt | 2 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/spi/spi-bus.txt | 76 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/spi/spi-meson.txt | 31 | ||||
-rw-r--r-- | Documentation/spi/spi-summary | 27 | ||||
-rw-r--r-- | drivers/spi/Kconfig | 34 | ||||
-rw-r--r-- | drivers/spi/Makefile | 5 | ||||
-rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 1 | ||||
-rw-r--r-- | drivers/spi/spi-fsl-dspi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-meson-spicc.c | 619 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 3 | ||||
-rw-r--r-- | drivers/spi/spi-orion.c | 10 | ||||
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 111 | ||||
-rw-r--r-- | drivers/spi/spi-slave-system-control.c | 154 | ||||
-rw-r--r-- | drivers/spi/spi-slave-time.c | 129 | ||||
-rw-r--r-- | drivers/spi/spi-st-ssc4.c | 38 | ||||
-rw-r--r-- | drivers/spi/spi.c | 185 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 11 | ||||
-rw-r--r-- | include/linux/spi/sh_msiof.h | 6 | ||||
-rw-r--r-- | include/linux/spi/spi.h | 35 |
19 files changed, 1343 insertions, 137 deletions
diff --git a/Documentation/devicetree/bindings/spi/sh-msiof.txt b/Documentation/devicetree/bindings/spi/sh-msiof.txt index dc975064fa27..64ee489571c4 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/Documentation/devicetree/bindings/spi/spi-bus.txt b/Documentation/devicetree/bindings/spi/spi-bus.txt index 4b1d6e74c744..1f6e86f787ef 100644 --- a/Documentation/devicetree/bindings/spi/spi-bus.txt +++ b/Documentation/devicetree/bindings/spi/spi-bus.txt @@ -1,17 +1,23 @@ SPI (Serial Peripheral Interface) busses -SPI busses can be described with a node for the SPI master device -and a set of child nodes for each SPI slave on the bus. For this -discussion, it is assumed that the system's SPI controller is in -SPI master mode. This binding does not describe SPI controllers -in slave mode. +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. The system's SPI +controller may be described for use in SPI master mode or in SPI slave mode, +but not for both at the same time. -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 on the SPI bus. - #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 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 @@ -21,7 +27,7 @@ assumption that board specific platform code will be used to manage chip selects. Individual drivers can define additional properties to support describing the chip select layout. -Optional properties: +Optional properties (master mode only): - cs-gpios - gpios chip select. - num-cs - total number of chipselects. @@ -41,28 +47,36 @@ cs1 : native cs2 : &gpio1 1 0 cs3 : &gpio1 2 0 -SPI slave nodes must be children of the SPI master node and can -contain the following properties. -- reg - (required) chip select address of device. -- compatible - (required) name of SPI device following generic names - recommended practice. -- spi-max-frequency - (required) Maximum SPI clocking speed of device in Hz. -- spi-cpol - (optional) Empty property indicating device requires - inverse clock polarity (CPOL) mode. -- spi-cpha - (optional) Empty property indicating device requires - shifted clock phase (CPHA) mode. -- spi-cs-high - (optional) Empty property indicating device requires - chip select active high. -- spi-3wire - (optional) Empty property indicating device requires - 3-wire mode. -- spi-lsb-first - (optional) Empty property indicating device requires - LSB first mode. -- spi-tx-bus-width - (optional) The bus width (number of data wires) that is - used for MOSI. Defaults to 1 if not present. -- spi-rx-bus-width - (optional) The bus width (number of data wires) that is - used for MISO. Defaults to 1 if not present. -- spi-rx-delay-us - (optional) Microsecond delay after a read transfer. -- spi-tx-delay-us - (optional) Microsecond delay after a write transfer. + +SPI slave nodes must be children of the SPI controller node. + +In master mode, one or more slave nodes (up to the number of chip selects) can +be present. Required properties are: +- compatible - Name of SPI device following generic names recommended + practice. +- reg - Chip select address of device. +- spi-max-frequency - Maximum SPI clocking speed of device in Hz. + +In slave mode, the (single) slave node is optional. +If present, it must be called "slave". Required properties are: +- compatible - Name of SPI device following generic names recommended + practice. + +All slave nodes can contain the following optional properties: +- spi-cpol - Empty property indicating device requires inverse clock + polarity (CPOL) mode. +- spi-cpha - Empty property indicating device requires shifted clock + phase (CPHA) mode. +- spi-cs-high - Empty property indicating device requires chip select + 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. It allows data in the SPI system to be transferred using 2 wires (DUAL) or 4 diff --git a/Documentation/devicetree/bindings/spi/spi-meson.txt b/Documentation/devicetree/bindings/spi/spi-meson.txt index dc6d0313324a..825c39cae74a 100644 --- a/Documentation/devicetree/bindings/spi/spi-meson.txt +++ b/Documentation/devicetree/bindings/spi/spi-meson.txt @@ -20,3 +20,34 @@ Required properties: #address-cells = <1>; #size-cells = <0>; }; + +* SPICC (SPI Communication Controller) + +The Meson SPICC is generic SPI controller for general purpose Full-Duplex +communications with dedicated 16 words RX/TX PIO FIFOs. + +Required properties: + - compatible: should be "amlogic,meson-gx-spicc" on Amlogic GX SoCs. + - reg: physical base address and length of the controller registers + - interrupts: The interrupt specifier + - clock-names: Must contain "core" + - clocks: phandle of the input clock for the baud rate generator + - #address-cells: should be 1 + - #size-cells: should be 0 + +Optional properties: + - resets: phandle of the internal reset line + +See ../spi/spi-bus.txt for more details on SPI bus master and slave devices +required and optional properties. + +Example : + spi@c1108d80 { + compatible = "amlogic,meson-gx-spicc"; + reg = <0xc1108d80 0x80>; + interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>; + clock-names = "core"; + clocks = <&clk81>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/spi/spi-summary b/Documentation/spi/spi-summary index d1824b399b2d..1721c1b570c3 100644 --- a/Documentation/spi/spi-summary +++ b/Documentation/spi/spi-summary @@ -62,8 +62,8 @@ chips described as using "three wire" signaling: SCK, data, nCSx. (That data line is sometimes called MOMI or SISO.) Microcontrollers often support both master and slave sides of the SPI -protocol. This document (and Linux) currently only supports the master -side of SPI interactions. +protocol. This document (and Linux) supports both the master and slave +sides of SPI interactions. Who uses it? On what kinds of systems? @@ -154,9 +154,8 @@ control audio interfaces, present touchscreen sensors as input interfaces, or monitor temperature and voltage levels during industrial processing. And those might all be sharing the same controller driver. -A "struct spi_device" encapsulates the master-side interface between -those two types of driver. At this writing, Linux has no slave side -programming interface. +A "struct spi_device" encapsulates the controller-side interface between +those two types of drivers. There is a minimal core of SPI programming interfaces, focussing on using the driver model to connect controller and protocol drivers using @@ -177,10 +176,24 @@ shows up in sysfs in several locations: /sys/bus/spi/drivers/D ... driver for one or more spi*.* devices /sys/class/spi_master/spiB ... symlink (or actual device node) to - a logical node which could hold class related state for the - controller managing bus "B". All spiB.* devices share one + a logical node which could hold class related state for the SPI + master controller managing bus "B". All spiB.* devices share one 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 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 diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 1761c9004fc1..fd1b4fdb72a4 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -393,6 +393,13 @@ config SPI_FSL_ESPI From MPC8536, 85xx platform uses the controller, and all P10xx, P20xx, P30xx,P40xx, P50xx uses this controller. +config SPI_MESON_SPICC + tristate "Amlogic Meson SPICC controller" + depends on ARCH_MESON || COMPILE_TEST + help + This enables master mode support for the SPICC (SPI communication + controller) available in Amlogic Meson SoCs. + config SPI_MESON_SPIFC tristate "Amlogic Meson SPIFC controller" depends on ARCH_MESON || COMPILE_TEST @@ -457,6 +464,7 @@ config SPI_OMAP24XX config SPI_TI_QSPI tristate "DRA7xxx QSPI controller support" + depends on HAS_DMA depends on ARCH_OMAP2PLUS || COMPILE_TEST help QSPI master controller for DRA7xxx used for flash devices. @@ -784,6 +792,30 @@ config SPI_TLE62X0 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 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index b375a7a89216..31dccfbb335e 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_SPI_LANTIQ_SSC) += spi-lantiq-ssc.o obj-$(CONFIG_SPI_JCORE) += spi-jcore.o obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o +obj-$(CONFIG_SPI_MESON_SPICC) += spi-meson-spicc.o obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o obj-$(CONFIG_SPI_MPC512x_PSC) += spi-mpc512x-psc.o obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o @@ -105,3 +106,7 @@ obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o obj-$(CONFIG_SPI_XLP) += spi-xlp.o 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 +obj-$(CONFIG_SPI_SLAVE_SYSTEM_CONTROL) += spi-slave-system-control.o diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 5514cd02e93a..4da2d4a524ca 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -484,6 +484,7 @@ static const struct of_device_id bcm63xx_hsspi_of_match[] = { { .compatible = "brcm,bcm6328-hsspi", }, { }, }; +MODULE_DEVICE_TABLE(of, bcm63xx_hsspi_of_match); static struct platform_driver bcm63xx_hsspi_driver = { .driver = { diff --git a/drivers/spi/spi-fsl-dspi.c b/drivers/spi/spi-fsl-dspi.c index 15201645bdc4..d89127f4a46d 100644 --- a/drivers/spi/spi-fsl-dspi.c +++ b/drivers/spi/spi-fsl-dspi.c @@ -1032,7 +1032,8 @@ static int dspi_probe(struct platform_device *pdev) goto out_master_put; if (dspi->devtype_data->trans_mode == DSPI_DMA_MODE) { - if (dspi_request_dma(dspi, res->start)) { + ret = dspi_request_dma(dspi, res->start); + if (ret < 0) { dev_err(&pdev->dev, "can't get dma channels\n"); goto out_clk_put; } diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c new file mode 100644 index 000000000000..7f8429635502 --- /dev/null +++ b/drivers/spi/spi-meson-spicc.c @@ -0,0 +1,619 @@ +/* + * Driver for Amlogic Meson SPI communication controller (SPICC) + * + * Copyright (C) BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/interrupt.h> +#include <linux/reset.h> +#include <linux/gpio.h> + +/* + * The Meson SPICC controller could support DMA based transfers, but is not + * implemented by the vendor code, and while having the registers documentation + * it has never worked on the GXL Hardware. + * The PIO mode is the only mode implemented, and due to badly designed HW : + * - all transfers are cutted in 16 words burst because the FIFO hangs on + * TX underflow, and there is no TX "Half-Empty" interrupt, so we go by + * FIFO max size chunk only + * - CS management is dumb, and goes UP between every burst, so is really a + * "Data Valid" signal than a Chip Select, GPIO link should be used instead + * to have a CS go down over the full transfer + */ + +#define SPICC_MAX_FREQ 30000000 +#define SPICC_MAX_BURST 128 + +/* Register Map */ +#define SPICC_RXDATA 0x00 + +#define SPICC_TXDATA 0x04 + +#define SPICC_CONREG 0x08 +#define SPICC_ENABLE BIT(0) +#define SPICC_MODE_MASTER BIT(1) +#define SPICC_XCH BIT(2) +#define SPICC_SMC BIT(3) +#define SPICC_POL BIT(4) +#define SPICC_PHA BIT(5) +#define SPICC_SSCTL BIT(6) +#define SPICC_SSPOL BIT(7) +#define SPICC_DRCTL_MASK GENMASK(9, 8) +#define SPICC_DRCTL_IGNORE 0 +#define SPICC_DRCTL_FALLING 1 +#define SPICC_DRCTL_LOWLEVEL 2 +#define SPICC_CS_MASK GENMASK(13, 12) +#define SPICC_DATARATE_MASK GENMASK(18, 16) +#define SPICC_DATARATE_DIV4 0 +#define SPICC_DATARATE_DIV8 1 +#define SPICC_DATARATE_DIV16 2 +#define SPICC_DATARATE_DIV32 3 +#define SPICC_BITLENGTH_MASK GENMASK(24, 19) +#define SPICC_BURSTLENGTH_MASK GENMASK(31, 25) + +#define SPICC_INTREG 0x0c +#define SPICC_TE_EN BIT(0) /* TX FIFO Empty Interrupt */ +#define SPICC_TH_EN BIT(1) /* TX FIFO Half-Full Interrupt */ +#define SPICC_TF_EN BIT(2) /* TX FIFO Full Interrupt */ +#define SPICC_RR_EN BIT(3) /* RX FIFO Ready Interrupt */ +#define SPICC_RH_EN BIT(4) /* RX FIFO Half-Full Interrupt */ +#define SPICC_RF_EN BIT(5) /* RX FIFO Full Interrupt */ +#define SPICC_RO_EN BIT(6) /* RX FIFO Overflow Interrupt */ +#define SPICC_TC_EN BIT(7) /* Transfert Complete Interrupt */ + +#define SPICC_DMAREG 0x10 +#define SPICC_DMA_ENABLE BIT(0) +#define SPICC_TXFIFO_THRESHOLD_MASK GENMASK(5, 1) +#define SPICC_RXFIFO_THRESHOLD_MASK GENMASK(10, 6) +#define SPICC_READ_BURST_MASK GENMASK(14, 11) +#define SPICC_WRITE_BURST_MASK GENMASK(18, 15) +#define SPICC_DMA_URGENT BIT(19) +#define SPICC_DMA_THREADID_MASK GENMASK(25, 20) +#define SPICC_DMA_BURSTNUM_MASK GENMASK(31, 26) + +#define SPICC_STATREG 0x14 +#define SPICC_TE BIT(0) /* TX FIFO Empty Interrupt */ +#define SPICC_TH BIT(1) /* TX FIFO Half-Full Interrupt */ +#define SPICC_TF BIT(2) /* TX FIFO Full Interrupt */ +#define SPICC_RR BIT(3) /* RX FIFO Ready Interrupt */ +#define SPICC_RH BIT(4) /* RX FIFO Half-Full Interrupt */ +#define SPICC_RF BIT(5) /* RX FIFO Full Interrupt */ +#define SPICC_RO BIT(6) /* RX FIFO Overflow Interrupt */ +#define SPICC_TC BIT(7) /* Transfert Complete Interrupt */ + +#define SPICC_PERIODREG 0x18 +#define SPICC_PERIOD GENMASK(14, 0) /* Wait cycles */ + +#define SPICC_TESTREG 0x1c +#define SPICC_TXCNT_MASK GENMASK(4, 0) /* TX FIFO Counter */ +#define SPICC_RXCNT_MASK GENMASK(9, 5) /* RX FIFO Counter */ +#define SPICC_SMSTATUS_MASK GENMASK(12, 10) /* State Machine Status */ +#define SPICC_LBC_RO BIT(13) /* Loop Back Control Read-Only */ +#define SPICC_LBC_W1 BIT(14) /* Loop Back Control Write-Only */ +#define SPICC_SWAP_RO BIT(14) /* RX FIFO Data Swap Read-Only */ +#define SPICC_SWAP_W1 BIT(15) /* RX FIFO Data Swap Write-Only */ +#define SPICC_DLYCTL_RO_MASK GENMASK(20, 15) /* Delay Control Read-Only */ +#define SPICC_DLYCTL_W1_MASK GENMASK(21, 16) /* Delay Control Write-Only */ +#define SPICC_FIFORST_RO_MASK GENMASK(22, 21) /* FIFO Softreset Read-Only */ +#define SPICC_FIFORST_W1_MASK GENMASK(23, 22) /* FIFO Softreset Write-Only */ + +#define SPICC_DRADDR 0x20 /* Read Address of DMA */ + +#define SPICC_DWADDR 0x24 /* Write Address of DMA */ + +#define writel_bits_relaxed(mask, val, addr) \ + writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) + +#define SPICC_BURST_MAX 16 +#define SPICC_FIFO_HALF 10 + +struct meson_spicc_device { + struct spi_master *master; + struct platform_device *pdev; + void __iomem *base; + struct clk *core; + struct spi_message *message; + struct spi_transfer *xfer; + u8 *tx_buf; + u8 *rx_buf; + unsigned int bytes_per_word; + unsigned long tx_remain; + unsigned long txb_remain; + unsigned long rx_remain; + unsigned long rxb_remain; + unsigned long xfer_remain; + bool is_burst_end; + bool is_last_burst; +}; + +static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc) +{ + return !!FIELD_GET(SPICC_TF, + readl_relaxed(spicc->base + SPICC_STATREG)); +} + +static inline bool meson_spicc_rxready(struct meson_spicc_device *spicc) +{ + return FIELD_GET(SPICC_RH | SPICC_RR | SPICC_RF_EN, + readl_relaxed(spicc->base + SPICC_STATREG)); +} + +static inline u32 meson_spicc_pull_data(struct meson_spicc_device *spicc) +{ + unsigned int bytes = spicc->bytes_per_word; + unsigned int byte_shift = 0; + u32 data = 0; + u8 byte; + + while (bytes--) { + byte = *spicc->tx_buf++; + data |= (byte & 0xff) << byte_shift; + byte_shift += 8; + } + + spicc->tx_remain--; + return data; +} + +static inline void meson_spicc_push_data(struct meson_spicc_device *spicc, + u32 data) +{ + unsigned int bytes = spicc->bytes_per_word; + unsigned int byte_shift = 0; + u8 byte; + + while (bytes--) { + byte = (data >> byte_shift) & 0xff; + *spicc->rx_buf++ = byte; + byte_shift += 8; + } + + spicc->rx_remain--; +} + +static inline void meson_spicc_rx(struct meson_spicc_device *spicc) +{ + /* Empty RX FIFO */ + while (spicc->rx_remain && + meson_spicc_rxready(spicc)) + meson_spicc_push_data(spicc, + readl_relaxed(spicc->base + SPICC_RXDATA)); +} + +static inline void meson_spicc_tx(struct meson_spicc_device *spicc) +{ + /* Fill Up TX FIFO */ + while (spicc->tx_remain && + !meson_spicc_txfull(spicc)) + writel_relaxed(meson_spicc_pull_data(spicc), + spicc->base + SPICC_TXDATA); +} + +static inline u32 meson_spicc_setup_rx_irq(struct meson_spicc_device *spicc, + u32 irq_ctrl) +{ + if (spicc->rx_remain > SPICC_FIFO_HALF) + irq_ctrl |= SPICC_RH_EN; + else + irq_ctrl |= SPICC_RR_EN; + + return irq_ctrl; +} + +static inline void meson_spicc_setup_burst(struct meson_spicc_device *spicc, + unsigned int burst_len) +{ + /* Setup Xfer variables */ + spicc->tx_remain = burst_len; + spicc->rx_remain = burst_len; + spicc->xfer_remain -= burst_len * spicc->bytes_per_word; + spicc->is_burst_end = false; + if (burst_len < SPICC_BURST_MAX || !spicc->xfer_remain) + spicc->is_last_burst = true; + else + spicc->is_last_burst = false; + + /* Setup burst length */ + writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, + FIELD_PREP(SPICC_BURSTLENGTH_MASK, + burst_len), + spicc->base + SPICC_CONREG); + + /* Fill TX FIFO */ + meson_spicc_tx(spicc); +} + +static irqreturn_t meson_spicc_irq(int irq, void *data) +{ + struct meson_spicc_device *spicc = (void *) data; + u32 ctrl = readl_relaxed(spicc->base + SPICC_INTREG); + u32 stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; + + ctrl &= ~(SPICC_RH_EN | SPICC_RR_EN); + + /* Empty RX FIFO */ + meson_spicc_rx(spicc); + + /* Enable TC interrupt since we transferred everything */ + if (!spicc->tx_remain && !spicc->rx_remain) { + spicc->is_burst_end = true; + + /* Enable TC interrupt */ + ctrl |= SPICC_TC_EN; + + /* Reload IRQ status */ + stat = readl_relaxed(spicc->base + SPICC_STATREG) & ctrl; + } + + /* Check transfer complete */ + if ((stat & SPICC_TC) && spicc->is_burst_end) { + unsigned int burst_len; + + /* Clear TC bit */ + writel_relaxed(SPICC_TC, spicc->base + SPICC_STATREG); + + /* Disable TC interrupt */ + ctrl &= ~SPICC_TC_EN; + + if (spicc->is_last_burst) { + /* Disable all IRQs */ + writel(0, spicc->base + SPICC_INTREG); + + spi_finalize_current_transfer(spicc->master); + + return IRQ_HANDLED; + } + + burst_len = min_t(unsigned int, + spicc->xfer_remain / spicc->bytes_per_word, + SPICC_BURST_MAX); + + /* Setup burst */ + meson_spicc_setup_burst(spicc, burst_len); + + /* Restart burst */ + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, + spicc->base + SPICC_CONREG); + } + + /* Setup RX interrupt trigger */ + ctrl = meson_spicc_setup_rx_irq(spicc, ctrl); + + /* Reconfigure interrupts */ + writel(ctrl, spicc->base + SPICC_INTREG); + + return IRQ_HANDLED; +} + +static u32 meson_spicc_setup_speed(struct meson_spicc_device *spicc, u32 conf, + u32 speed) +{ + unsigned long parent, value; + unsigned int i, div; + + parent = clk_get_rate(spicc->core); + + /* Find closest inferior/equal possible speed */ + for (i = 0 ; i < 7 ; ++i) { + /* 2^(data_rate+2) */ + value = parent >> (i + 2); + + if (value <= speed) + break; + } + + /* If provided speed it lower than max divider, use max divider */ + if (i > 7) { + div = 7; + dev_warn_once(&spicc->pdev->dev, "unable to get close to speed %u\n", + speed); + } else + div = i; + + dev_dbg(&spicc->pdev->dev, "parent %lu, speed %u -> %lu (%u)\n", + parent, speed, value, div); + + conf &= ~SPICC_DATARATE_MASK; + conf |= FIELD_PREP(SPICC_DATARATE_MASK, div); + + return conf; +} + +static void meson_spicc_setup_xfer(struct meson_spicc_device *spicc, + struct spi_transfer *xfer) +{ + u32 conf, conf_orig; + + /* Read original configuration */ + conf = conf_orig = readl_relaxed(spicc->base + SPICC_CONREG); + + /* Select closest divider */ + conf = meson_spicc_setup_speed(spicc, conf, xfer->speed_hz); + + /* Setup word width */ + conf &= ~SPICC_BITLENGTH_MASK; + conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, + (spicc->bytes_per_word << 3) - 1); + + /* Ignore if unchanged */ + if (conf != conf_orig) + writel_relaxed(conf, spicc->base + SPICC_CONREG); +} + +static int meson_spicc_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) +{ + struct meson_spicc_device *spicc = spi_master_get_devdata(master); + unsigned int burst_len; + u32 irq = 0; + + /* Store current transfer */ + spicc->xfer = xfer; + + /* Setup transfer parameters */ + spicc->tx_buf = (u8 *)xfer->tx_buf; + spicc->rx_buf = (u8 *)xfer->rx_buf; + spicc->xfer_remain = xfer->len; + + /* Pre-calculate word size */ + spicc->bytes_per_word = + DIV_ROUND_UP(spicc->xfer->bits_per_word, 8); + + /* Setup transfer parameters */ + meson_spicc_setup_xfer(spicc, xfer); + + burst_len = min_t(unsigned int, + spicc->xfer_remain / spicc->bytes_per_word, + SPICC_BURST_MAX); + + meson_spicc_setup_burst(spicc, burst_len); + + irq = meson_spicc_setup_rx_irq(spicc, irq); + + /* Start burst */ + writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG); + + /* Enable interrupts */ + writel_relaxed(irq, spicc->base + SPICC_INTREG); + + return 1; +} + +static int meson_spicc_prepare_message(struct spi_master *master, + struct spi_message *message) +{ + struct meson_spicc_device *spicc = spi_master_get_devdata(master); + struct spi_device *spi = message->spi; + u32 conf = 0; + + /* Store current message */ + spicc->message = message; + + /* Enable Master */ + conf |= SPICC_ENABLE; + conf |= SPICC_MODE_MASTER; + + /* SMC = 0 */ + + /* Setup transfer mode */ + if (spi->mode & SPI_CPOL) + conf |= SPICC_POL; + else + conf &= ~SPICC_POL; + + if (spi->mode & SPI_CPHA) + conf |= SPICC_PHA; + else + conf &= ~SPICC_PHA; + + /* SSCTL = 0 */ + + if (spi->mode & SPI_CS_HIGH) + conf |= SPICC_SSPOL; + else + conf &= ~SPICC_SSPOL; + + if (spi->mode & SPI_READY) + conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_LOWLEVEL); + else + conf |= FIELD_PREP(SPICC_DRCTL_MASK, SPICC_DRCTL_IGNORE); + + /* Select CS */ + conf |= FIELD_PREP(SPICC_CS_MASK, spi->chip_select); + + /* Default Clock rate core/4 */ + + /* Default 8bit word */ + conf |= FIELD_PREP(SPICC_BITLENGTH_MASK, 8 - 1); + + writel_relaxed(conf, spicc->base + SPICC_CONREG); + + /* Setup no wait cycles by default */ + writel_relaxed(0, spicc->base + SPICC_PERIODREG); + + writel_bits_relaxed(BIT(24), BIT(24), spicc->base + SPICC_TESTREG); + + return 0; +} + +static int meson_spicc_unprepare_transfer(struct spi_master *master) +{ + struct meson_spicc_device *spicc = spi_master_get_devdata(master); + + /* Disable all IRQs */ + writel(0, spicc->base + SPICC_INTREG); + + /* Disable controller */ + writel_bits_relaxed(SPICC_ENABLE, 0, spicc->base + SPICC_CONREG); + + device_reset_optional(&spicc->pdev->dev); + + return 0; +} + +static int meson_spicc_setup(struct spi_device *spi) +{ + int ret = 0; + + if (!spi->controller_state) + spi->controller_state = spi_master_get_devdata(spi->master); + else if (gpio_is_valid(spi->cs_gpio)) + goto out_gpio; + else if (spi->cs_gpio == -ENOENT) + return 0; + + if (gpio_is_valid(spi->cs_gpio)) { + ret = gpio_request(spi->cs_gpio, dev_name(&spi->dev)); + if (ret) { + dev_err(&spi->dev, "failed to request cs gpio\n"); + return ret; + } + } + +out_gpio: + ret = gpio_direction_output(spi->cs_gpio, + !(spi->mode & SPI_CS_HIGH)); + + return ret; +} + +static void meson_spicc_cleanup(struct spi_device *spi) +{ + if (gpio_is_valid(spi->cs_gpio)) + gpio_free(spi->cs_gpio); + + spi->controller_state = NULL; +} + +static int meson_spicc_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct meson_spicc_device *spicc; + struct resource *res; + int ret, irq, rate; + + master = spi_alloc_master(&pdev->dev, sizeof(*spicc)); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + spicc = spi_master_get_devdata(master); + spicc->master = master; + + spicc->pdev = pdev; + platform_set_drvdata(pdev, spicc); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + spicc->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(spicc->base)) { + dev_err(&pdev->dev, "io resource mapping failed\n"); + ret = PTR_ERR(spicc->base); + goto out_master; + } + + /* Disable all IRQs */ + writel_relaxed(0, spicc->base + SPICC_INTREG); + + irq = platform_get_irq(pdev, 0); + ret = devm_request_irq(&pdev->dev, irq, meson_spicc_irq, + 0, NULL, spicc); + if (ret) { + dev_err(&pdev->dev, "irq request failed\n"); + goto out_master; + } + + spicc->core = devm_clk_get(&pdev->dev, "core"); + if (IS_ERR(spicc->core)) { + dev_err(&pdev->dev, "core clock request failed\n"); + ret = PTR_ERR(spicc->core); + goto out_master; + } + + ret = clk_prepare_enable(spicc->core); + if (ret) { + dev_err(&pdev->dev, "core clock enable failed\n"); + goto out_master; + } + rate = clk_get_rate(spicc->core); + + device_reset_optional(&pdev->dev); + + master->num_chipselect = 4; + master->dev.of_node = pdev->dev.of_node; + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH; + master->bits_per_word_mask = SPI_BPW_MASK(32) | + SPI_BPW_MASK(24) | + SPI_BPW_MASK(16) | + SPI_BPW_MASK(8); + master->flags = (SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX); + master->min_speed_hz = rate >> 9; + master->setup = meson_spicc_setup; + master->cleanup = meson_spicc_cleanup; + master->prepare_message = meson_spicc_prepare_message; + master->unprepare_transfer_hardware = meson_spicc_unprepare_transfer; + master->transfer_one = meson_spicc_transfer_one; + + /* Setup max rate according to the Meson GX datasheet */ + if ((rate >> 2) > SPICC_MAX_FREQ) + master->max_speed_hz = SPICC_MAX_FREQ; + else + master->max_speed_hz = rate >> 2; + + ret = devm_spi_register_master(&pdev->dev, master); + if (!ret) + return 0; + + dev_err(&pdev->dev, "spi master registration failed\n"); + +out_master: + spi_master_put(master); + + return ret; +} + +static int meson_spicc_remove(struct platform_device *pdev) +{ + struct meson_spicc_device *spicc = platform_get_drvdata(pdev); + + /* Disable SPI */ + writel(0, spicc->base + SPICC_CONREG); + + clk_disable_unprepare(spicc->core); + + return 0; +} + +static const struct of_device_id meson_spicc_of_match[] = { + { .compatible = "amlogic,meson-gx-spicc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_spicc_of_match); + +static struct platform_driver meson_spicc_driver = { + .probe = meson_spicc_probe, + .remove = meson_spicc_remove, + .driver = { + .name = "meson-spicc", + .of_match_table = of_match_ptr(meson_spicc_of_match), + }, +}; + +module_platform_driver(meson_spicc_driver); + +MODULE_DESCRIPTION("Meson SPI Communication Controller driver"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 7275223dbcd4..e048268d8ba2 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1412,9 +1412,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) sprintf(mcspi->dma_channels[i].dma_tx_ch_name, "tx%d", i); } - if (status < 0) - goto free_master; - pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); pm_runtime_enable(&pdev->dev); diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index be2e87ee8b31..28fc9f161b9d 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -22,6 +22,7 @@ #include <linux/of_device.h> #include <linux/clk.h> #include <linux/sizes.h> +#include <linux/gpio.h> #include <asm/unaligned.h> #define DRIVER_NAME "orion_spi" @@ -320,12 +321,18 @@ orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t) static void orion_spi_set_cs(struct spi_device *spi, bool enable) { struct orion_spi *orion_spi; + int cs; + + if (gpio_is_valid(spi->cs_gpio)) + cs = 0; + else + cs = spi->chip_select; orion_spi = spi_master_get_devdata(spi->master); orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK); orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, - ORION_SPI_CS(spi->chip_select)); + ORION_SPI_CS(cs)); /* Chip select logic is inverted from spi_set_cs */ if (!enable) @@ -606,6 +613,7 @@ static int orion_spi_probe(struct platform_device *pdev) master->setup = orion_spi_setup; master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16); master->auto_runtime_pm = true; + master->flags = SPI_MASTER_GPIO_SS; platform_set_drvdata(pdev, master); diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 2ce15ca97782..c304c7167866 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 @@ -33,7 +34,6 @@ #include <asm/unaligned.h> - struct sh_msiof_chipdata { u16 tx_fifo_size; u16 rx_fifo_size; @@ -53,6 +53,7 @@ struct sh_msiof_spi_priv { void *rx_dma_page; dma_addr_t tx_dma_addr; dma_addr_t rx_dma_addr; + bool slave_aborted; }; #define TMDR1 0x00 /* Transmit Mode Register 1 */ @@ -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,49 @@ 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); + + p->slave_aborted = true; + complete(&p->done); + return 0; +} + +static int sh_msiof_wait_for_completion(struct sh_msiof_spi_priv *p) +{ + if (spi_controller_is_slave(p->master)) { + if (wait_for_completion_interruptible(&p->done) || + p->slave_aborted) { + dev_dbg(&p->pdev->dev, "interrupted\n"); + return -EINTR; + } + } 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), @@ -628,6 +663,7 @@ static int sh_msiof_spi_txrx_once(struct sh_msiof_spi_priv *p, tx_fifo(p, tx_buf, words, fifo_shift); reinit_completion(&p->done); + p->slave_aborted = false; ret = sh_msiof_spi_start(p, rx_buf); if (ret) { @@ -636,11 +672,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) @@ -732,6 +766,7 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, sh_msiof_write(p, IER, ier_bits); reinit_completion(&p->done); + p->slave_aborted = false; /* Now start DMA */ if (rx) @@ -746,11 +781,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 +876,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 +1032,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 +1197,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 +1281,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/drivers/spi/spi-slave-system-control.c b/drivers/spi/spi-slave-system-control.c new file mode 100644 index 000000000000..c0257e937995 --- /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"); diff --git a/drivers/spi/spi-slave-time.c b/drivers/spi/spi-slave-time.c new file mode 100644 index 000000000000..f2e07a392d68 --- /dev/null +++ b/drivers/spi/spi-slave-time.c @@ -0,0 +1,129 @@ +/* + * 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"); diff --git a/drivers/spi/spi-st-ssc4.c b/drivers/spi/spi-st-ssc4.c index e54b59638458..a4e43fc19ece 100644 --- a/drivers/spi/spi-st-ssc4.c +++ b/drivers/spi/spi-st-ssc4.c @@ -229,42 +229,42 @@ static int spi_st_setup(struct spi_device *spi) "setting baudrate:target= %u hz, actual= %u hz, sscbrg= %u\n", hz, spi_st->baud, sscbrg); - /* Set SSC_CTL and enable SSC */ - var = readl_relaxed(spi_st->base + SSC_CTL); - var |= SSC_CTL_MS; + /* Set SSC_CTL and enable SSC */ + var = readl_relaxed(spi_st->base + SSC_CTL); + var |= SSC_CTL_MS; - if (spi->mode & SPI_CPOL) + if (spi->mode & SPI_CPOL) var |= SSC_CTL_PO; - else + else var &= ~SSC_CTL_PO; - if (spi->mode & SPI_CPHA) + if (spi->mode & SPI_CPHA) var |= SSC_CTL_PH; - else + else var &= ~SSC_CTL_PH; - if ((spi->mode & SPI_LSB_FIRST) == 0) + if ((spi->mode & SPI_LSB_FIRST) == 0) var |= SSC_CTL_HB; - else + else var &= ~SSC_CTL_HB; - if (spi->mode & SPI_LOOP) + if (spi->mode & SPI_LOOP) var |= SSC_CTL_LPB; - else + else var &= ~SSC_CTL_LPB; - var &= ~SSC_CTL_DATA_WIDTH_MSK; - var |= (spi->bits_per_word - 1); + var &= ~SSC_CTL_DATA_WIDTH_MSK; + var |= (spi->bits_per_word - 1); - var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; - var |= SSC_CTL_EN; + var |= SSC_CTL_EN_TX_FIFO | SSC_CTL_EN_RX_FIFO; + var |= SSC_CTL_EN; - writel_relaxed(var, spi_st->base + SSC_CTL); + writel_relaxed(var, spi_st->base + SSC_CTL); - /* Clear the status register */ - readl_relaxed(spi_st->base + SSC_RBUF); + /* Clear the status register */ + readl_relaxed(spi_st->base + SSC_RBUF); - return 0; + return 0; out_free_gpio: gpio_free(cs); diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 89254a55eb2e..c3f6b524b3ce 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -80,7 +80,7 @@ static ssize_t spi_master_##field##_show(struct device *dev, \ return spi_statistics_##field##_show(&master->statistics, buf); \ } \ static struct device_attribute dev_attr_spi_master_##field = { \ - .attr = { .name = file, .mode = S_IRUGO }, \ + .attr = { .name = file, .mode = 0444 }, \ .show = spi_master_##field##_show, \ }; \ static ssize_t spi_device_##field##_show(struct device *dev, \ @@ -91,7 +91,7 @@ static ssize_t spi_device_##field##_show(struct device *dev, \ return spi_statistics_##field##_show(&spi->statistics, buf); \ } \ static struct device_attribute dev_attr_spi_device_##field = { \ - .attr = { .name = file, .mode = S_IRUGO }, \ + .attr = { .name = file, .mode = 0444 }, \ .show = spi_device_##field##_show, \ } @@ -1535,15 +1535,6 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi, u32 value; int rc; - /* Device address */ - rc = of_property_read_u32(nc, "reg", &value); - if (rc) { - dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n", - nc->full_name, rc); - return rc; - } - spi->chip_select = value; - /* Mode (clock phase/polarity/etc.) */ if (of_find_property(nc, "spi-cpha", NULL)) spi->mode |= SPI_CPHA; @@ -1593,6 +1584,24 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi, } } + if (spi_controller_is_slave(master)) { + if (strcmp(nc->name, "slave")) { + dev_err(&master->dev, "%s is not called 'slave'\n", + nc->full_name); + return -EINVAL; + } + return 0; + } + + /* Device address */ + rc = of_property_read_u32(nc, "reg", &value); + if (rc) { + dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n", + nc->full_name, rc); + return rc; + } + spi->chip_select = value; + /* Device speed */ rc = of_property_read_u32(nc, "spi-max-frequency", &value); if (rc) { @@ -1658,8 +1667,8 @@ err_out: * of_register_spi_devices() - Register child devices onto the SPI bus * @master: Pointer to spi_master device * - * Registers an spi_device for each child node of master node which has a 'reg' - * property. + * Registers an spi_device for each child node of controller node which + * represents a valid SPI slave. */ static void of_register_spi_devices(struct spi_master *master) { @@ -1828,28 +1837,129 @@ static struct class spi_master_class = { .dev_groups = spi_master_groups, }; +#ifdef CONFIG_SPI_SLAVE +/** + * spi_slave_abort - abort the ongoing transfer request on an SPI slave + * controller + * @spi: device used for the current transfer + */ +int spi_slave_abort(struct spi_device *spi) +{ + struct spi_master *master = spi->master; + + if (spi_controller_is_slave(master) && master->slave_abort) + return master->slave_abort(master); + + return -ENOTSUPP; +} +EXPORT_SYMBOL_GPL(spi_slave_abort); + +static int match_true(struct device *dev, void *data) +{ + return 1; +} + +static ssize_t spi_slave_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct spi_master *ctlr = container_of(dev, struct spi_master, dev); + struct device *child; + + child = device_find_child(&ctlr->dev, NULL, match_true); + return sprintf(buf, "%s\n", + child ? to_spi_device(child)->modalias : NULL); +} + +static ssize_t spi_slave_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct spi_master *ctlr = container_of(dev, struct spi_master, dev); + struct spi_device *spi; + struct device *child; + char name[32]; + int rc; + + rc = sscanf(buf, "%31s", name); + if (rc != 1 || !name[0]) + return -EINVAL; + + child = device_find_child(&ctlr->dev, NULL, match_true); + if (child) { + /* Remove registered slave */ + device_unregister(child); + put_device(child); + } + + if (strcmp(name, "(null)")) { + /* Register new slave */ + spi = spi_alloc_device(ctlr); + if (!spi) + return -ENOMEM; + + strlcpy(spi->modalias, name, sizeof(spi->modalias)); + + rc = spi_add_device(spi); + if (rc) { + spi_dev_put(spi); + return rc; + } + } + + return count; +} + +static DEVICE_ATTR(slave, 0644, spi_slave_show, spi_slave_store); + +static struct attribute *spi_slave_attrs[] = { + &dev_attr_slave.attr, + NULL, +}; + +static const struct attribute_group spi_slave_group = { + .attrs = spi_slave_attrs, +}; + +static const struct attribute_group *spi_slave_groups[] = { + &spi_master_statistics_group, + &spi_slave_group, + NULL, +}; + +static struct class spi_slave_class = { + .name = "spi_slave", + .owner = THIS_MODULE, + .dev_release = spi_master_release, + .dev_groups = spi_slave_groups, +}; +#else +extern struct class spi_slave_class; /* dummy */ +#endif /** - * spi_alloc_master - allocate SPI master controller + * __spi_alloc_controller - allocate an SPI master or slave controller * @dev: the controller, possibly using the platform_bus * @size: how much zeroed driver-private data to allocate; the pointer to this * memory is in the driver_data field of the returned device, * accessible with spi_master_get_devdata(). + * @slave: flag indicating whether to allocate an SPI master (false) or SPI + * slave (true) controller * Context: can sleep * - * This call is used only by SPI master controller drivers, which are the + * This call is used only by SPI controller drivers, which are the * only ones directly touching chip registers. It's how they allocate * an spi_master structure, prior to calling spi_register_master(). * * This must be called from context that can sleep. * - * The caller is responsible for assigning the bus number and initializing - * the master's methods before calling spi_register_master(); and (after errors + * The caller is responsible for assigning the bus number and initializing the + * controller's methods before calling spi_register_master(); and (after errors * adding the device) calling spi_master_put() to prevent a memory leak. * - * Return: the SPI master structure on success, else NULL. + * Return: the SPI controller structure on success, else NULL. */ -struct spi_master *spi_alloc_master(struct device *dev, unsigned size) +struct spi_master *__spi_alloc_controller(struct device *dev, + unsigned int size, bool slave) { struct spi_master *master; @@ -1863,14 +1973,18 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size) device_initialize(&master->dev); master->bus_num = -1; master->num_chipselect = 1; - master->dev.class = &spi_master_class; + master->slave = slave; + if (IS_ENABLED(CONFIG_SPI_SLAVE) && slave) + master->dev.class = &spi_slave_class; + else + master->dev.class = &spi_master_class; master->dev.parent = dev; pm_suspend_ignore_children(&master->dev, true); spi_master_set_devdata(master, &master[1]); return master; } -EXPORT_SYMBOL_GPL(spi_alloc_master); +EXPORT_SYMBOL_GPL(__spi_alloc_controller); #ifdef CONFIG_OF static int of_spi_register_master(struct spi_master *master) @@ -1946,9 +2060,11 @@ int spi_register_master(struct spi_master *master) if (!dev) return -ENODEV; - status = of_spi_register_master(master); - if (status) - return status; + if (!spi_controller_is_slave(master)) { + status = of_spi_register_master(master); + if (status) + return status; + } /* even if it's just one always-selected device, there must * be at least one chipselect @@ -1985,8 +2101,9 @@ int spi_register_master(struct spi_master *master) status = device_add(&master->dev); if (status < 0) goto done; - dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev), - dynamic ? " (dynamic)" : ""); + dev_dbg(dev, "registered %s %s%s\n", + spi_controller_is_slave(master) ? "slave" : "master", + dev_name(&master->dev), dynamic ? " (dynamic)" : ""); /* If we're using a queued driver, start the queue */ if (master->transfer) @@ -2021,7 +2138,7 @@ static void devm_spi_unregister(struct device *dev, void *res) } /** - * dev_spi_register_master - register managed SPI master controller + * devm_spi_register_master - register managed SPI master controller * @dev: device managing SPI master * @master: initialized master, originally from spi_alloc_master() * Context: can sleep @@ -3159,6 +3276,9 @@ static struct spi_master *of_find_spi_master_by_node(struct device_node *node) dev = class_find_device(&spi_master_class, NULL, node, __spi_of_master_match); + if (!dev && IS_ENABLED(CONFIG_SPI_SLAVE)) + dev = class_find_device(&spi_slave_class, NULL, node, + __spi_of_master_match); if (!dev) return NULL; @@ -3240,6 +3360,9 @@ static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev) dev = class_find_device(&spi_master_class, NULL, adev, spi_acpi_master_match); + if (!dev && IS_ENABLED(CONFIG_SPI_SLAVE)) + dev = class_find_device(&spi_slave_class, NULL, adev, + spi_acpi_master_match); if (!dev) return NULL; @@ -3312,6 +3435,12 @@ static int __init spi_init(void) if (status < 0) goto err2; + if (IS_ENABLED(CONFIG_SPI_SLAVE)) { + status = class_register(&spi_slave_class); + if (status < 0) + goto err3; + } + if (IS_ENABLED(CONFIG_OF_DYNAMIC)) WARN_ON(of_reconfig_notifier_register(&spi_of_notifier)); if (IS_ENABLED(CONFIG_ACPI)) @@ -3319,6 +3448,8 @@ static int __init spi_init(void) return 0; +err3: + class_unregister(&spi_master_class); err2: bus_unregister(&spi_bus_type); err1: diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index 9a2a79a871ba..d4d2d8d9f3e7 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c @@ -99,7 +99,6 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); static ssize_t spidev_sync(struct spidev_data *spidev, struct spi_message *message) { - DECLARE_COMPLETION_ONSTACK(done); int status; struct spi_device *spi; @@ -325,7 +324,6 @@ static struct spi_ioc_transfer * spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, unsigned *n_ioc) { - struct spi_ioc_transfer *ioc; u32 tmp; /* Check type, command number and direction */ @@ -342,14 +340,7 @@ spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc, return NULL; /* copy into scratch area */ - ioc = kmalloc(tmp, GFP_KERNEL); - if (!ioc) - return ERR_PTR(-ENOMEM); - if (__copy_from_user(ioc, u_ioc, tmp)) { - kfree(ioc); - return ERR_PTR(-EFAULT); - } - return ioc; + return memdup_user(u_ioc, tmp); } static long diff --git a/include/linux/spi/sh_msiof.h b/include/linux/spi/sh_msiof.h index b087a85f5f72..f74b581f242f 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; diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 935bd2854ff1..0a78745e5766 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -29,8 +29,8 @@ struct spi_transfer; struct spi_flash_read_message; /* - * INTERFACES between SPI master-side drivers and SPI infrastructure. - * (There's no SPI slave support for Linux yet...) + * INTERFACES between SPI master-side drivers and SPI slave protocol handlers, + * and SPI infrastructure. */ extern struct bus_type spi_bus_type; @@ -311,6 +311,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @min_speed_hz: Lowest supported transfer speed * @max_speed_hz: Highest supported transfer speed * @flags: other constraints relevant to this driver + * @slave: indicates that this is an SPI slave controller * @max_transfer_size: function that returns the max transfer size for * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. * @max_message_size: function that returns the max message size for @@ -374,6 +375,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @handle_err: the subsystem calls the driver to handle an error that occurs * in the generic implementation of transfer_one_message(). * @unprepare_message: undo any work done by prepare_message(). + * @slave_abort: abort the ongoing transfer request on an SPI slave controller * @spi_flash_read: to support spi-controller hardwares that provide * accelerated interface to read from flash devices. * @spi_flash_can_dma: analogous to can_dma() interface, but for @@ -447,6 +449,9 @@ struct spi_master { #define SPI_MASTER_MUST_TX BIT(4) /* requires tx */ #define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */ + /* flag indicating this is an SPI slave controller */ + bool slave; + /* * on some hardware transfer / message size may be constrained * the limit may depend on device transfer settings @@ -539,6 +544,7 @@ struct spi_master { struct spi_message *message); int (*unprepare_message)(struct spi_master *master, struct spi_message *message); + int (*slave_abort)(struct spi_master *spi); int (*spi_flash_read)(struct spi_device *spi, struct spi_flash_read_message *msg); bool (*spi_flash_can_dma)(struct spi_device *spi, @@ -595,6 +601,11 @@ static inline void spi_master_put(struct spi_master *master) put_device(&master->dev); } +static inline bool spi_controller_is_slave(struct spi_master *ctlr) +{ + return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->slave; +} + /* PM calls that need to be issued by the driver */ extern int spi_master_suspend(struct spi_master *master); extern int spi_master_resume(struct spi_master *master); @@ -605,8 +616,23 @@ extern void spi_finalize_current_message(struct spi_master *master); extern void spi_finalize_current_transfer(struct spi_master *master); /* the spi driver core manages memory for the spi_master classdev */ -extern struct spi_master * -spi_alloc_master(struct device *host, unsigned size); +extern struct spi_master *__spi_alloc_controller(struct device *host, + unsigned int size, bool slave); + +static inline struct spi_master *spi_alloc_master(struct device *host, + unsigned int size) +{ + return __spi_alloc_controller(host, size, false); +} + +static inline struct spi_master *spi_alloc_slave(struct device *host, + unsigned int size) +{ + if (!IS_ENABLED(CONFIG_SPI_SLAVE)) + return NULL; + + return __spi_alloc_controller(host, size, true); +} extern int spi_register_master(struct spi_master *master); extern int devm_spi_register_master(struct device *dev, @@ -912,6 +938,7 @@ extern int spi_setup(struct spi_device *spi); extern int spi_async(struct spi_device *spi, struct spi_message *message); extern int spi_async_locked(struct spi_device *spi, struct spi_message *message); +extern int spi_slave_abort(struct spi_device *spi); static inline size_t spi_max_message_size(struct spi_device *spi) |