summaryrefslogtreecommitdiff
path: root/drivers/spi
diff options
context:
space:
mode:
authorKen Ma <make@marvell.com>2021-04-30 16:26:29 +0300
committerStefan Roese <sr@denx.de>2021-05-16 07:48:45 +0300
commit037818c544c44b31dd532409e3abc3747e24a60d (patch)
treeb09273e05b6a8918be77507c4af4d3d5b03cbc73 /drivers/spi
parent76a516452b38c4f1542db382ebaf05054797100c (diff)
downloadu-boot-037818c544c44b31dd532409e3abc3747e24a60d.tar.xz
spi: kirkwood: support extended baud rates
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>
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/kirkwood_spi.c60
1 files changed, 55 insertions, 5 deletions
diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c
index 43812da0eb..3dc62f351a 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);