]>
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" | |
11 | #include "qemu-common.h" | |
12 | #include "qemu/log.h" | |
13 | #include "target/ppc/cpu.h" | |
14 | #include "hw/ppc/fdt.h" | |
15 | #include "hw/pci-host/pnv_phb4_regs.h" | |
16 | #include "hw/pci-host/pnv_phb4.h" | |
17 | #include "hw/ppc/pnv_xscom.h" | |
18 | #include "hw/pci/pci_bridge.h" | |
19 | #include "hw/pci/pci_bus.h" | |
20 | #include "hw/ppc/pnv.h" | |
21 | #include "hw/qdev-properties.h" | |
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 | ||
114 | static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr, | |
115 | unsigned size) | |
116 | { | |
117 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); | |
118 | uint32_t reg = addr >> 3; | |
119 | ||
120 | /* TODO: add list of allowed registers and error out if not */ | |
121 | return stack->nest_regs[reg]; | |
122 | } | |
123 | ||
124 | static void pnv_pec_stk_update_map(PnvPhb4PecStack *stack) | |
125 | { | |
126 | PnvPhb4PecState *pec = stack->pec; | |
127 | MemoryRegion *sysmem = pec->system_memory; | |
128 | uint64_t bar_en = stack->nest_regs[PEC_NEST_STK_BAR_EN]; | |
129 | uint64_t bar, mask, size; | |
130 | char name[64]; | |
131 | ||
132 | /* | |
133 | * NOTE: This will really not work well if those are remapped | |
134 | * after the PHB has created its sub regions. We could do better | |
135 | * if we had a way to resize regions but we don't really care | |
136 | * that much in practice as the stuff below really only happens | |
137 | * once early during boot | |
138 | */ | |
139 | ||
140 | /* Handle unmaps */ | |
141 | if (memory_region_is_mapped(&stack->mmbar0) && | |
142 | !(bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) { | |
143 | memory_region_del_subregion(sysmem, &stack->mmbar0); | |
144 | } | |
145 | if (memory_region_is_mapped(&stack->mmbar1) && | |
146 | !(bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) { | |
147 | memory_region_del_subregion(sysmem, &stack->mmbar1); | |
148 | } | |
149 | if (memory_region_is_mapped(&stack->phbbar) && | |
150 | !(bar_en & PEC_NEST_STK_BAR_EN_PHB)) { | |
151 | memory_region_del_subregion(sysmem, &stack->phbbar); | |
152 | } | |
153 | if (memory_region_is_mapped(&stack->intbar) && | |
154 | !(bar_en & PEC_NEST_STK_BAR_EN_INT)) { | |
155 | memory_region_del_subregion(sysmem, &stack->intbar); | |
156 | } | |
157 | ||
158 | /* Update PHB */ | |
159 | pnv_phb4_update_regions(stack); | |
160 | ||
161 | /* Handle maps */ | |
162 | if (!memory_region_is_mapped(&stack->mmbar0) && | |
163 | (bar_en & PEC_NEST_STK_BAR_EN_MMIO0)) { | |
164 | bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0] >> 8; | |
165 | mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR0_MASK]; | |
166 | size = ((~mask) >> 8) + 1; | |
167 | snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio0", | |
168 | pec->chip_id, pec->index, stack->stack_no); | |
169 | memory_region_init(&stack->mmbar0, OBJECT(stack), name, size); | |
170 | memory_region_add_subregion(sysmem, bar, &stack->mmbar0); | |
171 | stack->mmio0_base = bar; | |
172 | stack->mmio0_size = size; | |
173 | } | |
174 | if (!memory_region_is_mapped(&stack->mmbar1) && | |
175 | (bar_en & PEC_NEST_STK_BAR_EN_MMIO1)) { | |
176 | bar = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1] >> 8; | |
177 | mask = stack->nest_regs[PEC_NEST_STK_MMIO_BAR1_MASK]; | |
178 | size = ((~mask) >> 8) + 1; | |
179 | snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-mmio1", | |
180 | pec->chip_id, pec->index, stack->stack_no); | |
181 | memory_region_init(&stack->mmbar1, OBJECT(stack), name, size); | |
182 | memory_region_add_subregion(sysmem, bar, &stack->mmbar1); | |
183 | stack->mmio1_base = bar; | |
184 | stack->mmio1_size = size; | |
185 | } | |
186 | if (!memory_region_is_mapped(&stack->phbbar) && | |
187 | (bar_en & PEC_NEST_STK_BAR_EN_PHB)) { | |
188 | bar = stack->nest_regs[PEC_NEST_STK_PHB_REGS_BAR] >> 8; | |
189 | size = PNV_PHB4_NUM_REGS << 3; | |
190 | snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-phb", | |
191 | pec->chip_id, pec->index, stack->stack_no); | |
192 | memory_region_init(&stack->phbbar, OBJECT(stack), name, size); | |
193 | memory_region_add_subregion(sysmem, bar, &stack->phbbar); | |
194 | } | |
195 | if (!memory_region_is_mapped(&stack->intbar) && | |
196 | (bar_en & PEC_NEST_STK_BAR_EN_INT)) { | |
197 | bar = stack->nest_regs[PEC_NEST_STK_INT_BAR] >> 8; | |
198 | size = PNV_PHB4_MAX_INTs << 16; | |
199 | snprintf(name, sizeof(name), "pec-%d.%d-stack-%d-int", | |
200 | stack->pec->chip_id, stack->pec->index, stack->stack_no); | |
201 | memory_region_init(&stack->intbar, OBJECT(stack), name, size); | |
202 | memory_region_add_subregion(sysmem, bar, &stack->intbar); | |
203 | } | |
204 | ||
205 | /* Update PHB */ | |
206 | pnv_phb4_update_regions(stack); | |
207 | } | |
208 | ||
209 | static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, | |
210 | uint64_t val, unsigned size) | |
211 | { | |
212 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); | |
213 | PnvPhb4PecState *pec = stack->pec; | |
214 | uint32_t reg = addr >> 3; | |
215 | ||
216 | switch (reg) { | |
217 | case PEC_NEST_STK_PCI_NEST_FIR: | |
218 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val; | |
219 | break; | |
220 | case PEC_NEST_STK_PCI_NEST_FIR_CLR: | |
221 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val; | |
222 | break; | |
223 | case PEC_NEST_STK_PCI_NEST_FIR_SET: | |
224 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val; | |
225 | break; | |
226 | case PEC_NEST_STK_PCI_NEST_FIR_MSK: | |
227 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val; | |
228 | break; | |
229 | case PEC_NEST_STK_PCI_NEST_FIR_MSKC: | |
230 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val; | |
231 | break; | |
232 | case PEC_NEST_STK_PCI_NEST_FIR_MSKS: | |
233 | stack->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] |= val; | |
234 | break; | |
235 | case PEC_NEST_STK_PCI_NEST_FIR_ACT0: | |
236 | case PEC_NEST_STK_PCI_NEST_FIR_ACT1: | |
237 | stack->nest_regs[reg] = val; | |
238 | break; | |
239 | case PEC_NEST_STK_PCI_NEST_FIR_WOF: | |
240 | stack->nest_regs[reg] = 0; | |
241 | break; | |
242 | case PEC_NEST_STK_ERR_REPORT_0: | |
243 | case PEC_NEST_STK_ERR_REPORT_1: | |
244 | case PEC_NEST_STK_PBCQ_GNRL_STATUS: | |
245 | /* Flag error ? */ | |
246 | break; | |
247 | case PEC_NEST_STK_PBCQ_MODE: | |
248 | stack->nest_regs[reg] = val & 0xff00000000000000ull; | |
249 | break; | |
250 | case PEC_NEST_STK_MMIO_BAR0: | |
251 | case PEC_NEST_STK_MMIO_BAR0_MASK: | |
252 | case PEC_NEST_STK_MMIO_BAR1: | |
253 | case PEC_NEST_STK_MMIO_BAR1_MASK: | |
254 | if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & | |
255 | (PEC_NEST_STK_BAR_EN_MMIO0 | | |
256 | PEC_NEST_STK_BAR_EN_MMIO1)) { | |
257 | phb_pec_error(pec, "Changing enabled BAR unsupported\n"); | |
258 | } | |
259 | stack->nest_regs[reg] = val & 0xffffffffff000000ull; | |
260 | break; | |
261 | case PEC_NEST_STK_PHB_REGS_BAR: | |
262 | if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) { | |
263 | phb_pec_error(pec, "Changing enabled BAR unsupported\n"); | |
264 | } | |
265 | stack->nest_regs[reg] = val & 0xffffffffffc00000ull; | |
266 | break; | |
267 | case PEC_NEST_STK_INT_BAR: | |
268 | if (stack->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) { | |
269 | phb_pec_error(pec, "Changing enabled BAR unsupported\n"); | |
270 | } | |
271 | stack->nest_regs[reg] = val & 0xfffffff000000000ull; | |
272 | break; | |
273 | case PEC_NEST_STK_BAR_EN: | |
274 | stack->nest_regs[reg] = val & 0xf000000000000000ull; | |
275 | pnv_pec_stk_update_map(stack); | |
276 | break; | |
277 | case PEC_NEST_STK_DATA_FRZ_TYPE: | |
278 | case PEC_NEST_STK_PBCQ_TUN_BAR: | |
279 | /* Not used for now */ | |
280 | stack->nest_regs[reg] = val; | |
281 | break; | |
282 | default: | |
283 | qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx | |
284 | "=%"PRIx64"\n", addr, val); | |
285 | } | |
286 | } | |
287 | ||
288 | static const MemoryRegionOps pnv_pec_stk_nest_xscom_ops = { | |
289 | .read = pnv_pec_stk_nest_xscom_read, | |
290 | .write = pnv_pec_stk_nest_xscom_write, | |
291 | .valid.min_access_size = 8, | |
292 | .valid.max_access_size = 8, | |
293 | .impl.min_access_size = 8, | |
294 | .impl.max_access_size = 8, | |
295 | .endianness = DEVICE_BIG_ENDIAN, | |
296 | }; | |
297 | ||
298 | static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr, | |
299 | unsigned size) | |
300 | { | |
301 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); | |
302 | uint32_t reg = addr >> 3; | |
303 | ||
304 | /* TODO: add list of allowed registers and error out if not */ | |
305 | return stack->pci_regs[reg]; | |
306 | } | |
307 | ||
308 | static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, | |
309 | uint64_t val, unsigned size) | |
310 | { | |
311 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(opaque); | |
312 | uint32_t reg = addr >> 3; | |
313 | ||
314 | switch (reg) { | |
315 | case PEC_PCI_STK_PCI_FIR: | |
316 | stack->nest_regs[reg] = val; | |
317 | break; | |
318 | case PEC_PCI_STK_PCI_FIR_CLR: | |
319 | stack->nest_regs[PEC_PCI_STK_PCI_FIR] &= val; | |
320 | break; | |
321 | case PEC_PCI_STK_PCI_FIR_SET: | |
322 | stack->nest_regs[PEC_PCI_STK_PCI_FIR] |= val; | |
323 | break; | |
324 | case PEC_PCI_STK_PCI_FIR_MSK: | |
325 | stack->nest_regs[reg] = val; | |
326 | break; | |
327 | case PEC_PCI_STK_PCI_FIR_MSKC: | |
328 | stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val; | |
329 | break; | |
330 | case PEC_PCI_STK_PCI_FIR_MSKS: | |
331 | stack->nest_regs[PEC_PCI_STK_PCI_FIR_MSK] |= val; | |
332 | break; | |
333 | case PEC_PCI_STK_PCI_FIR_ACT0: | |
334 | case PEC_PCI_STK_PCI_FIR_ACT1: | |
335 | stack->nest_regs[reg] = val; | |
336 | break; | |
337 | case PEC_PCI_STK_PCI_FIR_WOF: | |
338 | stack->nest_regs[reg] = 0; | |
339 | break; | |
340 | case PEC_PCI_STK_ETU_RESET: | |
341 | stack->nest_regs[reg] = val & 0x8000000000000000ull; | |
342 | /* TODO: Implement reset */ | |
343 | break; | |
344 | case PEC_PCI_STK_PBAIB_ERR_REPORT: | |
345 | break; | |
346 | case PEC_PCI_STK_PBAIB_TX_CMD_CRED: | |
347 | case PEC_PCI_STK_PBAIB_TX_DAT_CRED: | |
348 | stack->nest_regs[reg] = val; | |
349 | break; | |
350 | default: | |
351 | qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx | |
352 | "=%"PRIx64"\n", addr, val); | |
353 | } | |
354 | } | |
355 | ||
356 | static const MemoryRegionOps pnv_pec_stk_pci_xscom_ops = { | |
357 | .read = pnv_pec_stk_pci_xscom_read, | |
358 | .write = pnv_pec_stk_pci_xscom_write, | |
359 | .valid.min_access_size = 8, | |
360 | .valid.max_access_size = 8, | |
361 | .impl.min_access_size = 8, | |
362 | .impl.max_access_size = 8, | |
363 | .endianness = DEVICE_BIG_ENDIAN, | |
364 | }; | |
365 | ||
366 | static void pnv_pec_instance_init(Object *obj) | |
367 | { | |
368 | PnvPhb4PecState *pec = PNV_PHB4_PEC(obj); | |
369 | int i; | |
370 | ||
371 | for (i = 0; i < PHB4_PEC_MAX_STACKS; i++) { | |
372 | object_initialize_child(obj, "stack[*]", &pec->stacks[i], | |
9fc7fc4d | 373 | TYPE_PNV_PHB4_PEC_STACK); |
4f9924c4 BH |
374 | } |
375 | } | |
376 | ||
377 | static void pnv_pec_realize(DeviceState *dev, Error **errp) | |
378 | { | |
379 | PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); | |
4f9924c4 BH |
380 | char name[64]; |
381 | int i; | |
382 | ||
383 | assert(pec->system_memory); | |
384 | ||
385 | /* Create stacks */ | |
386 | for (i = 0; i < pec->num_stacks; i++) { | |
387 | PnvPhb4PecStack *stack = &pec->stacks[i]; | |
388 | Object *stk_obj = OBJECT(stack); | |
389 | ||
5325cc34 MA |
390 | object_property_set_int(stk_obj, "stack-no", i, &error_abort); |
391 | object_property_set_link(stk_obj, "pec", OBJECT(pec), &error_abort); | |
668f62ec | 392 | if (!qdev_realize(DEVICE(stk_obj), NULL, errp)) { |
4f9924c4 BH |
393 | return; |
394 | } | |
395 | } | |
efa05595 MA |
396 | for (; i < PHB4_PEC_MAX_STACKS; i++) { |
397 | object_unparent(OBJECT(&pec->stacks[i])); | |
398 | } | |
4f9924c4 BH |
399 | |
400 | /* Initialize the XSCOM regions for the PEC registers */ | |
401 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest", pec->chip_id, | |
402 | pec->index); | |
403 | pnv_xscom_region_init(&pec->nest_regs_mr, OBJECT(dev), | |
404 | &pnv_pec_nest_xscom_ops, pec, name, | |
405 | PHB4_PEC_NEST_REGS_COUNT); | |
406 | ||
407 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci", pec->chip_id, | |
408 | pec->index); | |
409 | pnv_xscom_region_init(&pec->pci_regs_mr, OBJECT(dev), | |
410 | &pnv_pec_pci_xscom_ops, pec, name, | |
411 | PHB4_PEC_PCI_REGS_COUNT); | |
412 | } | |
413 | ||
414 | static int pnv_pec_dt_xscom(PnvXScomInterface *dev, void *fdt, | |
415 | int xscom_offset) | |
416 | { | |
417 | PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); | |
418 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(dev); | |
419 | uint32_t nbase = pecc->xscom_nest_base(pec); | |
420 | uint32_t pbase = pecc->xscom_pci_base(pec); | |
421 | int offset, i; | |
422 | char *name; | |
423 | uint32_t reg[] = { | |
424 | cpu_to_be32(nbase), | |
425 | cpu_to_be32(pecc->xscom_nest_size), | |
426 | cpu_to_be32(pbase), | |
427 | cpu_to_be32(pecc->xscom_pci_size), | |
428 | }; | |
429 | ||
430 | name = g_strdup_printf("pbcq@%x", nbase); | |
431 | offset = fdt_add_subnode(fdt, xscom_offset, name); | |
432 | _FDT(offset); | |
433 | g_free(name); | |
434 | ||
435 | _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); | |
436 | ||
437 | _FDT((fdt_setprop_cell(fdt, offset, "ibm,pec-index", pec->index))); | |
438 | _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 1))); | |
439 | _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 0))); | |
440 | _FDT((fdt_setprop(fdt, offset, "compatible", pecc->compat, | |
441 | pecc->compat_size))); | |
442 | ||
443 | for (i = 0; i < pec->num_stacks; i++) { | |
444 | PnvPhb4PecStack *stack = &pec->stacks[i]; | |
445 | PnvPHB4 *phb = &stack->phb; | |
446 | int stk_offset; | |
447 | ||
448 | name = g_strdup_printf("stack@%x", i); | |
449 | stk_offset = fdt_add_subnode(fdt, offset, name); | |
450 | _FDT(stk_offset); | |
451 | g_free(name); | |
452 | _FDT((fdt_setprop(fdt, stk_offset, "compatible", pecc->stk_compat, | |
453 | pecc->stk_compat_size))); | |
454 | _FDT((fdt_setprop_cell(fdt, stk_offset, "reg", i))); | |
455 | _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb->phb_id))); | |
456 | } | |
457 | ||
458 | return 0; | |
459 | } | |
460 | ||
461 | static Property pnv_pec_properties[] = { | |
462 | DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), | |
463 | DEFINE_PROP_UINT32("num-stacks", PnvPhb4PecState, num_stacks, 0), | |
464 | DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), | |
465 | DEFINE_PROP_LINK("system-memory", PnvPhb4PecState, system_memory, | |
466 | TYPE_MEMORY_REGION, MemoryRegion *), | |
467 | DEFINE_PROP_END_OF_LIST(), | |
468 | }; | |
469 | ||
470 | static uint32_t pnv_pec_xscom_pci_base(PnvPhb4PecState *pec) | |
471 | { | |
472 | return PNV9_XSCOM_PEC_PCI_BASE + 0x1000000 * pec->index; | |
473 | } | |
474 | ||
475 | static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) | |
476 | { | |
477 | return PNV9_XSCOM_PEC_NEST_BASE + 0x400 * pec->index; | |
478 | } | |
479 | ||
480 | static void pnv_pec_class_init(ObjectClass *klass, void *data) | |
481 | { | |
482 | DeviceClass *dc = DEVICE_CLASS(klass); | |
483 | PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); | |
484 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); | |
485 | static const char compat[] = "ibm,power9-pbcq"; | |
486 | static const char stk_compat[] = "ibm,power9-phb-stack"; | |
487 | ||
488 | xdc->dt_xscom = pnv_pec_dt_xscom; | |
489 | ||
490 | dc->realize = pnv_pec_realize; | |
491 | device_class_set_props(dc, pnv_pec_properties); | |
23a782eb | 492 | dc->user_creatable = false; |
4f9924c4 BH |
493 | |
494 | pecc->xscom_nest_base = pnv_pec_xscom_nest_base; | |
495 | pecc->xscom_pci_base = pnv_pec_xscom_pci_base; | |
496 | pecc->xscom_nest_size = PNV9_XSCOM_PEC_NEST_SIZE; | |
497 | pecc->xscom_pci_size = PNV9_XSCOM_PEC_PCI_SIZE; | |
498 | pecc->compat = compat; | |
499 | pecc->compat_size = sizeof(compat); | |
500 | pecc->stk_compat = stk_compat; | |
501 | pecc->stk_compat_size = sizeof(stk_compat); | |
502 | } | |
503 | ||
504 | static const TypeInfo pnv_pec_type_info = { | |
505 | .name = TYPE_PNV_PHB4_PEC, | |
506 | .parent = TYPE_DEVICE, | |
507 | .instance_size = sizeof(PnvPhb4PecState), | |
508 | .instance_init = pnv_pec_instance_init, | |
509 | .class_init = pnv_pec_class_init, | |
510 | .class_size = sizeof(PnvPhb4PecClass), | |
511 | .interfaces = (InterfaceInfo[]) { | |
512 | { TYPE_PNV_XSCOM_INTERFACE }, | |
513 | { } | |
514 | } | |
515 | }; | |
516 | ||
517 | static void pnv_pec_stk_instance_init(Object *obj) | |
518 | { | |
519 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(obj); | |
520 | ||
9fc7fc4d | 521 | object_initialize_child(obj, "phb", &stack->phb, TYPE_PNV_PHB4); |
4f9924c4 BH |
522 | } |
523 | ||
524 | static void pnv_pec_stk_realize(DeviceState *dev, Error **errp) | |
525 | { | |
526 | PnvPhb4PecStack *stack = PNV_PHB4_PEC_STACK(dev); | |
527 | PnvPhb4PecState *pec = stack->pec; | |
528 | char name[64]; | |
529 | ||
530 | assert(pec); | |
531 | ||
532 | /* Initialize the XSCOM regions for the stack registers */ | |
533 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-nest-stack-%d", | |
534 | pec->chip_id, pec->index, stack->stack_no); | |
535 | pnv_xscom_region_init(&stack->nest_regs_mr, OBJECT(stack), | |
536 | &pnv_pec_stk_nest_xscom_ops, stack, name, | |
537 | PHB4_PEC_NEST_STK_REGS_COUNT); | |
538 | ||
539 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d", | |
540 | pec->chip_id, pec->index, stack->stack_no); | |
541 | pnv_xscom_region_init(&stack->pci_regs_mr, OBJECT(stack), | |
542 | &pnv_pec_stk_pci_xscom_ops, stack, name, | |
543 | PHB4_PEC_PCI_STK_REGS_COUNT); | |
544 | ||
545 | /* PHB pass-through */ | |
546 | snprintf(name, sizeof(name), "xscom-pec-%d.%d-pci-stack-%d-phb", | |
547 | pec->chip_id, pec->index, stack->stack_no); | |
548 | pnv_xscom_region_init(&stack->phb_regs_mr, OBJECT(&stack->phb), | |
549 | &pnv_phb4_xscom_ops, &stack->phb, name, 0x40); | |
550 | ||
551 | /* | |
552 | * Let the machine/chip realize the PHB object to customize more | |
553 | * easily some fields | |
554 | */ | |
555 | } | |
556 | ||
557 | static Property pnv_pec_stk_properties[] = { | |
558 | DEFINE_PROP_UINT32("stack-no", PnvPhb4PecStack, stack_no, 0), | |
559 | DEFINE_PROP_LINK("pec", PnvPhb4PecStack, pec, TYPE_PNV_PHB4_PEC, | |
560 | PnvPhb4PecState *), | |
561 | DEFINE_PROP_END_OF_LIST(), | |
562 | }; | |
563 | ||
564 | static void pnv_pec_stk_class_init(ObjectClass *klass, void *data) | |
565 | { | |
566 | DeviceClass *dc = DEVICE_CLASS(klass); | |
567 | ||
568 | device_class_set_props(dc, pnv_pec_stk_properties); | |
569 | dc->realize = pnv_pec_stk_realize; | |
23a782eb | 570 | dc->user_creatable = false; |
4f9924c4 BH |
571 | |
572 | /* TODO: reset regs ? */ | |
573 | } | |
574 | ||
575 | static const TypeInfo pnv_pec_stk_type_info = { | |
576 | .name = TYPE_PNV_PHB4_PEC_STACK, | |
577 | .parent = TYPE_DEVICE, | |
578 | .instance_size = sizeof(PnvPhb4PecStack), | |
579 | .instance_init = pnv_pec_stk_instance_init, | |
580 | .class_init = pnv_pec_stk_class_init, | |
581 | .interfaces = (InterfaceInfo[]) { | |
582 | { TYPE_PNV_XSCOM_INTERFACE }, | |
583 | { } | |
584 | } | |
585 | }; | |
586 | ||
587 | static void pnv_pec_register_types(void) | |
588 | { | |
589 | type_register_static(&pnv_pec_type_info); | |
590 | type_register_static(&pnv_pec_stk_type_info); | |
591 | } | |
592 | ||
593 | type_init(pnv_pec_register_types); |