Re: [PATCH v4 00/16] spi: cadence-quadspi: add PHY tuning support

From: Santhosh Kumar K

Date: Thu Jul 16 2026 - 02:05:04 EST


Hello Amit,

Apologies for the delayed response.

On 22/06/26 10:00, Mahapatra, Amit Kumar wrote:
AMD General

Hello Santosh,

-----Original Message-----
From: Santhosh Kumar K <s-k6@xxxxxx>
Sent: Thursday, June 18, 2026 1:07 PM
To: broonie@xxxxxxxxxx; robh@xxxxxxxxxx; krzk+dt@xxxxxxxxxx;
conor+dt@xxxxxxxxxx; miquel.raynal@xxxxxxxxxxx; richard@xxxxxx;
vigneshr@xxxxxx; pratyush@xxxxxxxxxx; mwalle@xxxxxxxxxx;
takahiro.kuwano@xxxxxxxxxxxx
Cc: linux-spi@xxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; linux-
kernel@xxxxxxxxxxxxxxx; linux-mtd@xxxxxxxxxxxxxxxxxxx; praneeth@xxxxxx; u-
kumar1@xxxxxx; a-dutta@xxxxxx; s-k6@xxxxxx
Subject: [PATCH v4 00/16] spi: cadence-quadspi: add PHY tuning support

This series implements PHY tuning support for the Cadence QSPI controller to
enable reliable high-speed operations. Without PHY tuning, controllers use
conservative timing that limits performance. PHY tuning calibrates RX/TX delay lines
to find optimal data capture timing windows, enabling operation up to the controller's
maximum frequency.

Background:
High-speed SPI memory controllers require precise timing calibration for reliable
operation. At higher frequencies, board-to-board variations make fixed timing
parameters inadequate. The Cadence QSPI controller includes a PHY interface with
programmable delay lines (0-127 taps) for RX and TX paths, but these require
runtime calibration to find the valid timing window.

Approach:
Add SDR/DDR PHY tuning algorithms for the Cadence controller:

SDR Mode Tuning (1D search):
- Searches for two consecutive valid RX delay windows
- Selects the larger window and uses its midpoint for maximum margin
- TX delay fixed at maximum (127) as it's less critical in SDR

DDR Mode Tuning (2D search):
- Finds RX boundaries (rxlow/rxhigh) using TX window sweeps
- Finds TX boundaries (txlow/txhigh) at fixed RX positions
- Defines valid region corners and detects gaps via binary search
- Applies temperature compensation for optimal point selection
- Handles single or dual passing regions with different strategies

Thank you for this series. I had a question regarding the Virtual Concat
driver patch series [1]. Now that it has been merged into the kernel and
enables support for multiple flash devices connected in stacked mode-where
each flash device is probed and configured independently-if both flash
parts are required to operate in DDR mode, each device would need to
perform tuning and store its tuning data separately.

Given this, should we consider this use case and adapt the tuning
architecture to support it?

I'd appreciate your thoughts on this.

Good point.

The current implementation already stores the calibration state per
chip select (delay line settings, tuned flags, and operation templates),
so the data model supports independent tuning for each device.

However, the runtime path that enables PHY mode does not restore the
per-CS delay line settings when switching between chip selects. As a
result, consecutive PHY accesses to different devices could end up using
the wrong delay line settings.

This can be addressed with a small change by reading the currently
programmed DLL and reprogramming the PHY configuration registers
whenever they differ from the calibrated values for the selected chip.

Thanks,
Santhosh.


[1] https://lore.kernel.org/all/20260204-mtd-virt-concat-v17-0-5e98239bb55b@xxxxxxxxxxx/


Regards,
Amit


Patch description:
Infrastructure (1-5):
- Patch 1: Add spi-max-post-config-frequency to describe maximum
frequency achievable post controller configuration
- Patch 2: Add spi-phy-pattern-partition phandle for
NOR flash PHY tuning pattern location
- Patch 3: Parse spi-max-post-config-frequency in spi.c; adds
spi_device.post_config_max_speed_hz (0 when not set
keeping all existing DT fully compatible)
- Patch 4: Extend spi_mem_adjust_op_freq() with a bypass: if
op->max_freq equals post_config_max_speed_hz, return
immediately leaving op->max_freq unchanged. All other
ops are capped to max_speed_hz
- Patch 5: Add execute_tuning callback to spi_controller_mem_ops and
spi_mem_execute_tuning() wrapper in SPI-MEM core

Cadence QSPI Implementation (6-12):
- Patch 6: Move cqspi_readdata_capture() earlier (preparatory)
- Patch 7: Add DQS bit to cqspi_readdata_capture() (preparatory)
- Patch 8: Add complete PHY tuning support: DLL management, pattern
verification (NOR via spi-phy-pattern-partition phandle,
NAND via write-to-cache), SDR 1D and DDR 2D search
algorithms with temperature compensation, AM654-specific
execute_tuning entry point;
- Patch 9: Reject 2-byte-address DDR operations via a new
CQSPI_NO_2BYTE_ADDR_PHY_DDR quirk flag to work around
AM654 OSPI erratum i2383
- Patch 10: Refactor direct read path for PHY support (preparatory)
- Patch 11: Enable PHY for direct reads, split the transfer into an
unaligned head, a 16-byte-aligned middle section with PHY
active, and an unaligned tail
- Patch 12: Enable PHY for indirect writes of at least
CQSPI_PHY_MIN_INDIRECT_WRITE_LEN bytes

MTD core (13-16):
- Patch 13: Extract spinand_select_op_variant() into a shared helper
spinand_op_find_best() with a skip_mask
- Patch 14: Negotiate optimal PHY operating point before dirmap
creation
- Patch 15: Extract spi_nor_spimem_get_read_op() helper (preparatory)
- Patch 16: Execute PHY tuning in spi_nor_probe() before creating
dirmaps

Testing:
This series was tested on TI's
AM62Ax SK with OSPI NAND flash and
AM62Px SK with OSPI NOR flash:

Read throughput:
|----------------------------------------|
| | non-PHY | PHY |
|----------------------------------------|
| OSPI NOR (8D) | 37.5 MB/s | 216 MB/s |
|----------------------------------------|
| OSPI NAND (8S) | 9.2 MB/s | 35.1 MB/s |
|----------------------------------------|

Write throughput:
|----------------------------------------|
| | non-PHY | PHY |
|----------------------------------------|
| OSPI NAND (8S) | 6 MB/s | 9.2 MB/s |
|----------------------------------------|

Test log: https://gist.github.com/santhosh21/fe98754e52970287eb9011154100b62d
Repo: https://github.com/santhosh21/linux/commits/phy_tuning_v4/

Changes in v4:
- Add spi-max-post-config-frequency instead of extending spi-max-frequency
to accept an optional second value
- Replace spi_mem_apply_base_freq_cap() with spi_mem_adjust_op_freq()
extension
- For SPI NOR/NAND, execute PHY tuning before the dirmap creation
- For SPI NAND, execute PHY tuning across all operation variants available,
perform duration comparison, and select the best resulting variant
by taking controller-specific restrictions into account
- Move i2383 check from cqspi_supports_mem_op() to
cqspi_am654_ospi_execute_tuning()
- Rename cdns,phy-pattern-partition to spi-phy-pattern-partition,
cqspi_phy_enable to cqspi_tune_phy and f_pdata->use_phy to use_tuned_phy
- Remove redundant spi-max-frequency parsing in driver cqspi_of_get_flash_pdata()
- Extract DMA refactoring into a preparatory patch
- Rebase on v7.1
- Collect tags from Miquel
- Link to v3: https://lore.kernel.org/linux-spi/20260527175527.2247679-1-s-
k6@xxxxxx/

Changes in v3:
- Drop spi-has-dqs DT property; DQS is now enabled automatically when
the selected read operation uses DDR signalling (dtr flags in the op)
- Extend spi-max-frequency to accept an optional second value forming a
[base-freq, max-freq] pair; the presence of two values signals PHY
tuning intent and encodes both the conservative base speed and the
calibration target in one property
- Add base_speed_hz to struct spi_device (spi.c/spi.h) and parse the
two-element array there; single-value DT is fully backward-compatible
- Move frequency enforcement from the cadence driver to core: new
spi_mem_apply_base_freq_cap() called from spi_mem_exec_op() replaces
the per-driver cqspi_op_matches_tuned() and non_phy_clk_rate field
- Propagate the tuned max_freq to dirmap op templates after
execute_tuning() succeeds; store persistent op templates in
spi_nor.max_read_op and spinand.{max_read,max_write}_op so the
frequency writeback survives across the probe call
- Replace NOR pattern partition lookup by name with a
cdns,phy-pattern-partition DT phandle pointing directly to the
partition node
- Add CQSPI_NO_2BYTE_ADDR_PHY_DDR quirk and reject 2-byte-address DDR
ops in cqspi_supports_mem_op() to work around AM654 erratum i2383
- Remove RFC tag
- Rebase on v7.1-rc5
- Collect tags from Miquel
- Link to v2: https://lore.kernel.org/linux-spi/20260113141617.1905039-1-s-
k6@xxxxxx/

Changes in v2:
- Restructure the .execute_tuning() call from spi-mem clients instead
of mtdcore with best read_op and write_op (optional) passed
- Add compatible-specific .execute_tuning() call which can be called by
spi_mem_execute_tuning() if exists
- Handle tuning requirement check by controller instead of spi-mem
clients
- Add support to write the phy_pattern to cache if relevant write_op
is passed or get the partition offset which contains the phy_pattern
- Add tuning algorithm for DDR mode
- Add support for DQS
- Restrict PHY frequency to tuned operations
- Link to v1: https://lore.kernel.org/linux-spi/20250811193219.731851-1-s-
k6@xxxxxx/

Signed-off-by: Santhosh Kumar K <s-k6@xxxxxx>

Pratyush Yadav (1):
mtd: spi-nor: extract read op template construction into helper

Santhosh Kumar K (15):
spi: dt-bindings: add spi-max-post-config-frequency property
spi: dt-bindings: add spi-phy-pattern-partition property
spi: parse spi-max-post-config-frequency into post_config_max_speed_hz
spi: spi-mem: teach spi_mem_adjust_op_freq() about post-config ops
spi: spi-mem: add execute_tuning callback and spi_mem_execute_tuning()
spi: cadence-quadspi: move cqspi_readdata_capture earlier
spi: cadence-quadspi: add DQS support to read data capture
spi: cadence-quadspi: add PHY tuning support
spi: cadence-quadspi: skip DDR PHY tuning for 2-byte-address ops
(i2383)
spi: cadence-quadspi: refactor direct read path for PHY support
spi: cadence-quadspi: enable PHY for direct reads
spi: cadence-quadspi: enable PHY for indirect writes
mtd: spinand: extract variant ranking logic into
spinand_op_find_best()
mtd: spinand: negotiate optimal PHY operating point before dirmap
creation
mtd: spi-nor: run PHY tuning after init and update dirmap frequency

.../bindings/spi/cdns,qspi-nor.yaml | 19 +
.../bindings/spi/spi-peripheral-props.yaml | 13 +
drivers/mtd/nand/spi/core.c | 246 +-
drivers/mtd/spi-nor/core.c | 80 +-
drivers/spi/spi-cadence-quadspi.c | 2265 +++++++++++++++--
drivers/spi/spi-mem.c | 40 +
drivers/spi/spi.c | 2 +
include/linux/mtd/spi-nor.h | 3 +
include/linux/mtd/spinand.h | 11 +
include/linux/spi/spi-mem.h | 14 +
include/linux/spi/spi.h | 3 +
11 files changed, 2493 insertions(+), 203 deletions(-)

--
2.34.1