[v5 12/14] spi: aspeed: Support customized decoded address ranges

Chin-Ting Kuo chin-ting_kuo at aspeedtech.com
Fri Aug 19 11:01:13 CEST 2022


If "decoded-ranges" is defined in the device tree, the
driver will apply the decoded address ranges from this
property to the controller during probe stage.

This patch refers to the following OpenBMC u-boot patch.
https://patchwork.ozlabs.org/project/openbmc/list/?series=306969

Signed-off-by: Chin-Ting Kuo <chin-ting_kuo at aspeedtech.com>
---
 drivers/spi/spi-aspeed-smc.c | 136 ++++++++++++++++++++++++++++++++---
 1 file changed, 127 insertions(+), 9 deletions(-)

diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 4287f5d8c0..2b6c4b48bd 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -74,6 +74,7 @@ struct aspeed_spi_priv {
 	struct aspeed_spi_regs *regs;
 	struct aspeed_spi_info *info;
 	struct aspeed_spi_flash flashes[ASPEED_SPI_MAX_CS];
+	bool fixed_decoded_range;
 };
 
 struct aspeed_spi_info {
@@ -87,7 +88,15 @@ struct aspeed_spi_info {
 	int (*adjust_decoded_sz)(struct udevice *bus);
 };
 
+struct aspeed_spi_decoded_range {
+	u32 cs;
+	u32 ahb_base;
+	u32 sz;
+};
+
 static const struct aspeed_spi_info ast2400_spi_info;
+static const struct aspeed_spi_info ast2500_fmc_info;
+static const struct aspeed_spi_info ast2500_spi_info;
 static int aspeed_spi_decoded_range_config(struct udevice *bus);
 static int aspeed_spi_trim_decoded_size(struct udevice *bus);
 
@@ -618,6 +627,9 @@ static void aspeed_spi_decoded_base_calculate(struct udevice *bus)
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
 	u32 cs;
 
+	if (priv->fixed_decoded_range)
+		return;
+
 	priv->flashes[0].ahb_base = plat->ahb_base;
 
 	for (cs = 1; cs < plat->max_cs; cs++) {
@@ -654,7 +666,8 @@ static int aspeed_spi_decoded_range_config(struct udevice *bus)
 	int ret = 0;
 	struct aspeed_spi_priv *priv = dev_get_priv(bus);
 
-	if (priv->info->adjust_decoded_sz) {
+	if (priv->info->adjust_decoded_sz &&
+	    !priv->fixed_decoded_range) {
 		ret = priv->info->adjust_decoded_sz(bus);
 		if (ret != 0)
 			return ret;
@@ -666,6 +679,104 @@ static int aspeed_spi_decoded_range_config(struct udevice *bus)
 	return ret;
 }
 
+static int aspeed_spi_decoded_ranges_sanity(struct udevice *bus)
+{
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	u32 cs;
+	u32 total_sz = 0;
+
+	/* Check overall size. */
+	for (cs = 0; cs < plat->max_cs; cs++)
+		total_sz += priv->flashes[cs].ahb_decoded_sz;
+
+	if (total_sz > plat->ahb_sz) {
+		dev_err(bus, "invalid total size 0x%08x\n", total_sz);
+		return -EINVAL;
+	}
+
+	/* Check each decoded range size for AST2500. */
+	if (priv->info == &ast2500_fmc_info ||
+	    priv->info == &ast2500_spi_info) {
+		for (cs = 0; cs < plat->max_cs; cs++) {
+			if (priv->flashes[cs].ahb_decoded_sz <
+			    priv->info->min_decoded_sz) {
+				dev_err(bus, "insufficient decoded range.\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	/*
+	 * Check overlay. Here, we assume the deccded ranges and
+	 * address base	are monotonic increasing with CE#.
+	 */
+	for (cs = plat->max_cs - 1; cs > 0; cs--) {
+		if ((u32)priv->flashes[cs].ahb_base != 0 &&
+		    (u32)priv->flashes[cs].ahb_base <
+		    (u32)priv->flashes[cs - 1].ahb_base +
+		    priv->flashes[cs - 1].ahb_decoded_sz) {
+			dev_err(bus, "decoded range overlay 0x%08x 0x%08x\n",
+				(u32)priv->flashes[cs].ahb_base,
+				(u32)priv->flashes[cs - 1].ahb_base);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int aspeed_spi_read_fixed_decoded_ranges(struct udevice *bus)
+{
+	int ret = 0;
+	struct aspeed_spi_plat *plat = dev_get_plat(bus);
+	struct aspeed_spi_priv *priv = dev_get_priv(bus);
+	const char *range_prop = "decoded-ranges";
+	struct aspeed_spi_decoded_range ranges[ASPEED_SPI_MAX_CS];
+	const struct property *prop;
+	u32 prop_sz;
+	u32 count;
+	u32 i;
+
+	priv->fixed_decoded_range = false;
+
+	prop = dev_read_prop(bus, range_prop, &prop_sz);
+	if (!prop)
+		return 0;
+
+	count = prop_sz / sizeof(struct aspeed_spi_decoded_range);
+	if (count > plat->max_cs || count < priv->num_cs) {
+		dev_err(bus, "invalid '%s' property %d %d\n",
+			range_prop, count, priv->num_cs);
+		return -EINVAL;
+	}
+
+	ret = dev_read_u32_array(bus, range_prop, (u32 *)ranges, count * 3);
+	if (ret)
+		return ret;
+
+	for (i = 0; i < count; i++) {
+		priv->flashes[ranges[i].cs].ahb_base =
+				(void __iomem *)ranges[i].ahb_base;
+		priv->flashes[ranges[i].cs].ahb_decoded_sz =
+				ranges[i].sz;
+	}
+
+	for (i = 0; i < plat->max_cs; i++) {
+		dev_dbg(bus, "ahb_base: 0x%p, size: 0x%08x\n",
+			priv->flashes[i].ahb_base,
+			priv->flashes[i].ahb_decoded_sz);
+	}
+
+	ret = aspeed_spi_decoded_ranges_sanity(bus);
+	if (ret != 0)
+		return ret;
+
+	priv->fixed_decoded_range = true;
+
+	return 0;
+}
+
 /*
  * Initialize SPI controller for each chip select.
  * Here, only the minimum decode range is configured
@@ -709,16 +820,23 @@ static int aspeed_spi_ctrl_init(struct udevice *bus)
 		return 0;
 	}
 
-	/* Assign basic AHB decoded size for each CS. */
-	for (cs = 0; cs < plat->max_cs; cs++) {
-		reg_val = readl(&priv->regs->segment_addr[cs]);
-		decoded_sz = priv->info->segment_end(bus, reg_val) -
-			     priv->info->segment_start(bus, reg_val);
 
-		if (decoded_sz < priv->info->min_decoded_sz)
-			decoded_sz = priv->info->min_decoded_sz;
+	ret = aspeed_spi_read_fixed_decoded_ranges(bus);
+	if (ret != 0)
+		return ret;
+
+	if (!priv->fixed_decoded_range) {
+		/* Assign basic AHB decoded size for each CS. */
+		for (cs = 0; cs < plat->max_cs; cs++) {
+			reg_val = readl(&priv->regs->segment_addr[cs]);
+			decoded_sz = priv->info->segment_end(bus, reg_val) -
+				     priv->info->segment_start(bus, reg_val);
+
+			if (decoded_sz < priv->info->min_decoded_sz)
+				decoded_sz = priv->info->min_decoded_sz;
 
-		priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+			priv->flashes[cs].ahb_decoded_sz = decoded_sz;
+		}
 	}
 
 	ret = aspeed_spi_decoded_range_config(bus);
-- 
2.25.1



More information about the U-Boot mailing list