spi: Updates for v4.18

Quite a busy release for SPI, mainly as a result of Boris Brezillon's
 work on improving the integration with MTD for accelerated SPI flash
 controllers.  He's added a new spi_mem interface which works a lot
 better with general hardware and converted the users over to it, as a
 result of this work we've got some MTD changes in here as well.
 
 Other highlights include:
 
  - Lots of spring cleaning for the s3c64xx driver.
  - Removal of the bcm53xx, the hardware is also supported by the mspi
    driver but SoC naming had caused people to miss the duplication.
  - Conversion of the pxa2xx driver to use the standard message
    processing loop rather than open coding.
  - A bunch of improvements to the runtime PM of the OMAP McSPI driver.
 -----BEGIN PGP SIGNATURE-----
 
 iQFHBAABCgAxFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAlsVG2cTHGJyb29uaWVA
 a2VybmVsLm9yZwAKCRAk1otyXVSH0D6eB/9VOpypnB2Yxa64X2NzP05sRhg2rqMB
 0Rwc0r/98PH5GkmA8286oT39zq04PIYoGdTPpJEe/Tza8errJcM8ccCFv74DbJIp
 Yws4+GsibY5TfYCzh4AiD2oHna714AKA/r858utnZOlYPZ3JP8tn708+LiTjxhkY
 RbxCBcqQjBszmYaDCo2iQFxWO4YAUBkDgJvRf/1Q7n5sRa0YhO7z1NG/lVMb1BYh
 m56YnGki4ZgJsbn6agxPC1OX/+by8p0q7bqSf6BnTiiv9ErXLjQLrFOdkzYvdBHg
 /82+TETmgpNxg9iKJeeyss6EmnVJsjrd/tRjq6WMK/Ac5HHiv9t0VBs6
 =QCbn
 -----END PGP SIGNATURE-----

Merge tag 'spi-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi

Pull spi updates from Mark Brown:
 "Quite a busy release for SPI, mainly as a result of Boris Brezillon's
  work on improving the integration with MTD for accelerated SPI flash
  controllers. He's added a new spi_mem interface which works a lot
  better with general hardware and converted the users over to it, as a
  result of this work we've got some MTD changes in here as well.

  Other highlights include:

   - Lots of spring cleaning for the s3c64xx driver.

   - Removal of the bcm53xx, the hardware is also supported by the mspi
     driver but SoC naming had caused people to miss the duplication.

   - Conversion of the pxa2xx driver to use the standard message
     processing loop rather than open coding.

   - A bunch of improvements to the runtime PM of the OMAP McSPI driver"

* tag 'spi-v4.18' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi: (47 commits)
  spi: Fix typo on SPI_MEM help text
  spi: sh-msiof: Fix setting SIRMDR1.SYNCAC to match SITMDR1.SYNCAC
  mtd: devices: m25p80: Use spi_mem_set_drvdata() instead of spi_set_drvdata()
  spi: omap2-mcspi: Remove unnecessary pm_runtime_force_suspend()
  spi: Add missing pm_runtime_put_noidle() after failed get
  spi: ti-qspi: Make sure res_mmap != NULL before dereferencing it
  spi: spi-s3c64xx: Fix system resume support
  spi: bcm-qspi: Fix build failure caused by spi_flash_read() API removal
  spi: Get rid of the spi_flash_read() API
  mtd: spi-nor: Use the spi_mem_xx() API
  spi: ti-qspi: Implement the spi_mem interface
  spi: bcm-qspi: Implement the spi_mem interface
  spi: Make support for regular transfers optional when ->mem_ops != NULL
  spi: Extend the core to ease integration of SPI memory controllers
  spi: remove forgotten CONFIG_SPI_BCM53XX
  spi: remove the older/duplicated bcm53xx driver
  spi: pxa2xx: check clk_prepare_enable() return value
  spi: lpspi: Switch to SPDX identifier
  spi: mxs: Switch to SPDX identifier
  spi: imx: Switch to SPDX identifier
  ...
This commit is contained in:
Linus Torvalds 2018-06-04 11:34:06 -07:00
commit cfd12db4f1
28 changed files with 1428 additions and 1314 deletions

View File

@ -81,6 +81,7 @@ config MTD_DATAFLASH_OTP
config MTD_M25P80
tristate "Support most SPI Flash chips (AT26DF, M25P, W25X, ...)"
depends on SPI_MASTER && MTD_SPI_NOR
select SPI_MEM
help
This enables access to most modern SPI flash chips, used for
program and data storage. Series supported include Atmel AT26DF,

View File

@ -24,12 +24,13 @@
#include <linux/mtd/partitions.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/spi/flash.h>
#include <linux/mtd/spi-nor.h>
#define MAX_CMD_SIZE 6
struct m25p {
struct spi_device *spi;
struct spi_mem *spimem;
struct spi_nor spi_nor;
u8 command[MAX_CMD_SIZE];
};
@ -37,97 +38,68 @@ struct m25p {
static int m25p80_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(code, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_IN(len, val, 1));
int ret;
ret = spi_write_then_read(spi, &code, 1, val, len);
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret < 0)
dev_err(&spi->dev, "error %d reading %x\n", ret, code);
dev_err(&flash->spimem->spi->dev, "error %d reading %x\n", ret,
code);
return ret;
}
static void m25p_addr2cmd(struct spi_nor *nor, unsigned int addr, u8 *cmd)
{
/* opcode is in cmd[0] */
cmd[1] = addr >> (nor->addr_width * 8 - 8);
cmd[2] = addr >> (nor->addr_width * 8 - 16);
cmd[3] = addr >> (nor->addr_width * 8 - 24);
cmd[4] = addr >> (nor->addr_width * 8 - 32);
}
static int m25p_cmdsz(struct spi_nor *nor)
{
return 1 + nor->addr_width;
}
static int m25p80_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf, int len)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(opcode, 1),
SPI_MEM_OP_NO_ADDR,
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(len, buf, 1));
flash->command[0] = opcode;
if (buf)
memcpy(&flash->command[1], buf, len);
return spi_write(spi, flash->command, len + 1);
return spi_mem_exec_op(flash->spimem, &op);
}
static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
struct spi_transfer t[3] = {};
struct spi_message m;
int cmd_sz = m25p_cmdsz(nor);
ssize_t ret;
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, to, 1),
SPI_MEM_OP_DUMMY(0, 1),
SPI_MEM_OP_DATA_OUT(len, buf, 1));
size_t remaining = len;
int ret;
/* get transfer protocols. */
inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
spi_message_init(&m);
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->write_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->write_proto);
op.dummy.buswidth = op.addr.buswidth;
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
cmd_sz = 1;
op.addr.nbytes = 0;
flash->command[0] = nor->program_opcode;
m25p_addr2cmd(nor, to, flash->command);
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
t[0].tx_buf = flash->command;
t[0].tx_nbits = inst_nbits;
t[0].len = cmd_sz;
spi_message_add_tail(&t[0], &m);
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
/* split the op code and address bytes into two transfers if needed. */
data_idx = 1;
if (addr_nbits != inst_nbits) {
t[0].len = 1;
t[1].tx_buf = &flash->command[1];
t[1].tx_nbits = addr_nbits;
t[1].len = cmd_sz - 1;
spi_message_add_tail(&t[1], &m);
data_idx = 2;
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
op.data.buf.out += op.data.nbytes;
}
t[data_idx].tx_buf = buf;
t[data_idx].tx_nbits = data_nbits;
t[data_idx].len = len;
spi_message_add_tail(&t[data_idx], &m);
ret = spi_sync(spi, &m);
if (ret)
return ret;
ret = m.actual_length - cmd_sz;
if (ret < 0)
return -EIO;
return ret;
return len;
}
/*
@ -138,92 +110,39 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *buf)
{
struct m25p *flash = nor->priv;
struct spi_device *spi = flash->spi;
unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
struct spi_transfer t[3];
struct spi_message m;
unsigned int dummy = nor->read_dummy;
ssize_t ret;
int cmd_sz;
struct spi_mem_op op =
SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 1),
SPI_MEM_OP_ADDR(nor->addr_width, from, 1),
SPI_MEM_OP_DUMMY(nor->read_dummy, 1),
SPI_MEM_OP_DATA_IN(len, buf, 1));
size_t remaining = len;
int ret;
/* get transfer protocols. */
inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
op.cmd.buswidth = spi_nor_get_protocol_inst_nbits(nor->read_proto);
op.addr.buswidth = spi_nor_get_protocol_addr_nbits(nor->read_proto);
op.dummy.buswidth = op.addr.buswidth;
op.data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
/* convert the dummy cycles to the number of bytes */
dummy = (dummy * addr_nbits) / 8;
op.dummy.nbytes = (nor->read_dummy * op.dummy.buswidth) / 8;
if (spi_flash_read_supported(spi)) {
struct spi_flash_read_message msg;
memset(&msg, 0, sizeof(msg));
msg.buf = buf;
msg.from = from;
msg.len = len;
msg.read_opcode = nor->read_opcode;
msg.addr_width = nor->addr_width;
msg.dummy_bytes = dummy;
msg.opcode_nbits = inst_nbits;
msg.addr_nbits = addr_nbits;
msg.data_nbits = data_nbits;
ret = spi_flash_read(spi, &msg);
if (ret < 0)
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
ret = spi_mem_adjust_op_size(flash->spimem, &op);
if (ret)
return ret;
return msg.retlen;
ret = spi_mem_exec_op(flash->spimem, &op);
if (ret)
return ret;
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
op.data.buf.in += op.data.nbytes;
}
spi_message_init(&m);
memset(t, 0, (sizeof t));
flash->command[0] = nor->read_opcode;
m25p_addr2cmd(nor, from, flash->command);
t[0].tx_buf = flash->command;
t[0].tx_nbits = inst_nbits;
t[0].len = m25p_cmdsz(nor) + dummy;
spi_message_add_tail(&t[0], &m);
/*
* Set all dummy/mode cycle bits to avoid sending some manufacturer
* specific pattern, which might make the memory enter its Continuous
* Read mode by mistake.
* Based on the different mode cycle bit patterns listed and described
* in the JESD216B specification, the 0xff value works for all memories
* and all manufacturers.
*/
cmd_sz = t[0].len;
memset(flash->command + cmd_sz - dummy, 0xff, dummy);
/* split the op code and address bytes into two transfers if needed. */
data_idx = 1;
if (addr_nbits != inst_nbits) {
t[0].len = 1;
t[1].tx_buf = &flash->command[1];
t[1].tx_nbits = addr_nbits;
t[1].len = cmd_sz - 1;
spi_message_add_tail(&t[1], &m);
data_idx = 2;
}
t[data_idx].rx_buf = buf;
t[data_idx].rx_nbits = data_nbits;
t[data_idx].len = min3(len, spi_max_transfer_size(spi),
spi_max_message_size(spi) - cmd_sz);
spi_message_add_tail(&t[data_idx], &m);
ret = spi_sync(spi, &m);
if (ret)
return ret;
ret = m.actual_length - cmd_sz;
if (ret < 0)
return -EIO;
return ret;
return len;
}
/*
@ -231,8 +150,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
* matches what the READ command supports, at least until this driver
* understands FAST_READ (for clocks over 25 MHz).
*/
static int m25p_probe(struct spi_device *spi)
static int m25p_probe(struct spi_mem *spimem)
{
struct spi_device *spi = spimem->spi;
struct flash_platform_data *data;
struct m25p *flash;
struct spi_nor *nor;
@ -244,9 +164,9 @@ static int m25p_probe(struct spi_device *spi)
char *flash_name;
int ret;
data = dev_get_platdata(&spi->dev);
data = dev_get_platdata(&spimem->spi->dev);
flash = devm_kzalloc(&spi->dev, sizeof(*flash), GFP_KERNEL);
flash = devm_kzalloc(&spimem->spi->dev, sizeof(*flash), GFP_KERNEL);
if (!flash)
return -ENOMEM;
@ -258,12 +178,12 @@ static int m25p_probe(struct spi_device *spi)
nor->write_reg = m25p80_write_reg;
nor->read_reg = m25p80_read_reg;
nor->dev = &spi->dev;
nor->dev = &spimem->spi->dev;
spi_nor_set_flash_node(nor, spi->dev.of_node);
nor->priv = flash;
spi_set_drvdata(spi, flash);
flash->spi = spi;
spi_mem_set_drvdata(spimem, flash);
flash->spimem = spimem;
if (spi->mode & SPI_RX_QUAD) {
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
@ -303,9 +223,9 @@ static int m25p_probe(struct spi_device *spi)
}
static int m25p_remove(struct spi_device *spi)
static int m25p_remove(struct spi_mem *spimem)
{
struct m25p *flash = spi_get_drvdata(spi);
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
@ -313,9 +233,9 @@ static int m25p_remove(struct spi_device *spi)
return mtd_device_unregister(&flash->spi_nor.mtd);
}
static void m25p_shutdown(struct spi_device *spi)
static void m25p_shutdown(struct spi_mem *spimem)
{
struct m25p *flash = spi_get_drvdata(spi);
struct m25p *flash = spi_mem_get_drvdata(spimem);
spi_nor_restore(&flash->spi_nor);
}
@ -386,12 +306,14 @@ static const struct of_device_id m25p_of_table[] = {
};
MODULE_DEVICE_TABLE(of, m25p_of_table);
static struct spi_driver m25p80_driver = {
.driver = {
.name = "m25p80",
.of_match_table = m25p_of_table,
static struct spi_mem_driver m25p80_driver = {
.spidrv = {
.driver = {
.name = "m25p80",
.of_match_table = m25p_of_table,
},
.id_table = m25p_ids,
},
.id_table = m25p_ids,
.probe = m25p_probe,
.remove = m25p_remove,
.shutdown = m25p_shutdown,
@ -402,7 +324,7 @@ static struct spi_driver m25p80_driver = {
*/
};
module_spi_driver(m25p80_driver);
module_spi_mem_driver(m25p80_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mike Lavender");

View File

@ -47,6 +47,13 @@ config SPI_MASTER
if SPI_MASTER
config SPI_MEM
bool "SPI memory extension"
help
Enable this option if you want to enable the SPI memory extension.
This extension is meant to simplify interaction with SPI memories
by providing a high-level interface to send memory-like commands.
comment "SPI Master Controller Drivers"
config SPI_ALTERA
@ -71,7 +78,6 @@ config SPI_ARMADA_3700
config SPI_ATMEL
tristate "Atmel SPI Controller"
depends on HAS_DMA
depends on ARCH_AT91 || COMPILE_TEST
help
This selects a driver for the Atmel SPI Controller, present on
@ -115,14 +121,6 @@ config SPI_BCM2835AUX
"universal SPI master", and the regular SPI controller.
This driver is for the universal/auxiliary SPI controller.
config SPI_BCM53XX
tristate "Broadcom BCM53xx SPI controller"
depends on ARCH_BCM_5301X
depends on BCMA_POSSIBLE
select BCMA
help
Enable support for the SPI controller on Broadcom BCM53xx ARM SoCs.
config SPI_BCM63XX
tristate "Broadcom BCM63xx SPI controller"
depends on BCM63XX || COMPILE_TEST
@ -233,7 +231,6 @@ config SPI_EFM32
config SPI_EP93XX
tristate "Cirrus Logic EP93xx SPI controller"
depends on HAS_DMA
depends on ARCH_EP93XX || COMPILE_TEST
help
This enables using the Cirrus EP93xx SPI controller in master
@ -355,7 +352,6 @@ config SPI_FSL_SPI
config SPI_FSL_DSPI
tristate "Freescale DSPI controller"
select REGMAP_MMIO
depends on HAS_DMA
depends on SOC_VF610 || SOC_LS1021A || ARCH_LAYERSCAPE || M5441x || COMPILE_TEST
help
This enables support for the Freescale DSPI controller in master
@ -431,7 +427,6 @@ config SPI_OMAP_UWIRE
config SPI_OMAP24XX
tristate "McSPI driver for OMAP"
depends on HAS_DMA
depends on ARCH_OMAP2PLUS || COMPILE_TEST
select SG_SPLIT
help
@ -440,7 +435,6 @@ config SPI_OMAP24XX
config SPI_TI_QSPI
tristate "DRA7xxx QSPI controller support"
depends on HAS_DMA
depends on ARCH_OMAP2PLUS || COMPILE_TEST
help
QSPI master controller for DRA7xxx used for flash devices.
@ -469,7 +463,6 @@ config SPI_PIC32
config SPI_PIC32_SQI
tristate "Microchip PIC32 Quad SPI driver"
depends on MACH_PIC32 || COMPILE_TEST
depends on HAS_DMA
help
SPI driver for PIC32 Quad SPI controller.
@ -572,7 +565,7 @@ config SPI_SC18IS602
config SPI_SH_MSIOF
tristate "SuperH MSIOF SPI controller"
depends on HAVE_CLK && HAS_DMA
depends on HAVE_CLK
depends on ARCH_SHMOBILE || ARCH_RENESAS || COMPILE_TEST
help
SPI driver for SuperH and SH Mobile MSIOF blocks.
@ -650,7 +643,7 @@ config SPI_MXS
config SPI_TEGRA114
tristate "NVIDIA Tegra114 SPI Controller"
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
depends on RESET_CONTROLLER && HAS_DMA
depends on RESET_CONTROLLER
help
SPI driver for NVIDIA Tegra114 SPI Controller interface. This controller
is different than the older SoCs SPI controller and also register interface
@ -668,7 +661,7 @@ config SPI_TEGRA20_SFLASH
config SPI_TEGRA20_SLINK
tristate "Nvidia Tegra20/Tegra30 SLINK Controller"
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
depends on RESET_CONTROLLER && HAS_DMA
depends on RESET_CONTROLLER
help
SPI driver for Nvidia Tegra20/Tegra30 SLINK Controller interface.

View File

@ -8,6 +8,7 @@ ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
# small core, mostly translating board-specific
# config declarations into driver model code
obj-$(CONFIG_SPI_MASTER) += spi.o
obj-$(CONFIG_SPI_MEM) += spi-mem.o
obj-$(CONFIG_SPI_SPIDEV) += spidev.o
obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
@ -20,7 +21,6 @@ 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
obj-$(CONFIG_SPI_BCM63XX) += spi-bcm63xx.o
obj-$(CONFIG_SPI_BCM63XX_HSSPI) += spi-bcm63xx-hsspi.o
obj-$(CONFIG_SPI_BCM_QSPI) += spi-iproc-qspi.o spi-brcmstb-qspi.o spi-bcm-qspi.o

43
drivers/spi/internals.h Normal file
View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
*
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
*
* Helpers needed by the spi or spi-mem logic. Should not be used outside of
* spi-mem.c and spi.c.
*/
#ifndef __LINUX_SPI_INTERNALS_H
#define __LINUX_SPI_INTERNALS_H
#include <linux/device.h>
#include <linux/dma-direction.h>
#include <linux/scatterlist.h>
#include <linux/spi/spi.h>
void spi_flush_queue(struct spi_controller *ctrl);
#ifdef CONFIG_HAS_DMA
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir);
void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, enum dma_data_direction dir);
#else /* !CONFIG_HAS_DMA */
static inline int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir)
{
return -EINVAL;
}
static inline void spi_unmap_buf(struct spi_controller *ctlr,
struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir)
{
}
#endif /* CONFIG_HAS_DMA */
#endif /* __LINUX_SPI_INTERNALS_H */

View File

@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include "spi-bcm-qspi.h"
@ -215,10 +216,10 @@ struct bcm_qspi {
int bspi_maj_rev;
int bspi_min_rev;
int bspi_enabled;
struct spi_flash_read_message *bspi_rf_msg;
u32 bspi_rf_msg_idx;
u32 bspi_rf_msg_len;
u32 bspi_rf_msg_status;
const struct spi_mem_op *bspi_rf_op;
u32 bspi_rf_op_idx;
u32 bspi_rf_op_len;
u32 bspi_rf_op_status;
struct bcm_xfer_mode xfer_mode;
u32 s3_strap_override_ctrl;
bool bspi_mode;
@ -313,26 +314,26 @@ static inline void bcm_qspi_bspi_lr_clear(struct bcm_qspi *qspi)
static void bcm_qspi_bspi_lr_data_read(struct bcm_qspi *qspi)
{
u32 *buf = (u32 *)qspi->bspi_rf_msg->buf;
u32 *buf = (u32 *)qspi->bspi_rf_op->data.buf.in;
u32 data = 0;
dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_msg,
qspi->bspi_rf_msg->buf, qspi->bspi_rf_msg_len);
dev_dbg(&qspi->pdev->dev, "xfer %p rx %p rxlen %d\n", qspi->bspi_rf_op,
qspi->bspi_rf_op->data.buf.in, qspi->bspi_rf_op_len);
while (!bcm_qspi_bspi_lr_is_fifo_empty(qspi)) {
data = bcm_qspi_bspi_lr_read_fifo(qspi);
if (likely(qspi->bspi_rf_msg_len >= 4) &&
if (likely(qspi->bspi_rf_op_len >= 4) &&
IS_ALIGNED((uintptr_t)buf, 4)) {
buf[qspi->bspi_rf_msg_idx++] = data;
qspi->bspi_rf_msg_len -= 4;
buf[qspi->bspi_rf_op_idx++] = data;
qspi->bspi_rf_op_len -= 4;
} else {
/* Read out remaining bytes, make sure*/
u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_msg_idx];
u8 *cbuf = (u8 *)&buf[qspi->bspi_rf_op_idx];
data = cpu_to_le32(data);
while (qspi->bspi_rf_msg_len) {
while (qspi->bspi_rf_op_len) {
*cbuf++ = (u8)data;
data >>= 8;
qspi->bspi_rf_msg_len--;
qspi->bspi_rf_op_len--;
}
}
}
@ -349,14 +350,12 @@ static void bcm_qspi_bspi_set_xfer_params(struct bcm_qspi *qspi, u8 cmd_byte,
}
static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
struct spi_flash_read_message *msg,
int hp)
const struct spi_mem_op *op, int hp)
{
int bpc = 0, bpp = 0;
u8 command = msg->read_opcode;
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
int addrlen = msg->addr_width;
int addr_nbits = msg->addr_nbits ? msg->addr_nbits : SPI_NBITS_SINGLE;
u8 command = op->cmd.opcode;
int width = op->cmd.buswidth ? op->cmd.buswidth : SPI_NBITS_SINGLE;
int addrlen = op->addr.nbytes * 8;
int flex_mode = 1;
dev_dbg(&qspi->pdev->dev, "set flex mode w %x addrlen %x hp %d\n",
@ -365,7 +364,7 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
if (addrlen == BSPI_ADDRLEN_4BYTES)
bpp = BSPI_BPP_ADDR_SELECT_MASK;
bpp |= msg->dummy_bytes * (8/addr_nbits);
bpp |= (op->dummy.nbytes * 8) / op->dummy.buswidth;
switch (width) {
case SPI_NBITS_SINGLE:
@ -397,11 +396,10 @@ static int bcm_qspi_bspi_set_flex_mode(struct bcm_qspi *qspi,
}
static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
struct spi_flash_read_message *msg,
int hp)
const struct spi_mem_op *op, int hp)
{
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
int addrlen = msg->addr_width;
int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
int addrlen = op->addr.nbytes;
u32 data = bcm_qspi_read(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL);
dev_dbg(&qspi->pdev->dev, "set override mode w %x addrlen %x hp %d\n",
@ -437,17 +435,17 @@ static int bcm_qspi_bspi_set_override(struct bcm_qspi *qspi,
/* set the override mode */
data |= BSPI_STRAP_OVERRIDE_CTRL_OVERRIDE;
bcm_qspi_write(qspi, BSPI, BSPI_STRAP_OVERRIDE_CTRL, data);
bcm_qspi_bspi_set_xfer_params(qspi, msg->read_opcode, 0, 0, 0);
bcm_qspi_bspi_set_xfer_params(qspi, op->cmd.opcode, 0, 0, 0);
return 0;
}
static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
struct spi_flash_read_message *msg, int hp)
const struct spi_mem_op *op, int hp)
{
int error = 0;
int width = msg->data_nbits ? msg->data_nbits : SPI_NBITS_SINGLE;
int addrlen = msg->addr_width;
int width = op->data.buswidth ? op->data.buswidth : SPI_NBITS_SINGLE;
int addrlen = op->addr.nbytes;
/* default mode */
qspi->xfer_mode.flex_mode = true;
@ -460,12 +458,12 @@ static int bcm_qspi_bspi_set_mode(struct bcm_qspi *qspi,
if (val & mask || qspi->s3_strap_override_ctrl & mask) {
qspi->xfer_mode.flex_mode = false;
bcm_qspi_write(qspi, BSPI, BSPI_FLEX_MODE_ENABLE, 0);
error = bcm_qspi_bspi_set_override(qspi, msg, hp);
error = bcm_qspi_bspi_set_override(qspi, op, hp);
}
}
if (qspi->xfer_mode.flex_mode)
error = bcm_qspi_bspi_set_flex_mode(qspi, msg, hp);
error = bcm_qspi_bspi_set_flex_mode(qspi, op, hp);
if (error) {
dev_warn(&qspi->pdev->dev,
@ -802,19 +800,20 @@ static int write_to_hw(struct bcm_qspi *qspi, struct spi_device *spi)
return slot;
}
static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
static int bcm_qspi_bspi_exec_mem_op(struct spi_device *spi,
const struct spi_mem_op *op)
{
struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
u32 addr = 0, len, rdlen, len_words;
u32 addr = 0, len, rdlen, len_words, from = 0;
int ret = 0;
unsigned long timeo = msecs_to_jiffies(100);
struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
if (bcm_qspi_bspi_ver_three(qspi))
if (msg->addr_width == BSPI_ADDRLEN_4BYTES)
if (op->addr.nbytes == BSPI_ADDRLEN_4BYTES)
return -EIO;
from = op->addr.val;
bcm_qspi_chip_select(qspi, spi->chip_select);
bcm_qspi_write(qspi, MSPI, MSPI_WRITE_LOCK, 0);
@ -823,15 +822,15 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
* the upper address byte to bspi
*/
if (bcm_qspi_bspi_ver_three(qspi) == false) {
addr = msg->from & 0xff000000;
addr = from & 0xff000000;
bcm_qspi_write(qspi, BSPI,
BSPI_BSPI_FLASH_UPPER_ADDR_BYTE, addr);
}
if (!qspi->xfer_mode.flex_mode)
addr = msg->from;
addr = from;
else
addr = msg->from & 0x00ffffff;
addr = from & 0x00ffffff;
if (bcm_qspi_bspi_ver_three(qspi) == true)
addr = (addr + 0xc00000) & 0xffffff;
@ -840,8 +839,8 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
* read into the entire buffer by breaking the reads
* into RAF buffer read lengths
*/
len = msg->len;
qspi->bspi_rf_msg_idx = 0;
len = op->data.nbytes;
qspi->bspi_rf_op_idx = 0;
do {
if (len > BSPI_READ_LENGTH)
@ -852,9 +851,9 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
reinit_completion(&qspi->bspi_done);
bcm_qspi_enable_bspi(qspi);
len_words = (rdlen + 3) >> 2;
qspi->bspi_rf_msg = msg;
qspi->bspi_rf_msg_status = 0;
qspi->bspi_rf_msg_len = rdlen;
qspi->bspi_rf_op = op;
qspi->bspi_rf_op_status = 0;
qspi->bspi_rf_op_len = rdlen;
dev_dbg(&qspi->pdev->dev,
"bspi xfr addr 0x%x len 0x%x", addr, rdlen);
bcm_qspi_write(qspi, BSPI, BSPI_RAF_START_ADDR, addr);
@ -879,7 +878,6 @@ static int bcm_qspi_bspi_flash_read(struct spi_device *spi,
}
/* set msg return length */
msg->retlen += rdlen;
addr += rdlen;
len -= rdlen;
} while (len);
@ -914,61 +912,63 @@ static int bcm_qspi_transfer_one(struct spi_master *master,
return 0;
}
static int bcm_qspi_mspi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
static int bcm_qspi_mspi_exec_mem_op(struct spi_device *spi,
const struct spi_mem_op *op)
{
struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
struct spi_master *master = spi->master;
struct bcm_qspi *qspi = spi_master_get_devdata(master);
struct spi_transfer t[2];
u8 cmd[6];
int ret;
u8 cmd[6] = { };
int ret, i;
memset(cmd, 0, sizeof(cmd));
memset(t, 0, sizeof(t));
/* tx */
/* opcode is in cmd[0] */
cmd[0] = msg->read_opcode;
cmd[1] = msg->from >> (msg->addr_width * 8 - 8);
cmd[2] = msg->from >> (msg->addr_width * 8 - 16);
cmd[3] = msg->from >> (msg->addr_width * 8 - 24);
cmd[4] = msg->from >> (msg->addr_width * 8 - 32);
cmd[0] = op->cmd.opcode;
for (i = 0; i < op->addr.nbytes; i++)
cmd[1 + i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
t[0].tx_buf = cmd;
t[0].len = msg->addr_width + msg->dummy_bytes + 1;
t[0].len = op->addr.nbytes + op->dummy.nbytes + 1;
t[0].bits_per_word = spi->bits_per_word;
t[0].tx_nbits = msg->opcode_nbits;
t[0].tx_nbits = op->cmd.buswidth;
/* lets mspi know that this is not last transfer */
qspi->trans_pos.mspi_last_trans = false;
ret = bcm_qspi_transfer_one(spi->master, spi, &t[0]);
ret = bcm_qspi_transfer_one(master, spi, &t[0]);
/* rx */
qspi->trans_pos.mspi_last_trans = true;
if (!ret) {
/* rx */
t[1].rx_buf = msg->buf;
t[1].len = msg->len;
t[1].rx_nbits = msg->data_nbits;
t[1].rx_buf = op->data.buf.in;
t[1].len = op->data.nbytes;
t[1].rx_nbits = op->data.buswidth;
t[1].bits_per_word = spi->bits_per_word;
ret = bcm_qspi_transfer_one(spi->master, spi, &t[1]);
ret = bcm_qspi_transfer_one(master, spi, &t[1]);
}
if (!ret)
msg->retlen = msg->len;
return ret;
}
static int bcm_qspi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
static int bcm_qspi_exec_mem_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
struct spi_device *spi = mem->spi;
struct bcm_qspi *qspi = spi_master_get_devdata(spi->master);
int ret = 0;
bool mspi_read = false;
u32 addr, len;
u32 addr = 0, len;
u_char *buf;
buf = msg->buf;
addr = msg->from;
len = msg->len;
if (!op->data.nbytes || !op->addr.nbytes || op->addr.nbytes > 4 ||
op->data.dir != SPI_MEM_DATA_IN)
return -ENOTSUPP;
buf = op->data.buf.in;
addr = op->addr.val;
len = op->data.nbytes;
if (bcm_qspi_bspi_ver_three(qspi) == true) {
/*
@ -990,12 +990,12 @@ static int bcm_qspi_flash_read(struct spi_device *spi,
mspi_read = true;
if (mspi_read)
return bcm_qspi_mspi_flash_read(spi, msg);
return bcm_qspi_mspi_exec_mem_op(spi, op);
ret = bcm_qspi_bspi_set_mode(qspi, msg, -1);
ret = bcm_qspi_bspi_set_mode(qspi, op, -1);
if (!ret)
ret = bcm_qspi_bspi_flash_read(spi, msg);
ret = bcm_qspi_bspi_exec_mem_op(spi, op);
return ret;
}
@ -1034,10 +1034,10 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
u32 status = qspi_dev_id->irqp->mask;
if (qspi->bspi_enabled && qspi->bspi_rf_msg) {
if (qspi->bspi_enabled && qspi->bspi_rf_op) {
bcm_qspi_bspi_lr_data_read(qspi);
if (qspi->bspi_rf_msg_len == 0) {
qspi->bspi_rf_msg = NULL;
if (qspi->bspi_rf_op_len == 0) {
qspi->bspi_rf_op = NULL;
if (qspi->soc_intc) {
/* disable soc BSPI interrupt */
soc_intc->bcm_qspi_int_set(soc_intc, BSPI_DONE,
@ -1046,7 +1046,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
status = INTR_BSPI_LR_SESSION_DONE_MASK;
}
if (qspi->bspi_rf_msg_status)
if (qspi->bspi_rf_op_status)
bcm_qspi_bspi_lr_clear(qspi);
else
bcm_qspi_bspi_flush_prefetch_buffers(qspi);
@ -1058,7 +1058,7 @@ static irqreturn_t bcm_qspi_bspi_lr_l2_isr(int irq, void *dev_id)
}
status &= INTR_BSPI_LR_SESSION_DONE_MASK;
if (qspi->bspi_enabled && status && qspi->bspi_rf_msg_len == 0)
if (qspi->bspi_enabled && status && qspi->bspi_rf_op_len == 0)
complete(&qspi->bspi_done);
return IRQ_HANDLED;
@ -1071,7 +1071,7 @@ static irqreturn_t bcm_qspi_bspi_lr_err_l2_isr(int irq, void *dev_id)
struct bcm_qspi_soc_intc *soc_intc = qspi->soc_intc;
dev_err(&qspi->pdev->dev, "BSPI INT error\n");
qspi->bspi_rf_msg_status = -EIO;
qspi->bspi_rf_op_status = -EIO;
if (qspi->soc_intc)
/* clear soc interrupt */
soc_intc->bcm_qspi_int_ack(soc_intc, BSPI_ERR);
@ -1194,6 +1194,10 @@ static void bcm_qspi_hw_uninit(struct bcm_qspi *qspi)
}
static const struct spi_controller_mem_ops bcm_qspi_mem_ops = {
.exec_op = bcm_qspi_exec_mem_op,
};
static const struct of_device_id bcm_qspi_of_match[] = {
{ .compatible = "brcm,spi-bcm-qspi" },
{},
@ -1236,7 +1240,7 @@ int bcm_qspi_probe(struct platform_device *pdev,
master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_RX_DUAL | SPI_RX_QUAD;
master->setup = bcm_qspi_setup;
master->transfer_one = bcm_qspi_transfer_one;
master->spi_flash_read = bcm_qspi_flash_read;
master->mem_ops = &bcm_qspi_mem_ops;
master->cleanup = bcm_qspi_cleanup;
master->dev.of_node = dev->of_node;
master->num_chipselect = NUM_CHIPSELECT;

View File

@ -1,360 +0,0 @@
/*
* Copyright (C) 2014-2016 Rafał Miłecki <rafal@milecki.pl>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/bcma/bcma.h>
#include <linux/spi/spi.h>
#include "spi-bcm53xx.h"
#define BCM53XXSPI_MAX_SPI_BAUD 13500000 /* 216 MHz? */
#define BCM53XXSPI_FLASH_WINDOW SZ_32M
/* The longest observed required wait was 19 ms */
#define BCM53XXSPI_SPE_TIMEOUT_MS 80
struct bcm53xxspi {
struct bcma_device *core;
struct spi_master *master;
void __iomem *mmio_base;
bool bspi; /* Boot SPI mode with memory mapping */
};
static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset)
{
return bcma_read32(b53spi->core, offset);
}
static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset,
u32 value)
{
bcma_write32(b53spi->core, offset, value);
}
static void bcm53xxspi_disable_bspi(struct bcm53xxspi *b53spi)
{
struct device *dev = &b53spi->core->dev;
unsigned long deadline;
u32 tmp;
if (!b53spi->bspi)
return;
tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL);
if (tmp & 0x1)
return;
deadline = jiffies + usecs_to_jiffies(200);
do {
tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_BUSY_STATUS);
if (!(tmp & 0x1)) {
bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL,
0x1);
ndelay(200);
b53spi->bspi = false;
return;
}
udelay(1);
} while (!time_after_eq(jiffies, deadline));
dev_warn(dev, "Timeout disabling BSPI\n");
}
static void bcm53xxspi_enable_bspi(struct bcm53xxspi *b53spi)
{
u32 tmp;
if (b53spi->bspi)
return;
tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL);
if (!(tmp & 0x1))
return;
bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL, 0x0);
b53spi->bspi = true;
}
static inline unsigned int bcm53xxspi_calc_timeout(size_t len)
{
/* Do some magic calculation based on length and buad. Add 10% and 1. */
return (len * 9000 / BCM53XXSPI_MAX_SPI_BAUD * 110 / 100) + 1;
}
static int bcm53xxspi_wait(struct bcm53xxspi *b53spi, unsigned int timeout_ms)
{
unsigned long deadline;
u32 tmp;
/* SPE bit has to be 0 before we read MSPI STATUS */
deadline = jiffies + msecs_to_jiffies(BCM53XXSPI_SPE_TIMEOUT_MS);
do {
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
if (!(tmp & B53SPI_MSPI_SPCR2_SPE))
break;
udelay(5);
} while (!time_after_eq(jiffies, deadline));
if (tmp & B53SPI_MSPI_SPCR2_SPE)
goto spi_timeout;
/* Check status */
deadline = jiffies + msecs_to_jiffies(timeout_ms);
do {
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_MSPI_STATUS);
if (tmp & B53SPI_MSPI_MSPI_STATUS_SPIF) {
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
return 0;
}
cpu_relax();
udelay(100);
} while (!time_after_eq(jiffies, deadline));
spi_timeout:
bcm53xxspi_write(b53spi, B53SPI_MSPI_MSPI_STATUS, 0);
pr_err("Timeout waiting for SPI to be ready!\n");
return -EBUSY;
}
static void bcm53xxspi_buf_write(struct bcm53xxspi *b53spi, u8 *w_buf,
size_t len, bool cont)
{
u32 tmp;
int i;
for (i = 0; i < len; i++) {
/* Transmit Register File MSB */
bcm53xxspi_write(b53spi, B53SPI_MSPI_TXRAM + 4 * (i * 2),
(unsigned int)w_buf[i]);
}
for (i = 0; i < len; i++) {
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
B53SPI_CDRAM_PCS_DSCK;
if (!cont && i == len - 1)
tmp &= ~B53SPI_CDRAM_CONT;
tmp &= ~0x1;
/* Command Register File */
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
}
/* Set queue pointers */
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
if (cont)
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
/* Start SPI transfer */
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
tmp |= B53SPI_MSPI_SPCR2_SPE;
if (cont)
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
/* Wait for SPI to finish */
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
if (!cont)
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
}
static void bcm53xxspi_buf_read(struct bcm53xxspi *b53spi, u8 *r_buf,
size_t len, bool cont)
{
u32 tmp;
int i;
for (i = 0; i < len; i++) {
tmp = B53SPI_CDRAM_CONT | B53SPI_CDRAM_PCS_DISABLE_ALL |
B53SPI_CDRAM_PCS_DSCK;
if (!cont && i == len - 1)
tmp &= ~B53SPI_CDRAM_CONT;
tmp &= ~0x1;
/* Command Register File */
bcm53xxspi_write(b53spi, B53SPI_MSPI_CDRAM + 4 * i, tmp);
}
/* Set queue pointers */
bcm53xxspi_write(b53spi, B53SPI_MSPI_NEWQP, 0);
bcm53xxspi_write(b53spi, B53SPI_MSPI_ENDQP, len - 1);
if (cont)
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 1);
/* Start SPI transfer */
tmp = bcm53xxspi_read(b53spi, B53SPI_MSPI_SPCR2);
tmp |= B53SPI_MSPI_SPCR2_SPE;
if (cont)
tmp |= B53SPI_MSPI_SPCR2_CONT_AFTER_CMD;
bcm53xxspi_write(b53spi, B53SPI_MSPI_SPCR2, tmp);
/* Wait for SPI to finish */
bcm53xxspi_wait(b53spi, bcm53xxspi_calc_timeout(len));
if (!cont)
bcm53xxspi_write(b53spi, B53SPI_MSPI_WRITE_LOCK, 0);
for (i = 0; i < len; ++i) {
u16 reg = B53SPI_MSPI_RXRAM + 4 * (1 + i * 2);
/* Data stored in the transmit register file LSB */
r_buf[i] = (u8)bcm53xxspi_read(b53spi, reg);
}
}
static int bcm53xxspi_transfer_one(struct spi_master *master,
struct spi_device *spi,
struct spi_transfer *t)
{
struct bcm53xxspi *b53spi = spi_master_get_devdata(master);
u8 *buf;
size_t left;
bcm53xxspi_disable_bspi(b53spi);
if (t->tx_buf) {
buf = (u8 *)t->tx_buf;
left = t->len;
while (left) {
size_t to_write = min_t(size_t, 16, left);
bool cont = !spi_transfer_is_last(master, t) ||
left - to_write > 0;
bcm53xxspi_buf_write(b53spi, buf, to_write, cont);
left -= to_write;
buf += to_write;
}
}
if (t->rx_buf) {
buf = (u8 *)t->rx_buf;
left = t->len;
while (left) {
size_t to_read = min_t(size_t, 16, left);
bool cont = !spi_transfer_is_last(master, t) ||
left - to_read > 0;
bcm53xxspi_buf_read(b53spi, buf, to_read, cont);
left -= to_read;
buf += to_read;
}
}
return 0;
}
static int bcm53xxspi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
{
struct bcm53xxspi *b53spi = spi_master_get_devdata(spi->master);
int ret = 0;
if (msg->from + msg->len > BCM53XXSPI_FLASH_WINDOW)
return -EINVAL;
bcm53xxspi_enable_bspi(b53spi);
memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len);
msg->retlen = msg->len;
return ret;
}
/**************************************************
* BCMA
**************************************************/
static const struct bcma_device_id bcm53xxspi_bcma_tbl[] = {
BCMA_CORE(BCMA_MANUF_BCM, BCMA_CORE_NS_QSPI, BCMA_ANY_REV, BCMA_ANY_CLASS),
{},
};
MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl);
static int bcm53xxspi_bcma_probe(struct bcma_device *core)
{
struct device *dev = &core->dev;
struct bcm53xxspi *b53spi;
struct spi_master *master;
int err;
if (core->bus->drv_cc.core->id.rev != 42) {
pr_err("SPI on SoC with unsupported ChipCommon rev\n");
return -ENOTSUPP;
}
master = spi_alloc_master(dev, sizeof(*b53spi));
if (!master)
return -ENOMEM;
b53spi = spi_master_get_devdata(master);
b53spi->master = master;
b53spi->core = core;
if (core->addr_s[0])
b53spi->mmio_base = devm_ioremap(dev, core->addr_s[0],
BCM53XXSPI_FLASH_WINDOW);
b53spi->bspi = true;
bcm53xxspi_disable_bspi(b53spi);
master->dev.of_node = dev->of_node;
master->transfer_one = bcm53xxspi_transfer_one;
if (b53spi->mmio_base)
master->spi_flash_read = bcm53xxspi_flash_read;
bcma_set_drvdata(core, b53spi);
err = devm_spi_register_master(dev, master);
if (err) {
spi_master_put(master);
bcma_set_drvdata(core, NULL);
return err;
}
return 0;
}
static struct bcma_driver bcm53xxspi_bcma_driver = {
.name = KBUILD_MODNAME,
.id_table = bcm53xxspi_bcma_tbl,
.probe = bcm53xxspi_bcma_probe,
};
/**************************************************
* Init & exit
**************************************************/
static int __init bcm53xxspi_module_init(void)
{
int err = 0;
err = bcma_driver_register(&bcm53xxspi_bcma_driver);
if (err)
pr_err("Failed to register bcma driver: %d\n", err);
return err;
}
static void __exit bcm53xxspi_module_exit(void)
{
bcma_driver_unregister(&bcm53xxspi_bcma_driver);
}
module_init(bcm53xxspi_module_init);
module_exit(bcm53xxspi_module_exit);
MODULE_DESCRIPTION("Broadcom BCM53xx SPI Controller driver");
MODULE_AUTHOR("Rafał Miłecki <zajec5@gmail.com>");
MODULE_LICENSE("GPL v2");

View File

@ -1,73 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef SPI_BCM53XX_H
#define SPI_BCM53XX_H
#define B53SPI_BSPI_REVISION_ID 0x000
#define B53SPI_BSPI_SCRATCH 0x004
#define B53SPI_BSPI_MAST_N_BOOT_CTRL 0x008
#define B53SPI_BSPI_BUSY_STATUS 0x00c
#define B53SPI_BSPI_INTR_STATUS 0x010
#define B53SPI_BSPI_B0_STATUS 0x014
#define B53SPI_BSPI_B0_CTRL 0x018
#define B53SPI_BSPI_B1_STATUS 0x01c
#define B53SPI_BSPI_B1_CTRL 0x020
#define B53SPI_BSPI_STRAP_OVERRIDE_CTRL 0x024
#define B53SPI_BSPI_FLEX_MODE_ENABLE 0x028
#define B53SPI_BSPI_BITS_PER_CYCLE 0x02c
#define B53SPI_BSPI_BITS_PER_PHASE 0x030
#define B53SPI_BSPI_CMD_AND_MODE_BYTE 0x034
#define B53SPI_BSPI_BSPI_FLASH_UPPER_ADDR_BYTE 0x038
#define B53SPI_BSPI_BSPI_XOR_VALUE 0x03c
#define B53SPI_BSPI_BSPI_XOR_ENABLE 0x040
#define B53SPI_BSPI_BSPI_PIO_MODE_ENABLE 0x044
#define B53SPI_BSPI_BSPI_PIO_IODIR 0x048
#define B53SPI_BSPI_BSPI_PIO_DATA 0x04c
/* RAF */
#define B53SPI_RAF_START_ADDR 0x100
#define B53SPI_RAF_NUM_WORDS 0x104
#define B53SPI_RAF_CTRL 0x108
#define B53SPI_RAF_FULLNESS 0x10c
#define B53SPI_RAF_WATERMARK 0x110
#define B53SPI_RAF_STATUS 0x114
#define B53SPI_RAF_READ_DATA 0x118
#define B53SPI_RAF_WORD_CNT 0x11c
#define B53SPI_RAF_CURR_ADDR 0x120
/* MSPI */
#define B53SPI_MSPI_SPCR0_LSB 0x200
#define B53SPI_MSPI_SPCR0_MSB 0x204
#define B53SPI_MSPI_SPCR1_LSB 0x208
#define B53SPI_MSPI_SPCR1_MSB 0x20c
#define B53SPI_MSPI_NEWQP 0x210
#define B53SPI_MSPI_ENDQP 0x214
#define B53SPI_MSPI_SPCR2 0x218
#define B53SPI_MSPI_SPCR2_SPE 0x00000040
#define B53SPI_MSPI_SPCR2_CONT_AFTER_CMD 0x00000080
#define B53SPI_MSPI_MSPI_STATUS 0x220
#define B53SPI_MSPI_MSPI_STATUS_SPIF 0x00000001
#define B53SPI_MSPI_CPTQP 0x224
#define B53SPI_MSPI_TXRAM 0x240 /* 32 registers, up to 0x2b8 */
#define B53SPI_MSPI_RXRAM 0x2c0 /* 32 registers, up to 0x33c */
#define B53SPI_MSPI_CDRAM 0x340 /* 16 registers, up to 0x37c */
#define B53SPI_CDRAM_PCS_PCS0 0x00000001
#define B53SPI_CDRAM_PCS_PCS1 0x00000002
#define B53SPI_CDRAM_PCS_PCS2 0x00000004
#define B53SPI_CDRAM_PCS_PCS3 0x00000008
#define B53SPI_CDRAM_PCS_DISABLE_ALL 0x0000000f
#define B53SPI_CDRAM_PCS_DSCK 0x00000010
#define B53SPI_CDRAM_BITSE 0x00000040
#define B53SPI_CDRAM_CONT 0x00000080
#define B53SPI_MSPI_WRITE_LOCK 0x380
#define B53SPI_MSPI_DISABLE_FLUSH_GEN 0x384
/* Interrupt */
#define B53SPI_INTR_RAF_LR_FULLNESS_REACHED 0x3a0
#define B53SPI_INTR_RAF_LR_TRUNCATED 0x3a4
#define B53SPI_INTR_RAF_LR_IMPATIENT 0x3a8
#define B53SPI_INTR_RAF_LR_SESSION_DONE 0x3ac
#define B53SPI_INTR_RAF_LR_OVERREAD 0x3b0
#define B53SPI_INTR_MSPI_DONE 0x3b4
#define B53SPI_INTR_MSPI_HALT_SET_TRANSACTION_DONE 0x3b8
#endif /* SPI_BCM53XX_H */

View File

@ -352,22 +352,31 @@ static int bcm63xx_hsspi_probe(struct platform_device *pdev)
if (IS_ERR(clk))
return PTR_ERR(clk);
ret = clk_prepare_enable(clk);
if (ret)
return ret;
rate = clk_get_rate(clk);
if (!rate) {
struct clk *pll_clk = devm_clk_get(dev, "pll");
if (IS_ERR(pll_clk))
return PTR_ERR(pll_clk);
if (IS_ERR(pll_clk)) {
ret = PTR_ERR(pll_clk);
goto out_disable_clk;
}
ret = clk_prepare_enable(pll_clk);
if (ret)
goto out_disable_clk;
rate = clk_get_rate(pll_clk);
if (!rate)
return -EINVAL;
clk_disable_unprepare(pll_clk);
if (!rate) {
ret = -EINVAL;
goto out_disable_clk;
}
}
ret = clk_prepare_enable(clk);
if (ret)
return ret;
master = spi_alloc_master(&pdev->dev, sizeof(*bs));
if (!master) {
ret = -ENOMEM;

View File

@ -694,8 +694,7 @@ static int cdns_spi_remove(struct platform_device *pdev)
*/
static int __maybe_unused cdns_spi_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_master *master = dev_get_drvdata(dev);
return spi_master_suspend(master);
}
@ -710,8 +709,7 @@ static int __maybe_unused cdns_spi_suspend(struct device *dev)
*/
static int __maybe_unused cdns_spi_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_master *master = dev_get_drvdata(dev);
struct cdns_spi *xspi = spi_master_get_devdata(master);
cdns_spi_init_hw(xspi);

View File

@ -1,19 +1,8 @@
/*
* Freescale i.MX7ULP LPSPI driver
*
* Copyright 2016 Freescale Semiconductor, Inc.
*
* 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.
*
*/
// SPDX-License-Identifier: GPL-2.0+
//
// Freescale i.MX7ULP LPSPI driver
//
// Copyright 2016 Freescale Semiconductor, Inc.
#include <linux/clk.h>
#include <linux/completion.h>

View File

@ -1,22 +1,6 @@
/*
* Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
* Copyright (C) 2008 Juergen Beisert
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the
* Free Software Foundation
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
// SPDX-License-Identifier: GPL-2.0+
// Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
// Copyright (C) 2008 Juergen Beisert
#include <linux/clk.h>
#include <linux/completion.h>

410
drivers/spi/spi-mem.c Normal file
View File

@ -0,0 +1,410 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
*
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
*/
#include <linux/dmaengine.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include "internals.h"
/**
* spi_controller_dma_map_mem_op_data() - DMA-map the buffer attached to a
* memory operation
* @ctlr: the SPI controller requesting this dma_map()
* @op: the memory operation containing the buffer to map
* @sgt: a pointer to a non-initialized sg_table that will be filled by this
* function
*
* Some controllers might want to do DMA on the data buffer embedded in @op.
* This helper prepares everything for you and provides a ready-to-use
* sg_table. This function is not intended to be called from spi drivers.
* Only SPI controller drivers should use it.
* Note that the caller must ensure the memory region pointed by
* op->data.buf.{in,out} is DMA-able before calling this function.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sgt)
{
struct device *dmadev;
if (!op->data.nbytes)
return -EINVAL;
if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx)
dmadev = ctlr->dma_tx->device->dev;
else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx)
dmadev = ctlr->dma_rx->device->dev;
else
dmadev = ctlr->dev.parent;
if (!dmadev)
return -EINVAL;
return spi_map_buf(ctlr, dmadev, sgt, op->data.buf.in, op->data.nbytes,
op->data.dir == SPI_MEM_DATA_IN ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
EXPORT_SYMBOL_GPL(spi_controller_dma_map_mem_op_data);
/**
* spi_controller_dma_unmap_mem_op_data() - DMA-unmap the buffer attached to a
* memory operation
* @ctlr: the SPI controller requesting this dma_unmap()
* @op: the memory operation containing the buffer to unmap
* @sgt: a pointer to an sg_table previously initialized by
* spi_controller_dma_map_mem_op_data()
*
* Some controllers might want to do DMA on the data buffer embedded in @op.
* This helper prepares things so that the CPU can access the
* op->data.buf.{in,out} buffer again.
*
* This function is not intended to be called from SPI drivers. Only SPI
* controller drivers should use it.
*
* This function should be called after the DMA operation has finished and is
* only valid if the previous spi_controller_dma_map_mem_op_data() call
* returned 0.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sgt)
{
struct device *dmadev;
if (!op->data.nbytes)
return;
if (op->data.dir == SPI_MEM_DATA_OUT && ctlr->dma_tx)
dmadev = ctlr->dma_tx->device->dev;
else if (op->data.dir == SPI_MEM_DATA_IN && ctlr->dma_rx)
dmadev = ctlr->dma_rx->device->dev;
else
dmadev = ctlr->dev.parent;
spi_unmap_buf(ctlr, dmadev, sgt,
op->data.dir == SPI_MEM_DATA_IN ?
DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
EXPORT_SYMBOL_GPL(spi_controller_dma_unmap_mem_op_data);
static int spi_check_buswidth_req(struct spi_mem *mem, u8 buswidth, bool tx)
{
u32 mode = mem->spi->mode;
switch (buswidth) {
case 1:
return 0;
case 2:
if ((tx && (mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ||
(!tx && (mode & (SPI_RX_DUAL | SPI_RX_QUAD))))
return 0;
break;
case 4:
if ((tx && (mode & SPI_TX_QUAD)) ||
(!tx && (mode & SPI_RX_QUAD)))
return 0;
break;
default:
break;
}
return -ENOTSUPP;
}
static bool spi_mem_default_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
if (spi_check_buswidth_req(mem, op->cmd.buswidth, true))
return false;
if (op->addr.nbytes &&
spi_check_buswidth_req(mem, op->addr.buswidth, true))
return false;
if (op->dummy.nbytes &&
spi_check_buswidth_req(mem, op->dummy.buswidth, true))
return false;
if (op->data.nbytes &&
spi_check_buswidth_req(mem, op->data.buswidth,
op->data.dir == SPI_MEM_DATA_OUT))
return false;
return true;
}
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
/**
* spi_mem_supports_op() - Check if a memory device and the controller it is
* connected to support a specific memory operation
* @mem: the SPI memory
* @op: the memory operation to check
*
* Some controllers are only supporting Single or Dual IOs, others might only
* support specific opcodes, or it can even be that the controller and device
* both support Quad IOs but the hardware prevents you from using it because
* only 2 IO lines are connected.
*
* This function checks whether a specific operation is supported.
*
* Return: true if @op is supported, false otherwise.
*/
bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
struct spi_controller *ctlr = mem->spi->controller;
if (ctlr->mem_ops && ctlr->mem_ops->supports_op)
return ctlr->mem_ops->supports_op(mem, op);
return spi_mem_default_supports_op(mem, op);
}
EXPORT_SYMBOL_GPL(spi_mem_supports_op);
/**
* spi_mem_exec_op() - Execute a memory operation
* @mem: the SPI memory
* @op: the memory operation to execute
*
* Executes a memory operation.
*
* This function first checks that @op is supported and then tries to execute
* it.
*
* Return: 0 in case of success, a negative error code otherwise.
*/
int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
{
unsigned int tmpbufsize, xferpos = 0, totalxferlen = 0;
struct spi_controller *ctlr = mem->spi->controller;
struct spi_transfer xfers[4] = { };
struct spi_message msg;
u8 *tmpbuf;
int ret;
if (!spi_mem_supports_op(mem, op))
return -ENOTSUPP;
if (ctlr->mem_ops) {
/*
* Flush the message queue before executing our SPI memory
* operation to prevent preemption of regular SPI transfers.
*/
spi_flush_queue(ctlr);
if (ctlr->auto_runtime_pm) {
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
dev_err(&ctlr->dev,
"Failed to power device: %d\n",
ret);
return ret;
}
}
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
ret = ctlr->mem_ops->exec_op(mem, op);
mutex_unlock(&ctlr->io_mutex);
mutex_unlock(&ctlr->bus_lock_mutex);
if (ctlr->auto_runtime_pm)
pm_runtime_put(ctlr->dev.parent);
/*
* Some controllers only optimize specific paths (typically the
* read path) and expect the core to use the regular SPI
* interface in other cases.
*/
if (!ret || ret != -ENOTSUPP)
return ret;
}
tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
op->dummy.nbytes;
/*
* Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
* we're guaranteed that this buffer is DMA-able, as required by the
* SPI layer.
*/
tmpbuf = kzalloc(tmpbufsize, GFP_KERNEL | GFP_DMA);
if (!tmpbuf)
return -ENOMEM;
spi_message_init(&msg);
tmpbuf[0] = op->cmd.opcode;
xfers[xferpos].tx_buf = tmpbuf;
xfers[xferpos].len = sizeof(op->cmd.opcode);
xfers[xferpos].tx_nbits = op->cmd.buswidth;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen++;
if (op->addr.nbytes) {
int i;
for (i = 0; i < op->addr.nbytes; i++)
tmpbuf[i + 1] = op->addr.val >>
(8 * (op->addr.nbytes - i - 1));
xfers[xferpos].tx_buf = tmpbuf + 1;
xfers[xferpos].len = op->addr.nbytes;
xfers[xferpos].tx_nbits = op->addr.buswidth;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->addr.nbytes;
}
if (op->dummy.nbytes) {
memset(tmpbuf + op->addr.nbytes + 1, 0xff, op->dummy.nbytes);
xfers[xferpos].tx_buf = tmpbuf + op->addr.nbytes + 1;
xfers[xferpos].len = op->dummy.nbytes;
xfers[xferpos].tx_nbits = op->dummy.buswidth;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->dummy.nbytes;
}
if (op->data.nbytes) {
if (op->data.dir == SPI_MEM_DATA_IN) {
xfers[xferpos].rx_buf = op->data.buf.in;
xfers[xferpos].rx_nbits = op->data.buswidth;
} else {
xfers[xferpos].tx_buf = op->data.buf.out;
xfers[xferpos].tx_nbits = op->data.buswidth;
}
xfers[xferpos].len = op->data.nbytes;
spi_message_add_tail(&xfers[xferpos], &msg);
xferpos++;
totalxferlen += op->data.nbytes;
}
ret = spi_sync(mem->spi, &msg);
kfree(tmpbuf);
if (ret)
return ret;
if (msg.actual_length != totalxferlen)
return -EIO;
return 0;
}
EXPORT_SYMBOL_GPL(spi_mem_exec_op);
/**
* spi_mem_adjust_op_size() - Adjust the data size of a SPI mem operation to
* match controller limitations
* @mem: the SPI memory
* @op: the operation to adjust
*
* Some controllers have FIFO limitations and must split a data transfer
* operation into multiple ones, others require a specific alignment for
* optimized accesses. This function allows SPI mem drivers to split a single
* operation into multiple sub-operations when required.
*
* Return: a negative error code if the controller can't properly adjust @op,
* 0 otherwise. Note that @op->data.nbytes will be updated if @op
* can't be handled in a single step.
*/
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
{
struct spi_controller *ctlr = mem->spi->controller;
if (ctlr->mem_ops && ctlr->mem_ops->adjust_op_size)
return ctlr->mem_ops->adjust_op_size(mem, op);
return 0;
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
{
return container_of(drv, struct spi_mem_driver, spidrv.driver);
}
static int spi_mem_probe(struct spi_device *spi)
{
struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
struct spi_mem *mem;
mem = devm_kzalloc(&spi->dev, sizeof(*mem), GFP_KERNEL);
if (!mem)
return -ENOMEM;
mem->spi = spi;
spi_set_drvdata(spi, mem);
return memdrv->probe(mem);
}
static int spi_mem_remove(struct spi_device *spi)
{
struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
struct spi_mem *mem = spi_get_drvdata(spi);
if (memdrv->remove)
return memdrv->remove(mem);
return 0;
}
static void spi_mem_shutdown(struct spi_device *spi)
{
struct spi_mem_driver *memdrv = to_spi_mem_drv(spi->dev.driver);
struct spi_mem *mem = spi_get_drvdata(spi);
if (memdrv->shutdown)
memdrv->shutdown(mem);
}
/**
* spi_mem_driver_register_with_owner() - Register a SPI memory driver
* @memdrv: the SPI memory driver to register
* @owner: the owner of this driver
*
* Registers a SPI memory driver.
*
* Return: 0 in case of success, a negative error core otherwise.
*/
int spi_mem_driver_register_with_owner(struct spi_mem_driver *memdrv,
struct module *owner)
{
memdrv->spidrv.probe = spi_mem_probe;
memdrv->spidrv.remove = spi_mem_remove;
memdrv->spidrv.shutdown = spi_mem_shutdown;
return __spi_register_driver(owner, &memdrv->spidrv);
}
EXPORT_SYMBOL_GPL(spi_mem_driver_register_with_owner);
/**
* spi_mem_driver_unregister_with_owner() - Unregister a SPI memory driver
* @memdrv: the SPI memory driver to unregister
*
* Unregisters a SPI memory driver.
*/
void spi_mem_driver_unregister(struct spi_mem_driver *memdrv)
{
spi_unregister_driver(&memdrv->spidrv);
}
EXPORT_SYMBOL_GPL(spi_mem_driver_unregister);

View File

@ -574,10 +574,15 @@ static int meson_spicc_probe(struct platform_device *pdev)
master->max_speed_hz = rate >> 2;
ret = devm_spi_register_master(&pdev->dev, master);
if (!ret)
return 0;
if (ret) {
dev_err(&pdev->dev, "spi master registration failed\n");
goto out_clk;
}
dev_err(&pdev->dev, "spi master registration failed\n");
return 0;
out_clk:
clk_disable_unprepare(spicc->core);
out_master:
spi_master_put(master);

View File

@ -447,7 +447,7 @@ static int mpc52xx_spi_probe(struct platform_device *op)
for (i = 0; i < ms->gpio_cs_count; i++) {
gpio_cs = of_get_gpio(op->dev.of_node, i);
if (gpio_cs < 0) {
if (!gpio_is_valid(gpio_cs)) {
dev_err(&op->dev,
"could not parse the gpio field in oftree\n");
rc = -ENODEV;

View File

@ -1,32 +1,22 @@
/*
* Freescale MXS SPI master driver
*
* Copyright 2012 DENX Software Engineering, GmbH.
* Copyright 2012 Freescale Semiconductor, Inc.
* Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
*
* Rework and transition to new API by:
* Marek Vasut <marex@denx.de>
*
* Based on previous attempt by:
* Fabio Estevam <fabio.estevam@freescale.com>
*
* Based on code from U-Boot bootloader by:
* Marek Vasut <marex@denx.de>
*
* Based on spi-stmp.c, which is:
* Author: Dmitry Pervushin <dimka@embeddedalley.com>
*
* 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.
*/
// SPDX-License-Identifier: GPL-2.0+
//
// Freescale MXS SPI master driver
//
// Copyright 2012 DENX Software Engineering, GmbH.
// Copyright 2012 Freescale Semiconductor, Inc.
// Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
//
// Rework and transition to new API by:
// Marek Vasut <marex@denx.de>
//
// Based on previous attempt by:
// Fabio Estevam <fabio.estevam@freescale.com>
//
// Based on code from U-Boot bootloader by:
// Marek Vasut <marex@denx.de>
//
// Based on spi-stmp.c, which is:
// Author: Dmitry Pervushin <dimka@embeddedalley.com>
#include <linux/kernel.h>
#include <linux/ioport.h>

View File

@ -255,6 +255,7 @@ static void omap2_mcspi_set_cs(struct spi_device *spi, bool enable)
if (spi->controller_state) {
int err = pm_runtime_get_sync(mcspi->dev);
if (err < 0) {
pm_runtime_put_noidle(mcspi->dev);
dev_err(mcspi->dev, "failed to get sync: %d\n", err);
return;
}
@ -350,20 +351,6 @@ static void omap2_mcspi_set_fifo(const struct spi_device *spi,
mcspi->fifo_depth = 0;
}
static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
{
struct spi_master *spi_cntrl = mcspi->master;
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
struct omap2_mcspi_cs *cs;
/* McSPI: context restore */
mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL, ctx->modulctrl);
mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE, ctx->wakeupenable);
list_for_each_entry(cs, &ctx->cs, node)
writel_relaxed(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
}
static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
{
unsigned long timeout;
@ -1065,8 +1052,11 @@ static int omap2_mcspi_setup(struct spi_device *spi)
}
ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0)
if (ret < 0) {
pm_runtime_put_noidle(mcspi->dev);
return ret;
}
ret = omap2_mcspi_setup_transfer(spi, NULL);
pm_runtime_mark_last_busy(mcspi->dev);
@ -1284,8 +1274,11 @@ static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
int ret = 0;
ret = pm_runtime_get_sync(mcspi->dev);
if (ret < 0)
if (ret < 0) {
pm_runtime_put_noidle(mcspi->dev);
return ret;
}
mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE,
OMAP2_MCSPI_WAKEUPENABLE_WKEN);
@ -1297,14 +1290,39 @@ static int omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
return 0;
}
/*
* When SPI wake up from off-mode, CS is in activate state. If it was in
* inactive state when driver was suspend, then force it to inactive state at
* wake up.
*/
static int omap_mcspi_runtime_resume(struct device *dev)
{
struct omap2_mcspi *mcspi;
struct spi_master *master;
struct spi_master *master = dev_get_drvdata(dev);
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
struct omap2_mcspi_cs *cs;
master = dev_get_drvdata(dev);
mcspi = spi_master_get_devdata(master);
omap2_mcspi_restore_ctx(mcspi);
/* McSPI: context restore */
mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, ctx->modulctrl);
mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, ctx->wakeupenable);
list_for_each_entry(cs, &ctx->cs, node) {
/*
* We need to toggle CS state for OMAP take this
* change in account.
*/
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE;
writel_relaxed(cs->chconf0,
cs->base + OMAP2_MCSPI_CHCONF0);
cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
writel_relaxed(cs->chconf0,
cs->base + OMAP2_MCSPI_CHCONF0);
} else {
writel_relaxed(cs->chconf0,
cs->base + OMAP2_MCSPI_CHCONF0);
}
}
return 0;
}
@ -1447,50 +1465,33 @@ static int omap2_mcspi_remove(struct platform_device *pdev)
MODULE_ALIAS("platform:omap2_mcspi");
#ifdef CONFIG_SUSPEND
/*
* When SPI wake up from off-mode, CS is in activate state. If it was in
* unactive state when driver was suspend, then force it to unactive state at
* wake up.
*/
static int omap2_mcspi_resume(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
struct omap2_mcspi_regs *ctx = &mcspi->ctx;
struct omap2_mcspi_cs *cs;
pm_runtime_get_sync(mcspi->dev);
list_for_each_entry(cs, &ctx->cs, node) {
if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
/*
* We need to toggle CS state for OMAP take this
* change in account.
*/
cs->chconf0 |= OMAP2_MCSPI_CHCONF_FORCE;
writel_relaxed(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
cs->chconf0 &= ~OMAP2_MCSPI_CHCONF_FORCE;
writel_relaxed(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
}
}
pm_runtime_mark_last_busy(mcspi->dev);
pm_runtime_put_autosuspend(mcspi->dev);
return pinctrl_pm_select_default_state(dev);
}
static int omap2_mcspi_suspend(struct device *dev)
static int omap2_mcspi_suspend_noirq(struct device *dev)
{
return pinctrl_pm_select_sleep_state(dev);
}
static int omap2_mcspi_resume_noirq(struct device *dev)
{
struct spi_master *master = dev_get_drvdata(dev);
struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
int error;
error = pinctrl_pm_select_default_state(dev);
if (error)
dev_warn(mcspi->dev, "%s: failed to set pins: %i\n",
__func__, error);
return 0;
}
#else
#define omap2_mcspi_suspend NULL
#define omap2_mcspi_resume NULL
#define omap2_mcspi_suspend_noirq NULL
#define omap2_mcspi_resume_noirq NULL
#endif
static const struct dev_pm_ops omap2_mcspi_pm_ops = {
.resume = omap2_mcspi_resume,
.suspend = omap2_mcspi_suspend,
.suspend_noirq = omap2_mcspi_suspend_noirq,
.resume_noirq = omap2_mcspi_resume_noirq,
.runtime_resume = omap_mcspi_runtime_resume,
};

View File

@ -51,19 +51,15 @@ static void pxa2xx_spi_dma_transfer_complete(struct driver_data *drv_data,
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
if (!error) {
msg->actual_length += drv_data->len;
msg->state = pxa2xx_spi_next_transfer(drv_data);
} else {
if (error) {
/* In case we got an error we disable the SSP now */
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0)
& ~SSCR0_SSE);
msg->state = ERROR_STATE;
msg->status = -EIO;
}
tasklet_schedule(&drv_data->pump_transfers);
spi_finalize_current_transfer(drv_data->master);
}
}
@ -74,11 +70,11 @@ static void pxa2xx_spi_dma_callback(void *data)
static struct dma_async_tx_descriptor *
pxa2xx_spi_dma_prepare_one(struct driver_data *drv_data,
enum dma_transfer_direction dir)
enum dma_transfer_direction dir,
struct spi_transfer *xfer)
{
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
struct spi_transfer *xfer = drv_data->cur_transfer;
enum dma_slave_buswidth width;
struct dma_slave_config cfg;
struct dma_chan *chan;
@ -144,12 +140,13 @@ irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data)
return IRQ_NONE;
}
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
int pxa2xx_spi_dma_prepare(struct driver_data *drv_data,
struct spi_transfer *xfer)
{
struct dma_async_tx_descriptor *tx_desc, *rx_desc;
int err;
tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV);
tx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_MEM_TO_DEV, xfer);
if (!tx_desc) {
dev_err(&drv_data->pdev->dev,
"failed to get DMA TX descriptor\n");
@ -157,7 +154,7 @@ int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst)
goto err_tx;
}
rx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM);
rx_desc = pxa2xx_spi_dma_prepare_one(drv_data, DMA_DEV_TO_MEM, xfer);
if (!rx_desc) {
dev_err(&drv_data->pdev->dev,
"failed to get DMA RX descriptor\n");
@ -187,6 +184,13 @@ void pxa2xx_spi_dma_start(struct driver_data *drv_data)
atomic_set(&drv_data->dma_running, 1);
}
void pxa2xx_spi_dma_stop(struct driver_data *drv_data)
{
atomic_set(&drv_data->dma_running, 0);
dmaengine_terminate_sync(drv_data->master->dma_rx);
dmaengine_terminate_sync(drv_data->master->dma_tx);
}
int pxa2xx_spi_dma_setup(struct driver_data *drv_data)
{
struct pxa2xx_spi_master *pdata = drv_data->master_info;

View File

@ -340,9 +340,11 @@ static void lpss_ssp_setup(struct driver_data *drv_data)
}
}
static void lpss_ssp_select_cs(struct driver_data *drv_data,
static void lpss_ssp_select_cs(struct spi_device *spi,
const struct lpss_config *config)
{
struct driver_data *drv_data =
spi_controller_get_devdata(spi->controller);
u32 value, cs;
if (!config->cs_sel_mask)
@ -350,7 +352,7 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data,
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
cs = drv_data->master->cur_msg->spi->chip_select;
cs = spi->chip_select;
cs <<= config->cs_sel_shift;
if (cs != (value & config->cs_sel_mask)) {
/*
@ -369,15 +371,17 @@ static void lpss_ssp_select_cs(struct driver_data *drv_data,
}
}
static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
static void lpss_ssp_cs_control(struct spi_device *spi, bool enable)
{
struct driver_data *drv_data =
spi_controller_get_devdata(spi->controller);
const struct lpss_config *config;
u32 value;
config = lpss_get_config(drv_data);
if (enable)
lpss_ssp_select_cs(drv_data, config);
lpss_ssp_select_cs(spi, config);
value = __lpss_ssp_read_priv(drv_data, config->reg_cs_ctrl);
if (enable)
@ -387,10 +391,11 @@ static void lpss_ssp_cs_control(struct driver_data *drv_data, bool enable)
__lpss_ssp_write_priv(drv_data, config->reg_cs_ctrl, value);
}
static void cs_assert(struct driver_data *drv_data)
static void cs_assert(struct spi_device *spi)
{
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
struct chip_data *chip = spi_get_ctldata(spi);
struct driver_data *drv_data =
spi_controller_get_devdata(spi->controller);
if (drv_data->ssp_type == CE4100_SSP) {
pxa2xx_spi_write(drv_data, SSSR, chip->frm);
@ -408,13 +413,14 @@ static void cs_assert(struct driver_data *drv_data)
}
if (is_lpss_ssp(drv_data))
lpss_ssp_cs_control(drv_data, true);
lpss_ssp_cs_control(spi, true);
}
static void cs_deassert(struct driver_data *drv_data)
static void cs_deassert(struct spi_device *spi)
{
struct chip_data *chip =
spi_get_ctldata(drv_data->master->cur_msg->spi);
struct chip_data *chip = spi_get_ctldata(spi);
struct driver_data *drv_data =
spi_controller_get_devdata(spi->controller);
unsigned long timeout;
if (drv_data->ssp_type == CE4100_SSP)
@ -437,7 +443,15 @@ static void cs_deassert(struct driver_data *drv_data)
}
if (is_lpss_ssp(drv_data))
lpss_ssp_cs_control(drv_data, false);
lpss_ssp_cs_control(spi, false);
}
static void pxa2xx_spi_set_cs(struct spi_device *spi, bool level)
{
if (level)
cs_deassert(spi);
else
cs_assert(spi);
}
int pxa2xx_spi_flush(struct driver_data *drv_data)
@ -549,70 +563,6 @@ static int u32_reader(struct driver_data *drv_data)
return drv_data->rx == drv_data->rx_end;
}
void *pxa2xx_spi_next_transfer(struct driver_data *drv_data)
{
struct spi_message *msg = drv_data->master->cur_msg;
struct spi_transfer *trans = drv_data->cur_transfer;
/* Move to next transfer */
if (trans->transfer_list.next != &msg->transfers) {
drv_data->cur_transfer =
list_entry(trans->transfer_list.next,
struct spi_transfer,
transfer_list);
return RUNNING_STATE;
} else
return DONE_STATE;
}
/* caller already set message->status; dma and pio irqs are blocked */
static void giveback(struct driver_data *drv_data)
{
struct spi_transfer* last_transfer;
struct spi_message *msg;
msg = drv_data->master->cur_msg;
drv_data->cur_transfer = NULL;
last_transfer = list_last_entry(&msg->transfers, struct spi_transfer,
transfer_list);
/* Delay if requested before any change in chip select */
if (last_transfer->delay_usecs)
udelay(last_transfer->delay_usecs);
/* Drop chip select UNLESS cs_change is true or we are returning
* a message with an error, or next message is for another chip
*/
if (!last_transfer->cs_change)
cs_deassert(drv_data);
else {
struct spi_message *next_msg;
/* Holding of cs was hinted, but we need to make sure
* the next message is for the same chip. Don't waste
* time with the following tests unless this was hinted.
*
* We cannot postpone this until pump_messages, because
* after calling msg->complete (below) the driver that
* sent the current message could be unloaded, which
* could invalidate the cs_control() callback...
*/
/* get a pointer to the next message, if any */
next_msg = spi_get_next_queued_message(drv_data->master);
/* see if the next and current messages point
* to the same chip
*/
if ((next_msg && next_msg->spi != msg->spi) ||
msg->state == ERROR_STATE)
cs_deassert(drv_data);
}
spi_finalize_current_message(drv_data->master);
}
static void reset_sccr1(struct driver_data *drv_data)
{
struct chip_data *chip =
@ -648,8 +598,8 @@ static void int_error_stop(struct driver_data *drv_data, const char* msg)
dev_err(&drv_data->pdev->dev, "%s\n", msg);
drv_data->master->cur_msg->state = ERROR_STATE;
tasklet_schedule(&drv_data->pump_transfers);
drv_data->master->cur_msg->status = -EIO;
spi_finalize_current_transfer(drv_data->master);
}
static void int_transfer_complete(struct driver_data *drv_data)
@ -660,19 +610,7 @@ static void int_transfer_complete(struct driver_data *drv_data)
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
/* Update total byte transferred return count actual bytes read */
drv_data->master->cur_msg->actual_length += drv_data->len -
(drv_data->rx_end - drv_data->rx);
/* Transfer delays and chip select release are
* handled in pump_transfers or giveback
*/
/* Move to next transfer */
drv_data->master->cur_msg->state = pxa2xx_spi_next_transfer(drv_data);
/* Schedule transfer tasklet */
tasklet_schedule(&drv_data->pump_transfers);
spi_finalize_current_transfer(drv_data->master);
}
static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
@ -973,17 +911,16 @@ static bool pxa2xx_spi_can_dma(struct spi_controller *master,
xfer->len >= chip->dma_burst_size;
}
static void pump_transfers(unsigned long data)
static int pxa2xx_spi_transfer_one(struct spi_controller *master,
struct spi_device *spi,
struct spi_transfer *transfer)
{
struct driver_data *drv_data = (struct driver_data *)data;
struct spi_controller *master = drv_data->master;
struct driver_data *drv_data = spi_controller_get_devdata(master);
struct spi_message *message = master->cur_msg;
struct chip_data *chip = spi_get_ctldata(message->spi);
u32 dma_thresh = chip->dma_threshold;
u32 dma_burst = chip->dma_burst_size;
u32 change_mask = pxa2xx_spi_get_ssrc1_change_mask(drv_data);
struct spi_transfer *transfer;
struct spi_transfer *previous;
u32 clk_div;
u8 bits;
u32 speed;
@ -992,36 +929,6 @@ static void pump_transfers(unsigned long data)
int err;
int dma_mapped;
/* Get current state information */
transfer = drv_data->cur_transfer;
/* Handle for abort */
if (message->state == ERROR_STATE) {
message->status = -EIO;
giveback(drv_data);
return;
}
/* Handle end of message */
if (message->state == DONE_STATE) {
message->status = 0;
giveback(drv_data);
return;
}
/* Delay if requested at end of transfer before CS change */
if (message->state == RUNNING_STATE) {
previous = list_entry(transfer->transfer_list.prev,
struct spi_transfer,
transfer_list);
if (previous->delay_usecs)
udelay(previous->delay_usecs);
/* Drop chip select only if cs_change is requested */
if (previous->cs_change)
cs_deassert(drv_data);
}
/* Check if we can DMA this transfer */
if (transfer->len > MAX_DMA_LEN && chip->enable_dma) {
@ -1029,34 +936,27 @@ static void pump_transfers(unsigned long data)
if (message->is_dma_mapped
|| transfer->rx_dma || transfer->tx_dma) {
dev_err(&drv_data->pdev->dev,
"pump_transfers: mapped transfer length of "
"%u is greater than %d\n",
"Mapped transfer length of %u is greater than %d\n",
transfer->len, MAX_DMA_LEN);
message->status = -EINVAL;
giveback(drv_data);
return;
return -EINVAL;
}
/* warn ... we force this to PIO mode */
dev_warn_ratelimited(&message->spi->dev,
"pump_transfers: DMA disabled for transfer length %ld "
"greater than %d\n",
(long)drv_data->len, MAX_DMA_LEN);
"DMA disabled for transfer length %ld greater than %d\n",
(long)transfer->len, MAX_DMA_LEN);
}
/* Setup the transfer state based on the type of transfer */
if (pxa2xx_spi_flush(drv_data) == 0) {
dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
message->status = -EIO;
giveback(drv_data);
return;
dev_err(&drv_data->pdev->dev, "Flush failed\n");
return -EIO;
}
drv_data->n_bytes = chip->n_bytes;
drv_data->tx = (void *)transfer->tx_buf;
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->len = transfer->len;
drv_data->write = drv_data->tx ? chip->write : null_writer;
drv_data->read = drv_data->rx ? chip->read : null_reader;
@ -1095,11 +995,9 @@ static void pump_transfers(unsigned long data)
bits, &dma_burst,
&dma_thresh))
dev_warn_ratelimited(&message->spi->dev,
"pump_transfers: DMA burst size reduced to match bits_per_word\n");
"DMA burst size reduced to match bits_per_word\n");
}
message->state = RUNNING_STATE;
dma_mapped = master->can_dma &&
master->can_dma(master, message->spi, transfer) &&
master->cur_msg_mapped;
@ -1108,12 +1006,9 @@ static void pump_transfers(unsigned long data)
/* Ensure we have the correct interrupt handler */
drv_data->transfer_handler = pxa2xx_spi_dma_transfer;
err = pxa2xx_spi_dma_prepare(drv_data, dma_burst);
if (err) {
message->status = err;
giveback(drv_data);
return;
}
err = pxa2xx_spi_dma_prepare(drv_data, transfer);
if (err)
return err;
/* Clear status and start DMA engine */
cr1 = chip->cr1 | dma_thresh | drv_data->dma_cr1;
@ -1175,27 +1070,40 @@ static void pump_transfers(unsigned long data)
pxa2xx_spi_write(drv_data, SSTO, chip->timeout);
}
cs_assert(drv_data);
/* after chip select, release the data by enabling service
* requests and interrupts, without changing any mode bits */
/*
* Release the data by enabling service requests and interrupts,
* without changing any mode bits
*/
pxa2xx_spi_write(drv_data, SSCR1, cr1);
return 1;
}
static int pxa2xx_spi_transfer_one_message(struct spi_controller *master,
struct spi_message *msg)
static void pxa2xx_spi_handle_err(struct spi_controller *master,
struct spi_message *msg)
{
struct driver_data *drv_data = spi_controller_get_devdata(master);
/* Initial message state*/
msg->state = START_STATE;
drv_data->cur_transfer = list_entry(msg->transfers.next,
struct spi_transfer,
transfer_list);
/* Disable the SSP */
pxa2xx_spi_write(drv_data, SSCR0,
pxa2xx_spi_read(drv_data, SSCR0) & ~SSCR0_SSE);
/* Clear and disable interrupts and service requests */
write_SSSR_CS(drv_data, drv_data->clear_sr);
pxa2xx_spi_write(drv_data, SSCR1,
pxa2xx_spi_read(drv_data, SSCR1)
& ~(drv_data->int_cr1 | drv_data->dma_cr1));
if (!pxa25x_ssp_comp(drv_data))
pxa2xx_spi_write(drv_data, SSTO, 0);
/* Mark as busy and launch transfers */
tasklet_schedule(&drv_data->pump_transfers);
return 0;
/*
* Stop the DMA if running. Note DMA callback handler may have unset
* the dma_running already, which is fine as stopping is not needed
* then but we shouldn't rely this flag for anything else than
* stopping. For instance to differentiate between PIO and DMA
* transfers.
*/
if (atomic_read(&drv_data->dma_running))
pxa2xx_spi_dma_stop(drv_data);
}
static int pxa2xx_spi_unprepare_transfer(struct spi_controller *master)
@ -1651,7 +1559,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
master->dma_alignment = DMA_ALIGNMENT;
master->cleanup = cleanup;
master->setup = setup;
master->transfer_one_message = pxa2xx_spi_transfer_one_message;
master->set_cs = pxa2xx_spi_set_cs;
master->transfer_one = pxa2xx_spi_transfer_one;
master->handle_err = pxa2xx_spi_handle_err;
master->unprepare_transfer_hardware = pxa2xx_spi_unprepare_transfer;
master->fw_translate_cs = pxa2xx_spi_fw_translate_cs;
master->auto_runtime_pm = true;
@ -1702,7 +1612,9 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
}
/* Enable SOC clock */
clk_prepare_enable(ssp->clk);
status = clk_prepare_enable(ssp->clk);
if (status)
goto out_error_dma_irq_alloc;
master->max_speed_hz = clk_get_rate(ssp->clk);
@ -1787,9 +1699,6 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
}
}
tasklet_init(&drv_data->pump_transfers, pump_transfers,
(unsigned long)drv_data);
pm_runtime_set_autosuspend_delay(&pdev->dev, 50);
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_active(&pdev->dev);
@ -1809,6 +1718,8 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(ssp->clk);
out_error_dma_irq_alloc:
pxa2xx_spi_dma_release(drv_data);
free_irq(ssp->irq, drv_data);
@ -1882,8 +1793,11 @@ static int pxa2xx_spi_resume(struct device *dev)
int status;
/* Enable the SSP clock */
if (!pm_runtime_suspended(dev))
clk_prepare_enable(ssp->clk);
if (!pm_runtime_suspended(dev)) {
status = clk_prepare_enable(ssp->clk);
if (status)
return status;
}
/* Restore LPSS private register bits */
if (is_lpss_ssp(drv_data))
@ -1912,9 +1826,10 @@ static int pxa2xx_spi_runtime_suspend(struct device *dev)
static int pxa2xx_spi_runtime_resume(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
int status;
clk_prepare_enable(drv_data->ssp->clk);
return 0;
status = clk_prepare_enable(drv_data->ssp->clk);
return status;
}
#endif

View File

@ -46,15 +46,10 @@ struct driver_data {
u32 clear_sr;
u32 mask_sr;
/* Message Transfer pump */
struct tasklet_struct pump_transfers;
/* DMA engine support */
atomic_t dma_running;
/* Current message transfer state info */
struct spi_transfer *cur_transfer;
size_t len;
/* Current transfer state info */
void *tx;
void *tx_end;
void *rx;
@ -104,11 +99,6 @@ static inline void pxa2xx_spi_write(const struct driver_data *drv_data,
__raw_writel(val, drv_data->ioaddr + reg);
}
#define START_STATE ((void *)0)
#define RUNNING_STATE ((void *)1)
#define DONE_STATE ((void *)2)
#define ERROR_STATE ((void *)-1)
#define DMA_ALIGNMENT 8
static inline int pxa25x_ssp_comp(struct driver_data *drv_data)
@ -133,14 +123,15 @@ 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);
#define MAX_DMA_LEN SZ_64K
#define DEFAULT_DMA_CR1 (SSCR1_TSRE | SSCR1_RSRE | SSCR1_TRAIL)
extern irqreturn_t pxa2xx_spi_dma_transfer(struct driver_data *drv_data);
extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data, u32 dma_burst);
extern int pxa2xx_spi_dma_prepare(struct driver_data *drv_data,
struct spi_transfer *xfer);
extern void pxa2xx_spi_dma_start(struct driver_data *drv_data);
extern void pxa2xx_spi_dma_stop(struct driver_data *drv_data);
extern int pxa2xx_spi_dma_setup(struct driver_data *drv_data);
extern void pxa2xx_spi_dma_release(struct driver_data *drv_data);
extern int pxa2xx_spi_set_dma_burst_and_threshold(struct chip_data *chip,

View File

@ -28,15 +28,15 @@
#define S3C64XX_SPI_CH_CFG 0x00
#define S3C64XX_SPI_CLK_CFG 0x04
#define S3C64XX_SPI_MODE_CFG 0x08
#define S3C64XX_SPI_SLAVE_SEL 0x0C
#define S3C64XX_SPI_MODE_CFG 0x08
#define S3C64XX_SPI_SLAVE_SEL 0x0C
#define S3C64XX_SPI_INT_EN 0x10
#define S3C64XX_SPI_STATUS 0x14
#define S3C64XX_SPI_TX_DATA 0x18
#define S3C64XX_SPI_RX_DATA 0x1C
#define S3C64XX_SPI_PACKET_CNT 0x20
#define S3C64XX_SPI_PENDING_CLR 0x24
#define S3C64XX_SPI_SWAP_CFG 0x28
#define S3C64XX_SPI_PACKET_CNT 0x20
#define S3C64XX_SPI_PENDING_CLR 0x24
#define S3C64XX_SPI_SWAP_CFG 0x28
#define S3C64XX_SPI_FB_CLK 0x2C
#define S3C64XX_SPI_CH_HS_EN (1<<6) /* High Speed Enable */
@ -77,9 +77,9 @@
#define S3C64XX_SPI_INT_TX_FIFORDY_EN (1<<0)
#define S3C64XX_SPI_ST_RX_OVERRUN_ERR (1<<5)
#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4)
#define S3C64XX_SPI_ST_RX_UNDERRUN_ERR (1<<4)
#define S3C64XX_SPI_ST_TX_OVERRUN_ERR (1<<3)
#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2)
#define S3C64XX_SPI_ST_TX_UNDERRUN_ERR (1<<2)
#define S3C64XX_SPI_ST_RX_FIFORDY (1<<1)
#define S3C64XX_SPI_ST_TX_FIFORDY (1<<0)
@ -100,7 +100,7 @@
#define S3C64XX_SPI_SWAP_TX_BIT (1<<1)
#define S3C64XX_SPI_SWAP_TX_EN (1<<0)
#define S3C64XX_SPI_FBCLK_MSK (3<<0)
#define S3C64XX_SPI_FBCLK_MSK (3<<0)
#define FIFO_LVL_MASK(i) ((i)->port_conf->fifo_lvl_mask[i->port_id])
#define S3C64XX_SPI_ST_TX_DONE(v, i) (((v) & \
@ -156,7 +156,6 @@ struct s3c64xx_spi_port_config {
* @ioclk: Pointer to the i/o clock between master and slave
* @master: Pointer to the SPI Protocol master.
* @cntrlr_info: Platform specific data for the controller this driver manages.
* @tgl_spi: Pointer to the last CS left untoggled by the cs_change hint.
* @lock: Controller specific lock.
* @state: Set of FLAGS to indicate status.
* @rx_dmach: Controller's DMA channel for Rx.
@ -177,7 +176,6 @@ struct s3c64xx_spi_driver_data {
struct platform_device *pdev;
struct spi_master *master;
struct s3c64xx_spi_info *cntrlr_info;
struct spi_device *tgl_spi;
spinlock_t lock;
unsigned long sfr_start;
struct completion xfer_completion;
@ -190,7 +188,7 @@ struct s3c64xx_spi_driver_data {
unsigned int port_id;
};
static void flush_fifo(struct s3c64xx_spi_driver_data *sdd)
static void s3c64xx_flush_fifo(struct s3c64xx_spi_driver_data *sdd)
{
void __iomem *regs = sdd->regs;
unsigned long loops;
@ -350,9 +348,8 @@ static bool s3c64xx_spi_can_dma(struct spi_master *master,
return xfer->len > (FIFO_LVL_MASK(sdd) >> 1) + 1;
}
static void enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_device *spi,
struct spi_transfer *xfer, int dma_mode)
static void s3c64xx_enable_datapath(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer, int dma_mode)
{
void __iomem *regs = sdd->regs;
u32 modecfg, chcfg;
@ -442,8 +439,8 @@ static u32 s3c64xx_spi_wait_for_timeout(struct s3c64xx_spi_driver_data *sdd,
return RX_FIFO_LVL(status, sdd);
}
static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer)
static int s3c64xx_wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer)
{
void __iomem *regs = sdd->regs;
unsigned long val;
@ -485,8 +482,8 @@ static int wait_for_dma(struct s3c64xx_spi_driver_data *sdd,
return 0;
}
static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer)
static int s3c64xx_wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
struct spi_transfer *xfer)
{
void __iomem *regs = sdd->regs;
unsigned long val;
@ -505,6 +502,8 @@ static int wait_for_pio(struct s3c64xx_spi_driver_data *sdd,
status = readl(regs + S3C64XX_SPI_STATUS);
} while (RX_FIFO_LVL(status, sdd) < xfer->len && --val);
if (!val)
return -EIO;
/* If it was only Tx */
if (!xfer->rx_buf) {
@ -635,11 +634,15 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
struct spi_transfer *xfer)
{
struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master);
const unsigned int fifo_len = (FIFO_LVL_MASK(sdd) >> 1) + 1;
const void *tx_buf = NULL;
void *rx_buf = NULL;
int target_len = 0, origin_len = 0;
int use_dma = 0;
int status;
u32 speed;
u8 bpw;
unsigned long flags;
int use_dma;
reinit_completion(&sdd->xfer_completion);
@ -654,48 +657,77 @@ static int s3c64xx_spi_transfer_one(struct spi_master *master,
s3c64xx_spi_config(sdd);
}
/* Polling method for xfers not bigger than FIFO capacity */
use_dma = 0;
if (!is_polling(sdd) &&
(sdd->rx_dma.ch && sdd->tx_dma.ch &&
(xfer->len > ((FIFO_LVL_MASK(sdd) >> 1) + 1))))
if (!is_polling(sdd) && (xfer->len > fifo_len) &&
sdd->rx_dma.ch && sdd->tx_dma.ch) {
use_dma = 1;
spin_lock_irqsave(&sdd->lock, flags);
} else if (is_polling(sdd) && xfer->len > fifo_len) {
tx_buf = xfer->tx_buf;
rx_buf = xfer->rx_buf;
origin_len = xfer->len;
/* Pending only which is to be done */
sdd->state &= ~RXBUSY;
sdd->state &= ~TXBUSY;
target_len = xfer->len;
if (xfer->len > fifo_len)
xfer->len = fifo_len;
}
enable_datapath(sdd, spi, xfer, use_dma);
do {
spin_lock_irqsave(&sdd->lock, flags);
/* Start the signals */
s3c64xx_spi_set_cs(spi, true);
/* Pending only which is to be done */
sdd->state &= ~RXBUSY;
sdd->state &= ~TXBUSY;
spin_unlock_irqrestore(&sdd->lock, flags);
s3c64xx_enable_datapath(sdd, xfer, use_dma);
if (use_dma)
status = wait_for_dma(sdd, xfer);
else
status = wait_for_pio(sdd, xfer);
/* Start the signals */
s3c64xx_spi_set_cs(spi, true);
if (status) {
dev_err(&spi->dev, "I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
(sdd->state & RXBUSY) ? 'f' : 'p',
(sdd->state & TXBUSY) ? 'f' : 'p',
xfer->len);
spin_unlock_irqrestore(&sdd->lock, flags);
if (use_dma) {
if (xfer->tx_buf != NULL
&& (sdd->state & TXBUSY))
dmaengine_terminate_all(sdd->tx_dma.ch);
if (xfer->rx_buf != NULL
&& (sdd->state & RXBUSY))
dmaengine_terminate_all(sdd->rx_dma.ch);
if (use_dma)
status = s3c64xx_wait_for_dma(sdd, xfer);
else
status = s3c64xx_wait_for_pio(sdd, xfer);
if (status) {
dev_err(&spi->dev,
"I/O Error: rx-%d tx-%d res:rx-%c tx-%c len-%d\n",
xfer->rx_buf ? 1 : 0, xfer->tx_buf ? 1 : 0,
(sdd->state & RXBUSY) ? 'f' : 'p',
(sdd->state & TXBUSY) ? 'f' : 'p',
xfer->len);
if (use_dma) {
if (xfer->tx_buf && (sdd->state & TXBUSY))
dmaengine_terminate_all(sdd->tx_dma.ch);
if (xfer->rx_buf && (sdd->state & RXBUSY))
dmaengine_terminate_all(sdd->rx_dma.ch);
}
} else {
s3c64xx_flush_fifo(sdd);
}
} else {
flush_fifo(sdd);
if (target_len > 0) {
target_len -= xfer->len;
if (xfer->tx_buf)
xfer->tx_buf += xfer->len;
if (xfer->rx_buf)
xfer->rx_buf += xfer->len;
if (target_len > fifo_len)
xfer->len = fifo_len;
else
xfer->len = target_len;
}
} while (target_len > 0);
if (origin_len) {
/* Restore original xfer buffers and length */
xfer->tx_buf = tx_buf;
xfer->rx_buf = rx_buf;
xfer->len = origin_len;
}
return status;
@ -891,7 +923,7 @@ static irqreturn_t s3c64xx_spi_irq(int irq, void *data)
return IRQ_HANDLED;
}
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd)
{
struct s3c64xx_spi_info *sci = sdd->cntrlr_info;
void __iomem *regs = sdd->regs;
@ -929,7 +961,7 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel)
val |= (S3C64XX_SPI_TRAILCNT << S3C64XX_SPI_TRAILCNT_OFF);
writel(val, regs + S3C64XX_SPI_MODE_CFG);
flush_fifo(sdd);
s3c64xx_flush_fifo(sdd);
}
#ifdef CONFIG_OF
@ -1145,7 +1177,7 @@ static int s3c64xx_spi_probe(struct platform_device *pdev)
pm_runtime_get_sync(&pdev->dev);
/* Setup Deufult Mode */
s3c64xx_spi_hwinit(sdd, sdd->port_id);
s3c64xx_spi_hwinit(sdd);
spin_lock_init(&sdd->lock);
init_completion(&sdd->xfer_completion);
@ -1260,8 +1292,6 @@ static int s3c64xx_spi_resume(struct device *dev)
if (ret < 0)
return ret;
s3c64xx_spi_hwinit(sdd, sdd->port_id);
return spi_master_resume(master);
}
#endif /* CONFIG_PM_SLEEP */
@ -1299,6 +1329,8 @@ static int s3c64xx_spi_runtime_resume(struct device *dev)
if (ret != 0)
goto err_disable_src_clk;
s3c64xx_spi_hwinit(sdd);
return 0;
err_disable_src_clk:
@ -1344,15 +1376,6 @@ static struct s3c64xx_spi_port_config exynos4_spi_port_config = {
.clk_from_cmu = true,
};
static struct s3c64xx_spi_port_config exynos5440_spi_port_config = {
.fifo_lvl_mask = { 0x1ff },
.rx_lvl_offset = 15,
.tx_st_done = 25,
.high_speed = true,
.clk_from_cmu = true,
.quirks = S3C64XX_SPI_QUIRK_POLL,
};
static struct s3c64xx_spi_port_config exynos7_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F, 0x7F, 0x7F, 0x1ff},
.rx_lvl_offset = 15,
@ -1396,9 +1419,6 @@ static const struct of_device_id s3c64xx_spi_dt_match[] = {
{ .compatible = "samsung,exynos4210-spi",
.data = (void *)&exynos4_spi_port_config,
},
{ .compatible = "samsung,exynos5440-spi",
.data = (void *)&exynos5440_spi_port_config,
},
{ .compatible = "samsung,exynos7-spi",
.data = (void *)&exynos7_spi_port_config,
},

View File

@ -39,7 +39,7 @@ struct sh_msiof_chipdata {
u16 tx_fifo_size;
u16 rx_fifo_size;
u16 master_flags;
u16 min_div;
u16 min_div_pow;
};
struct sh_msiof_spi_priv {
@ -51,7 +51,7 @@ struct sh_msiof_spi_priv {
struct completion done;
unsigned int tx_fifo_size;
unsigned int rx_fifo_size;
unsigned int min_div;
unsigned int min_div_pow;
void *tx_dma_page;
void *rx_dma_page;
dma_addr_t tx_dma_addr;
@ -249,43 +249,46 @@ static irqreturn_t sh_msiof_spi_irq(int irq, void *data)
return IRQ_HANDLED;
}
static struct {
unsigned short div;
unsigned short brdv;
} const sh_msiof_spi_div_table[] = {
{ 1, SCR_BRDV_DIV_1 },
{ 2, SCR_BRDV_DIV_2 },
{ 4, SCR_BRDV_DIV_4 },
{ 8, SCR_BRDV_DIV_8 },
{ 16, SCR_BRDV_DIV_16 },
{ 32, SCR_BRDV_DIV_32 },
static const u32 sh_msiof_spi_div_array[] = {
SCR_BRDV_DIV_1, SCR_BRDV_DIV_2, SCR_BRDV_DIV_4,
SCR_BRDV_DIV_8, SCR_BRDV_DIV_16, SCR_BRDV_DIV_32,
};
static void sh_msiof_spi_set_clk_regs(struct sh_msiof_spi_priv *p,
unsigned long parent_rate, u32 spi_hz)
{
unsigned long div = 1024;
unsigned long div;
u32 brps, scr;
size_t k;
unsigned int div_pow = p->min_div_pow;
if (!WARN_ON(!spi_hz || !parent_rate))
div = DIV_ROUND_UP(parent_rate, spi_hz);
div = max_t(unsigned long, div, p->min_div);
for (k = 0; k < ARRAY_SIZE(sh_msiof_spi_div_table); k++) {
brps = DIV_ROUND_UP(div, sh_msiof_spi_div_table[k].div);
/* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */
if (sh_msiof_spi_div_table[k].div == 1 && brps > 2)
continue;
if (brps <= 32) /* max of brdv is 32 */
break;
if (!spi_hz || !parent_rate) {
WARN(1, "Invalid clock rate parameters %lu and %u\n",
parent_rate, spi_hz);
return;
}
k = min_t(int, k, ARRAY_SIZE(sh_msiof_spi_div_table) - 1);
brps = min_t(int, brps, 32);
div = DIV_ROUND_UP(parent_rate, spi_hz);
if (div <= 1024) {
/* SCR_BRDV_DIV_1 is valid only if BRPS is x 1/1 or x 1/2 */
if (!div_pow && div <= 32 && div > 2)
div_pow = 1;
scr = sh_msiof_spi_div_table[k].brdv | SCR_BRPS(brps);
if (div_pow)
brps = (div + 1) >> div_pow;
else
brps = div;
for (; brps > 32; div_pow++)
brps = (brps + 1) >> 1;
} else {
/* Set transfer rate composite divisor to 2^5 * 32 = 1024 */
dev_err(&p->pdev->dev,
"Requested SPI transfer rate %d is too low\n", spi_hz);
div_pow = 5;
brps = 32;
}
scr = sh_msiof_spi_div_array[div_pow] | SCR_BRPS(brps);
sh_msiof_write(p, TSCR, scr);
if (!(p->master->flags & SPI_MASTER_MUST_TX))
sh_msiof_write(p, RSCR, scr);
@ -564,14 +567,16 @@ static int sh_msiof_spi_setup(struct spi_device *spi)
/* Configure native chip select mode/polarity early */
clr = MDR1_SYNCMD_MASK;
set = MDR1_TRMD | TMDR1_PCON | MDR1_SYNCMD_SPI;
set = MDR1_SYNCMD_SPI;
if (spi->mode & SPI_CS_HIGH)
clr |= BIT(MDR1_SYNCAC_SHIFT);
else
set |= BIT(MDR1_SYNCAC_SHIFT);
pm_runtime_get_sync(&p->pdev->dev);
tmp = sh_msiof_read(p, TMDR1) & ~clr;
sh_msiof_write(p, TMDR1, tmp | set);
sh_msiof_write(p, TMDR1, tmp | set | MDR1_TRMD | TMDR1_PCON);
tmp = sh_msiof_read(p, RMDR1) & ~clr;
sh_msiof_write(p, RMDR1, tmp | set);
pm_runtime_put(&p->pdev->dev);
p->native_cs_high = spi->mode & SPI_CS_HIGH;
p->native_cs_inited = true;
@ -1041,21 +1046,21 @@ static const struct sh_msiof_chipdata sh_data = {
.tx_fifo_size = 64,
.rx_fifo_size = 64,
.master_flags = 0,
.min_div = 1,
.min_div_pow = 0,
};
static const struct sh_msiof_chipdata rcar_gen2_data = {
.tx_fifo_size = 64,
.rx_fifo_size = 64,
.master_flags = SPI_MASTER_MUST_TX,
.min_div = 1,
.min_div_pow = 0,
};
static const struct sh_msiof_chipdata rcar_gen3_data = {
.tx_fifo_size = 64,
.rx_fifo_size = 64,
.master_flags = SPI_MASTER_MUST_TX,
.min_div = 2,
.min_div_pow = 1,
};
static const struct of_device_id sh_msiof_match[] = {
@ -1319,7 +1324,7 @@ static int sh_msiof_spi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, p);
p->master = master;
p->info = info;
p->min_div = chipdata->min_div;
p->min_div_pow = chipdata->min_div_pow;
init_completion(&p->done);

View File

@ -1129,7 +1129,7 @@ static int stm32_spi_probe(struct platform_device *pdev)
if (!spi->clk_rate) {
dev_err(&pdev->dev, "clk rate = 0\n");
ret = -EINVAL;
goto err_master_put;
goto err_clk_disable;
}
spi->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL);

View File

@ -36,6 +36,7 @@
#include <linux/sizes.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
struct ti_qspi_regs {
u32 clkctrl;
@ -50,6 +51,7 @@ struct ti_qspi {
struct spi_master *master;
void __iomem *base;
void __iomem *mmap_base;
size_t mmap_size;
struct regmap *ctrl_base;
unsigned int ctrl_reg;
struct clk *fclk;
@ -434,12 +436,10 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst,
return 0;
}
static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi,
struct spi_flash_read_message *msg)
static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, loff_t offs,
void *to, size_t readsize)
{
size_t readsize = msg->len;
void *to = msg->buf;
dma_addr_t dma_src = qspi->mmap_phys_base + msg->from;
dma_addr_t dma_src = qspi->mmap_phys_base + offs;
int ret = 0;
/*
@ -507,13 +507,14 @@ static void ti_qspi_disable_memory_map(struct spi_device *spi)
qspi->mmap_enabled = false;
}
static void ti_qspi_setup_mmap_read(struct spi_device *spi,
struct spi_flash_read_message *msg)
static void ti_qspi_setup_mmap_read(struct spi_device *spi, u8 opcode,
u8 data_nbits, u8 addr_width,
u8 dummy_bytes)
{
struct ti_qspi *qspi = spi_master_get_devdata(spi->master);
u32 memval = msg->read_opcode;
u32 memval = opcode;
switch (msg->data_nbits) {
switch (data_nbits) {
case SPI_NBITS_QUAD:
memval |= QSPI_SETUP_RD_QUAD;
break;
@ -524,48 +525,64 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi,
memval |= QSPI_SETUP_RD_NORMAL;
break;
}
memval |= ((msg->addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
msg->dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
memval |= ((addr_width - 1) << QSPI_SETUP_ADDR_SHIFT |
dummy_bytes << QSPI_SETUP_DUMMY_SHIFT);
ti_qspi_write(qspi, memval,
QSPI_SPI_SETUP_REG(spi->chip_select));
}
static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi,
struct spi_flash_read_message *msg)
static int ti_qspi_exec_mem_op(struct spi_mem *mem,
const struct spi_mem_op *op)
{
return virt_addr_valid(msg->buf);
}
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);
struct ti_qspi *qspi = spi_master_get_devdata(mem->spi->master);
u32 from = 0;
int ret = 0;
/* Only optimize read path. */
if (!op->data.nbytes || op->data.dir != SPI_MEM_DATA_IN ||
!op->addr.nbytes || op->addr.nbytes > 4)
return -ENOTSUPP;
/* Address exceeds MMIO window size, fall back to regular mode. */
from = op->addr.val;
if (from + op->data.nbytes > qspi->mmap_size)
return -ENOTSUPP;
mutex_lock(&qspi->list_lock);
if (!qspi->mmap_enabled)
ti_qspi_enable_memory_map(spi);
ti_qspi_setup_mmap_read(spi, msg);
ti_qspi_enable_memory_map(mem->spi);
ti_qspi_setup_mmap_read(mem->spi, op->cmd.opcode, op->data.buswidth,
op->addr.nbytes, op->dummy.nbytes);
if (qspi->rx_chan) {
if (msg->cur_msg_mapped)
ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from);
else
ret = ti_qspi_dma_bounce_buffer(qspi, msg);
if (ret)
goto err_unlock;
} else {
memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len);
}
msg->retlen = msg->len;
struct sg_table sgt;
if (virt_addr_valid(op->data.buf.in) &&
!spi_controller_dma_map_mem_op_data(mem->spi->master, op,
&sgt)) {
ret = ti_qspi_dma_xfer_sg(qspi, sgt, from);
spi_controller_dma_unmap_mem_op_data(mem->spi->master,
op, &sgt);
} else {
ret = ti_qspi_dma_bounce_buffer(qspi, from,
op->data.buf.in,
op->data.nbytes);
}
} else {
memcpy_fromio(op->data.buf.in, qspi->mmap_base + from,
op->data.nbytes);
}
err_unlock:
mutex_unlock(&qspi->list_lock);
return ret;
}
static const struct spi_controller_mem_ops ti_qspi_mem_ops = {
.exec_op = ti_qspi_exec_mem_op,
};
static int ti_qspi_start_transfer_one(struct spi_master *master,
struct spi_message *m)
{
@ -672,7 +689,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
master->dev.of_node = pdev->dev.of_node;
master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) |
SPI_BPW_MASK(8);
master->spi_flash_read = ti_qspi_spi_flash_read;
master->mem_ops = &ti_qspi_mem_ops;
if (!of_property_read_u32(np, "num-cs", &num_cs))
master->num_chipselect = num_cs;
@ -702,6 +719,9 @@ static int ti_qspi_probe(struct platform_device *pdev)
}
}
if (res_mmap)
qspi->mmap_size = resource_size(res_mmap);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "no irq resource?\n");
@ -770,7 +790,6 @@ static int ti_qspi_probe(struct platform_device *pdev)
dma_release_channel(qspi->rx_chan);
goto no_dma;
}
master->spi_flash_can_dma = ti_qspi_spi_flash_can_dma;
master->dma_rx = qspi->rx_chan;
init_completion(&qspi->transfer_complete);
if (res_mmap)
@ -784,7 +803,7 @@ static int ti_qspi_probe(struct platform_device *pdev)
"mmap failed with error %ld using PIO mode\n",
PTR_ERR(qspi->mmap_base));
qspi->mmap_base = NULL;
master->spi_flash_read = NULL;
master->mem_ops = NULL;
}
}
qspi->mmap_enabled = false;

View File

@ -20,6 +20,7 @@
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
@ -135,6 +136,7 @@
#define GQSPI_DMA_UNALIGN 0x3
#define GQSPI_DEFAULT_NUM_CS 1 /* Default number of chip selects */
#define SPI_AUTOSUSPEND_TIMEOUT 3000
enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA};
/**
@ -356,21 +358,9 @@ static void zynqmp_qspi_copy_read_data(struct zynqmp_qspi *xqspi,
static int zynqmp_prepare_transfer_hardware(struct spi_master *master)
{
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret;
ret = clk_enable(xqspi->refclk);
if (ret)
return ret;
ret = clk_enable(xqspi->pclk);
if (ret)
goto clk_err;
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, GQSPI_EN_MASK);
return 0;
clk_err:
clk_disable(xqspi->refclk);
return ret;
}
/**
@ -387,8 +377,6 @@ static int zynqmp_unprepare_transfer_hardware(struct spi_master *master)
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
clk_disable(xqspi->refclk);
clk_disable(xqspi->pclk);
return 0;
}
@ -918,8 +906,7 @@ static int zynqmp_qspi_start_transfer(struct spi_master *master,
*/
static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_master *master = dev_get_drvdata(dev);
spi_master_suspend(master);
@ -939,8 +926,7 @@ static int __maybe_unused zynqmp_qspi_suspend(struct device *dev)
*/
static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct spi_master *master = dev_get_drvdata(dev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret = 0;
@ -959,11 +945,67 @@ static int __maybe_unused zynqmp_qspi_resume(struct device *dev)
spi_master_resume(master);
clk_disable(xqspi->refclk);
clk_disable(xqspi->pclk);
return 0;
}
static SIMPLE_DEV_PM_OPS(zynqmp_qspi_dev_pm_ops, zynqmp_qspi_suspend,
zynqmp_qspi_resume);
/**
* zynqmp_runtime_suspend - Runtime suspend method for the SPI driver
* @dev: Address of the platform_device structure
*
* This function disables the clocks
*
* Return: Always 0
*/
static int __maybe_unused zynqmp_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
clk_disable(xqspi->refclk);
clk_disable(xqspi->pclk);
return 0;
}
/**
* zynqmp_runtime_resume - Runtime resume method for the SPI driver
* @dev: Address of the platform_device structure
*
* This function enables the clocks
*
* Return: 0 on success and error value on error
*/
static int __maybe_unused zynqmp_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = platform_get_drvdata(pdev);
struct zynqmp_qspi *xqspi = spi_master_get_devdata(master);
int ret;
ret = clk_enable(xqspi->pclk);
if (ret) {
dev_err(dev, "Cannot enable APB clock.\n");
return ret;
}
ret = clk_enable(xqspi->refclk);
if (ret) {
dev_err(dev, "Cannot enable device clock.\n");
clk_disable(xqspi->pclk);
return ret;
}
return 0;
}
static const struct dev_pm_ops zynqmp_qspi_dev_pm_ops = {
SET_RUNTIME_PM_OPS(zynqmp_runtime_suspend,
zynqmp_runtime_resume, NULL)
SET_SYSTEM_SLEEP_PM_OPS(zynqmp_qspi_suspend, zynqmp_qspi_resume)
};
/**
* zynqmp_qspi_probe: Probe method for the QSPI driver
@ -1023,9 +1065,15 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
goto clk_dis_pclk;
}
pm_runtime_use_autosuspend(&pdev->dev);
pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
pm_runtime_set_active(&pdev->dev);
pm_runtime_enable(&pdev->dev);
/* QSPI controller initializations */
zynqmp_qspi_init_hw(xqspi);
pm_runtime_mark_last_busy(&pdev->dev);
pm_runtime_put_autosuspend(&pdev->dev);
xqspi->irq = platform_get_irq(pdev, 0);
if (xqspi->irq <= 0) {
ret = -ENXIO;
@ -1063,6 +1111,8 @@ static int zynqmp_qspi_probe(struct platform_device *pdev)
return 0;
clk_dis_all:
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(xqspi->refclk);
clk_dis_pclk:
clk_disable_unprepare(xqspi->pclk);
@ -1090,6 +1140,8 @@ static int zynqmp_qspi_remove(struct platform_device *pdev)
zynqmp_gqspi_write(xqspi, GQSPI_EN_OFST, 0x0);
clk_disable_unprepare(xqspi->refclk);
clk_disable_unprepare(xqspi->pclk);
pm_runtime_set_suspended(&pdev->dev);
pm_runtime_disable(&pdev->dev);
spi_unregister_master(master);

View File

@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/mod_devicetable.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi-mem.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/pm_domain.h>
@ -46,6 +47,8 @@
#define CREATE_TRACE_POINTS
#include <trace/events/spi.h>
#include "internals.h"
static DEFINE_IDR(spi_master_idr);
static void spidev_release(struct device *dev)
@ -740,9 +743,9 @@ static void spi_set_cs(struct spi_device *spi, bool enable)
}
#ifdef CONFIG_HAS_DMA
static int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir)
int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir)
{
const bool vmalloced_buf = is_vmalloc_addr(buf);
unsigned int max_seg_size = dma_get_max_seg_size(dev);
@ -821,8 +824,8 @@ static int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
return 0;
}
static void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, enum dma_data_direction dir)
void spi_unmap_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, enum dma_data_direction dir)
{
if (sgt->orig_nents) {
dma_unmap_sg(dev, sgt->sgl, sgt->orig_nents, dir);
@ -907,19 +910,6 @@ static int __spi_unmap_msg(struct spi_controller *ctlr, struct spi_message *msg)
return 0;
}
#else /* !CONFIG_HAS_DMA */
static inline int spi_map_buf(struct spi_controller *ctlr, struct device *dev,
struct sg_table *sgt, void *buf, size_t len,
enum dma_data_direction dir)
{
return -EINVAL;
}
static inline void spi_unmap_buf(struct spi_controller *ctlr,
struct device *dev, struct sg_table *sgt,
enum dma_data_direction dir)
{
}
static inline int __spi_map_msg(struct spi_controller *ctlr,
struct spi_message *msg)
{
@ -1222,6 +1212,7 @@ static void __spi_pump_messages(struct spi_controller *ctlr, bool in_kthread)
if (!was_busy && ctlr->auto_runtime_pm) {
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
pm_runtime_put_noidle(ctlr->dev.parent);
dev_err(&ctlr->dev, "Failed to power device: %d\n",
ret);
mutex_unlock(&ctlr->io_mutex);
@ -1533,6 +1524,22 @@ static int spi_controller_initialize_queue(struct spi_controller *ctlr)
return ret;
}
/**
* spi_flush_queue - Send all pending messages in the queue from the callers'
* context
* @ctlr: controller to process queue for
*
* This should be used when one wants to ensure all pending messages have been
* sent before doing something. Is used by the spi-mem code to make sure SPI
* memory operations do not preempt regular SPI transfers that have been queued
* before the spi-mem operation.
*/
void spi_flush_queue(struct spi_controller *ctlr)
{
if (ctlr->transfer == spi_queued_transfer)
__spi_pump_messages(ctlr, false);
}
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_OF)
@ -2063,6 +2070,26 @@ static int of_spi_register_master(struct spi_controller *ctlr)
}
#endif
static int spi_controller_check_ops(struct spi_controller *ctlr)
{
/*
* The controller may implement only the high-level SPI-memory like
* operations if it does not support regular SPI transfers, and this is
* valid use case.
* If ->mem_ops is NULL, we request that at least one of the
* ->transfer_xxx() method be implemented.
*/
if (ctlr->mem_ops) {
if (!ctlr->mem_ops->exec_op)
return -EINVAL;
} else if (!ctlr->transfer && !ctlr->transfer_one &&
!ctlr->transfer_one_message) {
return -EINVAL;
}
return 0;
}
/**
* spi_register_controller - register SPI master or slave controller
* @ctlr: initialized master, originally from spi_alloc_master() or
@ -2096,6 +2123,14 @@ int spi_register_controller(struct spi_controller *ctlr)
if (!dev)
return -ENODEV;
/*
* Make sure all necessary hooks are implemented before registering
* the SPI controller.
*/
status = spi_controller_check_ops(ctlr);
if (status)
return status;
if (!spi_controller_is_slave(ctlr)) {
status = of_spi_register_master(ctlr);
if (status)
@ -2161,10 +2196,14 @@ int spi_register_controller(struct spi_controller *ctlr)
spi_controller_is_slave(ctlr) ? "slave" : "master",
dev_name(&ctlr->dev));
/* If we're using a queued driver, start the queue */
if (ctlr->transfer)
/*
* If we're using a queued driver, start the queue. Note that we don't
* need the queueing logic if the driver is only supporting high-level
* memory operations.
*/
if (ctlr->transfer) {
dev_info(dev, "controller is unqueued, this is deprecated\n");
else {
} else if (ctlr->transfer_one || ctlr->transfer_one_message) {
status = spi_controller_initialize_queue(ctlr);
if (status) {
device_del(&ctlr->dev);
@ -2894,6 +2933,13 @@ static int __spi_async(struct spi_device *spi, struct spi_message *message)
{
struct spi_controller *ctlr = spi->controller;
/*
* Some controllers do not support doing regular SPI transfers. Return
* ENOTSUPP when this is the case.
*/
if (!ctlr->transfer)
return -ENOTSUPP;
message->spi = spi;
SPI_STATISTICS_INCREMENT_FIELD(&ctlr->statistics, spi_async);
@ -3010,63 +3056,6 @@ 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_controller *master = spi->controller;
struct device *rx_dev = NULL;
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);
mutex_lock(&master->io_mutex);
if (master->dma_rx && master->spi_flash_can_dma(spi, msg)) {
rx_dev = master->dma_rx->device->dev;
ret = spi_map_buf(master, rx_dev, &msg->rx_sg,
msg->buf, msg->len,
DMA_FROM_DEVICE);
if (!ret)
msg->cur_msg_mapped = true;
}
ret = master->spi_flash_read(spi, msg);
if (msg->cur_msg_mapped)
spi_unmap_buf(master, rx_dev, &msg->rx_sg,
DMA_FROM_DEVICE);
mutex_unlock(&master->io_mutex);
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 protocol drivers, layered on

249
include/linux/spi/spi-mem.h Normal file
View File

@ -0,0 +1,249 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2018 Exceet Electronics GmbH
* Copyright (C) 2018 Bootlin
*
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
*/
#ifndef __LINUX_SPI_MEM_H
#define __LINUX_SPI_MEM_H
#include <linux/spi/spi.h>
#define SPI_MEM_OP_CMD(__opcode, __buswidth) \
{ \
.buswidth = __buswidth, \
.opcode = __opcode, \
}
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
{ \
.nbytes = __nbytes, \
.val = __val, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_ADDR { }
#define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \
{ \
.nbytes = __nbytes, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_DUMMY { }
#define SPI_MEM_OP_DATA_IN(__nbytes, __buf, __buswidth) \
{ \
.dir = SPI_MEM_DATA_IN, \
.nbytes = __nbytes, \
.buf.in = __buf, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_DATA_OUT(__nbytes, __buf, __buswidth) \
{ \
.dir = SPI_MEM_DATA_OUT, \
.nbytes = __nbytes, \
.buf.out = __buf, \
.buswidth = __buswidth, \
}
#define SPI_MEM_OP_NO_DATA { }
/**
* enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective
* @SPI_MEM_DATA_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory
*/
enum spi_mem_data_dir {
SPI_MEM_DATA_IN,
SPI_MEM_DATA_OUT,
};
/**
* struct spi_mem_op - describes a SPI memory operation
* @cmd.buswidth: number of IO lines used to transmit the command
* @cmd.opcode: operation opcode
* @addr.nbytes: number of address bytes to send. Can be zero if the operation
* does not need to send an address
* @addr.buswidth: number of IO lines used to transmit the address cycles
* @addr.val: address value. This value is always sent MSB first on the bus.
* Note that only @addr.nbytes are taken into account in this
* address value, so users should make sure the value fits in the
* assigned number of bytes.
* @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
* be zero if the operation does not require dummy bytes
* @dummy.buswidth: number of IO lanes used to transmit the dummy bytes
* @data.buswidth: number of IO lanes used to send/receive the data
* @data.dir: direction of the transfer
* @data.buf.in: input buffer
* @data.buf.out: output buffer
*/
struct spi_mem_op {
struct {
u8 buswidth;
u8 opcode;
} cmd;
struct {
u8 nbytes;
u8 buswidth;
u64 val;
} addr;
struct {
u8 nbytes;
u8 buswidth;
} dummy;
struct {
u8 buswidth;
enum spi_mem_data_dir dir;
unsigned int nbytes;
/* buf.{in,out} must be DMA-able. */
union {
void *in;
const void *out;
} buf;
} data;
};
#define SPI_MEM_OP(__cmd, __addr, __dummy, __data) \
{ \
.cmd = __cmd, \
.addr = __addr, \
.dummy = __dummy, \
.data = __data, \
}
/**
* struct spi_mem - describes a SPI memory device
* @spi: the underlying SPI device
* @drvpriv: spi_mem_drviver private data
*
* Extra information that describe the SPI memory device and may be needed by
* the controller to properly handle this device should be placed here.
*
* One example would be the device size since some controller expose their SPI
* mem devices through a io-mapped region.
*/
struct spi_mem {
struct spi_device *spi;
void *drvpriv;
};
/**
* struct spi_mem_set_drvdata() - attach driver private data to a SPI mem
* device
* @mem: memory device
* @data: data to attach to the memory device
*/
static inline void spi_mem_set_drvdata(struct spi_mem *mem, void *data)
{
mem->drvpriv = data;
}
/**
* struct spi_mem_get_drvdata() - get driver private data attached to a SPI mem
* device
* @mem: memory device
*
* Return: the data attached to the mem device.
*/
static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
{
return mem->drvpriv;
}
/**
* struct spi_controller_mem_ops - SPI memory operations
* @adjust_op_size: shrink the data xfer of an operation to match controller's
* limitations (can be alignment of max RX/TX size
* limitations)
* @supports_op: check if an operation is supported by the controller
* @exec_op: execute a SPI memory operation
*
* This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the
* case for QSPI controllers.
*/
struct spi_controller_mem_ops {
int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
bool (*supports_op)(struct spi_mem *mem,
const struct spi_mem_op *op);
int (*exec_op)(struct spi_mem *mem,
const struct spi_mem_op *op);
};
/**
* struct spi_mem_driver - SPI memory driver
* @spidrv: inherit from a SPI driver
* @probe: probe a SPI memory. Usually where detection/initialization takes
* place
* @remove: remove a SPI memory
* @shutdown: take appropriate action when the system is shutdown
*
* This is just a thin wrapper around a spi_driver. The core takes care of
* allocating the spi_mem object and forwarding the probe/remove/shutdown
* request to the spi_mem_driver. The reason we use this wrapper is because
* we might have to stuff more information into the spi_mem struct to let
* SPI controllers know more about the SPI memory they interact with, and
* having this intermediate layer allows us to do that without adding more
* useless fields to the spi_device object.
*/
struct spi_mem_driver {
struct spi_driver spidrv;
int (*probe)(struct spi_mem *mem);
int (*remove)(struct spi_mem *mem);
void (*shutdown)(struct spi_mem *mem);
};
#if IS_ENABLED(CONFIG_SPI_MEM)
int spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg);
void spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg);
#else
static inline int
spi_controller_dma_map_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg)
{
return -ENOTSUPP;
}
static inline void
spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
const struct spi_mem_op *op,
struct sg_table *sg)
{
}
#endif /* CONFIG_SPI_MEM */
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
bool spi_mem_supports_op(struct spi_mem *mem,
const struct spi_mem_op *op);
int spi_mem_exec_op(struct spi_mem *mem,
const struct spi_mem_op *op);
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner);
void spi_mem_driver_unregister(struct spi_mem_driver *drv);
#define spi_mem_driver_register(__drv) \
spi_mem_driver_register_with_owner(__drv, THIS_MODULE)
#define module_spi_mem_driver(__drv) \
module_driver(__drv, spi_mem_driver_register, \
spi_mem_driver_unregister)
#endif /* __LINUX_SPI_MEM_H */

View File

@ -26,7 +26,7 @@ struct dma_chan;
struct property_entry;
struct spi_controller;
struct spi_transfer;
struct spi_flash_read_message;
struct spi_controller_mem_ops;
/*
* INTERFACES between SPI master-side drivers and SPI slave protocol handlers,
@ -376,13 +376,11 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* transfer_one callback.
* @handle_err: the subsystem calls the driver to handle an error that occurs
* in the generic implementation of transfer_one_message().
* @mem_ops: optimized/dedicated operations for interactions with SPI memory.
* This field is optional and should only be implemented if the
* controller has native support for memory like operations.
* @unprepare_message: undo any work done by prepare_message().
* @slave_abort: abort the ongoing transfer request on an SPI slave controller
* @spi_flash_read: to support spi-controller hardwares that provide
* accelerated interface to read from flash devices.
* @spi_flash_can_dma: analogous to can_dma() interface, but for
* controllers implementing spi_flash_read.
* @flash_read_supported: spi device supports flash read
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
@ -548,11 +546,6 @@ struct spi_controller {
int (*unprepare_message)(struct spi_controller *ctlr,
struct spi_message *message);
int (*slave_abort)(struct spi_controller *ctlr);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*spi_flash_can_dma)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*flash_read_supported)(struct spi_device *spi);
/*
* These hooks are for drivers that use a generic implementation
@ -564,6 +557,9 @@ struct spi_controller {
void (*handle_err)(struct spi_controller *ctlr,
struct spi_message *message);
/* Optimized handlers for SPI memory-like operations. */
const struct spi_controller_mem_ops *mem_ops;
/* gpio chip select */
int *cs_gpios;
@ -1183,48 +1179,6 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd)
return be16_to_cpu(result);
}
/**
* struct spi_flash_read_message - flash specific information for
* spi-masters that provide accelerated flash read interfaces
* @buf: buffer to read data
* @from: offset within the flash from where data is to be read
* @len: length of data to be read
* @retlen: actual length of data read
* @read_opcode: read_opcode to be used to communicate with flash
* @addr_width: number of address bytes
* @dummy_bytes: number of dummy bytes
* @opcode_nbits: number of lines to send opcode
* @addr_nbits: number of lines to send address
* @data_nbits: number of lines for data
* @rx_sg: Scatterlist for receive data read from flash
* @cur_msg_mapped: message has been mapped for DMA
*/
struct spi_flash_read_message {
void *buf;
loff_t from;
size_t len;
size_t retlen;
u8 read_opcode;
u8 addr_width;
u8 dummy_bytes;
u8 opcode_nbits;
u8 addr_nbits;
u8 data_nbits;
struct sg_table rx_sg;
bool cur_msg_mapped;
};
/* SPI core interface for flash read support */
static inline bool spi_flash_read_supported(struct spi_device *spi)
{
return spi->controller->spi_flash_read &&
(!spi->controller->flash_read_supported ||
spi->controller->flash_read_supported(spi));
}
int spi_flash_read(struct spi_device *spi,
struct spi_flash_read_message *msg);
/*---------------------------------------------------------------------------*/
/*