This pull request contains the following notable changes:
- introduce support to the SPI 1-2-2 and 1-4-4 protocols. - introduce support to the Double Data Rate (DDR) mode. - introduce support to the Octo SPI protocols. - add support to new memory parts for Spansion, Macronix and Winbond. - add fixes for the Aspeed, STM32 and Cadence QSPI controler drivers. - clean up the st_spi_fsm driver. -----BEGIN PGP SIGNATURE----- iQI4BAABCAAiBQJZV6mQGxxjeXJpbGxlLnBpdGNoZW5Ad2VkZXY0dS5mcgAKCRDn 4OgLHRpJcnqMD/9BVRi6qdKeGU3u6d1osgXSCwnFQ8K4vKzgjht+oNmd3vUTDjbd cbqtSQUGeXsBRwA6JJXQlRJOB9+hsaBrBjfeG/LuDFgFvGWJ2vevgd1v0FftSiqE jA+Vg6CuIZJvNjdl1E1UsXJ8hcP4xcQADzd8bhjqhQsZOLYfGqG6/nPSpiUI4JWU 3KsNhJ6HoN/PWNBRNjanGm6d6HRyu0C/GtDT0N9tGKqupas/xkH/cPCHyCkF8nBH Tp/IPTw6KmyVBmtMzcTzlR/SVUld3LEZfFQk5FpO8InwUArx4lyKCDp4EIWhhBEC QSTL7EmqUuAZTq+JhSyniVjUw9GA54hKcHBaH3NC5uaV0d3cePaWt8rtxKGXVH13 9SY5U4IBlAzEDC3gpD777Lu4Qhj3YelLetRPUtumnc6i7t0qYFIWgO/3yFxYSWdH pFYmIooqG0DQzpwmFTFFiGep4fftxNyMA5DhlzZWfU3OKbvPJ2035QSz5nu/iysZ hhYV205FTsE0D33eBFoVkijKn3mEosTlVyvVofkaFbz6dqIY3t8vv8Wx07aEpTqF WcQ3iCw7x9nO/dBQ/Q25fTvssOlcNzrB4v+SlGfxcFH3gHprwx7PEaT1YHf9qtaF 4hP4UludGHCUZgOZGxfk0r5aLNmXD8aNUhEGkt44pdrItDDRfWQTUc0fcQ== =ZHnm -----END PGP SIGNATURE----- Merge tag 'spi-nor/for-4.13' into MTD From Cyrille: """ This pull request contains the following notable changes: - introduce support to the SPI 1-2-2 and 1-4-4 protocols. - introduce support to the Double Data Rate (DDR) mode. - introduce support to the Octo SPI protocols. - add support to new memory parts for Spansion, Macronix and Winbond. - add fixes for the Aspeed, STM32 and Cadence QSPI controler drivers. - clean up the st_spi_fsm driver. """
This commit is contained in:
commit
8b9ef8f955
@ -78,11 +78,17 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
struct spi_device *spi = flash->spi;
|
||||
struct spi_transfer t[2] = {};
|
||||
unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
|
||||
struct spi_transfer t[3] = {};
|
||||
struct spi_message m;
|
||||
int cmd_sz = m25p_cmdsz(nor);
|
||||
ssize_t ret;
|
||||
|
||||
/* get transfer protocols. */
|
||||
inst_nbits = spi_nor_get_protocol_inst_nbits(nor->write_proto);
|
||||
addr_nbits = spi_nor_get_protocol_addr_nbits(nor->write_proto);
|
||||
data_nbits = spi_nor_get_protocol_data_nbits(nor->write_proto);
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
if (nor->program_opcode == SPINOR_OP_AAI_WP && nor->sst_write_second)
|
||||
@ -92,12 +98,27 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
m25p_addr2cmd(nor, to, flash->command);
|
||||
|
||||
t[0].tx_buf = flash->command;
|
||||
t[0].tx_nbits = inst_nbits;
|
||||
t[0].len = cmd_sz;
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
|
||||
t[1].tx_buf = buf;
|
||||
t[1].len = len;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
/* split the op code and address bytes into two transfers if needed. */
|
||||
data_idx = 1;
|
||||
if (addr_nbits != inst_nbits) {
|
||||
t[0].len = 1;
|
||||
|
||||
t[1].tx_buf = &flash->command[1];
|
||||
t[1].tx_nbits = addr_nbits;
|
||||
t[1].len = cmd_sz - 1;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
data_idx = 2;
|
||||
}
|
||||
|
||||
t[data_idx].tx_buf = buf;
|
||||
t[data_idx].tx_nbits = data_nbits;
|
||||
t[data_idx].len = len;
|
||||
spi_message_add_tail(&t[data_idx], &m);
|
||||
|
||||
ret = spi_sync(spi, &m);
|
||||
if (ret)
|
||||
@ -109,18 +130,6 @@ static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
|
||||
{
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_DUAL:
|
||||
return 2;
|
||||
case SPI_NOR_QUAD:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Read an address range from the nor chip. The address range
|
||||
* may be any size provided it is within the physical boundaries.
|
||||
@ -130,13 +139,20 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
struct spi_device *spi = flash->spi;
|
||||
struct spi_transfer t[2];
|
||||
unsigned int inst_nbits, addr_nbits, data_nbits, data_idx;
|
||||
struct spi_transfer t[3];
|
||||
struct spi_message m;
|
||||
unsigned int dummy = nor->read_dummy;
|
||||
ssize_t ret;
|
||||
int cmd_sz;
|
||||
|
||||
/* get transfer protocols. */
|
||||
inst_nbits = spi_nor_get_protocol_inst_nbits(nor->read_proto);
|
||||
addr_nbits = spi_nor_get_protocol_addr_nbits(nor->read_proto);
|
||||
data_nbits = spi_nor_get_protocol_data_nbits(nor->read_proto);
|
||||
|
||||
/* convert the dummy cycles to the number of bytes */
|
||||
dummy /= 8;
|
||||
dummy = (dummy * addr_nbits) / 8;
|
||||
|
||||
if (spi_flash_read_supported(spi)) {
|
||||
struct spi_flash_read_message msg;
|
||||
@ -149,10 +165,9 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
msg.read_opcode = nor->read_opcode;
|
||||
msg.addr_width = nor->addr_width;
|
||||
msg.dummy_bytes = dummy;
|
||||
/* TODO: Support other combinations */
|
||||
msg.opcode_nbits = SPI_NBITS_SINGLE;
|
||||
msg.addr_nbits = SPI_NBITS_SINGLE;
|
||||
msg.data_nbits = m25p80_rx_nbits(nor);
|
||||
msg.opcode_nbits = inst_nbits;
|
||||
msg.addr_nbits = addr_nbits;
|
||||
msg.data_nbits = data_nbits;
|
||||
|
||||
ret = spi_flash_read(spi, &msg);
|
||||
if (ret < 0)
|
||||
@ -167,20 +182,45 @@ static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
m25p_addr2cmd(nor, from, flash->command);
|
||||
|
||||
t[0].tx_buf = flash->command;
|
||||
t[0].tx_nbits = inst_nbits;
|
||||
t[0].len = m25p_cmdsz(nor) + dummy;
|
||||
spi_message_add_tail(&t[0], &m);
|
||||
|
||||
t[1].rx_buf = buf;
|
||||
t[1].rx_nbits = m25p80_rx_nbits(nor);
|
||||
t[1].len = min3(len, spi_max_transfer_size(spi),
|
||||
spi_max_message_size(spi) - t[0].len);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
/*
|
||||
* Set all dummy/mode cycle bits to avoid sending some manufacturer
|
||||
* specific pattern, which might make the memory enter its Continuous
|
||||
* Read mode by mistake.
|
||||
* Based on the different mode cycle bit patterns listed and described
|
||||
* in the JESD216B specification, the 0xff value works for all memories
|
||||
* and all manufacturers.
|
||||
*/
|
||||
cmd_sz = t[0].len;
|
||||
memset(flash->command + cmd_sz - dummy, 0xff, dummy);
|
||||
|
||||
/* split the op code and address bytes into two transfers if needed. */
|
||||
data_idx = 1;
|
||||
if (addr_nbits != inst_nbits) {
|
||||
t[0].len = 1;
|
||||
|
||||
t[1].tx_buf = &flash->command[1];
|
||||
t[1].tx_nbits = addr_nbits;
|
||||
t[1].len = cmd_sz - 1;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
data_idx = 2;
|
||||
}
|
||||
|
||||
t[data_idx].rx_buf = buf;
|
||||
t[data_idx].rx_nbits = data_nbits;
|
||||
t[data_idx].len = min3(len, spi_max_transfer_size(spi),
|
||||
spi_max_message_size(spi) - cmd_sz);
|
||||
spi_message_add_tail(&t[data_idx], &m);
|
||||
|
||||
ret = spi_sync(spi, &m);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = m.actual_length - m25p_cmdsz(nor) - dummy;
|
||||
ret = m.actual_length - cmd_sz;
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
return ret;
|
||||
@ -196,7 +236,11 @@ static int m25p_probe(struct spi_device *spi)
|
||||
struct flash_platform_data *data;
|
||||
struct m25p *flash;
|
||||
struct spi_nor *nor;
|
||||
enum read_mode mode = SPI_NOR_NORMAL;
|
||||
struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
char *flash_name;
|
||||
int ret;
|
||||
|
||||
@ -221,10 +265,19 @@ static int m25p_probe(struct spi_device *spi)
|
||||
spi_set_drvdata(spi, flash);
|
||||
flash->spi = spi;
|
||||
|
||||
if (spi->mode & SPI_RX_QUAD)
|
||||
mode = SPI_NOR_QUAD;
|
||||
else if (spi->mode & SPI_RX_DUAL)
|
||||
mode = SPI_NOR_DUAL;
|
||||
if (spi->mode & SPI_RX_QUAD) {
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
||||
|
||||
if (spi->mode & SPI_TX_QUAD)
|
||||
hwcaps.mask |= (SNOR_HWCAPS_READ_1_4_4 |
|
||||
SNOR_HWCAPS_PP_1_1_4 |
|
||||
SNOR_HWCAPS_PP_1_4_4);
|
||||
} else if (spi->mode & SPI_RX_DUAL) {
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
||||
|
||||
if (spi->mode & SPI_TX_DUAL)
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_2_2;
|
||||
}
|
||||
|
||||
if (data && data->name)
|
||||
nor->mtd.name = data->name;
|
||||
@ -241,7 +294,7 @@ static int m25p_probe(struct spi_device *spi)
|
||||
else
|
||||
flash_name = spi->modalias;
|
||||
|
||||
ret = spi_nor_scan(nor, flash_name, mode);
|
||||
ret = spi_nor_scan(nor, flash_name, &hwcaps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -13,7 +13,6 @@
|
||||
#define _MTD_SERIAL_FLASH_CMDS_H
|
||||
|
||||
/* Generic Flash Commands/OPCODEs */
|
||||
#define SPINOR_OP_RDSR2 0x35
|
||||
#define SPINOR_OP_WRVCR 0x81
|
||||
#define SPINOR_OP_RDVCR 0x85
|
||||
|
||||
|
@ -1445,7 +1445,7 @@ static int stfsm_s25fl_config(struct stfsm *fsm)
|
||||
}
|
||||
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &cr1, 1);
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDCR, &cr1, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
if (!(cr1 & STFSM_S25FL_CONFIG_QE)) {
|
||||
@ -1490,7 +1490,7 @@ static int stfsm_w25q_config(struct stfsm *fsm)
|
||||
return ret;
|
||||
|
||||
/* Check status of 'QE' bit, update if required. */
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDSR2, &sr2, 1);
|
||||
stfsm_read_status(fsm, SPINOR_OP_RDCR, &sr2, 1);
|
||||
data_pads = ((fsm->stfsm_seq_read.seq_cfg >> 16) & 0x3) + 1;
|
||||
if (data_pads == 4) {
|
||||
if (!(sr2 & W25Q_STATUS_QE)) {
|
||||
|
@ -108,7 +108,7 @@ config SPI_INTEL_SPI_PLATFORM
|
||||
|
||||
config SPI_STM32_QUADSPI
|
||||
tristate "STM32 Quad SPI controller"
|
||||
depends on ARCH_STM32
|
||||
depends on ARCH_STM32 || COMPILE_TEST
|
||||
help
|
||||
This enables support for the STM32 Quad SPI controller.
|
||||
We only connect the NOR to this controller.
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/mtd/spi-nor.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#define DEVICE_NAME "aspeed-smc"
|
||||
@ -97,6 +98,7 @@ struct aspeed_smc_chip {
|
||||
struct aspeed_smc_controller *controller;
|
||||
void __iomem *ctl; /* control register */
|
||||
void __iomem *ahb_base; /* base of chip window */
|
||||
u32 ahb_window_size; /* chip mapping window size */
|
||||
u32 ctl_val[smc_max]; /* control settings */
|
||||
enum aspeed_smc_flash_type type; /* what type of flash */
|
||||
struct spi_nor nor;
|
||||
@ -109,6 +111,7 @@ struct aspeed_smc_controller {
|
||||
const struct aspeed_smc_info *info; /* type info of controller */
|
||||
void __iomem *regs; /* controller registers */
|
||||
void __iomem *ahb_base; /* per-chip windows resource */
|
||||
u32 ahb_window_size; /* full mapping window size */
|
||||
|
||||
struct aspeed_smc_chip *chips[0]; /* pointers to attached chips */
|
||||
};
|
||||
@ -180,8 +183,7 @@ struct aspeed_smc_controller {
|
||||
|
||||
#define CONTROL_KEEP_MASK \
|
||||
(CONTROL_AAF_MODE | CONTROL_CE_INACTIVE_MASK | CONTROL_CLK_DIV4 | \
|
||||
CONTROL_IO_DUMMY_MASK | CONTROL_CLOCK_FREQ_SEL_MASK | \
|
||||
CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
|
||||
CONTROL_CLOCK_FREQ_SEL_MASK | CONTROL_LSB_FIRST | CONTROL_CLOCK_MODE_3)
|
||||
|
||||
/*
|
||||
* The Segment Register uses a 8MB unit to encode the start address
|
||||
@ -194,6 +196,10 @@ struct aspeed_smc_controller {
|
||||
#define SEGMENT_ADDR_REG0 0x30
|
||||
#define SEGMENT_ADDR_START(_r) ((((_r) >> 16) & 0xFF) << 23)
|
||||
#define SEGMENT_ADDR_END(_r) ((((_r) >> 24) & 0xFF) << 23)
|
||||
#define SEGMENT_ADDR_VALUE(start, end) \
|
||||
(((((start) >> 23) & 0xFF) << 16) | ((((end) >> 23) & 0xFF) << 24))
|
||||
#define SEGMENT_ADDR_REG(controller, cs) \
|
||||
((controller)->regs + SEGMENT_ADDR_REG0 + (cs) * 4)
|
||||
|
||||
/*
|
||||
* In user mode all data bytes read or written to the chip decode address
|
||||
@ -439,8 +445,7 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
|
||||
u32 reg;
|
||||
|
||||
if (controller->info->nce > 1) {
|
||||
reg = readl(controller->regs + SEGMENT_ADDR_REG0 +
|
||||
chip->cs * 4);
|
||||
reg = readl(SEGMENT_ADDR_REG(controller, chip->cs));
|
||||
|
||||
if (SEGMENT_ADDR_START(reg) >= SEGMENT_ADDR_END(reg))
|
||||
return NULL;
|
||||
@ -451,6 +456,146 @@ static void __iomem *aspeed_smc_chip_base(struct aspeed_smc_chip *chip,
|
||||
return controller->ahb_base + offset;
|
||||
}
|
||||
|
||||
static u32 aspeed_smc_ahb_base_phy(struct aspeed_smc_controller *controller)
|
||||
{
|
||||
u32 seg0_val = readl(SEGMENT_ADDR_REG(controller, 0));
|
||||
|
||||
return SEGMENT_ADDR_START(seg0_val);
|
||||
}
|
||||
|
||||
static u32 chip_set_segment(struct aspeed_smc_chip *chip, u32 cs, u32 start,
|
||||
u32 size)
|
||||
{
|
||||
struct aspeed_smc_controller *controller = chip->controller;
|
||||
void __iomem *seg_reg;
|
||||
u32 seg_oldval, seg_newval, ahb_base_phy, end;
|
||||
|
||||
ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
|
||||
|
||||
seg_reg = SEGMENT_ADDR_REG(controller, cs);
|
||||
seg_oldval = readl(seg_reg);
|
||||
|
||||
/*
|
||||
* If the chip size is not specified, use the default segment
|
||||
* size, but take into account the possible overlap with the
|
||||
* previous segment
|
||||
*/
|
||||
if (!size)
|
||||
size = SEGMENT_ADDR_END(seg_oldval) - start;
|
||||
|
||||
/*
|
||||
* The segment cannot exceed the maximum window size of the
|
||||
* controller.
|
||||
*/
|
||||
if (start + size > ahb_base_phy + controller->ahb_window_size) {
|
||||
size = ahb_base_phy + controller->ahb_window_size - start;
|
||||
dev_warn(chip->nor.dev, "CE%d window resized to %dMB",
|
||||
cs, size >> 20);
|
||||
}
|
||||
|
||||
end = start + size;
|
||||
seg_newval = SEGMENT_ADDR_VALUE(start, end);
|
||||
writel(seg_newval, seg_reg);
|
||||
|
||||
/*
|
||||
* Restore default value if something goes wrong. The chip
|
||||
* might have set some bogus value and we would loose access
|
||||
* to the chip.
|
||||
*/
|
||||
if (seg_newval != readl(seg_reg)) {
|
||||
dev_err(chip->nor.dev, "CE%d window invalid", cs);
|
||||
writel(seg_oldval, seg_reg);
|
||||
start = SEGMENT_ADDR_START(seg_oldval);
|
||||
end = SEGMENT_ADDR_END(seg_oldval);
|
||||
size = end - start;
|
||||
}
|
||||
|
||||
dev_info(chip->nor.dev, "CE%d window [ 0x%.8x - 0x%.8x ] %dMB",
|
||||
cs, start, end, size >> 20);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/*
|
||||
* The segment register defines the mapping window on the AHB bus and
|
||||
* it needs to be configured depending on the chip size. The segment
|
||||
* register of the following CE also needs to be tuned in order to
|
||||
* provide a contiguous window across multiple chips.
|
||||
*
|
||||
* This is expected to be called in increasing CE order
|
||||
*/
|
||||
static u32 aspeed_smc_chip_set_segment(struct aspeed_smc_chip *chip)
|
||||
{
|
||||
struct aspeed_smc_controller *controller = chip->controller;
|
||||
u32 ahb_base_phy, start;
|
||||
u32 size = chip->nor.mtd.size;
|
||||
|
||||
/*
|
||||
* Each controller has a chip size limit for direct memory
|
||||
* access
|
||||
*/
|
||||
if (size > controller->info->maxsize)
|
||||
size = controller->info->maxsize;
|
||||
|
||||
/*
|
||||
* The AST2400 SPI controller only handles one chip and does
|
||||
* not have segment registers. Let's use the chip size for the
|
||||
* AHB window.
|
||||
*/
|
||||
if (controller->info == &spi_2400_info)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The AST2500 SPI controller has a HW bug when the CE0 chip
|
||||
* size reaches 128MB. Enforce a size limit of 120MB to
|
||||
* prevent the controller from using bogus settings in the
|
||||
* segment register.
|
||||
*/
|
||||
if (chip->cs == 0 && controller->info == &spi_2500_info &&
|
||||
size == SZ_128M) {
|
||||
size = 120 << 20;
|
||||
dev_info(chip->nor.dev,
|
||||
"CE%d window resized to %dMB (AST2500 HW quirk)",
|
||||
chip->cs, size >> 20);
|
||||
}
|
||||
|
||||
ahb_base_phy = aspeed_smc_ahb_base_phy(controller);
|
||||
|
||||
/*
|
||||
* As a start address for the current segment, use the default
|
||||
* start address if we are handling CE0 or use the previous
|
||||
* segment ending address
|
||||
*/
|
||||
if (chip->cs) {
|
||||
u32 prev = readl(SEGMENT_ADDR_REG(controller, chip->cs - 1));
|
||||
|
||||
start = SEGMENT_ADDR_END(prev);
|
||||
} else {
|
||||
start = ahb_base_phy;
|
||||
}
|
||||
|
||||
size = chip_set_segment(chip, chip->cs, start, size);
|
||||
|
||||
/* Update chip base address on the AHB bus */
|
||||
chip->ahb_base = controller->ahb_base + (start - ahb_base_phy);
|
||||
|
||||
/*
|
||||
* Now, make sure the next segment does not overlap with the
|
||||
* current one we just configured, even if there is no
|
||||
* available chip. That could break access in Command Mode.
|
||||
*/
|
||||
if (chip->cs < controller->info->nce - 1)
|
||||
chip_set_segment(chip, chip->cs + 1, start + size, 0);
|
||||
|
||||
out:
|
||||
if (size < chip->nor.mtd.size)
|
||||
dev_warn(chip->nor.dev,
|
||||
"CE%d window too small for chip %dMB",
|
||||
chip->cs, (u32)chip->nor.mtd.size >> 20);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void aspeed_smc_chip_enable_write(struct aspeed_smc_chip *chip)
|
||||
{
|
||||
struct aspeed_smc_controller *controller = chip->controller;
|
||||
@ -524,7 +669,7 @@ static int aspeed_smc_chip_setup_init(struct aspeed_smc_chip *chip,
|
||||
*/
|
||||
chip->ahb_base = aspeed_smc_chip_base(chip, res);
|
||||
if (!chip->ahb_base) {
|
||||
dev_warn(chip->nor.dev, "CE segment window closed.\n");
|
||||
dev_warn(chip->nor.dev, "CE%d window closed", chip->cs);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -571,6 +716,9 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
|
||||
if (chip->nor.addr_width == 4 && info->set_4b)
|
||||
info->set_4b(chip);
|
||||
|
||||
/* This is for direct AHB access when using Command Mode. */
|
||||
chip->ahb_window_size = aspeed_smc_chip_set_segment(chip);
|
||||
|
||||
/*
|
||||
* base mode has not been optimized yet. use it for writes.
|
||||
*/
|
||||
@ -585,14 +733,12 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
|
||||
* TODO: Adjust clocks if fast read is supported and interpret
|
||||
* SPI-NOR flags to adjust controller settings.
|
||||
*/
|
||||
switch (chip->nor.flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
cmd = CONTROL_COMMAND_MODE_NORMAL;
|
||||
break;
|
||||
case SPI_NOR_FAST:
|
||||
cmd = CONTROL_COMMAND_MODE_FREAD;
|
||||
break;
|
||||
default:
|
||||
if (chip->nor.read_proto == SNOR_PROTO_1_1_1) {
|
||||
if (chip->nor.read_dummy == 0)
|
||||
cmd = CONTROL_COMMAND_MODE_NORMAL;
|
||||
else
|
||||
cmd = CONTROL_COMMAND_MODE_FREAD;
|
||||
} else {
|
||||
dev_err(chip->nor.dev, "unsupported SPI read mode\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -608,6 +754,11 @@ static int aspeed_smc_chip_setup_finish(struct aspeed_smc_chip *chip)
|
||||
static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
|
||||
struct device_node *np, struct resource *r)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
const struct aspeed_smc_info *info = controller->info;
|
||||
struct device *dev = controller->dev;
|
||||
struct device_node *child;
|
||||
@ -671,11 +822,11 @@ static int aspeed_smc_setup_flash(struct aspeed_smc_controller *controller,
|
||||
break;
|
||||
|
||||
/*
|
||||
* TODO: Add support for SPI_NOR_QUAD and SPI_NOR_DUAL
|
||||
* TODO: Add support for Dual and Quad SPI protocols
|
||||
* attach when board support is present as determined
|
||||
* by of property.
|
||||
*/
|
||||
ret = spi_nor_scan(nor, NULL, SPI_NOR_NORMAL);
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
@ -731,6 +882,8 @@ static int aspeed_smc_probe(struct platform_device *pdev)
|
||||
if (IS_ERR(controller->ahb_base))
|
||||
return PTR_ERR(controller->ahb_base);
|
||||
|
||||
controller->ahb_window_size = resource_size(res);
|
||||
|
||||
ret = aspeed_smc_setup_flash(controller, np, res);
|
||||
if (ret)
|
||||
dev_err(dev, "Aspeed SMC probe failed %d\n", ret);
|
||||
|
@ -275,14 +275,48 @@ static void atmel_qspi_debug_command(struct atmel_qspi *aq,
|
||||
|
||||
static int atmel_qspi_run_command(struct atmel_qspi *aq,
|
||||
const struct atmel_qspi_command *cmd,
|
||||
u32 ifr_tfrtyp, u32 ifr_width)
|
||||
u32 ifr_tfrtyp, enum spi_nor_protocol proto)
|
||||
{
|
||||
u32 iar, icr, ifr, sr;
|
||||
int err = 0;
|
||||
|
||||
iar = 0;
|
||||
icr = 0;
|
||||
ifr = ifr_tfrtyp | ifr_width;
|
||||
ifr = ifr_tfrtyp;
|
||||
|
||||
/* Set the SPI protocol */
|
||||
switch (proto) {
|
||||
case SNOR_PROTO_1_1_1:
|
||||
ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_1_1_2:
|
||||
ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_1_1_4:
|
||||
ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_1_2_2:
|
||||
ifr |= QSPI_IFR_WIDTH_DUAL_IO;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_1_4_4:
|
||||
ifr |= QSPI_IFR_WIDTH_QUAD_IO;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_2_2_2:
|
||||
ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
|
||||
break;
|
||||
|
||||
case SNOR_PROTO_4_4_4:
|
||||
ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Compute instruction parameters */
|
||||
if (cmd->enable.bits.instruction) {
|
||||
@ -434,7 +468,7 @@ static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode,
|
||||
cmd.rx_buf = buf;
|
||||
cmd.buf_len = len;
|
||||
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
|
||||
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
|
||||
nor->reg_proto);
|
||||
}
|
||||
|
||||
static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
@ -450,7 +484,7 @@ static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
cmd.tx_buf = buf;
|
||||
cmd.buf_len = len;
|
||||
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
|
||||
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
|
||||
nor->reg_proto);
|
||||
}
|
||||
|
||||
static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
@ -469,7 +503,7 @@ static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
cmd.tx_buf = write_buf;
|
||||
cmd.buf_len = len;
|
||||
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
|
||||
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
|
||||
nor->write_proto);
|
||||
return (ret < 0) ? ret : len;
|
||||
}
|
||||
|
||||
@ -484,7 +518,7 @@ static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
|
||||
cmd.instruction = nor->erase_opcode;
|
||||
cmd.address = (u32)offs;
|
||||
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
|
||||
QSPI_IFR_WIDTH_SINGLE_BIT_SPI);
|
||||
nor->reg_proto);
|
||||
}
|
||||
|
||||
static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
@ -493,27 +527,8 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
struct atmel_qspi *aq = nor->priv;
|
||||
struct atmel_qspi_command cmd;
|
||||
u8 num_mode_cycles, num_dummy_cycles;
|
||||
u32 ifr_width;
|
||||
ssize_t ret;
|
||||
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
ifr_width = QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
|
||||
break;
|
||||
|
||||
case SPI_NOR_DUAL:
|
||||
ifr_width = QSPI_IFR_WIDTH_DUAL_OUTPUT;
|
||||
break;
|
||||
|
||||
case SPI_NOR_QUAD:
|
||||
ifr_width = QSPI_IFR_WIDTH_QUAD_OUTPUT;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (nor->read_dummy >= 2) {
|
||||
num_mode_cycles = 2;
|
||||
num_dummy_cycles = nor->read_dummy - 2;
|
||||
@ -536,7 +551,7 @@ static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
cmd.rx_buf = read_buf;
|
||||
cmd.buf_len = len;
|
||||
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
|
||||
ifr_width);
|
||||
nor->read_proto);
|
||||
return (ret < 0) ? ret : len;
|
||||
}
|
||||
|
||||
@ -590,6 +605,20 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
|
||||
|
||||
static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_READ_1_1_2 |
|
||||
SNOR_HWCAPS_READ_1_2_2 |
|
||||
SNOR_HWCAPS_READ_2_2_2 |
|
||||
SNOR_HWCAPS_READ_1_1_4 |
|
||||
SNOR_HWCAPS_READ_1_4_4 |
|
||||
SNOR_HWCAPS_READ_4_4_4 |
|
||||
SNOR_HWCAPS_PP |
|
||||
SNOR_HWCAPS_PP_1_1_4 |
|
||||
SNOR_HWCAPS_PP_1_4_4 |
|
||||
SNOR_HWCAPS_PP_4_4_4,
|
||||
};
|
||||
struct device_node *child, *np = pdev->dev.of_node;
|
||||
struct atmel_qspi *aq;
|
||||
struct resource *res;
|
||||
@ -679,7 +708,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
if (err)
|
||||
goto disable_clk;
|
||||
|
||||
err = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||
err = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (err)
|
||||
goto disable_clk;
|
||||
|
||||
|
@ -855,15 +855,14 @@ static int cqspi_set_protocol(struct spi_nor *nor, const int read)
|
||||
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
|
||||
|
||||
if (read) {
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
switch (nor->read_proto) {
|
||||
case SNOR_PROTO_1_1_1:
|
||||
f_pdata->data_width = CQSPI_INST_TYPE_SINGLE;
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
case SNOR_PROTO_1_1_2:
|
||||
f_pdata->data_width = CQSPI_INST_TYPE_DUAL;
|
||||
break;
|
||||
case SPI_NOR_QUAD:
|
||||
case SNOR_PROTO_1_1_4:
|
||||
f_pdata->data_width = CQSPI_INST_TYPE_QUAD;
|
||||
break;
|
||||
default:
|
||||
@ -1069,6 +1068,13 @@ static void cqspi_controller_init(struct cqspi_st *cqspi)
|
||||
|
||||
static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_READ_1_1_2 |
|
||||
SNOR_HWCAPS_READ_1_1_4 |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
struct platform_device *pdev = cqspi->pdev;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cqspi_flash_pdata *f_pdata;
|
||||
@ -1123,7 +1129,7 @@ static int cqspi_setup_flash(struct cqspi_st *cqspi, struct device_node *np)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
@ -1277,7 +1283,7 @@ static const struct dev_pm_ops cqspi__dev_pm_ops = {
|
||||
#define CQSPI_DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct of_device_id const cqspi_dt_ids[] = {
|
||||
static const struct of_device_id cqspi_dt_ids[] = {
|
||||
{.compatible = "cdns,qspi-nor",},
|
||||
{ /* end of table */ }
|
||||
};
|
||||
|
@ -957,6 +957,10 @@ static void fsl_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
|
||||
|
||||
static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ_1_1_4 |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fsl_qspi *q;
|
||||
@ -1065,7 +1069,7 @@ static int fsl_qspi_probe(struct platform_device *pdev)
|
||||
/* set the chip address for READID */
|
||||
fsl_qspi_set_base_addr(q, nor);
|
||||
|
||||
ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
goto mutex_failed;
|
||||
|
||||
|
@ -120,19 +120,24 @@ static inline int wait_op_finish(struct hifmc_host *host)
|
||||
(reg & FMC_INT_OP_DONE), 0, FMC_WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
static int get_if_type(enum read_mode flash_read)
|
||||
static int get_if_type(enum spi_nor_protocol proto)
|
||||
{
|
||||
enum hifmc_iftype if_type;
|
||||
|
||||
switch (flash_read) {
|
||||
case SPI_NOR_DUAL:
|
||||
switch (proto) {
|
||||
case SNOR_PROTO_1_1_2:
|
||||
if_type = IF_TYPE_DUAL;
|
||||
break;
|
||||
case SPI_NOR_QUAD:
|
||||
case SNOR_PROTO_1_2_2:
|
||||
if_type = IF_TYPE_DIO;
|
||||
break;
|
||||
case SNOR_PROTO_1_1_4:
|
||||
if_type = IF_TYPE_QUAD;
|
||||
break;
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
case SNOR_PROTO_1_4_4:
|
||||
if_type = IF_TYPE_QIO;
|
||||
break;
|
||||
case SNOR_PROTO_1_1_1:
|
||||
default:
|
||||
if_type = IF_TYPE_STD;
|
||||
break;
|
||||
@ -253,7 +258,10 @@ static int hisi_spi_nor_dma_transfer(struct spi_nor *nor, loff_t start_off,
|
||||
writel(FMC_DMA_LEN_SET(len), host->regbase + FMC_DMA_LEN);
|
||||
|
||||
reg = OP_CFG_FM_CS(priv->chipselect);
|
||||
if_type = get_if_type(nor->flash_read);
|
||||
if (op_type == FMC_OP_READ)
|
||||
if_type = get_if_type(nor->read_proto);
|
||||
else
|
||||
if_type = get_if_type(nor->write_proto);
|
||||
reg |= OP_CFG_MEM_IF_TYPE(if_type);
|
||||
if (op_type == FMC_OP_READ)
|
||||
reg |= OP_CFG_DUMMY_NUM(nor->read_dummy >> 3);
|
||||
@ -321,6 +329,13 @@ static ssize_t hisi_spi_nor_write(struct spi_nor *nor, loff_t to,
|
||||
static int hisi_spi_nor_register(struct device_node *np,
|
||||
struct hifmc_host *host)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_READ_1_1_2 |
|
||||
SNOR_HWCAPS_READ_1_1_4 |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
struct device *dev = host->dev;
|
||||
struct spi_nor *nor;
|
||||
struct hifmc_priv *priv;
|
||||
@ -362,7 +377,7 @@ static int hisi_spi_nor_register(struct device_node *np,
|
||||
nor->read = hisi_spi_nor_read;
|
||||
nor->write = hisi_spi_nor_write;
|
||||
nor->erase = NULL;
|
||||
ret = spi_nor_scan(nor, NULL, SPI_NOR_QUAD);
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -715,6 +715,11 @@ static void intel_spi_fill_partition(struct intel_spi *ispi,
|
||||
struct intel_spi *intel_spi_probe(struct device *dev,
|
||||
struct resource *mem, const struct intel_spi_boardinfo *info)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
struct mtd_partition part;
|
||||
struct intel_spi *ispi;
|
||||
int ret;
|
||||
@ -746,7 +751,7 @@ struct intel_spi *intel_spi_probe(struct device *dev,
|
||||
ispi->nor.write = intel_spi_write;
|
||||
ispi->nor.erase = intel_spi_erase;
|
||||
|
||||
ret = spi_nor_scan(&ispi->nor, NULL, SPI_NOR_NORMAL);
|
||||
ret = spi_nor_scan(&ispi->nor, NULL, &hwcaps);
|
||||
if (ret) {
|
||||
dev_info(dev, "failed to locate the chip\n");
|
||||
return ERR_PTR(ret);
|
||||
|
@ -123,20 +123,20 @@ static void mt8173_nor_set_read_mode(struct mt8173_nor *mt8173_nor)
|
||||
{
|
||||
struct spi_nor *nor = &mt8173_nor->nor;
|
||||
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_FAST:
|
||||
switch (nor->read_proto) {
|
||||
case SNOR_PROTO_1_1_1:
|
||||
writeb(nor->read_opcode, mt8173_nor->base +
|
||||
MTK_NOR_PRGDATA3_REG);
|
||||
writeb(MTK_NOR_FAST_READ, mt8173_nor->base +
|
||||
MTK_NOR_CFG1_REG);
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
case SNOR_PROTO_1_1_2:
|
||||
writeb(nor->read_opcode, mt8173_nor->base +
|
||||
MTK_NOR_PRGDATA3_REG);
|
||||
writeb(MTK_NOR_DUAL_READ_EN, mt8173_nor->base +
|
||||
MTK_NOR_DUAL_REG);
|
||||
break;
|
||||
case SPI_NOR_QUAD:
|
||||
case SNOR_PROTO_1_1_4:
|
||||
writeb(nor->read_opcode, mt8173_nor->base +
|
||||
MTK_NOR_PRGDATA4_REG);
|
||||
writeb(MTK_NOR_QUAD_READ_EN, mt8173_nor->base +
|
||||
@ -408,6 +408,11 @@ static int mt8173_nor_write_reg(struct spi_nor *nor, u8 opcode, u8 *buf,
|
||||
static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
|
||||
struct device_node *flash_node)
|
||||
{
|
||||
const struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_READ_1_1_2 |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
int ret;
|
||||
struct spi_nor *nor;
|
||||
|
||||
@ -426,7 +431,7 @@ static int mtk_nor_init(struct mt8173_nor *mt8173_nor,
|
||||
nor->write_reg = mt8173_nor_write_reg;
|
||||
nor->mtd.name = "mtk_nor";
|
||||
/* initialized with NULL */
|
||||
ret = spi_nor_scan(nor, NULL, SPI_NOR_DUAL);
|
||||
ret = spi_nor_scan(nor, NULL, &hwcaps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -240,13 +240,12 @@ static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
|
||||
|
||||
static int nxp_spifi_setup_memory_cmd(struct nxp_spifi *spifi)
|
||||
{
|
||||
switch (spifi->nor.flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
switch (spifi->nor.read_proto) {
|
||||
case SNOR_PROTO_1_1_1:
|
||||
spifi->mcmd = SPIFI_CMD_FIELDFORM_ALL_SERIAL;
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
case SPI_NOR_QUAD:
|
||||
case SNOR_PROTO_1_1_2:
|
||||
case SNOR_PROTO_1_1_4:
|
||||
spifi->mcmd = SPIFI_CMD_FIELDFORM_QUAD_DUAL_DATA;
|
||||
break;
|
||||
default:
|
||||
@ -274,7 +273,11 @@ static void nxp_spifi_dummy_id_read(struct spi_nor *nor)
|
||||
static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
|
||||
struct device_node *np)
|
||||
{
|
||||
enum read_mode flash_read;
|
||||
struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
u32 ctrl, property;
|
||||
u16 mode = 0;
|
||||
int ret;
|
||||
@ -308,13 +311,12 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
|
||||
|
||||
if (mode & SPI_RX_DUAL) {
|
||||
ctrl |= SPIFI_CTRL_DUAL;
|
||||
flash_read = SPI_NOR_DUAL;
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
||||
} else if (mode & SPI_RX_QUAD) {
|
||||
ctrl &= ~SPIFI_CTRL_DUAL;
|
||||
flash_read = SPI_NOR_QUAD;
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
||||
} else {
|
||||
ctrl |= SPIFI_CTRL_DUAL;
|
||||
flash_read = SPI_NOR_NORMAL;
|
||||
}
|
||||
|
||||
switch (mode & (SPI_CPHA | SPI_CPOL)) {
|
||||
@ -351,7 +353,7 @@ static int nxp_spifi_setup_flash(struct nxp_spifi *spifi,
|
||||
*/
|
||||
nxp_spifi_dummy_id_read(&spifi->nor);
|
||||
|
||||
ret = spi_nor_scan(&spifi->nor, NULL, flash_read);
|
||||
ret = spi_nor_scan(&spifi->nor, NULL, &hwcaps);
|
||||
if (ret) {
|
||||
dev_err(spifi->dev, "device scan failed\n");
|
||||
return ret;
|
||||
|
@ -149,24 +149,6 @@ static int read_cr(struct spi_nor *nor)
|
||||
return val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dummy Cycle calculation for different type of read.
|
||||
* It can be used to support more commands with
|
||||
* different dummy cycle requirements.
|
||||
*/
|
||||
static inline int spi_nor_read_dummy_cycles(struct spi_nor *nor)
|
||||
{
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_FAST:
|
||||
case SPI_NOR_DUAL:
|
||||
case SPI_NOR_QUAD:
|
||||
return 8;
|
||||
case SPI_NOR_NORMAL:
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Write status register 1 byte
|
||||
* Returns negative if error occurred.
|
||||
@ -221,6 +203,10 @@ static inline u8 spi_nor_convert_3to4_read(u8 opcode)
|
||||
{ SPINOR_OP_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B },
|
||||
{ SPINOR_OP_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B },
|
||||
{ SPINOR_OP_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B },
|
||||
|
||||
{ SPINOR_OP_READ_1_1_1_DTR, SPINOR_OP_READ_1_1_1_DTR_4B },
|
||||
{ SPINOR_OP_READ_1_2_2_DTR, SPINOR_OP_READ_1_2_2_DTR_4B },
|
||||
{ SPINOR_OP_READ_1_4_4_DTR, SPINOR_OP_READ_1_4_4_DTR_4B },
|
||||
};
|
||||
|
||||
return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
|
||||
@ -1022,10 +1008,12 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "mx25u6435f", INFO(0xc22537, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "mx25l12805d", INFO(0xc22018, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l12855e", INFO(0xc22618, 0, 64 * 1024, 256, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx25l25635e", INFO(0xc22019, 0, 64 * 1024, 512, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "mx25u25635f", INFO(0xc22539, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_OPCODES) },
|
||||
{ "mx25l25655e", INFO(0xc22619, 0, 64 * 1024, 512, 0) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_QUAD_READ) },
|
||||
{ "mx66l51235l", INFO(0xc2201a, 0, 64 * 1024, 1024, SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "mx66u51235f", INFO(0xc2253a, 0, 64 * 1024, 1024, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ "mx66l1g45g", INFO(0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "mx66l1g55g", INFO(0xc2261b, 0, 64 * 1024, 2048, SPI_NOR_QUAD_READ) },
|
||||
|
||||
/* Micron */
|
||||
@ -1036,7 +1024,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "n25q064a", INFO(0x20bb17, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q128a11", INFO(0x20bb18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q128a13", INFO(0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256a", INFO(0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q256ax1", INFO(0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512a", INFO(0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
{ "n25q512ax3", INFO(0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ) },
|
||||
@ -1076,6 +1064,7 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "s25fl164k", INFO(0x014017, 0, 64 * 1024, 128, SECT_4K) },
|
||||
{ "s25fl204k", INFO(0x014013, 0, 64 * 1024, 8, SECT_4K | SPI_NOR_DUAL_READ) },
|
||||
{ "s25fl208k", INFO(0x014014, 0, 64 * 1024, 16, SECT_4K | SPI_NOR_DUAL_READ) },
|
||||
{ "s25fl064l", INFO(0x016017, 0, 64 * 1024, 128, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
|
||||
/* SST -- large erase sizes are "overlays", "sectors" are 4K */
|
||||
{ "sst25vf040b", INFO(0xbf258d, 0, 64 * 1024, 8, SECT_4K | SST_WRITE) },
|
||||
@ -1159,7 +1148,9 @@ static const struct flash_info spi_nor_ids[] = {
|
||||
{ "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
|
||||
{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
|
||||
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
|
||||
{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
|
||||
{ "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
|
||||
SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
|
||||
|
||||
/* Catalyst / On Semiconductor -- non-JEDEC */
|
||||
{ "cat25c11", CAT25_INFO( 16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
|
||||
@ -1403,8 +1394,9 @@ static int macronix_quad_enable(struct spi_nor *nor)
|
||||
|
||||
write_sr(nor, val | SR_QUAD_EN_MX);
|
||||
|
||||
if (spi_nor_wait_till_ready(nor))
|
||||
return 1;
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = read_sr(nor);
|
||||
if (!(ret > 0 && (ret & SR_QUAD_EN_MX))) {
|
||||
@ -1460,30 +1452,6 @@ static int spansion_quad_enable(struct spi_nor *nor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_quad_mode(struct spi_nor *nor, const struct flash_info *info)
|
||||
{
|
||||
int status;
|
||||
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case SNOR_MFR_MACRONIX:
|
||||
status = macronix_quad_enable(nor);
|
||||
if (status) {
|
||||
dev_err(nor->dev, "Macronix quad-read not enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return status;
|
||||
case SNOR_MFR_MICRON:
|
||||
return 0;
|
||||
default:
|
||||
status = spansion_quad_enable(nor);
|
||||
if (status) {
|
||||
dev_err(nor->dev, "Spansion quad-read not enabled\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
static int spi_nor_check(struct spi_nor *nor)
|
||||
{
|
||||
if (!nor->dev || !nor->read || !nor->write ||
|
||||
@ -1536,8 +1504,349 @@ static int s3an_nor_scan(const struct flash_info *info, struct spi_nor *nor)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
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_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_MAX
|
||||
};
|
||||
|
||||
struct spi_nor_flash_parameter {
|
||||
u64 size;
|
||||
u32 page_size;
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
static void
|
||||
spi_nor_set_read_settings(struct spi_nor_read_command *read,
|
||||
u8 num_mode_clocks,
|
||||
u8 num_wait_states,
|
||||
u8 opcode,
|
||||
enum spi_nor_protocol proto)
|
||||
{
|
||||
read->num_mode_clocks = num_mode_clocks;
|
||||
read->num_wait_states = num_wait_states;
|
||||
read->opcode = opcode;
|
||||
read->proto = proto;
|
||||
}
|
||||
|
||||
static void
|
||||
spi_nor_set_pp_settings(struct spi_nor_pp_command *pp,
|
||||
u8 opcode,
|
||||
enum spi_nor_protocol proto)
|
||||
{
|
||||
pp->opcode = opcode;
|
||||
pp->proto = proto;
|
||||
}
|
||||
|
||||
static int spi_nor_init_params(struct spi_nor *nor,
|
||||
const struct flash_info *info,
|
||||
struct spi_nor_flash_parameter *params)
|
||||
{
|
||||
/* Set legacy flash parameters as default. */
|
||||
memset(params, 0, sizeof(*params));
|
||||
|
||||
/* Set SPI NOR sizes. */
|
||||
params->size = info->sector_size * info->n_sectors;
|
||||
params->page_size = info->page_size;
|
||||
|
||||
/* (Fast) Read settings. */
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_READ;
|
||||
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ],
|
||||
0, 0, SPINOR_OP_READ,
|
||||
SNOR_PROTO_1_1_1);
|
||||
|
||||
if (!(info->flags & SPI_NOR_NO_FR)) {
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
||||
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_FAST],
|
||||
0, 8, SPINOR_OP_READ_FAST,
|
||||
SNOR_PROTO_1_1_1);
|
||||
}
|
||||
|
||||
if (info->flags & SPI_NOR_DUAL_READ) {
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
||||
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_2],
|
||||
0, 8, SPINOR_OP_READ_1_1_2,
|
||||
SNOR_PROTO_1_1_2);
|
||||
}
|
||||
|
||||
if (info->flags & SPI_NOR_QUAD_READ) {
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
||||
spi_nor_set_read_settings(¶ms->reads[SNOR_CMD_READ_1_1_4],
|
||||
0, 8, SPINOR_OP_READ_1_1_4,
|
||||
SNOR_PROTO_1_1_4);
|
||||
}
|
||||
|
||||
/* Page Program settings. */
|
||||
params->hwcaps.mask |= SNOR_HWCAPS_PP;
|
||||
spi_nor_set_pp_settings(¶ms->page_programs[SNOR_CMD_PP],
|
||||
SPINOR_OP_PP, SNOR_PROTO_1_1_1);
|
||||
|
||||
/* Select the procedure to set the Quad Enable bit. */
|
||||
if (params->hwcaps.mask & (SNOR_HWCAPS_READ_QUAD |
|
||||
SNOR_HWCAPS_PP_QUAD)) {
|
||||
switch (JEDEC_MFR(info)) {
|
||||
case SNOR_MFR_MACRONIX:
|
||||
params->quad_enable = macronix_quad_enable;
|
||||
break;
|
||||
|
||||
case SNOR_MFR_MICRON:
|
||||
break;
|
||||
|
||||
default:
|
||||
params->quad_enable = spansion_quad_enable;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_hwcaps2cmd(u32 hwcaps, const int table[][2], size_t size)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < size; i++)
|
||||
if (table[i][0] == (int)hwcaps)
|
||||
return table[i][1];
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int spi_nor_hwcaps_read2cmd(u32 hwcaps)
|
||||
{
|
||||
static const int hwcaps_read2cmd[][2] = {
|
||||
{ SNOR_HWCAPS_READ, SNOR_CMD_READ },
|
||||
{ SNOR_HWCAPS_READ_FAST, SNOR_CMD_READ_FAST },
|
||||
{ SNOR_HWCAPS_READ_1_1_1_DTR, SNOR_CMD_READ_1_1_1_DTR },
|
||||
{ SNOR_HWCAPS_READ_1_1_2, SNOR_CMD_READ_1_1_2 },
|
||||
{ SNOR_HWCAPS_READ_1_2_2, SNOR_CMD_READ_1_2_2 },
|
||||
{ SNOR_HWCAPS_READ_2_2_2, SNOR_CMD_READ_2_2_2 },
|
||||
{ SNOR_HWCAPS_READ_1_2_2_DTR, SNOR_CMD_READ_1_2_2_DTR },
|
||||
{ SNOR_HWCAPS_READ_1_1_4, SNOR_CMD_READ_1_1_4 },
|
||||
{ SNOR_HWCAPS_READ_1_4_4, SNOR_CMD_READ_1_4_4 },
|
||||
{ SNOR_HWCAPS_READ_4_4_4, SNOR_CMD_READ_4_4_4 },
|
||||
{ SNOR_HWCAPS_READ_1_4_4_DTR, SNOR_CMD_READ_1_4_4_DTR },
|
||||
{ SNOR_HWCAPS_READ_1_1_8, SNOR_CMD_READ_1_1_8 },
|
||||
{ SNOR_HWCAPS_READ_1_8_8, SNOR_CMD_READ_1_8_8 },
|
||||
{ SNOR_HWCAPS_READ_8_8_8, SNOR_CMD_READ_8_8_8 },
|
||||
{ SNOR_HWCAPS_READ_1_8_8_DTR, SNOR_CMD_READ_1_8_8_DTR },
|
||||
};
|
||||
|
||||
return spi_nor_hwcaps2cmd(hwcaps, hwcaps_read2cmd,
|
||||
ARRAY_SIZE(hwcaps_read2cmd));
|
||||
}
|
||||
|
||||
static int spi_nor_hwcaps_pp2cmd(u32 hwcaps)
|
||||
{
|
||||
static const int hwcaps_pp2cmd[][2] = {
|
||||
{ SNOR_HWCAPS_PP, SNOR_CMD_PP },
|
||||
{ SNOR_HWCAPS_PP_1_1_4, SNOR_CMD_PP_1_1_4 },
|
||||
{ SNOR_HWCAPS_PP_1_4_4, SNOR_CMD_PP_1_4_4 },
|
||||
{ SNOR_HWCAPS_PP_4_4_4, SNOR_CMD_PP_4_4_4 },
|
||||
{ SNOR_HWCAPS_PP_1_1_8, SNOR_CMD_PP_1_1_8 },
|
||||
{ SNOR_HWCAPS_PP_1_8_8, SNOR_CMD_PP_1_8_8 },
|
||||
{ SNOR_HWCAPS_PP_8_8_8, SNOR_CMD_PP_8_8_8 },
|
||||
};
|
||||
|
||||
return spi_nor_hwcaps2cmd(hwcaps, hwcaps_pp2cmd,
|
||||
ARRAY_SIZE(hwcaps_pp2cmd));
|
||||
}
|
||||
|
||||
static int spi_nor_select_read(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 shared_hwcaps)
|
||||
{
|
||||
int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_READ_MASK) - 1;
|
||||
const struct spi_nor_read_command *read;
|
||||
|
||||
if (best_match < 0)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = spi_nor_hwcaps_read2cmd(BIT(best_match));
|
||||
if (cmd < 0)
|
||||
return -EINVAL;
|
||||
|
||||
read = ¶ms->reads[cmd];
|
||||
nor->read_opcode = read->opcode;
|
||||
nor->read_proto = read->proto;
|
||||
|
||||
/*
|
||||
* In the spi-nor framework, we don't need to make the difference
|
||||
* between mode clock cycles and wait state clock cycles.
|
||||
* Indeed, the value of the mode clock cycles is used by a QSPI
|
||||
* flash memory to know whether it should enter or leave its 0-4-4
|
||||
* (Continuous Read / XIP) mode.
|
||||
* eXecution In Place is out of the scope of the mtd sub-system.
|
||||
* Hence we choose to merge both mode and wait state clock cycles
|
||||
* into the so called dummy clock cycles.
|
||||
*/
|
||||
nor->read_dummy = read->num_mode_clocks + read->num_wait_states;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_select_pp(struct spi_nor *nor,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
u32 shared_hwcaps)
|
||||
{
|
||||
int cmd, best_match = fls(shared_hwcaps & SNOR_HWCAPS_PP_MASK) - 1;
|
||||
const struct spi_nor_pp_command *pp;
|
||||
|
||||
if (best_match < 0)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = spi_nor_hwcaps_pp2cmd(BIT(best_match));
|
||||
if (cmd < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pp = ¶ms->page_programs[cmd];
|
||||
nor->program_opcode = pp->opcode;
|
||||
nor->write_proto = pp->proto;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_select_erase(struct spi_nor *nor,
|
||||
const struct flash_info *info)
|
||||
{
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
|
||||
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
||||
/* prefer "small sector" erase if possible */
|
||||
if (info->flags & SECT_4K) {
|
||||
nor->erase_opcode = SPINOR_OP_BE_4K;
|
||||
mtd->erasesize = 4096;
|
||||
} else if (info->flags & SECT_4K_PMC) {
|
||||
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
|
||||
mtd->erasesize = 4096;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
nor->erase_opcode = SPINOR_OP_SE;
|
||||
mtd->erasesize = info->sector_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int spi_nor_setup(struct spi_nor *nor, const struct flash_info *info,
|
||||
const struct spi_nor_flash_parameter *params,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
{
|
||||
u32 ignored_mask, shared_mask;
|
||||
bool enable_quad_io;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* Keep only the hardware capabilities supported by both the SPI
|
||||
* controller and the SPI flash memory.
|
||||
*/
|
||||
shared_mask = hwcaps->mask & params->hwcaps.mask;
|
||||
|
||||
/* SPI n-n-n protocols are not supported yet. */
|
||||
ignored_mask = (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);
|
||||
if (shared_mask & ignored_mask) {
|
||||
dev_dbg(nor->dev,
|
||||
"SPI n-n-n protocols are not supported yet.\n");
|
||||
shared_mask &= ~ignored_mask;
|
||||
}
|
||||
|
||||
/* Select the (Fast) Read command. */
|
||||
err = spi_nor_select_read(nor, params, shared_mask);
|
||||
if (err) {
|
||||
dev_err(nor->dev,
|
||||
"can't select read settings supported by both the SPI controller and memory.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Select the Page Program command. */
|
||||
err = spi_nor_select_pp(nor, params, shared_mask);
|
||||
if (err) {
|
||||
dev_err(nor->dev,
|
||||
"can't select write settings supported by both the SPI controller and memory.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Select the Sector Erase command. */
|
||||
err = spi_nor_select_erase(nor, info);
|
||||
if (err) {
|
||||
dev_err(nor->dev,
|
||||
"can't select erase settings supported by both the SPI controller and memory.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Enable Quad I/O if needed. */
|
||||
enable_quad_io = (spi_nor_get_protocol_width(nor->read_proto) == 4 ||
|
||||
spi_nor_get_protocol_width(nor->write_proto) == 4);
|
||||
if (enable_quad_io && params->quad_enable) {
|
||||
err = params->quad_enable(nor);
|
||||
if (err) {
|
||||
dev_err(nor->dev, "quad mode not supported\n");
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
const struct spi_nor_hwcaps *hwcaps)
|
||||
{
|
||||
struct spi_nor_flash_parameter params;
|
||||
const struct flash_info *info = NULL;
|
||||
struct device *dev = nor->dev;
|
||||
struct mtd_info *mtd = &nor->mtd;
|
||||
@ -1549,6 +1858,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Reset SPI protocol for all commands. */
|
||||
nor->reg_proto = SNOR_PROTO_1_1_1;
|
||||
nor->read_proto = SNOR_PROTO_1_1_1;
|
||||
nor->write_proto = SNOR_PROTO_1_1_1;
|
||||
|
||||
if (name)
|
||||
info = spi_nor_match_id(name);
|
||||
/* Try to auto-detect if chip name wasn't specified or not found */
|
||||
@ -1591,6 +1905,11 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
if (info->flags & SPI_S3AN)
|
||||
nor->flags |= SNOR_F_READY_XSR_RDY;
|
||||
|
||||
/* Parse the Serial Flash Discoverable Parameters table. */
|
||||
ret = spi_nor_init_params(nor, info, ¶ms);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Atmel, SST, Intel/Numonyx, and others serial NOR tend to power up
|
||||
* with the software protection bits set
|
||||
@ -1611,7 +1930,7 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
mtd->type = MTD_NORFLASH;
|
||||
mtd->writesize = 1;
|
||||
mtd->flags = MTD_CAP_NORFLASH;
|
||||
mtd->size = info->sector_size * info->n_sectors;
|
||||
mtd->size = params.size;
|
||||
mtd->_erase = spi_nor_erase;
|
||||
mtd->_read = spi_nor_read;
|
||||
|
||||
@ -1642,75 +1961,38 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
if (info->flags & NO_CHIP_ERASE)
|
||||
nor->flags |= SNOR_F_NO_OP_CHIP_ERASE;
|
||||
|
||||
#ifdef CONFIG_MTD_SPI_NOR_USE_4K_SECTORS
|
||||
/* prefer "small sector" erase if possible */
|
||||
if (info->flags & SECT_4K) {
|
||||
nor->erase_opcode = SPINOR_OP_BE_4K;
|
||||
mtd->erasesize = 4096;
|
||||
} else if (info->flags & SECT_4K_PMC) {
|
||||
nor->erase_opcode = SPINOR_OP_BE_4K_PMC;
|
||||
mtd->erasesize = 4096;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
nor->erase_opcode = SPINOR_OP_SE;
|
||||
mtd->erasesize = info->sector_size;
|
||||
}
|
||||
|
||||
if (info->flags & SPI_NOR_NO_ERASE)
|
||||
mtd->flags |= MTD_NO_ERASE;
|
||||
|
||||
mtd->dev.parent = dev;
|
||||
nor->page_size = info->page_size;
|
||||
nor->page_size = params.page_size;
|
||||
mtd->writebufsize = nor->page_size;
|
||||
|
||||
if (np) {
|
||||
/* If we were instantiated by DT, use it */
|
||||
if (of_property_read_bool(np, "m25p,fast-read"))
|
||||
nor->flash_read = SPI_NOR_FAST;
|
||||
params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
||||
else
|
||||
nor->flash_read = SPI_NOR_NORMAL;
|
||||
params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
|
||||
} else {
|
||||
/* If we weren't instantiated by DT, default to fast-read */
|
||||
nor->flash_read = SPI_NOR_FAST;
|
||||
params.hwcaps.mask |= SNOR_HWCAPS_READ_FAST;
|
||||
}
|
||||
|
||||
/* Some devices cannot do fast-read, no matter what DT tells us */
|
||||
if (info->flags & SPI_NOR_NO_FR)
|
||||
nor->flash_read = SPI_NOR_NORMAL;
|
||||
params.hwcaps.mask &= ~SNOR_HWCAPS_READ_FAST;
|
||||
|
||||
/* Quad/Dual-read mode takes precedence over fast/normal */
|
||||
if (mode == SPI_NOR_QUAD && info->flags & SPI_NOR_QUAD_READ) {
|
||||
ret = set_quad_mode(nor, info);
|
||||
if (ret) {
|
||||
dev_err(dev, "quad mode not supported\n");
|
||||
return ret;
|
||||
}
|
||||
nor->flash_read = SPI_NOR_QUAD;
|
||||
} else if (mode == SPI_NOR_DUAL && info->flags & SPI_NOR_DUAL_READ) {
|
||||
nor->flash_read = SPI_NOR_DUAL;
|
||||
}
|
||||
|
||||
/* Default commands */
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_QUAD:
|
||||
nor->read_opcode = SPINOR_OP_READ_1_1_4;
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
nor->read_opcode = SPINOR_OP_READ_1_1_2;
|
||||
break;
|
||||
case SPI_NOR_FAST:
|
||||
nor->read_opcode = SPINOR_OP_READ_FAST;
|
||||
break;
|
||||
case SPI_NOR_NORMAL:
|
||||
nor->read_opcode = SPINOR_OP_READ;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "No Read opcode defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nor->program_opcode = SPINOR_OP_PP;
|
||||
/*
|
||||
* Configure the SPI memory:
|
||||
* - select op codes for (Fast) Read, Page Program and Sector Erase.
|
||||
* - set the number of dummy cycles (mode cycles + wait states).
|
||||
* - set the SPI protocols for register and memory accesses.
|
||||
* - set the Quad Enable bit if needed (required by SPI x-y-4 protos).
|
||||
*/
|
||||
ret = spi_nor_setup(nor, info, ¶ms, hwcaps);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (info->addr_width)
|
||||
nor->addr_width = info->addr_width;
|
||||
@ -1732,8 +2014,6 @@ int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
nor->read_dummy = spi_nor_read_dummy_cycles(nor);
|
||||
|
||||
if (info->flags & SPI_S3AN) {
|
||||
ret = s3an_nor_scan(info, nor);
|
||||
if (ret)
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
#include <linux/sizes.h>
|
||||
|
||||
#define QUADSPI_CR 0x00
|
||||
#define CR_EN BIT(0)
|
||||
@ -192,15 +193,15 @@ static void stm32_qspi_set_framemode(struct spi_nor *nor,
|
||||
cmd->framemode = CCR_IMODE_1;
|
||||
|
||||
if (read) {
|
||||
switch (nor->flash_read) {
|
||||
case SPI_NOR_NORMAL:
|
||||
case SPI_NOR_FAST:
|
||||
switch (nor->read_proto) {
|
||||
default:
|
||||
case SNOR_PROTO_1_1_1:
|
||||
dmode = CCR_DMODE_1;
|
||||
break;
|
||||
case SPI_NOR_DUAL:
|
||||
case SNOR_PROTO_1_1_2:
|
||||
dmode = CCR_DMODE_2;
|
||||
break;
|
||||
case SPI_NOR_QUAD:
|
||||
case SNOR_PROTO_1_1_4:
|
||||
dmode = CCR_DMODE_4;
|
||||
break;
|
||||
}
|
||||
@ -375,7 +376,7 @@ static ssize_t stm32_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
struct stm32_qspi_cmd cmd;
|
||||
int err;
|
||||
|
||||
dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#x\n",
|
||||
dev_dbg(qspi->dev, "read(%#.2x): buf:%p from:%#.8x len:%#zx\n",
|
||||
nor->read_opcode, buf, (u32)from, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
@ -402,7 +403,7 @@ static ssize_t stm32_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
struct stm32_qspi_cmd cmd;
|
||||
int err;
|
||||
|
||||
dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#x\n",
|
||||
dev_dbg(dev, "write(%#.2x): buf:%p to:%#.8x len:%#zx\n",
|
||||
nor->program_opcode, buf, (u32)to, len);
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
@ -480,7 +481,12 @@ static void stm32_qspi_unprep(struct spi_nor *nor, enum spi_nor_ops ops)
|
||||
static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
|
||||
struct device_node *np)
|
||||
{
|
||||
u32 width, flash_read, presc, cs_num, max_rate = 0;
|
||||
struct spi_nor_hwcaps hwcaps = {
|
||||
.mask = SNOR_HWCAPS_READ |
|
||||
SNOR_HWCAPS_READ_FAST |
|
||||
SNOR_HWCAPS_PP,
|
||||
};
|
||||
u32 width, presc, cs_num, max_rate = 0;
|
||||
struct stm32_qspi_flash *flash;
|
||||
struct mtd_info *mtd;
|
||||
int ret;
|
||||
@ -499,12 +505,10 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
|
||||
width = 1;
|
||||
|
||||
if (width == 4)
|
||||
flash_read = SPI_NOR_QUAD;
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_4;
|
||||
else if (width == 2)
|
||||
flash_read = SPI_NOR_DUAL;
|
||||
else if (width == 1)
|
||||
flash_read = SPI_NOR_NORMAL;
|
||||
else
|
||||
hwcaps.mask |= SNOR_HWCAPS_READ_1_1_2;
|
||||
else if (width != 1)
|
||||
return -EINVAL;
|
||||
|
||||
flash = &qspi->flash[cs_num];
|
||||
@ -539,7 +543,7 @@ static int stm32_qspi_flash_setup(struct stm32_qspi *qspi,
|
||||
*/
|
||||
flash->fsize = FSIZE_VAL(SZ_1K);
|
||||
|
||||
ret = spi_nor_scan(&flash->nor, NULL, flash_read);
|
||||
ret = spi_nor_scan(&flash->nor, NULL, &hwcaps);
|
||||
if (ret) {
|
||||
dev_err(qspi->dev, "device scan failed\n");
|
||||
return ret;
|
||||
|
@ -73,6 +73,15 @@
|
||||
#define SPINOR_OP_BE_32K_4B 0x5c /* Erase 32KiB block */
|
||||
#define SPINOR_OP_SE_4B 0xdc /* Sector erase (usually 64KiB) */
|
||||
|
||||
/* Double Transfer Rate opcodes - defined in JEDEC JESD216B. */
|
||||
#define SPINOR_OP_READ_1_1_1_DTR 0x0d
|
||||
#define SPINOR_OP_READ_1_2_2_DTR 0xbd
|
||||
#define SPINOR_OP_READ_1_4_4_DTR 0xed
|
||||
|
||||
#define SPINOR_OP_READ_1_1_1_DTR_4B 0x0e
|
||||
#define SPINOR_OP_READ_1_2_2_DTR_4B 0xbe
|
||||
#define SPINOR_OP_READ_1_4_4_DTR_4B 0xee
|
||||
|
||||
/* Used for SST flashes only. */
|
||||
#define SPINOR_OP_BP 0x02 /* Byte program */
|
||||
#define SPINOR_OP_WRDI 0x04 /* Write disable */
|
||||
@ -119,13 +128,81 @@
|
||||
/* Configuration Register bits. */
|
||||
#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */
|
||||
|
||||
enum read_mode {
|
||||
SPI_NOR_NORMAL = 0,
|
||||
SPI_NOR_FAST,
|
||||
SPI_NOR_DUAL,
|
||||
SPI_NOR_QUAD,
|
||||
/* Supported SPI protocols */
|
||||
#define SNOR_PROTO_INST_MASK GENMASK(23, 16)
|
||||
#define SNOR_PROTO_INST_SHIFT 16
|
||||
#define SNOR_PROTO_INST(_nbits) \
|
||||
((((unsigned long)(_nbits)) << SNOR_PROTO_INST_SHIFT) & \
|
||||
SNOR_PROTO_INST_MASK)
|
||||
|
||||
#define SNOR_PROTO_ADDR_MASK GENMASK(15, 8)
|
||||
#define SNOR_PROTO_ADDR_SHIFT 8
|
||||
#define SNOR_PROTO_ADDR(_nbits) \
|
||||
((((unsigned long)(_nbits)) << SNOR_PROTO_ADDR_SHIFT) & \
|
||||
SNOR_PROTO_ADDR_MASK)
|
||||
|
||||
#define SNOR_PROTO_DATA_MASK GENMASK(7, 0)
|
||||
#define SNOR_PROTO_DATA_SHIFT 0
|
||||
#define SNOR_PROTO_DATA(_nbits) \
|
||||
((((unsigned long)(_nbits)) << SNOR_PROTO_DATA_SHIFT) & \
|
||||
SNOR_PROTO_DATA_MASK)
|
||||
|
||||
#define SNOR_PROTO_IS_DTR BIT(24) /* Double Transfer Rate */
|
||||
|
||||
#define SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits) \
|
||||
(SNOR_PROTO_INST(_inst_nbits) | \
|
||||
SNOR_PROTO_ADDR(_addr_nbits) | \
|
||||
SNOR_PROTO_DATA(_data_nbits))
|
||||
#define SNOR_PROTO_DTR(_inst_nbits, _addr_nbits, _data_nbits) \
|
||||
(SNOR_PROTO_IS_DTR | \
|
||||
SNOR_PROTO_STR(_inst_nbits, _addr_nbits, _data_nbits))
|
||||
|
||||
enum spi_nor_protocol {
|
||||
SNOR_PROTO_1_1_1 = SNOR_PROTO_STR(1, 1, 1),
|
||||
SNOR_PROTO_1_1_2 = SNOR_PROTO_STR(1, 1, 2),
|
||||
SNOR_PROTO_1_1_4 = SNOR_PROTO_STR(1, 1, 4),
|
||||
SNOR_PROTO_1_1_8 = SNOR_PROTO_STR(1, 1, 8),
|
||||
SNOR_PROTO_1_2_2 = SNOR_PROTO_STR(1, 2, 2),
|
||||
SNOR_PROTO_1_4_4 = SNOR_PROTO_STR(1, 4, 4),
|
||||
SNOR_PROTO_1_8_8 = SNOR_PROTO_STR(1, 8, 8),
|
||||
SNOR_PROTO_2_2_2 = SNOR_PROTO_STR(2, 2, 2),
|
||||
SNOR_PROTO_4_4_4 = SNOR_PROTO_STR(4, 4, 4),
|
||||
SNOR_PROTO_8_8_8 = SNOR_PROTO_STR(8, 8, 8),
|
||||
|
||||
SNOR_PROTO_1_1_1_DTR = SNOR_PROTO_DTR(1, 1, 1),
|
||||
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),
|
||||
};
|
||||
|
||||
static inline bool spi_nor_protocol_is_dtr(enum spi_nor_protocol proto)
|
||||
{
|
||||
return !!(proto & SNOR_PROTO_IS_DTR);
|
||||
}
|
||||
|
||||
static inline u8 spi_nor_get_protocol_inst_nbits(enum spi_nor_protocol proto)
|
||||
{
|
||||
return ((unsigned long)(proto & SNOR_PROTO_INST_MASK)) >>
|
||||
SNOR_PROTO_INST_SHIFT;
|
||||
}
|
||||
|
||||
static inline u8 spi_nor_get_protocol_addr_nbits(enum spi_nor_protocol proto)
|
||||
{
|
||||
return ((unsigned long)(proto & SNOR_PROTO_ADDR_MASK)) >>
|
||||
SNOR_PROTO_ADDR_SHIFT;
|
||||
}
|
||||
|
||||
static inline u8 spi_nor_get_protocol_data_nbits(enum spi_nor_protocol proto)
|
||||
{
|
||||
return ((unsigned long)(proto & SNOR_PROTO_DATA_MASK)) >>
|
||||
SNOR_PROTO_DATA_SHIFT;
|
||||
}
|
||||
|
||||
static inline u8 spi_nor_get_protocol_width(enum spi_nor_protocol proto)
|
||||
{
|
||||
return spi_nor_get_protocol_data_nbits(proto);
|
||||
}
|
||||
|
||||
#define SPI_NOR_MAX_CMD_SIZE 8
|
||||
enum spi_nor_ops {
|
||||
SPI_NOR_OPS_READ = 0,
|
||||
@ -154,9 +231,11 @@ enum spi_nor_option_flags {
|
||||
* @read_opcode: the read opcode
|
||||
* @read_dummy: the dummy needed by the read operation
|
||||
* @program_opcode: the program opcode
|
||||
* @flash_read: the mode of the read
|
||||
* @sst_write_second: used by the SST write operation
|
||||
* @flags: flag options for the current SPI-NOR (SNOR_F_*)
|
||||
* @read_proto: the SPI protocol for read operations
|
||||
* @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
|
||||
* @prepare: [OPTIONAL] do some preparations for the
|
||||
* read/write/erase/lock/unlock operations
|
||||
@ -185,7 +264,9 @@ struct spi_nor {
|
||||
u8 read_opcode;
|
||||
u8 read_dummy;
|
||||
u8 program_opcode;
|
||||
enum read_mode flash_read;
|
||||
enum spi_nor_protocol read_proto;
|
||||
enum spi_nor_protocol write_proto;
|
||||
enum spi_nor_protocol reg_proto;
|
||||
bool sst_write_second;
|
||||
u32 flags;
|
||||
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
|
||||
@ -219,11 +300,72 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
|
||||
return mtd_get_of_node(&nor->mtd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @name: the chip type name
|
||||
* @mode: the read mode supported by the driver
|
||||
* @hwcaps: the hardware capabilities supported by the controller driver
|
||||
*
|
||||
* The drivers can use this fuction to scan the SPI NOR.
|
||||
* In the scanning, it will try to get all the necessary information to
|
||||
@ -233,6 +375,7 @@ static inline struct device_node *spi_nor_get_flash_node(struct spi_nor *nor)
|
||||
*
|
||||
* Return: 0 for success, others for failure.
|
||||
*/
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name, enum read_mode mode);
|
||||
int spi_nor_scan(struct spi_nor *nor, const char *name,
|
||||
const struct spi_nor_hwcaps *hwcaps);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user