@samitouri / QOSamiQemu / commits / 29139270d8

hw/ssi/aspeed_smc: Add Data FIFO-based flash access support for AST2700

AST2700 supports a Data FIFO mode where flash accesses can be performed directly through Data FIFO MMIO offsets. The Data FIFO start offset increments by one for every 16MB of flash address space, allowing the chip select (CS) to be decoded from the Data FIFO offset. This change adds Data FIFO support to the Aspeed SMC model and introduces a class callback to translate Data FIFO offsets into CS indices. For AST2700, the Data FIFO offset is matched against the segment start address of each CS to determine the target flash device. The SMC register region size (nregs) is also extended dynamically based on the number of supported chip selects to cover all possible Data FIFO regions. This breaks migration compatibility with older QEMU builds for the affected models, even though Aspeed machines are not officially covered by migration compatibility guarantees. Bump version_id to 4 and minimum_version_id to 2 to reflect the incompatible format. Signed-off-by: Jamin Lin <jamin_lin@aspeedtech.com> Reviewed-by: Cédric Le Goater <clg@redhat.com> Tested-by: Philippe Mathieu-Daudé <philmd@oss.qualcomm.com> Link: https://lore.kernel.org/qemu-devel/20260717084559.3477061-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater <clg@redhat.com>

Jamin Lin committed Jul 17, 2026 at 08:46 UTC 29139270d8ad70f2c422cc7cf483f465fba42397
2 files changed +109 -11
hw/ssi/aspeed_smc.c
+107 -10
@@ -163,6 +163,9 @@
163 /* Read Timing Compensation Register */
164 #define R_TIMINGS (0x94 / 4)
165
166 +/* Data fifo */
167 +#define R_DATA_FIFO (0x200 / 4)
168 +
169 /* SPI controller registers and bits (AST2400) */
170 #define R_SPI_CONF (0x00 / 4)
171 #define SPI_CONF_ENABLE_W0 0
@@ -209,6 +212,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[];
212 #define ASPEED_SMC_FEATURE_DMA_GRANT 0x2
213 #define ASPEED_SMC_FEATURE_WDT_CONTROL 0x4
214 #define ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH 0x08
215 +#define ASPEED_SMC_FEATURE_DATA_FIFO 0x10
216
217 static inline bool aspeed_smc_has_dma(const AspeedSMCClass *asc)
218 {
@@ -225,6 +229,11 @@ static inline bool aspeed_smc_has_dma64(const AspeedSMCClass *asc)
229 return !!(asc->features & ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH);
230 }
231
232 +static inline bool aspeed_smc_has_data_fifo(const AspeedSMCClass *asc)
233 +{
234 + return !!(asc->features & ASPEED_SMC_FEATURE_DATA_FIFO);
235 +}
236 +
237 #define aspeed_smc_error(fmt, ...) \
238 qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt "\n", __func__, ## __VA_ARGS__)
239
@@ -664,6 +673,7 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data,
673 {
674 AspeedSMCState *s = ASPEED_SMC(opaque);
675 AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(opaque);
676 + int cs;
677
678 addr >>= 2;
679
@@ -689,6 +699,18 @@ static MemTxResult aspeed_smc_read(void *opaque, hwaddr addr, uint64_t *data,
699 trace_aspeed_smc_read(addr << 2, size, s->regs[addr]);
700
701 *data = s->regs[addr];
702 + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) {
703 + cs = asc->data_fifo_offset_to_cs(s, addr << 2);
704 + if (cs >= 0) {
705 + /*
706 + * Data fifo mode only supports SPI user mode.
707 + * The flash address is provided by the SPI command/address cycles,
708 + * the MMIO addr parameter is ignored.
709 + */
710 + return aspeed_smc_flash_read(&s->flashes[cs], 0, data, size, attrs);
711 + }
712 + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2);
713 + return MEMTX_ERROR;
714 } else {
715 qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
716 __func__, addr);
@@ -1063,6 +1085,19 @@ static MemTxResult aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data,
1085 } else if (aspeed_smc_has_dma(asc) && aspeed_smc_has_dma64(asc) &&
1086 addr == R_DMA_DRAM_ADDR_HIGH) {
1087 s->regs[addr] = DMA_DRAM_ADDR_HIGH(value);
1088 + } else if (aspeed_smc_has_data_fifo(asc) && addr >= R_DATA_FIFO) {
1089 + int cs = asc->data_fifo_offset_to_cs(s, addr << 2);
1090 + if (cs >= 0) {
1091 + /*
1092 + * Data fifo mode only supports SPI user mode.
1093 + * The flash address is provided by the SPI command/address cycles,
1094 + * the MMIO addr parameter is ignored.
1095 + */
1096 + return aspeed_smc_flash_write(&s->flashes[cs], 0, data, size,
1097 + attrs);
1098 + }
1099 + aspeed_smc_error("Invalid data fifo offset %" HWADDR_PRIx, addr << 2);
1100 + return MEMTX_ERROR;
1101 } else {
1102 qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n",
1103 __func__, addr);
@@ -1183,8 +1218,8 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp)
1218
1219 static const VMStateDescription vmstate_aspeed_smc = {
1220 .name = "aspeed.smc",
1186 - .version_id = 3,
1187 - .minimum_version_id = 1,
1221 + .version_id = 4,
1222 + .minimum_version_id = 2,
1223 .fields = (const VMStateField[]) {
1224 VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX),
1225 VMSTATE_UNUSED_V(2, 2), /* was snoop_index/snoop_dummies */
@@ -1808,6 +1843,39 @@ static void aspeed_2700_smc_reg_to_segment(const AspeedSMCState *s,
1843 }
1844 }
1845
1846 +/*
1847 + * Convert a data fifo offset to a chip select (CS).
1848 + *
1849 + * Data fifo access starts at 0x200. The data fifo offset index is
1850 + * calculated by subtracting the data fifo base offset from the MMIO address.
1851 + *
1852 + * The data fifo offset index increments by 1 for every 16MB of flash address
1853 + * space. Each offset step therefore represents a 16MB address decode range.
1854 + *
1855 + * The CS is determined by matching the data fifo offset index against the
1856 + * segment start address of each CS.
1857 + *
1858 + * Returns the CS index on success, or -1 if the offset is invalid.
1859 + */
1860 +static int aspeed_2700_smc_data_fifo_offset_to_cs(const AspeedSMCState *s,
1861 + uint32_t offset)
1862 +{
1863 + AspeedSMCClass *asc = ASPEED_SMC_GET_CLASS(s);
1864 + uint32_t start_offset;
1865 + uint32_t fifo_offset;
1866 + int i;
1867 +
1868 + for (i = 0; i < asc->cs_num_max; i++) {
1869 + start_offset = (s->regs[R_SEG_ADDR0 + i] & 0x0000ffff) << 16;
1870 + fifo_offset = start_offset / 0x1000000;
1871 + if (fifo_offset == offset - (R_DATA_FIFO << 2)) {
1872 + return i;
1873 + }
1874 + }
1875 +
1876 + return -1;
1877 +}
1878 +
1879 static const uint32_t aspeed_2700_fmc_resets[ASPEED_SMC_R_MAX] = {
1880 [R_CONF] = (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0 |
1881 CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE1),
@@ -1842,6 +1910,27 @@ static const AspeedSegments aspeed_2700_fmc_segments[] = {
1910 { 0x0, 0 }, /* disabled */
1911 };
1912
1913 +/*
1914 + * AST2700 supports data fifo mode with a base data fifo start offset of 0x200.
1915 + *
1916 + * The data fifo start offset increments by 1 for every 16MB of flash address
1917 + * space. Each offset step therefore represents a 16MB address decode range.
1918 + *
1919 + * Assuming each chip select (CS) can use the maximum flash size of 256MB:
1920 + * 256MB / 16MB = 0x10 offset steps per CS.
1921 + *
1922 + * Data fifo start offset for CSn:
1923 + * 0x200 + (n * 0x10)
1924 + *
1925 + * Examples:
1926 + * CS0: 0x200
1927 + * CS1: 0x210
1928 + * CS2: 0x220
1929 + * CS3: 0x230
1930 + *
1931 + * asc->nregs should be set to: 0x200 + (asc->cs_num_max * 0x10)
1932 + * to cover all possible data fifo regions.
1933 + */
1934 static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data)
1935 {
1936 DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1861,14 +1950,16 @@ static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data)
1950 asc->flash_window_base = 0x100000000;
1951 asc->flash_window_size = 1 * GiB;
1952 asc->features = ASPEED_SMC_FEATURE_DMA |
1864 - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH;
1953 + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH |
1954 + ASPEED_SMC_FEATURE_DATA_FIFO;
1955 asc->dma_flash_mask = 0x2FFFFFFC;
1956 asc->dma_dram_mask = 0xFFFFFFFC;
1957 asc->dma_start_length = 1;
1868 - asc->nregs = ASPEED_SMC_R_MAX;
1958 + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2;
1959 asc->segment_to_reg = aspeed_2700_smc_segment_to_reg;
1960 asc->reg_to_segment = aspeed_2700_smc_reg_to_segment;
1961 asc->dma_ctrl = aspeed_2600_smc_dma_ctrl;
1962 + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs;
1963 asc->reg_ops = &aspeed_2700_smc_flash_ops;
1964 }
1965
@@ -1896,14 +1987,16 @@ static void aspeed_2700_spi0_class_init(ObjectClass *klass, const void *data)
1987 asc->flash_window_base = 0x180000000;
1988 asc->flash_window_size = 1 * GiB;
1989 asc->features = ASPEED_SMC_FEATURE_DMA |
1899 - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH;
1990 + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH |
1991 + ASPEED_SMC_FEATURE_DATA_FIFO;
1992 asc->dma_flash_mask = 0x2FFFFFFC;
1993 asc->dma_dram_mask = 0xFFFFFFFC;
1994 asc->dma_start_length = 1;
1903 - asc->nregs = ASPEED_SMC_R_MAX;
1995 + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2;
1996 asc->segment_to_reg = aspeed_2700_smc_segment_to_reg;
1997 asc->reg_to_segment = aspeed_2700_smc_reg_to_segment;
1998 asc->dma_ctrl = aspeed_2600_smc_dma_ctrl;
1999 + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs;
2000 asc->reg_ops = &aspeed_2700_smc_flash_ops;
2001 }
2002
@@ -1930,14 +2023,16 @@ static void aspeed_2700_spi1_class_init(ObjectClass *klass, const void *data)
2023 asc->flash_window_base = 0x200000000;
2024 asc->flash_window_size = 1 * GiB;
2025 asc->features = ASPEED_SMC_FEATURE_DMA |
1933 - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH;
2026 + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH |
2027 + ASPEED_SMC_FEATURE_DATA_FIFO;
2028 asc->dma_flash_mask = 0x2FFFFFFC;
2029 asc->dma_dram_mask = 0xFFFFFFFC;
2030 asc->dma_start_length = 1;
1937 - asc->nregs = ASPEED_SMC_R_MAX;
2031 + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2;
2032 asc->segment_to_reg = aspeed_2700_smc_segment_to_reg;
2033 asc->reg_to_segment = aspeed_2700_smc_reg_to_segment;
2034 asc->dma_ctrl = aspeed_2600_smc_dma_ctrl;
2035 + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs;
2036 asc->reg_ops = &aspeed_2700_smc_flash_ops;
2037 }
2038
@@ -1964,14 +2059,16 @@ static void aspeed_2700_spi2_class_init(ObjectClass *klass, const void *data)
2059 asc->flash_window_base = 0x280000000;
2060 asc->flash_window_size = 1 * GiB;
2061 asc->features = ASPEED_SMC_FEATURE_DMA |
1967 - ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH;
2062 + ASPEED_SMC_FEATURE_DMA_DRAM_ADDR_HIGH |
2063 + ASPEED_SMC_FEATURE_DATA_FIFO;
2064 asc->dma_flash_mask = 0x0FFFFFFC;
2065 asc->dma_dram_mask = 0xFFFFFFFC;
2066 asc->dma_start_length = 1;
1971 - asc->nregs = ASPEED_SMC_R_MAX;
2067 + asc->nregs = (0x200 + (asc->cs_num_max * 0x10)) >> 2;
2068 asc->segment_to_reg = aspeed_2700_smc_segment_to_reg;
2069 asc->reg_to_segment = aspeed_2700_smc_reg_to_segment;
2070 asc->dma_ctrl = aspeed_2600_smc_dma_ctrl;
2071 + asc->data_fifo_offset_to_cs = aspeed_2700_smc_data_fifo_offset_to_cs;
2072 asc->reg_ops = &aspeed_2700_smc_flash_ops;
2073 }
2074
include/hw/ssi/aspeed_smc.h
+2 -1
@@ -47,7 +47,7 @@ struct AspeedSMCFlash {
47 #define TYPE_ASPEED_SMC "aspeed.smc"
48 OBJECT_DECLARE_TYPE(AspeedSMCState, AspeedSMCClass, ASPEED_SMC)
49
50 -#define ASPEED_SMC_R_MAX (0x100 / 4)
50 +#define ASPEED_SMC_R_MAX (0x300 / 4)
51 #define ASPEED_SMC_CS_MAX 5
52
53 struct AspeedSMCState {
@@ -114,6 +114,7 @@ struct AspeedSMCClass {
114 AspeedSegments *seg);
115 void (*dma_ctrl)(AspeedSMCState *s, uint32_t value);
116 int (*addr_width)(const AspeedSMCState *s);
117 + int (*data_fifo_offset_to_cs)(const AspeedSMCState *s, uint32_t offset);
118 const MemoryRegionOps *reg_ops;
119 };
120