diff options
| author | Ryan Chen <ryan_chen@aspeedtech.com> | 2026-06-12 17:43:08 +0800 |
|---|---|---|
| committer | Tom Rini <trini@konsulko.com> | 2026-06-29 13:43:20 -0600 |
| commit | 3fa1b49c940fffa5566560d5134f74b063645b7b (patch) | |
| tree | 9dd82842f70b43af6d8282b2c1784b6bf0a0188d | |
| parent | 3cdce049f90d77c3f9221f1eb73922384c8972c3 (diff) | |
| download | u-boot-3fa1b49c940fffa5566560d5134f74b063645b7b.tar.gz u-boot-3fa1b49c940fffa5566560d5134f74b063645b7b.zip | |
spi: aspeed: add AST2700 support
AST2700 is a 64-bit SoC whose flash AHB windows are decoded above
the 32-bit address space, so rework AHB addresses to uintptr_t and
decoded window sizes to size_t.
Signed-off-by: Ryan Chen <ryan_chen@aspeedtech.com>
| -rw-r--r-- | drivers/spi/spi-aspeed-smc.c | 219 |
1 files changed, 158 insertions, 61 deletions
diff --git a/drivers/spi/spi-aspeed-smc.c b/drivers/spi/spi-aspeed-smc.c index ca29cfd7c88..0186b01ad9a 100644 --- a/drivers/spi/spi-aspeed-smc.c +++ b/drivers/spi/spi-aspeed-smc.c @@ -53,18 +53,20 @@ struct aspeed_spi_regs { u32 dma_len; /* 0x8c DMA Length Register */ u32 dma_checksum; /* 0x90 Checksum Calculation Result */ u32 timings[ASPEED_SPI_MAX_CS]; /* 0x94 Read Timing Compensation */ + u32 _reserved3[83]; /* 0xA8 - 0x1F0 */ + u32 val_kept_wdt; /* 0x1F4 Value Kept WDT */ }; struct aspeed_spi_plat { u8 max_cs; - void __iomem *ahb_base; /* AHB address base for all flash devices. */ + uintptr_t ahb_base; /* AHB address base for all flash devices. */ fdt_size_t ahb_sz; /* Overall AHB window size for all flash device. */ u32 hclk_rate; /* AHB clock rate */ }; struct aspeed_spi_flash { - void __iomem *ahb_base; - u32 ahb_decoded_sz; + uintptr_t ahb_base; + size_t ahb_decoded_sz; u32 ce_ctrl_user; u32 ce_ctrl_read; u32 max_freq; @@ -84,9 +86,9 @@ struct aspeed_spi_info { u32 min_decoded_sz; u32 clk_ctrl_mask; void (*set_4byte)(struct udevice *bus, u32 cs); - u32 (*segment_start)(struct udevice *bus, u32 reg); - u32 (*segment_end)(struct udevice *bus, u32 reg); - u32 (*segment_reg)(u32 start, u32 end); + uintptr_t (*segment_start)(struct udevice *bus, u32 reg); + uintptr_t (*segment_end)(struct udevice *bus, u32 reg); + u32 (*segment_reg)(uintptr_t start, uintptr_t end); int (*adjust_decoded_sz)(struct udevice *bus); u32 (*get_clk_setting)(struct udevice *dev, uint hz); }; @@ -118,30 +120,30 @@ static u32 aspeed_spi_get_io_mode(u32 bus_width) } } -static u32 ast2400_spi_segment_start(struct udevice *bus, u32 reg) +static uintptr_t ast2400_spi_segment_start(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 start_offset = ((reg >> 16) & 0xff) << 23; + uintptr_t start_offset = ((reg >> 16) & 0xff) << 23; if (start_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + start_offset; + return plat->ahb_base + start_offset; } -static u32 ast2400_spi_segment_end(struct udevice *bus, u32 reg) +static uintptr_t ast2400_spi_segment_end(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 end_offset = ((reg >> 24) & 0xff) << 23; + uintptr_t end_offset = ((reg >> 24) & 0xff) << 23; /* Meaningless end_offset, set to physical ahb base. */ if (end_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + end_offset; + return plat->ahb_base + end_offset; } -static u32 ast2400_spi_segment_reg(u32 start, u32 end) +static u32 ast2400_spi_segment_reg(uintptr_t start, uintptr_t end) { if (start == end) return 0; @@ -206,30 +208,30 @@ static u32 ast2400_get_clk_setting(struct udevice *dev, uint max_hz) return hclk_div; } -static u32 ast2500_spi_segment_start(struct udevice *bus, u32 reg) +static uintptr_t ast2500_spi_segment_start(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 start_offset = ((reg >> 16) & 0xff) << 23; + uintptr_t start_offset = ((reg >> 16) & 0xff) << 23; if (start_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + start_offset; + return plat->ahb_base + start_offset; } -static u32 ast2500_spi_segment_end(struct udevice *bus, u32 reg) +static uintptr_t ast2500_spi_segment_end(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 end_offset = ((reg >> 24) & 0xff) << 23; + uintptr_t end_offset = ((reg >> 24) & 0xff) << 23; /* Meaningless end_offset, set to physical ahb base. */ if (end_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + end_offset; + return plat->ahb_base + end_offset; } -static u32 ast2500_spi_segment_reg(u32 start, u32 end) +static u32 ast2500_spi_segment_reg(uintptr_t start, uintptr_t end) { if (start == end) return 0; @@ -346,30 +348,30 @@ end: return hclk_div; } -static u32 ast2600_spi_segment_start(struct udevice *bus, u32 reg) +static uintptr_t ast2600_spi_segment_start(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 start_offset = (reg << 16) & 0x0ff00000; + uintptr_t start_offset = (reg << 16) & 0x0ff00000; if (start_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + start_offset; + return plat->ahb_base + start_offset; } -static u32 ast2600_spi_segment_end(struct udevice *bus, u32 reg) +static uintptr_t ast2600_spi_segment_end(struct udevice *bus, u32 reg) { struct aspeed_spi_plat *plat = dev_get_plat(bus); - u32 end_offset = reg & 0x0ff00000; + uintptr_t end_offset = reg & 0x0ff00000; /* Meaningless end_offset, set to physical ahb base. */ if (end_offset == 0) - return (u32)plat->ahb_base; + return plat->ahb_base; - return (u32)plat->ahb_base + end_offset + 0x100000; + return plat->ahb_base + end_offset + 0x100000; } -static u32 ast2600_spi_segment_reg(u32 start, u32 end) +static u32 ast2600_spi_segment_reg(uintptr_t start, uintptr_t end) { if (start == end) return 0; @@ -473,6 +475,70 @@ static u32 ast2600_get_clk_setting(struct udevice *dev, uint max_hz) return hclk_div; } +static uintptr_t ast2700_spi_segment_start(struct udevice *bus, u32 reg) +{ + struct aspeed_spi_plat *plat = dev_get_plat(bus); + uintptr_t start_offset = (reg & 0x0000ffff) << 16; + + if (start_offset == 0) + return plat->ahb_base; + + return plat->ahb_base + start_offset; +} + +static uintptr_t ast2700_spi_segment_end(struct udevice *bus, u32 reg) +{ + struct aspeed_spi_plat *plat = dev_get_plat(bus); + uintptr_t end_offset = reg & 0xffff0000; + + /* Meaningless end_offset, set to physical ahb base. */ + if (end_offset == 0) + return plat->ahb_base; + + return plat->ahb_base + end_offset; +} + +static u32 ast2700_spi_segment_reg(uintptr_t start, uintptr_t end) +{ + if (start == end) + return 0; + + return (((start >> 16) & 0x7fff) | ((end + 1) & 0x7fff0000)); +} + +static void ast2700_spi_chip_set_4byte(struct udevice *bus, u32 cs) +{ + struct aspeed_spi_priv *priv = dev_get_priv(bus); + u32 reg_val; + + reg_val = readl(&priv->regs->ctrl); + reg_val |= 0x11 << cs; + writel(reg_val, &priv->regs->ctrl); + + reg_val = readl(&priv->regs->val_kept_wdt); + reg_val |= (0x11 << 4) << cs; + writel(reg_val, &priv->regs->val_kept_wdt); +} + +static int ast2700_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 cs; + + /* Close unused CS. */ + for (cs = priv->num_cs; cs < plat->max_cs; cs++) + flashes[cs].ahb_decoded_sz = 0; + + 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 @@ -512,12 +578,12 @@ static int aspeed_spi_trim_decoded_size(struct udevice *bus) return 0; } -static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf, +static int aspeed_spi_read_from_ahb(uintptr_t ahb_base, void *buf, size_t len) { size_t offset = 0; - if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) && + if (IS_ALIGNED(ahb_base, sizeof(uintptr_t)) && IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { readsl(ahb_base, buf, len >> 2); offset = len & ~0x3; @@ -529,12 +595,12 @@ static int aspeed_spi_read_from_ahb(void __iomem *ahb_base, void *buf, return 0; } -static int aspeed_spi_write_to_ahb(void __iomem *ahb_base, const void *buf, +static int aspeed_spi_write_to_ahb(uintptr_t ahb_base, const void *buf, size_t len) { size_t offset = 0; - if (IS_ALIGNED((uintptr_t)ahb_base, sizeof(uintptr_t)) && + if (IS_ALIGNED(ahb_base, sizeof(uintptr_t)) && IS_ALIGNED((uintptr_t)buf, sizeof(uintptr_t))) { writesl(ahb_base, buf, len >> 2); offset = len & ~0x3; @@ -589,7 +655,7 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave, struct aspeed_spi_priv *priv = dev_get_priv(bus); struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(slave->dev); u32 cs = slave_plat->cs[0]; - u32 ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs]; + uintptr_t ce_ctrl_reg = (uintptr_t)&priv->regs->ce_ctrl[cs]; u32 ce_ctrl_val; struct aspeed_spi_flash *flash = &priv->flashes[cs]; u8 dummy_data[16] = {0}; @@ -602,7 +668,7 @@ static int aspeed_spi_exec_op_user_mode(struct spi_slave *slave, op->data.nbytes, op->data.buswidth); if (priv->info == &ast2400_spi_info) - ce_ctrl_reg = (u32)&priv->regs->ctrl; + ce_ctrl_reg = (uintptr_t)&priv->regs->ctrl; /* * Set controller to 4-byte address mode @@ -670,7 +736,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) u32 i; u32 cs = slave_plat->cs[0]; u32 cmd_io_conf; - u32 ce_ctrl_reg; + uintptr_t ce_ctrl_reg; if (desc->info.op_tmpl.data.dir == SPI_MEM_DATA_OUT) { /* @@ -681,9 +747,9 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) return -EOPNOTSUPP; } - ce_ctrl_reg = (u32)&priv->regs->ce_ctrl[cs]; + ce_ctrl_reg = (uintptr_t)&priv->regs->ce_ctrl[cs]; if (info == &ast2400_spi_info) - ce_ctrl_reg = (u32)&priv->regs->ctrl; + ce_ctrl_reg = (uintptr_t)&priv->regs->ctrl; if (desc->info.length > 0x1000000) priv->info->set_4byte(bus, cs); @@ -693,7 +759,7 @@ static int aspeed_spi_dirmap_create(struct spi_mem_dirmap_desc *desc) priv->flashes[cs].ahb_decoded_sz = desc->info.length; for (i = 0; i < priv->num_cs; i++) { - dev_dbg(dev, "cs: %d, sz: 0x%x\n", i, + dev_dbg(dev, "cs: %d, sz: 0x%zx\n", i, priv->flashes[cs].ahb_decoded_sz); } @@ -728,7 +794,7 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, u32 cs = slave_plat->cs[0]; int ret; - dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%x\n", + dev_dbg(dev, "read op:0x%x, addr:0x%llx, len:0x%zx\n", desc->info.op_tmpl.cmd.opcode, offs, len); if (priv->flashes[cs].ahb_decoded_sz < offs + len || @@ -738,7 +804,10 @@ static ssize_t aspeed_spi_dirmap_read(struct spi_mem_dirmap_desc *desc, if (ret != 0) return 0; } else { - memcpy_fromio(buf, priv->flashes[cs].ahb_base + offs, len); + memcpy_fromio(buf, + (void __iomem *)(priv->flashes[cs].ahb_base + + (uintptr_t)offs), + len); } return len; @@ -783,19 +852,19 @@ static void aspeed_spi_decoded_range_set(struct udevice *bus) struct aspeed_spi_plat *plat = dev_get_plat(bus); struct aspeed_spi_priv *priv = dev_get_priv(bus); u32 decoded_reg_val; - u32 start_addr, end_addr; + uintptr_t start_addr, end_addr; u32 cs; for (cs = 0; cs < plat->max_cs; cs++) { - start_addr = (u32)priv->flashes[cs].ahb_base; - end_addr = (u32)priv->flashes[cs].ahb_base + + start_addr = priv->flashes[cs].ahb_base; + end_addr = priv->flashes[cs].ahb_base + priv->flashes[cs].ahb_decoded_sz; decoded_reg_val = priv->info->segment_reg(start_addr, end_addr); writel(decoded_reg_val, &priv->regs->segment_addr[cs]); - dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%x, end: 0x%x\n", + dev_dbg(bus, "cs: %d, decoded_reg: 0x%x, start: 0x%lx, end: 0x%lx\n", cs, decoded_reg_val, start_addr, end_addr); } } @@ -851,13 +920,13 @@ static int aspeed_spi_decoded_ranges_sanity(struct udevice *bus) * 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 + + if (priv->flashes[cs].ahb_base != 0 && + priv->flashes[cs].ahb_base < + 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); + dev_err(bus, "decoded range overlay 0x%08lx 0x%08lx\n", + priv->flashes[cs].ahb_base, + priv->flashes[cs - 1].ahb_base); return -EINVAL; } } @@ -895,14 +964,13 @@ static int aspeed_spi_read_fixed_decoded_ranges(struct udevice *bus) 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_base = 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", + dev_dbg(bus, "ahb_base: 0x%lx, size: 0x%08zx\n", priv->flashes[i].ahb_base, priv->flashes[i].ahb_decoded_sz); } @@ -1063,6 +1131,32 @@ static const struct aspeed_spi_info ast2600_spi_info = { .get_clk_setting = ast2600_get_clk_setting, }; +static const struct aspeed_spi_info ast2700_fmc_info = { + .io_mode_mask = 0xf0000000, + .max_bus_width = 4, + .min_decoded_sz = 0x10000, + .clk_ctrl_mask = 0x0f000f00, + .set_4byte = ast2700_spi_chip_set_4byte, + .segment_start = ast2700_spi_segment_start, + .segment_end = ast2700_spi_segment_end, + .segment_reg = ast2700_spi_segment_reg, + .adjust_decoded_sz = ast2700_adjust_decoded_size, + .get_clk_setting = ast2600_get_clk_setting, +}; + +static const struct aspeed_spi_info ast2700_spi_info = { + .io_mode_mask = 0xf0000000, + .max_bus_width = 4, + .min_decoded_sz = 0x10000, + .clk_ctrl_mask = 0x0f000f00, + .set_4byte = ast2700_spi_chip_set_4byte, + .segment_start = ast2700_spi_segment_start, + .segment_end = ast2700_spi_segment_end, + .segment_reg = ast2700_spi_segment_reg, + .adjust_decoded_sz = ast2700_adjust_decoded_size, + .get_clk_setting = ast2600_get_clk_setting, +}; + static int aspeed_spi_claim_bus(struct udevice *dev) { struct udevice *bus = dev->parent; @@ -1129,7 +1223,8 @@ static int apseed_spi_of_to_plat(struct udevice *bus) return -EINVAL; } - plat->ahb_base = devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahb_sz); + plat->ahb_base = + (uintptr_t)devfdt_get_addr_size_index_ptr(bus, 1, &plat->ahb_sz); if (!plat->ahb_base) { dev_err(bus, "wrong AHB base\n"); return -EINVAL; @@ -1147,8 +1242,8 @@ static int apseed_spi_of_to_plat(struct udevice *bus) plat->hclk_rate = clk_get_rate(&hclk); - dev_dbg(bus, "ctrl_base = 0x%x, ahb_base = 0x%p, size = 0x%llx\n", - (u32)priv->regs, plat->ahb_base, (fdt64_t)plat->ahb_sz); + dev_dbg(bus, "ctrl_base = 0x%p, ahb_base = 0x%lx, size = 0x%llx\n", + priv->regs, plat->ahb_base, (fdt64_t)plat->ahb_sz); dev_dbg(bus, "hclk = %dMHz, max_cs = %d\n", plat->hclk_rate / 1000000, plat->max_cs); @@ -1199,6 +1294,8 @@ static const struct udevice_id aspeed_spi_ids[] = { { .compatible = "aspeed,ast2500-spi", .data = (ulong)&ast2500_spi_info, }, { .compatible = "aspeed,ast2600-fmc", .data = (ulong)&ast2600_fmc_info, }, { .compatible = "aspeed,ast2600-spi", .data = (ulong)&ast2600_spi_info, }, + { .compatible = "aspeed,ast2700-fmc", .data = (ulong)&ast2700_fmc_info, }, + { .compatible = "aspeed,ast2700-spi", .data = (ulong)&ast2700_spi_info, }, { } }; |
