Skip to content

Commit cff3811

Browse files
robhancocksednashif
authored andcommitted
drivers: spi_xlnx_axi_quadspi: add STARTUP block workaround support
Add support for a workaround required when using the Xilinx Quad SPI core with the USE_STARTUP option, which routes the core's SPI clock to the FPGA's dedicated CCLK pin rather than a normal I/O pin. This is typically used when interfacing with the same SPI flash device used for FPGA configuration. In this mode, the SPI core cannot actually take control of the CCLK pin until a few clock cycles are issued, which would break the first transfer issued by the core. This workaround applies a dummy command to the connected device to ensure that the clock signal is in the correct state for subsequent commands. See Xilinx answer record at: https://support.xilinx.com/s/article/52626?language=en_US Signed-off-by: Robert Hancock <[email protected]>
1 parent f61950a commit cff3811

File tree

2 files changed

+82
-4
lines changed

2 files changed

+82
-4
lines changed

drivers/spi/spi_xlnx_axi_quadspi.c

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ struct xlnx_quadspi_config {
8686
void (*irq_config_func)(const struct device *dev);
8787
uint8_t num_ss_bits;
8888
uint8_t num_xfer_bytes;
89+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(xlnx_startup_block)
90+
bool startup_block;
91+
#endif
8992
};
9093

9194
struct xlnx_quadspi_data {
@@ -439,6 +442,54 @@ static void xlnx_quadspi_isr(const struct device *dev)
439442
}
440443
}
441444

445+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(xlnx_startup_block)
446+
static int xlnx_quadspi_startup_block_workaround(const struct device *dev)
447+
{
448+
const struct xlnx_quadspi_config *config = dev->config;
449+
uint32_t spissr = BIT_MASK(config->num_ss_bits);
450+
uint32_t spicr;
451+
452+
/**
453+
* See https://support.xilinx.com/s/article/52626?language=en_US
454+
* Up to 3 clock cycles must be issued before the output clock signal
455+
* is passed to the output CCLK pin from the SPI core.
456+
* Use JEDEC READ ID as dummy command to chip select 0.
457+
*/
458+
spissr &= ~BIT(0);
459+
xlnx_quadspi_write32(dev, spissr, SPISSR_OFFSET);
460+
461+
xlnx_quadspi_write32(dev, 0x9F, SPI_DTR_OFFSET);
462+
xlnx_quadspi_write32(dev, 0, SPI_DTR_OFFSET);
463+
xlnx_quadspi_write32(dev, 0, SPI_DTR_OFFSET);
464+
465+
spicr = SPICR_MANUAL_SS | SPICR_MASTER | SPICR_SPE;
466+
xlnx_quadspi_write32(dev, spicr, SPICR_OFFSET);
467+
468+
for (int i = 0;
469+
i < 10 && (xlnx_quadspi_read32(dev, SPISR_OFFSET) & SPISR_TX_EMPTY) == 0; i++) {
470+
k_msleep(1);
471+
}
472+
if ((xlnx_quadspi_read32(dev, SPISR_OFFSET) & SPISR_TX_EMPTY) == 0) {
473+
LOG_ERR("timeout waiting for TX_EMPTY");
474+
return -EIO;
475+
}
476+
spicr |= SPICR_MASTER_XFER_INH;
477+
xlnx_quadspi_write32(dev, spicr, SPICR_OFFSET);
478+
479+
while ((xlnx_quadspi_read32(dev, SPISR_OFFSET) & SPISR_RX_EMPTY) == 0) {
480+
xlnx_quadspi_read32(dev, SPI_DRR_OFFSET);
481+
}
482+
483+
spissr = BIT_MASK(config->num_ss_bits);
484+
xlnx_quadspi_write32(dev, spissr, SPISSR_OFFSET);
485+
486+
/* Reset controller to clean up */
487+
xlnx_quadspi_write32(dev, SRR_SOFTRESET_MAGIC, SRR_OFFSET);
488+
489+
return 0;
490+
}
491+
#endif
492+
442493
static int xlnx_quadspi_init(const struct device *dev)
443494
{
444495
int err;
@@ -450,17 +501,26 @@ static int xlnx_quadspi_init(const struct device *dev)
450501

451502
config->irq_config_func(dev);
452503

453-
/* Enable DTR Empty interrupt */
454-
xlnx_quadspi_write32(dev, IPIXR_DTR_EMPTY, IPIER_OFFSET);
455-
xlnx_quadspi_write32(dev, DGIER_GIE, DGIER_OFFSET);
456-
457504
err = spi_context_cs_configure_all(&data->ctx);
458505
if (err < 0) {
459506
return err;
460507
}
461508

462509
spi_context_unlock_unconditionally(&data->ctx);
463510

511+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(xlnx_startup_block)
512+
if (config->startup_block) {
513+
err = xlnx_quadspi_startup_block_workaround(dev);
514+
if (err < 0) {
515+
return err;
516+
}
517+
}
518+
#endif
519+
520+
/* Enable DTR Empty interrupt */
521+
xlnx_quadspi_write32(dev, IPIXR_DTR_EMPTY, IPIER_OFFSET);
522+
xlnx_quadspi_write32(dev, DGIER_GIE, DGIER_OFFSET);
523+
464524
return 0;
465525
}
466526

@@ -471,6 +531,11 @@ static const struct spi_driver_api xlnx_quadspi_driver_api = {
471531
#endif /* CONFIG_SPI_ASYNC */
472532
.release = xlnx_quadspi_release,
473533
};
534+
#if DT_ANY_INST_HAS_PROP_STATUS_OKAY(xlnx_startup_block)
535+
#define STARTUP_BLOCK_INIT(n) .startup_block = DT_INST_PROP(n, xlnx_startup_block),
536+
#else
537+
#define STARTUP_BLOCK_INIT(n)
538+
#endif
474539

475540
#define XLNX_QUADSPI_INIT(n) \
476541
static void xlnx_quadspi_config_func_##n(const struct device *dev); \
@@ -481,6 +546,7 @@ static const struct spi_driver_api xlnx_quadspi_driver_api = {
481546
.num_ss_bits = DT_INST_PROP(n, xlnx_num_ss_bits), \
482547
.num_xfer_bytes = \
483548
DT_INST_PROP(n, xlnx_num_transfer_bits) / 8, \
549+
STARTUP_BLOCK_INIT(n) \
484550
}; \
485551
\
486552
static struct xlnx_quadspi_data xlnx_quadspi_data_##n = { \

dts/bindings/spi/xlnx,xps-spi-2.00.a.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,15 @@ properties:
3737
- 32
3838
description: |
3939
Number of bits per transfer
40+
41+
xlnx,startup-block:
42+
type: boolean
43+
description: |
44+
Indicates the core is instantiated with the STARTUP block option, as is
45+
typically used when interfacing with the FPGA's configuration flash
46+
device. In this configuration the SPI clock is routed through the
47+
STARTUP block rather than normal signal routing.
48+
In this case, a workaround is required to issue a dummy
49+
transaction to the SPI flash device to ensure the STARTUP block is
50+
disengaged and allow the SPI core to control the CCLK line properly.
51+
The dummy READ_ID transaction will be issued to chip select 0.

0 commit comments

Comments
 (0)