diff options
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spi/Makefile | 1 | ||||
-rw-r--r-- | drivers/spi/spi-cavium-octeon.c | 104 | ||||
-rw-r--r-- | drivers/spi/spi-cavium.c | 151 | ||||
-rw-r--r-- | drivers/spi/spi-cavium.h | 329 | ||||
-rw-r--r-- | drivers/spi/spi-octeon.c | 255 | ||||
-rw-r--r-- | drivers/spi/spi-omap2-mcspi.c | 145 | ||||
-rw-r--r-- | drivers/spi/spi-orion.c | 88 | ||||
-rw-r--r-- | drivers/spi/spi-pic32-sqi.c | 7 | ||||
-rw-r--r-- | drivers/spi/spi-pic32.c | 5 |
10 files changed, 750 insertions, 336 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b931ec8d90b..d6fb8d4b7786 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -411,6 +411,7 @@ config SPI_OMAP24XX tristate "McSPI driver for OMAP" depends on HAS_DMA depends on ARCH_OMAP2PLUS || COMPILE_TEST + select SG_SPLIT help SPI master controller for OMAP24XX and later Multichannel SPI (McSPI) modules. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3c74d003535b..185367ef6576 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_SPI_MT65XX) += spi-mt65xx.o obj-$(CONFIG_SPI_MXS) += spi-mxs.o obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o +spi-octeon-objs := spi-cavium.o spi-cavium-octeon.o obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o diff --git a/drivers/spi/spi-cavium-octeon.c b/drivers/spi/spi-cavium-octeon.c new file mode 100644 index 000000000000..ee4703e84622 --- /dev/null +++ b/drivers/spi/spi-cavium-octeon.c @@ -0,0 +1,104 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/of.h> + +#include <asm/octeon/octeon.h> + +#include "spi-cavium.h" + +static int octeon_spi_probe(struct platform_device *pdev) +{ + struct resource *res_mem; + void __iomem *reg_base; + struct spi_master *master; + struct octeon_spi *p; + int err = -ENOENT; + + master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); + if (!master) + return -ENOMEM; + p = spi_master_get_devdata(master); + platform_set_drvdata(pdev, master); + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + reg_base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(reg_base)) { + err = PTR_ERR(reg_base); + goto fail; + } + + p->register_base = reg_base; + p->sys_freq = octeon_get_io_clock_rate(); + + p->regs.config = 0; + p->regs.status = 0x08; + p->regs.tx = 0x10; + p->regs.data = 0x80; + + master->num_chipselect = 4; + master->mode_bits = SPI_CPHA | + SPI_CPOL | + SPI_CS_HIGH | + SPI_LSB_FIRST | + SPI_3WIRE; + + master->transfer_one_message = octeon_spi_transfer_one_message; + master->bits_per_word_mask = SPI_BPW_MASK(8); + master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; + + master->dev.of_node = pdev->dev.of_node; + err = devm_spi_register_master(&pdev->dev, master); + if (err) { + dev_err(&pdev->dev, "register master failed: %d\n", err); + goto fail; + } + + dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); + + return 0; +fail: + spi_master_put(master); + return err; +} + +static int octeon_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct octeon_spi *p = spi_master_get_devdata(master); + + /* Clear the CSENA* and put everything in a known state. */ + writeq(0, p->register_base + OCTEON_SPI_CFG(p)); + + return 0; +} + +static const struct of_device_id octeon_spi_match[] = { + { .compatible = "cavium,octeon-3010-spi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_spi_match); + +static struct platform_driver octeon_spi_driver = { + .driver = { + .name = "spi-octeon", + .of_match_table = octeon_spi_match, + }, + .probe = octeon_spi_probe, + .remove = octeon_spi_remove, +}; + +module_platform_driver(octeon_spi_driver); + +MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); +MODULE_AUTHOR("David Daney"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-cavium.c b/drivers/spi/spi-cavium.c new file mode 100644 index 000000000000..5aaf21582cb5 --- /dev/null +++ b/drivers/spi/spi-cavium.c @@ -0,0 +1,151 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2011, 2012 Cavium, Inc. + */ + +#include <linux/spi/spi.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include "spi-cavium.h" + +static void octeon_spi_wait_ready(struct octeon_spi *p) +{ + union cvmx_mpi_sts mpi_sts; + unsigned int loops = 0; + + do { + if (loops++) + __delay(500); + mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); + } while (mpi_sts.s.busy); +} + +static int octeon_spi_do_transfer(struct octeon_spi *p, + struct spi_message *msg, + struct spi_transfer *xfer, + bool last_xfer) +{ + struct spi_device *spi = msg->spi; + union cvmx_mpi_cfg mpi_cfg; + union cvmx_mpi_tx mpi_tx; + unsigned int clkdiv; + int mode; + bool cpha, cpol; + const u8 *tx_buf; + u8 *rx_buf; + int len; + int i; + + mode = spi->mode; + cpha = mode & SPI_CPHA; + cpol = mode & SPI_CPOL; + + clkdiv = p->sys_freq / (2 * xfer->speed_hz); + + mpi_cfg.u64 = 0; + + mpi_cfg.s.clkdiv = clkdiv; + mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; + mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; + mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; + mpi_cfg.s.idlelo = cpha != cpol; + mpi_cfg.s.cslate = cpha ? 1 : 0; + mpi_cfg.s.enable = 1; + + if (spi->chip_select < 4) + p->cs_enax |= 1ull << (12 + spi->chip_select); + mpi_cfg.u64 |= p->cs_enax; + + if (mpi_cfg.u64 != p->last_cfg) { + p->last_cfg = mpi_cfg.u64; + writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); + } + tx_buf = xfer->tx_buf; + rx_buf = xfer->rx_buf; + len = xfer->len; + while (len > OCTEON_SPI_MAX_BYTES) { + for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { + u8 d; + if (tx_buf) + d = *tx_buf++; + else + d = 0; + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + } + mpi_tx.u64 = 0; + mpi_tx.s.csid = spi->chip_select; + mpi_tx.s.leavecs = 1; + mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; + mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); + + octeon_spi_wait_ready(p); + if (rx_buf) + for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + *rx_buf++ = (u8)v; + } + len -= OCTEON_SPI_MAX_BYTES; + } + + for (i = 0; i < len; i++) { + u8 d; + if (tx_buf) + d = *tx_buf++; + else + d = 0; + writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + } + + mpi_tx.u64 = 0; + mpi_tx.s.csid = spi->chip_select; + if (last_xfer) + mpi_tx.s.leavecs = xfer->cs_change; + else + mpi_tx.s.leavecs = !xfer->cs_change; + mpi_tx.s.txnum = tx_buf ? len : 0; + mpi_tx.s.totnum = len; + writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); + + octeon_spi_wait_ready(p); + if (rx_buf) + for (i = 0; i < len; i++) { + u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); + *rx_buf++ = (u8)v; + } + + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + return xfer->len; +} + +int octeon_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg) +{ + struct octeon_spi *p = spi_master_get_devdata(master); + unsigned int total_len = 0; + int status = 0; + struct spi_transfer *xfer; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + bool last_xfer = list_is_last(&xfer->transfer_list, + &msg->transfers); + int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); + if (r < 0) { + status = r; + goto err; + } + total_len += r; + } +err: + msg->status = status; + msg->actual_length = total_len; + spi_finalize_current_message(master); + return status; +} diff --git a/drivers/spi/spi-cavium.h b/drivers/spi/spi-cavium.h new file mode 100644 index 000000000000..88c5f36e7ea7 --- /dev/null +++ b/drivers/spi/spi-cavium.h @@ -0,0 +1,329 @@ +#ifndef __SPI_CAVIUM_H +#define __SPI_CAVIUM_H + +#define OCTEON_SPI_MAX_BYTES 9 +#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 + +struct octeon_spi_regs { + int config; + int status; + int tx; + int data; +}; + +struct octeon_spi { + void __iomem *register_base; + u64 last_cfg; + u64 cs_enax; + int sys_freq; + struct octeon_spi_regs regs; +}; + +#define OCTEON_SPI_CFG(x) (x->regs.config) +#define OCTEON_SPI_STS(x) (x->regs.status) +#define OCTEON_SPI_TX(x) (x->regs.tx) +#define OCTEON_SPI_DAT0(x) (x->regs.data) + +int octeon_spi_transfer_one_message(struct spi_master *master, + struct spi_message *msg); + +/* MPI register descriptions */ + +#define CVMX_MPI_CFG (CVMX_ADD_IO_SEG(0x0001070000001000ull)) +#define CVMX_MPI_DATX(offset) (CVMX_ADD_IO_SEG(0x0001070000001080ull) + ((offset) & 15) * 8) +#define CVMX_MPI_STS (CVMX_ADD_IO_SEG(0x0001070000001008ull)) +#define CVMX_MPI_TX (CVMX_ADD_IO_SEG(0x0001070000001010ull)) + +union cvmx_mpi_cfg { + uint64_t u64; + struct cvmx_mpi_cfg_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t csena3:1; + uint64_t csena2:1; + uint64_t csena1:1; + uint64_t csena0:1; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t csena0:1; + uint64_t csena1:1; + uint64_t csena2:1; + uint64_t csena3:1; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } s; + struct cvmx_mpi_cfg_cn30xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_12_15:4; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t reserved_12_15:4; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn30xx; + struct cvmx_mpi_cfg_cn31xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_11_15:5; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t csena:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t csena:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t reserved_11_15:5; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn31xx; + struct cvmx_mpi_cfg_cn30xx cn50xx; + struct cvmx_mpi_cfg_cn61xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t reserved_14_15:2; + uint64_t csena1:1; + uint64_t csena0:1; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t reserved_6_6:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t reserved_6_6:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t csena0:1; + uint64_t csena1:1; + uint64_t reserved_14_15:2; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn61xx; + struct cvmx_mpi_cfg_cn66xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_29_63:35; + uint64_t clkdiv:13; + uint64_t csena3:1; + uint64_t csena2:1; + uint64_t reserved_12_13:2; + uint64_t cslate:1; + uint64_t tritx:1; + uint64_t idleclks:2; + uint64_t cshi:1; + uint64_t reserved_6_6:1; + uint64_t int_ena:1; + uint64_t lsbfirst:1; + uint64_t wireor:1; + uint64_t clk_cont:1; + uint64_t idlelo:1; + uint64_t enable:1; +#else + uint64_t enable:1; + uint64_t idlelo:1; + uint64_t clk_cont:1; + uint64_t wireor:1; + uint64_t lsbfirst:1; + uint64_t int_ena:1; + uint64_t reserved_6_6:1; + uint64_t cshi:1; + uint64_t idleclks:2; + uint64_t tritx:1; + uint64_t cslate:1; + uint64_t reserved_12_13:2; + uint64_t csena2:1; + uint64_t csena3:1; + uint64_t clkdiv:13; + uint64_t reserved_29_63:35; +#endif + } cn66xx; + struct cvmx_mpi_cfg_cn61xx cnf71xx; +}; + +union cvmx_mpi_datx { + uint64_t u64; + struct cvmx_mpi_datx_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_8_63:56; + uint64_t data:8; +#else + uint64_t data:8; + uint64_t reserved_8_63:56; +#endif + } s; + struct cvmx_mpi_datx_s cn30xx; + struct cvmx_mpi_datx_s cn31xx; + struct cvmx_mpi_datx_s cn50xx; + struct cvmx_mpi_datx_s cn61xx; + struct cvmx_mpi_datx_s cn66xx; + struct cvmx_mpi_datx_s cnf71xx; +}; + +union cvmx_mpi_sts { + uint64_t u64; + struct cvmx_mpi_sts_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_13_63:51; + uint64_t rxnum:5; + uint64_t reserved_1_7:7; + uint64_t busy:1; +#else + uint64_t busy:1; + uint64_t reserved_1_7:7; + uint64_t rxnum:5; + uint64_t reserved_13_63:51; +#endif + } s; + struct cvmx_mpi_sts_s cn30xx; + struct cvmx_mpi_sts_s cn31xx; + struct cvmx_mpi_sts_s cn50xx; + struct cvmx_mpi_sts_s cn61xx; + struct cvmx_mpi_sts_s cn66xx; + struct cvmx_mpi_sts_s cnf71xx; +}; + +union cvmx_mpi_tx { + uint64_t u64; + struct cvmx_mpi_tx_s { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_22_63:42; + uint64_t csid:2; + uint64_t reserved_17_19:3; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_19:3; + uint64_t csid:2; + uint64_t reserved_22_63:42; +#endif + } s; + struct cvmx_mpi_tx_cn30xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_17_63:47; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_63:47; +#endif + } cn30xx; + struct cvmx_mpi_tx_cn30xx cn31xx; + struct cvmx_mpi_tx_cn30xx cn50xx; + struct cvmx_mpi_tx_cn61xx { +#ifdef __BIG_ENDIAN_BITFIELD + uint64_t reserved_21_63:43; + uint64_t csid:1; + uint64_t reserved_17_19:3; + uint64_t leavecs:1; + uint64_t reserved_13_15:3; + uint64_t txnum:5; + uint64_t reserved_5_7:3; + uint64_t totnum:5; +#else + uint64_t totnum:5; + uint64_t reserved_5_7:3; + uint64_t txnum:5; + uint64_t reserved_13_15:3; + uint64_t leavecs:1; + uint64_t reserved_17_19:3; + uint64_t csid:1; + uint64_t reserved_21_63:43; +#endif + } cn61xx; + struct cvmx_mpi_tx_s cn66xx; + struct cvmx_mpi_tx_cn61xx cnf71xx; +}; + +#endif /* __SPI_CAVIUM_H */ diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c deleted file mode 100644 index 3b170093989f..000000000000 --- a/drivers/spi/spi-octeon.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * - * Copyright (C) 2011, 2012 Cavium, Inc. - */ - -#include <linux/platform_device.h> -#include <linux/interrupt.h> -#include <linux/spi/spi.h> -#include <linux/module.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/of.h> - -#include <asm/octeon/octeon.h> -#include <asm/octeon/cvmx-mpi-defs.h> - -#define OCTEON_SPI_CFG 0 -#define OCTEON_SPI_STS 0x08 -#define OCTEON_SPI_TX 0x10 -#define OCTEON_SPI_DAT0 0x80 - -#define OCTEON_SPI_MAX_BYTES 9 - -#define OCTEON_SPI_MAX_CLOCK_HZ 16000000 - -struct octeon_spi { - u64 register_base; - u64 last_cfg; - u64 cs_enax; -}; - -static void octeon_spi_wait_ready(struct octeon_spi *p) -{ - union cvmx_mpi_sts mpi_sts; - unsigned int loops = 0; - - do { - if (loops++) - __delay(500); - mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS); - } while (mpi_sts.s.busy); -} - -static int octeon_spi_do_transfer(struct octeon_spi *p, - struct spi_message *msg, - struct spi_transfer *xfer, - bool last_xfer) -{ - struct spi_device *spi = msg->spi; - union cvmx_mpi_cfg mpi_cfg; - union cvmx_mpi_tx mpi_tx; - unsigned int clkdiv; - unsigned int speed_hz; - int mode; - bool cpha, cpol; - const u8 *tx_buf; - u8 *rx_buf; - int len; - int i; - - mode = spi->mode; - cpha = mode & SPI_CPHA; - cpol = mode & SPI_CPOL; - - speed_hz = xfer->speed_hz; - - clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz); - - mpi_cfg.u64 = 0; - - mpi_cfg.s.clkdiv = clkdiv; - mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; - mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; - mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; - mpi_cfg.s.idlelo = cpha != cpol; - mpi_cfg.s.cslate = cpha ? 1 : 0; - mpi_cfg.s.enable = 1; - - if (spi->chip_select < 4) - p->cs_enax |= 1ull << (12 + spi->chip_select); - mpi_cfg.u64 |= p->cs_enax; - - if (mpi_cfg.u64 != p->last_cfg) { - p->last_cfg = mpi_cfg.u64; - cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64); - } - tx_buf = xfer->tx_buf; - rx_buf = xfer->rx_buf; - len = xfer->len; - while (len > OCTEON_SPI_MAX_BYTES) { - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); - } - mpi_tx.u64 = 0; - mpi_tx.s.csid = spi->chip_select; - mpi_tx.s.leavecs = 1; - mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; - mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); - *rx_buf++ = (u8)v; - } - len -= OCTEON_SPI_MAX_BYTES; - } - - for (i = 0; i < len; i++) { - u8 d; - if (tx_buf) - d = *tx_buf++; - else - d = 0; - cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d); - } - - mpi_tx.u64 = 0; - mpi_tx.s.csid = spi->chip_select; - if (last_xfer) - mpi_tx.s.leavecs = xfer->cs_change; - else - mpi_tx.s.leavecs = !xfer->cs_change; - mpi_tx.s.txnum = tx_buf ? len : 0; - mpi_tx.s.totnum = len; - cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64); - - octeon_spi_wait_ready(p); - if (rx_buf) - for (i = 0; i < len; i++) { - u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i)); - *rx_buf++ = (u8)v; - } - - if (xfer->delay_usecs) - udelay(xfer->delay_usecs); - - return xfer->len; -} - -static int octeon_spi_transfer_one_message(struct spi_master *master, - struct spi_message *msg) -{ - struct octeon_spi *p = spi_master_get_devdata(master); - unsigned int total_len = 0; - int status = 0; - struct spi_transfer *xfer; - - list_for_each_entry(xfer, &msg->transfers, transfer_list) { - bool last_xfer = list_is_last(&xfer->transfer_list, - &msg->transfers); - int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); - if (r < 0) { - status = r; - goto err; - } - total_len += r; - } -err: - msg->status = status; - msg->actual_length = total_len; - spi_finalize_current_message(master); - return status; -} - -static int octeon_spi_probe(struct platform_device *pdev) -{ - struct resource *res_mem; - void __iomem *reg_base; - struct spi_master *master; - struct octeon_spi *p; - int err = -ENOENT; - - master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi)); - if (!master) - return -ENOMEM; - p = spi_master_get_devdata(master); - platform_set_drvdata(pdev, master); - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - reg_base = devm_ioremap_resource(&pdev->dev, res_mem); - if (IS_ERR(reg_base)) { - err = PTR_ERR(reg_base); - goto fail; - } - - p->register_base = (u64)reg_base; - - master->num_chipselect = 4; - master->mode_bits = SPI_CPHA | - SPI_CPOL | - SPI_CS_HIGH | - SPI_LSB_FIRST | - SPI_3WIRE; - - master->transfer_one_message = octeon_spi_transfer_one_message; - master->bits_per_word_mask = SPI_BPW_MASK(8); - master->max_speed_hz = OCTEON_SPI_MAX_CLOCK_HZ; - - master->dev.of_node = pdev->dev.of_node; - err = devm_spi_register_master(&pdev->dev, master); - if (err) { - dev_err(&pdev->dev, "register master failed: %d\n", err); - goto fail; - } - - dev_info(&pdev->dev, "OCTEON SPI bus driver\n"); - - return 0; -fail: - spi_master_put(master); - return err; -} - -static int octeon_spi_remove(struct platform_device *pdev) -{ - struct spi_master *master = platform_get_drvdata(pdev); - struct octeon_spi *p = spi_master_get_devdata(master); - u64 register_base = p->register_base; - - /* Clear the CSENA* and put everything in a known state. */ - cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0); - - return 0; -} - -static const struct of_device_id octeon_spi_match[] = { - { .compatible = "cavium,octeon-3010-spi", }, - {}, -}; -MODULE_DEVICE_TABLE(of, octeon_spi_match); - -static struct platform_driver octeon_spi_driver = { - .driver = { - .name = "spi-octeon", - .of_match_table = octeon_spi_match, - }, - .probe = octeon_spi_probe, - .remove = octeon_spi_remove, -}; - -module_platform_driver(octeon_spi_driver); - -MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver"); -MODULE_AUTHOR("David Daney"); -MODULE_LICENSE("GPL"); diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 1d237e93a289..d5157b2222ce 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -419,16 +419,13 @@ static void omap2_mcspi_tx_dma(struct spi_device *spi, if (mcspi_dma->dma_tx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_tx, &cfg); - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->tx_dma; - sg_dma_len(&sg) = xfer->len; - - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, &sg, 1, - DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_tx, xfer->tx_sg.sgl, + xfer->tx_sg.nents, + DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_tx_callback; tx->callback_param = spi; @@ -449,7 +446,10 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, { struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; - unsigned int count, dma_count; + unsigned int count, transfer_reduction = 0; + struct scatterlist *sg_out[2]; + int nb_sizes = 0, out_mapped_nents[2], ret, x; + size_t sizes[2]; u32 l; int elements = 0; int word_len, element_count; @@ -457,10 +457,14 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, mcspi = spi_master_get_devdata(spi->master); mcspi_dma = &mcspi->dma_channels[spi->chip_select]; count = xfer->len; - dma_count = xfer->len; + /* + * In the "End-of-Transfer Procedure" section for DMA RX in OMAP35x TRM + * it mentions reducing DMA transfer length by one element in master + * normal mode. + */ if (mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction = es; word_len = cs->word_len; l = mcspi_cached_chconf0(spi); @@ -474,20 +478,46 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, if (mcspi_dma->dma_rx) { struct dma_async_tx_descriptor *tx; - struct scatterlist sg; dmaengine_slave_config(mcspi_dma->dma_rx, &cfg); + /* + * Reduce DMA transfer length by one more if McSPI is + * configured in turbo mode. + */ if ((l & OMAP2_MCSPI_CHCONF_TURBO) && mcspi->fifo_depth == 0) - dma_count -= es; + transfer_reduction += es; + + if (transfer_reduction) { + /* Split sgl into two. The second sgl won't be used. */ + sizes[0] = count - transfer_reduction; + sizes[1] = transfer_reduction; + nb_sizes = 2; + } else { + /* + * Don't bother splitting the sgl. This essentially + * clones the original sgl. + */ + sizes[0] = count; + nb_sizes = 1; + } + + ret = sg_split(xfer->rx_sg.sgl, xfer->rx_sg.nents, + 0, nb_sizes, + sizes, + sg_out, out_mapped_nents, + GFP_KERNEL); - sg_init_table(&sg, 1); - sg_dma_address(&sg) = xfer->rx_dma; - sg_dma_len(&sg) = dma_count; + if (ret < 0) { + dev_err(&spi->dev, "sg_split failed\n"); + return 0; + } - tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, &sg, 1, - DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); + tx = dmaengine_prep_slave_sg(mcspi_dma->dma_rx, + sg_out[0], + out_mapped_nents[0], + DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (tx) { tx->callback = omap2_mcspi_rx_callback; tx->callback_param = spi; @@ -501,12 +531,17 @@ omap2_mcspi_rx_dma(struct spi_device *spi, struct spi_transfer *xfer, omap2_mcspi_set_dma_req(spi, 1, 1); wait_for_completion(&mcspi_dma->dma_rx_completion); - dma_unmap_single(mcspi->dev, xfer->rx_dma, count, - DMA_FROM_DEVICE); + + for (x = 0; x < nb_sizes; x++) + kfree(sg_out[x]); if (mcspi->fifo_depth > 0) return count; + /* + * Due to the DMA transfer length reduction the missing bytes must + * be read manually to receive all of the expected data. + */ omap2_mcspi_set_enable(spi, 0); elements = element_count - 1; @@ -615,8 +650,6 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) if (tx != NULL) { wait_for_completion(&mcspi_dma->dma_tx_completion); - dma_unmap_single(mcspi->dev, xfer->tx_dma, xfer->len, - DMA_TO_DEVICE); if (mcspi->fifo_depth > 0) { irqstat_reg = mcspi->base + OMAP2_MCSPI_IRQSTATUS; @@ -1074,8 +1107,9 @@ static void omap2_mcspi_cleanup(struct spi_device *spi) gpio_free(spi->cs_gpio); } -static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, - struct spi_device *spi, struct spi_transfer *t) +static int omap2_mcspi_transfer_one(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *t) { /* We only enable one channel at a time -- the one whose message is @@ -1085,7 +1119,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, * chipselect with the FORCE bit ... CS != channel enable. */ - struct spi_master *master; + struct omap2_mcspi *mcspi; struct omap2_mcspi_dma *mcspi_dma; struct omap2_mcspi_cs *cs; struct omap2_mcspi_device_config *cd; @@ -1093,7 +1127,7 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, int status = 0; u32 chconf; - master = spi->master; + mcspi = spi_master_get_devdata(master); mcspi_dma = mcspi->dma_channels + spi->chip_select; cs = spi->controller_state; cd = spi->controller_data; @@ -1153,7 +1187,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, unsigned count; if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) omap2_mcspi_set_fifo(spi, t, 1); omap2_mcspi_set_enable(spi, 1); @@ -1164,7 +1199,8 @@ static int omap2_mcspi_work_one(struct omap2_mcspi *mcspi, + OMAP2_MCSPI_TX0); if ((mcspi_dma->dma_rx && mcspi_dma->dma_tx) && - (t->len >= DMA_MIN_BYTES)) + master->cur_msg_mapped && + master->can_dma(master, spi, t)) count = omap2_mcspi_txrx_dma(spi, t); else count = omap2_mcspi_txrx_pio(spi, t); @@ -1233,55 +1269,11 @@ static int omap2_mcspi_prepare_message(struct spi_master *master, return 0; } -static int omap2_mcspi_transfer_one(struct spi_master *master, - struct spi_device *spi, struct spi_transfer *t) +static bool omap2_mcspi_can_dma(struct spi_master *master, + struct spi_device *spi, + struct spi_transfer *xfer) { - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *mcspi_dma; - const void *tx_buf = t->tx_buf; - void *rx_buf = t->rx_buf; - unsigned len = t->len; - - mcspi = spi_master_get_devdata(master); - mcspi_dma = mcspi->dma_channels + spi->chip_select; - - if ((len && !(rx_buf || tx_buf))) { - dev_dbg(mcspi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n", - t->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - t->bits_per_word); - return -EINVAL; - } - - if (len < DMA_MIN_BYTES) - goto skip_dma_map; - - if (mcspi_dma->dma_tx && tx_buf != NULL) { - t->tx_dma = dma_map_single(mcspi->dev, (void *) tx_buf, - len, DMA_TO_DEVICE); - if (dma_mapping_error(mcspi->dev, t->tx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'T', len); - return -EINVAL; - } - } - if (mcspi_dma->dma_rx && rx_buf != NULL) { - t->rx_dma = dma_map_single(mcspi->dev, rx_buf, t->len, - DMA_FROM_DEVICE); - if (dma_mapping_error(mcspi->dev, t->rx_dma)) { - dev_dbg(mcspi->dev, "dma %cX %d bytes error\n", - 'R', len); - if (tx_buf != NULL) - dma_unmap_single(mcspi->dev, t->tx_dma, - len, DMA_TO_DEVICE); - return -EINVAL; - } - } - -skip_dma_map: - return omap2_mcspi_work_one(mcspi, spi, t); + return (xfer->len >= DMA_MIN_BYTES); } static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi) @@ -1361,6 +1353,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) master->setup = omap2_mcspi_setup; master->auto_runtime_pm = true; master->prepare_message = omap2_mcspi_prepare_message; + master->can_dma = omap2_mcspi_can_dma; master->transfer_one = omap2_mcspi_transfer_one; master->set_cs = omap2_mcspi_set_cs; master->cleanup = omap2_mcspi_cleanup; diff --git a/drivers/spi/spi-orion.c b/drivers/spi/spi-orion.c index a87cfd4ba17b..ded37025b445 100644 --- a/drivers/spi/spi-orion.c +++ b/drivers/spi/spi-orion.c @@ -18,6 +18,7 @@ #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_device.h> #include <linux/clk.h> #include <linux/sizes.h> @@ -43,6 +44,9 @@ #define ORION_SPI_INT_CAUSE_REG 0x10 #define ORION_SPI_TIMING_PARAMS_REG 0x18 +/* Register for the "Direct Mode" */ +#define SPI_DIRECT_WRITE_CONFIG_REG 0x20 + #define ORION_SPI_TMISO_SAMPLE_MASK (0x3 << 6) #define ORION_SPI_TMISO_SAMPLE_1 (1 << 6) #define ORION_SPI_TMISO_SAMPLE_2 (2 << 6) @@ -78,11 +82,18 @@ struct orion_spi_dev { bool is_errata_50mhz_ac; }; +struct orion_direct_acc { + void __iomem *vaddr; + u32 size; +}; + struct orion_spi { struct spi_master *master; void __iomem *base; struct clk *clk; const struct orion_spi_dev *devdata; + + struct orion_direct_acc direct_access[ORION_NUM_CHIPSELECTS]; }; static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg) @@ -372,10 +383,39 @@ orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer) { unsigned int count; int word_len; + struct orion_spi *orion_spi; + int cs = spi->chip_select; word_len = spi->bits_per_word; count = xfer->len; + orion_spi = spi_master_get_devdata(spi->master); + + /* + * Use SPI direct write mode if base address is available. Otherwise + * fall back to PIO mode for this transfer. + */ + if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) && + (word_len == 8)) { + unsigned int cnt = count / 4; + unsigned int rem = count % 4; + + /* + * Send the TX-data to the SPI device via the direct + * mapped address window + */ + iowrite32_rep(orion_spi->direct_access[cs].vaddr, + xfer->tx_buf, cnt); + if (rem) { + u32 *buf = (u32 *)xfer->tx_buf; + + iowrite8_rep(orion_spi->direct_access[cs].vaddr, + &buf[cnt], rem); + } + + return count; + } + if (word_len == 8) { const u8 *tx = xfer->tx_buf; u8 *rx = xfer->rx_buf; @@ -425,6 +465,10 @@ static int orion_spi_reset(struct orion_spi *orion_spi) { /* Verify that the CS is deasserted */ orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1); + + /* Don't deassert CS between the direct mapped SPI transfers */ + writel(0, spi_reg(orion_spi, SPI_DIRECT_WRITE_CONFIG_REG)); + return 0; } @@ -504,6 +548,7 @@ static int orion_spi_probe(struct platform_device *pdev) struct resource *r; unsigned long tclk_hz; int status = 0; + struct device_node *np; master = spi_alloc_master(&pdev->dev, sizeof(*spi)); if (master == NULL) { @@ -576,6 +621,49 @@ static int orion_spi_probe(struct platform_device *pdev) goto out_rel_clk; } + /* Scan all SPI devices of this controller for direct mapped devices */ + for_each_available_child_of_node(pdev->dev.of_node, np) { + u32 cs; + + /* Get chip-select number from the "reg" property */ + status = of_property_read_u32(np, "reg", &cs); + if (status) { + dev_err(&pdev->dev, + "%s has no valid 'reg' property (%d)\n", + np->full_name, status); + status = 0; + continue; + } + + /* + * Check if an address is configured for this SPI device. If + * not, the MBus mapping via the 'ranges' property in the 'soc' + * node is not configured and this device should not use the + * direct mode. In this case, just continue with the next + * device. + */ + status = of_address_to_resource(pdev->dev.of_node, cs + 1, r); + if (status) + continue; + + /* + * Only map one page for direct access. This is enough for the + * simple TX transfer which only writes to the first word. + * This needs to get extended for the direct SPI-NOR / SPI-NAND + * support, once this gets implemented. + */ + spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev, + r->start, + PAGE_SIZE); + if (!spi->direct_access[cs].vaddr) { + status = -ENOMEM; + goto out_rel_clk; + } + spi->direct_access[cs].size = PAGE_SIZE; + + dev_info(&pdev->dev, "CS%d configured for direct access\n", cs); + } + pm_runtime_set_active(&pdev->dev); pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); diff --git a/drivers/spi/spi-pic32-sqi.c b/drivers/spi/spi-pic32-sqi.c index ca3c8d94b290..c41abddab318 100644 --- a/drivers/spi/spi-pic32-sqi.c +++ b/drivers/spi/spi-pic32-sqi.c @@ -354,6 +354,7 @@ static int pic32_sqi_one_message(struct spi_master *master, struct spi_transfer *xfer; struct pic32_sqi *sqi; int ret = 0, mode; + unsigned long timeout; u32 val; sqi = spi_master_get_devdata(master); @@ -419,10 +420,10 @@ static int pic32_sqi_one_message(struct spi_master *master, writel(val, sqi->regs + PESQI_BD_CTRL_REG); /* wait for xfer completion */ - ret = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ); - if (ret <= 0) { + timeout = wait_for_completion_timeout(&sqi->xfer_done, 5 * HZ); + if (timeout == 0) { dev_err(&sqi->master->dev, "wait timedout/interrupted\n"); - ret = -EIO; + ret = -ETIMEDOUT; msg->status = ret; } else { /* success */ diff --git a/drivers/spi/spi-pic32.c b/drivers/spi/spi-pic32.c index 73db87f805a1..fefb688a3432 100644 --- a/drivers/spi/spi-pic32.c +++ b/drivers/spi/spi-pic32.c @@ -507,6 +507,7 @@ static int pic32_spi_one_transfer(struct spi_master *master, { struct pic32_spi *pic32s; bool dma_issued = false; + unsigned long timeout; int ret; pic32s = spi_master_get_devdata(master); @@ -553,8 +554,8 @@ static int pic32_spi_one_transfer(struct spi_master *master, } /* wait for completion */ - ret = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); - if (ret <= 0) { + timeout = wait_for_completion_timeout(&pic32s->xfer_done, 2 * HZ); + if (timeout == 0) { dev_err(&spi->dev, "wait error/timedout\n"); if (dma_issued) { dmaengine_terminate_all(master->dma_rx); |