[PATCH v3 11/13] mtd: spinand: run PHY tuning after init and update dirmap frequencies
From: Santhosh Kumar K
Date: Wed May 27 2026 - 13:58:16 EST
Run spi_mem_execute_tuning() in spinand_probe() after spinand_init()
completes. The read and write op templates are copied into persistent
fields in spinand_device so the controller can write the validated
frequency directly back into them. On success, propagate that frequency
to every dirmap's primary and secondary op templates. Updating the
secondary template ensures continuous-read dirmaps also benefit from
the validated speed, not just the primary read path.
Signed-off-by: Santhosh Kumar K <s-k6@xxxxxx>
---
drivers/mtd/nand/spi/core.c | 35 +++++++++++++++++++++++++++++++++++
include/linux/mtd/spinand.h | 4 ++++
2 files changed, 39 insertions(+)
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index f1084d5e04b9..9b54e4607cfe 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -2030,6 +2030,41 @@ static int spinand_probe(struct spi_mem *mem)
if (ret)
return ret;
+ /*
+ * Copy the read and write op templates into persistent fields so
+ * execute_tuning can write the validated frequency back into them.
+ * Tuning failure is non-fatal; the device operates at base speed.
+ */
+ spinand->max_read_op = *spinand->op_templates->read_cache;
+ spinand->max_write_op = *spinand->op_templates->write_cache;
+
+ ret = spi_mem_execute_tuning(mem, &spinand->max_read_op,
+ &spinand->max_write_op);
+ if (ret && ret != -EOPNOTSUPP)
+ dev_warn(&mem->spi->dev, "Failed to execute PHY tuning: %d\n",
+ ret);
+
+ /*
+ * Dirmaps were set up in spinand_init() before tuning ran; update
+ * their op templates to use the validated frequency.
+ */
+ if (!ret) {
+ struct nand_device *nand = spinand_to_nand(spinand);
+ int i;
+
+ for (i = 0; i < nand->memorg.planes_per_lun; i++) {
+ if (spinand->dirmaps[i].rdesc) {
+ spinand->dirmaps[i].rdesc->info.primary_op_tmpl.max_freq =
+ spinand->max_read_op.max_freq;
+ spinand->dirmaps[i].rdesc->info.secondary_op_tmpl.max_freq =
+ spinand->max_read_op.max_freq;
+ }
+ if (spinand->dirmaps[i].wdesc)
+ spinand->dirmaps[i].wdesc->info.primary_op_tmpl.max_freq =
+ spinand->max_write_op.max_freq;
+ }
+ }
+
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
goto err_spinand_cleanup;
diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h
index 44f4347104d6..e5af90281762 100644
--- a/include/linux/mtd/spinand.h
+++ b/include/linux/mtd/spinand.h
@@ -786,6 +786,10 @@ struct spinand_device {
struct spinand_dirmap *dirmaps;
+ /* Persistent op templates updated by execute_tuning with validated speed. */
+ struct spi_mem_op max_read_op;
+ struct spi_mem_op max_write_op;
+
int (*select_target)(struct spinand_device *spinand,
unsigned int target);
unsigned int cur_target;
--
2.34.1