Previously, the driver mapped the entire SPI address decoding region during
probe. On systems with small flash or limited memory, this could lead to
excessive memory usage or allocation failures.
This patch changes the strategy to initially map a small address window
for SPI flash device probing. After determining each chip select's flash
size, the driver unmaps the temporary region and remaps only the required
address window accordingly.
Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
---
drivers/spi/spi-aspeed-smc.c | 39 +++++++++++++++++++++++++++++-------
1 file changed, 32 insertions(+), 7 deletions(-)
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c
index 4f6ae48dd904..0c3de371fd39 100644
--- a/drivers/spi/spi-aspeed-smc.c
+++ b/drivers/spi/spi-aspeed-smc.c
@@ -7,6 +7,7 @@
*/
#include <linux/clk.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -96,7 +97,6 @@ struct aspeed_spi {
const struct aspeed_spi_data *data;
void __iomem *regs;
- void __iomem *ahb_base;
u32 ahb_base_phy;
u32 ahb_window_size;
u32 num_cs;
@@ -394,6 +394,13 @@ static int aspeed_spi_set_window(struct aspeed_spi *aspi)
u32 cs;
size_t window_size;
+ for (cs = 0; cs < aspi->data->max_cs; cs++) {
+ if (aspi->chips[cs].ahb_base) {
+ iounmap(aspi->chips[cs].ahb_base);
+ aspi->chips[cs].ahb_base = NULL;
+ }
+ }
+
for (cs = 0; cs < aspi->data->max_cs; cs++) {
seg_reg = seg_reg_base + cs * 4;
seg_val_backup = readl(seg_reg);
@@ -425,13 +432,29 @@ static int aspeed_spi_set_window(struct aspeed_spi *aspi)
else
dev_dbg(dev, "CE%d window closed\n", cs);
- aspi->chips[cs].ahb_base = aspi->ahb_base + offset;
offset += window_size;
if (offset > aspi->ahb_window_size) {
dev_err(dev, "CE%d offset value 0x%llx is too large.\n",
cs, (u64)offset);
return -ENOSPC;
}
+
+ /*
+ * No need to map the address deocding range when
+ * - window size is 0.
+ * - the CS is unused.
+ */
+ if (window_size == 0 || cs >= aspi->num_cs)
+ continue;
+
+ aspi->chips[cs].ahb_base =
+ devm_ioremap(aspi->dev, start, window_size);
+ if (!aspi->chips[cs].ahb_base) {
+ dev_err(aspi->dev,
+ "Fail to remap window [0x%.9llx - 0x%.9llx]\n",
+ (u64)start, (u64)end - 1);
+ return -ENOMEM;
+ }
}
return 0;
@@ -447,7 +470,9 @@ static int aspeed_spi_chip_set_default_window(struct aspeed_spi *aspi)
/* No segment registers for the AST2400 SPI controller */
if (aspi->data == &ast2400_spi_data) {
- aspi->chips[0].ahb_base = aspi->ahb_base;
+ aspi->chips[0].ahb_base = devm_ioremap(aspi->dev,
+ aspi->ahb_base_phy,
+ aspi->ahb_window_size);
aspi->chips[0].ahb_window_size = aspi->ahb_window_size;
return 0;
}
@@ -839,10 +864,10 @@ static int aspeed_spi_probe(struct platform_device *pdev)
if (IS_ERR(aspi->regs))
return PTR_ERR(aspi->regs);
- aspi->ahb_base = devm_platform_get_and_ioremap_resource(pdev, 1, &res);
- if (IS_ERR(aspi->ahb_base)) {
- dev_err(dev, "missing AHB mapping window\n");
- return PTR_ERR(aspi->ahb_base);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (IS_ERR(res)) {
+ dev_err(dev, "missing AHB memory\n");
+ return PTR_ERR(res);
}
aspi->ahb_window_size = resource_size(res);
--
2.34.1