[PATCH v3 04/13] spi: spi-mem: add spi_mem_apply_base_freq_cap()
From: Santhosh Kumar K
Date: Wed May 27 2026 - 14:01:27 EST
When a device exposes both a conservative speed and a maximum speed,
operations that have not been configured for max-speed use must be
prevented from running at the device maximum. Without this, any op with
max_freq == 0 would be silently raised to max_speed_hz by
spi_mem_adjust_op_freq(), bypassing the intended conservative limit.
Add spi_mem_apply_base_freq_cap(). When base_speed_hz is set it caps
op->max_freq to that value, unless the op already has max_freq set to
max_speed_hz, which signals it has been configured for max-speed use.
Call it in spi_mem_exec_op() before spi_mem_adjust_op_freq() so the
final frequency is within both the base-speed constraint and the device
maximum.
Signed-off-by: Santhosh Kumar K <s-k6@xxxxxx>
---
drivers/spi/spi-mem.c | 26 +++++++++++++++++++++++++-
include/linux/spi/spi-mem.h | 1 +
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/drivers/spi/spi-mem.c b/drivers/spi/spi-mem.c
index a88b9f038356..d16986274cbc 100644
--- a/drivers/spi/spi-mem.c
+++ b/drivers/spi/spi-mem.c
@@ -398,7 +398,11 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
u8 *tmpbuf;
int ret;
- /* Make sure the operation frequency is correct before going futher */
+ /*
+ * Ops not configured for maximum speed are limited to the conservative
+ * base speed; spi_mem_adjust_op_freq() then caps to the device maximum.
+ */
+ spi_mem_apply_base_freq_cap(mem, (struct spi_mem_op *)op);
spi_mem_adjust_op_freq(mem, (struct spi_mem_op *)op);
dev_vdbg(&mem->spi->dev, "[cmd: 0x%02x][%dB addr: %#8llx][%2dB dummy][%4dB data %s] %d%c-%d%c-%d%c-%d%c @ %uHz\n",
@@ -599,6 +603,26 @@ void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op)
}
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_freq);
+/**
+ * spi_mem_apply_base_freq_cap() - Enforce the conservative base speed for
+ * operations that are not explicitly validated
+ * @mem: the SPI memory
+ * @op: the operation to adjust
+ *
+ * When @mem->spi->base_speed_hz is non-zero, caps @op->max_freq to that
+ * value unless @op->max_freq is already set to @mem->spi->max_speed_hz,
+ * which signals the operation has been configured for max-speed use.
+ */
+void spi_mem_apply_base_freq_cap(struct spi_mem *mem, struct spi_mem_op *op)
+{
+ if (!mem->spi->base_speed_hz || op->max_freq == mem->spi->max_speed_hz)
+ return;
+
+ if (!op->max_freq || op->max_freq > mem->spi->base_speed_hz)
+ op->max_freq = mem->spi->base_speed_hz;
+}
+EXPORT_SYMBOL_GPL(spi_mem_apply_base_freq_cap);
+
/**
* spi_mem_calc_op_duration() - Derives the theoretical length (in ns) of an
* operation. This helps finding the best variant
diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h
index 722abd9aee3c..98125cb4cc6b 100644
--- a/include/linux/spi/spi-mem.h
+++ b/include/linux/spi/spi-mem.h
@@ -462,6 +462,7 @@ bool spi_mem_default_supports_op(struct spi_mem *mem,
int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op);
void spi_mem_adjust_op_freq(struct spi_mem *mem, struct spi_mem_op *op);
+void spi_mem_apply_base_freq_cap(struct spi_mem *mem, struct spi_mem_op *op);
u64 spi_mem_calc_op_duration(struct spi_mem *mem, struct spi_mem_op *op);
bool spi_mem_supports_op(struct spi_mem *mem,
--
2.34.1