From: Mikhail Kshevetskiy Date: Sun, 9 Nov 2025 07:06:48 +0000 (+0300) Subject: spi: airoha: add support of dual/quad wires spi modes to exec_op() handler X-Git-Tag: v2026.01-rc3~8^2~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ba04299ff78a72ad7088fb5688caed68efcfe58d;p=thirdparty%2Fu-boot.git spi: airoha: add support of dual/quad wires spi modes to exec_op() handler Booting without this patch and disabled dirmap support results in [ 2.980719] spi-nand spi0.0: Micron SPI NAND was found. [ 2.986040] spi-nand spi0.0: 256 MiB, block size: 128 KiB, page size: 2048, OOB size: 128 [ 2.994709] 2 fixed-partitions partitions found on MTD device spi0.0 [ 3.001075] Creating 2 MTD partitions on "spi0.0": [ 3.005862] 0x000000000000-0x000000020000 : "bl2" [ 3.011272] 0x000000020000-0x000010000000 : "ubi" ... [ 6.195594] ubi0: attaching mtd1 [ 13.338398] ubi0: scanning is finished [ 13.342188] ubi0 error: ubi_read_volume_table: the layout volume was not found [ 13.349784] ubi0 error: ubi_attach_mtd_dev: failed to attach mtd1, error -22 [ 13.356897] UBI error: cannot attach mtd1 If dirmap is disabled or not supported in the spi driver, the dirmap requests will be executed via exec_op() handler. Thus, if the hardware supports dual/quad spi modes, then corresponding requests will be sent to exec_op() handler. Current driver does not support such requests, so error is arrised. As result the flash can't be read/write. This patch adds support of dual and quad wires spi modes to exec_op() handler. Signed-off-by: Mikhail Kshevetskiy --- diff --git a/drivers/spi/airoha_snfi_spi.c b/drivers/spi/airoha_snfi_spi.c index 4eb01038404..7cd409ba44a 100644 --- a/drivers/spi/airoha_snfi_spi.c +++ b/drivers/spi/airoha_snfi_spi.c @@ -186,6 +186,14 @@ #define SPI_NAND_OP_RESET 0xff #define SPI_NAND_OP_DIE_SELECT 0xc2 +/* SNAND FIFO commands */ +#define SNAND_FIFO_TX_BUSWIDTH_SINGLE 0x08 +#define SNAND_FIFO_TX_BUSWIDTH_DUAL 0x09 +#define SNAND_FIFO_TX_BUSWIDTH_QUAD 0x0a +#define SNAND_FIFO_RX_BUSWIDTH_SINGLE 0x0c +#define SNAND_FIFO_RX_BUSWIDTH_DUAL 0x0e +#define SNAND_FIFO_RX_BUSWIDTH_QUAD 0x0f + #define SPI_NAND_CACHE_SIZE (SZ_4K + SZ_256) #define SPI_MAX_TRANSFER_SIZE 511 @@ -380,10 +388,26 @@ static int airoha_snand_set_mode(struct airoha_snand_priv *priv, return regmap_write(priv->regmap_ctrl, REG_SPI_CTRL_DUMMY, 0); } -static int airoha_snand_write_data(struct airoha_snand_priv *priv, u8 cmd, - const u8 *data, int len) +static int airoha_snand_write_data(struct airoha_snand_priv *priv, + const u8 *data, int len, int buswidth) { int i, data_len; + u8 cmd; + + switch (buswidth) { + case 0: + case 1: + cmd = SNAND_FIFO_TX_BUSWIDTH_SINGLE; + break; + case 2: + cmd = SNAND_FIFO_TX_BUSWIDTH_DUAL; + break; + case 4: + cmd = SNAND_FIFO_TX_BUSWIDTH_QUAD; + break; + default: + return -EINVAL; + } for (i = 0; i < len; i += data_len) { int err; @@ -402,16 +426,32 @@ static int airoha_snand_write_data(struct airoha_snand_priv *priv, u8 cmd, return 0; } -static int airoha_snand_read_data(struct airoha_snand_priv *priv, u8 *data, - int len) +static int airoha_snand_read_data(struct airoha_snand_priv *priv, + u8 *data, int len, int buswidth) { int i, data_len; + u8 cmd; + + switch (buswidth) { + case 0: + case 1: + cmd = SNAND_FIFO_RX_BUSWIDTH_SINGLE; + break; + case 2: + cmd = SNAND_FIFO_RX_BUSWIDTH_DUAL; + break; + case 4: + cmd = SNAND_FIFO_RX_BUSWIDTH_QUAD; + break; + default: + return -EINVAL; + } for (i = 0; i < len; i += data_len) { int err; data_len = min(len - i, SPI_MAX_TRANSFER_SIZE); - err = airoha_snand_set_fifo_op(priv, 0xc, data_len); + err = airoha_snand_set_fifo_op(priv, cmd, data_len); if (err) return err; @@ -525,6 +565,38 @@ static int airoha_snand_nfi_config(struct airoha_snand_priv *priv) SPI_NFI_CUS_SEC_SIZE, val); } +static bool airoha_snand_is_page_ops(const struct spi_mem_op *op) +{ + if (op->addr.nbytes != 2) + return false; + + if (op->addr.buswidth != 1 && op->addr.buswidth != 2 && + op->addr.buswidth != 4) + return false; + + switch (op->data.dir) { + case SPI_MEM_DATA_IN: + if (op->dummy.nbytes * BITS_PER_BYTE / op->dummy.buswidth > 0xf) + return false; + + /* quad in / quad out */ + if (op->addr.buswidth == 4) + return op->data.buswidth == 4; + + if (op->addr.buswidth == 2) + return op->data.buswidth == 2; + + /* standard spi */ + return op->data.buswidth == 4 || op->data.buswidth == 2 || + op->data.buswidth == 1; + case SPI_MEM_DATA_OUT: + return !op->dummy.nbytes && op->addr.buswidth == 1 && + (op->data.buswidth == 4 || op->data.buswidth == 1); + default: + return false; + } +} + static bool airoha_snand_supports_op(struct spi_slave *slave, const struct spi_mem_op *op) { @@ -534,6 +606,9 @@ static bool airoha_snand_supports_op(struct spi_slave *slave, if (op->cmd.buswidth != 1) return false; + if (airoha_snand_is_page_ops(op)) + return true; + return (!op->addr.nbytes || op->addr.buswidth == 1) && (!op->dummy.nbytes || op->dummy.buswidth == 1) && (!op->data.nbytes || op->data.buswidth == 1); @@ -542,13 +617,29 @@ static bool airoha_snand_supports_op(struct spi_slave *slave, static int airoha_snand_exec_op(struct spi_slave *slave, const struct spi_mem_op *op) { - u8 data[8], cmd, opcode = op->cmd.opcode; struct udevice *bus = slave->dev->parent; struct airoha_snand_priv *priv; + int op_len, addr_len, dummy_len; + u8 buf[20], *data; int i, err; priv = dev_get_priv(bus); + op_len = op->cmd.nbytes; + addr_len = op->addr.nbytes; + dummy_len = op->dummy.nbytes; + + if (op_len + dummy_len + addr_len > sizeof(buf)) + return -EIO; + + data = buf; + for (i = 0; i < op_len; i++) + *data++ = op->cmd.opcode >> (8 * (op_len - i - 1)); + for (i = 0; i < addr_len; i++) + *data++ = op->addr.val >> (8 * (addr_len - i - 1)); + for (i = 0; i < dummy_len; i++) + *data++ = 0xff; + /* switch to manual mode */ err = airoha_snand_set_mode(priv, SPI_MODE_MANUAL); if (err < 0) @@ -559,40 +650,40 @@ static int airoha_snand_exec_op(struct spi_slave *slave, return err; /* opcode */ - err = airoha_snand_write_data(priv, 0x8, &opcode, sizeof(opcode)); + data = buf; + err = airoha_snand_write_data(priv, data, op_len, + op->cmd.buswidth); if (err) return err; /* addr part */ - cmd = opcode == SPI_NAND_OP_GET_FEATURE ? 0x11 : 0x8; - put_unaligned_be64(op->addr.val, data); - - for (i = ARRAY_SIZE(data) - op->addr.nbytes; - i < ARRAY_SIZE(data); i++) { - err = airoha_snand_write_data(priv, cmd, &data[i], - sizeof(data[0])); + data += op_len; + if (addr_len) { + err = airoha_snand_write_data(priv, data, addr_len, + op->addr.buswidth); if (err) return err; } /* dummy */ - data[0] = 0xff; - for (i = 0; i < op->dummy.nbytes; i++) { - err = airoha_snand_write_data(priv, 0x8, &data[0], - sizeof(data[0])); + data += addr_len; + if (dummy_len) { + err = airoha_snand_write_data(priv, data, dummy_len, + op->dummy.buswidth); if (err) return err; } /* data */ - if (op->data.dir == SPI_MEM_DATA_IN) { - err = airoha_snand_read_data(priv, op->data.buf.in, - op->data.nbytes); - if (err) - return err; - } else { - err = airoha_snand_write_data(priv, 0x8, op->data.buf.out, - op->data.nbytes); + if (op->data.nbytes) { + if (op->data.dir == SPI_MEM_DATA_IN) + err = airoha_snand_read_data(priv, op->data.buf.in, + op->data.nbytes, + op->data.buswidth); + else + err = airoha_snand_write_data(priv, op->data.buf.out, + op->data.nbytes, + op->data.buswidth); if (err) return err; }