]>
Commit | Line | Data |
---|---|---|
3887d241 B |
1 | /* |
2 | * QEMU PowerPC PowerNV Emulation of a few HOMER related registers | |
3 | * | |
4 | * Copyright (c) 2019, IBM Corporation. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License, version 2, as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, see <http://www.gnu.org/licenses/>. | |
17 | */ | |
18 | ||
19 | #include "qemu/osdep.h" | |
8f092316 | 20 | #include "qemu/log.h" |
3887d241 B |
21 | #include "qapi/error.h" |
22 | #include "exec/hwaddr.h" | |
23 | #include "exec/memory.h" | |
24 | #include "sysemu/cpus.h" | |
25 | #include "hw/qdev-core.h" | |
f2582acf | 26 | #include "hw/qdev-properties.h" |
3887d241 B |
27 | #include "hw/ppc/pnv.h" |
28 | #include "hw/ppc/pnv_homer.h" | |
8f092316 | 29 | #include "hw/ppc/pnv_xscom.h" |
3887d241 B |
30 | |
31 | ||
32 | static bool core_max_array(PnvHomer *homer, hwaddr addr) | |
33 | { | |
34 | int i; | |
35 | PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); | |
36 | ||
37 | for (i = 0; i <= homer->chip->nr_cores; i++) { | |
38 | if (addr == (hmrc->core_max_base + i)) { | |
39 | return true; | |
40 | } | |
41 | } | |
42 | return false; | |
43 | } | |
44 | ||
45 | /* P8 Pstate table */ | |
46 | ||
47 | #define PNV8_OCC_PSTATE_VERSION 0x1f8001 | |
48 | #define PNV8_OCC_PSTATE_MIN 0x1f8003 | |
49 | #define PNV8_OCC_PSTATE_VALID 0x1f8000 | |
50 | #define PNV8_OCC_PSTATE_THROTTLE 0x1f8002 | |
51 | #define PNV8_OCC_PSTATE_NOM 0x1f8004 | |
52 | #define PNV8_OCC_PSTATE_TURBO 0x1f8005 | |
53 | #define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006 | |
54 | #define PNV8_OCC_PSTATE_DATA 0x1f8008 | |
55 | #define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010 | |
56 | #define PNV8_OCC_PSTATE_ID_ONE 0x1f8018 | |
57 | #define PNV8_OCC_PSTATE_ID_TWO 0x1f8020 | |
58 | #define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012 | |
59 | #define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013 | |
60 | #define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014 | |
61 | #define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c | |
62 | #define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024 | |
63 | #define PNV8_CORE_MAX_BASE 0x1f8810 | |
64 | ||
65 | ||
66 | static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, | |
67 | unsigned size) | |
68 | { | |
69 | PnvHomer *homer = PNV_HOMER(opaque); | |
70 | ||
71 | switch (addr) { | |
72 | case PNV8_OCC_PSTATE_VERSION: | |
73 | case PNV8_OCC_PSTATE_MIN: | |
74 | case PNV8_OCC_PSTATE_ID_ZERO: | |
75 | return 0; | |
76 | case PNV8_OCC_PSTATE_VALID: | |
77 | case PNV8_OCC_PSTATE_THROTTLE: | |
78 | case PNV8_OCC_PSTATE_NOM: | |
79 | case PNV8_OCC_PSTATE_TURBO: | |
80 | case PNV8_OCC_PSTATE_ID_ONE: | |
81 | case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER: | |
82 | case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: | |
83 | return 1; | |
84 | case PNV8_OCC_PSTATE_ULTRA_TURBO: | |
85 | case PNV8_OCC_PSTATE_ID_TWO: | |
86 | return 2; | |
87 | case PNV8_OCC_PSTATE_DATA: | |
88 | return 0x1000000000000000; | |
89 | /* P8 frequency for 0, 1, and 2 pstates */ | |
90 | case PNV8_OCC_PSTATE_ZERO_FREQUENCY: | |
91 | case PNV8_OCC_PSTATE_ONE_FREQUENCY: | |
92 | case PNV8_OCC_PSTATE_TWO_FREQUENCY: | |
93 | return 3000; | |
94 | } | |
95 | /* pstate table core max array */ | |
96 | if (core_max_array(homer, addr)) { | |
97 | return 1; | |
98 | } | |
99 | return 0; | |
100 | } | |
101 | ||
102 | static void pnv_power8_homer_write(void *opaque, hwaddr addr, | |
103 | uint64_t val, unsigned size) | |
104 | { | |
105 | /* callback function defined to homer write */ | |
106 | return; | |
107 | } | |
108 | ||
109 | static const MemoryRegionOps pnv_power8_homer_ops = { | |
110 | .read = pnv_power8_homer_read, | |
111 | .write = pnv_power8_homer_write, | |
112 | .valid.min_access_size = 1, | |
113 | .valid.max_access_size = 8, | |
114 | .impl.min_access_size = 1, | |
115 | .impl.max_access_size = 8, | |
116 | .endianness = DEVICE_BIG_ENDIAN, | |
117 | }; | |
118 | ||
8f092316 CLG |
119 | /* P8 PBA BARs */ |
120 | #define PBA_BAR0 0x00 | |
121 | #define PBA_BAR1 0x01 | |
122 | #define PBA_BAR2 0x02 | |
123 | #define PBA_BAR3 0x03 | |
124 | #define PBA_BARMASK0 0x04 | |
125 | #define PBA_BARMASK1 0x05 | |
126 | #define PBA_BARMASK2 0x06 | |
127 | #define PBA_BARMASK3 0x07 | |
128 | ||
129 | static uint64_t pnv_homer_power8_pba_read(void *opaque, hwaddr addr, | |
130 | unsigned size) | |
131 | { | |
132 | PnvHomer *homer = PNV_HOMER(opaque); | |
133 | PnvChip *chip = homer->chip; | |
134 | uint32_t reg = addr >> 3; | |
135 | uint64_t val = 0; | |
136 | ||
137 | switch (reg) { | |
138 | case PBA_BAR0: | |
139 | val = PNV_HOMER_BASE(chip); | |
140 | break; | |
141 | case PBA_BARMASK0: /* P8 homer region mask */ | |
142 | val = (PNV_HOMER_SIZE - 1) & 0x300000; | |
143 | break; | |
144 | case PBA_BAR3: /* P8 occ common area */ | |
145 | val = PNV_OCC_COMMON_AREA_BASE; | |
146 | break; | |
147 | case PBA_BARMASK3: /* P8 occ common area mask */ | |
148 | val = (PNV_OCC_COMMON_AREA_SIZE - 1) & 0x700000; | |
149 | break; | |
150 | default: | |
151 | qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" | |
152 | HWADDR_PRIx "\n", addr >> 3); | |
153 | } | |
154 | return val; | |
155 | } | |
156 | ||
157 | static void pnv_homer_power8_pba_write(void *opaque, hwaddr addr, | |
158 | uint64_t val, unsigned size) | |
159 | { | |
160 | qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" | |
161 | HWADDR_PRIx "\n", addr >> 3); | |
162 | } | |
163 | ||
164 | static const MemoryRegionOps pnv_homer_power8_pba_ops = { | |
165 | .read = pnv_homer_power8_pba_read, | |
166 | .write = pnv_homer_power8_pba_write, | |
167 | .valid.min_access_size = 8, | |
168 | .valid.max_access_size = 8, | |
169 | .impl.min_access_size = 8, | |
170 | .impl.max_access_size = 8, | |
171 | .endianness = DEVICE_BIG_ENDIAN, | |
172 | }; | |
173 | ||
3887d241 B |
174 | static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) |
175 | { | |
176 | PnvHomerClass *homer = PNV_HOMER_CLASS(klass); | |
177 | ||
8f092316 CLG |
178 | homer->pba_size = PNV_XSCOM_PBA_SIZE; |
179 | homer->pba_ops = &pnv_homer_power8_pba_ops; | |
3887d241 B |
180 | homer->homer_size = PNV_HOMER_SIZE; |
181 | homer->homer_ops = &pnv_power8_homer_ops; | |
182 | homer->core_max_base = PNV8_CORE_MAX_BASE; | |
183 | } | |
184 | ||
185 | static const TypeInfo pnv_homer_power8_type_info = { | |
186 | .name = TYPE_PNV8_HOMER, | |
187 | .parent = TYPE_PNV_HOMER, | |
188 | .instance_size = sizeof(PnvHomer), | |
189 | .class_init = pnv_homer_power8_class_init, | |
190 | }; | |
191 | ||
192 | /* P9 Pstate table */ | |
193 | ||
194 | #define PNV9_OCC_PSTATE_ID_ZERO 0xe2018 | |
195 | #define PNV9_OCC_PSTATE_ID_ONE 0xe2020 | |
196 | #define PNV9_OCC_PSTATE_ID_TWO 0xe2028 | |
197 | #define PNV9_OCC_PSTATE_DATA 0xe2000 | |
198 | #define PNV9_OCC_PSTATE_DATA_AREA 0xe2008 | |
199 | #define PNV9_OCC_PSTATE_MIN 0xe2003 | |
200 | #define PNV9_OCC_PSTATE_NOM 0xe2004 | |
201 | #define PNV9_OCC_PSTATE_TURBO 0xe2005 | |
202 | #define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818 | |
203 | #define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006 | |
204 | #define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001 | |
205 | #define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85 | |
206 | #define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008 | |
207 | #define PNV9_CHIP_HOMER_BASE 0x0 | |
208 | #define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c | |
209 | #define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024 | |
210 | #define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c | |
211 | #define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002 | |
212 | #define PNV9_CORE_MAX_BASE 0xe2819 | |
213 | ||
214 | ||
215 | static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, | |
216 | unsigned size) | |
217 | { | |
218 | PnvHomer *homer = PNV_HOMER(opaque); | |
219 | ||
220 | switch (addr) { | |
221 | case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO: | |
222 | case PNV9_OCC_PSTATE_ID_ZERO: | |
223 | return 0; | |
224 | case PNV9_OCC_PSTATE_DATA: | |
225 | case PNV9_OCC_ROLE_MASTER_OR_SLAVE: | |
226 | case PNV9_OCC_PSTATE_NOM: | |
227 | case PNV9_OCC_PSTATE_TURBO: | |
228 | case PNV9_OCC_PSTATE_ID_ONE: | |
229 | case PNV9_OCC_PSTATE_ULTRA_TURBO: | |
230 | case PNV9_OCC_OPAL_RUNTIME_DATA: | |
231 | return 1; | |
232 | case PNV9_OCC_PSTATE_MIN: | |
233 | case PNV9_OCC_PSTATE_ID_TWO: | |
234 | return 2; | |
235 | ||
236 | /* 3000 khz frequency for 0, 1, and 2 pstates */ | |
237 | case PNV9_OCC_PSTATE_ZERO_FREQUENCY: | |
238 | case PNV9_OCC_PSTATE_ONE_FREQUENCY: | |
239 | case PNV9_OCC_PSTATE_TWO_FREQUENCY: | |
240 | return 3000; | |
241 | case PNV9_OCC_PSTATE_MAJOR_VERSION: | |
242 | return 0x90; | |
243 | case PNV9_CHIP_HOMER_BASE: | |
244 | case PNV9_OCC_PSTATE_DATA_AREA: | |
245 | case PNV9_CHIP_HOMER_IMAGE_POINTER: | |
246 | return 0x1000000000000000; | |
247 | } | |
248 | /* pstate table core max array */ | |
249 | if (core_max_array(homer, addr)) { | |
250 | return 1; | |
251 | } | |
252 | return 0; | |
253 | } | |
254 | ||
255 | static void pnv_power9_homer_write(void *opaque, hwaddr addr, | |
256 | uint64_t val, unsigned size) | |
257 | { | |
258 | /* callback function defined to homer write */ | |
259 | return; | |
260 | } | |
261 | ||
262 | static const MemoryRegionOps pnv_power9_homer_ops = { | |
263 | .read = pnv_power9_homer_read, | |
264 | .write = pnv_power9_homer_write, | |
265 | .valid.min_access_size = 1, | |
266 | .valid.max_access_size = 8, | |
267 | .impl.min_access_size = 1, | |
268 | .impl.max_access_size = 8, | |
269 | .endianness = DEVICE_BIG_ENDIAN, | |
270 | }; | |
271 | ||
8f092316 CLG |
272 | static uint64_t pnv_homer_power9_pba_read(void *opaque, hwaddr addr, |
273 | unsigned size) | |
274 | { | |
275 | PnvHomer *homer = PNV_HOMER(opaque); | |
276 | PnvChip *chip = homer->chip; | |
277 | uint32_t reg = addr >> 3; | |
278 | uint64_t val = 0; | |
279 | ||
280 | switch (reg) { | |
281 | case PBA_BAR0: | |
282 | val = PNV9_HOMER_BASE(chip); | |
283 | break; | |
284 | case PBA_BARMASK0: /* P9 homer region mask */ | |
285 | val = (PNV9_HOMER_SIZE - 1) & 0x300000; | |
286 | break; | |
287 | case PBA_BAR2: /* P9 occ common area */ | |
288 | val = PNV9_OCC_COMMON_AREA_BASE; | |
289 | break; | |
290 | case PBA_BARMASK2: /* P9 occ common area size */ | |
291 | val = (PNV9_OCC_COMMON_AREA_SIZE - 1) & 0x700000; | |
292 | break; | |
293 | default: | |
294 | qemu_log_mask(LOG_UNIMP, "PBA: read to unimplemented register: Ox%" | |
295 | HWADDR_PRIx "\n", addr >> 3); | |
296 | } | |
297 | return val; | |
298 | } | |
299 | ||
300 | static void pnv_homer_power9_pba_write(void *opaque, hwaddr addr, | |
301 | uint64_t val, unsigned size) | |
302 | { | |
303 | qemu_log_mask(LOG_UNIMP, "PBA: write to unimplemented register: Ox%" | |
304 | HWADDR_PRIx "\n", addr >> 3); | |
305 | } | |
306 | ||
307 | static const MemoryRegionOps pnv_homer_power9_pba_ops = { | |
308 | .read = pnv_homer_power9_pba_read, | |
309 | .write = pnv_homer_power9_pba_write, | |
310 | .valid.min_access_size = 8, | |
311 | .valid.max_access_size = 8, | |
312 | .impl.min_access_size = 8, | |
313 | .impl.max_access_size = 8, | |
314 | .endianness = DEVICE_BIG_ENDIAN, | |
315 | }; | |
316 | ||
3887d241 B |
317 | static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) |
318 | { | |
319 | PnvHomerClass *homer = PNV_HOMER_CLASS(klass); | |
320 | ||
8f092316 CLG |
321 | homer->pba_size = PNV9_XSCOM_PBA_SIZE; |
322 | homer->pba_ops = &pnv_homer_power9_pba_ops; | |
3887d241 B |
323 | homer->homer_size = PNV9_HOMER_SIZE; |
324 | homer->homer_ops = &pnv_power9_homer_ops; | |
325 | homer->core_max_base = PNV9_CORE_MAX_BASE; | |
326 | } | |
327 | ||
328 | static const TypeInfo pnv_homer_power9_type_info = { | |
329 | .name = TYPE_PNV9_HOMER, | |
330 | .parent = TYPE_PNV_HOMER, | |
331 | .instance_size = sizeof(PnvHomer), | |
332 | .class_init = pnv_homer_power9_class_init, | |
333 | }; | |
334 | ||
335 | static void pnv_homer_realize(DeviceState *dev, Error **errp) | |
336 | { | |
337 | PnvHomer *homer = PNV_HOMER(dev); | |
338 | PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); | |
f2582acf GK |
339 | |
340 | assert(homer->chip); | |
341 | ||
8f092316 CLG |
342 | pnv_xscom_region_init(&homer->pba_regs, OBJECT(dev), hmrc->pba_ops, |
343 | homer, "xscom-pba", hmrc->pba_size); | |
344 | ||
3887d241 B |
345 | /* homer region */ |
346 | memory_region_init_io(&homer->regs, OBJECT(dev), | |
347 | hmrc->homer_ops, homer, "homer-main-memory", | |
348 | hmrc->homer_size); | |
349 | } | |
350 | ||
f2582acf GK |
351 | static Property pnv_homer_properties[] = { |
352 | DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), | |
353 | DEFINE_PROP_END_OF_LIST(), | |
354 | }; | |
355 | ||
3887d241 B |
356 | static void pnv_homer_class_init(ObjectClass *klass, void *data) |
357 | { | |
358 | DeviceClass *dc = DEVICE_CLASS(klass); | |
359 | ||
360 | dc->realize = pnv_homer_realize; | |
361 | dc->desc = "PowerNV HOMER Memory"; | |
4f67d30b | 362 | device_class_set_props(dc, pnv_homer_properties); |
23a782eb | 363 | dc->user_creatable = false; |
3887d241 B |
364 | } |
365 | ||
366 | static const TypeInfo pnv_homer_type_info = { | |
367 | .name = TYPE_PNV_HOMER, | |
368 | .parent = TYPE_DEVICE, | |
369 | .instance_size = sizeof(PnvHomer), | |
370 | .class_init = pnv_homer_class_init, | |
371 | .class_size = sizeof(PnvHomerClass), | |
372 | .abstract = true, | |
373 | }; | |
374 | ||
375 | static void pnv_homer_register_types(void) | |
376 | { | |
377 | type_register_static(&pnv_homer_type_info); | |
378 | type_register_static(&pnv_homer_power8_type_info); | |
379 | type_register_static(&pnv_homer_power9_type_info); | |
380 | } | |
381 | ||
382 | type_init(pnv_homer_register_types); |