[PATCH v10 00/19] spi: cadence-quadspi: add PHY tuning support

Boyapally, Srikanth Srikanth.Boyapally at amd.com
Thu Oct 15 10:47:23 PDT 2026


AMD General

Hi Santhosh,

I tested this series on SPI-NOR flash devices on the AMD Versal
Net platform by integrating AMD SoC-specific PHY support on top of
your patch series.

Read throughput:

  DTR-PHY (200 MHz):      271 MB/s
  SDR non-PHY (20 MHz):  19.60 MB/s

To validate functionality and robustness, I completed JFFS2 and
raw MTD erase/write/read-back testing with random data across
10/10 successful runs. I also verified odd length reads, which
consistently matched the corresponding aligned reference reads.

For the SPI, spi-mem, SPI NOR, and cadence-quadspi patches,
please feel free to add:

Tested-by: Srikanth Boyapally <srikanth.boyapally at amd.com>

Thank you,
Srikanth
> -----Original Message-----
> From: Santhosh Kumar K <s-k6 at ti.com>
> Sent: Tuesday, September 29, 2026 12:05 PM
> To: broonie at kernel.org; robh at kernel.org; krzk+dt at kernel.org;
> conor+dt at kernel.org; miquel.raynal at bootlin.com; richard at nod.at;
> vigneshr at ti.com; pratyush at kernel.org; mwalle at kernel.org;
> takahiro.kuwano at infineon.com
> Cc: linux-spi at vger.kernel.org; devicetree at vger.kernel.org; linux-
> kernel at vger.kernel.org; linux-mtd at lists.infradead.org; praneeth at ti.com;
> sebin.francis at ti.com; u-kumar1 at ti.com; a-dutta at ti.com; s-k6 at ti.com
> Subject: [PATCH v10 00/19] spi: cadence-quadspi: add PHY tuning support
>
> Caution: This message originated from an External Source. Use proper caution
> when opening attachments, clicking links, or responding.
>
>
> 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
>
> Patch description:
> Infrastructure (1-5):
>  - Patch 1:   Add spi-max-post-config-frequency-hz 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-hz 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. Also require per_op_freq
>               controller capability for post-config ops in
>               spi_mem_default_supports_op()
>  - Patch 5:   Add execute_tuning callback to spi_controller_mem_ops and
>               spi_mem_execute_tuning() wrapper in SPI-MEM core;
>               serialises access via spi_mem_access_start/end and
>               rejects GPIO chip selects
>
> Cadence QSPI Implementation (6-14):
>  - 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_PHY_TUNING_WITH_ODDR_2B_ADDR 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 at the calibrated frequency
>               when both transfer boundaries are 16-byte aligned,
>               falling back to non-PHY DMA otherwise; also reprograms
>               DLL on chip-select switch when two devices have
>               different calibrated settings
>  - Patch 12:  Enable PHY for indirect writes of at least
>               CQSPI_PHY_MIN_INDIRECT_WRITE_LEN bytes
>  - Patch 13:  Reprogram per-device CS timing on any chip-select switch
>  - Patch 14:  Reprogram the PHY DLL (master config and RX/TX taps) on
>               runtime resume, since the DLL loses its lock when the
>               OSPI controller is power-gated
>
> MTD core (15-19):
>  - Patch 15:  Extract spinand_select_op_variant() into a shared helper
>               spinand_op_find_best_variant() with a skip_mask
>  - Patch 16:  Negotiate optimal controller operating point before dirmap
>               creation; iterate all read variants in performance order
>               across ODTR and SSDR interfaces; re-pair cont_read_cache after
>               PHY tuning via protocol-shape matching
>  - Patch 17:  Extract spi_nor_spimem_get_read_op() helper (preparatory)
>  - Patch 18:  Execute controller optimization in spi_nor_probe() before
>               creating dirmaps
>  - Patch 19:  Reset max_read_op frequency for security register reads
>
> 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 | 218 MB/s  |
> |----------------------------------------|
> | OSPI NAND (8S) | 9.2 MB/s  | 35.6 MB/s |
> |----------------------------------------|
>
> Write throughput:
> |----------------------------------------|
> |                | non-PHY   | PHY       |
> |----------------------------------------|
> | OSPI NAND (8S) | 6 MB/s    | 9.2 MB/s  |
> |----------------------------------------|
>
> Test log:
> https://gist.github.com/santhosh21/e1b5c7cc5cad4acb94b67e826d971ab9
> Repo: https://github.com/santhosh21/linux/commits/phy_tuning_v10
>
> Changes in v10:
>  - Gate the frequency spi_nor_spimem_get_read_op() propagates on
> matching
>    protocol shape, and add a patch resetting max_read_op.max_freq around
>    the security-register read override
>  - Split cqspi_tune_phy() into cqspi_tune_phy_read()/cqspi_tune_phy_write()
>    so PHY_PIPELINE is never set on writes, per AM65x erratum i2189
>  - Fix spinand_try_ranked_variant() to only clear, never set
>    cont_read_possible on ranked-variant fallback
>  - Fix other, smaller correctness issues raised by sashiko-bot
>  - Collect tag from Miquel
>  - Link to v9: https://lore.kernel.org/linux-spi/20260825171737.776052-1-s-
> k6 at ti.com/
>
> Changes in v9:
>  - Fix off-by-one in DDR PHY TX search loops
>  - Fix cqspi_tune_phy() to unconditionally execute the disable path
>    and restore CQSPI_REG_READCAPTURE on enable error rollback
>  - Fix other, smaller correctness issues raised by sashiko-bot
>  - Link to v8: https://lore.kernel.org/linux-spi/20260821114822.4040338-1-
> s-k6 at ti.com/
>
> Changes in v8:
>  - Re-pair cont_read_cache with the selected read_cache variant via
>    protocol-shape matching after PHY tuning instead of unconditionally
>    diabling continuous read
>  - Rebase on v7.2
>  - Collect tag from Miquel
>  - Link to v7: https://lore.kernel.org/linux-spi/20260811183313.1550425-1-
> s-k6 at ti.com/
>
> Changes in v7:
>  - Serialise spi_mem_execute_tuning() with spi_mem_access_start/end and
>    reject GPIO chip-select configurations
>  - Fix cqspi_phy_apply_setting() to also write the calibrated
>    read_delay to CQSPI_REG_READCAPTURE, previously software-only
>  - Use local op copies for pattern write/verify instead of mutating
>    the caller's op templates in place
>  - Fix the READCAPTURE bypass bit in cqspi_tune_phy() to match the
>    value validated during calibration instead of being hardcoded
>  - Enable PHY before starting the indirect-write state machine instead
>    of after; the old order was a guaranteed deadlock
>  - Add a new patch to reprogram the PHY DLL on runtime resume
>  - Fix continuous-read and ODTR/SSDR template handling on ranked
>    fallback in spinand_try_ranked_variant(), preventing data
>    corruption after a fallback
>  - Fix other, smaller correctness issues raised by sashiko-bot
>  - Rebase on v7.2-rc7
>  - Link to v6: https://lore.kernel.org/linux-spi/20260723110219.3180747-1-
> s-k6 at ti.com/
>
> Changes in v6:
>  - Add maxItems to spi-max-post-config-frequency-hz property
>  - Rebase on v7.2-rc4
>  - Collect tag from Krzysztof
>  - Link to v5: https://lore.kernel.org/linux-spi/20260717185116.2065505-1-
> s-k6 at ti.com/
>
> Changes in v5:
>  - Rename spi-max-post-config-frequency to spi-max-post-config-frequency-
> hz
>    per DT schema unit suffix convention
>  - Rename CQSPI_NO_2BYTE_ADDR_PHY_DDR quirk to
>    CQSPI_NO_PHY_TUNING_WITH_ODDR_2B_ADDR for clarity
>  - Rename SPI NAND identifiers to remove PHY-specific terminology:
>      spinand_configure_phy()     -> spinand_optimize_controller()
>      spinand_try_phy_ranked()    -> spinand_try_ranked_variant()
>      spinand_reset_max_ops()     -> spinand_reset_max_freq_ops()
>      spinand_op_find_best()      -> spinand_op_find_best_variant()
>      phy_read_variants (field)   -> all_read_variants
>  - Replace bool odtr parameter with enum spinand_bus_interface iface in
>    spinand_op_find_best_variant() to preserve the existing enumeration
>  - Replace cqspi_get_phy_pattern_offset() with per-flash probe-time
>    phandle lookup in cqspi_of_get_flash_pdata(); fixes incorrect pattern
>    offset selection when multiple flash devices share a controller
>  - In cqspi_tune_phy(), reprogramme and resync the DLL when the active
>    device's calibrated settings differ from hardware
>  - Edit cqspi_phy_apply_setting() to update phy_setting.rx and
>    phy_setting.tx alongside phy_setting.read_delay
>  - Reprogram per-device CS timing (CQSPI_REG_DELAY and read-capture
> register)
>    on any chip-select switch, not only on clock changes
>  - Move post_config_max_speed_hz early-return to the top of
>    spinand_optimize_controller(), before any state is modified
>  - Remove dead NULL guards on SSDR op templates in Pass 2
>  - Downgrade tuning-failure log messages from dev_warn to dev_dbg
>  - Rebase on v7.2-rc3
>  - Collect tags
>  - Link to v4: https://lore.kernel.org/linux-spi/20260618073725.84733-1-s-
> k6 at ti.com/
>
> 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 at ti.com/
>
> 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 at ti.com/
>
> 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 at ti.com/
>
> Signed-off-by: Santhosh Kumar K <s-k6 at ti.com>
>
> Pratyush Yadav (1):
>   mtd: spi-nor: extract read op template construction  into helper
>
> Santhosh Kumar K (18):
>   spi: dt-bindings: add spi-max-post-config-frequency-hz property
>   spi: dt-bindings: add spi-phy-pattern-partition property
>   spi: parse spi-max-post-config-frequency-hz 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
>   spi: cadence-quadspi: reprogram CS timing on every chip-select switch
>   spi: cadence-quadspi: reprogram PHY DLL on runtime resume
>   mtd: spinand: extract variant ranking logic into
>     spinand_op_find_best_variant()
>   mtd: spinand: negotiate optimal controller operating point before
>     dirmap creation
>   mtd: spi-nor: run controller optimization before dirmap  creation
>   mtd: spi-nor: reset max_read_op frequency for security register reads
>
>  .../bindings/spi/cdns,qspi-nor.yaml           |   17 +
>  .../bindings/spi/spi-peripheral-props.yaml    |   13 +
>  drivers/mtd/nand/spi/core.c                   |  328 ++-
>  drivers/mtd/spi-nor/core.c                    |   94 +-
>  drivers/mtd/spi-nor/otp.c                     |    5 +
>  drivers/spi/spi-cadence-quadspi.c             | 2275 ++++++++++++++++-
>  drivers/spi/spi-mem.c                         |   55 +-
>  drivers/spi/spi.c                             |    2 +
>  include/linux/mtd/spi-nor.h                   |    4 +
>  include/linux/mtd/spinand.h                   |   15 +
>  include/linux/spi/spi-mem.h                   |   14 +
>  include/linux/spi/spi.h                       |    3 +
>  12 files changed, 2671 insertions(+), 154 deletions(-)
>
> --
> 2.34.1




More information about the linux-mtd mailing list