diff options
Diffstat (limited to 'drivers/spi')
| -rw-r--r-- | drivers/spi/Kconfig | 96 | ||||
| -rw-r--r-- | drivers/spi/Makefile | 5 | ||||
| -rw-r--r-- | drivers/spi/spi-axi-spi-engine.c | 591 | ||||
| -rw-r--r-- | drivers/spi/spi-bcm2835.c | 5 | ||||
| -rw-r--r-- | drivers/spi/spi-bcm2835aux.c | 72 | ||||
| -rw-r--r-- | drivers/spi/spi-dw-mid.c | 4 | ||||
| -rw-r--r-- | drivers/spi/spi-dw-mmio.c | 5 | ||||
| -rw-r--r-- | drivers/spi/spi-imx.c | 354 | ||||
| -rw-r--r-- | drivers/spi/spi-lp8841-rtc.c | 256 | ||||
| -rw-r--r-- | drivers/spi/spi-pl022.c | 7 | ||||
| -rw-r--r-- | drivers/spi/spi-pxa2xx-dma.c | 8 | ||||
| -rw-r--r-- | drivers/spi/spi-pxa2xx-pci.c | 13 | ||||
| -rw-r--r-- | drivers/spi/spi-pxa2xx.c | 151 | ||||
| -rw-r--r-- | drivers/spi/spi-pxa2xx.h | 37 | ||||
| -rw-r--r-- | drivers/spi/spi-rockchip.c | 71 | ||||
| -rw-r--r-- | drivers/spi/spi-ti-qspi.c | 139 | ||||
| -rw-r--r-- | drivers/spi/spi.c | 426 | 
17 files changed, 1839 insertions, 401 deletions
| diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 77064160dd76..9d8c84bb1544 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,11 +75,26 @@ config SPI_ATMEL  	  This selects a driver for the Atmel SPI Controller, present on  	  many AT32 (AVR32) and AT91 (ARM) chips. +config SPI_AU1550 +	tristate "Au1550/Au1200/Au1300 SPI Controller" +	depends on MIPS_ALCHEMY +	select SPI_BITBANG +	help +	  If you say yes to this option, support will be included for the +	  PSC SPI controller found on Au1550, Au1200 and Au1300 series. + +config SPI_AXI_SPI_ENGINE +	tristate "Analog Devices AXI SPI Engine controller" +	depends on HAS_IOMEM +	help +	  This enables support for the Analog Devices AXI SPI Engine SPI controller. +	  It is part of the SPI Engine framework that is used in some Analog Devices +	  reference designs for FPGAs. +  config SPI_BCM2835  	tristate "BCM2835 SPI controller"  	depends on GPIOLIB  	depends on ARCH_BCM2835 || COMPILE_TEST -	depends on GPIOLIB  	help  	  This selects a driver for the Broadcom BCM2835 SPI master. @@ -90,8 +105,7 @@ config SPI_BCM2835  config SPI_BCM2835AUX  	tristate "BCM2835 SPI auxiliary controller" -	depends on ARCH_BCM2835 || COMPILE_TEST -	depends on GPIOLIB +	depends on (ARCH_BCM2835 && GPIOLIB) || COMPILE_TEST  	help  	  This selects a driver for the Broadcom BCM2835 SPI aux master. @@ -118,14 +132,6 @@ config SPI_BFIN_SPORT  	help  	  Enable support for a SPI bus via the Blackfin SPORT peripheral. -config SPI_AU1550 -	tristate "Au1550/Au1200/Au1300 SPI Controller" -	depends on MIPS_ALCHEMY -	select SPI_BITBANG -	help -	  If you say yes to this option, support will be included for the -	  PSC SPI controller found on Au1550, Au1200 and Au1300 series. -  config SPI_BCM53XX  	tristate "Broadcom BCM53xx SPI controller"  	depends on ARCH_BCM_5301X @@ -197,6 +203,23 @@ config SPI_DAVINCI  	help  	  SPI master controller for DaVinci/DA8x/OMAP-L/AM1x SPI modules. +config SPI_DESIGNWARE +	tristate "DesignWare SPI controller core support" +	help +	  general driver for SPI controller core from DesignWare + +config SPI_DW_PCI +	tristate "PCI interface driver for DW SPI core" +	depends on SPI_DESIGNWARE && PCI + +config SPI_DW_MID_DMA +	bool "DMA support for DW SPI controller on Intel MID platform" +	depends on SPI_DW_PCI && DW_DMAC_PCI + +config SPI_DW_MMIO +	tristate "Memory-mapped io interface driver for DW SPI core" +	depends on SPI_DESIGNWARE +  config SPI_DLN2         tristate "Diolan DLN-2 USB SPI adapter"         depends on MFD_DLN2 @@ -271,6 +294,16 @@ config SPI_LM70_LLP  	  which interfaces to an LM70 temperature sensor using  	  a parallel port. +config SPI_LP8841_RTC +	tristate "ICP DAS LP-8841 SPI Controller for RTC" +	depends on MACH_PXA27X_DT || COMPILE_TEST +	help +	  This driver provides an SPI master device to drive Maxim +	  DS-1302 real time clock. + +	  Say N here unless you plan to run the kernel on an ICP DAS +	  LP-8x4x industrial computer. +  config SPI_MPC52xx  	tristate "Freescale MPC52xx SPI (non-PSC) controller support"  	depends on PPC_MPC52xx @@ -346,6 +379,13 @@ config SPI_MT65XX  	  say Y or M here.If you are not sure, say N.  	  SPI drivers for Mediatek MT65XX and MT81XX series ARM SoCs. +config SPI_NUC900 +	tristate "Nuvoton NUC900 series SPI" +	depends on ARCH_W90X900 +	select SPI_BITBANG +	help +	  SPI driver for Nuvoton NUC900 series ARM SoCs +  config SPI_OC_TINY  	tristate "OpenCores tiny SPI"  	depends on GPIOLIB || COMPILE_TEST @@ -415,10 +455,6 @@ config SPI_PPC4xx  	help  	  This selects a driver for the PPC4xx SPI Controller. -config SPI_PXA2XX_DMA -	def_bool y -	depends on SPI_PXA2XX -  config SPI_PXA2XX  	tristate "PXA2xx SSP SPI master"  	depends on (ARCH_PXA || PCI || ACPI) @@ -451,7 +487,7 @@ config SPI_RB4XX  config SPI_RSPI  	tristate "Renesas RSPI/QSPI controller" -	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST +	depends on SUPERH || ARCH_RENESAS || COMPILE_TEST  	help  	  SPI driver for Renesas RSPI and QSPI blocks. @@ -501,7 +537,7 @@ config SPI_SC18IS602  config SPI_SH_MSIOF  	tristate "SuperH MSIOF SPI controller"  	depends on HAVE_CLK && HAS_DMA -	depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST +	depends on SUPERH || ARCH_RENESAS || COMPILE_TEST  	help  	  SPI driver for SuperH and SH Mobile MSIOF blocks. @@ -520,7 +556,7 @@ config SPI_SH_SCI  config SPI_SH_HSPI  	tristate "SuperH HSPI controller" -	depends on ARCH_SHMOBILE || COMPILE_TEST +	depends on ARCH_RENESAS || COMPILE_TEST  	help  	  SPI driver for SuperH HSPI blocks. @@ -647,34 +683,10 @@ config SPI_ZYNQMP_GQSPI  	help  	  Enables Xilinx GQSPI controller driver for Zynq UltraScale+ MPSoC. -config SPI_NUC900 -	tristate "Nuvoton NUC900 series SPI" -	depends on ARCH_W90X900 -	select SPI_BITBANG -	help -	  SPI driver for Nuvoton NUC900 series ARM SoCs -  #  # Add new SPI master controllers in alphabetical order above this line  # -config SPI_DESIGNWARE -	tristate "DesignWare SPI controller core support" -	help -	  general driver for SPI controller core from DesignWare - -config SPI_DW_PCI -	tristate "PCI interface driver for DW SPI core" -	depends on SPI_DESIGNWARE && PCI - -config SPI_DW_MID_DMA -	bool "DMA support for DW SPI controller on Intel MID platform" -	depends on SPI_DW_PCI && DW_DMAC_PCI - -config SPI_DW_MMIO -	tristate "Memory-mapped io interface driver for DW SPI core" -	depends on SPI_DESIGNWARE -  #  # There are lots of SPI device types, with sensors and memory  # being probably the most widely used ones. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8991ffce6e12..fbb255c5a608 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SPI_ALTERA)		+= spi-altera.o  obj-$(CONFIG_SPI_ATMEL)			+= spi-atmel.o  obj-$(CONFIG_SPI_ATH79)			+= spi-ath79.o  obj-$(CONFIG_SPI_AU1550)		+= spi-au1550.o +obj-$(CONFIG_SPI_AXI_SPI_ENGINE)	+= spi-axi-spi-engine.o  obj-$(CONFIG_SPI_BCM2835)		+= spi-bcm2835.o  obj-$(CONFIG_SPI_BCM2835AUX)		+= spi-bcm2835aux.o  obj-$(CONFIG_SPI_BCM53XX)		+= spi-bcm53xx.o @@ -46,6 +47,7 @@ obj-$(CONFIG_SPI_GPIO)			+= spi-gpio.o  obj-$(CONFIG_SPI_IMG_SPFI)		+= spi-img-spfi.o  obj-$(CONFIG_SPI_IMX)			+= spi-imx.o  obj-$(CONFIG_SPI_LM70_LLP)		+= spi-lm70llp.o +obj-$(CONFIG_SPI_LP8841_RTC)		+= spi-lp8841-rtc.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 @@ -62,8 +64,7 @@ obj-$(CONFIG_SPI_TI_QSPI)		+= spi-ti-qspi.o  obj-$(CONFIG_SPI_ORION)			+= spi-orion.o  obj-$(CONFIG_SPI_PL022)			+= spi-pl022.o  obj-$(CONFIG_SPI_PPC4xx)		+= spi-ppc4xx.o -spi-pxa2xx-platform-objs		:= spi-pxa2xx.o -spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA)	+= spi-pxa2xx-dma.o +spi-pxa2xx-platform-objs		:= spi-pxa2xx.o spi-pxa2xx-dma.o  obj-$(CONFIG_SPI_PXA2XX)		+= spi-pxa2xx-platform.o  obj-$(CONFIG_SPI_PXA2XX_PCI)		+= spi-pxa2xx-pci.o  obj-$(CONFIG_SPI_QUP)			+= spi-qup.o diff --git a/drivers/spi/spi-axi-spi-engine.c b/drivers/spi/spi-axi-spi-engine.c new file mode 100644 index 000000000000..c968ab210a51 --- /dev/null +++ b/drivers/spi/spi-axi-spi-engine.c @@ -0,0 +1,591 @@ +/* + * SPI-Engine SPI controller driver + * Copyright 2015 Analog Devices Inc. + *  Author: Lars-Peter Clausen <[email protected]> + * + * Licensed under the GPL-2. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> + +#define SPI_ENGINE_VERSION_MAJOR(x)	((x >> 16) & 0xff) +#define SPI_ENGINE_VERSION_MINOR(x)	((x >> 8) & 0xff) +#define SPI_ENGINE_VERSION_PATCH(x)	(x & 0xff) + +#define SPI_ENGINE_REG_VERSION			0x00 + +#define SPI_ENGINE_REG_RESET			0x40 + +#define SPI_ENGINE_REG_INT_ENABLE		0x80 +#define SPI_ENGINE_REG_INT_PENDING		0x84 +#define SPI_ENGINE_REG_INT_SOURCE		0x88 + +#define SPI_ENGINE_REG_SYNC_ID			0xc0 + +#define SPI_ENGINE_REG_CMD_FIFO_ROOM		0xd0 +#define SPI_ENGINE_REG_SDO_FIFO_ROOM		0xd4 +#define SPI_ENGINE_REG_SDI_FIFO_LEVEL		0xd8 + +#define SPI_ENGINE_REG_CMD_FIFO			0xe0 +#define SPI_ENGINE_REG_SDO_DATA_FIFO		0xe4 +#define SPI_ENGINE_REG_SDI_DATA_FIFO		0xe8 +#define SPI_ENGINE_REG_SDI_DATA_FIFO_PEEK	0xec + +#define SPI_ENGINE_INT_CMD_ALMOST_EMPTY		BIT(0) +#define SPI_ENGINE_INT_SDO_ALMOST_EMPTY		BIT(1) +#define SPI_ENGINE_INT_SDI_ALMOST_FULL		BIT(2) +#define SPI_ENGINE_INT_SYNC			BIT(3) + +#define SPI_ENGINE_CONFIG_CPHA			BIT(0) +#define SPI_ENGINE_CONFIG_CPOL			BIT(1) +#define SPI_ENGINE_CONFIG_3WIRE			BIT(2) + +#define SPI_ENGINE_INST_TRANSFER		0x0 +#define SPI_ENGINE_INST_ASSERT			0x1 +#define SPI_ENGINE_INST_WRITE			0x2 +#define SPI_ENGINE_INST_MISC			0x3 + +#define SPI_ENGINE_CMD_REG_CLK_DIV		0x0 +#define SPI_ENGINE_CMD_REG_CONFIG		0x1 + +#define SPI_ENGINE_MISC_SYNC			0x0 +#define SPI_ENGINE_MISC_SLEEP			0x1 + +#define SPI_ENGINE_TRANSFER_WRITE		0x1 +#define SPI_ENGINE_TRANSFER_READ		0x2 + +#define SPI_ENGINE_CMD(inst, arg1, arg2) \ +	(((inst) << 12) | ((arg1) << 8) | (arg2)) + +#define SPI_ENGINE_CMD_TRANSFER(flags, n) \ +	SPI_ENGINE_CMD(SPI_ENGINE_INST_TRANSFER, (flags), (n)) +#define SPI_ENGINE_CMD_ASSERT(delay, cs) \ +	SPI_ENGINE_CMD(SPI_ENGINE_INST_ASSERT, (delay), (cs)) +#define SPI_ENGINE_CMD_WRITE(reg, val) \ +	SPI_ENGINE_CMD(SPI_ENGINE_INST_WRITE, (reg), (val)) +#define SPI_ENGINE_CMD_SLEEP(delay) \ +	SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SLEEP, (delay)) +#define SPI_ENGINE_CMD_SYNC(id) \ +	SPI_ENGINE_CMD(SPI_ENGINE_INST_MISC, SPI_ENGINE_MISC_SYNC, (id)) + +struct spi_engine_program { +	unsigned int length; +	uint16_t instructions[]; +}; + +struct spi_engine { +	struct clk *clk; +	struct clk *ref_clk; + +	spinlock_t lock; + +	void __iomem *base; + +	struct spi_message *msg; +	struct spi_engine_program *p; +	unsigned cmd_length; +	const uint16_t *cmd_buf; + +	struct spi_transfer *tx_xfer; +	unsigned int tx_length; +	const uint8_t *tx_buf; + +	struct spi_transfer *rx_xfer; +	unsigned int rx_length; +	uint8_t *rx_buf; + +	unsigned int sync_id; +	unsigned int completed_id; + +	unsigned int int_enable; +}; + +static void spi_engine_program_add_cmd(struct spi_engine_program *p, +	bool dry, uint16_t cmd) +{ +	if (!dry) +		p->instructions[p->length] = cmd; +	p->length++; +} + +static unsigned int spi_engine_get_config(struct spi_device *spi) +{ +	unsigned int config = 0; + +	if (spi->mode & SPI_CPOL) +		config |= SPI_ENGINE_CONFIG_CPOL; +	if (spi->mode & SPI_CPHA) +		config |= SPI_ENGINE_CONFIG_CPHA; +	if (spi->mode & SPI_3WIRE) +		config |= SPI_ENGINE_CONFIG_3WIRE; + +	return config; +} + +static unsigned int spi_engine_get_clk_div(struct spi_engine *spi_engine, +	struct spi_device *spi, struct spi_transfer *xfer) +{ +	unsigned int clk_div; + +	clk_div = DIV_ROUND_UP(clk_get_rate(spi_engine->ref_clk), +		xfer->speed_hz * 2); +	if (clk_div > 255) +		clk_div = 255; +	else if (clk_div > 0) +		clk_div -= 1; + +	return clk_div; +} + +static void spi_engine_gen_xfer(struct spi_engine_program *p, bool dry, +	struct spi_transfer *xfer) +{ +	unsigned int len = xfer->len; + +	while (len) { +		unsigned int n = min(len, 256U); +		unsigned int flags = 0; + +		if (xfer->tx_buf) +			flags |= SPI_ENGINE_TRANSFER_WRITE; +		if (xfer->rx_buf) +			flags |= SPI_ENGINE_TRANSFER_READ; + +		spi_engine_program_add_cmd(p, dry, +			SPI_ENGINE_CMD_TRANSFER(flags, n - 1)); +		len -= n; +	} +} + +static void spi_engine_gen_sleep(struct spi_engine_program *p, bool dry, +	struct spi_engine *spi_engine, unsigned int clk_div, unsigned int delay) +{ +	unsigned int spi_clk = clk_get_rate(spi_engine->ref_clk); +	unsigned int t; + +	if (delay == 0) +		return; + +	t = DIV_ROUND_UP(delay * spi_clk, (clk_div + 1) * 2); +	while (t) { +		unsigned int n = min(t, 256U); + +		spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_SLEEP(n - 1)); +		t -= n; +	} +} + +static void spi_engine_gen_cs(struct spi_engine_program *p, bool dry, +		struct spi_device *spi, bool assert) +{ +	unsigned int mask = 0xff; + +	if (assert) +		mask ^= BIT(spi->chip_select); + +	spi_engine_program_add_cmd(p, dry, SPI_ENGINE_CMD_ASSERT(1, mask)); +} + +static int spi_engine_compile_message(struct spi_engine *spi_engine, +	struct spi_message *msg, bool dry, struct spi_engine_program *p) +{ +	struct spi_device *spi = msg->spi; +	struct spi_transfer *xfer; +	int clk_div, new_clk_div; +	bool cs_change = true; + +	clk_div = -1; + +	spi_engine_program_add_cmd(p, dry, +		SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CONFIG, +			spi_engine_get_config(spi))); + +	list_for_each_entry(xfer, &msg->transfers, transfer_list) { +		new_clk_div = spi_engine_get_clk_div(spi_engine, spi, xfer); +		if (new_clk_div != clk_div) { +			clk_div = new_clk_div; +			spi_engine_program_add_cmd(p, dry, +				SPI_ENGINE_CMD_WRITE(SPI_ENGINE_CMD_REG_CLK_DIV, +					clk_div)); +		} + +		if (cs_change) +			spi_engine_gen_cs(p, dry, spi, true); + +		spi_engine_gen_xfer(p, dry, xfer); +		spi_engine_gen_sleep(p, dry, spi_engine, clk_div, +			xfer->delay_usecs); + +		cs_change = xfer->cs_change; +		if (list_is_last(&xfer->transfer_list, &msg->transfers)) +			cs_change = !cs_change; + +		if (cs_change) +			spi_engine_gen_cs(p, dry, spi, false); +	} + +	return 0; +} + +static void spi_engine_xfer_next(struct spi_engine *spi_engine, +	struct spi_transfer **_xfer) +{ +	struct spi_message *msg = spi_engine->msg; +	struct spi_transfer *xfer = *_xfer; + +	if (!xfer) { +		xfer = list_first_entry(&msg->transfers, +			struct spi_transfer, transfer_list); +	} else if (list_is_last(&xfer->transfer_list, &msg->transfers)) { +		xfer = NULL; +	} else { +		xfer = list_next_entry(xfer, transfer_list); +	} + +	*_xfer = xfer; +} + +static void spi_engine_tx_next(struct spi_engine *spi_engine) +{ +	struct spi_transfer *xfer = spi_engine->tx_xfer; + +	do { +		spi_engine_xfer_next(spi_engine, &xfer); +	} while (xfer && !xfer->tx_buf); + +	spi_engine->tx_xfer = xfer; +	if (xfer) { +		spi_engine->tx_length = xfer->len; +		spi_engine->tx_buf = xfer->tx_buf; +	} else { +		spi_engine->tx_buf = NULL; +	} +} + +static void spi_engine_rx_next(struct spi_engine *spi_engine) +{ +	struct spi_transfer *xfer = spi_engine->rx_xfer; + +	do { +		spi_engine_xfer_next(spi_engine, &xfer); +	} while (xfer && !xfer->rx_buf); + +	spi_engine->rx_xfer = xfer; +	if (xfer) { +		spi_engine->rx_length = xfer->len; +		spi_engine->rx_buf = xfer->rx_buf; +	} else { +		spi_engine->rx_buf = NULL; +	} +} + +static bool spi_engine_write_cmd_fifo(struct spi_engine *spi_engine) +{ +	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_CMD_FIFO; +	unsigned int n, m, i; +	const uint16_t *buf; + +	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_CMD_FIFO_ROOM); +	while (n && spi_engine->cmd_length) { +		m = min(n, spi_engine->cmd_length); +		buf = spi_engine->cmd_buf; +		for (i = 0; i < m; i++) +			writel_relaxed(buf[i], addr); +		spi_engine->cmd_buf += m; +		spi_engine->cmd_length -= m; +		n -= m; +	} + +	return spi_engine->cmd_length != 0; +} + +static bool spi_engine_write_tx_fifo(struct spi_engine *spi_engine) +{ +	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDO_DATA_FIFO; +	unsigned int n, m, i; +	const uint8_t *buf; + +	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDO_FIFO_ROOM); +	while (n && spi_engine->tx_length) { +		m = min(n, spi_engine->tx_length); +		buf = spi_engine->tx_buf; +		for (i = 0; i < m; i++) +			writel_relaxed(buf[i], addr); +		spi_engine->tx_buf += m; +		spi_engine->tx_length -= m; +		n -= m; +		if (spi_engine->tx_length == 0) +			spi_engine_tx_next(spi_engine); +	} + +	return spi_engine->tx_length != 0; +} + +static bool spi_engine_read_rx_fifo(struct spi_engine *spi_engine) +{ +	void __iomem *addr = spi_engine->base + SPI_ENGINE_REG_SDI_DATA_FIFO; +	unsigned int n, m, i; +	uint8_t *buf; + +	n = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_SDI_FIFO_LEVEL); +	while (n && spi_engine->rx_length) { +		m = min(n, spi_engine->rx_length); +		buf = spi_engine->rx_buf; +		for (i = 0; i < m; i++) +			buf[i] = readl_relaxed(addr); +		spi_engine->rx_buf += m; +		spi_engine->rx_length -= m; +		n -= m; +		if (spi_engine->rx_length == 0) +			spi_engine_rx_next(spi_engine); +	} + +	return spi_engine->rx_length != 0; +} + +static irqreturn_t spi_engine_irq(int irq, void *devid) +{ +	struct spi_master *master = devid; +	struct spi_engine *spi_engine = spi_master_get_devdata(master); +	unsigned int disable_int = 0; +	unsigned int pending; + +	pending = readl_relaxed(spi_engine->base + SPI_ENGINE_REG_INT_PENDING); + +	if (pending & SPI_ENGINE_INT_SYNC) { +		writel_relaxed(SPI_ENGINE_INT_SYNC, +			spi_engine->base + SPI_ENGINE_REG_INT_PENDING); +		spi_engine->completed_id = readl_relaxed( +			spi_engine->base + SPI_ENGINE_REG_SYNC_ID); +	} + +	spin_lock(&spi_engine->lock); + +	if (pending & SPI_ENGINE_INT_CMD_ALMOST_EMPTY) { +		if (!spi_engine_write_cmd_fifo(spi_engine)) +			disable_int |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY; +	} + +	if (pending & SPI_ENGINE_INT_SDO_ALMOST_EMPTY) { +		if (!spi_engine_write_tx_fifo(spi_engine)) +			disable_int |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY; +	} + +	if (pending & (SPI_ENGINE_INT_SDI_ALMOST_FULL | SPI_ENGINE_INT_SYNC)) { +		if (!spi_engine_read_rx_fifo(spi_engine)) +			disable_int |= SPI_ENGINE_INT_SDI_ALMOST_FULL; +	} + +	if (pending & SPI_ENGINE_INT_SYNC) { +		if (spi_engine->msg && +		    spi_engine->completed_id == spi_engine->sync_id) { +			struct spi_message *msg = spi_engine->msg; + +			kfree(spi_engine->p); +			msg->status = 0; +			msg->actual_length = msg->frame_length; +			spi_engine->msg = NULL; +			spi_finalize_current_message(master); +			disable_int |= SPI_ENGINE_INT_SYNC; +		} +	} + +	if (disable_int) { +		spi_engine->int_enable &= ~disable_int; +		writel_relaxed(spi_engine->int_enable, +			spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); +	} + +	spin_unlock(&spi_engine->lock); + +	return IRQ_HANDLED; +} + +static int spi_engine_transfer_one_message(struct spi_master *master, +	struct spi_message *msg) +{ +	struct spi_engine_program p_dry, *p; +	struct spi_engine *spi_engine = spi_master_get_devdata(master); +	unsigned int int_enable = 0; +	unsigned long flags; +	size_t size; + +	p_dry.length = 0; +	spi_engine_compile_message(spi_engine, msg, true, &p_dry); + +	size = sizeof(*p->instructions) * (p_dry.length + 1); +	p = kzalloc(sizeof(*p) + size, GFP_KERNEL); +	if (!p) +		return -ENOMEM; +	spi_engine_compile_message(spi_engine, msg, false, p); + +	spin_lock_irqsave(&spi_engine->lock, flags); +	spi_engine->sync_id = (spi_engine->sync_id + 1) & 0xff; +	spi_engine_program_add_cmd(p, false, +		SPI_ENGINE_CMD_SYNC(spi_engine->sync_id)); + +	spi_engine->msg = msg; +	spi_engine->p = p; + +	spi_engine->cmd_buf = p->instructions; +	spi_engine->cmd_length = p->length; +	if (spi_engine_write_cmd_fifo(spi_engine)) +		int_enable |= SPI_ENGINE_INT_CMD_ALMOST_EMPTY; + +	spi_engine_tx_next(spi_engine); +	if (spi_engine_write_tx_fifo(spi_engine)) +		int_enable |= SPI_ENGINE_INT_SDO_ALMOST_EMPTY; + +	spi_engine_rx_next(spi_engine); +	if (spi_engine->rx_length != 0) +		int_enable |= SPI_ENGINE_INT_SDI_ALMOST_FULL; + +	int_enable |= SPI_ENGINE_INT_SYNC; + +	writel_relaxed(int_enable, +		spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); +	spi_engine->int_enable = int_enable; +	spin_unlock_irqrestore(&spi_engine->lock, flags); + +	return 0; +} + +static int spi_engine_probe(struct platform_device *pdev) +{ +	struct spi_engine *spi_engine; +	struct spi_master *master; +	unsigned int version; +	struct resource *res; +	int irq; +	int ret; + +	irq = platform_get_irq(pdev, 0); +	if (irq <= 0) +		return -ENXIO; + +	spi_engine = devm_kzalloc(&pdev->dev, sizeof(*spi_engine), GFP_KERNEL); +	if (!spi_engine) +		return -ENOMEM; + +	master = spi_alloc_master(&pdev->dev, 0); +	if (!master) +		return -ENOMEM; + +	spi_master_set_devdata(master, spi_engine); + +	spin_lock_init(&spi_engine->lock); + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	spi_engine->base = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(spi_engine->base)) { +		ret = PTR_ERR(spi_engine->base); +		goto err_put_master; +	} + +	version = readl(spi_engine->base + SPI_ENGINE_REG_VERSION); +	if (SPI_ENGINE_VERSION_MAJOR(version) != 1) { +		dev_err(&pdev->dev, "Unsupported peripheral version %u.%u.%c\n", +			SPI_ENGINE_VERSION_MAJOR(version), +			SPI_ENGINE_VERSION_MINOR(version), +			SPI_ENGINE_VERSION_PATCH(version)); +		return -ENODEV; +	} + +	spi_engine->clk = devm_clk_get(&pdev->dev, "s_axi_aclk"); +	if (IS_ERR(spi_engine->clk)) { +		ret = PTR_ERR(spi_engine->clk); +		goto err_put_master; +	} + +	spi_engine->ref_clk = devm_clk_get(&pdev->dev, "spi_clk"); +	if (IS_ERR(spi_engine->ref_clk)) { +		ret = PTR_ERR(spi_engine->ref_clk); +		goto err_put_master; +	} + +	ret = clk_prepare_enable(spi_engine->clk); +	if (ret) +		goto err_put_master; + +	ret = clk_prepare_enable(spi_engine->ref_clk); +	if (ret) +		goto err_clk_disable; + +	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_RESET); +	writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); +	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); + +	ret = request_irq(irq, spi_engine_irq, 0, pdev->name, master); +	if (ret) +		goto err_ref_clk_disable; + +	master->dev.parent = &pdev->dev; +	master->dev.of_node = pdev->dev.of_node; +	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_3WIRE; +	master->bits_per_word_mask = SPI_BPW_MASK(8); +	master->max_speed_hz = clk_get_rate(spi_engine->ref_clk) / 2; +	master->transfer_one_message = spi_engine_transfer_one_message; +	master->num_chipselect = 8; + +	ret = spi_register_master(master); +	if (ret) +		goto err_free_irq; + +	platform_set_drvdata(pdev, master); + +	return 0; +err_free_irq: +	free_irq(irq, master); +err_ref_clk_disable: +	clk_disable_unprepare(spi_engine->ref_clk); +err_clk_disable: +	clk_disable_unprepare(spi_engine->clk); +err_put_master: +	spi_master_put(master); +	return ret; +} + +static int spi_engine_remove(struct platform_device *pdev) +{ +	struct spi_master *master = platform_get_drvdata(pdev); +	struct spi_engine *spi_engine = spi_master_get_devdata(master); +	int irq = platform_get_irq(pdev, 0); + +	spi_unregister_master(master); + +	free_irq(irq, master); + +	writel_relaxed(0xff, spi_engine->base + SPI_ENGINE_REG_INT_PENDING); +	writel_relaxed(0x00, spi_engine->base + SPI_ENGINE_REG_INT_ENABLE); +	writel_relaxed(0x01, spi_engine->base + SPI_ENGINE_REG_RESET); + +	clk_disable_unprepare(spi_engine->ref_clk); +	clk_disable_unprepare(spi_engine->clk); + +	return 0; +} + +static const struct of_device_id spi_engine_match_table[] = { +	{ .compatible = "adi,axi-spi-engine-1.00.a" }, +	{ }, +}; + +static struct platform_driver spi_engine_driver = { +	.probe = spi_engine_probe, +	.remove = spi_engine_remove, +	.driver = { +		.name = "spi-engine", +		.of_match_table = spi_engine_match_table, +	}, +}; +module_platform_driver(spi_engine_driver); + +MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>"); +MODULE_DESCRIPTION("Analog Devices SPI engine peripheral driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-bcm2835.c b/drivers/spi/spi-bcm2835.c index cf04960cc3e6..f35cc10772f6 100644 --- a/drivers/spi/spi-bcm2835.c +++ b/drivers/spi/spi-bcm2835.c @@ -727,11 +727,6 @@ static int bcm2835_spi_setup(struct spi_device *spi)  			spi->chip_select, spi->cs_gpio, err);  		return err;  	} -	/* the implementation of pinctrl-bcm2835 currently does not -	 * set the GPIO value when using gpio_direction_output -	 * so we are setting it here explicitly -	 */ -	gpio_set_value(spi->cs_gpio, (spi->mode & SPI_CS_HIGH) ? 0 : 1);  	return 0;  } diff --git a/drivers/spi/spi-bcm2835aux.c b/drivers/spi/spi-bcm2835aux.c index ecc73c0a97cf..7428091d3f5b 100644 --- a/drivers/spi/spi-bcm2835aux.c +++ b/drivers/spi/spi-bcm2835aux.c @@ -64,9 +64,9 @@  #define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH	0x00004000  #define BCM2835_AUX_SPI_CNTL0_DOUTHOLD	0x00003000  #define BCM2835_AUX_SPI_CNTL0_ENABLE	0x00000800 -#define BCM2835_AUX_SPI_CNTL0_CPHA_IN	0x00000400 +#define BCM2835_AUX_SPI_CNTL0_IN_RISING	0x00000400  #define BCM2835_AUX_SPI_CNTL0_CLEARFIFO	0x00000200 -#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT	0x00000100 +#define BCM2835_AUX_SPI_CNTL0_OUT_RISING	0x00000100  #define BCM2835_AUX_SPI_CNTL0_CPOL	0x00000080  #define BCM2835_AUX_SPI_CNTL0_MSBF_OUT	0x00000040  #define BCM2835_AUX_SPI_CNTL0_SHIFTLEN	0x0000003F @@ -92,9 +92,6 @@  #define BCM2835_AUX_SPI_POLLING_LIMIT_US	30  #define BCM2835_AUX_SPI_POLLING_JIFFIES		2 -#define BCM2835_AUX_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH \ -				  | SPI_NO_CS) -  struct bcm2835aux_spi {  	void __iomem *regs;  	struct clk *clk; @@ -212,9 +209,15 @@ static irqreturn_t bcm2835aux_spi_interrupt(int irq, void *dev_id)  		ret = IRQ_HANDLED;  	} -	/* and if rx_len is 0 then wake up completion and disable spi */ +	if (!bs->tx_len) { +		/* disable tx fifo empty interrupt */ +		bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1] | +			BCM2835_AUX_SPI_CNTL1_IDLE); +	} + +	/* and if rx_len is 0 then disable interrupts and wake up completion */  	if (!bs->rx_len) { -		bcm2835aux_spi_reset_hw(bs); +		bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]);  		complete(&master->xfer_completion);  	} @@ -307,9 +310,6 @@ static int bcm2835aux_spi_transfer_one_poll(struct spi_master *master,  		}  	} -	/* Transfer complete - reset SPI HW */ -	bcm2835aux_spi_reset_hw(bs); -  	/* and return without waiting for completion */  	return 0;  } @@ -330,10 +330,6 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,  	 * resulting (potentially) in more interrupts when transferring  	 * more than 12 bytes  	 */ -	bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE | -		      BCM2835_AUX_SPI_CNTL0_VAR_WIDTH | -		      BCM2835_AUX_SPI_CNTL0_MSBF_OUT; -	bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN;  	/* set clock */  	spi_hz = tfr->speed_hz; @@ -348,17 +344,13 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,  	} else { /* the slowest we can go */  		speed = BCM2835_AUX_SPI_CNTL0_SPEED_MAX;  	} +	/* mask out old speed from previous spi_transfer */ +	bs->cntl[0] &= ~(BCM2835_AUX_SPI_CNTL0_SPEED); +	/* set the new speed */  	bs->cntl[0] |= speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT;  	spi_used_hz = clk_hz / (2 * (speed + 1)); -	/* handle all the modes */ -	if (spi->mode & SPI_CPOL) -		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL; -	if (spi->mode & SPI_CPHA) -		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPHA_OUT | -			       BCM2835_AUX_SPI_CNTL0_CPHA_IN; -  	/* set transmit buffers and length */  	bs->tx_buf = tfr->tx_buf;  	bs->rx_buf = tfr->rx_buf; @@ -382,6 +374,40 @@ static int bcm2835aux_spi_transfer_one(struct spi_master *master,  	return bcm2835aux_spi_transfer_one_irq(master, spi, tfr);  } +static int bcm2835aux_spi_prepare_message(struct spi_master *master, +					  struct spi_message *msg) +{ +	struct spi_device *spi = msg->spi; +	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + +	bs->cntl[0] = BCM2835_AUX_SPI_CNTL0_ENABLE | +		      BCM2835_AUX_SPI_CNTL0_VAR_WIDTH | +		      BCM2835_AUX_SPI_CNTL0_MSBF_OUT; +	bs->cntl[1] = BCM2835_AUX_SPI_CNTL1_MSBF_IN; + +	/* handle all the modes */ +	if (spi->mode & SPI_CPOL) { +		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_CPOL; +		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_OUT_RISING; +	} else { +		bs->cntl[0] |= BCM2835_AUX_SPI_CNTL0_IN_RISING; +	} +	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL1, bs->cntl[1]); +	bcm2835aux_wr(bs, BCM2835_AUX_SPI_CNTL0, bs->cntl[0]); + +	return 0; +} + +static int bcm2835aux_spi_unprepare_message(struct spi_master *master, +					    struct spi_message *msg) +{ +	struct bcm2835aux_spi *bs = spi_master_get_devdata(master); + +	bcm2835aux_spi_reset_hw(bs); + +	return 0; +} +  static void bcm2835aux_spi_handle_err(struct spi_master *master,  				      struct spi_message *msg)  { @@ -405,11 +431,13 @@ static int bcm2835aux_spi_probe(struct platform_device *pdev)  	}  	platform_set_drvdata(pdev, master); -	master->mode_bits = BCM2835_AUX_SPI_MODE_BITS; +	master->mode_bits = (SPI_CPOL | SPI_CS_HIGH | SPI_NO_CS);  	master->bits_per_word_mask = SPI_BPW_MASK(8);  	master->num_chipselect = -1;  	master->transfer_one = bcm2835aux_spi_transfer_one;  	master->handle_err = bcm2835aux_spi_handle_err; +	master->prepare_message = bcm2835aux_spi_prepare_message; +	master->unprepare_message = bcm2835aux_spi_unprepare_message;  	master->dev.of_node = pdev->dev.of_node;  	bs = spi_master_get_devdata(master); diff --git a/drivers/spi/spi-dw-mid.c b/drivers/spi/spi-dw-mid.c index 9185f6c08459..e31971f91475 100644 --- a/drivers/spi/spi-dw-mid.c +++ b/drivers/spi/spi-dw-mid.c @@ -89,10 +89,10 @@ static void mid_spi_dma_exit(struct dw_spi *dws)  	if (!dws->dma_inited)  		return; -	dmaengine_terminate_all(dws->txchan); +	dmaengine_terminate_sync(dws->txchan);  	dma_release_channel(dws->txchan); -	dmaengine_terminate_all(dws->rxchan); +	dmaengine_terminate_sync(dws->rxchan);  	dma_release_channel(dws->rxchan);  } diff --git a/drivers/spi/spi-dw-mmio.c b/drivers/spi/spi-dw-mmio.c index a6d7029a85ac..447497e9124c 100644 --- a/drivers/spi/spi-dw-mmio.c +++ b/drivers/spi/spi-dw-mmio.c @@ -47,11 +47,6 @@ static int dw_spi_mmio_probe(struct platform_device *pdev)  	/* Get basic io resource and map it */  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	if (!mem) { -		dev_err(&pdev->dev, "no mem resource?\n"); -		return -EINVAL; -	} -  	dws->regs = devm_ioremap_resource(&pdev->dev, mem);  	if (IS_ERR(dws->regs)) {  		dev_err(&pdev->dev, "SPI region map failed\n"); diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 6a4ff27f4357..e7a19be87c38 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -56,7 +56,6 @@  /* The maximum  bytes that a sdma BD can transfer.*/  #define MAX_SDMA_BD_BYTES  (1 << 15) -#define IMX_DMA_TIMEOUT (msecs_to_jiffies(3000))  struct spi_imx_config {  	unsigned int speed_hz;  	unsigned int bpw; @@ -86,12 +85,18 @@ struct spi_imx_devtype_data {  struct spi_imx_data {  	struct spi_bitbang bitbang; +	struct device *dev;  	struct completion xfer_done;  	void __iomem *base; +	unsigned long base_phys; +  	struct clk *clk_per;  	struct clk *clk_ipg;  	unsigned long spi_clk; +	unsigned int spi_bus_clk; + +	unsigned int bytes_per_word;  	unsigned int count;  	void (*tx)(struct spi_imx_data *); @@ -101,8 +106,6 @@ struct spi_imx_data {  	unsigned int txfifo; /* number of words pushed in tx FIFO */  	/* DMA */ -	unsigned int dma_is_inited; -	unsigned int dma_finished;  	bool usedma;  	u32 wml;  	struct completion dma_rx_completion; @@ -199,15 +202,35 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,  	return 7;  } +static int spi_imx_bytes_per_word(const int bpw) +{ +	return DIV_ROUND_UP(bpw, BITS_PER_BYTE); +} +  static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,  			 struct spi_transfer *transfer)  {  	struct spi_imx_data *spi_imx = spi_master_get_devdata(master); +	unsigned int bpw = transfer->bits_per_word; + +	if (!master->dma_rx) +		return false; -	if (spi_imx->dma_is_inited && -	    transfer->len > spi_imx->wml * sizeof(u32)) -		return true; -	return false; +	if (!bpw) +		bpw = spi->bits_per_word; + +	bpw = spi_imx_bytes_per_word(bpw); + +	if (bpw != 1 && bpw != 2 && bpw != 4) +		return false; + +	if (transfer->len < spi_imx->wml * bpw) +		return false; + +	if (transfer->len % (spi_imx->wml * bpw)) +		return false; + +	return true;  }  #define MX51_ECSPI_CTRL		0x08 @@ -232,16 +255,13 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,  #define MX51_ECSPI_INT_RREN		(1 <<  3)  #define MX51_ECSPI_DMA      0x14 -#define MX51_ECSPI_DMA_TX_WML_OFFSET	0 -#define MX51_ECSPI_DMA_TX_WML_MASK	0x3F -#define MX51_ECSPI_DMA_RX_WML_OFFSET	16 -#define MX51_ECSPI_DMA_RX_WML_MASK	(0x3F << 16) -#define MX51_ECSPI_DMA_RXT_WML_OFFSET	24 -#define MX51_ECSPI_DMA_RXT_WML_MASK	(0x3F << 24) +#define MX51_ECSPI_DMA_TX_WML(wml)	((wml) & 0x3f) +#define MX51_ECSPI_DMA_RX_WML(wml)	(((wml) & 0x3f) << 16) +#define MX51_ECSPI_DMA_RXT_WML(wml)	(((wml) & 0x3f) << 24) -#define MX51_ECSPI_DMA_TEDEN_OFFSET	7 -#define MX51_ECSPI_DMA_RXDEN_OFFSET	23 -#define MX51_ECSPI_DMA_RXTDEN_OFFSET	31 +#define MX51_ECSPI_DMA_TEDEN		(1 << 7) +#define MX51_ECSPI_DMA_RXDEN		(1 << 23) +#define MX51_ECSPI_DMA_RXTDEN		(1 << 31)  #define MX51_ECSPI_STAT		0x18  #define MX51_ECSPI_STAT_RR		(1 <<  3) @@ -250,14 +270,15 @@ static bool spi_imx_can_dma(struct spi_master *master, struct spi_device *spi,  #define MX51_ECSPI_TESTREG_LBC	BIT(31)  /* MX51 eCSPI */ -static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi, -				      unsigned int *fres) +static unsigned int mx51_ecspi_clkdiv(struct spi_imx_data *spi_imx, +				      unsigned int fspi, unsigned int *fres)  {  	/*  	 * there are two 4-bit dividers, the pre-divider divides by  	 * $pre, the post-divider by 2^$post  	 */  	unsigned int pre, post; +	unsigned int fin = spi_imx->spi_clk;  	if (unlikely(fspi > fin))  		return 0; @@ -270,14 +291,14 @@ static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi,  	post = max(4U, post) - 4;  	if (unlikely(post > 0xf)) { -		pr_err("%s: cannot set clock freq: %u (base freq: %u)\n", -				__func__, fspi, fin); +		dev_err(spi_imx->dev, "cannot set clock freq: %u (base freq: %u)\n", +				fspi, fin);  		return 0xff;  	}  	pre = DIV_ROUND_UP(fin, fspi << post) - 1; -	pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n", +	dev_dbg(spi_imx->dev, "%s: fin: %u, fspi: %u, post: %u, pre: %u\n",  			__func__, fin, fspi, post, pre);  	/* Resulting frequency for the SCLK line. */ @@ -302,22 +323,17 @@ static void __maybe_unused mx51_ecspi_intctrl(struct spi_imx_data *spi_imx, int  static void __maybe_unused mx51_ecspi_trigger(struct spi_imx_data *spi_imx)  { -	u32 reg = readl(spi_imx->base + MX51_ECSPI_CTRL); +	u32 reg; -	if (!spi_imx->usedma) -		reg |= MX51_ECSPI_CTRL_XCH; -	else if (!spi_imx->dma_finished) -		reg |= MX51_ECSPI_CTRL_SMC; -	else -		reg &= ~MX51_ECSPI_CTRL_SMC; +	reg = readl(spi_imx->base + MX51_ECSPI_CTRL); +	reg |= MX51_ECSPI_CTRL_XCH;  	writel(reg, spi_imx->base + MX51_ECSPI_CTRL);  }  static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  		struct spi_imx_config *config)  { -	u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0, dma = 0; -	u32 tx_wml_cfg, rx_wml_cfg, rxt_wml_cfg; +	u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0;  	u32 clk = config->speed_hz, delay, reg;  	/* @@ -330,7 +346,8 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  	ctrl |= MX51_ECSPI_CTRL_MODE_MASK;  	/* set clock speed */ -	ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz, &clk); +	ctrl |= mx51_ecspi_clkdiv(spi_imx, config->speed_hz, &clk); +	spi_imx->spi_bus_clk = clk;  	/* set chip select to use */  	ctrl |= MX51_ECSPI_CTRL_CS(config->cs); @@ -341,20 +358,16 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  	if (config->mode & SPI_CPHA)  		cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs); -	else -		cfg &= ~MX51_ECSPI_CONFIG_SCLKPHA(config->cs);  	if (config->mode & SPI_CPOL) {  		cfg |= MX51_ECSPI_CONFIG_SCLKPOL(config->cs);  		cfg |= MX51_ECSPI_CONFIG_SCLKCTL(config->cs); -	} else { -		cfg &= ~MX51_ECSPI_CONFIG_SCLKPOL(config->cs); -		cfg &= ~MX51_ECSPI_CONFIG_SCLKCTL(config->cs);  	}  	if (config->mode & SPI_CS_HIGH)  		cfg |= MX51_ECSPI_CONFIG_SSBPOL(config->cs); -	else -		cfg &= ~MX51_ECSPI_CONFIG_SSBPOL(config->cs); + +	if (spi_imx->usedma) +		ctrl |= MX51_ECSPI_CTRL_SMC;  	/* CTRL register always go first to bring out controller from reset */  	writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL); @@ -389,22 +402,12 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  	 * Configure the DMA register: setup the watermark  	 * and enable DMA request.  	 */ -	if (spi_imx->dma_is_inited) { -		dma = readl(spi_imx->base + MX51_ECSPI_DMA); - -		rx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RX_WML_OFFSET; -		tx_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_TX_WML_OFFSET; -		rxt_wml_cfg = spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET; -		dma = (dma & ~MX51_ECSPI_DMA_TX_WML_MASK -			   & ~MX51_ECSPI_DMA_RX_WML_MASK -			   & ~MX51_ECSPI_DMA_RXT_WML_MASK) -			   | rx_wml_cfg | tx_wml_cfg | rxt_wml_cfg -			   |(1 << MX51_ECSPI_DMA_TEDEN_OFFSET) -			   |(1 << MX51_ECSPI_DMA_RXDEN_OFFSET) -			   |(1 << MX51_ECSPI_DMA_RXTDEN_OFFSET); - -		writel(dma, spi_imx->base + MX51_ECSPI_DMA); -	} + +	writel(MX51_ECSPI_DMA_RX_WML(spi_imx->wml) | +		MX51_ECSPI_DMA_TX_WML(spi_imx->wml) | +		MX51_ECSPI_DMA_RXT_WML(spi_imx->wml) | +		MX51_ECSPI_DMA_TEDEN | MX51_ECSPI_DMA_RXDEN | +		MX51_ECSPI_DMA_RXTDEN, spi_imx->base + MX51_ECSPI_DMA);  	return 0;  } @@ -784,11 +787,63 @@ static irqreturn_t spi_imx_isr(int irq, void *dev_id)  	return IRQ_HANDLED;  } +static int spi_imx_dma_configure(struct spi_master *master, +				 int bytes_per_word) +{ +	int ret; +	enum dma_slave_buswidth buswidth; +	struct dma_slave_config rx = {}, tx = {}; +	struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + +	if (bytes_per_word == spi_imx->bytes_per_word) +		/* Same as last time */ +		return 0; + +	switch (bytes_per_word) { +	case 4: +		buswidth = DMA_SLAVE_BUSWIDTH_4_BYTES; +		break; +	case 2: +		buswidth = DMA_SLAVE_BUSWIDTH_2_BYTES; +		break; +	case 1: +		buswidth = DMA_SLAVE_BUSWIDTH_1_BYTE; +		break; +	default: +		return -EINVAL; +	} + +	tx.direction = DMA_MEM_TO_DEV; +	tx.dst_addr = spi_imx->base_phys + MXC_CSPITXDATA; +	tx.dst_addr_width = buswidth; +	tx.dst_maxburst = spi_imx->wml; +	ret = dmaengine_slave_config(master->dma_tx, &tx); +	if (ret) { +		dev_err(spi_imx->dev, "TX dma configuration failed with %d\n", ret); +		return ret; +	} + +	rx.direction = DMA_DEV_TO_MEM; +	rx.src_addr = spi_imx->base_phys + MXC_CSPIRXDATA; +	rx.src_addr_width = buswidth; +	rx.src_maxburst = spi_imx->wml; +	ret = dmaengine_slave_config(master->dma_rx, &rx); +	if (ret) { +		dev_err(spi_imx->dev, "RX dma configuration failed with %d\n", ret); +		return ret; +	} + +	spi_imx->bytes_per_word = bytes_per_word; + +	return 0; +} +  static int spi_imx_setupxfer(struct spi_device *spi,  				 struct spi_transfer *t)  {  	struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master);  	struct spi_imx_config config; +	int ret;  	config.bpw = t ? t->bits_per_word : spi->bits_per_word;  	config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz; @@ -812,6 +867,18 @@ static int spi_imx_setupxfer(struct spi_device *spi,  		spi_imx->tx = spi_imx_buf_tx_u32;  	} +	if (spi_imx_can_dma(spi_imx->bitbang.master, spi, t)) +		spi_imx->usedma = 1; +	else +		spi_imx->usedma = 0; + +	if (spi_imx->usedma) { +		ret = spi_imx_dma_configure(spi->master, +					    spi_imx_bytes_per_word(config.bpw)); +		if (ret) +			return ret; +	} +  	spi_imx->devtype_data->config(spi_imx, &config);  	return 0; @@ -830,15 +897,11 @@ static void spi_imx_sdma_exit(struct spi_imx_data *spi_imx)  		dma_release_channel(master->dma_tx);  		master->dma_tx = NULL;  	} - -	spi_imx->dma_is_inited = 0;  }  static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx, -			     struct spi_master *master, -			     const struct resource *res) +			     struct spi_master *master)  { -	struct dma_slave_config slave_config = {};  	int ret;  	/* use pio mode for i.mx6dl chip TKT238285 */ @@ -856,16 +919,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,  		goto err;  	} -	slave_config.direction = DMA_MEM_TO_DEV; -	slave_config.dst_addr = res->start + MXC_CSPITXDATA; -	slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -	slave_config.dst_maxburst = spi_imx->wml; -	ret = dmaengine_slave_config(master->dma_tx, &slave_config); -	if (ret) { -		dev_err(dev, "error in TX dma configuration.\n"); -		goto err; -	} -  	/* Prepare for RX : */  	master->dma_rx = dma_request_slave_channel_reason(dev, "rx");  	if (IS_ERR(master->dma_rx)) { @@ -875,15 +928,7 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,  		goto err;  	} -	slave_config.direction = DMA_DEV_TO_MEM; -	slave_config.src_addr = res->start + MXC_CSPIRXDATA; -	slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; -	slave_config.src_maxburst = spi_imx->wml; -	ret = dmaengine_slave_config(master->dma_rx, &slave_config); -	if (ret) { -		dev_err(dev, "error in RX dma configuration.\n"); -		goto err; -	} +	spi_imx_dma_configure(master, 1);  	init_completion(&spi_imx->dma_rx_completion);  	init_completion(&spi_imx->dma_tx_completion); @@ -891,7 +936,6 @@ static int spi_imx_sdma_init(struct device *dev, struct spi_imx_data *spi_imx,  	master->max_dma_len = MAX_SDMA_BD_BYTES;  	spi_imx->bitbang.master->flags = SPI_MASTER_MUST_RX |  					 SPI_MASTER_MUST_TX; -	spi_imx->dma_is_inited = 1;  	return 0;  err: @@ -913,108 +957,81 @@ static void spi_imx_dma_tx_callback(void *cookie)  	complete(&spi_imx->dma_tx_completion);  } +static int spi_imx_calculate_timeout(struct spi_imx_data *spi_imx, int size) +{ +	unsigned long timeout = 0; + +	/* Time with actual data transfer and CS change delay related to HW */ +	timeout = (8 + 4) * size / spi_imx->spi_bus_clk; + +	/* Add extra second for scheduler related activities */ +	timeout += 1; + +	/* Double calculated timeout */ +	return msecs_to_jiffies(2 * timeout * MSEC_PER_SEC); +} +  static int spi_imx_dma_transfer(struct spi_imx_data *spi_imx,  				struct spi_transfer *transfer)  { -	struct dma_async_tx_descriptor *desc_tx = NULL, *desc_rx = NULL; -	int ret; +	struct dma_async_tx_descriptor *desc_tx, *desc_rx; +	unsigned long transfer_timeout;  	unsigned long timeout; -	u32 dma; -	int left;  	struct spi_master *master = spi_imx->bitbang.master;  	struct sg_table *tx = &transfer->tx_sg, *rx = &transfer->rx_sg; -	if (tx) { -		desc_tx = dmaengine_prep_slave_sg(master->dma_tx, -					tx->sgl, tx->nents, DMA_MEM_TO_DEV, -					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -		if (!desc_tx) -			goto tx_nodma; - -		desc_tx->callback = spi_imx_dma_tx_callback; -		desc_tx->callback_param = (void *)spi_imx; -		dmaengine_submit(desc_tx); -	} +	/* +	 * The TX DMA setup starts the transfer, so make sure RX is configured +	 * before TX. +	 */ +	desc_rx = dmaengine_prep_slave_sg(master->dma_rx, +				rx->sgl, rx->nents, DMA_DEV_TO_MEM, +				DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +	if (!desc_rx) +		return -EINVAL; -	if (rx) { -		desc_rx = dmaengine_prep_slave_sg(master->dma_rx, -					rx->sgl, rx->nents, DMA_DEV_TO_MEM, -					DMA_PREP_INTERRUPT | DMA_CTRL_ACK); -		if (!desc_rx) -			goto rx_nodma; +	desc_rx->callback = spi_imx_dma_rx_callback; +	desc_rx->callback_param = (void *)spi_imx; +	dmaengine_submit(desc_rx); +	reinit_completion(&spi_imx->dma_rx_completion); +	dma_async_issue_pending(master->dma_rx); -		desc_rx->callback = spi_imx_dma_rx_callback; -		desc_rx->callback_param = (void *)spi_imx; -		dmaengine_submit(desc_rx); +	desc_tx = dmaengine_prep_slave_sg(master->dma_tx, +				tx->sgl, tx->nents, DMA_MEM_TO_DEV, +				DMA_PREP_INTERRUPT | DMA_CTRL_ACK); +	if (!desc_tx) { +		dmaengine_terminate_all(master->dma_tx); +		return -EINVAL;  	} -	reinit_completion(&spi_imx->dma_rx_completion); +	desc_tx->callback = spi_imx_dma_tx_callback; +	desc_tx->callback_param = (void *)spi_imx; +	dmaengine_submit(desc_tx);  	reinit_completion(&spi_imx->dma_tx_completion); - -	/* Trigger the cspi module. */ -	spi_imx->dma_finished = 0; - -	dma = readl(spi_imx->base + MX51_ECSPI_DMA); -	dma = dma & (~MX51_ECSPI_DMA_RXT_WML_MASK); -	/* Change RX_DMA_LENGTH trigger dma fetch tail data */ -	left = transfer->len % spi_imx->wml; -	if (left) -		writel(dma | (left << MX51_ECSPI_DMA_RXT_WML_OFFSET), -				spi_imx->base + MX51_ECSPI_DMA); -	/* -	 * Set these order to avoid potential RX overflow. The overflow may -	 * happen if we enable SPI HW before starting RX DMA due to rescheduling -	 * for another task and/or interrupt. -	 * So RX DMA enabled first to make sure data would be read out from FIFO -	 * ASAP. TX DMA enabled next to start filling TX FIFO with new data. -	 * And finaly SPI HW enabled to start actual data transfer. -	 */ -	dma_async_issue_pending(master->dma_rx);  	dma_async_issue_pending(master->dma_tx); -	spi_imx->devtype_data->trigger(spi_imx); + +	transfer_timeout = spi_imx_calculate_timeout(spi_imx, transfer->len);  	/* Wait SDMA to finish the data transfer.*/  	timeout = wait_for_completion_timeout(&spi_imx->dma_tx_completion, -						IMX_DMA_TIMEOUT); +						transfer_timeout);  	if (!timeout) { -		pr_warn("%s %s: I/O Error in DMA TX\n", -			dev_driver_string(&master->dev), -			dev_name(&master->dev)); +		dev_err(spi_imx->dev, "I/O Error in DMA TX\n");  		dmaengine_terminate_all(master->dma_tx);  		dmaengine_terminate_all(master->dma_rx); -	} else { -		timeout = wait_for_completion_timeout( -				&spi_imx->dma_rx_completion, IMX_DMA_TIMEOUT); -		if (!timeout) { -			pr_warn("%s %s: I/O Error in DMA RX\n", -				dev_driver_string(&master->dev), -				dev_name(&master->dev)); -			spi_imx->devtype_data->reset(spi_imx); -			dmaengine_terminate_all(master->dma_rx); -		} -		dma &= ~MX51_ECSPI_DMA_RXT_WML_MASK; -		writel(dma | -		       spi_imx->wml << MX51_ECSPI_DMA_RXT_WML_OFFSET, -		       spi_imx->base + MX51_ECSPI_DMA); +		return -ETIMEDOUT;  	} -	spi_imx->dma_finished = 1; -	spi_imx->devtype_data->trigger(spi_imx); - -	if (!timeout) -		ret = -ETIMEDOUT; -	else -		ret = transfer->len; - -	return ret; +	timeout = wait_for_completion_timeout(&spi_imx->dma_rx_completion, +					      transfer_timeout); +	if (!timeout) { +		dev_err(&master->dev, "I/O Error in DMA RX\n"); +		spi_imx->devtype_data->reset(spi_imx); +		dmaengine_terminate_all(master->dma_rx); +		return -ETIMEDOUT; +	} -rx_nodma: -	dmaengine_terminate_all(master->dma_tx); -tx_nodma: -	pr_warn_once("%s %s: DMA not available, falling back to PIO\n", -		     dev_driver_string(&master->dev), -		     dev_name(&master->dev)); -	return -EAGAIN; +	return transfer->len;  }  static int spi_imx_pio_transfer(struct spi_device *spi, @@ -1041,19 +1058,12 @@ static int spi_imx_pio_transfer(struct spi_device *spi,  static int spi_imx_transfer(struct spi_device *spi,  				struct spi_transfer *transfer)  { -	int ret;  	struct spi_imx_data *spi_imx = spi_master_get_devdata(spi->master); -	if (spi_imx->bitbang.master->can_dma && -	    spi_imx_can_dma(spi_imx->bitbang.master, spi, transfer)) { -		spi_imx->usedma = true; -		ret = spi_imx_dma_transfer(spi_imx, transfer); -		if (ret != -EAGAIN) -			return ret; -	} -	spi_imx->usedma = false; - -	return spi_imx_pio_transfer(spi, transfer); +	if (spi_imx->usedma) +		return spi_imx_dma_transfer(spi_imx, transfer); +	else +		return spi_imx_pio_transfer(spi, transfer);  }  static int spi_imx_setup(struct spi_device *spi) @@ -1143,6 +1153,7 @@ static int spi_imx_probe(struct platform_device *pdev)  	spi_imx = spi_master_get_devdata(master);  	spi_imx->bitbang.master = master; +	spi_imx->dev = &pdev->dev;  	spi_imx->devtype_data = of_id ? of_id->data :  		(struct spi_imx_devtype_data *)pdev->id_entry->driver_data; @@ -1183,6 +1194,7 @@ static int spi_imx_probe(struct platform_device *pdev)  		ret = PTR_ERR(spi_imx->base);  		goto out_master_put;  	} +	spi_imx->base_phys = res->start;  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) { @@ -1223,7 +1235,7 @@ static int spi_imx_probe(struct platform_device *pdev)  	 * other chips.  	 */  	if (is_imx51_ecspi(spi_imx)) { -		ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master, res); +		ret = spi_imx_sdma_init(&pdev->dev, spi_imx, master);  		if (ret == -EPROBE_DEFER)  			goto out_clk_put; diff --git a/drivers/spi/spi-lp8841-rtc.c b/drivers/spi/spi-lp8841-rtc.c new file mode 100644 index 000000000000..faa577d282c0 --- /dev/null +++ b/drivers/spi/spi-lp8841-rtc.c @@ -0,0 +1,256 @@ +/* + * SPI master driver for ICP DAS LP-8841 RTC + * + * Copyright (C) 2016 Sergei Ianovich + * + * based on + * + * Dallas DS1302 RTC Support + * Copyright (C) 2002 David McCullough + * Copyright (C) 2003 - 2007 Paul Mundt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/spi/spi.h> + +#define DRIVER_NAME	"spi_lp8841_rtc" + +#define SPI_LP8841_RTC_CE	0x01 +#define SPI_LP8841_RTC_CLK	0x02 +#define SPI_LP8841_RTC_nWE	0x04 +#define SPI_LP8841_RTC_MOSI	0x08 +#define SPI_LP8841_RTC_MISO	0x01 + +/* + * REVISIT If there is support for SPI_3WIRE and SPI_LSB_FIRST in SPI + * GPIO driver, this SPI driver can be replaced by a simple GPIO driver + * providing 3 GPIO pins. + */ + +struct spi_lp8841_rtc { +	void		*iomem; +	unsigned long	state; +}; + +static inline void +setsck(struct spi_lp8841_rtc *data, int is_on) +{ +	if (is_on) +		data->state |= SPI_LP8841_RTC_CLK; +	else +		data->state &= ~SPI_LP8841_RTC_CLK; +	writeb(data->state, data->iomem); +} + +static inline void +setmosi(struct spi_lp8841_rtc *data, int is_on) +{ +	if (is_on) +		data->state |= SPI_LP8841_RTC_MOSI; +	else +		data->state &= ~SPI_LP8841_RTC_MOSI; +	writeb(data->state, data->iomem); +} + +static inline int +getmiso(struct spi_lp8841_rtc *data) +{ +	return ioread8(data->iomem) & SPI_LP8841_RTC_MISO; +} + +static inline u32 +bitbang_txrx_be_cpha0_lsb(struct spi_lp8841_rtc *data, +		unsigned usecs, unsigned cpol, unsigned flags, +		u32 word, u8 bits) +{ +	/* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ + +	u32 shift = 32 - bits; +	/* clock starts at inactive polarity */ +	for (; likely(bits); bits--) { + +		/* setup LSB (to slave) on leading edge */ +		if ((flags & SPI_MASTER_NO_TX) == 0) +			setmosi(data, (word & 1)); + +		usleep_range(usecs, usecs + 1);	/* T(setup) */ + +		/* sample LSB (from slave) on trailing edge */ +		word >>= 1; +		if ((flags & SPI_MASTER_NO_RX) == 0) +			word |= (getmiso(data) << 31); + +		setsck(data, !cpol); +		usleep_range(usecs, usecs + 1); + +		setsck(data, cpol); +	} + +	word >>= shift; +	return word; +} + +static int +spi_lp8841_rtc_transfer_one(struct spi_master *master, +			    struct spi_device *spi, +			    struct spi_transfer *t) +{ +	struct spi_lp8841_rtc	*data = spi_master_get_devdata(master); +	unsigned		count = t->len; +	const u8		*tx = t->tx_buf; +	u8			*rx = t->rx_buf; +	u8			word = 0; +	int			ret = 0; + +	if (tx) { +		data->state &= ~SPI_LP8841_RTC_nWE; +		writeb(data->state, data->iomem); +		while (likely(count > 0)) { +			word = *tx++; +			bitbang_txrx_be_cpha0_lsb(data, 1, 0, +					SPI_MASTER_NO_RX, word, 8); +			count--; +		} +	} else if (rx) { +		data->state |= SPI_LP8841_RTC_nWE; +		writeb(data->state, data->iomem); +		while (likely(count > 0)) { +			word = bitbang_txrx_be_cpha0_lsb(data, 1, 0, +					SPI_MASTER_NO_TX, word, 8); +			*rx++ = word; +			count--; +		} +	} else { +		ret = -EINVAL; +	} + +	spi_finalize_current_transfer(master); + +	return ret; +} + +static void +spi_lp8841_rtc_set_cs(struct spi_device *spi, bool enable) +{ +	struct spi_lp8841_rtc *data = spi_master_get_devdata(spi->master); + +	data->state = 0; +	writeb(data->state, data->iomem); +	if (enable) { +		usleep_range(4, 5); +		data->state |= SPI_LP8841_RTC_CE; +		writeb(data->state, data->iomem); +		usleep_range(4, 5); +	} +} + +static int +spi_lp8841_rtc_setup(struct spi_device *spi) +{ +	if ((spi->mode & SPI_CS_HIGH) == 0) { +		dev_err(&spi->dev, "unsupported active low chip select\n"); +		return -EINVAL; +	} + +	if ((spi->mode & SPI_LSB_FIRST) == 0) { +		dev_err(&spi->dev, "unsupported MSB first mode\n"); +		return -EINVAL; +	} + +	if ((spi->mode & SPI_3WIRE) == 0) { +		dev_err(&spi->dev, "unsupported wiring. 3 wires required\n"); +		return -EINVAL; +	} + +	return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id spi_lp8841_rtc_dt_ids[] = { +	{ .compatible = "icpdas,lp8841-spi-rtc" }, +	{ } +}; + +MODULE_DEVICE_TABLE(of, spi_lp8841_rtc_dt_ids); +#endif + +static int +spi_lp8841_rtc_probe(struct platform_device *pdev) +{ +	int				ret; +	struct spi_master		*master; +	struct spi_lp8841_rtc		*data; +	void				*iomem; + +	master = spi_alloc_master(&pdev->dev, sizeof(*data)); +	if (!master) +		return -ENOMEM; +	platform_set_drvdata(pdev, master); + +	master->flags = SPI_MASTER_HALF_DUPLEX; +	master->mode_bits = SPI_CS_HIGH | SPI_3WIRE | SPI_LSB_FIRST; + +	master->bus_num = pdev->id; +	master->num_chipselect = 1; +	master->setup = spi_lp8841_rtc_setup; +	master->set_cs = spi_lp8841_rtc_set_cs; +	master->transfer_one = spi_lp8841_rtc_transfer_one; +	master->bits_per_word_mask = SPI_BPW_MASK(8); +#ifdef CONFIG_OF +	master->dev.of_node = pdev->dev.of_node; +#endif + +	data = spi_master_get_devdata(master); + +	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	data->iomem = devm_ioremap_resource(&pdev->dev, iomem); +	ret = PTR_ERR_OR_ZERO(data->iomem); +	if (ret) { +		dev_err(&pdev->dev, "failed to get IO address\n"); +		goto err_put_master; +	} + +	/* register with the SPI framework */ +	ret = devm_spi_register_master(&pdev->dev, master); +	if (ret) { +		dev_err(&pdev->dev, "cannot register spi master\n"); +		goto err_put_master; +	} + +	return ret; + + +err_put_master: +	spi_master_put(master); + +	return ret; +} + +MODULE_ALIAS("platform:" DRIVER_NAME); + +static struct platform_driver spi_lp8841_rtc_driver = { +	.driver = { +		.name	= DRIVER_NAME, +		.of_match_table = of_match_ptr(spi_lp8841_rtc_dt_ids), +	}, +	.probe		= spi_lp8841_rtc_probe, +}; +module_platform_driver(spi_lp8841_rtc_driver); + +MODULE_DESCRIPTION("SPI master driver for ICP DAS LP-8841 RTC"); +MODULE_AUTHOR("Sergei Ianovich"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c index 5e5fd77e2711..f7f7ba17b40e 100644 --- a/drivers/spi/spi-pl022.c +++ b/drivers/spi/spi-pl022.c @@ -346,13 +346,6 @@ struct vendor_data {   * @clk: outgoing clock "SPICLK" for the SPI bus   * @master: SPI framework hookup   * @master_info: controller-specific data from machine setup - * @kworker: thread struct for message pump - * @kworker_task: pointer to task for message pump kworker thread - * @pump_messages: work struct for scheduling work to the message pump - * @queue_lock: spinlock to syncronise access to message queue - * @queue: message queue - * @busy: message pump is busy - * @running: message pump is running   * @pump_transfers: Tasklet used in Interrupt Transfer mode   * @cur_msg: Pointer to current spi_message being processed   * @cur_transfer: Pointer to current spi_transfer diff --git a/drivers/spi/spi-pxa2xx-dma.c b/drivers/spi/spi-pxa2xx-dma.c index bd8b369a343c..365fc22c3572 100644 --- a/drivers/spi/spi-pxa2xx-dma.c +++ b/drivers/spi/spi-pxa2xx-dma.c @@ -254,8 +254,8 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)  	if (status & SSSR_ROR) {  		dev_err(&drv_data->pdev->dev, "FIFO overrun\n"); -		dmaengine_terminate_all(drv_data->rx_chan); -		dmaengine_terminate_all(drv_data->tx_chan); +		dmaengine_terminate_async(drv_data->rx_chan); +		dmaengine_terminate_async(drv_data->tx_chan);  		pxa2xx_spi_dma_transfer_complete(drv_data, true);  		return IRQ_HANDLED; @@ -331,13 +331,13 @@ int pxa2xx_spi_dma_setup(struct driver_data *drv_data)  void pxa2xx_spi_dma_release(struct driver_data *drv_data)  {  	if (drv_data->rx_chan) { -		dmaengine_terminate_all(drv_data->rx_chan); +		dmaengine_terminate_sync(drv_data->rx_chan);  		dma_release_channel(drv_data->rx_chan);  		sg_free_table(&drv_data->rx_sgt);  		drv_data->rx_chan = NULL;  	}  	if (drv_data->tx_chan) { -		dmaengine_terminate_all(drv_data->tx_chan); +		dmaengine_terminate_sync(drv_data->tx_chan);  		dma_release_channel(drv_data->tx_chan);  		sg_free_table(&drv_data->tx_sgt);  		drv_data->tx_chan = NULL; diff --git a/drivers/spi/spi-pxa2xx-pci.c b/drivers/spi/spi-pxa2xx-pci.c index d19d7f28aecb..520ed1dd5780 100644 --- a/drivers/spi/spi-pxa2xx-pci.c +++ b/drivers/spi/spi-pxa2xx-pci.c @@ -19,6 +19,7 @@ enum {  	PORT_BSW1,  	PORT_BSW2,  	PORT_QUARK_X1000, +	PORT_LPT,  };  struct pxa_spi_info { @@ -42,6 +43,9 @@ static struct dw_dma_slave bsw1_rx_param = { .src_id = 7 };  static struct dw_dma_slave bsw2_tx_param = { .dst_id = 8 };  static struct dw_dma_slave bsw2_rx_param = { .src_id = 9 }; +static struct dw_dma_slave lpt_tx_param = { .dst_id = 0 }; +static struct dw_dma_slave lpt_rx_param = { .src_id = 1 }; +  static bool lpss_dma_filter(struct dma_chan *chan, void *param)  {  	struct dw_dma_slave *dws = param; @@ -98,6 +102,14 @@ static struct pxa_spi_info spi_info_configs[] = {  		.num_chipselect = 1,  		.max_clk_rate = 50000000,  	}, +	[PORT_LPT] = { +		.type = LPSS_LPT_SSP, +		.port_id = 0, +		.num_chipselect = 1, +		.max_clk_rate = 50000000, +		.tx_param = &lpt_tx_param, +		.rx_param = &lpt_rx_param, +	},  };  static int pxa2xx_spi_pci_probe(struct pci_dev *dev, @@ -202,6 +214,7 @@ static const struct pci_device_id pxa2xx_spi_pci_devices[] = {  	{ PCI_VDEVICE(INTEL, 0x228e), PORT_BSW0 },  	{ PCI_VDEVICE(INTEL, 0x2290), PORT_BSW1 },  	{ PCI_VDEVICE(INTEL, 0x22ac), PORT_BSW2 }, +	{ PCI_VDEVICE(INTEL, 0x9ce6), PORT_LPT },  	{ },  };  MODULE_DEVICE_TABLE(pci, pxa2xx_spi_pci_devices); diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c index ab9914ad8365..85e59a406a4c 100644 --- a/drivers/spi/spi-pxa2xx.c +++ b/drivers/spi/spi-pxa2xx.c @@ -65,8 +65,6 @@ MODULE_ALIAS("platform:pxa2xx-spi");  #define LPSS_GENERAL_REG_RXTO_HOLDOFF_DISABLE	BIT(24)  #define LPSS_CS_CONTROL_SW_MODE			BIT(0)  #define LPSS_CS_CONTROL_CS_HIGH			BIT(1) -#define LPSS_CS_CONTROL_CS_SEL_SHIFT		8 -#define LPSS_CS_CONTROL_CS_SEL_MASK		(3 << LPSS_CS_CONTROL_CS_SEL_SHIFT)  #define LPSS_CAPS_CS_EN_SHIFT			9  #define LPSS_CAPS_CS_EN_MASK			(0xf << LPSS_CAPS_CS_EN_SHIFT) @@ -82,6 +80,10 @@ struct lpss_config {  	u32 rx_threshold;  	u32 tx_threshold_lo;  	u32 tx_threshold_hi; +	/* Chip select control */ +	unsigned cs_sel_shift; +	unsigned cs_sel_mask; +	unsigned cs_num;  };  /* Keep these sorted with enum pxa_ssp_type */ @@ -106,6 +108,19 @@ static const struct lpss_config lpss_platforms[] = {  		.tx_threshold_lo = 160,  		.tx_threshold_hi = 224,  	}, +	{	/* LPSS_BSW_SSP */ +		.offset = 0x400, +		.reg_general = 0x08, +		.reg_ssp = 0x0c, +		.reg_cs_ctrl = 0x18, +		.reg_capabilities = -1, +		.rx_threshold = 64, +		.tx_threshold_lo = 160, +		.tx_threshold_hi = 224, +		.cs_sel_shift = 2, +		.cs_sel_mask = 1 << 2, +		.cs_num = 2, +	},  	{	/* LPSS_SPT_SSP */  		.offset = 0x200,  		.reg_general = -1, @@ -125,6 +140,8 @@ static const struct lpss_config lpss_platforms[] = {  		.rx_threshold = 1,  		.tx_threshold_lo = 16,  		.tx_threshold_hi = 48, +		.cs_sel_shift = 8, +		.cs_sel_mask = 3 << 8,  	},  }; @@ -139,6 +156,7 @@ static bool is_lpss_ssp(const struct driver_data *drv_data)  	switch (drv_data->ssp_type) {  	case LPSS_LPT_SSP:  	case LPSS_BYT_SSP: +	case LPSS_BSW_SSP:  	case LPSS_SPT_SSP:  	case LPSS_BXT_SSP:  		return true; @@ -288,37 +306,50 @@ static void lpss_ssp_setup(struct driver_data *drv_data)  	}  } +static void lpss_ssp_select_cs(struct driver_data *drv_data, +			       const struct lpss_config *config) +{ +	u32 value, cs; + +	if (!config->cs_sel_mask) +		return; + +	value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); + +	cs = drv_data->cur_msg->spi->chip_select; +	cs <<= config->cs_sel_shift; +	if (cs != (value & config->cs_sel_mask)) { +		/* +		 * When switching another chip select output active the +		 * output must be selected first and wait 2 ssp_clk cycles +		 * before changing state to active. Otherwise a short +		 * glitch will occur on the previous chip select since +		 * output select is latched but state control is not. +		 */ +		value &= ~config->cs_sel_mask; +		value |= cs; +		__lpss_ssp_write_priv(drv_data, +				      config->reg_cs_ctrl, value); +		ndelay(1000000000 / +		       (drv_data->master->max_speed_hz / 2)); +	} +} +  static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)  {  	const struct lpss_config *config; -	u32 value, cs; +	u32 value;  	config = lpss_get_config(drv_data); +	if (enable) +		lpss_ssp_select_cs(drv_data, config); +  	value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl); -	if (enable) { -		cs = drv_data->cur_msg->spi->chip_select; -		cs <<= LPSS_CS_CONTROL_CS_SEL_SHIFT; -		if (cs != (value & LPSS_CS_CONTROL_CS_SEL_MASK)) { -			/* -			 * When switching another chip select output active -			 * the output must be selected first and wait 2 ssp_clk -			 * cycles before changing state to active. Otherwise -			 * a short glitch will occur on the previous chip -			 * select since output select is latched but state -			 * control is not. -			 */ -			value &= ~LPSS_CS_CONTROL_CS_SEL_MASK; -			value |= cs; -			__lpss_ssp_write_priv(drv_data, -					      config->reg_cs_ctrl, value); -			ndelay(1000000000 / -			       (drv_data->master->max_speed_hz / 2)); -		} +	if (enable)  		value &= ~LPSS_CS_CONTROL_CS_HIGH; -	} else { +	else  		value |= LPSS_CS_CONTROL_CS_HIGH; -	}  	__lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);  } @@ -496,6 +527,7 @@ static void giveback(struct driver_data *drv_data)  {  	struct spi_transfer* last_transfer;  	struct spi_message *msg; +	unsigned long timeout;  	msg = drv_data->cur_msg;  	drv_data->cur_msg = NULL; @@ -508,6 +540,12 @@ static void giveback(struct driver_data *drv_data)  	if (last_transfer->delay_usecs)  		udelay(last_transfer->delay_usecs); +	/* Wait until SSP becomes idle before deasserting the CS */ +	timeout = jiffies + msecs_to_jiffies(10); +	while (pxa2xx_spi_read(drv_data, SSSR) & SSSR_BSY && +	       !time_after(jiffies, timeout)) +		cpu_relax(); +  	/* Drop chip select UNLESS cs_change is true or we are returning  	 * a message with an error, or next message is for another chip  	 */ @@ -572,7 +610,7 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)  static void int_transfer_complete(struct driver_data *drv_data)  { -	/* Stop SSP */ +	/* Clear and disable interrupts */  	write_SSSR_CS(drv_data, drv_data->clear_sr);  	reset_sccr1(drv_data);  	if (!pxa25x_ssp_comp(drv_data)) @@ -957,8 +995,6 @@ static void pump_transfers(unsigned long data)  	drv_data->tx_end = drv_data->tx + transfer->len;  	drv_data->rx = transfer->rx_buf;  	drv_data->rx_end = drv_data->rx + transfer->len; -	drv_data->rx_dma = transfer->rx_dma; -	drv_data->tx_dma = transfer->tx_dma;  	drv_data->len = transfer->len;  	drv_data->write = drv_data->tx ? chip->write : null_writer;  	drv_data->read = drv_data->rx ? chip->read : null_reader; @@ -1001,19 +1037,6 @@ static void pump_transfers(unsigned long data)  					     "pump_transfers: DMA burst size reduced to match bits_per_word\n");  	} -	/* NOTE:  PXA25x_SSP _could_ use external clocking ... */ -	cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); -	if (!pxa25x_ssp_comp(drv_data)) -		dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", -			drv_data->master->max_speed_hz -				/ (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), -			chip->enable_dma ? "DMA" : "PIO"); -	else -		dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", -			drv_data->master->max_speed_hz / 2 -				/ (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), -			chip->enable_dma ? "DMA" : "PIO"); -  	message->state = RUNNING_STATE;  	drv_data->dma_mapped = 0; @@ -1040,6 +1063,19 @@ static void pump_transfers(unsigned long data)  		write_SSSR_CS(drv_data, drv_data->clear_sr);  	} +	/* NOTE:  PXA25x_SSP _could_ use external clocking ... */ +	cr0 = pxa2xx_configure_sscr0(drv_data, clk_div, bits); +	if (!pxa25x_ssp_comp(drv_data)) +		dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", +			drv_data->master->max_speed_hz +				/ (1 + ((cr0 & SSCR0_SCR(0xfff)) >> 8)), +			drv_data->dma_mapped ? "DMA" : "PIO"); +	else +		dev_dbg(&message->spi->dev, "%u Hz actual, %s\n", +			drv_data->master->max_speed_hz / 2 +				/ (1 + ((cr0 & SSCR0_SCR(0x0ff)) >> 8)), +			drv_data->dma_mapped ? "DMA" : "PIO"); +  	if (is_lpss_ssp(drv_data)) {  		if ((pxa2xx_spi_read(drv_data, SSIRF) & 0xff)  		    != chip->lpss_rx_threshold) @@ -1166,6 +1202,7 @@ static int setup(struct spi_device *spi)  		break;  	case LPSS_LPT_SSP:  	case LPSS_BYT_SSP: +	case LPSS_BSW_SSP:  	case LPSS_SPT_SSP:  	case LPSS_BXT_SSP:  		config = lpss_get_config(drv_data); @@ -1313,7 +1350,7 @@ static const struct acpi_device_id pxa2xx_spi_acpi_match[] = {  	{ "INT3430", LPSS_LPT_SSP },  	{ "INT3431", LPSS_LPT_SSP },  	{ "80860F0E", LPSS_BYT_SSP }, -	{ "8086228E", LPSS_BYT_SSP }, +	{ "8086228E", LPSS_BSW_SSP },  	{ },  };  MODULE_DEVICE_TABLE(acpi, pxa2xx_spi_acpi_match); @@ -1347,10 +1384,14 @@ static const struct pci_device_id pxa2xx_spi_pci_compound_match[] = {  	/* SPT-H */  	{ PCI_VDEVICE(INTEL, 0xa129), LPSS_SPT_SSP },  	{ PCI_VDEVICE(INTEL, 0xa12a), LPSS_SPT_SSP }, -	/* BXT */ +	/* BXT A-Step */  	{ PCI_VDEVICE(INTEL, 0x0ac2), LPSS_BXT_SSP },  	{ PCI_VDEVICE(INTEL, 0x0ac4), LPSS_BXT_SSP },  	{ PCI_VDEVICE(INTEL, 0x0ac6), LPSS_BXT_SSP }, +	/* BXT B-Step */ +	{ PCI_VDEVICE(INTEL, 0x1ac2), LPSS_BXT_SSP }, +	{ PCI_VDEVICE(INTEL, 0x1ac4), LPSS_BXT_SSP }, +	{ PCI_VDEVICE(INTEL, 0x1ac6), LPSS_BXT_SSP },  	/* APL */  	{ PCI_VDEVICE(INTEL, 0x5ac2), LPSS_BXT_SSP },  	{ PCI_VDEVICE(INTEL, 0x5ac4), LPSS_BXT_SSP }, @@ -1438,6 +1479,29 @@ pxa2xx_spi_init_pdata(struct platform_device *pdev)  }  #endif +static int pxa2xx_spi_fw_translate_cs(struct spi_master *master, unsigned cs) +{ +	struct driver_data *drv_data = spi_master_get_devdata(master); + +	if (has_acpi_companion(&drv_data->pdev->dev)) { +		switch (drv_data->ssp_type) { +		/* +		 * For Atoms the ACPI DeviceSelection used by the Windows +		 * driver starts from 1 instead of 0 so translate it here +		 * to match what Linux expects. +		 */ +		case LPSS_BYT_SSP: +		case LPSS_BSW_SSP: +			return cs - 1; + +		default: +			break; +		} +	} + +	return cs; +} +  static int pxa2xx_spi_probe(struct platform_device *pdev)  {  	struct device *dev = &pdev->dev; @@ -1490,6 +1554,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)  	master->setup = setup;  	master->transfer_one_message = pxa2xx_spi_transfer_one_message;  	master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer; +	master->fw_translate_cs = pxa2xx_spi_fw_translate_cs;  	master->auto_runtime_pm = true;  	drv_data->ssp_type = ssp->type; @@ -1576,6 +1641,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)  			tmp &= LPSS_CAPS_CS_EN_MASK;  			tmp >>= LPSS_CAPS_CS_EN_SHIFT;  			platform_info->num_chipselect = ffz(tmp); +		} else if (config->cs_num) { +			platform_info->num_chipselect = config->cs_num;  		}  	}  	master->num_chipselect = platform_info->num_chipselect; diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h index 58efa98313aa..a1ef88948144 100644 --- a/drivers/spi/spi-pxa2xx.h +++ b/drivers/spi/spi-pxa2xx.h @@ -69,8 +69,6 @@ struct driver_data {  	void *rx;  	void *rx_end;  	int dma_mapped; -	dma_addr_t rx_dma; -	dma_addr_t tx_dma;  	size_t rx_map_len;  	size_t tx_map_len;  	u8 n_bytes; @@ -147,20 +145,9 @@ static inline void write_SSSR_CS(struct driver_data *drv_data, u32 val)  extern int pxa2xx_spi_flush(struct driver_data *drv_data);  extern void *pxa2xx_spi_next_transfer(struct driver_data *drv_data); -/* - * Select the right DMA implementation. - */ -#if defined(CONFIG_SPI_PXA2XX_DMA) -#define SPI_PXA2XX_USE_DMA	1  #define MAX_DMA_LEN		SZ_64K  #define DEFAULT_DMA_CR1		(SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL) -#else -#undef SPI_PXA2XX_USE_DMA -#define MAX_DMA_LEN		0 -#define DEFAULT_DMA_CR1		0 -#endif -#ifdef SPI_PXA2XX_USE_DMA  extern bool pxa2xx_spi_dma_is_possible(size_t len);  extern int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data);  extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data); @@ -173,29 +160,5 @@ extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,  						  u8 bits_per_word,  						  u32 *burst_code,  						  u32 *threshold); -#else -static inline bool pxa2xx_spi_dma_is_possible(size_t len) { return false; } -static inline int pxa2xx_spi_map_dma_buffers(struct driver_data *drv_data) -{ -	return 0; -} -#define pxa2xx_spi_dma_transfer NULL -static inline void pxa2xx_spi_dma_prepare(struct driver_data *drv_data, -					  u32 dma_burst) {} -static inline void pxa2xx_spi_dma_start(struct driver_data *drv_data) {} -static inline int pxa2xx_spi_dma_setup(struct driver_data *drv_data) -{ -	return 0; -} -static inline void pxa2xx_spi_dma_release(struct driver_data *drv_data) {} -static inline int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip, -							 struct spi_device *spi, -							 u8 bits_per_word, -							 u32 *burst_code, -							 u32 *threshold) -{ -	return -ENODEV; -} -#endif  #endif /* SPI_PXA2XX_H */ diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 79a8bc4f6cec..8f50a4020f6f 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -13,20 +13,14 @@   *   */ -#include <linux/init.h> -#include <linux/module.h>  #include <linux/clk.h> -#include <linux/err.h> -#include <linux/delay.h> -#include <linux/interrupt.h> +#include <linux/dmaengine.h> +#include <linux/module.h> +#include <linux/of.h>  #include <linux/platform_device.h> -#include <linux/slab.h>  #include <linux/spi/spi.h> -#include <linux/scatterlist.h> -#include <linux/of.h>  #include <linux/pm_runtime.h> -#include <linux/io.h> -#include <linux/dmaengine.h> +#include <linux/scatterlist.h>  #define DRIVER_NAME "rockchip-spi" @@ -179,7 +173,7 @@ struct rockchip_spi {  	u8 tmode;  	u8 bpw;  	u8 n_bytes; -	u8 rsd_nsecs; +	u32 rsd_nsecs;  	unsigned len;  	u32 speed; @@ -192,13 +186,12 @@ struct rockchip_spi {  	/* protect state */  	spinlock_t lock; -	struct completion xfer_completion; -  	u32 use_dma;  	struct sg_table tx_sg;  	struct sg_table rx_sg;  	struct rockchip_spi_dma_data dma_rx;  	struct rockchip_spi_dma_data dma_tx; +	struct dma_slave_caps dma_caps;  };  static inline void spi_enable_chip(struct rockchip_spi *rs, int enable) @@ -265,7 +258,10 @@ static inline u32 rx_max(struct rockchip_spi *rs)  static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)  {  	u32 ser; -	struct rockchip_spi *rs = spi_master_get_devdata(spi->master); +	struct spi_master *master = spi->master; +	struct rockchip_spi *rs = spi_master_get_devdata(master); + +	pm_runtime_get_sync(rs->dev);  	ser = readl_relaxed(rs->regs + ROCKCHIP_SPI_SER) & SER_MASK; @@ -290,6 +286,8 @@ static void rockchip_spi_set_cs(struct spi_device *spi, bool enable)  		ser &= ~(1 << spi->chip_select);  	writel_relaxed(ser, rs->regs + ROCKCHIP_SPI_SER); + +	pm_runtime_put_sync(rs->dev);  }  static int rockchip_spi_prepare_message(struct spi_master *master, @@ -319,12 +317,12 @@ static void rockchip_spi_handle_err(struct spi_master *master,  	 */  	if (rs->use_dma) {  		if (rs->state & RXBUSY) { -			dmaengine_terminate_all(rs->dma_rx.ch); +			dmaengine_terminate_async(rs->dma_rx.ch);  			flush_fifo(rs);  		}  		if (rs->state & TXBUSY) -			dmaengine_terminate_all(rs->dma_tx.ch); +			dmaengine_terminate_async(rs->dma_tx.ch);  	}  	spin_unlock_irqrestore(&rs->lock, flags); @@ -433,7 +431,7 @@ static void rockchip_spi_dma_txcb(void *data)  	spin_unlock_irqrestore(&rs->lock, flags);  } -static void rockchip_spi_prepare_dma(struct rockchip_spi *rs) +static int rockchip_spi_prepare_dma(struct rockchip_spi *rs)  {  	unsigned long flags;  	struct dma_slave_config rxconf, txconf; @@ -449,13 +447,18 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)  		rxconf.direction = rs->dma_rx.direction;  		rxconf.src_addr = rs->dma_rx.addr;  		rxconf.src_addr_width = rs->n_bytes; -		rxconf.src_maxburst = rs->n_bytes; +		if (rs->dma_caps.max_burst > 4) +			rxconf.src_maxburst = 4; +		else +			rxconf.src_maxburst = 1;  		dmaengine_slave_config(rs->dma_rx.ch, &rxconf);  		rxdesc = dmaengine_prep_slave_sg(  				rs->dma_rx.ch,  				rs->rx_sg.sgl, rs->rx_sg.nents,  				rs->dma_rx.direction, DMA_PREP_INTERRUPT); +		if (!rxdesc) +			return -EINVAL;  		rxdesc->callback = rockchip_spi_dma_rxcb;  		rxdesc->callback_param = rs; @@ -466,13 +469,21 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)  		txconf.direction = rs->dma_tx.direction;  		txconf.dst_addr = rs->dma_tx.addr;  		txconf.dst_addr_width = rs->n_bytes; -		txconf.dst_maxburst = rs->n_bytes; +		if (rs->dma_caps.max_burst > 4) +			txconf.dst_maxburst = 4; +		else +			txconf.dst_maxburst = 1;  		dmaengine_slave_config(rs->dma_tx.ch, &txconf);  		txdesc = dmaengine_prep_slave_sg(  				rs->dma_tx.ch,  				rs->tx_sg.sgl, rs->tx_sg.nents,  				rs->dma_tx.direction, DMA_PREP_INTERRUPT); +		if (!txdesc) { +			if (rxdesc) +				dmaengine_terminate_sync(rs->dma_rx.ch); +			return -EINVAL; +		}  		txdesc->callback = rockchip_spi_dma_txcb;  		txdesc->callback_param = rs; @@ -494,6 +505,8 @@ static void rockchip_spi_prepare_dma(struct rockchip_spi *rs)  		dmaengine_submit(txdesc);  		dma_async_issue_pending(rs->dma_tx.ch);  	} + +	return 0;  }  static void rockchip_spi_config(struct rockchip_spi *rs) @@ -503,7 +516,8 @@ static void rockchip_spi_config(struct rockchip_spi *rs)  	int rsd = 0;  	u32 cr0 = (CR0_BHT_8BIT << CR0_BHT_OFFSET) -		| (CR0_SSD_ONE << CR0_SSD_OFFSET); +		| (CR0_SSD_ONE << CR0_SSD_OFFSET) +		| (CR0_EM_BIG << CR0_EM_OFFSET);  	cr0 |= (rs->n_bytes << CR0_DFS_OFFSET);  	cr0 |= ((rs->mode & 0x3) << CR0_SCPH_OFFSET); @@ -606,12 +620,12 @@ static int rockchip_spi_transfer_one(  	if (rs->use_dma) {  		if (rs->tmode == CR0_XFM_RO) {  			/* rx: dma must be prepared first */ -			rockchip_spi_prepare_dma(rs); +			ret = rockchip_spi_prepare_dma(rs);  			spi_enable_chip(rs, 1);  		} else {  			/* tx or tr: spi must be enabled first */  			spi_enable_chip(rs, 1); -			rockchip_spi_prepare_dma(rs); +			ret = rockchip_spi_prepare_dma(rs);  		}  	} else {  		spi_enable_chip(rs, 1); @@ -717,8 +731,14 @@ static int rockchip_spi_probe(struct platform_device *pdev)  	master->handle_err = rockchip_spi_handle_err;  	rs->dma_tx.ch = dma_request_slave_channel(rs->dev, "tx"); -	if (!rs->dma_tx.ch) +	if (IS_ERR_OR_NULL(rs->dma_tx.ch)) { +		/* Check tx to see if we need defer probing driver */ +		if (PTR_ERR(rs->dma_tx.ch) == -EPROBE_DEFER) { +			ret = -EPROBE_DEFER; +			goto err_get_fifo_len; +		}  		dev_warn(rs->dev, "Failed to request TX DMA channel\n"); +	}  	rs->dma_rx.ch = dma_request_slave_channel(rs->dev, "rx");  	if (!rs->dma_rx.ch) { @@ -730,6 +750,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)  	}  	if (rs->dma_tx.ch && rs->dma_rx.ch) { +		dma_get_slave_caps(rs->dma_rx.ch, &(rs->dma_caps));  		rs->dma_tx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_TXDR);  		rs->dma_rx.addr = (dma_addr_t)(mem->start + ROCKCHIP_SPI_RXDR);  		rs->dma_tx.direction = DMA_MEM_TO_DEV; @@ -749,6 +770,7 @@ static int rockchip_spi_probe(struct platform_device *pdev)  	return 0;  err_register_master: +	pm_runtime_disable(&pdev->dev);  	if (rs->dma_tx.ch)  		dma_release_channel(rs->dma_tx.ch);  	if (rs->dma_rx.ch) @@ -778,6 +800,8 @@ static int rockchip_spi_remove(struct platform_device *pdev)  	if (rs->dma_rx.ch)  		dma_release_channel(rs->dma_rx.ch); +	spi_master_put(master); +  	return 0;  } @@ -868,6 +892,7 @@ static const struct of_device_id rockchip_spi_dt_match[] = {  	{ .compatible = "rockchip,rk3066-spi", },  	{ .compatible = "rockchip,rk3188-spi", },  	{ .compatible = "rockchip,rk3288-spi", }, +	{ .compatible = "rockchip,rk3399-spi", },  	{ },  };  MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match); diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 64318fcfacf2..eac3c960b2de 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c @@ -31,6 +31,8 @@  #include <linux/of.h>  #include <linux/of_device.h>  #include <linux/pinctrl/consumer.h> +#include <linux/mfd/syscon.h> +#include <linux/regmap.h>  #include <linux/spi/spi.h> @@ -44,8 +46,9 @@ struct ti_qspi {  	struct spi_master	*master;  	void __iomem            *base; -	void __iomem            *ctrl_base;  	void __iomem            *mmap_base; +	struct regmap		*ctrl_base; +	unsigned int		ctrl_reg;  	struct clk		*fclk;  	struct device           *dev; @@ -55,7 +58,7 @@ struct ti_qspi {  	u32 cmd;  	u32 dc; -	bool ctrl_mod; +	bool mmap_enabled;  };  #define QSPI_PID			(0x0) @@ -65,11 +68,8 @@ struct ti_qspi {  #define QSPI_SPI_CMD_REG		(0x48)  #define QSPI_SPI_STATUS_REG		(0x4c)  #define QSPI_SPI_DATA_REG		(0x50) -#define QSPI_SPI_SETUP0_REG		(0x54) +#define QSPI_SPI_SETUP_REG(n)		((0x54 + 4 * n))  #define QSPI_SPI_SWITCH_REG		(0x64) -#define QSPI_SPI_SETUP1_REG		(0x58) -#define QSPI_SPI_SETUP2_REG		(0x5c) -#define QSPI_SPI_SETUP3_REG		(0x60)  #define QSPI_SPI_DATA_REG_1		(0x68)  #define QSPI_SPI_DATA_REG_2		(0x6c)  #define QSPI_SPI_DATA_REG_3		(0x70) @@ -109,6 +109,17 @@ struct ti_qspi {  #define QSPI_AUTOSUSPEND_TIMEOUT         2000 +#define MEM_CS_EN(n)			((n + 1) << 8) +#define MEM_CS_MASK			(7 << 8) + +#define MM_SWITCH			0x1 + +#define QSPI_SETUP_RD_NORMAL		(0x0 << 12) +#define QSPI_SETUP_RD_DUAL		(0x1 << 12) +#define QSPI_SETUP_RD_QUAD		(0x3 << 12) +#define QSPI_SETUP_ADDR_SHIFT		8 +#define QSPI_SETUP_DUMMY_SHIFT		10 +  static inline unsigned long ti_qspi_read(struct ti_qspi *qspi,  		unsigned long reg)  { @@ -366,6 +377,72 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t)  	return 0;  } +static void ti_qspi_enable_memory_map(struct spi_device *spi) +{ +	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master); + +	ti_qspi_write(qspi, MM_SWITCH, QSPI_SPI_SWITCH_REG); +	if (qspi->ctrl_base) { +		regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg, +				   MEM_CS_EN(spi->chip_select), +				   MEM_CS_MASK); +	} +	qspi->mmap_enabled = true; +} + +static void ti_qspi_disable_memory_map(struct spi_device *spi) +{ +	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master); + +	ti_qspi_write(qspi, 0, QSPI_SPI_SWITCH_REG); +	if (qspi->ctrl_base) +		regmap_update_bits(qspi->ctrl_base, qspi->ctrl_reg, +				   0, MEM_CS_MASK); +	qspi->mmap_enabled = false; +} + +static void ti_qspi_setup_mmap_read(struct spi_device *spi, +				    struct spi_flash_read_message *msg) +{ +	struct ti_qspi  *qspi = spi_master_get_devdata(spi->master); +	u32 memval = msg->read_opcode; + +	switch (msg->data_nbits) { +	case SPI_NBITS_QUAD: +		memval |= QSPI_SETUP_RD_QUAD; +		break; +	case SPI_NBITS_DUAL: +		memval |= QSPI_SETUP_RD_DUAL; +		break; +	default: +		memval |= QSPI_SETUP_RD_NORMAL; +		break; +	} +	memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT | +		   msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT); +	ti_qspi_write(qspi, memval, +		      QSPI_SPI_SETUP_REG(spi->chip_select)); +} + +static int ti_qspi_spi_flash_read(struct  spi_device *spi, +				  struct spi_flash_read_message *msg) +{ +	struct ti_qspi *qspi = spi_master_get_devdata(spi->master); +	int ret = 0; + +	mutex_lock(&qspi->list_lock); + +	if (!qspi->mmap_enabled) +		ti_qspi_enable_memory_map(spi); +	ti_qspi_setup_mmap_read(spi, msg); +	memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); +	msg->retlen = msg->len; + +	mutex_unlock(&qspi->list_lock); + +	return ret; +} +  static int ti_qspi_start_transfer_one(struct spi_master *master,  		struct spi_message *m)  { @@ -398,6 +475,9 @@ static int ti_qspi_start_transfer_one(struct spi_master *master,  	mutex_lock(&qspi->list_lock); +	if (qspi->mmap_enabled) +		ti_qspi_disable_memory_map(spi); +  	list_for_each_entry(t, &m->transfers, transfer_list) {  		qspi->cmd |= QSPI_WLEN(t->bits_per_word); @@ -441,7 +521,7 @@ static int ti_qspi_probe(struct platform_device *pdev)  {  	struct  ti_qspi *qspi;  	struct spi_master *master; -	struct resource         *r, *res_ctrl, *res_mmap; +	struct resource         *r, *res_mmap;  	struct device_node *np = pdev->dev.of_node;  	u32 max_freq;  	int ret = 0, num_cs, irq; @@ -487,16 +567,6 @@ static int ti_qspi_probe(struct platform_device *pdev)  		}  	} -	res_ctrl = platform_get_resource_byname(pdev, -			IORESOURCE_MEM, "qspi_ctrlmod"); -	if (res_ctrl == NULL) { -		res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 2); -		if (res_ctrl == NULL) { -			dev_dbg(&pdev->dev, -				"control module resources not required\n"); -		} -	} -  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) {  		dev_err(&pdev->dev, "no irq resource?\n"); @@ -511,20 +581,31 @@ static int ti_qspi_probe(struct platform_device *pdev)  		goto free_master;  	} -	if (res_ctrl) { -		qspi->ctrl_mod = true; -		qspi->ctrl_base = devm_ioremap_resource(&pdev->dev, res_ctrl); -		if (IS_ERR(qspi->ctrl_base)) { -			ret = PTR_ERR(qspi->ctrl_base); -			goto free_master; -		} -	} -  	if (res_mmap) { -		qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap); +		qspi->mmap_base = devm_ioremap_resource(&pdev->dev, +							res_mmap); +		master->spi_flash_read = ti_qspi_spi_flash_read;  		if (IS_ERR(qspi->mmap_base)) { -			ret = PTR_ERR(qspi->mmap_base); -			goto free_master; +			dev_err(&pdev->dev, +				"falling back to PIO mode\n"); +			master->spi_flash_read = NULL; +		} +	} +	qspi->mmap_enabled = false; + +	if (of_property_read_bool(np, "syscon-chipselects")) { +		qspi->ctrl_base = +		syscon_regmap_lookup_by_phandle(np, +						"syscon-chipselects"); +		if (IS_ERR(qspi->ctrl_base)) +			return PTR_ERR(qspi->ctrl_base); +		ret = of_property_read_u32_index(np, +						 "syscon-chipselects", +						 1, &qspi->ctrl_reg); +		if (ret) { +			dev_err(&pdev->dev, +				"couldn't get ctrl_mod reg index\n"); +			return ret;  		}  	} diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 47eff8012a77..de2f2f90d799 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -144,6 +144,8 @@ SPI_STATISTICS_TRANSFER_BYTES_HISTO(14, "16384-32767");  SPI_STATISTICS_TRANSFER_BYTES_HISTO(15, "32768-65535");  SPI_STATISTICS_TRANSFER_BYTES_HISTO(16, "65536+"); +SPI_STATISTICS_SHOW(transfers_split_maxsize, "%lu"); +  static struct attribute *spi_dev_attrs[] = {  	&dev_attr_modalias.attr,  	NULL, @@ -181,6 +183,7 @@ static struct attribute *spi_device_statistics_attrs[] = {  	&dev_attr_spi_device_transfer_bytes_histo14.attr,  	&dev_attr_spi_device_transfer_bytes_histo15.attr,  	&dev_attr_spi_device_transfer_bytes_histo16.attr, +	&dev_attr_spi_device_transfers_split_maxsize.attr,  	NULL,  }; @@ -223,6 +226,7 @@ static struct attribute *spi_master_statistics_attrs[] = {  	&dev_attr_spi_master_transfer_bytes_histo14.attr,  	&dev_attr_spi_master_transfer_bytes_histo15.attr,  	&dev_attr_spi_master_transfer_bytes_histo16.attr, +	&dev_attr_spi_master_transfers_split_maxsize.attr,  	NULL,  }; @@ -702,6 +706,7 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,  		       enum dma_data_direction dir)  {  	const bool vmalloced_buf = is_vmalloc_addr(buf); +	unsigned int max_seg_size = dma_get_max_seg_size(dev);  	int desc_len;  	int sgs;  	struct page *vm_page; @@ -710,10 +715,10 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,  	int i, ret;  	if (vmalloced_buf) { -		desc_len = PAGE_SIZE; +		desc_len = min_t(int, max_seg_size, PAGE_SIZE);  		sgs = DIV_ROUND_UP(len + offset_in_page(buf), desc_len);  	} else { -		desc_len = master->max_dma_len; +		desc_len = min_t(int, max_seg_size, master->max_dma_len);  		sgs = DIV_ROUND_UP(len, desc_len);  	} @@ -739,7 +744,6 @@ static int spi_map_buf(struct spi_master *master, struct device *dev,  			sg_set_buf(&sgt->sgl[i], sg_buf, min);  		} -  		buf += min;  		len -= min;  	} @@ -1024,6 +1028,8 @@ out:  	if (msg->status && master->handle_err)  		master->handle_err(master, msg); +	spi_res_release(master, msg); +  	spi_finalize_current_message(master);  	return ret; @@ -1047,6 +1053,7 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);   * __spi_pump_messages - function which processes spi message queue   * @master: master to process queue for   * @in_kthread: true if we are in the context of the message pump thread + * @bus_locked: true if the bus mutex is held when calling this function   *   * This function checks if there is any spi message in the queue that   * needs processing and if so call out to the driver to initialize hardware @@ -1056,7 +1063,8 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer);   * inside spi_sync(); the queue extraction handling at the top of the   * function should deal with this safely.   */ -static void __spi_pump_messages(struct spi_master *master, bool in_kthread) +static void __spi_pump_messages(struct spi_master *master, bool in_kthread, +				bool bus_locked)  {  	unsigned long flags;  	bool was_busy = false; @@ -1152,6 +1160,9 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)  		}  	} +	if (!bus_locked) +		mutex_lock(&master->bus_lock_mutex); +  	trace_spi_message_start(master->cur_msg);  	if (master->prepare_message) { @@ -1161,7 +1172,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)  				"failed to prepare message: %d\n", ret);  			master->cur_msg->status = ret;  			spi_finalize_current_message(master); -			return; +			goto out;  		}  		master->cur_msg_prepared = true;  	} @@ -1170,15 +1181,23 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread)  	if (ret) {  		master->cur_msg->status = ret;  		spi_finalize_current_message(master); -		return; +		goto out;  	}  	ret = master->transfer_one_message(master, master->cur_msg);  	if (ret) {  		dev_err(&master->dev,  			"failed to transfer one message from queue\n"); -		return; +		goto out;  	} + +out: +	if (!bus_locked) +		mutex_unlock(&master->bus_lock_mutex); + +	/* Prod the scheduler in case transfer_one() was busy waiting */ +	if (!ret) +		cond_resched();  }  /** @@ -1190,7 +1209,7 @@ static void spi_pump_messages(struct kthread_work *work)  	struct spi_master *master =  		container_of(work, struct spi_master, pump_messages); -	__spi_pump_messages(master, true); +	__spi_pump_messages(master, true, false);  }  static int spi_init_queue(struct spi_master *master) @@ -1581,13 +1600,30 @@ static void of_register_spi_devices(struct spi_master *master) { }  static int acpi_spi_add_resource(struct acpi_resource *ares, void *data)  {  	struct spi_device *spi = data; +	struct spi_master *master = spi->master;  	if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {  		struct acpi_resource_spi_serialbus *sb;  		sb = &ares->data.spi_serial_bus;  		if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_SPI) { -			spi->chip_select = sb->device_selection; +			/* +			 * ACPI DeviceSelection numbering is handled by the +			 * host controller driver in Windows and can vary +			 * from driver to driver. In Linux we always expect +			 * 0 .. max - 1 so we need to ask the driver to +			 * translate between the two schemes. +			 */ +			if (master->fw_translate_cs) { +				int cs = master->fw_translate_cs(master, +						sb->device_selection); +				if (cs < 0) +					return cs; +				spi->chip_select = cs; +			} else { +				spi->chip_select = sb->device_selection; +			} +  			spi->max_speed_hz = sb->connection_speed;  			if (sb->clock_phase == ACPI_SPI_SECOND_PHASE) @@ -2013,6 +2049,336 @@ struct spi_master *spi_busnum_to_master(u16 bus_num)  }  EXPORT_SYMBOL_GPL(spi_busnum_to_master); +/*-------------------------------------------------------------------------*/ + +/* Core methods for SPI resource management */ + +/** + * spi_res_alloc - allocate a spi resource that is life-cycle managed + *                 during the processing of a spi_message while using + *                 spi_transfer_one + * @spi:     the spi device for which we allocate memory + * @release: the release code to execute for this resource + * @size:    size to alloc and return + * @gfp:     GFP allocation flags + * + * Return: the pointer to the allocated data + * + * This may get enhanced in the future to allocate from a memory pool + * of the @spi_device or @spi_master to avoid repeated allocations. + */ +void *spi_res_alloc(struct spi_device *spi, +		    spi_res_release_t release, +		    size_t size, gfp_t gfp) +{ +	struct spi_res *sres; + +	sres = kzalloc(sizeof(*sres) + size, gfp); +	if (!sres) +		return NULL; + +	INIT_LIST_HEAD(&sres->entry); +	sres->release = release; + +	return sres->data; +} +EXPORT_SYMBOL_GPL(spi_res_alloc); + +/** + * spi_res_free - free an spi resource + * @res: pointer to the custom data of a resource + * + */ +void spi_res_free(void *res) +{ +	struct spi_res *sres = container_of(res, struct spi_res, data); + +	if (!res) +		return; + +	WARN_ON(!list_empty(&sres->entry)); +	kfree(sres); +} +EXPORT_SYMBOL_GPL(spi_res_free); + +/** + * spi_res_add - add a spi_res to the spi_message + * @message: the spi message + * @res:     the spi_resource + */ +void spi_res_add(struct spi_message *message, void *res) +{ +	struct spi_res *sres = container_of(res, struct spi_res, data); + +	WARN_ON(!list_empty(&sres->entry)); +	list_add_tail(&sres->entry, &message->resources); +} +EXPORT_SYMBOL_GPL(spi_res_add); + +/** + * spi_res_release - release all spi resources for this message + * @master:  the @spi_master + * @message: the @spi_message + */ +void spi_res_release(struct spi_master *master, +		     struct spi_message *message) +{ +	struct spi_res *res; + +	while (!list_empty(&message->resources)) { +		res = list_last_entry(&message->resources, +				      struct spi_res, entry); + +		if (res->release) +			res->release(master, message, res->data); + +		list_del(&res->entry); + +		kfree(res); +	} +} +EXPORT_SYMBOL_GPL(spi_res_release); + +/*-------------------------------------------------------------------------*/ + +/* Core methods for spi_message alterations */ + +static void __spi_replace_transfers_release(struct spi_master *master, +					    struct spi_message *msg, +					    void *res) +{ +	struct spi_replaced_transfers *rxfer = res; +	size_t i; + +	/* call extra callback if requested */ +	if (rxfer->release) +		rxfer->release(master, msg, res); + +	/* insert replaced transfers back into the message */ +	list_splice(&rxfer->replaced_transfers, rxfer->replaced_after); + +	/* remove the formerly inserted entries */ +	for (i = 0; i < rxfer->inserted; i++) +		list_del(&rxfer->inserted_transfers[i].transfer_list); +} + +/** + * spi_replace_transfers - replace transfers with several transfers + *                         and register change with spi_message.resources + * @msg:           the spi_message we work upon + * @xfer_first:    the first spi_transfer we want to replace + * @remove:        number of transfers to remove + * @insert:        the number of transfers we want to insert instead + * @release:       extra release code necessary in some circumstances + * @extradatasize: extra data to allocate (with alignment guarantees + *                 of struct @spi_transfer) + * @gfp:           gfp flags + * + * Returns: pointer to @spi_replaced_transfers, + *          PTR_ERR(...) in case of errors. + */ +struct spi_replaced_transfers *spi_replace_transfers( +	struct spi_message *msg, +	struct spi_transfer *xfer_first, +	size_t remove, +	size_t insert, +	spi_replaced_release_t release, +	size_t extradatasize, +	gfp_t gfp) +{ +	struct spi_replaced_transfers *rxfer; +	struct spi_transfer *xfer; +	size_t i; + +	/* allocate the structure using spi_res */ +	rxfer = spi_res_alloc(msg->spi, __spi_replace_transfers_release, +			      insert * sizeof(struct spi_transfer) +			      + sizeof(struct spi_replaced_transfers) +			      + extradatasize, +			      gfp); +	if (!rxfer) +		return ERR_PTR(-ENOMEM); + +	/* the release code to invoke before running the generic release */ +	rxfer->release = release; + +	/* assign extradata */ +	if (extradatasize) +		rxfer->extradata = +			&rxfer->inserted_transfers[insert]; + +	/* init the replaced_transfers list */ +	INIT_LIST_HEAD(&rxfer->replaced_transfers); + +	/* assign the list_entry after which we should reinsert +	 * the @replaced_transfers - it may be spi_message.messages! +	 */ +	rxfer->replaced_after = xfer_first->transfer_list.prev; + +	/* remove the requested number of transfers */ +	for (i = 0; i < remove; i++) { +		/* if the entry after replaced_after it is msg->transfers +		 * then we have been requested to remove more transfers +		 * than are in the list +		 */ +		if (rxfer->replaced_after->next == &msg->transfers) { +			dev_err(&msg->spi->dev, +				"requested to remove more spi_transfers than are available\n"); +			/* insert replaced transfers back into the message */ +			list_splice(&rxfer->replaced_transfers, +				    rxfer->replaced_after); + +			/* free the spi_replace_transfer structure */ +			spi_res_free(rxfer); + +			/* and return with an error */ +			return ERR_PTR(-EINVAL); +		} + +		/* remove the entry after replaced_after from list of +		 * transfers and add it to list of replaced_transfers +		 */ +		list_move_tail(rxfer->replaced_after->next, +			       &rxfer->replaced_transfers); +	} + +	/* create copy of the given xfer with identical settings +	 * based on the first transfer to get removed +	 */ +	for (i = 0; i < insert; i++) { +		/* we need to run in reverse order */ +		xfer = &rxfer->inserted_transfers[insert - 1 - i]; + +		/* copy all spi_transfer data */ +		memcpy(xfer, xfer_first, sizeof(*xfer)); + +		/* add to list */ +		list_add(&xfer->transfer_list, rxfer->replaced_after); + +		/* clear cs_change and delay_usecs for all but the last */ +		if (i) { +			xfer->cs_change = false; +			xfer->delay_usecs = 0; +		} +	} + +	/* set up inserted */ +	rxfer->inserted = insert; + +	/* and register it with spi_res/spi_message */ +	spi_res_add(msg, rxfer); + +	return rxfer; +} +EXPORT_SYMBOL_GPL(spi_replace_transfers); + +static int __spi_split_transfer_maxsize(struct spi_master *master, +					struct spi_message *msg, +					struct spi_transfer **xferp, +					size_t maxsize, +					gfp_t gfp) +{ +	struct spi_transfer *xfer = *xferp, *xfers; +	struct spi_replaced_transfers *srt; +	size_t offset; +	size_t count, i; + +	/* warn once about this fact that we are splitting a transfer */ +	dev_warn_once(&msg->spi->dev, +		      "spi_transfer of length %i exceed max length of %zu - needed to split transfers\n", +		      xfer->len, maxsize); + +	/* calculate how many we have to replace */ +	count = DIV_ROUND_UP(xfer->len, maxsize); + +	/* create replacement */ +	srt = spi_replace_transfers(msg, xfer, 1, count, NULL, 0, gfp); +	if (IS_ERR(srt)) +		return PTR_ERR(srt); +	xfers = srt->inserted_transfers; + +	/* now handle each of those newly inserted spi_transfers +	 * note that the replacements spi_transfers all are preset +	 * to the same values as *xferp, so tx_buf, rx_buf and len +	 * are all identical (as well as most others) +	 * so we just have to fix up len and the pointers. +	 * +	 * this also includes support for the depreciated +	 * spi_message.is_dma_mapped interface +	 */ + +	/* the first transfer just needs the length modified, so we +	 * run it outside the loop +	 */ +	xfers[0].len = min_t(size_t, maxsize, xfer[0].len); + +	/* all the others need rx_buf/tx_buf also set */ +	for (i = 1, offset = maxsize; i < count; offset += maxsize, i++) { +		/* update rx_buf, tx_buf and dma */ +		if (xfers[i].rx_buf) +			xfers[i].rx_buf += offset; +		if (xfers[i].rx_dma) +			xfers[i].rx_dma += offset; +		if (xfers[i].tx_buf) +			xfers[i].tx_buf += offset; +		if (xfers[i].tx_dma) +			xfers[i].tx_dma += offset; + +		/* update length */ +		xfers[i].len = min(maxsize, xfers[i].len - offset); +	} + +	/* we set up xferp to the last entry we have inserted, +	 * so that we skip those already split transfers +	 */ +	*xferp = &xfers[count - 1]; + +	/* increment statistics counters */ +	SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, +				       transfers_split_maxsize); +	SPI_STATISTICS_INCREMENT_FIELD(&msg->spi->statistics, +				       transfers_split_maxsize); + +	return 0; +} + +/** + * spi_split_tranfers_maxsize - split spi transfers into multiple transfers + *                              when an individual transfer exceeds a + *                              certain size + * @master:    the @spi_master for this transfer + * @msg:   the @spi_message to transform + * @maxsize:  the maximum when to apply this + * @gfp: GFP allocation flags + * + * Return: status of transformation + */ +int spi_split_transfers_maxsize(struct spi_master *master, +				struct spi_message *msg, +				size_t maxsize, +				gfp_t gfp) +{ +	struct spi_transfer *xfer; +	int ret; + +	/* iterate over the transfer_list, +	 * but note that xfer is advanced to the last transfer inserted +	 * to avoid checking sizes again unnecessarily (also xfer does +	 * potentiall belong to a different list by the time the +	 * replacement has happened +	 */ +	list_for_each_entry(xfer, &msg->transfers, transfer_list) { +		if (xfer->len > maxsize) { +			ret = __spi_split_transfer_maxsize( +				master, msg, &xfer, maxsize, gfp); +			if (ret) +				return ret; +		} +	} + +	return 0; +} +EXPORT_SYMBOL_GPL(spi_split_transfers_maxsize);  /*-------------------------------------------------------------------------*/ @@ -2351,6 +2717,46 @@ int spi_async_locked(struct spi_device *spi, struct spi_message *message)  EXPORT_SYMBOL_GPL(spi_async_locked); +int spi_flash_read(struct spi_device *spi, +		   struct spi_flash_read_message *msg) + +{ +	struct spi_master *master = spi->master; +	int ret; + +	if ((msg->opcode_nbits == SPI_NBITS_DUAL || +	     msg->addr_nbits == SPI_NBITS_DUAL) && +	    !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD))) +		return -EINVAL; +	if ((msg->opcode_nbits == SPI_NBITS_QUAD || +	     msg->addr_nbits == SPI_NBITS_QUAD) && +	    !(spi->mode & SPI_TX_QUAD)) +		return -EINVAL; +	if (msg->data_nbits == SPI_NBITS_DUAL && +	    !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD))) +		return -EINVAL; +	if (msg->data_nbits == SPI_NBITS_QUAD && +	    !(spi->mode &  SPI_RX_QUAD)) +		return -EINVAL; + +	if (master->auto_runtime_pm) { +		ret = pm_runtime_get_sync(master->dev.parent); +		if (ret < 0) { +			dev_err(&master->dev, "Failed to power device: %d\n", +				ret); +			return ret; +		} +	} +	mutex_lock(&master->bus_lock_mutex); +	ret = master->spi_flash_read(spi, msg); +	mutex_unlock(&master->bus_lock_mutex); +	if (master->auto_runtime_pm) +		pm_runtime_put(master->dev.parent); + +	return ret; +} +EXPORT_SYMBOL_GPL(spi_flash_read); +  /*-------------------------------------------------------------------------*/  /* Utility methods for SPI master protocol drivers, layered on @@ -2414,7 +2820,7 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message,  						       spi_sync_immediate);  			SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics,  						       spi_sync_immediate); -			__spi_pump_messages(master, false); +			__spi_pump_messages(master, false, bus_locked);  		}  		wait_for_completion(&done); |