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>
This commit is contained in:
Ken Ma 2021-04-30 15:26:29 +02:00 committed by Stefan Roese
parent 76a516452b
commit 037818c544

View 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 */
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;
/*
* 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;
}
data = ((sppr & 0x6) << 5) | ((sppr & 0x1) << 4) | spr;
/* program spi clock prescaler using max_hz */
writel(KWSPI_ADRLEN_3BYTE | data, &reg->cfg);