Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Jonas Gorski and committed by
Mark Brown
0f698d74 458800ea

+41 -23
+41 -23
drivers/spi/spi-bcm63xx-hsspi.c
··· 142 142 u32 wait_mode; 143 143 u32 xfer_mode; 144 144 u32 prepend_cnt; 145 + u32 md_start; 145 146 u8 *prepend_buf; 146 147 }; 147 148 ··· 269 268 { 270 269 271 270 struct bcm63xx_hsspi *bs = spi_controller_get_devdata(host); 272 - bool tx_only = false; 271 + bool tx_only = false, multidata = false; 273 272 struct spi_transfer *t; 274 273 275 274 /* 276 275 * Multiple transfers within a message may be combined into one transfer 277 276 * to the controller using its prepend feature. A SPI message is prependable 278 277 * only if the following are all true: 279 - * 1. One or more half duplex write transfer in single bit mode 280 - * 2. Optional full duplex read/write at the end 281 - * 3. No delay and cs_change between transfers 278 + * 1. One or more half duplex write transfers at the start 279 + * 2. Optional switch from single to dual bit within the write transfers 280 + * 3. Optional full duplex read/write at the end if all single bit 281 + * 4. No delay and cs_change between transfers 282 282 */ 283 283 bs->prepend_cnt = 0; 284 + bs->md_start = 0; 284 285 list_for_each_entry(t, &msg->transfers, transfer_list) { 285 286 if ((spi_delay_to_ns(&t->delay, t) > 0) || t->cs_change) { 286 287 bcm63xx_prepend_printk_on_checkfail(bs, ··· 300 297 return false; 301 298 } 302 299 303 - if (t->tx_nbits > SPI_NBITS_SINGLE && 304 - !list_is_last(&t->transfer_list, &msg->transfers)) { 300 + if (t->tx_nbits == SPI_NBITS_SINGLE && 301 + !list_is_last(&t->transfer_list, &msg->transfers) && 302 + multidata) { 305 303 bcm63xx_prepend_printk_on_checkfail(bs, 306 - "multi-bit prepend buf not supported!\n"); 304 + "single-bit after multi-bit not supported!\n"); 307 305 return false; 308 306 } 309 307 310 - if (t->tx_nbits == SPI_NBITS_SINGLE) { 311 - memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len); 312 - bs->prepend_cnt += t->len; 313 - } 308 + if (t->tx_nbits > SPI_NBITS_SINGLE) 309 + multidata = true; 310 + 311 + memcpy(bs->prepend_buf + bs->prepend_cnt, t->tx_buf, t->len); 312 + bs->prepend_cnt += t->len; 313 + 314 + if (t->tx_nbits == SPI_NBITS_SINGLE) 315 + bs->md_start += t->len; 316 + 314 317 } else { 315 318 if (!list_is_last(&t->transfer_list, &msg->transfers)) { 316 319 bcm63xx_prepend_printk_on_checkfail(bs, 317 320 "rx/tx_rx transfer not supported when it is not last one!\n"); 321 + return false; 322 + } 323 + 324 + if (t->rx_buf && t->rx_nbits == SPI_NBITS_SINGLE && 325 + multidata) { 326 + bcm63xx_prepend_printk_on_checkfail(bs, 327 + "single-bit after multi-bit not supported!\n"); 318 328 return false; 319 329 } 320 330 } ··· 335 319 if (list_is_last(&t->transfer_list, &msg->transfers)) { 336 320 memcpy(t_prepend, t, sizeof(struct spi_transfer)); 337 321 338 - if (tx_only && t->tx_nbits == SPI_NBITS_SINGLE) { 322 + if (tx_only) { 339 323 /* 340 - * if the last one is also a single bit tx only transfer, merge 324 + * if the last one is also a tx only transfer, merge 341 325 * all of them into one single tx transfer 342 326 */ 343 327 t_prepend->len = bs->prepend_cnt; ··· 345 329 bs->prepend_cnt = 0; 346 330 } else { 347 331 /* 348 - * if the last one is not a tx only transfer or dual tx xfer, all 332 + * if the last one is not a tx only transfer, all 349 333 * the previous transfers are sent through prepend bytes and 350 334 * make sure it does not exceed the max prepend len 351 335 */ ··· 354 338 "exceed max prepend len, abort prepending transfers!\n"); 355 339 return false; 356 340 } 341 + } 342 + /* 343 + * If switching from single-bit to multi-bit, make sure 344 + * the start offset does not exceed the maximum 345 + */ 346 + if (multidata && bs->md_start > HSSPI_MAX_PREPEND_LEN) { 347 + bcm63xx_prepend_printk_on_checkfail(bs, 348 + "exceed max multi-bit offset, abort prepending transfers!\n"); 349 + return false; 357 350 } 358 351 } 359 352 } ··· 406 381 407 382 if (t->rx_nbits == SPI_NBITS_DUAL) { 408 383 reg |= 1 << MODE_CTRL_MULTIDATA_RD_SIZE_SHIFT; 409 - reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT; 384 + reg |= bs->md_start << MODE_CTRL_MULTIDATA_RD_STRT_SHIFT; 410 385 } 411 386 if (t->tx_nbits == SPI_NBITS_DUAL) { 412 387 reg |= 1 << MODE_CTRL_MULTIDATA_WR_SIZE_SHIFT; 413 - reg |= bs->prepend_cnt << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT; 388 + reg |= bs->md_start << MODE_CTRL_MULTIDATA_WR_STRT_SHIFT; 414 389 } 415 390 } 416 391 ··· 715 690 const struct spi_mem_op *op) 716 691 { 717 692 if (!spi_mem_default_supports_op(mem, op)) 718 - return false; 719 - 720 - /* Controller doesn't support spi mem dual io mode */ 721 - if ((op->cmd.opcode == SPINOR_OP_READ_1_2_2) || 722 - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_4B) || 723 - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR) || 724 - (op->cmd.opcode == SPINOR_OP_READ_1_2_2_DTR_4B)) 725 693 return false; 726 694 727 695 return true;