mirror of
https://github.com/smaeul/u-boot.git
synced 2025-11-27 06:14:41 +00:00
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)
|
SPI NOR flashes using Serial Flash Discoverable Parameters (SFDP)
|
||||||
tables as per JESD216 standard.
|
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
|
config SPI_FLASH_BAR
|
||||||
bool "SPI flash Bank/Extended address register support"
|
bool "SPI flash Bank/Extended address register support"
|
||||||
help
|
help
|
||||||
@ -141,11 +167,27 @@ config SPI_FLASH_SPANSION
|
|||||||
help
|
help
|
||||||
Add support for various Spansion SPI flash chips (S25FLxxx)
|
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
|
config SPI_FLASH_STMICRO
|
||||||
bool "STMicro SPI flash support"
|
bool "STMicro SPI flash support"
|
||||||
help
|
help
|
||||||
Add support for various STMicro SPI flash chips (M25Pxxx and N25Qxxx)
|
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
|
config SPI_FLASH_SST
|
||||||
bool "SST SPI flash support"
|
bool "SST SPI flash support"
|
||||||
help
|
help
|
||||||
|
|||||||
@ -68,6 +68,7 @@ struct flash_info {
|
|||||||
#define USE_CLSR BIT(14) /* use CLSR command */
|
#define USE_CLSR BIT(14) /* use CLSR command */
|
||||||
#define SPI_NOR_HAS_SST26LOCK BIT(15) /* Flash supports lock/unlock via BPR */
|
#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_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[];
|
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)
|
static int spi_flash_std_remove(struct udevice *dev)
|
||||||
{
|
{
|
||||||
struct spi_flash *flash = dev_get_uclass_priv(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))
|
if (CONFIG_IS_ENABLED(SPI_FLASH_MTD))
|
||||||
spi_flash_mtd_unregister(flash);
|
spi_flash_mtd_unregister(flash);
|
||||||
@ -178,6 +183,7 @@ U_BOOT_DRIVER(jedec_spi_nor) = {
|
|||||||
.remove = spi_flash_std_remove,
|
.remove = spi_flash_std_remove,
|
||||||
.priv_auto = sizeof(struct spi_nor),
|
.priv_auto = sizeof(struct spi_nor),
|
||||||
.ops = &spi_flash_std_ops,
|
.ops = &spi_flash_std_ops,
|
||||||
|
.flags = DM_FLAG_OS_PREPARE,
|
||||||
};
|
};
|
||||||
|
|
||||||
DM_DRIVER_ALIAS(jedec_spi_nor, spansion_m25p16)
|
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("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("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("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) },
|
{ INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_OCTAL_READ | SPI_NOR_4B_OPCODES) },
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
|
#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("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("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) },
|
{ 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
|
#endif
|
||||||
#ifdef CONFIG_SPI_FLASH_SST /* SST */
|
#ifdef CONFIG_SPI_FLASH_SST /* SST */
|
||||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
/* 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 */
|
#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
|
static void
|
||||||
spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
||||||
u8 num_mode_clocks,
|
u8 num_mode_clocks,
|
||||||
|
|||||||
@ -255,6 +255,13 @@ config MXS_SPI
|
|||||||
Enable the MXS SPI controller driver. This driver can be used
|
Enable the MXS SPI controller driver. This driver can be used
|
||||||
on the i.MX23 and i.MX28 SoCs.
|
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
|
config NXP_FSPI
|
||||||
bool "NXP FlexSPI driver"
|
bool "NXP FlexSPI driver"
|
||||||
depends on SPI_MEM
|
depends on SPI_MEM
|
||||||
|
|||||||
@ -51,6 +51,7 @@ obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
|
|||||||
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
|
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
|
||||||
obj-$(CONFIG_PL022_SPI) += pl022_spi.o
|
obj-$(CONFIG_PL022_SPI) += pl022_spi.o
|
||||||
obj-$(CONFIG_SPI_QUP) += spi-qup.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_RENESAS_RPC_SPI) += renesas_rpc_spi.o
|
||||||
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
|
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
|
||||||
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
|
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
|
||||||
|
|||||||
@ -20,6 +20,8 @@
|
|||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include "cadence_qspi.h"
|
#include "cadence_qspi.h"
|
||||||
|
|
||||||
|
#define NSEC_PER_SEC 1000000000L
|
||||||
|
|
||||||
#define CQSPI_STIG_READ 0
|
#define CQSPI_STIG_READ 0
|
||||||
#define CQSPI_STIG_WRITE 1
|
#define CQSPI_STIG_WRITE 1
|
||||||
#define CQSPI_READ 2
|
#define CQSPI_READ 2
|
||||||
@ -41,20 +43,22 @@ static int cadence_spi_write_speed(struct udevice *bus, uint hz)
|
|||||||
return 0;
|
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),
|
struct spi_mem_op op = SPI_MEM_OP(SPI_MEM_OP_CMD(0x9F, 1),
|
||||||
SPI_MEM_OP_NO_ADDR,
|
SPI_MEM_OP_NO_ADDR,
|
||||||
SPI_MEM_OP_NO_DUMMY,
|
SPI_MEM_OP_NO_DUMMY,
|
||||||
SPI_MEM_OP_DATA_IN(len, idcode, 1));
|
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 */
|
/* Calibration sequence to determine the read data capture delay register */
|
||||||
static int spi_calibration(struct udevice *bus, uint hz)
|
static int spi_calibration(struct udevice *bus, uint hz)
|
||||||
{
|
{
|
||||||
struct cadence_spi_priv *priv = dev_get_priv(bus);
|
struct cadence_spi_priv *priv = dev_get_priv(bus);
|
||||||
|
struct cadence_spi_plat *plat = dev_get_plat(bus);
|
||||||
void *base = priv->regbase;
|
void *base = priv->regbase;
|
||||||
unsigned int idcode = 0, temp = 0;
|
unsigned int idcode = 0, temp = 0;
|
||||||
int err = 0, i, range_lo = -1, range_hi = -1;
|
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);
|
cadence_qspi_apb_controller_enable(base);
|
||||||
|
|
||||||
/* read the ID which will be our golden value */
|
/* 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) {
|
if (err) {
|
||||||
puts("SF: Calibration failed (read)\n");
|
puts("SF: Calibration failed (read)\n");
|
||||||
return err;
|
return err;
|
||||||
@ -88,7 +92,7 @@ static int spi_calibration(struct udevice *bus, uint hz)
|
|||||||
cadence_qspi_apb_controller_enable(base);
|
cadence_qspi_apb_controller_enable(base);
|
||||||
|
|
||||||
/* issue a RDID to get the ID value */
|
/* 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) {
|
if (err) {
|
||||||
puts("SF: Calibration failed (read)\n");
|
puts("SF: Calibration failed (read)\n");
|
||||||
return err;
|
return err;
|
||||||
@ -141,12 +145,20 @@ static int cadence_spi_set_speed(struct udevice *bus, uint hz)
|
|||||||
cadence_qspi_apb_controller_disable(priv->regbase);
|
cadence_qspi_apb_controller_disable(priv->regbase);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calibration required for different current SCLK speed, requested
|
* If the device tree already provides a read delay value, use that
|
||||||
* SCLK speed or chip select
|
* instead of calibrating.
|
||||||
*/
|
*/
|
||||||
if (priv->previous_hz != hz ||
|
if (plat->read_delay >= 0) {
|
||||||
priv->qspi_calibrated_hz != hz ||
|
cadence_spi_write_speed(bus, hz);
|
||||||
priv->qspi_calibrated_cs != spi_chip_select(bus)) {
|
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);
|
err = spi_calibration(bus, hz);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
@ -200,6 +212,8 @@ static int cadence_spi_probe(struct udevice *bus)
|
|||||||
priv->qspi_is_init = 1;
|
priv->qspi_is_init = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
plat->wr_delay = 50 * DIV_ROUND_UP(NSEC_PER_SEC, plat->ref_clk_hz);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,10 +273,14 @@ static int cadence_spi_mem_exec_op(struct spi_slave *spi,
|
|||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CQSPI_STIG_READ:
|
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;
|
break;
|
||||||
case CQSPI_STIG_WRITE:
|
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;
|
break;
|
||||||
case CQSPI_READ:
|
case CQSPI_READ:
|
||||||
err = cadence_qspi_apb_read_setup(plat, op);
|
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;
|
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)
|
static int cadence_spi_of_to_plat(struct udevice *bus)
|
||||||
{
|
{
|
||||||
struct cadence_spi_plat *plat = dev_get_plat(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);
|
255);
|
||||||
plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
|
plat->tchsh_ns = ofnode_read_u32_default(subnode, "cdns,tchsh-ns", 20);
|
||||||
plat->tslch_ns = ofnode_read_u32_default(subnode, "cdns,tslch-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",
|
debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
|
||||||
__func__, plat->regbase, plat->ahbbase, plat->max_hz,
|
__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 = {
|
static const struct spi_controller_mem_ops cadence_spi_mem_ops = {
|
||||||
.exec_op = cadence_spi_mem_exec_op,
|
.exec_op = cadence_spi_mem_exec_op,
|
||||||
|
.supports_op = cadence_spi_mem_supports_op,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct dm_spi_ops cadence_spi_ops = {
|
static const struct dm_spi_ops cadence_spi_ops = {
|
||||||
|
|||||||
@ -26,6 +26,8 @@ struct cadence_spi_plat {
|
|||||||
u32 trigger_address;
|
u32 trigger_address;
|
||||||
fdt_addr_t ahbsize;
|
fdt_addr_t ahbsize;
|
||||||
bool use_dac_mode;
|
bool use_dac_mode;
|
||||||
|
int read_delay;
|
||||||
|
u32 wr_delay;
|
||||||
|
|
||||||
/* Flash parameters */
|
/* Flash parameters */
|
||||||
u32 page_size;
|
u32 page_size;
|
||||||
@ -34,6 +36,12 @@ struct cadence_spi_plat {
|
|||||||
u32 tsd2d_ns;
|
u32 tsd2d_ns;
|
||||||
u32 tchsh_ns;
|
u32 tchsh_ns;
|
||||||
u32 tslch_ns;
|
u32 tslch_ns;
|
||||||
|
|
||||||
|
/* Transaction protocol parameters. */
|
||||||
|
u8 inst_width;
|
||||||
|
u8 addr_width;
|
||||||
|
u8 data_width;
|
||||||
|
bool dtr;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cadence_spi_priv {
|
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_controller_disable(void *reg_base_addr);
|
||||||
void cadence_qspi_apb_dac_mode_enable(void *reg_base);
|
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);
|
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);
|
const struct spi_mem_op *op);
|
||||||
|
|
||||||
int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
|
int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
|
||||||
|
|||||||
@ -51,7 +51,7 @@
|
|||||||
#define CQSPI_STIG_DATA_LEN_MAX 8
|
#define CQSPI_STIG_DATA_LEN_MAX 8
|
||||||
|
|
||||||
#define CQSPI_DUMMY_CLKS_PER_BYTE 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)
|
* 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_XIP_IMM BIT(18)
|
||||||
#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
|
#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
|
||||||
#define CQSPI_REG_CONFIG_BAUD_LSB 19
|
#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_IDLE_LSB 31
|
||||||
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
|
#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
|
||||||
#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
|
#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
|
||||||
@ -83,6 +85,7 @@
|
|||||||
|
|
||||||
#define CQSPI_REG_WR_INSTR 0x08
|
#define CQSPI_REG_WR_INSTR 0x08
|
||||||
#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0
|
#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_WR_INSTR_TYPE_DATA_LSB 16
|
||||||
|
|
||||||
#define CQSPI_REG_DELAY 0x0C
|
#define CQSPI_REG_DELAY 0x0C
|
||||||
@ -120,6 +123,9 @@
|
|||||||
#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF
|
#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF
|
||||||
#define CQSPI_REG_SDRAMLEVEL_WR_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_IRQSTATUS 0x40
|
||||||
#define CQSPI_REG_IRQMASK 0x44
|
#define CQSPI_REG_IRQMASK 0x44
|
||||||
|
|
||||||
@ -166,6 +172,11 @@
|
|||||||
#define CQSPI_REG_CMDWRITEDATALOWER 0xA8
|
#define CQSPI_REG_CMDWRITEDATALOWER 0xA8
|
||||||
#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC
|
#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) \
|
#define CQSPI_REG_IS_IDLE(base) \
|
||||||
((readl(base + CQSPI_REG_CONFIG) >> \
|
((readl(base + CQSPI_REG_CONFIG) >> \
|
||||||
CQSPI_REG_CONFIG_IDLE_LSB) & 0x1)
|
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);
|
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). */
|
/* Return 1 if idle, otherwise return 0 (busy). */
|
||||||
static unsigned int cadence_qspi_wait_idle(void *reg_base)
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For command RDID, RDSR. */
|
static int cadence_qspi_setup_opcode_ext(struct cadence_spi_plat *plat,
|
||||||
int cadence_qspi_apb_command_read(void *reg_base, const struct spi_mem_op *op)
|
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 reg;
|
||||||
unsigned int read_len;
|
unsigned int read_len;
|
||||||
int status;
|
int status;
|
||||||
unsigned int rxlen = op->data.nbytes;
|
unsigned int rxlen = op->data.nbytes;
|
||||||
void *rxbuf = op->data.buf.in;
|
void *rxbuf = op->data.buf.in;
|
||||||
|
unsigned int dummy_clk;
|
||||||
|
u8 opcode;
|
||||||
|
|
||||||
if (rxlen > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) {
|
if (rxlen > CQSPI_STIG_DATA_LEN_MAX || !rxbuf) {
|
||||||
printf("QSPI: Invalid input arguments rxlen %u\n", rxlen);
|
printf("QSPI: Invalid input arguments rxlen %u\n", rxlen);
|
||||||
return -EINVAL;
|
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);
|
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;
|
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. */
|
/* 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 reg = 0;
|
||||||
unsigned int wr_data;
|
unsigned int wr_data;
|
||||||
unsigned int wr_len;
|
unsigned int wr_len;
|
||||||
unsigned int txlen = op->data.nbytes;
|
unsigned int txlen = op->data.nbytes;
|
||||||
const void *txbuf = op->data.buf.out;
|
const void *txbuf = op->data.buf.out;
|
||||||
|
void *reg_base = plat->regbase;
|
||||||
u32 addr;
|
u32 addr;
|
||||||
|
u8 opcode;
|
||||||
|
|
||||||
/* Reorder address to SPI bus order if only transferring address */
|
/* Reorder address to SPI bus order if only transferring address */
|
||||||
if (!txlen) {
|
if (!txlen) {
|
||||||
@ -499,7 +691,12 @@ int cadence_qspi_apb_command_write(void *reg_base, const struct spi_mem_op *op)
|
|||||||
return -EINVAL;
|
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) {
|
if (txlen) {
|
||||||
/* writing data = yes */
|
/* writing data = yes */
|
||||||
@ -533,29 +730,39 @@ int cadence_qspi_apb_read_setup(struct cadence_spi_plat *plat,
|
|||||||
unsigned int rd_reg;
|
unsigned int rd_reg;
|
||||||
unsigned int dummy_clk;
|
unsigned int dummy_clk;
|
||||||
unsigned int dummy_bytes = op->dummy.nbytes;
|
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 */
|
/* Setup the indirect trigger address */
|
||||||
writel(plat->trigger_address,
|
writel(plat->trigger_address,
|
||||||
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
||||||
|
|
||||||
/* Configure the opcode */
|
/* 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)
|
rd_reg = opcode << CQSPI_REG_RD_INSTR_OPCODE_LSB;
|
||||||
/* Instruction and address at DQ0, data at DQ0-7. */
|
rd_reg |= cadence_qspi_calc_rdreg(plat);
|
||||||
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;
|
|
||||||
|
|
||||||
writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
|
writel(op->addr.val, plat->regbase + CQSPI_REG_INDIRECTRDSTARTADDR);
|
||||||
|
|
||||||
if (dummy_bytes) {
|
if (dummy_bytes) {
|
||||||
if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
|
|
||||||
dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
|
|
||||||
|
|
||||||
/* Convert to clock cycles. */
|
/* 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)
|
if (dummy_clk)
|
||||||
rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
|
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)
|
const struct spi_mem_op *op)
|
||||||
{
|
{
|
||||||
unsigned int reg;
|
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 */
|
/* Setup the indirect trigger address */
|
||||||
writel(plat->trigger_address,
|
writel(plat->trigger_address,
|
||||||
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
|
||||||
|
|
||||||
/* Configure the opcode */
|
/* 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);
|
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);
|
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 = readl(plat->regbase + CQSPI_REG_SIZE);
|
||||||
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
|
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
|
||||||
reg |= (op->addr.nbytes - 1);
|
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,
|
writel(CQSPI_REG_INDIRECTWR_START,
|
||||||
plat->regbase + CQSPI_REG_INDIRECTWR);
|
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) {
|
while (remaining > 0) {
|
||||||
write_bytes = remaining > page_size ? page_size : remaining;
|
write_bytes = remaining > page_size ? page_size : remaining;
|
||||||
writesl(plat->ahbbase, bb_txbuf, write_bytes >> 2);
|
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;
|
const void *buf = op->data.buf.out;
|
||||||
size_t len = op->data.nbytes;
|
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);
|
memcpy_toio(plat->ahbbase + to, buf, len);
|
||||||
if (!cadence_qspi_wait_idle(plat->regbase))
|
if (!cadence_qspi_wait_idle(plat->regbase))
|
||||||
return -EIO;
|
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.
|
* or the output+input data must not exceed the GPRAM size.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
nbytes = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||||
op->dummy.nbytes;
|
|
||||||
|
|
||||||
if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
|
if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ int spi_mem_exec_op(struct spi_slave *slave,
|
|||||||
tx_buf = op->data.buf.out;
|
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);
|
op_buf = calloc(1, op_len);
|
||||||
|
|
||||||
ret = spi_claim_bus(slave);
|
ret = spi_claim_bus(slave);
|
||||||
@ -89,7 +89,7 @@ int spi_mem_adjust_op_size(struct spi_slave *slave,
|
|||||||
{
|
{
|
||||||
unsigned int len;
|
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)
|
if (slave->max_write_size && len > slave->max_write_size)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
@ -105,3 +105,65 @@ int spi_mem_adjust_op_size(struct spi_slave *slave,
|
|||||||
|
|
||||||
return 0;
|
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;
|
return -ENOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool spi_mem_default_supports_op(struct spi_slave *slave,
|
static bool spi_mem_check_buswidth(struct spi_slave *slave,
|
||||||
const struct spi_mem_op *op)
|
const struct spi_mem_op *op)
|
||||||
{
|
{
|
||||||
if (spi_check_buswidth_req(slave, op->cmd.buswidth, true))
|
if (spi_check_buswidth_req(slave, op->cmd.buswidth, true))
|
||||||
return false;
|
return false;
|
||||||
@ -166,6 +166,38 @@ bool spi_mem_default_supports_op(struct spi_slave *slave,
|
|||||||
|
|
||||||
return true;
|
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);
|
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__
|
#ifndef __UBOOT__
|
||||||
tmpbufsize = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
tmpbufsize = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||||
op->dummy.nbytes;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a buffer to transmit the CMD, ADDR cycles with kmalloc() so
|
* 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;
|
tmpbuf[0] = op->cmd.opcode;
|
||||||
xfers[xferpos].tx_buf = tmpbuf;
|
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;
|
xfers[xferpos].tx_nbits = op->cmd.buswidth;
|
||||||
spi_message_add_tail(&xfers[xferpos], &msg);
|
spi_message_add_tail(&xfers[xferpos], &msg);
|
||||||
xferpos++;
|
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;
|
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
|
* 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) {
|
if (!ops->mem_ops || !ops->mem_ops->exec_op) {
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
|
||||||
len = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
len = op->cmd.nbytes + op->addr.nbytes + op->dummy.nbytes;
|
||||||
op->dummy.nbytes;
|
|
||||||
if (slave->max_write_size && len > slave->max_write_size)
|
if (slave->max_write_size && len > slave->max_write_size)
|
||||||
return -EINVAL;
|
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_CLFSR 0x50 /* Clear flag status register */
|
||||||
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
|
#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
|
||||||
#define SPINOR_OP_WREAR 0xc5 /* Write 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. */
|
/* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
|
||||||
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
|
#define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */
|
||||||
@ -124,6 +126,12 @@
|
|||||||
/* Used for Micron flashes only. */
|
/* Used for Micron flashes only. */
|
||||||
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
|
#define SPINOR_OP_RD_EVCR 0x65 /* Read EVCR register */
|
||||||
#define SPINOR_OP_WD_EVCR 0x61 /* Write 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. */
|
/* Status Register bits. */
|
||||||
#define SR_WIP BIT(0) /* Write in progress */
|
#define SR_WIP BIT(0) /* Write in progress */
|
||||||
@ -155,6 +163,19 @@
|
|||||||
/* Status Register 2 bits. */
|
/* Status Register 2 bits. */
|
||||||
#define SR2_QUAD_EN_BIT7 BIT(7)
|
#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 */
|
/* Supported SPI protocols */
|
||||||
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
|
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
|
||||||
#define SNOR_PROTO_INST_SHIFT 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_2_2_DTR = SNOR_PROTO_DTR(1, 2, 2),
|
||||||
SNOR_PROTO_1_4_4_DTR = SNOR_PROTO_DTR(1, 4, 4),
|
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_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)
|
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_READY_XSR_RDY = BIT(4),
|
||||||
SNOR_F_USE_CLSR = BIT(5),
|
SNOR_F_USE_CLSR = BIT(5),
|
||||||
SNOR_F_BROKEN_RESET = BIT(6),
|
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_opcode: the read opcode
|
||||||
* @read_dummy: the dummy needed by the read operation
|
* @read_dummy: the dummy needed by the read operation
|
||||||
* @program_opcode: the program opcode
|
* @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_read_cmd: Bank read cmd
|
||||||
* @bank_write_cmd: Bank write cmd
|
* @bank_write_cmd: Bank write cmd
|
||||||
* @bank_curr: Current flash bank
|
* @bank_curr: Current flash bank
|
||||||
@ -288,6 +481,8 @@ struct spi_flash {
|
|||||||
* @write_proto: the SPI protocol for write operations
|
* @write_proto: the SPI protocol for write operations
|
||||||
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations
|
* @reg_proto the SPI protocol for read_reg/write_reg/erase operations
|
||||||
* @cmd_buf: used by the write_reg
|
* @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
|
* @prepare: [OPTIONAL] do some preparations for the
|
||||||
* read/write/erase/lock/unlock operations
|
* read/write/erase/lock/unlock operations
|
||||||
* @unprepare: [OPTIONAL] do some post work after the
|
* @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
|
* @flash_is_locked: [FLASH-SPECIFIC] check if a region of the SPI NOR is
|
||||||
* completely locked
|
* completely locked
|
||||||
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
|
* @quad_enable: [FLASH-SPECIFIC] enables SPI NOR quad mode
|
||||||
|
* @octal_dtr_enable: [FLASH-SPECIFIC] enables SPI NOR octal DTR mode.
|
||||||
* @priv: the private data
|
* @priv: the private data
|
||||||
*/
|
*/
|
||||||
struct spi_nor {
|
struct spi_nor {
|
||||||
@ -318,6 +514,8 @@ struct spi_nor {
|
|||||||
u8 read_opcode;
|
u8 read_opcode;
|
||||||
u8 read_dummy;
|
u8 read_dummy;
|
||||||
u8 program_opcode;
|
u8 program_opcode;
|
||||||
|
u8 rdsr_dummy;
|
||||||
|
u8 rdsr_addr_nbytes;
|
||||||
#ifdef CONFIG_SPI_FLASH_BAR
|
#ifdef CONFIG_SPI_FLASH_BAR
|
||||||
u8 bank_read_cmd;
|
u8 bank_read_cmd;
|
||||||
u8 bank_write_cmd;
|
u8 bank_write_cmd;
|
||||||
@ -329,7 +527,11 @@ struct spi_nor {
|
|||||||
bool sst_write_second;
|
bool sst_write_second;
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
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);
|
int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
|
||||||
void (*unprepare)(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);
|
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_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 (*flash_is_locked)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
||||||
int (*quad_enable)(struct spi_nor *nor);
|
int (*quad_enable)(struct spi_nor *nor);
|
||||||
|
int (*octal_dtr_enable)(struct spi_nor *nor);
|
||||||
|
|
||||||
void *priv;
|
void *priv;
|
||||||
/* Compatibility for spi_flash, remove once sf layer is merged with mtd */
|
/* 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__ */
|
#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
|
* spi_nor_scan() - scan the SPI NOR
|
||||||
* @nor: the spi_nor structure
|
* @nor: the spi_nor structure
|
||||||
@ -441,4 +583,19 @@ struct spi_nor_hwcaps {
|
|||||||
*/
|
*/
|
||||||
int spi_nor_scan(struct spi_nor *nor);
|
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
|
#endif
|
||||||
|
|||||||
@ -17,6 +17,7 @@ struct udevice;
|
|||||||
{ \
|
{ \
|
||||||
.buswidth = __buswidth, \
|
.buswidth = __buswidth, \
|
||||||
.opcode = __opcode, \
|
.opcode = __opcode, \
|
||||||
|
.nbytes = 1, \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SPI_MEM_OP_ADDR(__nbytes, __val, __buswidth) \
|
#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
|
* 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.buswidth: number of IO lines used to transmit the command
|
||||||
* @cmd.opcode: operation opcode
|
* @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
|
* @addr.nbytes: number of address bytes to send. Can be zero if the operation
|
||||||
* does not need to send an address
|
* does not need to send an address
|
||||||
* @addr.buswidth: number of IO lines used to transmit the address cycles
|
* @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
|
* Note that only @addr.nbytes are taken into account in this
|
||||||
* address value, so users should make sure the value fits in the
|
* address value, so users should make sure the value fits in the
|
||||||
* assigned number of bytes.
|
* 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
|
* @dummy.nbytes: number of dummy bytes to send after an opcode or address. Can
|
||||||
* be zero if the operation does not require dummy bytes
|
* be zero if the operation does not require dummy bytes
|
||||||
* @dummy.buswidth: number of IO lanes used to transmit the 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.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.dir: direction of the transfer
|
||||||
* @data.buf.in: input buffer
|
* @data.buf.in: input buffer
|
||||||
* @data.buf.out: output buffer
|
* @data.buf.out: output buffer
|
||||||
*/
|
*/
|
||||||
struct spi_mem_op {
|
struct spi_mem_op {
|
||||||
struct {
|
struct {
|
||||||
|
u8 nbytes;
|
||||||
u8 buswidth;
|
u8 buswidth;
|
||||||
u8 opcode;
|
u8 dtr : 1;
|
||||||
|
u16 opcode;
|
||||||
} cmd;
|
} cmd;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u8 nbytes;
|
u8 nbytes;
|
||||||
u8 buswidth;
|
u8 buswidth;
|
||||||
|
u8 dtr : 1;
|
||||||
u64 val;
|
u64 val;
|
||||||
} addr;
|
} addr;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u8 nbytes;
|
u8 nbytes;
|
||||||
u8 buswidth;
|
u8 buswidth;
|
||||||
|
u8 dtr : 1;
|
||||||
} dummy;
|
} dummy;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
u8 buswidth;
|
u8 buswidth;
|
||||||
|
u8 dtr : 1;
|
||||||
enum spi_mem_data_dir dir;
|
enum spi_mem_data_dir dir;
|
||||||
unsigned int nbytes;
|
unsigned int nbytes;
|
||||||
/* buf.{in,out} must be DMA-able. */
|
/* 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);
|
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_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);
|
int spi_mem_exec_op(struct spi_slave *slave, const struct spi_mem_op *op);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user