2 * SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2024 IBM Corp.
5 * ASPEED APB-OPB FSI interface
6 * IBM On-chip Peripheral Bus
9 #include "qemu/osdep.h"
11 #include "qom/object.h"
12 #include "qapi/error.h"
15 #include "hw/fsi/aspeed_apb2opb.h"
16 #include "hw/qdev-core.h"
18 #define TO_REG(x) (x >> 2)
20 #define APB2OPB_VERSION TO_REG(0x00)
21 #define APB2OPB_TRIGGER TO_REG(0x04)
23 #define APB2OPB_CONTROL TO_REG(0x08)
24 #define APB2OPB_CONTROL_OFF BE_GENMASK(31, 13)
26 #define APB2OPB_OPB2FSI TO_REG(0x0c)
27 #define APB2OPB_OPB2FSI_OFF BE_GENMASK(31, 22)
29 #define APB2OPB_OPB0_SEL TO_REG(0x10)
30 #define APB2OPB_OPB1_SEL TO_REG(0x28)
31 #define APB2OPB_OPB_SEL_EN BIT(0)
33 #define APB2OPB_OPB0_MODE TO_REG(0x14)
34 #define APB2OPB_OPB1_MODE TO_REG(0x2c)
35 #define APB2OPB_OPB_MODE_RD BIT(0)
37 #define APB2OPB_OPB0_XFER TO_REG(0x18)
38 #define APB2OPB_OPB1_XFER TO_REG(0x30)
39 #define APB2OPB_OPB_XFER_FULL BIT(1)
40 #define APB2OPB_OPB_XFER_HALF BIT(0)
42 #define APB2OPB_OPB0_ADDR TO_REG(0x1c)
43 #define APB2OPB_OPB0_WRITE_DATA TO_REG(0x20)
45 #define APB2OPB_OPB1_ADDR TO_REG(0x34)
46 #define APB2OPB_OPB1_WRITE_DATA TO_REG(0x38)
48 #define APB2OPB_IRQ_STS TO_REG(0x48)
49 #define APB2OPB_IRQ_STS_OPB1_TX_ACK BIT(17)
50 #define APB2OPB_IRQ_STS_OPB0_TX_ACK BIT(16)
52 #define APB2OPB_OPB0_WRITE_WORD_ENDIAN TO_REG(0x4c)
53 #define APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE 0x0011101b
54 #define APB2OPB_OPB0_WRITE_BYTE_ENDIAN TO_REG(0x50)
55 #define APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE 0x0c330f3f
56 #define APB2OPB_OPB1_WRITE_WORD_ENDIAN TO_REG(0x54)
57 #define APB2OPB_OPB1_WRITE_BYTE_ENDIAN TO_REG(0x58)
58 #define APB2OPB_OPB0_READ_BYTE_ENDIAN TO_REG(0x5c)
59 #define APB2OPB_OPB1_READ_BYTE_ENDIAN TO_REG(0x60)
60 #define APB2OPB_OPB0_READ_WORD_ENDIAN_BE 0x00030b1b
62 #define APB2OPB_OPB0_READ_DATA TO_REG(0x84)
63 #define APB2OPB_OPB1_READ_DATA TO_REG(0x90)
66 * The following magic values came from AST2600 data sheet
67 * The register values are defined under section "FSI controller"
70 static const uint32_t aspeed_apb2opb_reset
[ASPEED_APB2OPB_NR_REGS
] = {
71 [APB2OPB_VERSION
] = 0x000000a1,
72 [APB2OPB_OPB0_WRITE_WORD_ENDIAN
] = 0x0044eee4,
73 [APB2OPB_OPB0_WRITE_BYTE_ENDIAN
] = 0x0055aaff,
74 [APB2OPB_OPB1_WRITE_WORD_ENDIAN
] = 0x00117717,
75 [APB2OPB_OPB1_WRITE_BYTE_ENDIAN
] = 0xffaa5500,
76 [APB2OPB_OPB0_READ_BYTE_ENDIAN
] = 0x0044eee4,
77 [APB2OPB_OPB1_READ_BYTE_ENDIAN
] = 0x00117717
80 static void fsi_opb_fsi_master_address(FSIMasterState
*fsi
, hwaddr addr
)
82 memory_region_transaction_begin();
83 memory_region_set_address(&fsi
->iomem
, addr
);
84 memory_region_transaction_commit();
87 static void fsi_opb_opb2fsi_address(FSIMasterState
*fsi
, hwaddr addr
)
89 memory_region_transaction_begin();
90 memory_region_set_address(&fsi
->opb2fsi
, addr
);
91 memory_region_transaction_commit();
94 static uint64_t fsi_aspeed_apb2opb_read(void *opaque
, hwaddr addr
,
97 AspeedAPB2OPBState
*s
= ASPEED_APB2OPB(opaque
);
98 unsigned int reg
= TO_REG(addr
);
100 trace_fsi_aspeed_apb2opb_read(addr
, size
);
102 if (reg
>= ASPEED_APB2OPB_NR_REGS
) {
103 qemu_log_mask(LOG_GUEST_ERROR
,
104 "%s: Out of bounds read: 0x%"HWADDR_PRIx
" for %u\n",
105 __func__
, addr
, size
);
112 static MemTxResult
fsi_aspeed_apb2opb_rw(AddressSpace
*as
, hwaddr addr
,
113 MemTxAttrs attrs
, uint32_t *data
,
114 uint32_t size
, bool is_write
)
121 address_space_stl_le(as
, addr
, *data
, attrs
, &res
);
124 address_space_stw_le(as
, addr
, *data
, attrs
, &res
);
127 address_space_stb(as
, addr
, *data
, attrs
, &res
);
130 g_assert_not_reached();
135 *data
= address_space_ldl_le(as
, addr
, attrs
, &res
);
138 *data
= address_space_lduw_le(as
, addr
, attrs
, &res
);
141 *data
= address_space_ldub(as
, addr
, attrs
, &res
);
144 g_assert_not_reached();
150 static void fsi_aspeed_apb2opb_write(void *opaque
, hwaddr addr
, uint64_t data
,
153 AspeedAPB2OPBState
*s
= ASPEED_APB2OPB(opaque
);
154 unsigned int reg
= TO_REG(addr
);
156 trace_fsi_aspeed_apb2opb_write(addr
, size
, data
);
158 if (reg
>= ASPEED_APB2OPB_NR_REGS
) {
159 qemu_log_mask(LOG_GUEST_ERROR
,
160 "%s: Out of bounds write: %"HWADDR_PRIx
" for %u\n",
161 __func__
, addr
, size
);
166 case APB2OPB_CONTROL
:
167 fsi_opb_fsi_master_address(&s
->fsi
[0],
168 data
& APB2OPB_CONTROL_OFF
);
170 case APB2OPB_OPB2FSI
:
171 fsi_opb_opb2fsi_address(&s
->fsi
[0],
172 data
& APB2OPB_OPB2FSI_OFF
);
174 case APB2OPB_OPB0_WRITE_WORD_ENDIAN
:
175 if (data
!= APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE
) {
176 qemu_log_mask(LOG_GUEST_ERROR
,
177 "%s: Bridge needs to be driven as BE (0x%x)\n",
178 __func__
, APB2OPB_OPB0_WRITE_WORD_ENDIAN_BE
);
181 case APB2OPB_OPB0_WRITE_BYTE_ENDIAN
:
182 if (data
!= APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE
) {
183 qemu_log_mask(LOG_GUEST_ERROR
,
184 "%s: Bridge needs to be driven as BE (0x%x)\n",
185 __func__
, APB2OPB_OPB0_WRITE_BYTE_ENDIAN_BE
);
188 case APB2OPB_OPB0_READ_BYTE_ENDIAN
:
189 if (data
!= APB2OPB_OPB0_READ_WORD_ENDIAN_BE
) {
190 qemu_log_mask(LOG_GUEST_ERROR
,
191 "%s: Bridge needs to be driven as BE (0x%x)\n",
192 __func__
, APB2OPB_OPB0_READ_WORD_ENDIAN_BE
);
195 case APB2OPB_TRIGGER
:
197 uint32_t opb
, op_mode
, op_size
, op_addr
, op_data
;
203 assert((s
->regs
[APB2OPB_OPB0_SEL
] & APB2OPB_OPB_SEL_EN
) ^
204 (s
->regs
[APB2OPB_OPB1_SEL
] & APB2OPB_OPB_SEL_EN
));
206 if (s
->regs
[APB2OPB_OPB0_SEL
] & APB2OPB_OPB_SEL_EN
) {
208 op_mode
= s
->regs
[APB2OPB_OPB0_MODE
];
209 op_size
= s
->regs
[APB2OPB_OPB0_XFER
];
210 op_addr
= s
->regs
[APB2OPB_OPB0_ADDR
];
211 op_data
= s
->regs
[APB2OPB_OPB0_WRITE_DATA
];
212 } else if (s
->regs
[APB2OPB_OPB1_SEL
] & APB2OPB_OPB_SEL_EN
) {
214 op_mode
= s
->regs
[APB2OPB_OPB1_MODE
];
215 op_size
= s
->regs
[APB2OPB_OPB1_XFER
];
216 op_addr
= s
->regs
[APB2OPB_OPB1_ADDR
];
217 op_data
= s
->regs
[APB2OPB_OPB1_WRITE_DATA
];
219 qemu_log_mask(LOG_GUEST_ERROR
,
220 "%s: Invalid operation: 0x%"HWADDR_PRIx
" for %u\n",
221 __func__
, addr
, size
);
225 if (op_size
& ~(APB2OPB_OPB_XFER_HALF
| APB2OPB_OPB_XFER_FULL
)) {
226 qemu_log_mask(LOG_GUEST_ERROR
,
227 "OPB transaction failed: Unrecognized access width: %d\n",
233 is_write
= !(op_mode
& APB2OPB_OPB_MODE_RD
);
234 index
= opb
? APB2OPB_OPB1_READ_DATA
: APB2OPB_OPB0_READ_DATA
;
235 as
= &s
->opb
[opb
].as
;
237 result
= fsi_aspeed_apb2opb_rw(as
, op_addr
, MEMTXATTRS_UNSPECIFIED
,
238 &op_data
, op_size
, is_write
);
239 if (result
!= MEMTX_OK
) {
240 qemu_log_mask(LOG_GUEST_ERROR
, "%s: OPB %s failed @%08x\n",
241 __func__
, is_write
? "write" : "read", op_addr
);
246 s
->regs
[index
] = op_data
;
249 s
->regs
[APB2OPB_IRQ_STS
] |= opb
? APB2OPB_IRQ_STS_OPB1_TX_ACK
250 : APB2OPB_IRQ_STS_OPB0_TX_ACK
;
258 static const struct MemoryRegionOps aspeed_apb2opb_ops
= {
259 .read
= fsi_aspeed_apb2opb_read
,
260 .write
= fsi_aspeed_apb2opb_write
,
261 .valid
.max_access_size
= 4,
262 .valid
.min_access_size
= 4,
263 .impl
.max_access_size
= 4,
264 .impl
.min_access_size
= 4,
265 .endianness
= DEVICE_LITTLE_ENDIAN
,
268 static void fsi_aspeed_apb2opb_init(Object
*o
)
270 AspeedAPB2OPBState
*s
= ASPEED_APB2OPB(o
);
273 for (i
= 0; i
< ASPEED_FSI_NUM
; i
++) {
274 object_initialize_child(o
, "fsi-master[*]", &s
->fsi
[i
],
279 static void fsi_aspeed_apb2opb_realize(DeviceState
*dev
, Error
**errp
)
281 SysBusDevice
*sbd
= SYS_BUS_DEVICE(dev
);
282 AspeedAPB2OPBState
*s
= ASPEED_APB2OPB(dev
);
286 * TODO: The OPBus model initializes the OPB address space in
287 * the .instance_init handler and this is problematic for test
288 * device-introspect-test. To avoid a memory corruption and a QEMU
289 * crash, qbus_init() should be called from realize(). Something to
290 * improve. Possibly, OPBus could also be removed.
292 for (i
= 0; i
< ASPEED_FSI_NUM
; i
++) {
293 qbus_init(&s
->opb
[i
], sizeof(s
->opb
[i
]), TYPE_OP_BUS
, DEVICE(s
),
297 sysbus_init_irq(sbd
, &s
->irq
);
299 memory_region_init_io(&s
->iomem
, OBJECT(s
), &aspeed_apb2opb_ops
, s
,
300 TYPE_ASPEED_APB2OPB
, 0x1000);
301 sysbus_init_mmio(sbd
, &s
->iomem
);
303 for (i
= 0; i
< ASPEED_FSI_NUM
; i
++) {
304 if (!qdev_realize(DEVICE(&s
->fsi
[i
]), BUS(&s
->opb
[i
]), errp
)) {
308 memory_region_add_subregion(&s
->opb
[i
].mr
, 0x80000000,
311 memory_region_add_subregion(&s
->opb
[i
].mr
, 0xa0000000,
316 static void fsi_aspeed_apb2opb_reset(DeviceState
*dev
)
318 AspeedAPB2OPBState
*s
= ASPEED_APB2OPB(dev
);
320 memcpy(s
->regs
, aspeed_apb2opb_reset
, ASPEED_APB2OPB_NR_REGS
);
323 static void fsi_aspeed_apb2opb_class_init(ObjectClass
*klass
, void *data
)
325 DeviceClass
*dc
= DEVICE_CLASS(klass
);
327 dc
->desc
= "ASPEED APB2OPB Bridge";
328 dc
->realize
= fsi_aspeed_apb2opb_realize
;
329 dc
->reset
= fsi_aspeed_apb2opb_reset
;
332 static const TypeInfo aspeed_apb2opb_info
= {
333 .name
= TYPE_ASPEED_APB2OPB
,
334 .parent
= TYPE_SYS_BUS_DEVICE
,
335 .instance_init
= fsi_aspeed_apb2opb_init
,
336 .instance_size
= sizeof(AspeedAPB2OPBState
),
337 .class_init
= fsi_aspeed_apb2opb_class_init
,
340 static void aspeed_apb2opb_register_types(void)
342 type_register_static(&aspeed_apb2opb_info
);
345 type_init(aspeed_apb2opb_register_types
);
347 static void fsi_opb_init(Object
*o
)
349 OPBus
*opb
= OP_BUS(o
);
351 memory_region_init(&opb
->mr
, 0, TYPE_FSI_OPB
, UINT32_MAX
);
352 address_space_init(&opb
->as
, &opb
->mr
, TYPE_FSI_OPB
);
355 static const TypeInfo opb_info
= {
358 .instance_init
= fsi_opb_init
,
359 .instance_size
= sizeof(OPBus
),
362 static void fsi_opb_register_types(void)
364 type_register_static(&opb_info
);
367 type_init(fsi_opb_register_types
);