spi: kirkwood: support extended baud rates
authorKen Ma <make@marvell.com>
Fri, 30 Apr 2021 13:26:29 +0000 (15:26 +0200)
committerStefan Roese <sr@denx.de>
Sun, 16 May 2021 04:48:45 +0000 (06:48 +0200)
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@marvell.com>
Signed-off-by: Stefan Roese <sr@denx.de>
drivers/spi/kirkwood_spi.c

index 43812da..3dc62f3 100644 (file)
@@ -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);