Merge branch '2022-09-13-add-aspeed-spi-controller' into next

To quote the author:
This patch series aims to porting ASPEED FMC/SPI memory controller
driver with spi-mem interface. spi-mem dirmap framework is also
synchronized from Linux. These patches have been verified on
AST2600, AST2500 and AST2400 EVBs.
This commit is contained in:
Tom Rini 2022-09-13 15:55:33 -04:00
commit c2238fcf0c
18 changed files with 1850 additions and 46 deletions

View File

@ -724,6 +724,13 @@ S: Maintained
F: drivers/pci/pcie_phytium.c
F: arch/arm/dts/phytium-durian.dts
ASPEED FMC SPI DRIVER
M: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
M: Cédric Le Goater <clg@kaod.org>
R: Aspeed BMC SW team <BMC-SW@aspeedtech.com>
S: Maintained
F: drivers/spi/spi-aspeed-smc.c
BINMAN
M: Simon Glass <sjg@chromium.org>
M: Alper Nebi Yasak <alpernebiyasak@gmail.com>

View File

@ -78,6 +78,39 @@
pinctrl-0 = <&pinctrl_sd2_default>;
};
&fmc {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fwspics1_default>;
flash@0 {
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <2>;
spi-rx-bus-width = <2>;
};
flash@1 {
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <2>;
spi-rx-bus-width = <2>;
};
};
&spi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi1cs1_default>;
flash@0 {
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <2>;
spi-rx-bus-width = <2>;
};
};
&i2c3 {
status = "okay";

View File

@ -57,23 +57,26 @@
ranges;
fmc: flash-controller@1e620000 {
reg = < 0x1e620000 0xc4
0x20000000 0x10000000 >;
reg = <0x1e620000 0xc4>, <0x20000000 0x10000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2500-fmc";
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <3>;
status = "disabled";
interrupts = <19>;
flash@0 {
reg = < 0 >;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@2 {
reg = < 2 >;
compatible = "jedec,spi-nor";
@ -82,17 +85,20 @@
};
spi1: flash-controller@1e630000 {
reg = < 0x1e630000 0xc4
0x30000000 0x08000000 >;
reg = <0x1e630000 0xc4>, <0x30000000 0x08000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2500-spi";
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <2>;
status = "disabled";
flash@0 {
reg = < 0 >;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
compatible = "jedec,spi-nor";
@ -101,17 +107,20 @@
};
spi2: flash-controller@1e631000 {
reg = < 0x1e631000 0xc4
0x38000000 0x08000000 >;
reg = <0x1e631000 0xc4>, <0x38000000 0x08000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2500-spi";
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <2>;
status = "disabled";
flash@0 {
reg = < 0 >;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
compatible = "jedec,spi-nor";

View File

@ -72,12 +72,10 @@
&fmc {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_fmcquad_default>;
flash@0 {
compatible = "spi-flash", "sst,w25q256";
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <4>;
@ -85,7 +83,6 @@
};
flash@1 {
compatible = "spi-flash", "sst,w25q256";
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <4>;
@ -93,7 +90,6 @@
};
flash@2 {
compatible = "spi-flash", "sst,w25q256";
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <4>;
@ -103,14 +99,12 @@
&spi1 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi1_default &pinctrl_spi1abr_default
&pinctrl_spi1cs1_default &pinctrl_spi1wp_default
&pinctrl_spi1wp_default &pinctrl_spi1quad_default>;
flash@0 {
compatible = "spi-flash", "sst,w25q256";
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <4>;
@ -120,13 +114,11 @@
&spi2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_spi2_default &pinctrl_spi2cs1_default
&pinctrl_spi2cs2_default &pinctrl_spi2quad_default>;
flash@0 {
compatible = "spi-flash", "sst,w25q256";
status = "okay";
spi-max-frequency = <50000000>;
spi-tx-bus-width = <4>;

View File

@ -129,74 +129,78 @@
};
fmc: flash-controller@1e620000 {
reg = < 0x1e620000 0xc4
0x20000000 0x10000000 >;
reg = <0x1e620000 0xc4>, <0x20000000 0x10000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2600-fmc";
status = "disabled";
interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <3>;
flash@0 {
reg = < 0 >;
reg = <0>;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
reg = <1>;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@2 {
reg = < 2 >;
reg = <2>;
compatible = "jedec,spi-nor";
status = "disabled";
};
};
spi1: flash-controller@1e630000 {
reg = < 0x1e630000 0xc4
0x30000000 0x08000000 >;
reg = <0x1e630000 0xc4>, <0x30000000 0x10000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2600-spi";
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <2>;
status = "disabled";
flash@0 {
reg = < 0 >;
reg = <0>;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
reg = <1>;
compatible = "jedec,spi-nor";
status = "disabled";
};
};
spi2: flash-controller@1e631000 {
reg = < 0x1e631000 0xc4
0x50000000 0x08000000 >;
reg = <0x1e631000 0xc4>, <0x50000000 0x10000000>;
#address-cells = <1>;
#size-cells = <0>;
compatible = "aspeed,ast2600-spi";
clocks = <&scu ASPEED_CLK_AHB>;
num-cs = <3>;
status = "disabled";
flash@0 {
reg = < 0 >;
reg = <0>;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@1 {
reg = < 1 >;
reg = <1>;
compatible = "jedec,spi-nor";
status = "disabled";
};
flash@2 {
reg = < 2 >;
reg = <2>;
compatible = "jedec,spi-nor";
status = "disabled";
};

View File

@ -39,6 +39,16 @@ CONFIG_SYS_I2C_ASPEED=y
CONFIG_I2C_EEPROM=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ASPEED=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_SFDP_SUPPORT=y
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_ISSI=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_SST=y
CONFIG_SPI_FLASH_WINBOND=y
# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
CONFIG_PHY_REALTEK=y
CONFIG_FTGMAC100=y
CONFIG_PHY=y
@ -47,6 +57,10 @@ CONFIG_RAM=y
CONFIG_DM_RESET=y
CONFIG_DM_SERIAL=y
CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_SPI_DIRMAP=y
CONFIG_SPI_ASPEED_SMC=y
CONFIG_SYSRESET=y
CONFIG_TIMER=y
CONFIG_WDT=y

View File

@ -87,6 +87,16 @@ CONFIG_SYS_I2C_ASPEED=y
CONFIG_I2C_EEPROM=y
CONFIG_MMC_SDHCI=y
CONFIG_MMC_SDHCI_ASPEED=y
CONFIG_DM_SPI_FLASH=y
CONFIG_SPI_FLASH_SFDP_SUPPORT=y
CONFIG_SPI_FLASH_GIGADEVICE=y
CONFIG_SPI_FLASH_ISSI=y
CONFIG_SPI_FLASH_MACRONIX=y
CONFIG_SPI_FLASH_SPANSION=y
CONFIG_SPI_FLASH_STMICRO=y
CONFIG_SPI_FLASH_SST=y
CONFIG_SPI_FLASH_WINBOND=y
# CONFIG_SPI_FLASH_USE_4K_SECTORS is not set
CONFIG_PHY_REALTEK=y
CONFIG_DM_MDIO=y
CONFIG_FTGMAC100=y
@ -98,6 +108,10 @@ CONFIG_SPL_RAM=y
CONFIG_DM_RESET=y
CONFIG_DM_SERIAL=y
CONFIG_SYS_NS16550=y
CONFIG_SPI=y
CONFIG_DM_SPI=y
CONFIG_SPI_DIRMAP=y
CONFIG_SPI_ASPEED_SMC=y
CONFIG_SYSRESET=y
CONFIG_SPL_SYSRESET=y
CONFIG_WDT=y

View File

@ -30,6 +30,12 @@
#define D2PLL_DEFAULT_RATE (250 * 1000 * 1000)
/*
* AXI/AHB clock selection, taken from Aspeed SDK
*/
#define SCU_HWSTRAP_AXIAHB_DIV_SHIFT 9
#define SCU_HWSTRAP_AXIAHB_DIV_MASK (0x7 << SCU_HWSTRAP_AXIAHB_DIV_SHIFT)
DECLARE_GLOBAL_DATA_PTR;
/*
@ -86,6 +92,20 @@ static ulong ast2500_get_clkin(struct ast2500_scu *scu)
? 25 * 1000 * 1000 : 24 * 1000 * 1000;
}
static u32 ast2500_get_hclk(ulong clkin, struct ast2500_scu *scu)
{
u32 hpll_reg = readl(&scu->h_pll_param);
ulong axi_div = 2;
u32 rate;
ulong ahb_div = 1 + ((readl(&scu->hwstrap)
& SCU_HWSTRAP_AXIAHB_DIV_MASK)
>> SCU_HWSTRAP_AXIAHB_DIV_SHIFT);
rate = ast2500_get_hpll_rate(clkin, hpll_reg);
return (rate / axi_div / ahb_div);
}
/**
* Get current rate or uart clock
*
@ -147,6 +167,9 @@ static ulong ast2500_clk_get_rate(struct clk *clk)
rate = rate / apb_div;
}
break;
case ASPEED_CLK_AHB:
rate = ast2500_get_hclk(clkin, priv->scu);
break;
case ASPEED_CLK_SDIO:
{
ulong apb_div = 4 + 4 * ((readl(&priv->scu->clk_sel1)

View File

@ -10,13 +10,69 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <linux/mtd/spi-nor.h>
#include <log.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include <spi-mem.h>
#include "sf_internal.h"
static int spi_nor_create_read_dirmap(struct spi_nor *nor)
{
struct spi_mem_dirmap_info info = {
.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->read_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
SPI_MEM_OP_DUMMY(nor->read_dummy, 0),
SPI_MEM_OP_DATA_IN(0, NULL, 0)),
.offset = 0,
.length = nor->mtd.size,
};
struct spi_mem_op *op = &info.op_tmpl;
/* get transfer protocols. */
spi_nor_setup_op(nor, op, nor->read_proto);
op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->read_proto);
/* convert the dummy cycles to the number of bytes */
op->dummy.nbytes = (nor->read_dummy * op->dummy.buswidth) / 8;
if (spi_nor_protocol_is_dtr(nor->read_proto))
op->dummy.nbytes *= 2;
nor->dirmap.rdesc = spi_mem_dirmap_create(nor->spi, &info);
if (IS_ERR(nor->dirmap.rdesc))
return PTR_ERR(nor->dirmap.rdesc);
return 0;
}
static int spi_nor_create_write_dirmap(struct spi_nor *nor)
{
struct spi_mem_dirmap_info info = {
.op_tmpl = SPI_MEM_OP(SPI_MEM_OP_CMD(nor->program_opcode, 0),
SPI_MEM_OP_ADDR(nor->addr_width, 0, 0),
SPI_MEM_OP_NO_DUMMY,
SPI_MEM_OP_DATA_OUT(0, NULL, 0)),
.offset = 0,
.length = nor->mtd.size,
};
struct spi_mem_op *op = &info.op_tmpl;
/* get transfer protocols. */
spi_nor_setup_op(nor, op, nor->write_proto);
op->data.buswidth = spi_nor_get_protocol_data_nbits(nor->write_proto);
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
op->addr.nbytes = 0;
nor->dirmap.wdesc = spi_mem_dirmap_create(nor->spi, &info);
if (IS_ERR(nor->dirmap.wdesc))
return PTR_ERR(nor->dirmap.wdesc);
return 0;
}
/**
* spi_flash_probe_slave() - Probe for a SPI flash device on a bus
*
@ -45,6 +101,16 @@ static int spi_flash_probe_slave(struct spi_flash *flash)
if (ret)
goto err_read_id;
if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
ret = spi_nor_create_read_dirmap(flash);
if (ret)
return ret;
ret = spi_nor_create_write_dirmap(flash);
if (ret)
return ret;
}
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
ret = spi_flash_mtd_register(flash);
@ -83,6 +149,11 @@ struct spi_flash *spi_flash_probe(unsigned int busnum, unsigned int cs,
void spi_flash_free(struct spi_flash *flash)
{
if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
spi_mem_dirmap_destroy(flash->dirmap.wdesc);
spi_mem_dirmap_destroy(flash->dirmap.rdesc);
}
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
spi_flash_mtd_unregister(flash);
@ -153,6 +224,11 @@ static int spi_flash_std_remove(struct udevice *dev)
struct spi_flash *flash = dev_get_uclass_priv(dev);
int ret;
if (CONFIG_IS_ENABLED(SPI_DIRMAP)) {
spi_mem_dirmap_destroy(flash->dirmap.wdesc);
spi_mem_dirmap_destroy(flash->dirmap.rdesc);
}
ret = spi_nor_remove(flash);
if (ret)
return ret;

View File

@ -246,9 +246,9 @@ static u8 spi_nor_get_cmd_ext(const struct spi_nor *nor,
* need to be initialized.
* @proto: the protocol from which the properties need to be set.
*/
static void spi_nor_setup_op(const struct spi_nor *nor,
struct spi_mem_op *op,
const enum spi_nor_protocol proto)
void spi_nor_setup_op(const struct spi_nor *nor,
struct spi_mem_op *op,
const enum spi_nor_protocol proto)
{
u8 ext;
@ -369,13 +369,29 @@ static ssize_t spi_nor_read_data(struct spi_nor *nor, loff_t from, size_t len,
while (remaining) {
op.data.nbytes = remaining < UINT_MAX ? remaining : UINT_MAX;
ret = spi_mem_adjust_op_size(nor->spi, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.rdesc) {
/*
* Record current operation information which may be used
* when the address or data length exceeds address mapping.
*/
memcpy(&nor->dirmap.rdesc->info.op_tmpl, &op,
sizeof(struct spi_mem_op));
ret = spi_mem_dirmap_read(nor->dirmap.rdesc,
op.addr.val, op.data.nbytes,
op.data.buf.in);
if (ret < 0)
return ret;
op.data.nbytes = ret;
} else {
ret = spi_mem_adjust_op_size(nor->spi, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
}
op.addr.val += op.data.nbytes;
remaining -= op.data.nbytes;
@ -400,14 +416,21 @@ static ssize_t spi_nor_write_data(struct spi_nor *nor, loff_t to, size_t len,
spi_nor_setup_op(nor, &op, nor->write_proto);
ret = spi_mem_adjust_op_size(nor->spi, &op);
if (ret)
return ret;
op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
if (CONFIG_IS_ENABLED(SPI_DIRMAP) && nor->dirmap.wdesc) {
memcpy(&nor->dirmap.wdesc->info.op_tmpl, &op,
sizeof(struct spi_mem_op));
op.data.nbytes = spi_mem_dirmap_write(nor->dirmap.wdesc, op.addr.val,
op.data.nbytes, op.data.buf.out);
} else {
ret = spi_mem_adjust_op_size(nor->spi, &op);
if (ret)
return ret;
op.data.nbytes = len < op.data.nbytes ? len : op.data.nbytes;
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(nor->spi, &op);
if (ret)
return ret;
}
return op.data.nbytes;
}

View File

@ -424,6 +424,11 @@ const struct flash_info spi_nor_ids[] = {
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q512jvq", 0xef4020, 0, 64 * 1024, 1024,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |
SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
},
{
INFO("w25q01jv", 0xef4021, 0, 64 * 1024, 2048,
SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ |

View File

@ -61,6 +61,8 @@ static const struct ast2500_group_config ast2500_groups[] = {
{ "MDIO2", 5, (1 << 2) },
{ "SD1", 5, (1 << 0) },
{ "SD2", 5, (1 << 1) },
{ "FWSPICS1", 3, (1 << 24) },
{ "SPI1CS1", 1, (1 << 15) },
};
static int ast2500_pinctrl_get_groups_count(struct udevice *dev)

View File

@ -40,6 +40,16 @@ config SPI_MEM
This extension is meant to simplify interaction with SPI memories
by providing an high-level interface to send memory-like commands.
config SPI_DIRMAP
bool "SPI direct mapping"
depends on SPI_MEM
help
Enable the SPI direct mapping API. Most modern SPI controllers can
directly map a SPI memory (or a portion of the SPI memory) in the CPU
address space. Most of the time this brings significant performance
improvements as it automates the whole process of sending SPI memory
operations every time a new region is accessed.
if DM_SPI
config ALTERA_SPI
@ -401,6 +411,14 @@ config SANDBOX_SPI
};
};
config SPI_ASPEED_SMC
bool "ASPEED SPI flash controller driver"
depends on DM_SPI && SPI_MEM
default n
help
Enable ASPEED SPI flash controller driver for AST2500
and AST2600 SoCs.
config SPI_SIFIVE
bool "SiFive SPI driver"
help

View File

@ -10,6 +10,7 @@ obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
obj-$(CONFIG_CADENCE_OSPI_VERSAL) += cadence_ospi_versal.o
obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
obj-$(CONFIG_SOFT_SPI) += soft_spi.o
obj-$(CONFIG_SPI_ASPEED_SMC) += spi-aspeed-smc.o
obj-$(CONFIG_SPI_MEM) += spi-mem.o
obj-$(CONFIG_TI_QSPI) += ti_qspi.o
obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o

1218
drivers/spi/spi-aspeed-smc.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -21,6 +21,8 @@
#include <spi.h>
#include <spi-mem.h>
#include <dm/device_compat.h>
#include <dm/devres.h>
#include <linux/bug.h>
#endif
#ifndef __UBOOT__
@ -491,6 +493,272 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.in = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->slave, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->slave, &op);
if (ret)
return ret;
return op.data.nbytes;
}
static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.out = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->slave, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->slave, &op);
if (ret)
return ret;
return op.data.nbytes;
}
/**
* spi_mem_dirmap_create() - Create a direct mapping descriptor
* @mem: SPI mem device this direct mapping should be created for
* @info: direct mapping information
*
* This function is creating a direct mapping descriptor which can then be used
* to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
* If the SPI controller driver does not support direct mapping, this function
* falls back to an implementation using spi_mem_exec_op(), so that the caller
* doesn't have to bother implementing a fallback on his own.
*
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
*/
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_slave *slave,
const struct spi_mem_dirmap_info *info)
{
struct udevice *bus = slave->dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
struct spi_mem_dirmap_desc *desc;
int ret = -EOPNOTSUPP;
/* Make sure the number of address cycles is between 1 and 8 bytes. */
if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
return ERR_PTR(-EINVAL);
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
return ERR_PTR(-EINVAL);
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
desc->slave = slave;
desc->info = *info;
if (ops->mem_ops && ops->mem_ops->dirmap_create)
ret = ops->mem_ops->dirmap_create(desc);
if (ret) {
desc->nodirmap = true;
if (!spi_mem_supports_op(desc->slave, &desc->info.op_tmpl))
ret = -EOPNOTSUPP;
else
ret = 0;
}
if (ret) {
kfree(desc);
return ERR_PTR(ret);
}
return desc;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
/**
* spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
* @desc: the direct mapping descriptor to destroy
*
* This function destroys a direct mapping descriptor previously created by
* spi_mem_dirmap_create().
*/
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
{
struct udevice *bus = desc->slave->dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
if (!desc->nodirmap && ops->mem_ops && ops->mem_ops->dirmap_destroy)
ops->mem_ops->dirmap_destroy(desc);
kfree(desc);
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
#ifndef __UBOOT__
static void devm_spi_mem_dirmap_release(struct udevice *dev, void *res)
{
struct spi_mem_dirmap_desc *desc = *(struct spi_mem_dirmap_desc **)res;
spi_mem_dirmap_destroy(desc);
}
/**
* devm_spi_mem_dirmap_create() - Create a direct mapping descriptor and attach
* it to a device
* @dev: device the dirmap desc will be attached to
* @mem: SPI mem device this direct mapping should be created for
* @info: direct mapping information
*
* devm_ variant of the spi_mem_dirmap_create() function. See
* spi_mem_dirmap_create() for more details.
*
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
*/
struct spi_mem_dirmap_desc *
devm_spi_mem_dirmap_create(struct udevice *dev, struct spi_slave *slave,
const struct spi_mem_dirmap_info *info)
{
struct spi_mem_dirmap_desc **ptr, *desc;
ptr = devres_alloc(devm_spi_mem_dirmap_release, sizeof(*ptr),
GFP_KERNEL);
if (!ptr)
return ERR_PTR(-ENOMEM);
desc = spi_mem_dirmap_create(slave, info);
if (IS_ERR(desc)) {
devres_free(ptr);
} else {
*ptr = desc;
devres_add(dev, ptr);
}
return desc;
}
EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_create);
static int devm_spi_mem_dirmap_match(struct udevice *dev, void *res, void *data)
{
struct spi_mem_dirmap_desc **ptr = res;
if (WARN_ON(!ptr || !*ptr))
return 0;
return *ptr == data;
}
/**
* devm_spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor attached
* to a device
* @dev: device the dirmap desc is attached to
* @desc: the direct mapping descriptor to destroy
*
* devm_ variant of the spi_mem_dirmap_destroy() function. See
* spi_mem_dirmap_destroy() for more details.
*/
void devm_spi_mem_dirmap_destroy(struct udevice *dev,
struct spi_mem_dirmap_desc *desc)
{
devres_release(dev, devm_spi_mem_dirmap_release,
devm_spi_mem_dirmap_match, desc);
}
EXPORT_SYMBOL_GPL(devm_spi_mem_dirmap_destroy);
#endif /* __UBOOT__ */
/**
* spi_mem_dirmap_read() - Read data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start reading from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: destination buffer. This buffer must be DMA-able
*
* This function reads data from a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data read from the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_read() again when that happens.
*/
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct udevice *bus = desc->slave->dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap)
ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
else if (ops->mem_ops && ops->mem_ops->dirmap_read)
ret = ops->mem_ops->dirmap_read(desc, offs, len, buf);
else
ret = -EOPNOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
/**
* spi_mem_dirmap_write() - Write data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start writing from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: source buffer. This buffer must be DMA-able
*
* This function writes data to a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data written to the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_write() again when that happens.
*/
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct udevice *bus = desc->slave->dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap)
ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
else if (ops->mem_ops && ops->mem_ops->dirmap_write)
ret = ops->mem_ops->dirmap_write(desc, offs, len, buf);
else
ret = -EOPNOTSUPP;
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
#ifndef __UBOOT__
static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
{

View File

@ -11,6 +11,7 @@
#include <linux/bitops.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/mtd.h>
#include <spi-mem.h>
/*
* Manufacturer IDs
@ -522,6 +523,7 @@ struct spi_flash {
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
* @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
* @ready: [FLASH-SPECIFIC] check if the flash is ready
* @dirmap: pointers to struct spi_mem_dirmap_desc for reads/writes.
* @priv: the private data
*/
struct spi_nor {
@ -572,6 +574,11 @@ struct spi_nor {
int (*octal_dtr_enable)(struct spi_nor *nor);
int (*ready)(struct spi_nor *nor);
struct {
struct spi_mem_dirmap_desc *rdesc;
struct spi_mem_dirmap_desc *wdesc;
} dirmap;
void *priv;
char mtd_name[MTD_NAME_SIZE(MTD_DEV_TYPE_NOR)];
/* Compatibility for spi_flash, remove once sf layer is merged with mtd */
@ -595,6 +602,17 @@ device_node *spi_nor_get_flash_node(struct spi_nor *nor)
}
#endif /* __UBOOT__ */
/**
* spi_nor_setup_op() - Set up common properties of a spi-mem op.
* @nor: pointer to a 'struct spi_nor'
* @op: pointer to the 'struct spi_mem_op' whose properties
* need to be initialized.
* @proto: the protocol from which the properties need to be set.
*/
void spi_nor_setup_op(const struct spi_nor *nor,
struct spi_mem_op *op,
const enum spi_nor_protocol proto);
/**
* spi_nor_scan() - scan the SPI NOR
* @nor: the spi_nor structure

View File

@ -134,6 +134,48 @@ struct spi_mem_op {
.dummy = __dummy, \
.data = __data, \
}
/**
* struct spi_mem_dirmap_info - Direct mapping information
* @op_tmpl: operation template that should be used by the direct mapping when
* the memory device is accessed
* @offset: absolute offset this direct mapping is pointing to
* @length: length in byte of this direct mapping
*
* This information is used by the controller specific implementation to know
* the portion of memory that is directly mapped and the spi_mem_op that should
* be used to access the device.
* A direct mapping is only valid for one direction (read or write) and this
* direction is directly encoded in the ->op_tmpl.data.dir field.
*/
struct spi_mem_dirmap_info {
struct spi_mem_op op_tmpl;
u64 offset;
u64 length;
};
/**
* struct spi_mem_dirmap_desc - Direct mapping descriptor
* @mem: the SPI memory device this direct mapping is attached to
* @info: information passed at direct mapping creation time
* @nodirmap: set to 1 if the SPI controller does not implement
* ->mem_ops->dirmap_create() or when this function returned an
* error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
* calls will use spi_mem_exec_op() to access the memory. This is a
* degraded mode that allows spi_mem drivers to use the same code
* no matter whether the controller supports direct mapping or not
* @priv: field pointing to controller specific data
*
* Common part of a direct mapping descriptor. This object is created by
* spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
* can create/attach direct mapping resources to the descriptor in the ->priv
* field.
*/
struct spi_mem_dirmap_desc {
struct spi_slave *slave;
struct spi_mem_dirmap_info info;
unsigned int nodirmap;
void *priv;
};
#ifndef __UBOOT__
/**
@ -183,10 +225,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* limitations)
* @supports_op: check if an operation is supported by the controller
* @exec_op: execute a SPI memory operation
* @dirmap_create: create a direct mapping descriptor that can later be used to
* access the memory device. This method is optional
* @dirmap_destroy: destroy a memory descriptor previous created by
* ->dirmap_create()
* @dirmap_read: read data from the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_read() is responsible for calling it again in
* this case.
* @dirmap_write: write data to the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in
* this case.
*
* 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.
*
* Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
* mapping from the CPU because doing that can stall the CPU waiting for the
* SPI mem transaction to finish, and this will make real-time maintainers
* unhappy and might make your system less reactive. Instead, drivers should
* use DMA to access this direct mapping.
*/
struct spi_controller_mem_ops {
int (*adjust_op_size)(struct spi_slave *slave, struct spi_mem_op *op);
@ -194,6 +258,12 @@ struct spi_controller_mem_ops {
const struct spi_mem_op *op);
int (*exec_op)(struct spi_slave *slave,
const struct spi_mem_op *op);
int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
};
#ifndef __UBOOT__
@ -260,6 +330,15 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
bool spi_mem_default_supports_op(struct spi_slave *mem,
const struct spi_mem_op *op);
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_slave *mem,
const struct spi_mem_dirmap_info *info);
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
#ifndef __UBOOT__
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner);