]>
Commit | Line | Data |
---|---|---|
4f9924c4 BH |
1 | /* |
2 | * QEMU PowerPC PowerNV (POWER9) PHB4 model | |
3 | * | |
4 | * Copyright (c) 2018-2020, IBM Corporation. | |
5 | * | |
6 | * This code is licensed under the GPL version 2 or later. See the | |
7 | * COPYING file in the top-level directory. | |
8 | */ | |
9 | #include "qemu/osdep.h" | |
10 | #include "qapi/error.h" | |
4f9924c4 BH |
11 | #include "qemu/log.h" |
12 | #include "target/ppc/cpu.h" | |
13 | #include "hw/ppc/fdt.h" | |
14 | #include "hw/pci-host/pnv_phb4_regs.h" | |
15 | #include "hw/pci-host/pnv_phb4.h" | |
16 | #include "hw/ppc/pnv_xscom.h" | |
17 | #include "hw/pci/pci_bridge.h" | |
18 | #include "hw/pci/pci_bus.h" | |
19 | #include "hw/ppc/pnv.h" | |
20 | #include "hw/qdev-properties.h" | |
5bc67b05 | 21 | #include "sysemu/sysemu.h" |
4f9924c4 BH |
22 | |
23 | #include <libfdt.h> | |
24 | ||
25 | #define phb_pec_error(pec, fmt, ...) \ | |
26 | qemu_log_mask(LOG_GUEST_ERROR, "phb4_pec[%d:%d]: " fmt "\n", \ | |
27 | (pec)->chip_id, (pec)->index, ## __VA_ARGS__) | |
28 | ||
29 | ||
30 | static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr, | |
31 | unsigned size) | |
32 | { | |
33 | PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); | |
34 | uint32_t reg = addr >> 3; | |
35 | ||
36 | /* TODO: add list of allowed registers and error out if not */ | |
37 | return pec->nest_regs[reg]; | |
38 | } | |
39 | ||
40 | static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr, | |
41 | uint64_t val, unsigned size) | |
42 | { | |
43 | PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); | |
44 | uint32_t reg = addr >> 3; | |
45 | ||
46 | switch (reg) { | |
47 | case PEC_NEST_PBCQ_HW_CONFIG: | |
48 | case PEC_NEST_DROP_PRIO_CTRL: | |
49 | case PEC_NEST_PBCQ_ERR_INJECT: | |
50 | case PEC_NEST_PCI_NEST_CLK_TRACE_CTL: | |
51 | case PEC_NEST_PBCQ_PMON_CTRL: | |
52 | case PEC_NEST_PBCQ_PBUS_ADDR_EXT: | |
53 | case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT: | |
54 | case PEC_NEST_CAPP_CTRL: | |
55 | case PEC_NEST_PBCQ_READ_STK_OVR: | |
56 | case PEC_NEST_PBCQ_WRITE_STK_OVR: | |
57 | case PEC_NEST_PBCQ_STORE_STK_OVR: | |
58 | case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL: | |
59 | pec->nest_regs[reg] = val; | |
60 | break; | |
61 | default: | |
62 | phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, | |
63 | addr, val); | |
64 | } | |
65 | } | |
66 | ||
67 | static const MemoryRegionOps pnv_pec_nest_xscom_ops = { | |
68 | .read = pnv_pec_nest_xscom_read, | |
69 | .write = pnv_pec_nest_xscom_write, | |
70 | .valid.min_access_size = 8, | |
71 | .valid.max_access_size = 8, | |
72 | .impl.min_access_size = 8, | |
73 | .impl.max_access_size = 8, | |
74 | .endianness = DEVICE_BIG_ENDIAN, | |
75 | }; | |
76 | ||
77 | static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr, | |
78 | unsigned size) | |
79 | { | |
80 | PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); | |
81 | uint32_t reg = addr >> 3; | |
82 | ||
83 | /* TODO: add list of allowed registers and error out if not */ | |
84 | return pec->pci_regs[reg]; | |
85 | } | |
86 | ||
87 | static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr, | |
88 | uint64_t val, unsigned size) | |
89 | { | |
90 | PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); | |
91 | uint32_t reg = addr >> 3; | |
92 | ||
93 | switch (reg) { | |
94 | case PEC_PCI_PBAIB_HW_CONFIG: | |
95 | case PEC_PCI_PBAIB_READ_STK_OVR: | |
96 | pec->pci_regs[reg] = val; | |
97 | break; | |
98 | default: | |
99 | phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, | |
100 | addr, val); | |
101 | } | |
102 | } | |
103 | ||
104 | static const MemoryRegionOps pnv_pec_pci_xscom_ops = { | |
105 | .read = pnv_pec_pci_xscom_read, | |
106 | .write = pnv_pec_pci_xscom_write, | |
107 | .valid.min_access_size = 8, | |
108 | .valid.max_access_size = 8, | |
109 | .impl.min_access_size = 8, | |
110 | .impl.max_access_size = 8, | |
111 | .endianness = DEVICE_BIG_ENDIAN, | |
112 | }; | |
113 | ||
3f4c369e | 114 | static void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, |
a479f0dc DHB |
115 | int stack_no, |
116 | Error **errp) | |
117 | { | |
d3df1f64 FB |
118 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); |
119 | PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type)); | |
a479f0dc DHB |
120 | int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no); |
121 | ||
9c10d86f | 122 | object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb)); |
3f4c369e | 123 | object_property_set_link(OBJECT(phb), "pec", OBJECT(pec), |
a479f0dc | 124 | &error_abort); |
3f4c369e | 125 | object_property_set_int(OBJECT(phb), "chip-id", pec->chip_id, |
a479f0dc | 126 | &error_fatal); |
3f4c369e | 127 | object_property_set_int(OBJECT(phb), "index", phb_id, |
a479f0dc | 128 | &error_fatal); |
a479f0dc | 129 | |
3f4c369e | 130 | if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) { |
a479f0dc DHB |
131 | return; |
132 | } | |
45d22dcf CLG |
133 | |
134 | /* Add a single Root port if running with defaults */ | |
d3df1f64 | 135 | pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model); |
a479f0dc DHB |
136 | } |
137 | ||
4f9924c4 BH |
138 | static void pnv_pec_realize(DeviceState *dev, Error **errp) |
139 | { | |
140 | PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); | |
cf0ee695 | 141 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); |
4f9924c4 BH |
142 | char name[64]; |
143 | int i; | |
144 | ||
6f43d255 CLG |
145 | if (pec->index >= PNV_CHIP_GET_CLASS(pec->chip)->num_pecs) { |
146 | error_setg(errp, "invalid PEC index: %d", pec->index); | |
147 | return; | |
148 | } | |
149 | ||
3f4c369e | 150 | pec->num_phbs = pecc->num_phbs[pec->index]; |
a479f0dc | 151 | |
3f4c369e | 152 | /* Create PHBs if running with defaults */ |
9c10d86f CLG |
153 | for (i = 0; i < pec->num_phbs; i++) { |
154 | pnv_pec_default_phb_realize(pec, i, errp); | |
efa05595 | 155 | } |
4f9924c4 BH |
156 | |
157 | /* Initialize the XSCOM regions for the PEC registers */ | |
158 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id, | |
159 | pec->index); | |
160 | pnv_xscom_region_init(&pec->nest_regs_mr, OBJECT(dev), | |
161 | &pnv_pec_nest_xscom_ops, pec, name, | |
162 | PHB4_PEC_NEST_REGS_COUNT); | |
163 | ||
164 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci", pec->chip_id, | |
165 | pec->index); | |
166 | pnv_xscom_region_init(&pec->pci_regs_mr, OBJECT(dev), | |
167 | &pnv_pec_pci_xscom_ops, pec, name, | |
168 | PHB4_PEC_PCI_REGS_COUNT); | |
169 | } | |
170 | ||
171 | static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, | |
172 | int xscom_offset) | |
173 | { | |
174 | PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); | |
175 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(dev); | |
176 | uint32_t nbase = pecc->xscom_nest_base(pec); | |
177 | uint32_t pbase = pecc->xscom_pci_base(pec); | |
178 | int offset, i; | |
179 | char *name; | |
180 | uint32_t reg[] = { | |
181 | cpu_to_be32(nbase), | |
182 | cpu_to_be32(pecc->xscom_nest_size), | |
183 | cpu_to_be32(pbase), | |
184 | cpu_to_be32(pecc->xscom_pci_size), | |
185 | }; | |
186 | ||
187 | name = g_strdup_printf("pbcq@%x", nbase); | |
188 | offset = fdt_add_subnode(fdt, xscom_offset, name); | |
189 | _FDT(offset); | |
190 | g_free(name); | |
191 | ||
192 | _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); | |
193 | ||
194 | _FDT((fdt_setprop_cell(fdt, offset, "ibm,pec-index", pec->index))); | |
195 | _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 1))); | |
196 | _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0))); | |
197 | _FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat, | |
198 | pecc->compat_size))); | |
199 | ||
3f4c369e | 200 | for (i = 0; i < pec->num_phbs; i++) { |
d22b0c94 | 201 | int phb_id = pnv_phb4_pec_get_phb_id(pec, i); |
4f9924c4 BH |
202 | int stk_offset; |
203 | ||
204 | name = g_strdup_printf("stack@%x", i); | |
205 | stk_offset = fdt_add_subnode(fdt, offset, name); | |
206 | _FDT(stk_offset); | |
207 | g_free(name); | |
208 | _FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat, | |
209 | pecc->stk_compat_size))); | |
210 | _FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i))); | |
d22b0c94 | 211 | _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb_id))); |
4f9924c4 BH |
212 | } |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | static Property pnv_pec_properties[] = { | |
218 | DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), | |
4f9924c4 | 219 | DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), |
6f43d255 CLG |
220 | DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, |
221 | PnvChip *), | |
4f9924c4 BH |
222 | DEFINE_PROP_END_OF_LIST(), |
223 | }; | |
224 | ||
225 | static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) | |
226 | { | |
227 | return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; | |
228 | } | |
229 | ||
230 | static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) | |
231 | { | |
232 | return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index; | |
233 | } | |
234 | ||
cf0ee695 | 235 | /* |
3f4c369e DHB |
236 | * PEC0 -> 1 phb |
237 | * PEC1 -> 2 phb | |
238 | * PEC2 -> 3 phbs | |
cf0ee695 | 239 | */ |
3f4c369e | 240 | static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 }; |
cf0ee695 | 241 | |
4f9924c4 BH |
242 | static void pnv_pec_class_init(ObjectClass *klass, void *data) |
243 | { | |
244 | DeviceClass *dc = DEVICE_CLASS(klass); | |
245 | PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); | |
246 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); | |
247 | static const char compat[] = "ibm,power9-pbcq"; | |
248 | static const char stk_compat[] = "ibm,power9-phb-stack"; | |
249 | ||
250 | xdc->dt_xscom = pnv_pec_dt_xscom; | |
251 | ||
252 | dc->realize = pnv_pec_realize; | |
253 | device_class_set_props(dc, pnv_pec_properties); | |
23a782eb | 254 | dc->user_creatable = false; |
4f9924c4 BH |
255 | |
256 | pecc->xscom_nest_base = pnv_pec_xscom_nest_base; | |
257 | pecc->xscom_pci_base = pnv_pec_xscom_pci_base; | |
258 | pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE; | |
259 | pecc->xscom_pci_size = PNV9_XSCOM_PEC_PCI_SIZE; | |
260 | pecc->compat = compat; | |
261 | pecc->compat_size = sizeof(compat); | |
262 | pecc->stk_compat = stk_compat; | |
263 | pecc->stk_compat_size = sizeof(stk_compat); | |
12060cbd | 264 | pecc->version = PNV_PHB4_VERSION; |
d3df1f64 | 265 | pecc->phb_type = TYPE_PNV_PHB4; |
3f4c369e | 266 | pecc->num_phbs = pnv_pec_num_phbs; |
32a07887 | 267 | pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT; |
4f9924c4 BH |
268 | } |
269 | ||
270 | static const TypeInfo pnv_pec_type_info = { | |
271 | .name = TYPE_PNV_PHB4_PEC, | |
272 | .parent = TYPE_DEVICE, | |
273 | .instance_size = sizeof(PnvPhb4PecState), | |
4f9924c4 BH |
274 | .class_init = pnv_pec_class_init, |
275 | .class_size = sizeof(PnvPhb4PecClass), | |
276 | .interfaces = (InterfaceInfo[]) { | |
277 | { TYPE_PNV_XSCOM_INTERFACE }, | |
278 | { } | |
279 | } | |
280 | }; | |
281 | ||
623575e1 CLG |
282 | /* |
283 | * POWER10 definitions | |
284 | */ | |
285 | ||
286 | static uint32_t pnv_phb5_pec_xscom_pci_base(PnvPhb4PecState *pec) | |
287 | { | |
288 | return PNV10_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; | |
289 | } | |
290 | ||
291 | static uint32_t pnv_phb5_pec_xscom_nest_base(PnvPhb4PecState *pec) | |
292 | { | |
293 | /* index goes down ... */ | |
294 | return PNV10_XSCOM_PEC_NEST_BASE - 0x1000000 * pec->index; | |
295 | } | |
296 | ||
297 | /* | |
298 | * PEC0 -> 3 stacks | |
299 | * PEC1 -> 3 stacks | |
300 | */ | |
301 | static const uint32_t pnv_phb5_pec_num_stacks[] = { 3, 3 }; | |
302 | ||
303 | static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) | |
304 | { | |
305 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); | |
306 | static const char compat[] = "ibm,power10-pbcq"; | |
307 | static const char stk_compat[] = "ibm,power10-phb-stack"; | |
308 | ||
309 | pecc->xscom_nest_base = pnv_phb5_pec_xscom_nest_base; | |
310 | pecc->xscom_pci_base = pnv_phb5_pec_xscom_pci_base; | |
311 | pecc->xscom_nest_size = PNV10_XSCOM_PEC_NEST_SIZE; | |
312 | pecc->xscom_pci_size = PNV10_XSCOM_PEC_PCI_SIZE; | |
313 | pecc->compat = compat; | |
314 | pecc->compat_size = sizeof(compat); | |
315 | pecc->stk_compat = stk_compat; | |
316 | pecc->stk_compat_size = sizeof(stk_compat); | |
317 | pecc->version = PNV_PHB5_VERSION; | |
d3df1f64 | 318 | pecc->phb_type = TYPE_PNV_PHB5; |
623575e1 CLG |
319 | pecc->num_phbs = pnv_phb5_pec_num_stacks; |
320 | pecc->rp_model = TYPE_PNV_PHB5_ROOT_PORT; | |
321 | } | |
322 | ||
323 | static const TypeInfo pnv_phb5_pec_type_info = { | |
324 | .name = TYPE_PNV_PHB5_PEC, | |
325 | .parent = TYPE_PNV_PHB4_PEC, | |
326 | .instance_size = sizeof(PnvPhb4PecState), | |
327 | .class_init = pnv_phb5_pec_class_init, | |
328 | .class_size = sizeof(PnvPhb4PecClass), | |
329 | .interfaces = (InterfaceInfo[]) { | |
330 | { TYPE_PNV_XSCOM_INTERFACE }, | |
331 | { } | |
332 | } | |
333 | }; | |
334 | ||
4f9924c4 BH |
335 | static void pnv_pec_register_types(void) |
336 | { | |
337 | type_register_static(&pnv_pec_type_info); | |
623575e1 | 338 | type_register_static(&pnv_phb5_pec_type_info); |
4f9924c4 BH |
339 | } |
340 | ||
341 | type_init(pnv_pec_register_types); |