diff options
| author | Jonas Gorski <jonas.gorski@gmail.com> | 2025-12-18 00:10:26 +0300 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2025-12-23 13:53:50 +0300 |
| commit | 0f698d742f628d02ab2a222f8cf5f793443865d0 (patch) | |
| tree | f9f68eb3fae2bf2d2709e6b7824e25384825db65 | |
| parent | 458800ea171b00d9c2af7c8cbf2819fd85af1aff (diff) | |
| download | linux-0f698d742f628d02ab2a222f8cf5f793443865d0.tar.xz | |
spi: bcm63xx-hsspi: add support for 1-2-2 read ops
Add support for 1-2-2 read ops by separately calculating the switch from
single-bit to multi-bit, and then switching within the prepend data.
This allows us to support single-bit write followed by multi-bit write
followed by multi-bit read, and we do not need to reject 1-2-2 read
operations anymore.
Tested on BCM963268BU_P300 with custom fixup to allow 1-2-2 on the
non-SDFP capable s25fl129p1 attached (which it actually supports):
root@OpenWrt:~# cat /sys/kernel/debug/spi-nor/spi1.0/params
name s25fl129p1
id 01 20 18 4d 01 01
size 16.0 MiB
write size 1
page size 256
address nbytes 3
flags HAS_16BIT_SR | NO_READ_CR
opcodes
read 0xbb
dummy cycles 4
erase 0xd8
program 0x02
8D extension none
protocols
read 1S-2S-2S
write 1S-1S-1S
register 1S-1S-1S
Reading from flash is still working as expected:
[ 1.070000] parser_imagetag: rootfs: CFE image tag found at 0x0 with version 6, board type 963168VX
[ 1.080000] parser_imagetag: Partition 0 is rootfs offset 100 and length 665000
[ 1.090000] parser_imagetag: Partition 1 is kernel offset 665100 and length 136fa1
[ 1.100000] parser_imagetag: Spare partition is offset 7b0000 and length 30000
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
Acked-by: William Zhang <william.zhang@broadcom.com>
Tested-by: David Regan <dregan@broadcom.com>
Link: https://patch.msgid.link/20251217211026.173946-1-jonas.gorski@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
| -rw-r--r-- | drivers/spi/spi-bcm63xx-hsspi.c | 64 |
1 files changed, 41 insertions, 23 deletions
diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index 18261cbd413b..d9e972ef2abd 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -142,6 +142,7 @@ struct bcm63xx_hsspi { u32 wait_mode; u32 xfer_mode; u32 prepend_cnt; + u32 md_start; u8 *prepend_buf; }; @@ -268,18 +269,20 @@ static bool bcm63xx_prepare_prepend_transfer(struct spi_controller *host, { struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host); - bool tx_only = false; + bool tx_only = false, multidata = false; struct spi_transfer *t; /* * Multiple transfers within a message may be combined into one transfer * to the controller using its prepend feature. A SPI message is prependable * only if the following are all true: - * 1. One or more half duplex write transfer in single bit mode - * 2. Optional full duplex read/write at the end - * 3. No delay and cs_change between transfers + * 1. One or more half duplex write transfers at the start + * 2. Optional switch from single to dual bit within the write transfers + * 3. Optional full duplex read/write at the end if all single bit + * 4. No delay and cs_change between transfers */ bs->prepend_cnt = 0; + bs->md_start = 0; list_for_each_entry(t, &msg->transfers, transfer_list) { if ((spi_delay_to_ns(&t->delay, t) > 0) || t->cs_change) { bcm63xx_prepend_printk_on_checkfail(bs, @@ -297,31 +300,44 @@ static bool bcm63xx_prepare_prepend_transfer(struct spi_controller *host, return false; } - if (t->tx_nbits > SPI_NBITS_SINGLE && - !list_is_last(&t->transfer_list, &msg->transfers)) { + if (t->tx_nbits == SPI_NBITS_SINGLE && + !list_is_last(&t->transfer_list, &msg->transfers) && + multidata) { bcm63xx_prepend_printk_on_checkfail(bs, - "multi-bit prepend buf not supported!\n"); + "single-bit after multi-bit not supported!\n"); return false; } - if (t->tx_nbits == SPI_NBITS_SINGLE) { - memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len); - bs->prepend_cnt += t->len; - } + if (t->tx_nbits > SPI_NBITS_SINGLE) + multidata = true; + + memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len); + bs->prepend_cnt += t->len; + + if (t->tx_nbits == SPI_NBITS_SINGLE) + bs->md_start += t->len; + } else { if (!list_is_last(&t->transfer_list, &msg->transfers)) { bcm63xx_prepend_printk_on_checkfail(bs, "rx/tx_rx transfer not supported when it is not last one!\n"); return false; } + + if (t->rx_buf && t->rx_nbits == SPI_NBITS_SINGLE && + multidata) { + bcm63xx_prepend_printk_on_checkfail(bs, + "single-bit after multi-bit not supported!\n"); + return false; + } } if (list_is_last(&t->transfer_list, &msg->transfers)) { memcpy(t_prepend, t, sizeof(struct spi_transfer)); - if (tx_only && t->tx_nbits == SPI_NBITS_SINGLE) { + if (tx_only) { /* - * if the last one is also a single bit tx only transfer, merge + * if the last one is also a tx only transfer, merge * all of them into one single tx transfer */ t_prepend->len = bs->prepend_cnt; @@ -329,7 +345,7 @@ static bool bcm63xx_prepare_prepend_transfer(struct spi_controller *host, bs->prepend_cnt = 0; } else { /* - * if the last one is not a tx only transfer or dual tx xfer, all + * if the last one is not a tx only transfer, all * the previous transfers are sent through prepend bytes and * make sure it does not exceed the max prepend len */ @@ -339,6 +355,15 @@ static bool bcm63xx_prepare_prepend_transfer(struct spi_controller *host, return false; } } + /* + * If switching from single-bit to multi-bit, make sure + * the start offset does not exceed the maximum + */ + if (multidata && bs->md_start > HSSPI_MAX_PREPEND_LEN) { + bcm63xx_prepend_printk_on_checkfail(bs, + "exceed max multi-bit offset, abort prepending transfers!\n"); + return false; + } } } @@ -381,11 +406,11 @@ static int bcm63xx_hsspi_do_prepend_txrx(struct spi_device *spi, if (t->rx_nbits == SPI_NBITS_DUAL) { reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; - reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT; + reg |= bs->md_start << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT; } if (t->tx_nbits == SPI_NBITS_DUAL) { reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; - reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT; + reg |= bs->md_start << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT; } } @@ -692,13 +717,6 @@ static bool bcm63xx_hsspi_mem_supports_op(struct spi_mem *mem, if (!spi_mem_default_supports_op(mem, op)) return false; - /* Controller doesn't support spi mem dual io mode */ - if ((op->cmd.opcode == SPINOR_OP_READ_1_2_2) || - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_4B) || - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR) || - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR_4B)) - return false; - return true; } |
