]>
Commit | Line | Data |
---|---|---|
54f59d78 CLG |
1 | /* |
2 | * QEMU PowerPC PowerNV Processor Service Interface (PSI) model | |
3 | * | |
4 | * Copyright (c) 2015-2017, IBM Corporation. | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. | |
18 | */ | |
19 | ||
20 | #include "qemu/osdep.h" | |
21 | #include "hw/hw.h" | |
22 | #include "target/ppc/cpu.h" | |
23 | #include "qemu/log.h" | |
24 | #include "qapi/error.h" | |
25 | ||
26 | #include "exec/address-spaces.h" | |
27 | ||
28 | #include "hw/ppc/fdt.h" | |
29 | #include "hw/ppc/pnv.h" | |
30 | #include "hw/ppc/pnv_xscom.h" | |
31 | #include "hw/ppc/pnv_psi.h" | |
32 | ||
33 | #include <libfdt.h> | |
34 | ||
35 | #define PSIHB_XSCOM_FIR_RW 0x00 | |
36 | #define PSIHB_XSCOM_FIR_AND 0x01 | |
37 | #define PSIHB_XSCOM_FIR_OR 0x02 | |
38 | #define PSIHB_XSCOM_FIRMASK_RW 0x03 | |
39 | #define PSIHB_XSCOM_FIRMASK_AND 0x04 | |
40 | #define PSIHB_XSCOM_FIRMASK_OR 0x05 | |
41 | #define PSIHB_XSCOM_FIRACT0 0x06 | |
42 | #define PSIHB_XSCOM_FIRACT1 0x07 | |
43 | ||
44 | /* Host Bridge Base Address Register */ | |
45 | #define PSIHB_XSCOM_BAR 0x0a | |
46 | #define PSIHB_BAR_EN 0x0000000000000001ull | |
47 | ||
48 | /* FSP Base Address Register */ | |
49 | #define PSIHB_XSCOM_FSPBAR 0x0b | |
50 | ||
51 | /* PSI Host Bridge Control/Status Register */ | |
52 | #define PSIHB_XSCOM_CR 0x0e | |
53 | #define PSIHB_CR_FSP_CMD_ENABLE 0x8000000000000000ull | |
54 | #define PSIHB_CR_FSP_MMIO_ENABLE 0x4000000000000000ull | |
55 | #define PSIHB_CR_FSP_IRQ_ENABLE 0x1000000000000000ull | |
56 | #define PSIHB_CR_FSP_ERR_RSP_ENABLE 0x0800000000000000ull | |
57 | #define PSIHB_CR_PSI_LINK_ENABLE 0x0400000000000000ull | |
58 | #define PSIHB_CR_FSP_RESET 0x0200000000000000ull | |
59 | #define PSIHB_CR_PSIHB_RESET 0x0100000000000000ull | |
60 | #define PSIHB_CR_PSI_IRQ 0x0000800000000000ull | |
61 | #define PSIHB_CR_FSP_IRQ 0x0000400000000000ull | |
62 | #define PSIHB_CR_FSP_LINK_ACTIVE 0x0000200000000000ull | |
63 | #define PSIHB_CR_IRQ_CMD_EXPECT 0x0000010000000000ull | |
64 | /* and more ... */ | |
65 | ||
66 | /* PSIHB Status / Error Mask Register */ | |
67 | #define PSIHB_XSCOM_SEMR 0x0f | |
68 | ||
69 | /* XIVR, to signal interrupts to the CEC firmware. more XIVR below. */ | |
70 | #define PSIHB_XSCOM_XIVR_FSP 0x10 | |
71 | #define PSIHB_XIVR_SERVER_SH 40 | |
72 | #define PSIHB_XIVR_SERVER_MSK (0xffffull << PSIHB_XIVR_SERVER_SH) | |
73 | #define PSIHB_XIVR_PRIO_SH 32 | |
74 | #define PSIHB_XIVR_PRIO_MSK (0xffull << PSIHB_XIVR_PRIO_SH) | |
75 | #define PSIHB_XIVR_SRC_SH 29 | |
76 | #define PSIHB_XIVR_SRC_MSK (0x7ull << PSIHB_XIVR_SRC_SH) | |
77 | #define PSIHB_XIVR_PENDING 0x01000000ull | |
78 | ||
79 | /* PSI Host Bridge Set Control/ Status Register */ | |
80 | #define PSIHB_XSCOM_SCR 0x12 | |
81 | ||
82 | /* PSI Host Bridge Clear Control/ Status Register */ | |
83 | #define PSIHB_XSCOM_CCR 0x13 | |
84 | ||
85 | /* DMA Upper Address Register */ | |
86 | #define PSIHB_XSCOM_DMA_UPADD 0x14 | |
87 | ||
88 | /* Interrupt Status */ | |
89 | #define PSIHB_XSCOM_IRQ_STAT 0x15 | |
90 | #define PSIHB_IRQ_STAT_OCC 0x0000001000000000ull | |
91 | #define PSIHB_IRQ_STAT_FSI 0x0000000800000000ull | |
92 | #define PSIHB_IRQ_STAT_LPCI2C 0x0000000400000000ull | |
93 | #define PSIHB_IRQ_STAT_LOCERR 0x0000000200000000ull | |
94 | #define PSIHB_IRQ_STAT_EXT 0x0000000100000000ull | |
95 | ||
96 | /* remaining XIVR */ | |
97 | #define PSIHB_XSCOM_XIVR_OCC 0x16 | |
98 | #define PSIHB_XSCOM_XIVR_FSI 0x17 | |
99 | #define PSIHB_XSCOM_XIVR_LPCI2C 0x18 | |
100 | #define PSIHB_XSCOM_XIVR_LOCERR 0x19 | |
101 | #define PSIHB_XSCOM_XIVR_EXT 0x1a | |
102 | ||
103 | /* Interrupt Requester Source Compare Register */ | |
104 | #define PSIHB_XSCOM_IRSN 0x1b | |
105 | #define PSIHB_IRSN_COMP_SH 45 | |
106 | #define PSIHB_IRSN_COMP_MSK (0x7ffffull << PSIHB_IRSN_COMP_SH) | |
107 | #define PSIHB_IRSN_IRQ_MUX 0x0000000800000000ull | |
108 | #define PSIHB_IRSN_IRQ_RESET 0x0000000400000000ull | |
109 | #define PSIHB_IRSN_DOWNSTREAM_EN 0x0000000200000000ull | |
110 | #define PSIHB_IRSN_UPSTREAM_EN 0x0000000100000000ull | |
111 | #define PSIHB_IRSN_COMPMASK_SH 13 | |
112 | #define PSIHB_IRSN_COMPMASK_MSK (0x7ffffull << PSIHB_IRSN_COMPMASK_SH) | |
113 | ||
114 | #define PSIHB_BAR_MASK 0x0003fffffff00000ull | |
115 | #define PSIHB_FSPBAR_MASK 0x0003ffff00000000ull | |
116 | ||
117 | static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) | |
118 | { | |
119 | MemoryRegion *sysmem = get_system_memory(); | |
120 | uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; | |
121 | ||
122 | psi->regs[PSIHB_XSCOM_BAR] = bar & (PSIHB_BAR_MASK | PSIHB_BAR_EN); | |
123 | ||
124 | /* Update MR, always remove it first */ | |
125 | if (old & PSIHB_BAR_EN) { | |
126 | memory_region_del_subregion(sysmem, &psi->regs_mr); | |
127 | } | |
128 | ||
129 | /* Then add it back if needed */ | |
130 | if (bar & PSIHB_BAR_EN) { | |
131 | uint64_t addr = bar & PSIHB_BAR_MASK; | |
132 | memory_region_add_subregion(sysmem, addr, &psi->regs_mr); | |
133 | } | |
134 | } | |
135 | ||
136 | static void pnv_psi_update_fsp_mr(PnvPsi *psi) | |
137 | { | |
138 | /* TODO: Update FSP MR if/when we support FSP BAR */ | |
139 | } | |
140 | ||
141 | static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr) | |
142 | { | |
143 | uint64_t old = psi->regs[PSIHB_XSCOM_CR]; | |
144 | ||
145 | psi->regs[PSIHB_XSCOM_CR] = cr; | |
146 | ||
147 | /* Check some bit changes */ | |
148 | if ((old ^ psi->regs[PSIHB_XSCOM_CR]) & PSIHB_CR_FSP_MMIO_ENABLE) { | |
149 | pnv_psi_update_fsp_mr(psi); | |
150 | } | |
151 | } | |
152 | ||
153 | static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) | |
154 | { | |
155 | ICSState *ics = &psi->ics; | |
156 | ||
157 | /* In this model we ignore the up/down enable bits for now | |
158 | * as SW doesn't use them (other than setting them at boot). | |
159 | * We ignore IRQ_MUX, its meaning isn't clear and we don't use | |
160 | * it and finally we ignore reset (XXX fix that ?) | |
161 | */ | |
162 | psi->regs[PSIHB_XSCOM_IRSN] = val & (PSIHB_IRSN_COMP_MSK | | |
163 | PSIHB_IRSN_IRQ_MUX | | |
164 | PSIHB_IRSN_IRQ_RESET | | |
165 | PSIHB_IRSN_DOWNSTREAM_EN | | |
166 | PSIHB_IRSN_UPSTREAM_EN); | |
167 | ||
168 | /* We ignore the compare mask as well, our ICS emulation is too | |
169 | * simplistic to make any use if it, and we extract the offset | |
170 | * from the compare value | |
171 | */ | |
172 | ics->offset = (val & PSIHB_IRSN_COMP_MSK) >> PSIHB_IRSN_COMP_SH; | |
173 | } | |
174 | ||
175 | /* | |
176 | * FSP and PSI interrupts are muxed under the same number. | |
177 | */ | |
178 | static const uint32_t xivr_regs[] = { | |
179 | [PSIHB_IRQ_PSI] = PSIHB_XSCOM_XIVR_FSP, | |
180 | [PSIHB_IRQ_FSP] = PSIHB_XSCOM_XIVR_FSP, | |
181 | [PSIHB_IRQ_OCC] = PSIHB_XSCOM_XIVR_OCC, | |
182 | [PSIHB_IRQ_FSI] = PSIHB_XSCOM_XIVR_FSI, | |
183 | [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_XIVR_LPCI2C, | |
184 | [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_XIVR_LOCERR, | |
185 | [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_XIVR_EXT, | |
186 | }; | |
187 | ||
188 | static const uint32_t stat_regs[] = { | |
189 | [PSIHB_IRQ_PSI] = PSIHB_XSCOM_CR, | |
190 | [PSIHB_IRQ_FSP] = PSIHB_XSCOM_CR, | |
191 | [PSIHB_IRQ_OCC] = PSIHB_XSCOM_IRQ_STAT, | |
192 | [PSIHB_IRQ_FSI] = PSIHB_XSCOM_IRQ_STAT, | |
193 | [PSIHB_IRQ_LPC_I2C] = PSIHB_XSCOM_IRQ_STAT, | |
194 | [PSIHB_IRQ_LOCAL_ERR] = PSIHB_XSCOM_IRQ_STAT, | |
195 | [PSIHB_IRQ_EXTERNAL] = PSIHB_XSCOM_IRQ_STAT, | |
196 | }; | |
197 | ||
198 | static const uint64_t stat_bits[] = { | |
199 | [PSIHB_IRQ_PSI] = PSIHB_CR_PSI_IRQ, | |
200 | [PSIHB_IRQ_FSP] = PSIHB_CR_FSP_IRQ, | |
201 | [PSIHB_IRQ_OCC] = PSIHB_IRQ_STAT_OCC, | |
202 | [PSIHB_IRQ_FSI] = PSIHB_IRQ_STAT_FSI, | |
203 | [PSIHB_IRQ_LPC_I2C] = PSIHB_IRQ_STAT_LPCI2C, | |
204 | [PSIHB_IRQ_LOCAL_ERR] = PSIHB_IRQ_STAT_LOCERR, | |
205 | [PSIHB_IRQ_EXTERNAL] = PSIHB_IRQ_STAT_EXT, | |
206 | }; | |
207 | ||
208 | void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) | |
209 | { | |
210 | ICSState *ics = &psi->ics; | |
211 | uint32_t xivr_reg; | |
212 | uint32_t stat_reg; | |
213 | uint32_t src; | |
214 | bool masked; | |
215 | ||
216 | if (irq > PSIHB_IRQ_EXTERNAL) { | |
217 | qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); | |
218 | return; | |
219 | } | |
220 | ||
221 | xivr_reg = xivr_regs[irq]; | |
222 | stat_reg = stat_regs[irq]; | |
223 | ||
224 | src = (psi->regs[xivr_reg] & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; | |
225 | if (state) { | |
226 | psi->regs[stat_reg] |= stat_bits[irq]; | |
227 | /* TODO: optimization, check mask here. That means | |
228 | * re-evaluating when unmasking | |
229 | */ | |
230 | qemu_irq_raise(ics->qirqs[src]); | |
231 | } else { | |
232 | psi->regs[stat_reg] &= ~stat_bits[irq]; | |
233 | ||
234 | /* FSP and PSI are muxed so don't lower if either is still set */ | |
235 | if (stat_reg != PSIHB_XSCOM_CR || | |
236 | !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { | |
237 | qemu_irq_lower(ics->qirqs[src]); | |
238 | } else { | |
239 | state = true; | |
240 | } | |
241 | } | |
242 | ||
243 | /* Note about the emulation of the pending bit: This isn't | |
244 | * entirely correct. The pending bit should be cleared when the | |
245 | * EOI has been received. However, we don't have callbacks on EOI | |
246 | * (especially not under KVM) so no way to emulate that properly, | |
247 | * so instead we just set that bit as the logical "output" of the | |
248 | * XIVR (ie pending & !masked) | |
249 | * | |
250 | * CLG: We could define a new ICS object with a custom eoi() | |
251 | * handler to clear the pending bit. But I am not sure this would | |
252 | * be useful for the software anyhow. | |
253 | */ | |
254 | masked = (psi->regs[xivr_reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK; | |
255 | if (state && !masked) { | |
256 | psi->regs[xivr_reg] |= PSIHB_XIVR_PENDING; | |
257 | } else { | |
258 | psi->regs[xivr_reg] &= ~PSIHB_XIVR_PENDING; | |
259 | } | |
260 | } | |
261 | ||
262 | static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val) | |
263 | { | |
264 | ICSState *ics = &psi->ics; | |
265 | uint16_t server; | |
266 | uint8_t prio; | |
267 | uint8_t src; | |
268 | ||
269 | psi->regs[reg] = (psi->regs[reg] & PSIHB_XIVR_PENDING) | | |
270 | (val & (PSIHB_XIVR_SERVER_MSK | | |
271 | PSIHB_XIVR_PRIO_MSK | | |
272 | PSIHB_XIVR_SRC_MSK)); | |
273 | val = psi->regs[reg]; | |
274 | server = (val & PSIHB_XIVR_SERVER_MSK) >> PSIHB_XIVR_SERVER_SH; | |
275 | prio = (val & PSIHB_XIVR_PRIO_MSK) >> PSIHB_XIVR_PRIO_SH; | |
276 | src = (val & PSIHB_XIVR_SRC_MSK) >> PSIHB_XIVR_SRC_SH; | |
277 | ||
278 | if (src >= PSI_NUM_INTERRUPTS) { | |
279 | qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", src); | |
280 | return; | |
281 | } | |
282 | ||
283 | /* Remove pending bit if the IRQ is masked */ | |
284 | if ((psi->regs[reg] & PSIHB_XIVR_PRIO_MSK) == PSIHB_XIVR_PRIO_MSK) { | |
285 | psi->regs[reg] &= ~PSIHB_XIVR_PENDING; | |
286 | } | |
287 | ||
288 | /* The low order 2 bits are the link pointer (Type II interrupts). | |
289 | * Shift back to get a valid IRQ server. | |
290 | */ | |
291 | server >>= 2; | |
292 | ||
293 | /* Now because of source remapping, weird things can happen | |
294 | * if you change the source number dynamically, our simple ICS | |
295 | * doesn't deal with remapping. So we just poke a different | |
296 | * ICS entry based on what source number was written. This will | |
297 | * do for now but a more accurate implementation would instead | |
298 | * use a fixed server/prio and a remapper of the generated irq. | |
299 | */ | |
300 | ics_simple_write_xive(ics, src, server, prio, prio); | |
301 | } | |
302 | ||
303 | static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio) | |
304 | { | |
305 | uint64_t val = 0xffffffffffffffffull; | |
306 | ||
307 | switch (offset) { | |
308 | case PSIHB_XSCOM_FIR_RW: | |
309 | case PSIHB_XSCOM_FIRACT0: | |
310 | case PSIHB_XSCOM_FIRACT1: | |
311 | case PSIHB_XSCOM_BAR: | |
312 | case PSIHB_XSCOM_FSPBAR: | |
313 | case PSIHB_XSCOM_CR: | |
314 | case PSIHB_XSCOM_XIVR_FSP: | |
315 | case PSIHB_XSCOM_XIVR_OCC: | |
316 | case PSIHB_XSCOM_XIVR_FSI: | |
317 | case PSIHB_XSCOM_XIVR_LPCI2C: | |
318 | case PSIHB_XSCOM_XIVR_LOCERR: | |
319 | case PSIHB_XSCOM_XIVR_EXT: | |
320 | case PSIHB_XSCOM_IRQ_STAT: | |
321 | case PSIHB_XSCOM_SEMR: | |
322 | case PSIHB_XSCOM_DMA_UPADD: | |
323 | case PSIHB_XSCOM_IRSN: | |
324 | val = psi->regs[offset]; | |
325 | break; | |
326 | default: | |
327 | qemu_log_mask(LOG_UNIMP, "PSI: read at Ox%" PRIx32 "\n", offset); | |
328 | } | |
329 | return val; | |
330 | } | |
331 | ||
332 | static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, | |
333 | bool mmio) | |
334 | { | |
335 | switch (offset) { | |
336 | case PSIHB_XSCOM_FIR_RW: | |
337 | case PSIHB_XSCOM_FIRACT0: | |
338 | case PSIHB_XSCOM_FIRACT1: | |
339 | case PSIHB_XSCOM_SEMR: | |
340 | case PSIHB_XSCOM_DMA_UPADD: | |
341 | psi->regs[offset] = val; | |
342 | break; | |
343 | case PSIHB_XSCOM_FIR_OR: | |
344 | psi->regs[PSIHB_XSCOM_FIR_RW] |= val; | |
345 | break; | |
346 | case PSIHB_XSCOM_FIR_AND: | |
347 | psi->regs[PSIHB_XSCOM_FIR_RW] &= val; | |
348 | break; | |
349 | case PSIHB_XSCOM_BAR: | |
350 | /* Only XSCOM can write this one */ | |
351 | if (!mmio) { | |
352 | pnv_psi_set_bar(psi, val); | |
353 | } else { | |
354 | qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of BAR\n"); | |
355 | } | |
356 | break; | |
357 | case PSIHB_XSCOM_FSPBAR: | |
358 | psi->regs[PSIHB_XSCOM_FSPBAR] = val & PSIHB_FSPBAR_MASK; | |
359 | pnv_psi_update_fsp_mr(psi); | |
360 | break; | |
361 | case PSIHB_XSCOM_CR: | |
362 | pnv_psi_set_cr(psi, val); | |
363 | break; | |
364 | case PSIHB_XSCOM_SCR: | |
365 | pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] | val); | |
366 | break; | |
367 | case PSIHB_XSCOM_CCR: | |
368 | pnv_psi_set_cr(psi, psi->regs[PSIHB_XSCOM_CR] & ~val); | |
369 | break; | |
370 | case PSIHB_XSCOM_XIVR_FSP: | |
371 | case PSIHB_XSCOM_XIVR_OCC: | |
372 | case PSIHB_XSCOM_XIVR_FSI: | |
373 | case PSIHB_XSCOM_XIVR_LPCI2C: | |
374 | case PSIHB_XSCOM_XIVR_LOCERR: | |
375 | case PSIHB_XSCOM_XIVR_EXT: | |
376 | pnv_psi_set_xivr(psi, offset, val); | |
377 | break; | |
378 | case PSIHB_XSCOM_IRQ_STAT: | |
379 | /* Read only */ | |
380 | qemu_log_mask(LOG_GUEST_ERROR, "PSI: invalid write of IRQ_STAT\n"); | |
381 | break; | |
382 | case PSIHB_XSCOM_IRSN: | |
383 | pnv_psi_set_irsn(psi, val); | |
384 | break; | |
385 | default: | |
386 | qemu_log_mask(LOG_UNIMP, "PSI: write at Ox%" PRIx32 "\n", offset); | |
387 | } | |
388 | } | |
389 | ||
390 | /* | |
391 | * The values of the registers when accessed through the MMIO region | |
392 | * follow the relation : xscom = (mmio + 0x50) >> 3 | |
393 | */ | |
394 | static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) | |
395 | { | |
396 | return pnv_psi_reg_read(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, true); | |
397 | } | |
398 | ||
399 | static void pnv_psi_mmio_write(void *opaque, hwaddr addr, | |
400 | uint64_t val, unsigned size) | |
401 | { | |
402 | pnv_psi_reg_write(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, val, true); | |
403 | } | |
404 | ||
405 | static const MemoryRegionOps psi_mmio_ops = { | |
406 | .read = pnv_psi_mmio_read, | |
407 | .write = pnv_psi_mmio_write, | |
408 | .endianness = DEVICE_BIG_ENDIAN, | |
409 | .valid = { | |
410 | .min_access_size = 8, | |
411 | .max_access_size = 8, | |
412 | }, | |
413 | .impl = { | |
414 | .min_access_size = 8, | |
415 | .max_access_size = 8, | |
416 | }, | |
417 | }; | |
418 | ||
419 | static uint64_t pnv_psi_xscom_read(void *opaque, hwaddr addr, unsigned size) | |
420 | { | |
421 | return pnv_psi_reg_read(opaque, addr >> 3, false); | |
422 | } | |
423 | ||
424 | static void pnv_psi_xscom_write(void *opaque, hwaddr addr, | |
425 | uint64_t val, unsigned size) | |
426 | { | |
427 | pnv_psi_reg_write(opaque, addr >> 3, val, false); | |
428 | } | |
429 | ||
430 | static const MemoryRegionOps pnv_psi_xscom_ops = { | |
431 | .read = pnv_psi_xscom_read, | |
432 | .write = pnv_psi_xscom_write, | |
433 | .endianness = DEVICE_BIG_ENDIAN, | |
434 | .valid = { | |
435 | .min_access_size = 8, | |
436 | .max_access_size = 8, | |
437 | }, | |
438 | .impl = { | |
439 | .min_access_size = 8, | |
440 | .max_access_size = 8, | |
441 | } | |
442 | }; | |
443 | ||
444 | static void pnv_psi_init(Object *obj) | |
445 | { | |
446 | PnvPsi *psi = PNV_PSI(obj); | |
447 | ||
448 | object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); | |
449 | object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); | |
450 | } | |
451 | ||
452 | static const uint8_t irq_to_xivr[] = { | |
453 | PSIHB_XSCOM_XIVR_FSP, | |
454 | PSIHB_XSCOM_XIVR_OCC, | |
455 | PSIHB_XSCOM_XIVR_FSI, | |
456 | PSIHB_XSCOM_XIVR_LPCI2C, | |
457 | PSIHB_XSCOM_XIVR_LOCERR, | |
458 | PSIHB_XSCOM_XIVR_EXT, | |
459 | }; | |
460 | ||
461 | static void pnv_psi_realize(DeviceState *dev, Error **errp) | |
462 | { | |
463 | PnvPsi *psi = PNV_PSI(dev); | |
464 | ICSState *ics = &psi->ics; | |
465 | Object *obj; | |
466 | Error *err = NULL; | |
467 | unsigned int i; | |
468 | ||
469 | obj = object_property_get_link(OBJECT(dev), "xics", &err); | |
470 | if (!obj) { | |
471 | error_setg(errp, "%s: required link 'xics' not found: %s", | |
472 | __func__, error_get_pretty(err)); | |
473 | return; | |
474 | } | |
475 | ||
476 | /* Create PSI interrupt control source */ | |
ad265631 GK |
477 | object_property_add_const_link(OBJECT(ics), ICS_PROP_XICS, obj, |
478 | &error_abort); | |
54f59d78 CLG |
479 | object_property_set_int(OBJECT(ics), PSI_NUM_INTERRUPTS, "nr-irqs", &err); |
480 | if (err) { | |
481 | error_propagate(errp, err); | |
482 | return; | |
483 | } | |
484 | object_property_set_bool(OBJECT(ics), true, "realized", &err); | |
485 | if (err) { | |
486 | error_propagate(errp, err); | |
487 | return; | |
488 | } | |
489 | ||
490 | for (i = 0; i < ics->nr_irqs; i++) { | |
491 | ics_set_irq_type(ics, i, true); | |
492 | } | |
493 | ||
494 | /* XSCOM region for PSI registers */ | |
495 | pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops, | |
496 | psi, "xscom-psi", PNV_XSCOM_PSIHB_SIZE); | |
497 | ||
498 | /* Initialize MMIO region */ | |
499 | memory_region_init_io(&psi->regs_mr, OBJECT(dev), &psi_mmio_ops, psi, | |
500 | "psihb", PNV_PSIHB_SIZE); | |
501 | ||
502 | /* Default BAR for MMIO region */ | |
503 | pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); | |
504 | ||
505 | /* Default sources in XIVR */ | |
506 | for (i = 0; i < PSI_NUM_INTERRUPTS; i++) { | |
507 | uint8_t xivr = irq_to_xivr[i]; | |
508 | psi->regs[xivr] = PSIHB_XIVR_PRIO_MSK | | |
509 | ((uint64_t) i << PSIHB_XIVR_SRC_SH); | |
510 | } | |
511 | } | |
512 | ||
b168a138 | 513 | static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) |
54f59d78 CLG |
514 | { |
515 | const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; | |
516 | char *name; | |
517 | int offset; | |
518 | uint32_t lpc_pcba = PNV_XSCOM_PSIHB_BASE; | |
519 | uint32_t reg[] = { | |
520 | cpu_to_be32(lpc_pcba), | |
521 | cpu_to_be32(PNV_XSCOM_PSIHB_SIZE) | |
522 | }; | |
523 | ||
524 | name = g_strdup_printf("psihb@%x", lpc_pcba); | |
525 | offset = fdt_add_subnode(fdt, xscom_offset, name); | |
526 | _FDT(offset); | |
527 | g_free(name); | |
528 | ||
529 | _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); | |
530 | ||
531 | _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); | |
532 | _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); | |
533 | _FDT((fdt_setprop(fdt, offset, "compatible", compat, | |
534 | sizeof(compat)))); | |
535 | return 0; | |
536 | } | |
537 | ||
538 | static Property pnv_psi_properties[] = { | |
539 | DEFINE_PROP_UINT64("bar", PnvPsi, bar, 0), | |
540 | DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), | |
541 | DEFINE_PROP_END_OF_LIST(), | |
542 | }; | |
543 | ||
544 | static void pnv_psi_class_init(ObjectClass *klass, void *data) | |
545 | { | |
546 | DeviceClass *dc = DEVICE_CLASS(klass); | |
547 | PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); | |
548 | ||
b168a138 | 549 | xdc->dt_xscom = pnv_psi_dt_xscom; |
54f59d78 CLG |
550 | |
551 | dc->realize = pnv_psi_realize; | |
552 | dc->props = pnv_psi_properties; | |
553 | } | |
554 | ||
555 | static const TypeInfo pnv_psi_info = { | |
556 | .name = TYPE_PNV_PSI, | |
557 | .parent = TYPE_SYS_BUS_DEVICE, | |
558 | .instance_size = sizeof(PnvPsi), | |
559 | .instance_init = pnv_psi_init, | |
560 | .class_init = pnv_psi_class_init, | |
561 | .interfaces = (InterfaceInfo[]) { | |
562 | { TYPE_PNV_XSCOM_INTERFACE }, | |
563 | { } | |
564 | } | |
565 | }; | |
566 | ||
567 | static void pnv_psi_register_types(void) | |
568 | { | |
569 | type_register_static(&pnv_psi_info); | |
570 | } | |
571 | ||
572 | type_init(pnv_psi_register_types) |