[PATCH 2/8] spi: atcspi200: Improve clock configuration and divider logic
Leo Yu-Chi Liang
ycliang at andestech.com
Fri Apr 17 04:20:58 CEST 2026
- Add default clock source (100 MHz) and max frequency (40 MHz)
fallbacks when DT properties are missing
- Read spi-max-frequency from DT to cap the operating frequency
- Use improved round-up divider formula that selects the closest
available frequency not exceeding the target
- Add SCLK_DIV_BYPASS (0xFF) and SCLK_DIV_MAX (0xFE) constants
- Remove mtiming field and timing restore in stop(), which was
incorrectly resetting clock settings after every transfer
- Change clock fields from ulong to unsigned int
Signed-off-by: CL Chin-Long Wang <cl634 at andestech.com>
Signed-off-by: Leo Yu-Chi Liang <ycliang at andestech.com>
---
drivers/spi/atcspi200_spi.c | 57 +++++++++++++++++++++++--------------
1 file changed, 36 insertions(+), 21 deletions(-)
diff --git a/drivers/spi/atcspi200_spi.c b/drivers/spi/atcspi200_spi.c
index 8cae96ee23c..33cf7583581 100644
--- a/drivers/spi/atcspi200_spi.c
+++ b/drivers/spi/atcspi200_spi.c
@@ -18,6 +18,8 @@
#define MAX_TRANSFER_LEN 512
#define CHUNK_SIZE 1
#define SPI_TIMEOUT 0x100000
+#define SPI_DEF_SRC_CLK 100000000
+#define SPI_DEF_MAX_CLK 40000000
/* Register offsets */
#define ATCSPI200_REG_FORMAT 0x10
@@ -61,15 +63,17 @@
/* TIMING register fields */
#define SCLK_DIV_MASK GENMASK(7, 0)
+#define SCLK_DIV_BYPASS 0xFF
+#define SCLK_DIV_MAX 0xFE
struct atcspi200_priv {
void *regs;
int to;
unsigned int freq;
- ulong clock;
+ unsigned int src_clk;
+ unsigned int max_clk;
unsigned int mode;
u8 num_cs;
- unsigned int mtiming;
size_t cmd_len;
u8 cmd_buf[16];
size_t data_len;
@@ -93,22 +97,28 @@ static inline void atcspi200_write(struct atcspi200_priv *priv, u32 offset,
static int atcspi200_hw_set_speed(struct atcspi200_priv *priv)
{
u32 tm;
- u8 div;
+ u32 div;
+ u32 spi_sclk;
- tm = atcspi200_read(priv, ATCSPI200_REG_TIMING);
- tm &= ~SCLK_DIV_MASK;
+ tm = atcspi200_read(priv, ATCSPI200_REG_TIMING) & ~SCLK_DIV_MASK;
- if (priv->freq >= priv->clock) {
- div = 0xff;
+ spi_sclk = min(priv->freq, priv->max_clk);
+
+ /*
+ * SCLK_DIV = 0xFF bypasses the divider, giving SCLK = src_clk.
+ * Otherwise SCLK = src_clk / (2 * (SCLK_DIV + 1)).
+ */
+ if (spi_sclk >= priv->src_clk) {
+ div = SCLK_DIV_BYPASS;
} else {
- for (div = 0; div < 0xff; div++) {
- if (priv->freq >= priv->clock / (2 * (div + 1)))
- break;
+ div = DIV_ROUND_UP(priv->src_clk, spi_sclk * 2) - 1;
+ if (div > SCLK_DIV_MAX) {
+ pr_err("Unsupported SPI clock %u\n", spi_sclk);
+ return -EINVAL;
}
}
- tm |= div;
- atcspi200_write(priv, ATCSPI200_REG_TIMING, tm);
+ atcspi200_write(priv, ATCSPI200_REG_TIMING, tm | div);
return 0;
}
@@ -166,7 +176,6 @@ static int atcspi200_hw_start(struct atcspi200_priv *priv)
static int atcspi200_hw_stop(struct atcspi200_priv *priv)
{
- atcspi200_write(priv, ATCSPI200_REG_TIMING, priv->mtiming);
while ((atcspi200_read(priv, ATCSPI200_REG_STATUS) & SPIBSY) &&
priv->to--)
if (!priv->to)
@@ -353,14 +362,21 @@ static int atcspi200_spi_get_clk(struct udevice *bus)
int ret;
ret = clk_get_by_index(bus, 0, &clk);
- if (ret)
- return -EINVAL;
-
- clk_rate = clk_get_rate(&clk);
- if (!clk_rate)
- return -EINVAL;
+ if (ret) {
+ dev_warn(bus, "no clock node, using default %d Hz\n",
+ SPI_DEF_SRC_CLK);
+ priv->src_clk = SPI_DEF_SRC_CLK;
+ } else {
+ clk_rate = clk_get_rate(&clk);
+ if (!clk_rate) {
+ dev_err(bus, "invalid clock rate\n");
+ return -EINVAL;
+ }
+ priv->src_clk = clk_rate;
+ }
- priv->clock = clk_rate;
+ priv->max_clk = dev_read_u32_default(bus, "spi-max-frequency",
+ SPI_DEF_MAX_CLK);
return 0;
}
@@ -371,7 +387,6 @@ static int atcspi200_spi_probe(struct udevice *bus)
priv->to = SPI_TIMEOUT;
priv->max_transfer_length = MAX_TRANSFER_LEN;
- priv->mtiming = atcspi200_read(priv, ATCSPI200_REG_TIMING);
atcspi200_spi_get_clk(bus);
return 0;
--
2.34.1
More information about the U-Boot
mailing list