[PATCH RFC 5/7] spi: Add RX sampling point adjustment
From: Frieder Schrempf
Date: Tue Mar 03 2026 - 11:38:51 EST
From: Frieder Schrempf <frieder.schrempf@xxxxxxxxxx>
Some SPI devices such as SPI NAND chips specify a clock-to-RX-sampling
delay constraint, which means that for the data read from the device
at a certain clock rate, we need to make sure that the point at
which the data is sampled is correct.
The default is to assume that the data can be sampled one half clock
cycle after the triggering clock edge. For high clock rates, this
can be too early.
To check this we introduce a new core function spi_set_rx_sampling_point()
and a handler set_rx_sampling_point() in the SPI controller.
Controllers implementing set_rx_sampling_point() can calculate the
sampling point delay using the helper spi_calc_rx_sampling_point()
and store the value to set the appropriate registers during transfer.
In case the controller capabilities are not sufficient to compensate
the RX delay, spi_set_rx_sampling_point() returns a reduced clock
rate value that is safe to use.
This commit does not introduce generic logic for controllers that
don't implement set_rx_sampling_point() in order to reduce the clock
rate if the RX sampling delay requirement can not be met.
Signed-off-by: Frieder Schrempf <frieder.schrempf@xxxxxxxxxx>
---
drivers/spi/spi.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/spi/spi.h | 8 ++++++
2 files changed, 81 insertions(+)
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 61f7bde8c7fbb..b039007ed430f 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -3988,6 +3988,77 @@ static int spi_set_cs_timing(struct spi_device *spi)
return status;
}
+/**
+ * spi_calc_rx_sampling_point - calculate RX sampling delay cycles
+ * @spi: the device that requires specific a RX sampling delay
+ * @freq: pointer to the clock frequency setpoint for the calculation. This gets
+ * altered to a reduced value if required.
+ * @max_delay_cycles: the upper limit of supported delay cycles
+ * @delay_cycles_per_clock_cycle: the ratio between delay cycles and
+ * master clock cycles
+ *
+ * This function takes in the rx_sampling_delay_ns value from the SPI device
+ * and the given clock frequency setpoint and calculates the required sampling
+ * delay cycles to meet the device's spec. It uses the given controller
+ * constraints and if those are exceeded, it adjusts the clock frequency
+ * setpoint to a lower value that is safe to be used.
+ *
+ * Return: calculated number of delay cycles
+ */
+unsigned int spi_calc_rx_sampling_point(struct spi_device *spi, unsigned int *freq,
+ u16 max_delay_cycles,
+ u16 delay_cycles_per_clock_cycle)
+{
+ unsigned long long temp;
+ u16 delay_cycles;
+
+ /* if sampling delay is zero, we assume the default sampling point can be used */
+ if (!spi->rx_sampling_delay_ns)
+ return 0;
+
+ temp = *freq * delay_cycles_per_clock_cycle * spi->rx_sampling_delay_ns;
+ do_div(temp, 1000000000UL);
+ delay_cycles = temp;
+
+ if (delay_cycles > max_delay_cycles) {
+ /*
+ * Reduce the clock to the point where the sampling delay requirement
+ * can be met.
+ */
+ delay_cycles = max_delay_cycles;
+ temp = (1000000000UL * delay_cycles);
+ do_div(temp, spi->rx_sampling_delay_ns * delay_cycles_per_clock_cycle);
+ *freq = temp;
+ }
+
+ dev_dbg(&spi->controller->dev, "calculated RX sampling point delay: %u cycle(s) at %lu KHz", delay_cycles, *freq / 1000);
+
+ return delay_cycles;
+}
+EXPORT_SYMBOL_GPL(spi_calc_rx_sampling_point);
+
+/**
+ * spi_set_rx_sampling_point - set the RX sampling delay in the controller driver
+ * @spi: the device that requires specific a RX sampling delay
+ * @freq: the clock frequency setpoint for the RX sampling delay calculation
+ *
+ * This function calls the set_rx_sampling_point() handle in the controller
+ * driver it is available. This makes sure that the controller uses the proper
+ * RX sampling point adjustment. This function should be called whenever
+ * the devices rx_sampling_delay_ns or the currently used clock frequency
+ * changes.
+ *
+ * Return: adjusted clock frequency
+ */
+unsigned int spi_set_rx_sampling_point(struct spi_device *spi, unsigned int freq)
+{
+ if (spi->controller->set_rx_sampling_point)
+ return spi->controller->set_rx_sampling_point(spi, spi->max_speed_hz);
+
+ return freq;
+}
+EXPORT_SYMBOL_GPL(spi_set_rx_sampling_point);
+
/**
* spi_setup - setup SPI mode and clock rate
* @spi: the device whose settings are being modified
@@ -4090,6 +4161,8 @@ int spi_setup(struct spi_device *spi)
}
}
+ spi->max_speed_hz = spi_set_rx_sampling_point(spi, spi->max_speed_hz);
+
status = spi_set_cs_timing(spi);
if (status) {
mutex_unlock(&spi->controller->io_mutex);
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 4f8a0c28e1d46..f5be4f54d1424 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -685,6 +685,9 @@ struct spi_controller {
*/
int (*set_cs_timing)(struct spi_device *spi);
+ /* set RX sampling point */
+ unsigned int (*set_rx_sampling_point)(struct spi_device *spi, unsigned int freq);
+
/*
* Bidirectional bulk transfers
*
@@ -1337,6 +1340,11 @@ extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_target_abort(struct spi_device *spi);
+unsigned int spi_calc_rx_sampling_point(struct spi_device *spi, unsigned int *freq,
+ u16 max_delay_cycles,
+ u16 delay_cycles_per_clock_cycle);
+unsigned int spi_set_rx_sampling_point(struct spi_device *spi, unsigned int freq);
+
static inline size_t
spi_max_message_size(struct spi_device *spi)
{
--
2.53.0