]>
Commit | Line | Data |
---|---|---|
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/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" | |
21 | #include "sysemu/sysemu.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 void pnv_pec_default_phb_realize(PnvPhb4PecState *pec, | |
115 | int stack_no, | |
116 | Error **errp) | |
117 | { | |
118 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); | |
119 | PnvPHB4 *phb = PNV_PHB4(qdev_new(pecc->phb_type)); | |
120 | int phb_id = pnv_phb4_pec_get_phb_id(pec, stack_no); | |
121 | ||
122 | object_property_add_child(OBJECT(pec), "phb[*]", OBJECT(phb)); | |
123 | object_property_set_link(OBJECT(phb), "pec", OBJECT(pec), | |
124 | &error_abort); | |
125 | object_property_set_int(OBJECT(phb), "chip-id", pec->chip_id, | |
126 | &error_fatal); | |
127 | object_property_set_int(OBJECT(phb), "index", phb_id, | |
128 | &error_fatal); | |
129 | ||
130 | if (!sysbus_realize(SYS_BUS_DEVICE(phb), errp)) { | |
131 | return; | |
132 | } | |
133 | ||
134 | /* Add a single Root port if running with defaults */ | |
135 | pnv_phb_attach_root_port(PCI_HOST_BRIDGE(phb), pecc->rp_model); | |
136 | } | |
137 | ||
138 | static void pnv_pec_realize(DeviceState *dev, Error **errp) | |
139 | { | |
140 | PnvPhb4PecState *pec = PNV_PHB4_PEC(dev); | |
141 | PnvPhb4PecClass *pecc = PNV_PHB4_PEC_GET_CLASS(pec); | |
142 | char name[64]; | |
143 | int i; | |
144 | ||
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 | ||
150 | pec->num_phbs = pecc->num_phbs[pec->index]; | |
151 | ||
152 | /* Create PHBs if running with defaults */ | |
153 | for (i = 0; i < pec->num_phbs; i++) { | |
154 | pnv_pec_default_phb_realize(pec, i, errp); | |
155 | } | |
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 | ||
200 | for (i = 0; i < pec->num_phbs; i++) { | |
201 | int phb_id = pnv_phb4_pec_get_phb_id(pec, i); | |
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))); | |
211 | _FDT((fdt_setprop_cell(fdt, stk_offset, "ibm,phb-index", phb_id))); | |
212 | } | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | static Property pnv_pec_properties[] = { | |
218 | DEFINE_PROP_UINT32("index", PnvPhb4PecState, index, 0), | |
219 | DEFINE_PROP_UINT32("chip-id", PnvPhb4PecState, chip_id, 0), | |
220 | DEFINE_PROP_LINK("chip", PnvPhb4PecState, chip, TYPE_PNV_CHIP, | |
221 | PnvChip *), | |
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 | ||
235 | /* | |
236 | * PEC0 -> 1 phb | |
237 | * PEC1 -> 2 phb | |
238 | * PEC2 -> 3 phbs | |
239 | */ | |
240 | static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 }; | |
241 | ||
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); | |
254 | dc->user_creatable = false; | |
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); | |
264 | pecc->version = PNV_PHB4_VERSION; | |
265 | pecc->phb_type = TYPE_PNV_PHB4; | |
266 | pecc->num_phbs = pnv_pec_num_phbs; | |
267 | pecc->rp_model = TYPE_PNV_PHB4_ROOT_PORT; | |
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), | |
274 | .class_init = pnv_pec_class_init, | |
275 | .class_size = sizeof(PnvPhb4PecClass), | |
276 | .interfaces = (InterfaceInfo[]) { | |
277 | { TYPE_PNV_XSCOM_INTERFACE }, | |
278 | { } | |
279 | } | |
280 | }; | |
281 | ||
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; | |
318 | pecc->phb_type = TYPE_PNV_PHB5; | |
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 | ||
335 | static void pnv_pec_register_types(void) | |
336 | { | |
337 | type_register_static(&pnv_pec_type_info); | |
338 | type_register_static(&pnv_phb5_pec_type_info); | |
339 | } | |
340 | ||
341 | type_init(pnv_pec_register_types); |