]> git.dujemihanovic.xyz Git - u-boot.git/commitdiff
spi: aspeed: Support customized decoded address ranges
authorChin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Fri, 19 Aug 2022 09:01:13 +0000 (17:01 +0800)
committerTom Rini <trini@konsulko.com>
Tue, 13 Sep 2022 16:08:41 +0000 (12:08 -0400)
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@aspeedtech.com>
drivers/spi/spi-aspeed-smc.c

index 4287f5d8c0a3ad8c74f4bb5665a1261c38c7a861..2b6c4b48bd94a85ff0a7aaa5d258fda403d7fc04 100644 (file)
@@ -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);