From 037818c544c44b31dd532409e3abc3747e24a60d Mon Sep 17 00:00:00 2001 From: Ken Ma Date: Fri, 30 Apr 2021 15:26:29 +0200 Subject: 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 Signed-off-by: Stefan Roese --- drivers/spi/kirkwood_spi.c | 60 ++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 43812da0ebb..3dc62f351a0 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, ®->cfg); -- cgit v1.3.1 From 562f8d5b36e4d35365aa9a545e4d11b5c2557ec1 Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Fri, 30 Apr 2021 15:26:30 +0200 Subject: spi: kirkwood: prevent configuring speed exceeding max controller freq This patch adds a limitation in the kirkwood_spi driver set_speed hook, which prevents setting too high transfer speed. Signed-off-by: Marcin Wojtas Reviewed-by: Kostya Porotchkin Tested-by: Kostya Porotchkin Signed-off-by: Stefan Roese --- drivers/spi/kirkwood_spi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 3dc62f351a0..063ed5f35a3 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -110,10 +110,17 @@ static int _spi_xfer(struct kwspi_registers *reg, unsigned int bitlen, static int mvebu_spi_set_speed(struct udevice *bus, uint hz) { struct mvebu_spi_plat *plat = dev_get_plat(bus); + struct dm_spi_bus *spi = dev_get_uclass_priv(bus); struct kwspi_registers *reg = plat->spireg; u32 data, divider; unsigned int spr, sppr; + if (hz > spi->max_hz) { + debug("%s: limit speed to the max_hz of the bus %d\n", + __func__, spi->max_hz); + hz = spi->max_hz; + } + /* * Calculate spi clock prescaller using max_hz. * SPPR is SPI Baud Rate Pre-selection, it holds bits 5 and 7:6 in -- cgit v1.3.1 From 9c84159ce1c7c6410640de66f0d5e52af09a115e Mon Sep 17 00:00:00 2001 From: Grzegorz Jaszczyk Date: Fri, 30 Apr 2021 15:26:31 +0200 Subject: spi: kirkwood: prevent limiting speed to 0 After commit 1fe929ed497bcc8975be8d37383ebafd22b99dd2 ("spi: kirkwood: prevent configuring speed exceeding max controller freq") the spi frequency could be set to 0 on platform where spi-max-frequency is not defined (e.g. on armada-388-gp). Prevent limiting speed in mentioned cases. Signed-off-by: Grzegorz Jaszczyk Tested-by: Kostya Porotchkin Reviewed-by: Marcin Wojtas Reviewed-by: Kostya Porotchkin Signed-off-by: Stefan Roese --- drivers/spi/kirkwood_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 063ed5f35a3..bc5da0a1e6e 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -115,7 +115,7 @@ static int mvebu_spi_set_speed(struct udevice *bus, uint hz) u32 data, divider; unsigned int spr, sppr; - if (hz > spi->max_hz) { + if (spi->max_hz && (hz > spi->max_hz)) { debug("%s: limit speed to the max_hz of the bus %d\n", __func__, spi->max_hz); hz = spi->max_hz; -- cgit v1.3.1 From 1fde894e793ebae10c6e43da68b90d5d9b47465e Mon Sep 17 00:00:00 2001 From: Marcin Wojtas Date: Fri, 30 Apr 2021 15:33:15 +0200 Subject: pcie: designware: mvebu: do not configure ATU for IO when not used The pcie_dw_mvebu configure ATU regions for memory, configuration and IO space types. However the latter is not obligatory and when not specified in the device tree, causes wrong ATU configuration. Fix that by adding a dependency on the detected PCIE regions count. Signed-off-by: Marcin Wojtas Reviewed-on: https://sj1git1.cavium.com/18136 Reviewed-by: Kostya Porotchkin Tested-by: Kostya Porotchkin --- drivers/pci/pcie_dw_mvebu.c | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie_dw_mvebu.c b/drivers/pci/pcie_dw_mvebu.c index 93e57cf0cf1..0490fd33770 100644 --- a/drivers/pci/pcie_dw_mvebu.c +++ b/drivers/pci/pcie_dw_mvebu.c @@ -115,6 +115,7 @@ struct pcie_dw_mvebu { int first_busno; /* IO and MEM PCI regions */ + int region_count; struct pci_region io; struct pci_region mem; }; @@ -267,9 +268,10 @@ static int pcie_dw_mvebu_read_config(const struct udevice *bus, pci_dev_t bdf, debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); *valuep = pci_conv_32_to_size(value, offset, size); - pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, - PCIE_ATU_TYPE_IO, pcie->io.phys_start, - pcie->io.bus_start, pcie->io.size); + if (pcie->region_count > 1) + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); return 0; } @@ -312,9 +314,10 @@ static int pcie_dw_mvebu_write_config(struct udevice *bus, pci_dev_t bdf, value = pci_conv_size_to_32(old, value, offset, size); writel(value, va_address); - pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, - PCIE_ATU_TYPE_IO, pcie->io.phys_start, - pcie->io.bus_start, pcie->io.size); + if (pcie->region_count > 1) + pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); return 0; } @@ -513,14 +516,24 @@ static int pcie_dw_mvebu_probe(struct udevice *dev) hose->first_busno); } + pcie->region_count = hose->region_count - CONFIG_NR_DRAM_BANKS; + /* Store the IO and MEM windows settings for future use by the ATU */ - pcie->io.phys_start = hose->regions[0].phys_start; /* IO base */ - pcie->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */ - pcie->io.size = hose->regions[0].size; /* IO size */ + if (pcie->region_count > 1) { + /* IO base */ + pcie->io.phys_start = hose->regions[0].phys_start; + /* IO_bus_addr */ + pcie->io.bus_start = hose->regions[0].bus_start; + /* IO size */ + pcie->io.size = hose->regions[0].size; + } - pcie->mem.phys_start = hose->regions[1].phys_start; /* MEM base */ - pcie->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */ - pcie->mem.size = hose->regions[1].size; /* MEM size */ + /* MEM base */ + pcie->mem.phys_start = hose->regions[pcie->region_count - 1].phys_start; + /* MEM_bus_addr */ + pcie->mem.bus_start = hose->regions[pcie->region_count - 1].bus_start; + /* MEM size */ + pcie->mem.size = hose->regions[pcie->region_count - 1].size; pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX1, PCIE_ATU_TYPE_MEM, pcie->mem.phys_start, -- cgit v1.3.1