]>
Commit | Line | Data |
---|---|---|
9ae1329e CLG |
1 | /* |
2 | * QEMU PowerPC PowerNV (POWER8) PHB3 model | |
3 | * | |
4 | * Copyright (c) 2014-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_phb3_regs.h" | |
16 | #include "hw/pci-host/pnv_phb3.h" | |
17 | #include "hw/ppc/pnv.h" | |
18 | #include "hw/ppc/pnv_xscom.h" | |
19 | #include "hw/pci/pci_bridge.h" | |
20 | #include "hw/pci/pci_bus.h" | |
21 | ||
22 | #include <libfdt.h> | |
23 | ||
24 | #define phb3_pbcq_error(pbcq, fmt, ...) \ | |
25 | qemu_log_mask(LOG_GUEST_ERROR, "phb3_pbcq[%d:%d]: " fmt "\n", \ | |
26 | (pbcq)->phb->chip_id, (pbcq)->phb->phb_id, ## __VA_ARGS__) | |
27 | ||
28 | static uint64_t pnv_pbcq_nest_xscom_read(void *opaque, hwaddr addr, | |
29 | unsigned size) | |
30 | { | |
31 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
32 | uint32_t offset = addr >> 3; | |
33 | ||
34 | return pbcq->nest_regs[offset]; | |
35 | } | |
36 | ||
37 | static uint64_t pnv_pbcq_pci_xscom_read(void *opaque, hwaddr addr, | |
38 | unsigned size) | |
39 | { | |
40 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
41 | uint32_t offset = addr >> 3; | |
42 | ||
43 | return pbcq->pci_regs[offset]; | |
44 | } | |
45 | ||
46 | static uint64_t pnv_pbcq_spci_xscom_read(void *opaque, hwaddr addr, | |
47 | unsigned size) | |
48 | { | |
49 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
50 | uint32_t offset = addr >> 3; | |
51 | ||
52 | if (offset == PBCQ_SPCI_ASB_DATA) { | |
53 | return pnv_phb3_reg_read(pbcq->phb, | |
54 | pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8); | |
55 | } | |
56 | return pbcq->spci_regs[offset]; | |
57 | } | |
58 | ||
59 | static void pnv_pbcq_update_map(PnvPBCQState *pbcq) | |
60 | { | |
61 | uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN]; | |
62 | uint64_t bar, mask, size; | |
63 | ||
64 | /* | |
65 | * NOTE: This will really not work well if those are remapped | |
66 | * after the PHB has created its sub regions. We could do better | |
67 | * if we had a way to resize regions but we don't really care | |
68 | * that much in practice as the stuff below really only happens | |
69 | * once early during boot | |
70 | */ | |
71 | ||
72 | /* Handle unmaps */ | |
73 | if (memory_region_is_mapped(&pbcq->mmbar0) && | |
74 | !(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { | |
75 | memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0); | |
76 | } | |
77 | if (memory_region_is_mapped(&pbcq->mmbar1) && | |
78 | !(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { | |
79 | memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1); | |
80 | } | |
81 | if (memory_region_is_mapped(&pbcq->phbbar) && | |
82 | !(bar_en & PBCQ_NEST_BAR_EN_PHB)) { | |
83 | memory_region_del_subregion(get_system_memory(), &pbcq->phbbar); | |
84 | } | |
85 | ||
86 | /* Update PHB */ | |
87 | pnv_phb3_update_regions(pbcq->phb); | |
88 | ||
89 | /* Handle maps */ | |
90 | if (!memory_region_is_mapped(&pbcq->mmbar0) && | |
91 | (bar_en & PBCQ_NEST_BAR_EN_MMIO0)) { | |
92 | bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14; | |
93 | mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0]; | |
94 | size = ((~mask) >> 14) + 1; | |
95 | memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size); | |
96 | memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0); | |
97 | pbcq->mmio0_base = bar; | |
98 | pbcq->mmio0_size = size; | |
99 | } | |
100 | if (!memory_region_is_mapped(&pbcq->mmbar1) && | |
101 | (bar_en & PBCQ_NEST_BAR_EN_MMIO1)) { | |
102 | bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14; | |
103 | mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1]; | |
104 | size = ((~mask) >> 14) + 1; | |
105 | memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size); | |
106 | memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1); | |
107 | pbcq->mmio1_base = bar; | |
108 | pbcq->mmio1_size = size; | |
109 | } | |
110 | if (!memory_region_is_mapped(&pbcq->phbbar) | |
111 | && (bar_en & PBCQ_NEST_BAR_EN_PHB)) { | |
112 | bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14; | |
113 | size = 0x1000; | |
114 | memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size); | |
115 | memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar); | |
116 | } | |
117 | ||
118 | /* Update PHB */ | |
119 | pnv_phb3_update_regions(pbcq->phb); | |
120 | } | |
121 | ||
122 | static void pnv_pbcq_nest_xscom_write(void *opaque, hwaddr addr, | |
123 | uint64_t val, unsigned size) | |
124 | { | |
125 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
126 | uint32_t reg = addr >> 3; | |
127 | ||
128 | switch (reg) { | |
129 | case PBCQ_NEST_MMIO_BAR0: | |
130 | case PBCQ_NEST_MMIO_BAR1: | |
131 | case PBCQ_NEST_MMIO_MASK0: | |
132 | case PBCQ_NEST_MMIO_MASK1: | |
133 | if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & | |
134 | (PBCQ_NEST_BAR_EN_MMIO0 | | |
135 | PBCQ_NEST_BAR_EN_MMIO1)) { | |
136 | phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported"); | |
137 | } | |
138 | pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull; | |
139 | break; | |
140 | case PBCQ_NEST_PHB_BAR: | |
141 | if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) { | |
142 | phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported"); | |
143 | } | |
144 | pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull; | |
145 | break; | |
146 | case PBCQ_NEST_BAR_EN: | |
147 | pbcq->nest_regs[reg] = val & 0xf800000000000000ull; | |
148 | pnv_pbcq_update_map(pbcq); | |
149 | pnv_phb3_remap_irqs(pbcq->phb); | |
150 | break; | |
151 | case PBCQ_NEST_IRSN_COMPARE: | |
152 | case PBCQ_NEST_IRSN_MASK: | |
153 | pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP; | |
154 | pnv_phb3_remap_irqs(pbcq->phb); | |
155 | break; | |
156 | case PBCQ_NEST_LSI_SRC_ID: | |
157 | pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC; | |
158 | pnv_phb3_remap_irqs(pbcq->phb); | |
159 | break; | |
160 | default: | |
161 | phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, | |
162 | addr, val); | |
163 | } | |
164 | } | |
165 | ||
166 | static void pnv_pbcq_pci_xscom_write(void *opaque, hwaddr addr, | |
167 | uint64_t val, unsigned size) | |
168 | { | |
169 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
170 | uint32_t reg = addr >> 3; | |
171 | ||
172 | switch (reg) { | |
173 | case PBCQ_PCI_BAR2: | |
174 | pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull; | |
175 | pnv_pbcq_update_map(pbcq); | |
e8ead7d5 | 176 | break; |
9ae1329e CLG |
177 | default: |
178 | phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, | |
179 | addr, val); | |
180 | } | |
181 | } | |
182 | ||
183 | static void pnv_pbcq_spci_xscom_write(void *opaque, hwaddr addr, | |
184 | uint64_t val, unsigned size) | |
185 | { | |
186 | PnvPBCQState *pbcq = PNV_PBCQ(opaque); | |
187 | uint32_t reg = addr >> 3; | |
188 | ||
189 | switch (reg) { | |
190 | case PBCQ_SPCI_ASB_ADDR: | |
191 | pbcq->spci_regs[reg] = val & 0xfff; | |
192 | break; | |
193 | case PBCQ_SPCI_ASB_STATUS: | |
194 | pbcq->spci_regs[reg] &= ~val; | |
195 | break; | |
196 | case PBCQ_SPCI_ASB_DATA: | |
197 | pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], | |
198 | val, 8); | |
199 | break; | |
200 | case PBCQ_SPCI_AIB_CAPP_EN: | |
201 | case PBCQ_SPCI_CAPP_SEC_TMR: | |
202 | break; | |
203 | default: | |
204 | phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__, | |
205 | addr, val); | |
206 | } | |
207 | } | |
208 | ||
209 | static const MemoryRegionOps pnv_pbcq_nest_xscom_ops = { | |
210 | .read = pnv_pbcq_nest_xscom_read, | |
211 | .write = pnv_pbcq_nest_xscom_write, | |
212 | .valid.min_access_size = 8, | |
213 | .valid.max_access_size = 8, | |
214 | .impl.min_access_size = 8, | |
215 | .impl.max_access_size = 8, | |
216 | .endianness = DEVICE_BIG_ENDIAN, | |
217 | }; | |
218 | ||
219 | static const MemoryRegionOps pnv_pbcq_pci_xscom_ops = { | |
220 | .read = pnv_pbcq_pci_xscom_read, | |
221 | .write = pnv_pbcq_pci_xscom_write, | |
222 | .valid.min_access_size = 8, | |
223 | .valid.max_access_size = 8, | |
224 | .impl.min_access_size = 8, | |
225 | .impl.max_access_size = 8, | |
226 | .endianness = DEVICE_BIG_ENDIAN, | |
227 | }; | |
228 | ||
229 | static const MemoryRegionOps pnv_pbcq_spci_xscom_ops = { | |
230 | .read = pnv_pbcq_spci_xscom_read, | |
231 | .write = pnv_pbcq_spci_xscom_write, | |
232 | .valid.min_access_size = 8, | |
233 | .valid.max_access_size = 8, | |
234 | .impl.min_access_size = 8, | |
235 | .impl.max_access_size = 8, | |
236 | .endianness = DEVICE_BIG_ENDIAN, | |
237 | }; | |
238 | ||
239 | static void pnv_pbcq_default_bars(PnvPBCQState *pbcq) | |
240 | { | |
241 | uint64_t mm0, mm1, reg; | |
242 | PnvPHB3 *phb = pbcq->phb; | |
243 | ||
244 | mm0 = 0x3d00000000000ull + 0x4000000000ull * phb->chip_id + | |
245 | 0x1000000000ull * phb->phb_id; | |
246 | mm1 = 0x3ff8000000000ull + 0x0200000000ull * phb->chip_id + | |
247 | 0x0080000000ull * phb->phb_id; | |
248 | reg = 0x3fffe40000000ull + 0x0000400000ull * phb->chip_id + | |
249 | 0x0000100000ull * phb->phb_id; | |
250 | ||
251 | pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14; | |
252 | pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14; | |
253 | pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14; | |
254 | pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14; | |
255 | pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14; | |
256 | pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14; | |
257 | } | |
258 | ||
259 | static void pnv_pbcq_realize(DeviceState *dev, Error **errp) | |
260 | { | |
261 | PnvPBCQState *pbcq = PNV_PBCQ(dev); | |
262 | PnvPHB3 *phb; | |
263 | char name[32]; | |
264 | ||
265 | assert(pbcq->phb); | |
266 | phb = pbcq->phb; | |
267 | ||
268 | /* TODO: Fix OPAL to do that: establish default BAR values */ | |
269 | pnv_pbcq_default_bars(pbcq); | |
270 | ||
271 | /* Initialize the XSCOM region for the PBCQ registers */ | |
272 | snprintf(name, sizeof(name), "xscom-pbcq-nest-%d.%d", | |
273 | phb->chip_id, phb->phb_id); | |
274 | pnv_xscom_region_init(&pbcq->xscom_nest_regs, OBJECT(dev), | |
275 | &pnv_pbcq_nest_xscom_ops, pbcq, name, | |
276 | PNV_XSCOM_PBCQ_NEST_SIZE); | |
277 | snprintf(name, sizeof(name), "xscom-pbcq-pci-%d.%d", | |
278 | phb->chip_id, phb->phb_id); | |
279 | pnv_xscom_region_init(&pbcq->xscom_pci_regs, OBJECT(dev), | |
280 | &pnv_pbcq_pci_xscom_ops, pbcq, name, | |
281 | PNV_XSCOM_PBCQ_PCI_SIZE); | |
282 | snprintf(name, sizeof(name), "xscom-pbcq-spci-%d.%d", | |
283 | phb->chip_id, phb->phb_id); | |
284 | pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev), | |
285 | &pnv_pbcq_spci_xscom_ops, pbcq, name, | |
286 | PNV_XSCOM_PBCQ_SPCI_SIZE); | |
287 | } | |
288 | ||
289 | static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt, | |
290 | int xscom_offset) | |
291 | { | |
292 | const char compat[] = "ibm,power8-pbcq"; | |
293 | PnvPHB3 *phb = PNV_PBCQ(dev)->phb; | |
294 | char *name; | |
295 | int offset; | |
296 | uint32_t lpc_pcba = PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id; | |
297 | uint32_t reg[] = { | |
298 | cpu_to_be32(lpc_pcba), | |
299 | cpu_to_be32(PNV_XSCOM_PBCQ_NEST_SIZE), | |
300 | cpu_to_be32(PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id), | |
301 | cpu_to_be32(PNV_XSCOM_PBCQ_PCI_SIZE), | |
302 | cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id), | |
303 | cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_SIZE) | |
304 | }; | |
305 | ||
306 | name = g_strdup_printf("pbcq@%x", lpc_pcba); | |
307 | offset = fdt_add_subnode(fdt, xscom_offset, name); | |
308 | _FDT(offset); | |
309 | g_free(name); | |
310 | ||
311 | _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); | |
312 | ||
313 | _FDT((fdt_setprop_cell(fdt, offset, "ibm,phb-index", phb->phb_id))); | |
314 | _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", phb->chip_id))); | |
315 | _FDT((fdt_setprop(fdt, offset, "compatible", compat, | |
316 | sizeof(compat)))); | |
317 | return 0; | |
318 | } | |
319 | ||
320 | static void phb3_pbcq_instance_init(Object *obj) | |
321 | { | |
322 | PnvPBCQState *pbcq = PNV_PBCQ(obj); | |
323 | ||
324 | object_property_add_link(obj, "phb", TYPE_PNV_PHB3, | |
325 | (Object **)&pbcq->phb, | |
326 | object_property_allow_set_link, | |
d2623129 | 327 | OBJ_PROP_LINK_STRONG); |
9ae1329e CLG |
328 | } |
329 | ||
330 | static void pnv_pbcq_class_init(ObjectClass *klass, void *data) | |
331 | { | |
332 | DeviceClass *dc = DEVICE_CLASS(klass); | |
333 | PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); | |
334 | ||
335 | xdc->dt_xscom = pnv_pbcq_dt_xscom; | |
336 | ||
337 | dc->realize = pnv_pbcq_realize; | |
23a782eb | 338 | dc->user_creatable = false; |
9ae1329e CLG |
339 | } |
340 | ||
341 | static const TypeInfo pnv_pbcq_type_info = { | |
342 | .name = TYPE_PNV_PBCQ, | |
343 | .parent = TYPE_DEVICE, | |
344 | .instance_size = sizeof(PnvPBCQState), | |
345 | .instance_init = phb3_pbcq_instance_init, | |
346 | .class_init = pnv_pbcq_class_init, | |
347 | .interfaces = (InterfaceInfo[]) { | |
348 | { TYPE_PNV_XSCOM_INTERFACE }, | |
349 | { } | |
350 | } | |
351 | }; | |
352 | ||
353 | static void pnv_pbcq_register_types(void) | |
354 | { | |
355 | type_register_static(&pnv_pbcq_type_info); | |
356 | } | |
357 | ||
358 | type_init(pnv_pbcq_register_types) |