diff options
author | Chin-Ting Kuo | 2022-08-19 17:01:12 +0800 |
---|---|---|
committer | Tom Rini | 2022-09-13 12:08:41 -0400 |
commit | 15a5c806a31522ab940f4ed252c917d7a77f8200 (patch) | |
tree | 416a6063d9d1248d0ccd74176360e0e042c1be91 /drivers/spi/spi-aspeed-smc.c | |
parent | 99325ee0568aa12f79b044ffcfc80c8ff348841d (diff) |
spi: aspeed: Adjust decoded range size support
There are some known HW problems about decoded
range register configurations on existing AST2500 and
AST2600 platforms. Additional callback function,
adjust_decoded_sz, is added to solve these problems
on each platform. Besides, aspeed_spi_trim_decoded_size
function is added to modify overall decoded address
size for fitting the maximum AHB decoded size.
Signed-off-by: Chin-Ting Kuo <chin-ting_kuo@aspeedtech.com>
Diffstat (limited to 'drivers/spi/spi-aspeed-smc.c')
-rw-r--r-- | drivers/spi/spi-aspeed-smc.c | 144 |
1 files changed, 143 insertions, 1 deletions
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index 6099b852551..4287f5d8c0a 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -84,10 +84,12 @@ struct aspeed_spi_info { u32 (*segment_start)(struct udevice *bus, u32 reg); u32 (*segment_end)(struct udevice *bus, u32 reg); u32 (*segment_reg)(u32 start, u32 end); + int (*adjust_decoded_sz)(struct udevice *bus); }; static const struct aspeed_spi_info ast2400_spi_info; static int aspeed_spi_decoded_range_config(struct udevice *bus); +static int aspeed_spi_trim_decoded_size(struct udevice *bus); static u32 aspeed_spi_get_io_mode(u32 bus_width) { @@ -195,6 +197,53 @@ static void ast2500_spi_chip_set_4byte(struct udevice *bus, u32 cs) writel(reg_val, &priv->regs->ctrl); } +/* + * For AST2500, the minimum address decoded size for each CS + * is 8MB instead of zero. This address decoded size is + * mandatory for each CS no matter whether it will be used. + * This is a HW limitation. + */ +static int ast2500_adjust_decoded_size(struct udevice *bus) +{ + struct aspeed_spi_plat *plat = dev_get_plat(bus); + struct aspeed_spi_priv *priv = dev_get_priv(bus); + struct aspeed_spi_flash *flashes = &priv->flashes[0]; + int ret; + int i; + int cs; + u32 pre_sz; + u32 lack_sz; + + /* Assign min_decoded_sz to unused CS. */ + for (cs = priv->num_cs; cs < plat->max_cs; cs++) + flashes[cs].ahb_decoded_sz = priv->info->min_decoded_sz; + + /* + * If commnad mode or normal mode is used, the start address of a + * decoded range should be multiple of its related flash size. + * Namely, the total decoded size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). + */ + for (cs = priv->num_cs - 1; cs >= 0; cs--) { + pre_sz = 0; + for (i = 0; i < cs; i++) + pre_sz += flashes[i].ahb_decoded_sz; + + if (flashes[cs].ahb_decoded_sz != 0 && + (pre_sz % flashes[cs].ahb_decoded_sz) != 0) { + lack_sz = flashes[cs].ahb_decoded_sz - + (pre_sz % flashes[cs].ahb_decoded_sz); + flashes[0].ahb_decoded_sz += lack_sz; + } + } + + ret = aspeed_spi_trim_decoded_size(bus); + if (ret != 0) + return ret; + + return 0; +} + static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); @@ -236,6 +285,86 @@ static void ast2600_spi_chip_set_4byte(struct udevice *bus, u32 cs) writel(reg_val, &priv->regs->ctrl); } +static int ast2600_adjust_decoded_size(struct udevice *bus) +{ + struct aspeed_spi_plat *plat = dev_get_plat(bus); + struct aspeed_spi_priv *priv = dev_get_priv(bus); + struct aspeed_spi_flash *flashes = &priv->flashes[0]; + int ret; + int i; + int cs; + u32 pre_sz; + u32 lack_sz; + + /* Close unused CS. */ + for (cs = priv->num_cs; cs < plat->max_cs; cs++) + flashes[cs].ahb_decoded_sz = 0; + + /* + * If commnad mode or normal mode is used, the start address of a + * decoded range should be multiple of its related flash size. + * Namely, the total decoded size from flash 0 to flash N should + * be multiple of the size of flash (N + 1). + */ + for (cs = priv->num_cs - 1; cs >= 0; cs--) { + pre_sz = 0; + for (i = 0; i < cs; i++) + pre_sz += flashes[i].ahb_decoded_sz; + + if (flashes[cs].ahb_decoded_sz != 0 && + (pre_sz % flashes[cs].ahb_decoded_sz) != 0) { + lack_sz = flashes[cs].ahb_decoded_sz - + (pre_sz % flashes[cs].ahb_decoded_sz); + flashes[0].ahb_decoded_sz += lack_sz; + } + } + + ret = aspeed_spi_trim_decoded_size(bus); + if (ret != 0) + return ret; + + return 0; +} + +/* + * As the flash size grows up, we need to trim some decoded + * size if needed for the sake of conforming the maximum + * decoded size. We trim the decoded size from the largest + * CS in order to avoid affecting the default boot up sequence + * from CS0 where command mode or normal mode is used. + * Notice, if a CS decoded size is trimmed, command mode may + * not work perfectly on that CS. + */ +static int aspeed_spi_trim_decoded_size(struct udevice *bus) +{ + struct aspeed_spi_plat *plat = dev_get_plat(bus); + struct aspeed_spi_priv *priv = dev_get_priv(bus); + struct aspeed_spi_flash *flashes = &priv->flashes[0]; + u32 total_sz; + int cs = plat->max_cs - 1; + u32 i; + + do { + total_sz = 0; + for (i = 0; i < plat->max_cs; i++) + total_sz += flashes[i].ahb_decoded_sz; + + if (flashes[cs].ahb_decoded_sz <= priv->info->min_decoded_sz) + cs--; + + if (cs < 0) + return -ENOMEM; + + if (total_sz > plat->ahb_sz) { + flashes[cs].ahb_decoded_sz -= + priv->info->min_decoded_sz; + total_sz -= priv->info->min_decoded_sz; + } + } while (total_sz > plat->ahb_sz); + + return 0; +} + static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf, size_t len) { @@ -522,10 +651,19 @@ static void aspeed_spi_decoded_range_set(struct udevice *bus) 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) { + ret = priv->info->adjust_decoded_sz(bus); + if (ret != 0) + return ret; + } + aspeed_spi_decoded_base_calculate(bus); aspeed_spi_decoded_range_set(bus); - return 0; + return ret; } /* @@ -616,6 +754,7 @@ static const struct aspeed_spi_info ast2500_fmc_info = { .segment_start = ast2500_spi_segment_start, .segment_end = ast2500_spi_segment_end, .segment_reg = ast2500_spi_segment_reg, + .adjust_decoded_sz = ast2500_adjust_decoded_size, }; /* @@ -630,6 +769,7 @@ static const struct aspeed_spi_info ast2500_spi_info = { .segment_start = ast2500_spi_segment_start, .segment_end = ast2500_spi_segment_end, .segment_reg = ast2500_spi_segment_reg, + .adjust_decoded_sz = ast2500_adjust_decoded_size, }; static const struct aspeed_spi_info ast2600_fmc_info = { @@ -640,6 +780,7 @@ static const struct aspeed_spi_info ast2600_fmc_info = { .segment_start = ast2600_spi_segment_start, .segment_end = ast2600_spi_segment_end, .segment_reg = ast2600_spi_segment_reg, + .adjust_decoded_sz = ast2600_adjust_decoded_size, }; static const struct aspeed_spi_info ast2600_spi_info = { @@ -650,6 +791,7 @@ static const struct aspeed_spi_info ast2600_spi_info = { .segment_start = ast2600_spi_segment_start, .segment_end = ast2600_spi_segment_end, .segment_reg = ast2600_spi_segment_reg, + .adjust_decoded_sz = ast2600_adjust_decoded_size, }; static int aspeed_spi_claim_bus(struct udevice *dev) |