Merge branch 'master' of git://git.denx.de/u-boot-spi
This is the PR for SPI-NAND changes along with few spi changes. [trini: Re-sync changes for ls1012afrwy_qspi*_defconfig] Signed-off-by: Tom Rini <trini@konsulko.com>
This commit is contained in:
@@ -18,6 +18,13 @@ config DM_SPI
|
||||
|
||||
if DM_SPI
|
||||
|
||||
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 an high-level interface to send memory-like commands.
|
||||
|
||||
config ALTERA_SPI
|
||||
bool "Altera SPI driver"
|
||||
help
|
||||
|
||||
@@ -8,6 +8,7 @@ ifdef CONFIG_DM_SPI
|
||||
obj-y += spi-uclass.o
|
||||
obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
|
||||
obj-$(CONFIG_SOFT_SPI) += soft_spi.o
|
||||
obj-$(CONFIG_SPI_MEM) += spi-mem.o
|
||||
else
|
||||
obj-y += spi.o
|
||||
obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <fdtdec.h>
|
||||
#include <reset.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <asm/io.h>
|
||||
@@ -111,6 +112,8 @@ struct dw_spi_priv {
|
||||
void *tx_end;
|
||||
void *rx;
|
||||
void *rx_end;
|
||||
|
||||
struct reset_ctl_bulk resets;
|
||||
};
|
||||
|
||||
static inline u32 dw_read(struct dw_spi_priv *priv, u32 offset)
|
||||
@@ -231,6 +234,34 @@ err_rate:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int dw_spi_reset(struct udevice *bus)
|
||||
{
|
||||
int ret;
|
||||
struct dw_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
ret = reset_get_bulk(bus, &priv->resets);
|
||||
if (ret) {
|
||||
/*
|
||||
* Return 0 if error due to !CONFIG_DM_RESET and reset
|
||||
* DT property is not present.
|
||||
*/
|
||||
if (ret == -ENOENT || ret == -ENOTSUPP)
|
||||
return 0;
|
||||
|
||||
dev_warn(bus, "Can't get reset: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = reset_deassert_bulk(&priv->resets);
|
||||
if (ret) {
|
||||
reset_release_bulk(&priv->resets);
|
||||
dev_err(bus, "Failed to reset: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct dw_spi_platdata *plat = dev_get_platdata(bus);
|
||||
@@ -244,6 +275,10 @@ static int dw_spi_probe(struct udevice *bus)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dw_spi_reset(bus);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Currently only bits_per_word == 8 supported */
|
||||
priv->bits_per_word = 8;
|
||||
|
||||
@@ -478,6 +513,13 @@ static int dw_spi_set_mode(struct udevice *bus, uint mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_spi_remove(struct udevice *bus)
|
||||
{
|
||||
struct dw_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
return reset_release_bulk(&priv->resets);
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops dw_spi_ops = {
|
||||
.xfer = dw_spi_xfer,
|
||||
.set_speed = dw_spi_set_speed,
|
||||
@@ -502,4 +544,5 @@ U_BOOT_DRIVER(dw_spi) = {
|
||||
.platdata_auto_alloc_size = sizeof(struct dw_spi_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct dw_spi_priv),
|
||||
.probe = dw_spi_probe,
|
||||
.remove = dw_spi_remove,
|
||||
};
|
||||
|
||||
@@ -84,7 +84,6 @@ DECLARE_GLOBAL_DATA_PTR;
|
||||
/* QSPI max chipselect signals number */
|
||||
#define FSL_QSPI_MAX_CHIPSELECT_NUM 4
|
||||
|
||||
#ifdef CONFIG_DM_SPI
|
||||
/**
|
||||
* struct fsl_qspi_platdata - platform data for Freescale QSPI
|
||||
*
|
||||
@@ -105,7 +104,6 @@ struct fsl_qspi_platdata {
|
||||
u32 flash_num;
|
||||
u32 num_chipselect;
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* struct fsl_qspi_priv - private data for Freescale QSPI
|
||||
@@ -136,12 +134,6 @@ struct fsl_qspi_priv {
|
||||
struct fsl_qspi_regs *regs;
|
||||
};
|
||||
|
||||
#ifndef CONFIG_DM_SPI
|
||||
struct fsl_qspi {
|
||||
struct spi_slave slave;
|
||||
struct fsl_qspi_priv priv;
|
||||
};
|
||||
#endif
|
||||
|
||||
static u32 qspi_read32(u32 flags, u32 *addr)
|
||||
{
|
||||
@@ -869,136 +861,7 @@ void qspi_cfg_smpr(struct fsl_qspi_priv *priv, u32 clear_bits, u32 set_bits)
|
||||
smpr_val |= set_bits;
|
||||
qspi_write32(priv->flags, &priv->regs->smpr, smpr_val);
|
||||
}
|
||||
#ifndef CONFIG_DM_SPI
|
||||
static unsigned long spi_bases[] = {
|
||||
QSPI0_BASE_ADDR,
|
||||
#ifdef CONFIG_MX6SX
|
||||
QSPI1_BASE_ADDR,
|
||||
#endif
|
||||
};
|
||||
|
||||
static unsigned long amba_bases[] = {
|
||||
QSPI0_AMBA_BASE,
|
||||
#ifdef CONFIG_MX6SX
|
||||
QSPI1_AMBA_BASE,
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline struct fsl_qspi *to_qspi_spi(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct fsl_qspi, slave);
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
u32 mcr_val;
|
||||
struct fsl_qspi *qspi;
|
||||
struct fsl_qspi_regs *regs;
|
||||
u32 total_size;
|
||||
|
||||
if (bus >= ARRAY_SIZE(spi_bases))
|
||||
return NULL;
|
||||
|
||||
if (cs >= FSL_QSPI_FLASH_NUM)
|
||||
return NULL;
|
||||
|
||||
qspi = spi_alloc_slave(struct fsl_qspi, bus, cs);
|
||||
if (!qspi)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_QSPI_BE
|
||||
qspi->priv.flags |= QSPI_FLAG_REGMAP_ENDIAN_BIG;
|
||||
#endif
|
||||
|
||||
regs = (struct fsl_qspi_regs *)spi_bases[bus];
|
||||
qspi->priv.regs = regs;
|
||||
/*
|
||||
* According cs, use different amba_base to choose the
|
||||
* corresponding flash devices.
|
||||
*
|
||||
* If not, only one flash device is used even if passing
|
||||
* different cs using `sf probe`
|
||||
*/
|
||||
qspi->priv.cur_amba_base = amba_bases[bus] + cs * FSL_QSPI_FLASH_SIZE;
|
||||
|
||||
qspi->slave.max_write_size = TX_BUFFER_SIZE;
|
||||
|
||||
mcr_val = qspi_read32(qspi->priv.flags, ®s->mcr);
|
||||
|
||||
/* Set endianness to LE for i.mx */
|
||||
if (IS_ENABLED(CONFIG_MX6) || IS_ENABLED(CONFIG_MX7))
|
||||
mcr_val = QSPI_MCR_END_CFD_LE;
|
||||
|
||||
qspi_write32(qspi->priv.flags, ®s->mcr,
|
||||
QSPI_MCR_RESERVED_MASK | QSPI_MCR_MDIS_MASK |
|
||||
(mcr_val & QSPI_MCR_END_CFD_MASK));
|
||||
|
||||
qspi_cfg_smpr(&qspi->priv,
|
||||
~(QSPI_SMPR_FSDLY_MASK | QSPI_SMPR_DDRSMP_MASK |
|
||||
QSPI_SMPR_FSPHS_MASK | QSPI_SMPR_HSENA_MASK), 0);
|
||||
|
||||
total_size = FSL_QSPI_FLASH_SIZE * FSL_QSPI_FLASH_NUM;
|
||||
/*
|
||||
* Any read access to non-implemented addresses will provide
|
||||
* undefined results.
|
||||
*
|
||||
* In case single die flash devices, TOP_ADDR_MEMA2 and
|
||||
* TOP_ADDR_MEMB2 should be initialized/programmed to
|
||||
* TOP_ADDR_MEMA1 and TOP_ADDR_MEMB1 respectively - in effect,
|
||||
* setting the size of these devices to 0. This would ensure
|
||||
* that the complete memory map is assigned to only one flash device.
|
||||
*/
|
||||
qspi_write32(qspi->priv.flags, ®s->sfa1ad,
|
||||
FSL_QSPI_FLASH_SIZE | amba_bases[bus]);
|
||||
qspi_write32(qspi->priv.flags, ®s->sfa2ad,
|
||||
FSL_QSPI_FLASH_SIZE | amba_bases[bus]);
|
||||
qspi_write32(qspi->priv.flags, ®s->sfb1ad,
|
||||
total_size | amba_bases[bus]);
|
||||
qspi_write32(qspi->priv.flags, ®s->sfb2ad,
|
||||
total_size | amba_bases[bus]);
|
||||
|
||||
qspi_set_lut(&qspi->priv);
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_QSPI_AHB
|
||||
qspi_init_ahb_read(&qspi->priv);
|
||||
#endif
|
||||
|
||||
qspi_module_disable(&qspi->priv, 0);
|
||||
|
||||
return &qspi->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct fsl_qspi *qspi = to_qspi_spi(slave);
|
||||
|
||||
free(qspi);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct fsl_qspi *qspi = to_qspi_spi(slave);
|
||||
|
||||
return qspi_xfer(&qspi->priv, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
/* Nothing to do */
|
||||
}
|
||||
#else
|
||||
static int fsl_qspi_child_pre_probe(struct udevice *dev)
|
||||
{
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
@@ -1265,4 +1128,3 @@ U_BOOT_DRIVER(fsl_qspi) = {
|
||||
.probe = fsl_qspi_probe,
|
||||
.child_pre_probe = fsl_qspi_child_pre_probe,
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -67,15 +67,12 @@ struct sh_qspi_regs {
|
||||
};
|
||||
|
||||
struct sh_qspi_slave {
|
||||
#ifndef CONFIG_DM_SPI
|
||||
struct spi_slave slave;
|
||||
#endif
|
||||
struct sh_qspi_regs *regs;
|
||||
};
|
||||
|
||||
static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct sh_qspi_slave, slave);
|
||||
}
|
||||
|
||||
static void sh_qspi_init(struct sh_qspi_slave *ss)
|
||||
{
|
||||
/* QSPI initialize */
|
||||
@@ -119,15 +116,8 @@ static void sh_qspi_init(struct sh_qspi_slave *ss)
|
||||
setbits_8(&ss->regs->spcr, SPCR_SPE);
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
static void sh_qspi_cs_activate(struct sh_qspi_slave *ss)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
|
||||
/* Set master mode only */
|
||||
writeb(SPCR_MSTR, &ss->regs->spcr);
|
||||
|
||||
@@ -147,61 +137,15 @@ void spi_cs_activate(struct spi_slave *slave)
|
||||
setbits_8(&ss->regs->spcr, SPCR_SPE);
|
||||
}
|
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave)
|
||||
static void sh_qspi_cs_deactivate(struct sh_qspi_slave *ss)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
|
||||
/* Disable SPI Function */
|
||||
clrbits_8(&ss->regs->spcr, SPCR_SPE);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
static int sh_qspi_xfer_common(struct sh_qspi_slave *ss, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct sh_qspi_slave *ss;
|
||||
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs);
|
||||
if (!ss) {
|
||||
printf("SPI_error: Fail to allocate sh_qspi_slave\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE;
|
||||
|
||||
/* Init SH QSPI */
|
||||
sh_qspi_init(ss);
|
||||
|
||||
return &ss->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct sh_qspi_slave *spi = to_sh_qspi(slave);
|
||||
|
||||
free(spi);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
void *din, unsigned long flags)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
u32 nbyte, chunk;
|
||||
int i, ret = 0;
|
||||
u8 dtdata = 0, drdata;
|
||||
@@ -210,7 +154,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
|
||||
if (dout == NULL && din == NULL) {
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(slave);
|
||||
sh_qspi_cs_deactivate(ss);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -222,7 +166,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
nbyte = bitlen / 8;
|
||||
|
||||
if (flags & SPI_XFER_BEGIN) {
|
||||
spi_cs_activate(slave);
|
||||
sh_qspi_cs_activate(ss);
|
||||
|
||||
/* Set 1048576 byte */
|
||||
writel(0x100000, spbmul0);
|
||||
@@ -273,7 +217,148 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(slave);
|
||||
sh_qspi_cs_deactivate(ss);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_DM_SPI
|
||||
static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave)
|
||||
{
|
||||
return container_of(slave, struct sh_qspi_slave, slave);
|
||||
}
|
||||
|
||||
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void spi_cs_activate(struct spi_slave *slave)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
|
||||
sh_qspi_cs_activate(ss);
|
||||
}
|
||||
|
||||
void spi_cs_deactivate(struct spi_slave *slave)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
|
||||
sh_qspi_cs_deactivate(ss);
|
||||
}
|
||||
|
||||
void spi_init(void)
|
||||
{
|
||||
/* nothing to do */
|
||||
}
|
||||
|
||||
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
|
||||
unsigned int max_hz, unsigned int mode)
|
||||
{
|
||||
struct sh_qspi_slave *ss;
|
||||
|
||||
if (!spi_cs_is_valid(bus, cs))
|
||||
return NULL;
|
||||
|
||||
ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs);
|
||||
if (!ss) {
|
||||
printf("SPI_error: Fail to allocate sh_qspi_slave\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE;
|
||||
|
||||
/* Init SH QSPI */
|
||||
sh_qspi_init(ss);
|
||||
|
||||
return &ss->slave;
|
||||
}
|
||||
|
||||
void spi_free_slave(struct spi_slave *slave)
|
||||
{
|
||||
struct sh_qspi_slave *spi = to_sh_qspi(slave);
|
||||
|
||||
free(spi);
|
||||
}
|
||||
|
||||
int spi_claim_bus(struct spi_slave *slave)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
}
|
||||
|
||||
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct sh_qspi_slave *ss = to_sh_qspi(slave);
|
||||
|
||||
return sh_qspi_xfer_common(ss, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <dm.h>
|
||||
|
||||
static int sh_qspi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct sh_qspi_slave *ss = dev_get_platdata(bus);
|
||||
|
||||
return sh_qspi_xfer_common(ss, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
static int sh_qspi_set_speed(struct udevice *dev, uint speed)
|
||||
{
|
||||
/* This is a SPI NOR controller, do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_qspi_set_mode(struct udevice *dev, uint mode)
|
||||
{
|
||||
/* This is a SPI NOR controller, do nothing. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_qspi_probe(struct udevice *dev)
|
||||
{
|
||||
struct sh_qspi_slave *ss = dev_get_platdata(dev);
|
||||
|
||||
sh_qspi_init(ss);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh_qspi_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct sh_qspi_slave *plat = dev_get_platdata(dev);
|
||||
|
||||
plat->regs = (struct sh_qspi_regs *)dev_read_addr(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops sh_qspi_ops = {
|
||||
.xfer = sh_qspi_xfer,
|
||||
.set_speed = sh_qspi_set_speed,
|
||||
.set_mode = sh_qspi_set_mode,
|
||||
};
|
||||
|
||||
static const struct udevice_id sh_qspi_ids[] = {
|
||||
{ .compatible = "renesas,qspi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sh_qspi) = {
|
||||
.name = "sh_qspi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = sh_qspi_ids,
|
||||
.ops = &sh_qspi_ops,
|
||||
.ofdata_to_platdata = sh_qspi_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct sh_qspi_slave),
|
||||
.probe = sh_qspi_probe,
|
||||
};
|
||||
#endif
|
||||
|
||||
501
drivers/spi/spi-mem.c
Normal file
501
drivers/spi/spi-mem.c
Normal file
@@ -0,0 +1,501 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018 Exceet Electronics GmbH
|
||||
* Copyright (C) 2018 Bootlin
|
||||
*
|
||||
* Author: Boris Brezillon <boris.brezillon@bootlin.com>
|
||||
*/
|
||||
|
||||
#ifndef __UBOOT__
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "internals.h"
|
||||
#else
|
||||
#include <spi.h>
|
||||
#include <spi-mem.h>
|
||||
#endif
|
||||
|
||||
#ifndef __UBOOT__
|
||||
/**
|
||||
* 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);
|
||||
#endif /* __UBOOT__ */
|
||||
|
||||
static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx)
|
||||
{
|
||||
u32 mode = slave->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;
|
||||
}
|
||||
|
||||
bool spi_mem_default_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (spi_check_buswidth_req(slave, op->cmd.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes &&
|
||||
spi_check_buswidth_req(slave, op->addr.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->dummy.nbytes &&
|
||||
spi_check_buswidth_req(slave, op->dummy.buswidth, true))
|
||||
return false;
|
||||
|
||||
if (op->data.nbytes &&
|
||||
spi_check_buswidth_req(slave, 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
|
||||
* @slave: the SPI device
|
||||
* @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_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct udevice *bus = slave->dev->parent;
|
||||
struct dm_spi_ops *ops = spi_get_ops(bus);
|
||||
|
||||
if (ops->mem_ops && ops->mem_ops->supports_op)
|
||||
return ops->mem_ops->supports_op(slave, op);
|
||||
|
||||
return spi_mem_default_supports_op(slave, op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_supports_op);
|
||||
|
||||
/**
|
||||
* spi_mem_exec_op() - Execute a memory operation
|
||||
* @slave: the SPI device
|
||||
* @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_slave *slave, const struct spi_mem_op *op)
|
||||
{
|
||||
struct udevice *bus = slave->dev->parent;
|
||||
struct dm_spi_ops *ops = spi_get_ops(bus);
|
||||
unsigned int pos = 0;
|
||||
const u8 *tx_buf = NULL;
|
||||
u8 *rx_buf = NULL;
|
||||
u8 *op_buf;
|
||||
int op_len;
|
||||
u32 flag;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (!spi_mem_supports_op(slave, op))
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (ops->mem_ops) {
|
||||
#ifndef __UBOOT__
|
||||
/*
|
||||
* 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);
|
||||
#endif
|
||||
ret = ops->mem_ops->exec_op(slave, op);
|
||||
#ifndef __UBOOT__
|
||||
mutex_unlock(&ctlr->io_mutex);
|
||||
mutex_unlock(&ctlr->bus_lock_mutex);
|
||||
|
||||
if (ctlr->auto_runtime_pm)
|
||||
pm_runtime_put(ctlr->dev.parent);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
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(slave, &msg);
|
||||
|
||||
kfree(tmpbuf);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (msg.actual_length != totalxferlen)
|
||||
return -EIO;
|
||||
#else
|
||||
|
||||
/* U-Boot does not support parallel SPI data lanes */
|
||||
if ((op->cmd.buswidth != 1) ||
|
||||
(op->addr.nbytes && op->addr.buswidth != 1) ||
|
||||
(op->dummy.nbytes && op->dummy.buswidth != 1) ||
|
||||
(op->data.nbytes && op->data.buswidth != 1)) {
|
||||
printf("Dual/Quad raw SPI transfers not supported\n");
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
rx_buf = op->data.buf.in;
|
||||
else
|
||||
tx_buf = op->data.buf.out;
|
||||
}
|
||||
|
||||
op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
op_buf = calloc(1, op_len);
|
||||
|
||||
ret = spi_claim_bus(slave);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
op_buf[pos++] = op->cmd.opcode;
|
||||
|
||||
if (op->addr.nbytes) {
|
||||
for (i = 0; i < op->addr.nbytes; i++)
|
||||
op_buf[pos + i] = op->addr.val >>
|
||||
(8 * (op->addr.nbytes - i - 1));
|
||||
|
||||
pos += op->addr.nbytes;
|
||||
}
|
||||
|
||||
if (op->dummy.nbytes)
|
||||
memset(op_buf + pos, 0xff, op->dummy.nbytes);
|
||||
|
||||
/* 1st transfer: opcode + address + dummy cycles */
|
||||
flag = SPI_XFER_BEGIN;
|
||||
/* Make sure to set END bit if no tx or rx data messages follow */
|
||||
if (!tx_buf && !rx_buf)
|
||||
flag |= SPI_XFER_END;
|
||||
|
||||
ret = spi_xfer(slave, op_len * 8, op_buf, NULL, flag);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* 2nd transfer: rx or tx data path */
|
||||
if (tx_buf || rx_buf) {
|
||||
ret = spi_xfer(slave, op->data.nbytes * 8, tx_buf,
|
||||
rx_buf, SPI_XFER_END);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_release_bus(slave);
|
||||
|
||||
for (i = 0; i < pos; i++)
|
||||
debug("%02x ", op_buf[i]);
|
||||
debug("| [%dB %s] ",
|
||||
tx_buf || rx_buf ? op->data.nbytes : 0,
|
||||
tx_buf || rx_buf ? (tx_buf ? "out" : "in") : "-");
|
||||
for (i = 0; i < op->data.nbytes; i++)
|
||||
debug("%02x ", tx_buf ? tx_buf[i] : rx_buf[i]);
|
||||
debug("[ret %d]\n", ret);
|
||||
|
||||
free(op_buf);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
#endif /* __UBOOT__ */
|
||||
|
||||
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
|
||||
* @slave: the SPI device
|
||||
* @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_slave *slave, struct spi_mem_op *op)
|
||||
{
|
||||
struct udevice *bus = slave->dev->parent;
|
||||
struct dm_spi_ops *ops = spi_get_ops(bus);
|
||||
|
||||
if (ops->mem_ops && ops->mem_ops->adjust_op_size)
|
||||
return ops->mem_ops->adjust_op_size(slave, op);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
|
||||
|
||||
#ifndef __UBOOT__
|
||||
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);
|
||||
#endif /* __UBOOT__ */
|
||||
Reference in New Issue
Block a user