[PATCH v1 1/3] spi: kirkwood: support extended baud rates

Stefan Roese sr at denx.de
Fri Apr 30 15:26:29 CEST 2021


From: Ken Ma <make at marvell.com>

The Armada SoC family implementation of this SPI hardware module has
extended the configuration register to allow for a wider range of SPI
clock rates. Specifically the Serial Baud Rate Pre-selection bits in the
SPI Interface Configuration Register now also use bits 6 and 7 as well.

Modify the baud rate calculation to handle these differences for the
Armada case. Potentially a baud rate can be setup using a number of
different pre-scalar and scalar combinations. This code tries all
possible pre-scalar divisors (8 in total) to try and find the most
accurate set.

Signed-off-by: Ken Ma <make at marvell.com>
Signed-off-by: Stefan Roese <sr at denx.de>
---

 drivers/spi/kirkwood_spi.c | 60 ++++++++++++++++++++++++++++++++++----
 1 file changed, 55 insertions(+), 5 deletions(-)

diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index 43812da0ebb7..3dc62f351a06 100644
--- a/drivers/spi/kirkwood_spi.c
+++ b/drivers/spi/kirkwood_spi.c
@@ -111,12 +111,62 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
 {
 	struct mvebu_spi_plat *plat = dev_get_plat(bus);
 	struct kwspi_registers *reg = plat->spireg;
-	u32 data;
+	u32 data, divider;
+	unsigned int spr, sppr;
+
+	/*
+	 * Calculate spi clock prescaller using max_hz.
+	 * SPPR is SPI Baud Rate Pre-selection, it holds bits 5 and 7:6 in
+	 * SPI Interface Configuration Register;
+	 * SPR is SPI Baud Rate Selection, it holds bits 3:0 in SPI Interface
+	 * Configuration Register.
+	 * The SPR together with the SPPR define the SPI CLK frequency as
+	 * follows:
+	 * SPI actual frequency = core_clk / (SPR * (2 ^ SPPR))
+	 */
+	divider = DIV_ROUND_UP(CONFIG_SYS_TCLK, hz);
+	if (divider < 16) {
+		/* This is the easy case, divider is less than 16 */
+		spr = divider;
+		sppr = 0;
+
+	} else {
+		unsigned int two_pow_sppr;
+		/*
+		 * Find the highest bit set in divider. This and the
+		 * three next bits define SPR (apart from rounding).
+		 * SPPR is then the number of zero bits that must be
+		 * appended:
+		 */
+		sppr = fls(divider) - 4;
+
+		/*
+		 * As SPR only has 4 bits, we have to round divider up
+		 * to the next multiple of 2 ** sppr.
+		 */
+		two_pow_sppr = 1 << sppr;
+		divider = (divider + two_pow_sppr - 1) & -two_pow_sppr;
+
+		/*
+		 * recalculate sppr as rounding up divider might have
+		 * increased it enough to change the position of the
+		 * highest set bit. In this case the bit that now
+		 * doesn't make it into SPR is 0, so there is no need to
+		 * round again.
+		 */
+		sppr = fls(divider) - 4;
+		spr = divider >> sppr;
+
+		/*
+		 * Now do range checking. SPR is constructed to have a
+		 * width of 4 bits, so this is fine for sure. So we
+		 * still need to check for sppr to fit into 3 bits:
+		 */
+		if (sppr > 7)
+			return -EINVAL;
+	}
 
-	/* calculate spi clock prescaller using max_hz */
-	data = ((CONFIG_SYS_TCLK / 2) / hz) + 0x10;
-	data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
-	data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
+	data = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
 
 	/* program spi clock prescaler using max_hz */
 	writel(KWSPI_ADRLEN_3BYTE | data, &reg->cfg);
-- 
2.31.1



More information about the U-Boot mailing list