Merge branch 'next' of https://source.denx.de/u-boot/custodians/u-boot-spi into next
- xSPI Octal DTR support (Pratyush Yadav) - MXIC SPI driver (Zhengxun)
This commit is contained in:
commit
296d5cffdd
@ -88,6 +88,32 @@ config SPI_FLASH_SFDP_SUPPORT
|
||||
SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP)
|
||||
tables as per JESD216 standard.
|
||||
|
||||
config SPI_FLASH_SMART_HWCAPS
|
||||
bool "Smart hardware capability detection based on SPI MEM supports_op() hook"
|
||||
default y
|
||||
help
|
||||
Enable support for smart hardware capability detection based on SPI
|
||||
MEM supports_op() hook that lets controllers express whether they
|
||||
can support a type of operation in a much more refined way compared
|
||||
to using flags like SPI_RX_DUAL, SPI_TX_QUAD, etc.
|
||||
|
||||
config SPI_FLASH_SOFT_RESET
|
||||
bool "Software Reset support for SPI NOR flashes"
|
||||
default n
|
||||
help
|
||||
Enable support for xSPI Software Reset. It will be used to switch from
|
||||
Octal DTR mode to legacy mode on shutdown and boot (if enabled).
|
||||
|
||||
config SPI_FLASH_SOFT_RESET_ON_BOOT
|
||||
bool "Perform a Software Reset on boot on flashes that boot in stateful mode"
|
||||
depends on SPI_FLASH_SOFT_RESET
|
||||
default n
|
||||
help
|
||||
Perform a Software Reset on boot to allow detecting flashes that are
|
||||
handed to us in Octal DTR mode. Do not enable this config on flashes
|
||||
that are not supposed to be handed to U-Boot in Octal DTR mode, even
|
||||
if they _do_ support the Soft Reset sequence.
|
||||
|
||||
config SPI_FLASH_BAR
|
||||
bool "SPI flash Bank/Extended address register support"
|
||||
help
|
||||
@ -141,11 +167,27 @@ config SPI_FLASH_SPANSION
|
||||
help
|
||||
Add support for various Spansion SPI flash chips (S25FLxxx)
|
||||
|
||||
config SPI_FLASH_S28HS512T
|
||||
bool "Cypress S28HS512T chip support"
|
||||
depends on SPI_FLASH_SPANSION
|
||||
help
|
||||
Add support for the Cypress S28HS512T chip. This is a separate config
|
||||
because the fixup hooks for this flash add extra size overhead. Boards
|
||||
that don't use the flash can disable this to save space.
|
||||
|
||||
config SPI_FLASH_STMICRO
|
||||
bool "STMicro SPI flash support"
|
||||
help
|
||||
Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx)
|
||||
|
||||
config SPI_FLASH_MT35XU
|
||||
bool "Micron MT35XU chip support"
|
||||
depends on SPI_FLASH_STMICRO
|
||||
help
|
||||
Add support for the Micron MT35XU chip. This is a separate config
|
||||
because the fixup hooks for this flash add extra size overhead. Boards
|
||||
that don't use the flash can disable this to save space.
|
||||
|
||||
config SPI_FLASH_SST
|
||||
bool "SST SPI flash support"
|
||||
help
|
||||
|
@ -68,6 +68,7 @@ struct flash_info {
|
||||
#define USE_CLSR BIT(14) /* use CLSR command */
|
||||
#define SPI_NOR_HAS_SST26LOCK BIT(15) /* Flash supports lock/unlock via BPR */
|
||||
#define SPI_NOR_OCTAL_READ BIT(16) /* Flash supports Octal Read */
|
||||
#define SPI_NOR_OCTAL_DTR_READ BIT(17) /* Flash supports Octal DTR Read */
|
||||
};
|
||||
|
||||
extern const struct flash_info spi_nor_ids[];
|
||||
|
@ -151,6 +151,11 @@ int spi_flash_std_probe(struct udevice *dev)
|
||||
static int spi_flash_std_remove(struct udevice *dev)
|
||||
{
|
||||
struct spi_flash *flash = dev_get_uclass_priv(dev);
|
||||
int ret;
|
||||
|
||||
ret = spi_nor_remove(flash);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
|
||||
spi_flash_mtd_unregister(flash);
|
||||
@ -178,6 +183,7 @@ U_BOOT_DRIVER(jedec_spi_nor) = {
|
||||
.remove = spi_flash_std_remove,
|
||||
.priv_auto = sizeof(struct spi_nor),
|
||||
.ops = &spi_flash_std_ops,
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
};
|
||||
|
||||
DM_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -193,7 +193,9 @@ const struct flash_info spi_nor_ids[] = {
|
||||
{ INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("mt25ql01g", 0x21ba20, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
|
||||
#ifdef CONFIG_SPI_FLASH_MT35XU
|
||||
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES | SPI_NOR_OCTAL_DTR_READ) },
|
||||
#endif /* CONFIG_SPI_FLASH_MT35XU */
|
||||
{ INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
|
||||
@ -223,6 +225,9 @@ const struct flash_info spi_nor_ids[] = {
|
||||
{ INFO("s25fl208k", 0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
|
||||
{ INFO("s25fl064l", 0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("s25fl128l", 0x016018, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
#ifdef CONFIG_SPI_FLASH_S28HS512T
|
||||
{ INFO("s28hs512t", 0x345b1a, 0, 256 * 1024, 256, SPI_NOR_OCTAL_DTR_READ) },
|
||||
#endif
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_SST /* SST */
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
|
@ -555,28 +555,6 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor)
|
||||
}
|
||||
#endif /* CONFIG_SPI_FLASH_SPANSION */
|
||||
|
||||
struct spi_nor_read_command {
|
||||
u8 num_mode_clocks;
|
||||
u8 num_wait_states;
|
||||
u8 opcode;
|
||||
enum spi_nor_protocol proto;
|
||||
};
|
||||
|
||||
enum spi_nor_read_command_index {
|
||||
SNOR_CMD_READ,
|
||||
SNOR_CMD_READ_FAST,
|
||||
|
||||
/* Quad SPI */
|
||||
SNOR_CMD_READ_1_1_4,
|
||||
|
||||
SNOR_CMD_READ_MAX
|
||||
};
|
||||
|
||||
struct spi_nor_flash_parameter {
|
||||
struct spi_nor_hwcaps hwcaps;
|
||||
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
|
||||
};
|
||||
|
||||
static void
|
||||
spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
||||
u8 num_mode_clocks,
|
||||
|
@ -255,6 +255,13 @@ config MXS_SPI
|
||||
Enable the MXS SPI controller driver. This driver can be used
|
||||
on the i.MX23 and i.MX28 SoCs.
|
||||
|
||||
config SPI_MXIC
|
||||
bool "Macronix MX25F0A SPI controller"
|
||||
help
|
||||
Enable the Macronix MX25F0A SPI controller driver. This driver
|
||||
can be used to access the SPI flash on platforms embedding
|
||||
this Macronix IP core.
|
||||
|
||||
config NXP_FSPI
|
||||
bool "NXP FlexSPI driver"
|
||||
depends on SPI_MEM
|
||||
|
@ -51,6 +51,7 @@ obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
|
||||
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
|
||||
obj-$(CONFIG_PL022_SPI) += pl022_spi.o
|
||||
obj-$(CONFIG_SPI_QUP) += spi-qup.o
|
||||
obj-$(CONFIG_SPI_MXIC) += spi-mxic.o
|
||||
obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
|
||||
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
|
||||
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <linux/sizes.h>
|
||||
#include "cadence_qspi.h"
|
||||
|
||||
#define NSEC_PER_SEC 1000000000L
|
||||
|
||||
#define CQSPI_STIG_READ 0
|
||||
#define CQSPI_STIG_WRITE 1
|
||||
#define CQSPI_READ 2
|
||||
@ -41,20 +43,22 @@ static int cadence_spi_write_speed(struct udevice *bus, uint hz)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cadence_spi_read_id(void *reg_base, u8 len, u8 *idcode)
|
||||
static int cadence_spi_read_id(struct cadence_spi_plat *plat, u8 len,
|
||||
u8 *idcode)
|
||||
{
|
||||
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
|
||||
SPI_MEM_OP_NO_ADDR,
|
||||
SPI_MEM_OP_NO_DUMMY,
|
||||
SPI_MEM_OP_DATA_IN(len, idcode, 1));
|
||||
|
||||
return cadence_qspi_apb_command_read(reg_base, &op);
|
||||
return cadence_qspi_apb_command_read(plat, &op);
|
||||
}
|
||||
|
||||
/* Calibration sequence to determine the read data capture delay register */
|
||||
static int spi_calibration(struct udevice *bus, uint hz)
|
||||
{
|
||||
struct cadence_spi_priv *priv = dev_get_priv(bus);
|
||||
struct cadence_spi_plat *plat = dev_get_plat(bus);
|
||||
void *base = priv->regbase;
|
||||
unsigned int idcode = 0, temp = 0;
|
||||
int err = 0, i, range_lo = -1, range_hi = -1;
|
||||
@ -69,7 +73,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
|
||||
cadence_qspi_apb_controller_enable(base);
|
||||
|
||||
/* read the ID which will be our golden value */
|
||||
err = cadence_spi_read_id(base, 3, (u8 *)&idcode);
|
||||
err = cadence_spi_read_id(plat, 3, (u8 *)&idcode);
|
||||
if (err) {
|
||||
puts("SF: Calibration failed (read)\n");
|
||||
return err;
|
||||
@ -88,7 +92,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
|
||||
cadence_qspi_apb_controller_enable(base);
|
||||
|
||||
/* issue a RDID to get the ID value */
|
||||
err = cadence_spi_read_id(base, 3, (u8 *)&temp);
|
||||
err = cadence_spi_read_id(plat, 3, (u8 *)&temp);
|
||||
if (err) {
|
||||
puts("SF: Calibration failed (read)\n");
|
||||
return err;
|
||||
@ -141,12 +145,20 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz)
|
||||
cadence_qspi_apb_controller_disable(priv->regbase);
|
||||
|
||||
/*
|
||||
* Calibration required for different current SCLK speed, requested
|
||||
* SCLK speed or chip select
|
||||
* If the device tree already provides a read delay value, use that
|
||||
* instead of calibrating.
|
||||
*/
|
||||
if (priv->previous_hz != hz ||
|
||||
priv->qspi_calibrated_hz != hz ||
|
||||
priv->qspi_calibrated_cs != spi_chip_select(bus)) {
|
||||
if (plat->read_delay >= 0) {
|
||||
cadence_spi_write_speed(bus, hz);
|
||||
cadence_qspi_apb_readdata_capture(priv->regbase, 1,
|
||||
plat->read_delay);
|
||||
} else if (priv->previous_hz != hz ||
|
||||
priv->qspi_calibrated_hz != hz ||
|
||||
priv->qspi_calibrated_cs != spi_chip_select(bus)) {
|
||||
/*
|
||||
* Calibration required for different current SCLK speed,
|
||||
* requested SCLK speed or chip select
|
||||
*/
|
||||
err = spi_calibration(bus, hz);
|
||||
if (err)
|
||||
return err;
|
||||
@ -200,6 +212,8 @@ static int cadence_spi_probe(struct udevice *bus)
|
||||
priv->qspi_is_init = 1;
|
||||
}
|
||||
|
||||
plat->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, plat->ref_clk_hz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -259,10 +273,14 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
|
||||
|
||||
switch (mode) {
|
||||
case CQSPI_STIG_READ:
|
||||
err = cadence_qspi_apb_command_read(base, op);
|
||||
err = cadence_qspi_apb_command_read_setup(plat, op);
|
||||
if (!err)
|
||||
err = cadence_qspi_apb_command_read(plat, op);
|
||||
break;
|
||||
case CQSPI_STIG_WRITE:
|
||||
err = cadence_qspi_apb_command_write(base, op);
|
||||
err = cadence_qspi_apb_command_write_setup(plat, op);
|
||||
if (!err)
|
||||
err = cadence_qspi_apb_command_write(plat, op);
|
||||
break;
|
||||
case CQSPI_READ:
|
||||
err = cadence_qspi_apb_read_setup(plat, op);
|
||||
@ -282,6 +300,26 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool cadence_spi_mem_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
bool all_true, all_false;
|
||||
|
||||
all_true = op->cmd.dtr && op->addr.dtr && op->dummy.dtr &&
|
||||
op->data.dtr;
|
||||
all_false = !op->cmd.dtr && !op->addr.dtr && !op->dummy.dtr &&
|
||||
!op->data.dtr;
|
||||
|
||||
/* Mixed DTR modes not supported. */
|
||||
if (!(all_true || all_false))
|
||||
return false;
|
||||
|
||||
if (all_true)
|
||||
return spi_mem_dtr_supports_op(slave, op);
|
||||
else
|
||||
return spi_mem_default_supports_op(slave, op);
|
||||
}
|
||||
|
||||
static int cadence_spi_of_to_plat(struct udevice *bus)
|
||||
{
|
||||
struct cadence_spi_plat *plat = dev_get_plat(bus);
|
||||
@ -320,6 +358,14 @@ static int cadence_spi_of_to_plat(struct udevice *bus)
|
||||
255);
|
||||
plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
|
||||
plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-ns", 20);
|
||||
/*
|
||||
* Read delay should be an unsigned value but we use a signed integer
|
||||
* so that negative values can indicate that the device tree did not
|
||||
* specify any signed values and we need to perform the calibration
|
||||
* sequence to find it out.
|
||||
*/
|
||||
plat->read_delay = ofnode_read_s32_default(subnode, "cdns,read-delay",
|
||||
-1);
|
||||
|
||||
debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
|
||||
__func__, plat->regbase, plat->ahbbase, plat->max_hz,
|
||||
@ -330,6 +376,7 @@ static int cadence_spi_of_to_plat(struct udevice *bus)
|
||||
|
||||
static const struct spi_controller_mem_ops cadence_spi_mem_ops = {
|
||||
.exec_op = cadence_spi_mem_exec_op,
|
||||
.supports_op = cadence_spi_mem_supports_op,
|
||||
};
|
||||
|
||||
static const struct dm_spi_ops cadence_spi_ops = {
|
||||
|
@ -26,6 +26,8 @@ struct cadence_spi_plat {
|
||||
u32 trigger_address;
|
||||
fdt_addr_t ahbsize;
|
||||
bool use_dac_mode;
|
||||
int read_delay;
|
||||
u32 wr_delay;
|
||||
|
||||
/* Flash parameters */
|
||||
u32 page_size;
|
||||
@ -34,6 +36,12 @@ struct cadence_spi_plat {
|
||||
u32 tsd2d_ns;
|
||||
u32 tchsh_ns;
|
||||
u32 tslch_ns;
|
||||
|
||||
/* Transaction protocol parameters. */
|
||||
u8 inst_width;
|
||||
u8 addr_width;
|
||||
u8 data_width;
|
||||
bool dtr;
|
||||
};
|
||||
|
||||
struct cadence_spi_priv {
|
||||
@ -57,9 +65,13 @@ void cadence_qspi_apb_controller_enable(void *reg_base_addr);
|
||||
void cadence_qspi_apb_controller_disable(void *reg_base_addr);
|
||||
void cadence_qspi_apb_dac_mode_enable(void *reg_base);
|
||||
|
||||
int cadence_qspi_apb_command_read(void *reg_base_addr,
|
||||
int cadence_qspi_apb_command_read_setup(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op);
|
||||
int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op);
|
||||
int cadence_qspi_apb_command_write(void *reg_base_addr,
|
||||
int cadence_qspi_apb_command_write_setup(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op);
|
||||
int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op);
|
||||
|
||||
int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
|
||||
|
@ -51,7 +51,7 @@
|
||||
#define CQSPI_STIG_DATA_LEN_MAX 8
|
||||
|
||||
#define CQSPI_DUMMY_CLKS_PER_BYTE 8
|
||||
#define CQSPI_DUMMY_BYTES_MAX 4
|
||||
#define CQSPI_DUMMY_CLKS_MAX 31
|
||||
|
||||
/****************************************************************************
|
||||
* Controller's configuration and status register (offset from QSPI_BASE)
|
||||
@ -65,6 +65,8 @@
|
||||
#define CQSPI_REG_CONFIG_XIP_IMM BIT(18)
|
||||
#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
|
||||
#define CQSPI_REG_CONFIG_BAUD_LSB 19
|
||||
#define CQSPI_REG_CONFIG_DTR_PROTO BIT(24)
|
||||
#define CQSPI_REG_CONFIG_DUAL_OPCODE BIT(30)
|
||||
#define CQSPI_REG_CONFIG_IDLE_LSB 31
|
||||
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
|
||||
#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
|
||||
@ -83,6 +85,7 @@
|
||||
|
||||
#define CQSPI_REG_WR_INSTR 0x08
|
||||
#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0
|
||||
#define CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB 12
|
||||
#define CQSPI_REG_WR_INSTR_TYPE_DATA_LSB 16
|
||||
|
||||
#define CQSPI_REG_DELAY 0x0C
|
||||
@ -120,6 +123,9 @@
|
||||
#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF
|
||||
#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF
|
||||
|
||||
#define CQSPI_REG_WR_COMPLETION_CTRL 0x38
|
||||
#define CQSPI_REG_WR_DISABLE_AUTO_POLL BIT(14)
|
||||
|
||||
#define CQSPI_REG_IRQSTATUS 0x40
|
||||
#define CQSPI_REG_IRQMASK 0x44
|
||||
|
||||
@ -166,6 +172,11 @@
|
||||
#define CQSPI_REG_CMDWRITEDATALOWER 0xA8
|
||||
#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC
|
||||
|
||||
#define CQSPI_REG_OP_EXT_LOWER 0xE0
|
||||
#define CQSPI_REG_OP_EXT_READ_LSB 24
|
||||
#define CQSPI_REG_OP_EXT_WRITE_LSB 16
|
||||
#define CQSPI_REG_OP_EXT_STIG_LSB 0
|
||||
|
||||
#define CQSPI_REG_IS_IDLE(base) \
|
||||
((readl(base + CQSPI_REG_CONFIG) >> \
|
||||
CQSPI_REG_CONFIG_IDLE_LSB) & 0x1)
|
||||
@ -203,6 +214,75 @@ void cadence_qspi_apb_dac_mode_enable(void *reg_base)
|
||||
writel(reg, reg_base + CQSPI_REG_CONFIG);
|
||||
}
|
||||
|
||||
static unsigned int cadence_qspi_calc_dummy(const struct spi_mem_op *op,
|
||||
bool dtr)
|
||||
{
|
||||
unsigned int dummy_clk;
|
||||
|
||||
dummy_clk = op->dummy.nbytes * (8 / op->dummy.buswidth);
|
||||
if (dtr)
|
||||
dummy_clk /= 2;
|
||||
|
||||
return dummy_clk;
|
||||
}
|
||||
|
||||
static u32 cadence_qspi_calc_rdreg(struct cadence_spi_plat *plat)
|
||||
{
|
||||
u32 rdreg = 0;
|
||||
|
||||
rdreg |= plat->inst_width << CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB;
|
||||
rdreg |= plat->addr_width << CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB;
|
||||
rdreg |= plat->data_width << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
|
||||
|
||||
return rdreg;
|
||||
}
|
||||
|
||||
static int cadence_qspi_buswidth_to_inst_type(u8 buswidth)
|
||||
{
|
||||
switch (buswidth) {
|
||||
case 0:
|
||||
case 1:
|
||||
return CQSPI_INST_TYPE_SINGLE;
|
||||
|
||||
case 2:
|
||||
return CQSPI_INST_TYPE_DUAL;
|
||||
|
||||
case 4:
|
||||
return CQSPI_INST_TYPE_QUAD;
|
||||
|
||||
case 8:
|
||||
return CQSPI_INST_TYPE_OCTAL;
|
||||
|
||||
default:
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
static int cadence_qspi_set_protocol(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
|
||||
plat->dtr = op->data.dtr && op->cmd.dtr && op->addr.dtr;
|
||||
|
||||
ret = cadence_qspi_buswidth_to_inst_type(op->cmd.buswidth);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
plat->inst_width = ret;
|
||||
|
||||
ret = cadence_qspi_buswidth_to_inst_type(op->addr.buswidth);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
plat->addr_width = ret;
|
||||
|
||||
ret = cadence_qspi_buswidth_to_inst_type(op->data.buswidth);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
plat->data_width = ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return 1 if idle, otherwise return 0 (busy). */
|
||||
static unsigned int cadence_qspi_wait_idle(void *reg_base)
|
||||
{
|
||||
@ -434,21 +514,109 @@ static int cadence_qspi_apb_exec_flash_cmd(void *reg_base,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For command RDID, RDSR. */
|
||||
int cadence_qspi_apb_command_read(void *reg_base, const struct spi_mem_op *op)
|
||||
static int cadence_qspi_setup_opcode_ext(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op,
|
||||
unsigned int shift)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 ext;
|
||||
|
||||
if (op->cmd.nbytes != 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* Opcode extension is the LSB. */
|
||||
ext = op->cmd.opcode & 0xff;
|
||||
|
||||
reg = readl(plat->regbase + CQSPI_REG_OP_EXT_LOWER);
|
||||
reg &= ~(0xff << shift);
|
||||
reg |= ext << shift;
|
||||
writel(reg, plat->regbase + CQSPI_REG_OP_EXT_LOWER);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cadence_qspi_enable_dtr(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op,
|
||||
unsigned int shift,
|
||||
bool enable)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
|
||||
reg = readl(plat->regbase + CQSPI_REG_CONFIG);
|
||||
|
||||
if (enable) {
|
||||
reg |= CQSPI_REG_CONFIG_DTR_PROTO;
|
||||
reg |= CQSPI_REG_CONFIG_DUAL_OPCODE;
|
||||
|
||||
/* Set up command opcode extension. */
|
||||
ret = cadence_qspi_setup_opcode_ext(plat, op, shift);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
reg &= ~CQSPI_REG_CONFIG_DTR_PROTO;
|
||||
reg &= ~CQSPI_REG_CONFIG_DUAL_OPCODE;
|
||||
}
|
||||
|
||||
writel(reg, plat->regbase + CQSPI_REG_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cadence_qspi_apb_command_read_setup(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
ret = cadence_qspi_set_protocol(plat, op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_STIG_LSB,
|
||||
plat->dtr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = cadence_qspi_calc_rdreg(plat);
|
||||
writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For command RDID, RDSR. */
|
||||
int cadence_qspi_apb_command_read(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
void *reg_base = plat->regbase;
|
||||
unsigned int reg;
|
||||
unsigned int read_len;
|
||||
int status;
|
||||
unsigned int rxlen = op->data.nbytes;
|
||||
void *rxbuf = op->data.buf.in;
|
||||
unsigned int dummy_clk;
|
||||
u8 opcode;
|
||||
|
||||
if (rxlen > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) {
|
||||
printf("QSPI: Invalid input arguments rxlen %u\n", rxlen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg = op->cmd.opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
|
||||
if (plat->dtr)
|
||||
opcode = op->cmd.opcode >> 8;
|
||||
else
|
||||
opcode = op->cmd.opcode;
|
||||
|
||||
reg = opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
|
||||
|
||||
/* Set up dummy cycles. */
|
||||
dummy_clk = cadence_qspi_calc_dummy(op, plat->dtr);
|
||||
if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (dummy_clk)
|
||||
reg |= (dummy_clk & CQSPI_REG_CMDCTRL_DUMMY_MASK)
|
||||
<< CQSPI_REG_CMDCTRL_DUMMY_LSB;
|
||||
|
||||
reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
|
||||
|
||||
@ -475,15 +643,39 @@ int cadence_qspi_apb_command_read(void *reg_base, const struct spi_mem_op *op)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cadence_qspi_apb_command_write_setup(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
int ret;
|
||||
unsigned int reg;
|
||||
|
||||
ret = cadence_qspi_set_protocol(plat, op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_STIG_LSB,
|
||||
plat->dtr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
reg = cadence_qspi_calc_rdreg(plat);
|
||||
writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
|
||||
int cadence_qspi_apb_command_write(void *reg_base, const struct spi_mem_op *op)
|
||||
int cadence_qspi_apb_command_write(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned int reg = 0;
|
||||
unsigned int wr_data;
|
||||
unsigned int wr_len;
|
||||
unsigned int txlen = op->data.nbytes;
|
||||
const void *txbuf = op->data.buf.out;
|
||||
void *reg_base = plat->regbase;
|
||||
u32 addr;
|
||||
u8 opcode;
|
||||
|
||||
/* Reorder address to SPI bus order if only transferring address */
|
||||
if (!txlen) {
|
||||
@ -499,7 +691,12 @@ int cadence_qspi_apb_command_write(void *reg_base, const struct spi_mem_op *op)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg |= op->cmd.opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
|
||||
if (plat->dtr)
|
||||
opcode = op->cmd.opcode >> 8;
|
||||
else
|
||||
opcode = op->cmd.opcode;
|
||||
|
||||
reg |= opcode << CQSPI_REG_CMDCTRL_OPCODE_LSB;
|
||||
|
||||
if (txlen) {
|
||||
/* writing data = yes */
|
||||
@ -533,29 +730,39 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
|
||||
unsigned int rd_reg;
|
||||
unsigned int dummy_clk;
|
||||
unsigned int dummy_bytes = op->dummy.nbytes;
|
||||
int ret;
|
||||
u8 opcode;
|
||||
|
||||
ret = cadence_qspi_set_protocol(plat, op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_READ_LSB,
|
||||
plat->dtr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup the indirect trigger address */
|
||||
writel(plat->trigger_address,
|
||||
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
||||
|
||||
/* Configure the opcode */
|
||||
rd_reg = op->cmd.opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
|
||||
if (plat->dtr)
|
||||
opcode = op->cmd.opcode >> 8;
|
||||
else
|
||||
opcode = op->cmd.opcode;
|
||||
|
||||
if (op->data.buswidth == 8)
|
||||
/* Instruction and address at DQ0, data at DQ0-7. */
|
||||
rd_reg |= CQSPI_INST_TYPE_OCTAL << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
|
||||
else if (op->data.buswidth == 4)
|
||||
/* Instruction and address at DQ0, data at DQ0-3. */
|
||||
rd_reg |= CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
|
||||
rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
|
||||
rd_reg |= cadence_qspi_calc_rdreg(plat);
|
||||
|
||||
writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
|
||||
|
||||
if (dummy_bytes) {
|
||||
if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
|
||||
dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
|
||||
|
||||
/* Convert to clock cycles. */
|
||||
dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
|
||||
dummy_clk = cadence_qspi_calc_dummy(op, plat->dtr);
|
||||
|
||||
if (dummy_clk > CQSPI_DUMMY_CLKS_MAX)
|
||||
return -ENOTSUPP;
|
||||
|
||||
if (dummy_clk)
|
||||
rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
|
||||
@ -682,17 +889,52 @@ int cadence_qspi_apb_write_setup(struct cadence_spi_plat *plat,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
unsigned int reg;
|
||||
int ret;
|
||||
u8 opcode;
|
||||
|
||||
ret = cadence_qspi_set_protocol(plat, op);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cadence_qspi_enable_dtr(plat, op, CQSPI_REG_OP_EXT_WRITE_LSB,
|
||||
plat->dtr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Setup the indirect trigger address */
|
||||
writel(plat->trigger_address,
|
||||
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
||||
|
||||
/* Configure the opcode */
|
||||
reg = op->cmd.opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB;
|
||||
if (plat->dtr)
|
||||
opcode = op->cmd.opcode >> 8;
|
||||
else
|
||||
opcode = op->cmd.opcode;
|
||||
|
||||
reg = opcode << CQSPI_REG_WR_INSTR_OPCODE_LSB;
|
||||
reg |= plat->data_width << CQSPI_REG_WR_INSTR_TYPE_DATA_LSB;
|
||||
reg |= plat->addr_width << CQSPI_REG_WR_INSTR_TYPE_ADDR_LSB;
|
||||
writel(reg, plat->regbase + CQSPI_REG_WR_INSTR);
|
||||
|
||||
reg = cadence_qspi_calc_rdreg(plat);
|
||||
writel(reg, plat->regbase + CQSPI_REG_RD_INSTR);
|
||||
|
||||
writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTWRSTARTADDR);
|
||||
|
||||
if (plat->dtr) {
|
||||
/*
|
||||
* Some flashes like the cypress Semper flash expect a 4-byte
|
||||
* dummy address with the Read SR command in DTR mode, but this
|
||||
* controller does not support sending address with the Read SR
|
||||
* command. So, disable write completion polling on the
|
||||
* controller's side. spi-nor will take care of polling the
|
||||
* status register.
|
||||
*/
|
||||
reg = readl(plat->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
|
||||
reg |= CQSPI_REG_WR_DISABLE_AUTO_POLL;
|
||||
writel(reg, plat->regbase + CQSPI_REG_WR_COMPLETION_CTRL);
|
||||
}
|
||||
|
||||
reg = readl(plat->regbase + CQSPI_REG_SIZE);
|
||||
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
|
||||
reg |= (op->addr.nbytes - 1);
|
||||
@ -730,6 +972,12 @@ cadence_qspi_apb_indirect_write_execute(struct cadence_spi_plat *plat,
|
||||
writel(CQSPI_REG_INDIRECTWR_START,
|
||||
plat->regbase + CQSPI_REG_INDIRECTWR);
|
||||
|
||||
/*
|
||||
* Some delay is required for the above bit to be internally
|
||||
* synchronized by the QSPI module.
|
||||
*/
|
||||
ndelay(plat->wr_delay);
|
||||
|
||||
while (remaining > 0) {
|
||||
write_bytes = remaining > page_size ? page_size : remaining;
|
||||
writesl(plat->ahbbase, bb_txbuf, write_bytes >> 2);
|
||||
@ -781,7 +1029,15 @@ int cadence_qspi_apb_write_execute(struct cadence_spi_plat *plat,
|
||||
const void *buf = op->data.buf.out;
|
||||
size_t len = op->data.nbytes;
|
||||
|
||||
if (plat->use_dac_mode && (to + len < plat->ahbsize)) {
|
||||
/*
|
||||
* Some flashes like the Cypress Semper flash expect a dummy 4-byte
|
||||
* address (all 0s) with the read status register command in DTR mode.
|
||||
* But this controller does not support sending dummy address bytes to
|
||||
* the flash when it is polling the write completion register in DTR
|
||||
* mode. So, we can not use direct mode when in DTR mode for writing
|
||||
* data.
|
||||
*/
|
||||
if (!plat->dtr && plat->use_dac_mode && (to + len < plat->ahbsize)) {
|
||||
memcpy_toio(plat->ahbbase + to, buf, len);
|
||||
if (!cadence_qspi_wait_idle(plat->regbase))
|
||||
return -EIO;
|
||||
|
@ -64,8 +64,7 @@ static int mtk_snfi_adjust_op_size(struct spi_slave *slave,
|
||||
* or the output+input data must not exceed the GPRAM size.
|
||||
*/
|
||||
|
||||
nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
nbytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
|
||||
return 0;
|
||||
|
@ -27,7 +27,7 @@ int spi_mem_exec_op(struct spi_slave *slave,
|
||||
tx_buf = op->data.buf.out;
|
||||
}
|
||||
|
||||
op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
op_buf = calloc(1, op_len);
|
||||
|
||||
ret = spi_claim_bus(slave);
|
||||
@ -89,7 +89,7 @@ int spi_mem_adjust_op_size(struct spi_slave *slave,
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
if (slave->max_write_size && len > slave->max_write_size)
|
||||
return -EINVAL;
|
||||
|
||||
@ -105,3 +105,65 @@ int spi_mem_adjust_op_size(struct spi_slave *slave,
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
case 8:
|
||||
if ((tx && (mode & SPI_TX_OCTAL)) ||
|
||||
(!tx && (mode & SPI_RX_OCTAL)))
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
bool spi_mem_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;
|
||||
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -145,8 +145,8 @@ static int spi_check_buswidth_req(struct spi_slave *slave, u8 buswidth, bool tx)
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
bool spi_mem_default_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
static bool spi_mem_check_buswidth(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (spi_check_buswidth_req(slave, op->cmd.buswidth, true))
|
||||
return false;
|
||||
@ -166,6 +166,38 @@ bool spi_mem_default_supports_op(struct spi_slave *slave,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool spi_mem_dtr_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (op->cmd.buswidth == 8 && op->cmd.nbytes % 2)
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes && op->addr.buswidth == 8 && op->addr.nbytes % 2)
|
||||
return false;
|
||||
|
||||
if (op->dummy.nbytes && op->dummy.buswidth == 8 && op->dummy.nbytes % 2)
|
||||
return false;
|
||||
|
||||
if (op->data.dir != SPI_MEM_NO_DATA &&
|
||||
op->dummy.buswidth == 8 && op->data.nbytes % 2)
|
||||
return false;
|
||||
|
||||
return spi_mem_check_buswidth(slave, op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_dtr_supports_op);
|
||||
|
||||
bool spi_mem_default_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr)
|
||||
return false;
|
||||
|
||||
if (op->cmd.nbytes != 1)
|
||||
return false;
|
||||
|
||||
return spi_mem_check_buswidth(slave, op);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(spi_mem_default_supports_op);
|
||||
|
||||
/**
|
||||
@ -270,8 +302,7 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
|
||||
}
|
||||
|
||||
#ifndef __UBOOT__
|
||||
tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
/*
|
||||
* Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
|
||||
@ -286,7 +317,7 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
|
||||
|
||||
tmpbuf[0] = op->cmd.opcode;
|
||||
xfers[xferpos].tx_buf = tmpbuf;
|
||||
xfers[xferpos].len = sizeof(op->cmd.opcode);
|
||||
xfers[xferpos].len = op->cmd.nbytes;
|
||||
xfers[xferpos].tx_nbits = op->cmd.buswidth;
|
||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||
xferpos++;
|
||||
@ -350,7 +381,7 @@ int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
|
||||
tx_buf = op->data.buf.out;
|
||||
}
|
||||
|
||||
op_len = sizeof(op->cmd.opcode) + op->addr.nbytes + op->dummy.nbytes;
|
||||
op_len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
|
||||
/*
|
||||
* Avoid using malloc() here so that we can use this code in SPL where
|
||||
@ -439,8 +470,7 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
|
||||
if (!ops->mem_ops || !ops->mem_ops->exec_op) {
|
||||
unsigned int len;
|
||||
|
||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||
if (slave->max_write_size && len > slave->max_write_size)
|
||||
return -EINVAL;
|
||||
|
||||
|
547
drivers/spi/spi-mxic.c
Normal file
547
drivers/spi/spi-mxic.c
Normal file
@ -0,0 +1,547 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2021 Macronix International Co., Ltd.
|
||||
*
|
||||
* Authors:
|
||||
* zhengxunli <zhengxunli@mxic.com.tw>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <asm/io.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <spi-mem.h>
|
||||
#include <linux/bug.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#define HC_CFG 0x0
|
||||
#define HC_CFG_IF_CFG(x) ((x) << 27)
|
||||
#define HC_CFG_DUAL_SLAVE BIT(31)
|
||||
#define HC_CFG_INDIVIDUAL BIT(30)
|
||||
#define HC_CFG_NIO(x) (((x) / 4) << 27)
|
||||
#define HC_CFG_TYPE(s, t) ((t) << (23 + ((s) * 2)))
|
||||
#define HC_CFG_TYPE_SPI_NOR 0
|
||||
#define HC_CFG_TYPE_SPI_NAND 1
|
||||
#define HC_CFG_TYPE_SPI_RAM 2
|
||||
#define HC_CFG_TYPE_RAW_NAND 3
|
||||
#define HC_CFG_SLV_ACT(x) ((x) << 21)
|
||||
#define HC_CFG_CLK_PH_EN BIT(20)
|
||||
#define HC_CFG_CLK_POL_INV BIT(19)
|
||||
#define HC_CFG_BIG_ENDIAN BIT(18)
|
||||
#define HC_CFG_DATA_PASS BIT(17)
|
||||
#define HC_CFG_IDLE_SIO_LVL(x) ((x) << 16)
|
||||
#define HC_CFG_MAN_START_EN BIT(3)
|
||||
#define HC_CFG_MAN_START BIT(2)
|
||||
#define HC_CFG_MAN_CS_EN BIT(1)
|
||||
#define HC_CFG_MAN_CS_ASSERT BIT(0)
|
||||
|
||||
#define INT_STS 0x4
|
||||
#define INT_STS_EN 0x8
|
||||
#define INT_SIG_EN 0xc
|
||||
#define INT_STS_ALL GENMASK(31, 0)
|
||||
#define INT_RDY_PIN BIT(26)
|
||||
#define INT_RDY_SR BIT(25)
|
||||
#define INT_LNR_SUSP BIT(24)
|
||||
#define INT_ECC_ERR BIT(17)
|
||||
#define INT_CRC_ERR BIT(16)
|
||||
#define INT_LWR_DIS BIT(12)
|
||||
#define INT_LRD_DIS BIT(11)
|
||||
#define INT_SDMA_INT BIT(10)
|
||||
#define INT_DMA_FINISH BIT(9)
|
||||
#define INT_RX_NOT_FULL BIT(3)
|
||||
#define INT_RX_NOT_EMPTY BIT(2)
|
||||
#define INT_TX_NOT_FULL BIT(1)
|
||||
#define INT_TX_EMPTY BIT(0)
|
||||
|
||||
#define HC_EN 0x10
|
||||
#define HC_EN_BIT BIT(0)
|
||||
|
||||
#define TXD(x) (0x14 + ((x) * 4))
|
||||
#define RXD 0x24
|
||||
|
||||
#define SS_CTRL(s) (0x30 + ((s) * 4))
|
||||
#define LRD_CFG 0x44
|
||||
#define LWR_CFG 0x80
|
||||
#define RWW_CFG 0x70
|
||||
#define OP_READ BIT(23)
|
||||
#define OP_DUMMY_CYC(x) ((x) << 17)
|
||||
#define OP_ADDR_BYTES(x) ((x) << 14)
|
||||
#define OP_CMD_BYTES(x) (((x) - 1) << 13)
|
||||
#define OP_OCTA_CRC_EN BIT(12)
|
||||
#define OP_DQS_EN BIT(11)
|
||||
#define OP_ENHC_EN BIT(10)
|
||||
#define OP_PREAMBLE_EN BIT(9)
|
||||
#define OP_DATA_DDR BIT(8)
|
||||
#define OP_DATA_BUSW(x) ((x) << 6)
|
||||
#define OP_ADDR_DDR BIT(5)
|
||||
#define OP_ADDR_BUSW(x) ((x) << 3)
|
||||
#define OP_CMD_DDR BIT(2)
|
||||
#define OP_CMD_BUSW(x) (x)
|
||||
#define OP_BUSW_1 0
|
||||
#define OP_BUSW_2 1
|
||||
#define OP_BUSW_4 2
|
||||
#define OP_BUSW_8 3
|
||||
|
||||
#define OCTA_CRC 0x38
|
||||
#define OCTA_CRC_IN_EN(s) BIT(3 + ((s) * 16))
|
||||
#define OCTA_CRC_CHUNK(s, x) ((fls((x) / 32)) << (1 + ((s) * 16)))
|
||||
#define OCTA_CRC_OUT_EN(s) BIT(0 + ((s) * 16))
|
||||
|
||||
#define ONFI_DIN_CNT(s) (0x3c + (s))
|
||||
|
||||
#define LRD_CTRL 0x48
|
||||
#define RWW_CTRL 0x74
|
||||
#define LWR_CTRL 0x84
|
||||
#define LMODE_EN BIT(31)
|
||||
#define LMODE_SLV_ACT(x) ((x) << 21)
|
||||
#define LMODE_CMD1(x) ((x) << 8)
|
||||
#define LMODE_CMD0(x) (x)
|
||||
|
||||
#define LRD_ADDR 0x4c
|
||||
#define LWR_ADDR 0x88
|
||||
#define LRD_RANGE 0x50
|
||||
#define LWR_RANGE 0x8c
|
||||
|
||||
#define AXI_SLV_ADDR 0x54
|
||||
|
||||
#define DMAC_RD_CFG 0x58
|
||||
#define DMAC_WR_CFG 0x94
|
||||
#define DMAC_CFG_PERIPH_EN BIT(31)
|
||||
#define DMAC_CFG_ALLFLUSH_EN BIT(30)
|
||||
#define DMAC_CFG_LASTFLUSH_EN BIT(29)
|
||||
#define DMAC_CFG_QE(x) (((x) + 1) << 16)
|
||||
#define DMAC_CFG_BURST_LEN(x) (((x) + 1) << 12)
|
||||
#define DMAC_CFG_BURST_SZ(x) ((x) << 8)
|
||||
#define DMAC_CFG_DIR_READ BIT(1)
|
||||
#define DMAC_CFG_START BIT(0)
|
||||
|
||||
#define DMAC_RD_CNT 0x5c
|
||||
#define DMAC_WR_CNT 0x98
|
||||
|
||||
#define SDMA_ADDR 0x60
|
||||
|
||||
#define DMAM_CFG 0x64
|
||||
#define DMAM_CFG_START BIT(31)
|
||||
#define DMAM_CFG_CONT BIT(30)
|
||||
#define DMAM_CFG_SDMA_GAP(x) (fls((x) / 8192) << 2)
|
||||
#define DMAM_CFG_DIR_READ BIT(1)
|
||||
#define DMAM_CFG_EN BIT(0)
|
||||
|
||||
#define DMAM_CNT 0x68
|
||||
|
||||
#define LNR_TIMER_TH 0x6c
|
||||
|
||||
#define RDM_CFG0 0x78
|
||||
#define RDM_CFG0_POLY(x) (x)
|
||||
|
||||
#define RDM_CFG1 0x7c
|
||||
#define RDM_CFG1_RDM_EN BIT(31)
|
||||
#define RDM_CFG1_SEED(x) (x)
|
||||
|
||||
#define LWR_SUSP_CTRL 0x90
|
||||
#define LWR_SUSP_CTRL_EN BIT(31)
|
||||
|
||||
#define DMAS_CTRL 0x9c
|
||||
#define DMAS_CTRL_EN BIT(31)
|
||||
#define DMAS_CTRL_DIR_READ BIT(30)
|
||||
|
||||
#define DATA_STROB 0xa0
|
||||
#define DATA_STROB_EDO_EN BIT(2)
|
||||
#define DATA_STROB_INV_POL BIT(1)
|
||||
#define DATA_STROB_DELAY_2CYC BIT(0)
|
||||
|
||||
#define IDLY_CODE(x) (0xa4 + ((x) * 4))
|
||||
#define IDLY_CODE_VAL(x, v) ((v) << (((x) % 4) * 8))
|
||||
|
||||
#define GPIO 0xc4
|
||||
#define GPIO_PT(x) BIT(3 + ((x) * 16))
|
||||
#define GPIO_RESET(x) BIT(2 + ((x) * 16))
|
||||
#define GPIO_HOLDB(x) BIT(1 + ((x) * 16))
|
||||
#define GPIO_WPB(x) BIT((x) * 16)
|
||||
|
||||
#define HC_VER 0xd0
|
||||
|
||||
#define HW_TEST(x) (0xe0 + ((x) * 4))
|
||||
|
||||
struct mxic_spi_priv {
|
||||
struct clk *send_clk;
|
||||
struct clk *send_dly_clk;
|
||||
void __iomem *regs;
|
||||
u32 cur_speed_hz;
|
||||
};
|
||||
|
||||
static int mxic_spi_clk_enable(struct mxic_spi_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->send_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_prepare_enable(priv->send_dly_clk);
|
||||
if (ret)
|
||||
goto err_send_dly_clk;
|
||||
|
||||
return ret;
|
||||
|
||||
err_send_dly_clk:
|
||||
clk_disable_unprepare(priv->send_clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mxic_spi_clk_disable(struct mxic_spi_priv *priv)
|
||||
{
|
||||
clk_disable_unprepare(priv->send_clk);
|
||||
clk_disable_unprepare(priv->send_dly_clk);
|
||||
}
|
||||
|
||||
static void mxic_spi_set_input_delay_dqs(struct mxic_spi_priv *priv,
|
||||
u8 idly_code)
|
||||
{
|
||||
writel(IDLY_CODE_VAL(0, idly_code) |
|
||||
IDLY_CODE_VAL(1, idly_code) |
|
||||
IDLY_CODE_VAL(2, idly_code) |
|
||||
IDLY_CODE_VAL(3, idly_code),
|
||||
priv->regs + IDLY_CODE(0));
|
||||
writel(IDLY_CODE_VAL(4, idly_code) |
|
||||
IDLY_CODE_VAL(5, idly_code) |
|
||||
IDLY_CODE_VAL(6, idly_code) |
|
||||
IDLY_CODE_VAL(7, idly_code),
|
||||
priv->regs + IDLY_CODE(1));
|
||||
}
|
||||
|
||||
static int mxic_spi_clk_setup(struct mxic_spi_priv *priv, uint freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = clk_set_rate(priv->send_clk, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_set_rate(priv->send_dly_clk, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* A constant delay range from 0x0 ~ 0x1F for input delay,
|
||||
* the unit is 78 ps, the max input delay is 2.418 ns.
|
||||
*/
|
||||
mxic_spi_set_input_delay_dqs(priv, 0xf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxic_spi_set_speed(struct udevice *bus, uint freq)
|
||||
{
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
if (priv->cur_speed_hz == freq)
|
||||
return 0;
|
||||
|
||||
mxic_spi_clk_disable(priv);
|
||||
ret = mxic_spi_clk_setup(priv, freq);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = mxic_spi_clk_enable(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
priv->cur_speed_hz = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxic_spi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
u32 hc_config = 0;
|
||||
|
||||
if (mode & SPI_CPHA)
|
||||
hc_config |= HC_CFG_CLK_PH_EN;
|
||||
if (mode & SPI_CPOL)
|
||||
hc_config |= HC_CFG_CLK_POL_INV;
|
||||
|
||||
writel(hc_config, priv->regs + HC_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mxic_spi_hw_init(struct mxic_spi_priv *priv)
|
||||
{
|
||||
writel(0, priv->regs + DATA_STROB);
|
||||
writel(INT_STS_ALL, priv->regs + INT_STS_EN);
|
||||
writel(0, priv->regs + HC_EN);
|
||||
writel(0, priv->regs + LRD_CFG);
|
||||
writel(0, priv->regs + LRD_CTRL);
|
||||
writel(HC_CFG_NIO(1) | HC_CFG_TYPE(0, HC_CFG_TYPE_SPI_NOR) |
|
||||
HC_CFG_SLV_ACT(0) | HC_CFG_MAN_CS_EN | HC_CFG_IDLE_SIO_LVL(1),
|
||||
priv->regs + HC_CFG);
|
||||
}
|
||||
|
||||
static int mxic_spi_data_xfer(struct mxic_spi_priv *priv, const void *txbuf,
|
||||
void *rxbuf, unsigned int len)
|
||||
{
|
||||
unsigned int pos = 0;
|
||||
|
||||
while (pos < len) {
|
||||
unsigned int nbytes = len - pos;
|
||||
u32 data = 0xffffffff;
|
||||
u32 sts;
|
||||
int ret;
|
||||
|
||||
if (nbytes > 4)
|
||||
nbytes = 4;
|
||||
|
||||
if (txbuf)
|
||||
memcpy(&data, txbuf + pos, nbytes);
|
||||
|
||||
ret = readl_poll_timeout(priv->regs + INT_STS, sts,
|
||||
sts & INT_TX_EMPTY, 1000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(data, priv->regs + TXD(nbytes % 4));
|
||||
|
||||
if (rxbuf) {
|
||||
ret = readl_poll_timeout(priv->regs + INT_STS, sts,
|
||||
sts & INT_TX_EMPTY,
|
||||
1000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = readl_poll_timeout(priv->regs + INT_STS, sts,
|
||||
sts & INT_RX_NOT_EMPTY,
|
||||
1000000);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data = readl(priv->regs + RXD);
|
||||
data >>= (8 * (4 - nbytes));
|
||||
memcpy(rxbuf + pos, &data, nbytes);
|
||||
WARN_ON(readl(priv->regs + INT_STS) & INT_RX_NOT_EMPTY);
|
||||
} else {
|
||||
readl(priv->regs + RXD);
|
||||
}
|
||||
WARN_ON(readl(priv->regs + INT_STS) & INT_RX_NOT_EMPTY);
|
||||
|
||||
pos += nbytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mxic_spi_mem_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (op->data.buswidth > 8 || op->addr.buswidth > 8 ||
|
||||
op->dummy.buswidth > 8 || op->cmd.buswidth > 8)
|
||||
return false;
|
||||
|
||||
if (op->addr.nbytes > 7)
|
||||
return false;
|
||||
|
||||
return spi_mem_default_supports_op(slave, op);
|
||||
}
|
||||
|
||||
static int mxic_spi_mem_exec_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev);
|
||||
struct udevice *bus = slave->dev->parent;
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
int nio = 1, i, ret;
|
||||
u32 ss_ctrl;
|
||||
u8 addr[8], dummy_bytes = 0;
|
||||
|
||||
if (slave->mode & (SPI_TX_OCTAL | SPI_RX_OCTAL))
|
||||
nio = 8;
|
||||
else if (slave->mode & (SPI_TX_QUAD | SPI_RX_QUAD))
|
||||
nio = 4;
|
||||
else if (slave->mode & (SPI_TX_DUAL | SPI_RX_DUAL))
|
||||
nio = 2;
|
||||
|
||||
writel(HC_CFG_NIO(nio) |
|
||||
HC_CFG_TYPE(slave_plat->cs, HC_CFG_TYPE_SPI_NOR) |
|
||||
HC_CFG_SLV_ACT(slave_plat->cs) | HC_CFG_IDLE_SIO_LVL(1) |
|
||||
HC_CFG_MAN_CS_EN,
|
||||
priv->regs + HC_CFG);
|
||||
writel(HC_EN_BIT, priv->regs + HC_EN);
|
||||
|
||||
ss_ctrl = OP_CMD_BYTES(1) | OP_CMD_BUSW(fls(op->cmd.buswidth) - 1);
|
||||
|
||||
if (op->addr.nbytes)
|
||||
ss_ctrl |= OP_ADDR_BYTES(op->addr.nbytes) |
|
||||
OP_ADDR_BUSW(fls(op->addr.buswidth) - 1);
|
||||
|
||||
/*
|
||||
* Since the SPI MXIC dummy buswidth is aligned with the data buswidth,
|
||||
* the dummy byte needs to be recalculated to send out the correct
|
||||
* dummy cycle.
|
||||
*/
|
||||
if (op->dummy.nbytes) {
|
||||
dummy_bytes = op->dummy.nbytes /
|
||||
op->addr.buswidth *
|
||||
op->data.buswidth;
|
||||
ss_ctrl |= OP_DUMMY_CYC(dummy_bytes);
|
||||
}
|
||||
|
||||
if (op->data.nbytes) {
|
||||
ss_ctrl |= OP_DATA_BUSW(fls(op->data.buswidth) - 1);
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
ss_ctrl |= OP_READ;
|
||||
}
|
||||
|
||||
writel(ss_ctrl, priv->regs + SS_CTRL(slave_plat->cs));
|
||||
|
||||
writel(readl(priv->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
|
||||
priv->regs + HC_CFG);
|
||||
|
||||
ret = mxic_spi_data_xfer(priv, &op->cmd.opcode, NULL, 1);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < op->addr.nbytes; i++)
|
||||
addr[i] = op->addr.val >> (8 * (op->addr.nbytes - i - 1));
|
||||
|
||||
ret = mxic_spi_data_xfer(priv, addr, NULL, op->addr.nbytes);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = mxic_spi_data_xfer(priv, NULL, NULL, dummy_bytes);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = mxic_spi_data_xfer(priv,
|
||||
op->data.dir == SPI_MEM_DATA_OUT ?
|
||||
op->data.buf.out : NULL,
|
||||
op->data.dir == SPI_MEM_DATA_IN ?
|
||||
op->data.buf.in : NULL,
|
||||
op->data.nbytes);
|
||||
|
||||
out:
|
||||
writel(readl(priv->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT,
|
||||
priv->regs + HC_CFG);
|
||||
writel(0, priv->regs + HC_EN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops mxic_spi_mem_ops = {
|
||||
.supports_op = mxic_spi_mem_supports_op,
|
||||
.exec_op = mxic_spi_mem_exec_op,
|
||||
};
|
||||
|
||||
static int mxic_spi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
writel(readl(priv->regs + HC_CFG) | HC_CFG_MAN_CS_EN,
|
||||
priv->regs + HC_CFG);
|
||||
writel(HC_EN_BIT, priv->regs + HC_EN);
|
||||
writel(readl(priv->regs + HC_CFG) | HC_CFG_MAN_CS_ASSERT,
|
||||
priv->regs + HC_CFG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxic_spi_release_bus(struct udevice *dev)
|
||||
{
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
writel(readl(priv->regs + HC_CFG) & ~HC_CFG_MAN_CS_ASSERT,
|
||||
priv->regs + HC_CFG);
|
||||
writel(0, priv->regs + HC_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxic_spi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct udevice *bus = dev_get_parent(dev);
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
struct spi_slave *slave = dev_get_parent_priv(dev);
|
||||
unsigned int busw = OP_BUSW_1;
|
||||
unsigned int len = bitlen / 8;
|
||||
int ret;
|
||||
|
||||
if (dout && din) {
|
||||
if (((slave->mode & SPI_TX_QUAD) &&
|
||||
!(slave->mode & SPI_RX_QUAD)) ||
|
||||
((slave->mode & SPI_TX_DUAL) &&
|
||||
!(slave->mode & SPI_RX_DUAL)))
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
if (din) {
|
||||
if (slave->mode & SPI_TX_QUAD)
|
||||
busw = OP_BUSW_4;
|
||||
else if (slave->mode & SPI_TX_DUAL)
|
||||
busw = OP_BUSW_2;
|
||||
} else if (dout) {
|
||||
if (slave->mode & SPI_RX_QUAD)
|
||||
busw = OP_BUSW_4;
|
||||
else if (slave->mode & SPI_RX_DUAL)
|
||||
busw = OP_BUSW_2;
|
||||
}
|
||||
|
||||
writel(OP_CMD_BYTES(1) | OP_CMD_BUSW(busw) |
|
||||
OP_DATA_BUSW(busw) | (din ? OP_READ : 0),
|
||||
priv->regs + SS_CTRL(0));
|
||||
|
||||
ret = mxic_spi_data_xfer(priv, dout, din, len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxic_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct mxic_spi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->regs = (void *)dev_read_addr(bus);
|
||||
|
||||
priv->send_clk = devm_clk_get(bus, "send_clk");
|
||||
if (IS_ERR(priv->send_clk))
|
||||
return PTR_ERR(priv->send_clk);
|
||||
|
||||
priv->send_dly_clk = devm_clk_get(bus, "send_dly_clk");
|
||||
if (IS_ERR(priv->send_dly_clk))
|
||||
return PTR_ERR(priv->send_dly_clk);
|
||||
|
||||
mxic_spi_hw_init(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops mxic_spi_ops = {
|
||||
.claim_bus = mxic_spi_claim_bus,
|
||||
.release_bus = mxic_spi_release_bus,
|
||||
.xfer = mxic_spi_xfer,
|
||||
.set_speed = mxic_spi_set_speed,
|
||||
.set_mode = mxic_spi_set_mode,
|
||||
.mem_ops = &mxic_spi_mem_ops,
|
||||
};
|
||||
|
||||
static const struct udevice_id mxic_spi_ids[] = {
|
||||
{ .compatible = "mxicy,mx25f0a-spi", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mxic_spi) = {
|
||||
.name = "mxic_spi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = mxic_spi_ids,
|
||||
.ops = &mxic_spi_ops,
|
||||
.priv_auto = sizeof(struct mxic_spi_priv),
|
||||
.probe = mxic_spi_probe,
|
||||
};
|
@ -67,6 +67,8 @@
|
||||
#define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */
|
||||
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
|
||||
#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */
|
||||
#define SPINOR_OP_SRSTEN 0x66 /* Software Reset Enable */
|
||||
#define SPINOR_OP_SRST 0x99 /* Software Reset */
|
||||
|
||||
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
|
||||
@ -124,6 +126,12 @@
|
||||
/* Used for Micron flashes only. */
|
||||
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
|
||||
#define SPINOR_OP_WD_EVCR 0x61 /* Write EVCR register */
|
||||
#define SPINOR_OP_MT_DTR_RD 0xfd /* Fast Read opcode in DTR mode */
|
||||
#define SPINOR_OP_MT_RD_ANY_REG 0x85 /* Read volatile register */
|
||||
#define SPINOR_OP_MT_WR_ANY_REG 0x81 /* Write volatile register */
|
||||
#define SPINOR_REG_MT_CFR0V 0x00 /* For setting octal DTR mode */
|
||||
#define SPINOR_REG_MT_CFR1V 0x01 /* For setting dummy cycles */
|
||||
#define SPINOR_MT_OCT_DTR 0xe7 /* Enable Octal DTR with DQS. */
|
||||
|
||||
/* Status Register bits. */
|
||||
#define SR_WIP BIT(0) /* Write in progress */
|
||||
@ -155,6 +163,19 @@
|
||||
/* Status Register 2 bits. */
|
||||
#define SR2_QUAD_EN_BIT7 BIT(7)
|
||||
|
||||
/* For Cypress flash. */
|
||||
#define SPINOR_OP_RD_ANY_REG 0x65 /* Read any register */
|
||||
#define SPINOR_OP_WR_ANY_REG 0x71 /* Write any register */
|
||||
#define SPINOR_OP_S28_SE_4K 0x21
|
||||
#define SPINOR_REG_CYPRESS_CFR2V 0x00800003
|
||||
#define SPINOR_REG_CYPRESS_CFR2V_MEMLAT_11_24 0xb
|
||||
#define SPINOR_REG_CYPRESS_CFR3V 0x00800004
|
||||
#define SPINOR_REG_CYPRESS_CFR3V_PGSZ BIT(4) /* Page size. */
|
||||
#define SPINOR_REG_CYPRESS_CFR3V_UNISECT BIT(3) /* Uniform sector mode */
|
||||
#define SPINOR_REG_CYPRESS_CFR5V 0x00800006
|
||||
#define SPINOR_REG_CYPRESS_CFR5V_OCT_DTR_EN 0x3
|
||||
#define SPINOR_OP_CYPRESS_RD_FAST 0xee
|
||||
|
||||
/* Supported SPI protocols */
|
||||
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
|
||||
#define SNOR_PROTO_INST_SHIFT 16
|
||||
@ -200,6 +221,7 @@ enum spi_nor_protocol {
|
||||
SNOR_PROTO_1_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2),
|
||||
SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4),
|
||||
SNOR_PROTO_1_8_8_DTR = SNOR_PROTO_DTR(1, 8, 8),
|
||||
SNOR_PROTO_8_8_8_DTR = SNOR_PROTO_DTR(8, 8, 8),
|
||||
};
|
||||
|
||||
static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
|
||||
@ -247,6 +269,174 @@ enum spi_nor_option_flags {
|
||||
SNOR_F_READY_XSR_RDY = BIT(4),
|
||||
SNOR_F_USE_CLSR = BIT(5),
|
||||
SNOR_F_BROKEN_RESET = BIT(6),
|
||||
SNOR_F_SOFT_RESET = BIT(7),
|
||||
};
|
||||
|
||||
struct spi_nor;
|
||||
|
||||
/**
|
||||
* struct spi_nor_hwcaps - Structure for describing the hardware capabilies
|
||||
* supported by the SPI controller (bus master).
|
||||
* @mask: the bitmask listing all the supported hw capabilies
|
||||
*/
|
||||
struct spi_nor_hwcaps {
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
/*
|
||||
*(Fast) Read capabilities.
|
||||
* MUST be ordered by priority: the higher bit position, the higher priority.
|
||||
* As a matter of performances, it is relevant to use Octo SPI protocols first,
|
||||
* then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
|
||||
* (Slow) Read.
|
||||
*/
|
||||
#define SNOR_HWCAPS_READ_MASK GENMASK(15, 0)
|
||||
#define SNOR_HWCAPS_READ BIT(0)
|
||||
#define SNOR_HWCAPS_READ_FAST BIT(1)
|
||||
#define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2)
|
||||
|
||||
#define SNOR_HWCAPS_READ_DUAL GENMASK(6, 3)
|
||||
#define SNOR_HWCAPS_READ_1_1_2 BIT(3)
|
||||
#define SNOR_HWCAPS_READ_1_2_2 BIT(4)
|
||||
#define SNOR_HWCAPS_READ_2_2_2 BIT(5)
|
||||
#define SNOR_HWCAPS_READ_1_2_2_DTR BIT(6)
|
||||
|
||||
#define SNOR_HWCAPS_READ_QUAD GENMASK(10, 7)
|
||||
#define SNOR_HWCAPS_READ_1_1_4 BIT(7)
|
||||
#define SNOR_HWCAPS_READ_1_4_4 BIT(8)
|
||||
#define SNOR_HWCAPS_READ_4_4_4 BIT(9)
|
||||
#define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10)
|
||||
|
||||
#define SNOR_HWCPAS_READ_OCTO GENMASK(15, 11)
|
||||
#define SNOR_HWCAPS_READ_1_1_8 BIT(11)
|
||||
#define SNOR_HWCAPS_READ_1_8_8 BIT(12)
|
||||
#define SNOR_HWCAPS_READ_8_8_8 BIT(13)
|
||||
#define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14)
|
||||
#define SNOR_HWCAPS_READ_8_8_8_DTR BIT(15)
|
||||
|
||||
/*
|
||||
* Page Program capabilities.
|
||||
* MUST be ordered by priority: the higher bit position, the higher priority.
|
||||
* Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
|
||||
* legacy SPI 1-1-1 protocol.
|
||||
* Note that Dual Page Programs are not supported because there is no existing
|
||||
* JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
|
||||
* implements such commands.
|
||||
*/
|
||||
#define SNOR_HWCAPS_PP_MASK GENMASK(23, 16)
|
||||
#define SNOR_HWCAPS_PP BIT(16)
|
||||
|
||||
#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17)
|
||||
#define SNOR_HWCAPS_PP_1_1_4 BIT(17)
|
||||
#define SNOR_HWCAPS_PP_1_4_4 BIT(18)
|
||||
#define SNOR_HWCAPS_PP_4_4_4 BIT(19)
|
||||
|
||||
#define SNOR_HWCAPS_PP_OCTO GENMASK(23, 20)
|
||||
#define SNOR_HWCAPS_PP_1_1_8 BIT(20)
|
||||
#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
|
||||
#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
|
||||
#define SNOR_HWCAPS_PP_8_8_8_DTR BIT(23)
|
||||
|
||||
#define SNOR_HWCAPS_X_X_X (SNOR_HWCAPS_READ_2_2_2 | \
|
||||
SNOR_HWCAPS_READ_4_4_4 | \
|
||||
SNOR_HWCAPS_READ_8_8_8 | \
|
||||
SNOR_HWCAPS_PP_4_4_4 | \
|
||||
SNOR_HWCAPS_PP_8_8_8)
|
||||
|
||||
#define SNOR_HWCAPS_X_X_X_DTR (SNOR_HWCAPS_READ_8_8_8_DTR | \
|
||||
SNOR_HWCAPS_PP_8_8_8_DTR)
|
||||
|
||||
#define SNOR_HWCAPS_DTR (SNOR_HWCAPS_READ_1_1_1_DTR | \
|
||||
SNOR_HWCAPS_READ_1_2_2_DTR | \
|
||||
SNOR_HWCAPS_READ_1_4_4_DTR | \
|
||||
SNOR_HWCAPS_READ_1_8_8_DTR)
|
||||
|
||||
#define SNOR_HWCAPS_ALL (SNOR_HWCAPS_READ_MASK | \
|
||||
SNOR_HWCAPS_PP_MASK)
|
||||
|
||||
struct spi_nor_read_command {
|
||||
u8 num_mode_clocks;
|
||||
u8 num_wait_states;
|
||||
u8 opcode;
|
||||
enum spi_nor_protocol proto;
|
||||
};
|
||||
|
||||
struct spi_nor_pp_command {
|
||||
u8 opcode;
|
||||
enum spi_nor_protocol proto;
|
||||
};
|
||||
|
||||
enum spi_nor_read_command_index {
|
||||
SNOR_CMD_READ,
|
||||
SNOR_CMD_READ_FAST,
|
||||
SNOR_CMD_READ_1_1_1_DTR,
|
||||
|
||||
/* Dual SPI */
|
||||
SNOR_CMD_READ_1_1_2,
|
||||
SNOR_CMD_READ_1_2_2,
|
||||
SNOR_CMD_READ_2_2_2,
|
||||
SNOR_CMD_READ_1_2_2_DTR,
|
||||
|
||||
/* Quad SPI */
|
||||
SNOR_CMD_READ_1_1_4,
|
||||
SNOR_CMD_READ_1_4_4,
|
||||
SNOR_CMD_READ_4_4_4,
|
||||
SNOR_CMD_READ_1_4_4_DTR,
|
||||
|
||||
/* Octo SPI */
|
||||
SNOR_CMD_READ_1_1_8,
|
||||
SNOR_CMD_READ_1_8_8,
|
||||
SNOR_CMD_READ_8_8_8,
|
||||
SNOR_CMD_READ_1_8_8_DTR,
|
||||
SNOR_CMD_READ_8_8_8_DTR,
|
||||
|
||||
SNOR_CMD_READ_MAX
|
||||
};
|
||||
|
||||
enum spi_nor_pp_command_index {
|
||||
SNOR_CMD_PP,
|
||||
|
||||
/* Quad SPI */
|
||||
SNOR_CMD_PP_1_1_4,
|
||||
SNOR_CMD_PP_1_4_4,
|
||||
SNOR_CMD_PP_4_4_4,
|
||||
|
||||
/* Octo SPI */
|
||||
SNOR_CMD_PP_1_1_8,
|
||||
SNOR_CMD_PP_1_8_8,
|
||||
SNOR_CMD_PP_8_8_8,
|
||||
SNOR_CMD_PP_8_8_8_DTR,
|
||||
|
||||
SNOR_CMD_PP_MAX
|
||||
};
|
||||
|
||||
struct spi_nor_flash_parameter {
|
||||
u64 size;
|
||||
u32 page_size;
|
||||
u8 rdsr_dummy;
|
||||
u8 rdsr_addr_nbytes;
|
||||
|
||||
struct spi_nor_hwcaps hwcaps;
|
||||
struct spi_nor_read_command reads[SNOR_CMD_READ_MAX];
|
||||
struct spi_nor_pp_command page_programs[SNOR_CMD_PP_MAX];
|
||||
|
||||
int (*quad_enable)(struct spi_nor *nor);
|
||||
};
|
||||
|
||||
/**
|
||||
* enum spi_nor_cmd_ext - describes the command opcode extension in DTR mode
|
||||
* @SPI_MEM_NOR_NONE: no extension. This is the default, and is used in Legacy
|
||||
* SPI mode
|
||||
* @SPI_MEM_NOR_REPEAT: the extension is same as the opcode
|
||||
* @SPI_MEM_NOR_INVERT: the extension is the bitwise inverse of the opcode
|
||||
* @SPI_MEM_NOR_HEX: the extension is any hex value. The command and opcode
|
||||
* combine to form a 16-bit opcode.
|
||||
*/
|
||||
enum spi_nor_cmd_ext {
|
||||
SPI_NOR_EXT_NONE = 0,
|
||||
SPI_NOR_EXT_REPEAT,
|
||||
SPI_NOR_EXT_INVERT,
|
||||
SPI_NOR_EXT_HEX,
|
||||
};
|
||||
|
||||
/**
|
||||
@ -279,6 +469,9 @@ struct spi_flash {
|
||||
* @read_opcode: the read opcode
|
||||
* @read_dummy: the dummy needed by the read operation
|
||||
* @program_opcode: the program opcode
|
||||
* @rdsr_dummy dummy cycles needed for Read Status Register command.
|
||||
* @rdsr_addr_nbytes: dummy address bytes needed for Read Status Register
|
||||
* command.
|
||||
* @bank_read_cmd: Bank read cmd
|
||||
* @bank_write_cmd: Bank write cmd
|
||||
* @bank_curr: Current flash bank
|
||||
@ -288,6 +481,8 @@ struct spi_flash {
|
||||
* @write_proto: the SPI protocol for write operations
|
||||
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations
|
||||
* @cmd_buf: used by the write_reg
|
||||
* @cmd_ext_type: the command opcode extension for DTR mode.
|
||||
* @fixups: flash-specific fixup hooks.
|
||||
* @prepare: [OPTIONAL] do some preparations for the
|
||||
* read/write/erase/lock/unlock operations
|
||||
* @unprepare: [OPTIONAL] do some post work after the
|
||||
@ -304,6 +499,7 @@ struct spi_flash {
|
||||
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
|
||||
* completely locked
|
||||
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
|
||||
* @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
|
||||
* @priv: the private data
|
||||
*/
|
||||
struct spi_nor {
|
||||
@ -318,6 +514,8 @@ struct spi_nor {
|
||||
u8 read_opcode;
|
||||
u8 read_dummy;
|
||||
u8 program_opcode;
|
||||
u8 rdsr_dummy;
|
||||
u8 rdsr_addr_nbytes;
|
||||
#ifdef CONFIG_SPI_FLASH_BAR
|
||||
u8 bank_read_cmd;
|
||||
u8 bank_write_cmd;
|
||||
@ -329,7 +527,11 @@ struct spi_nor {
|
||||
bool sst_write_second;
|
||||
u32 flags;
|
||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||
enum spi_nor_cmd_ext cmd_ext_type;
|
||||
struct spi_nor_fixups *fixups;
|
||||
|
||||
int (*setup)(struct spi_nor *nor, const struct flash_info *info,
|
||||
const struct spi_nor_flash_parameter *params);
|
||||
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
void (*unprepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
@ -345,6 +547,7 @@ struct spi_nor {
|
||||
int (*flash_unlock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
int (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||
int (*quad_enable)(struct spi_nor *nor);
|
||||
int (*octal_dtr_enable)(struct spi_nor *nor);
|
||||
|
||||
void *priv;
|
||||
/* Compatibility for spi_flash, remove once sf layer is merged with mtd */
|
||||
@ -368,67 +571,6 @@ device_node *spi_nor_get_flash_node(struct spi_nor *nor)
|
||||
}
|
||||
#endif /* __UBOOT__ */
|
||||
|
||||
/**
|
||||
* struct spi_nor_hwcaps - Structure for describing the hardware capabilies
|
||||
* supported by the SPI controller (bus master).
|
||||
* @mask: the bitmask listing all the supported hw capabilies
|
||||
*/
|
||||
struct spi_nor_hwcaps {
|
||||
u32 mask;
|
||||
};
|
||||
|
||||
/*
|
||||
*(Fast) Read capabilities.
|
||||
* MUST be ordered by priority: the higher bit position, the higher priority.
|
||||
* As a matter of performances, it is relevant to use Octo SPI protocols first,
|
||||
* then Quad SPI protocols before Dual SPI protocols, Fast Read and lastly
|
||||
* (Slow) Read.
|
||||
*/
|
||||
#define SNOR_HWCAPS_READ_MASK GENMASK(14, 0)
|
||||
#define SNOR_HWCAPS_READ BIT(0)
|
||||
#define SNOR_HWCAPS_READ_FAST BIT(1)
|
||||
#define SNOR_HWCAPS_READ_1_1_1_DTR BIT(2)
|
||||
|
||||
#define SNOR_HWCAPS_READ_DUAL GENMASK(6, 3)
|
||||
#define SNOR_HWCAPS_READ_1_1_2 BIT(3)
|
||||
#define SNOR_HWCAPS_READ_1_2_2 BIT(4)
|
||||
#define SNOR_HWCAPS_READ_2_2_2 BIT(5)
|
||||
#define SNOR_HWCAPS_READ_1_2_2_DTR BIT(6)
|
||||
|
||||
#define SNOR_HWCAPS_READ_QUAD GENMASK(10, 7)
|
||||
#define SNOR_HWCAPS_READ_1_1_4 BIT(7)
|
||||
#define SNOR_HWCAPS_READ_1_4_4 BIT(8)
|
||||
#define SNOR_HWCAPS_READ_4_4_4 BIT(9)
|
||||
#define SNOR_HWCAPS_READ_1_4_4_DTR BIT(10)
|
||||
|
||||
#define SNOR_HWCPAS_READ_OCTO GENMASK(14, 11)
|
||||
#define SNOR_HWCAPS_READ_1_1_8 BIT(11)
|
||||
#define SNOR_HWCAPS_READ_1_8_8 BIT(12)
|
||||
#define SNOR_HWCAPS_READ_8_8_8 BIT(13)
|
||||
#define SNOR_HWCAPS_READ_1_8_8_DTR BIT(14)
|
||||
|
||||
/*
|
||||
* Page Program capabilities.
|
||||
* MUST be ordered by priority: the higher bit position, the higher priority.
|
||||
* Like (Fast) Read capabilities, Octo/Quad SPI protocols are preferred to the
|
||||
* legacy SPI 1-1-1 protocol.
|
||||
* Note that Dual Page Programs are not supported because there is no existing
|
||||
* JEDEC/SFDP standard to define them. Also at this moment no SPI flash memory
|
||||
* implements such commands.
|
||||
*/
|
||||
#define SNOR_HWCAPS_PP_MASK GENMASK(22, 16)
|
||||
#define SNOR_HWCAPS_PP BIT(16)
|
||||
|
||||
#define SNOR_HWCAPS_PP_QUAD GENMASK(19, 17)
|
||||
#define SNOR_HWCAPS_PP_1_1_4 BIT(17)
|
||||
#define SNOR_HWCAPS_PP_1_4_4 BIT(18)
|
||||
#define SNOR_HWCAPS_PP_4_4_4 BIT(19)
|
||||
|
||||
#define SNOR_HWCAPS_PP_OCTO GENMASK(22, 20)
|
||||
#define SNOR_HWCAPS_PP_1_1_8 BIT(20)
|
||||
#define SNOR_HWCAPS_PP_1_8_8 BIT(21)
|
||||
#define SNOR_HWCAPS_PP_8_8_8 BIT(22)
|
||||
|
||||
/**
|
||||
* spi_nor_scan() - scan the SPI NOR
|
||||
* @nor: the spi_nor structure
|
||||
@ -441,4 +583,19 @@ struct spi_nor_hwcaps {
|
||||
*/
|
||||
int spi_nor_scan(struct spi_nor *nor);
|
||||
|
||||
#if CONFIG_IS_ENABLED(SPI_FLASH_TINY)
|
||||
static inline int spi_nor_remove(struct spi_nor *nor)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
/**
|
||||
* spi_nor_remove() - perform cleanup before booting to the next stage
|
||||
* @nor: the spi_nor structure
|
||||
*
|
||||
* Return: 0 for success, -errno for failure.
|
||||
*/
|
||||
int spi_nor_remove(struct spi_nor *nor);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -17,6 +17,7 @@ struct udevice;
|
||||
{ \
|
||||
.buswidth = __buswidth, \
|
||||
.opcode = __opcode, \
|
||||
.nbytes = 1, \
|
||||
}
|
||||
|
||||
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
|
||||
@ -69,8 +70,11 @@ enum spi_mem_data_dir {
|
||||
|
||||
/**
|
||||
* struct spi_mem_op - describes a SPI memory operation
|
||||
* @cmd.nbytes: number of opcode bytes (only 1 or 2 are valid). The opcode is
|
||||
* sent MSB-first.
|
||||
* @cmd.buswidth: number of IO lines used to transmit the command
|
||||
* @cmd.opcode: operation opcode
|
||||
* @cmd.dtr: whether the command opcode should be sent in DTR mode or not
|
||||
* @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
|
||||
@ -78,33 +82,41 @@ enum spi_mem_data_dir {
|
||||
* 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.
|
||||
* @addr.dtr: whether the address should be sent in DTR mode or not
|
||||
* @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
|
||||
* @dummy.dtr: whether the dummy bytes should be sent in DTR mode or not
|
||||
* @data.buswidth: number of IO lanes used to send/receive the data
|
||||
* @data.dtr: whether the data should be sent in DTR mode or not
|
||||
* @data.dir: direction of the transfer
|
||||
* @data.buf.in: input buffer
|
||||
* @data.buf.out: output buffer
|
||||
*/
|
||||
struct spi_mem_op {
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 opcode;
|
||||
u8 dtr : 1;
|
||||
u16 opcode;
|
||||
} cmd;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
u64 val;
|
||||
} addr;
|
||||
|
||||
struct {
|
||||
u8 nbytes;
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
} dummy;
|
||||
|
||||
struct {
|
||||
u8 buswidth;
|
||||
u8 dtr : 1;
|
||||
enum spi_mem_data_dir dir;
|
||||
unsigned int nbytes;
|
||||
/* buf.{in,out} must be DMA-able. */
|
||||
@ -237,6 +249,11 @@ spi_controller_dma_unmap_mem_op_data(struct spi_controller *ctlr,
|
||||
int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op);
|
||||
|
||||
bool spi_mem_supports_op(struct spi_slave *slave, const struct spi_mem_op *op);
|
||||
bool spi_mem_dtr_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op);
|
||||
|
||||
bool spi_mem_default_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op);
|
||||
|
||||
int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user