Commit 825bb581b447831b6bbc21981b1eb8ad27df119d
1 parent
e976c6a1
IBM PowerPC 4xx 32-bit PCI controller emulation
This PCI controller can be found on a number of 4xx SoCs, including the 440EP. Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com> Acked-by: Anthony Liguori <aliguori@us.ibm.com> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5862 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
3 changed files
with
428 additions
and
1 deletions
Makefile.target
... | ... | @@ -679,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o |
679 | 679 | # NewWorld PowerMac |
680 | 680 | OBJS+= unin_pci.o ppc_chrp.o |
681 | 681 | # PowerPC 4xx boards |
682 | -OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o | |
682 | +OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o | |
683 | 683 | endif |
684 | 684 | ifeq ($(TARGET_BASE_ARCH), mips) |
685 | 685 | OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o | ... | ... |
hw/ppc4xx.h
... | ... | @@ -25,6 +25,8 @@ |
25 | 25 | #if !defined(PPC_4XX_H) |
26 | 26 | #define PPC_4XX_H |
27 | 27 | |
28 | +#include "pci.h" | |
29 | + | |
28 | 30 | /* PowerPC 4xx core initialization */ |
29 | 31 | CPUState *ppc4xx_init (const char *cpu_model, |
30 | 32 | clk_setup_t *cpu_clk, clk_setup_t *tb_clk, |
... | ... | @@ -46,4 +48,10 @@ enum { |
46 | 48 | qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, |
47 | 49 | uint32_t dcr_base, int has_ssr, int has_vr); |
48 | 50 | |
51 | +PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], | |
52 | + target_phys_addr_t config_space, | |
53 | + target_phys_addr_t int_ack, | |
54 | + target_phys_addr_t special_cycle, | |
55 | + target_phys_addr_t registers); | |
56 | + | |
49 | 57 | #endif /* !defined(PPC_4XX_H) */ | ... | ... |
hw/ppc4xx_pci.c
0 → 100644
1 | +/* | |
2 | + * This program is free software; you can redistribute it and/or modify | |
3 | + * it under the terms of the GNU General Public License, version 2, as | |
4 | + * published by the Free Software Foundation. | |
5 | + * | |
6 | + * This program is distributed in the hope that it will be useful, | |
7 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
8 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
9 | + * GNU General Public License for more details. | |
10 | + * | |
11 | + * You should have received a copy of the GNU General Public License | |
12 | + * along with this program; if not, write to the Free Software | |
13 | + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
14 | + * | |
15 | + * Copyright IBM Corp. 2008 | |
16 | + * | |
17 | + * Authors: Hollis Blanchard <hollisb@us.ibm.com> | |
18 | + */ | |
19 | + | |
20 | +/* This file implements emulation of the 32-bit PCI controller found in some | |
21 | + * 4xx SoCs, such as the 440EP. */ | |
22 | + | |
23 | +#include "hw.h" | |
24 | + | |
25 | +typedef target_phys_addr_t pci_addr_t; | |
26 | +#include "pci.h" | |
27 | +#include "pci_host.h" | |
28 | +#include "bswap.h" | |
29 | + | |
30 | +#undef DEBUG | |
31 | +#ifdef DEBUG | |
32 | +#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0) | |
33 | +#else | |
34 | +#define DPRINTF(fmt, args...) | |
35 | +#endif /* DEBUG */ | |
36 | + | |
37 | +struct PCIMasterMap { | |
38 | + uint32_t la; | |
39 | + uint32_t ma; | |
40 | + uint32_t pcila; | |
41 | + uint32_t pciha; | |
42 | +}; | |
43 | + | |
44 | +struct PCITargetMap { | |
45 | + uint32_t ms; | |
46 | + uint32_t la; | |
47 | +}; | |
48 | + | |
49 | +#define PPC4xx_PCI_NR_PMMS 3 | |
50 | +#define PPC4xx_PCI_NR_PTMS 2 | |
51 | + | |
52 | +struct PPC4xxPCIState { | |
53 | + struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; | |
54 | + struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; | |
55 | + | |
56 | + PCIHostState pci_state; | |
57 | + PCIDevice *pci_dev; | |
58 | +}; | |
59 | +typedef struct PPC4xxPCIState PPC4xxPCIState; | |
60 | + | |
61 | +#define PCIC0_CFGADDR 0x0 | |
62 | +#define PCIC0_CFGDATA 0x4 | |
63 | + | |
64 | +/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to | |
65 | + * PCI accesses. */ | |
66 | +#define PCIL0_PMM0LA 0x0 | |
67 | +#define PCIL0_PMM0MA 0x4 | |
68 | +#define PCIL0_PMM0PCILA 0x8 | |
69 | +#define PCIL0_PMM0PCIHA 0xc | |
70 | +#define PCIL0_PMM1LA 0x10 | |
71 | +#define PCIL0_PMM1MA 0x14 | |
72 | +#define PCIL0_PMM1PCILA 0x18 | |
73 | +#define PCIL0_PMM1PCIHA 0x1c | |
74 | +#define PCIL0_PMM2LA 0x20 | |
75 | +#define PCIL0_PMM2MA 0x24 | |
76 | +#define PCIL0_PMM2PCILA 0x28 | |
77 | +#define PCIL0_PMM2PCIHA 0x2c | |
78 | + | |
79 | +/* PCI Target Map (PTM) registers specify which PCI addresses are translated to | |
80 | + * PLB accesses. */ | |
81 | +#define PCIL0_PTM1MS 0x30 | |
82 | +#define PCIL0_PTM1LA 0x34 | |
83 | +#define PCIL0_PTM2MS 0x38 | |
84 | +#define PCIL0_PTM2LA 0x3c | |
85 | +#define PCI_REG_SIZE 0x40 | |
86 | + | |
87 | + | |
88 | +static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr) | |
89 | +{ | |
90 | + PPC4xxPCIState *ppc4xx_pci = opaque; | |
91 | + | |
92 | + return ppc4xx_pci->pci_state.config_reg; | |
93 | +} | |
94 | + | |
95 | +static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = { | |
96 | + &pci4xx_cfgaddr_readl, | |
97 | + &pci4xx_cfgaddr_readl, | |
98 | + &pci4xx_cfgaddr_readl, | |
99 | +}; | |
100 | + | |
101 | +static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr, | |
102 | + uint32_t value) | |
103 | +{ | |
104 | + PPC4xxPCIState *ppc4xx_pci = opaque; | |
105 | + | |
106 | +#ifdef TARGET_WORDS_BIGENDIAN | |
107 | + value = bswap32(value); | |
108 | +#endif | |
109 | + | |
110 | + ppc4xx_pci->pci_state.config_reg = value & ~0x3; | |
111 | +} | |
112 | + | |
113 | +static CPUWriteMemoryFunc *pci4xx_cfgaddr_write[] = { | |
114 | + &pci4xx_cfgaddr_writel, | |
115 | + &pci4xx_cfgaddr_writel, | |
116 | + &pci4xx_cfgaddr_writel, | |
117 | +}; | |
118 | + | |
119 | +static CPUReadMemoryFunc *pci4xx_cfgdata_read[] = { | |
120 | + &pci_host_data_readb, | |
121 | + &pci_host_data_readw, | |
122 | + &pci_host_data_readl, | |
123 | +}; | |
124 | + | |
125 | +static CPUWriteMemoryFunc *pci4xx_cfgdata_write[] = { | |
126 | + &pci_host_data_writeb, | |
127 | + &pci_host_data_writew, | |
128 | + &pci_host_data_writel, | |
129 | +}; | |
130 | + | |
131 | +static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t offset, | |
132 | + uint32_t value) | |
133 | +{ | |
134 | + struct PPC4xxPCIState *pci = opaque; | |
135 | + | |
136 | +#ifdef TARGET_WORDS_BIGENDIAN | |
137 | + value = bswap32(value); | |
138 | +#endif | |
139 | + | |
140 | + /* We ignore all target attempts at PCI configuration, effectively | |
141 | + * assuming a bidirectional 1:1 mapping of PLB and PCI space. */ | |
142 | + | |
143 | + switch (offset) { | |
144 | + case PCIL0_PMM0LA: | |
145 | + pci->pmm[0].la = value; | |
146 | + break; | |
147 | + case PCIL0_PMM0MA: | |
148 | + pci->pmm[0].ma = value; | |
149 | + break; | |
150 | + case PCIL0_PMM0PCIHA: | |
151 | + pci->pmm[0].pciha = value; | |
152 | + break; | |
153 | + case PCIL0_PMM0PCILA: | |
154 | + pci->pmm[0].pcila = value; | |
155 | + break; | |
156 | + | |
157 | + case PCIL0_PMM1LA: | |
158 | + pci->pmm[1].la = value; | |
159 | + break; | |
160 | + case PCIL0_PMM1MA: | |
161 | + pci->pmm[1].ma = value; | |
162 | + break; | |
163 | + case PCIL0_PMM1PCIHA: | |
164 | + pci->pmm[1].pciha = value; | |
165 | + break; | |
166 | + case PCIL0_PMM1PCILA: | |
167 | + pci->pmm[1].pcila = value; | |
168 | + break; | |
169 | + | |
170 | + case PCIL0_PMM2LA: | |
171 | + pci->pmm[2].la = value; | |
172 | + break; | |
173 | + case PCIL0_PMM2MA: | |
174 | + pci->pmm[2].ma = value; | |
175 | + break; | |
176 | + case PCIL0_PMM2PCIHA: | |
177 | + pci->pmm[2].pciha = value; | |
178 | + break; | |
179 | + case PCIL0_PMM2PCILA: | |
180 | + pci->pmm[2].pcila = value; | |
181 | + break; | |
182 | + | |
183 | + case PCIL0_PTM1MS: | |
184 | + pci->ptm[0].ms = value; | |
185 | + break; | |
186 | + case PCIL0_PTM1LA: | |
187 | + pci->ptm[0].la = value; | |
188 | + break; | |
189 | + case PCIL0_PTM2MS: | |
190 | + pci->ptm[1].ms = value; | |
191 | + break; | |
192 | + case PCIL0_PTM2LA: | |
193 | + pci->ptm[1].la = value; | |
194 | + break; | |
195 | + | |
196 | + default: | |
197 | + printf("%s: unhandled PCI internal register 0x%lx\n", __func__, | |
198 | + (unsigned long)offset); | |
199 | + break; | |
200 | + } | |
201 | +} | |
202 | + | |
203 | +static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t offset) | |
204 | +{ | |
205 | + struct PPC4xxPCIState *pci = opaque; | |
206 | + uint32_t value; | |
207 | + | |
208 | + switch (offset) { | |
209 | + case PCIL0_PMM0LA: | |
210 | + value = pci->pmm[0].la; | |
211 | + break; | |
212 | + case PCIL0_PMM0MA: | |
213 | + value = pci->pmm[0].ma; | |
214 | + break; | |
215 | + case PCIL0_PMM0PCIHA: | |
216 | + value = pci->pmm[0].pciha; | |
217 | + break; | |
218 | + case PCIL0_PMM0PCILA: | |
219 | + value = pci->pmm[0].pcila; | |
220 | + break; | |
221 | + | |
222 | + case PCIL0_PMM1LA: | |
223 | + value = pci->pmm[1].la; | |
224 | + break; | |
225 | + case PCIL0_PMM1MA: | |
226 | + value = pci->pmm[1].ma; | |
227 | + break; | |
228 | + case PCIL0_PMM1PCIHA: | |
229 | + value = pci->pmm[1].pciha; | |
230 | + break; | |
231 | + case PCIL0_PMM1PCILA: | |
232 | + value = pci->pmm[1].pcila; | |
233 | + break; | |
234 | + | |
235 | + case PCIL0_PMM2LA: | |
236 | + value = pci->pmm[2].la; | |
237 | + break; | |
238 | + case PCIL0_PMM2MA: | |
239 | + value = pci->pmm[2].ma; | |
240 | + break; | |
241 | + case PCIL0_PMM2PCIHA: | |
242 | + value = pci->pmm[2].pciha; | |
243 | + break; | |
244 | + case PCIL0_PMM2PCILA: | |
245 | + value = pci->pmm[2].pcila; | |
246 | + break; | |
247 | + | |
248 | + case PCIL0_PTM1MS: | |
249 | + value = pci->ptm[0].ms; | |
250 | + break; | |
251 | + case PCIL0_PTM1LA: | |
252 | + value = pci->ptm[0].la; | |
253 | + break; | |
254 | + case PCIL0_PTM2MS: | |
255 | + value = pci->ptm[1].ms; | |
256 | + break; | |
257 | + case PCIL0_PTM2LA: | |
258 | + value = pci->ptm[1].la; | |
259 | + break; | |
260 | + | |
261 | + default: | |
262 | + printf("%s: invalid PCI internal register 0x%lx\n", __func__, | |
263 | + (unsigned long)offset); | |
264 | + value = 0; | |
265 | + } | |
266 | + | |
267 | +#ifdef TARGET_WORDS_BIGENDIAN | |
268 | + value = bswap32(value); | |
269 | +#endif | |
270 | + | |
271 | + return value; | |
272 | +} | |
273 | + | |
274 | +static CPUReadMemoryFunc *pci_reg_read[] = { | |
275 | + &ppc4xx_pci_reg_read4, | |
276 | + &ppc4xx_pci_reg_read4, | |
277 | + &ppc4xx_pci_reg_read4, | |
278 | +}; | |
279 | + | |
280 | +static CPUWriteMemoryFunc *pci_reg_write[] = { | |
281 | + &ppc4xx_pci_reg_write4, | |
282 | + &ppc4xx_pci_reg_write4, | |
283 | + &ppc4xx_pci_reg_write4, | |
284 | +}; | |
285 | + | |
286 | +static void ppc4xx_pci_reset(void *opaque) | |
287 | +{ | |
288 | + struct PPC4xxPCIState *pci = opaque; | |
289 | + | |
290 | + memset(pci->pmm, 0, sizeof(pci->pmm)); | |
291 | + memset(pci->ptm, 0, sizeof(pci->ptm)); | |
292 | +} | |
293 | + | |
294 | +/* On Bamboo, all pins from each slot are tied to a single board IRQ. This | |
295 | + * may need further refactoring for other boards. */ | |
296 | +static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) | |
297 | +{ | |
298 | + int slot = pci_dev->devfn >> 3; | |
299 | + | |
300 | + DPRINTF("%s: devfn %x irq %d -> %d\n", __func__, | |
301 | + pci_dev->devfn, irq_num, slot); | |
302 | + | |
303 | + return slot - 1; | |
304 | +} | |
305 | + | |
306 | +static void ppc4xx_pci_set_irq(qemu_irq *pci_irqs, int irq_num, int level) | |
307 | +{ | |
308 | + DPRINTF("%s: PCI irq %d\n", __func__, irq_num); | |
309 | + qemu_set_irq(pci_irqs[irq_num], level); | |
310 | +} | |
311 | + | |
312 | +static void ppc4xx_pci_save(QEMUFile *f, void *opaque) | |
313 | +{ | |
314 | + PPC4xxPCIState *controller = opaque; | |
315 | + int i; | |
316 | + | |
317 | + pci_device_save(&controller->pci_dev, f); | |
318 | + | |
319 | + for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) { | |
320 | + qemu_put_be32s(f, &controller->pmm[i].la); | |
321 | + qemu_put_be32s(f, &controller->pmm[i].ma); | |
322 | + qemu_put_be32s(f, &controller->pmm[i].pcila); | |
323 | + qemu_put_be32s(f, &controller->pmm[i].pciha); | |
324 | + } | |
325 | + | |
326 | + for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) { | |
327 | + qemu_put_be32s(f, &controller->ptm[i].ms); | |
328 | + qemu_put_be32s(f, &controller->ptm[i].la); | |
329 | + } | |
330 | +} | |
331 | + | |
332 | +static int ppc4xx_pci_load(QEMUFile *f, void *opaque, int version_id) | |
333 | +{ | |
334 | + PPC4xxPCIState *controller = opaque; | |
335 | + int i; | |
336 | + | |
337 | + if (version_id != 1) | |
338 | + return -EINVAL; | |
339 | + | |
340 | + pci_device_load(&controller->pci_dev, f); | |
341 | + | |
342 | + for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) { | |
343 | + qemu_get_be32s(f, &controller->pmm[i].la); | |
344 | + qemu_get_be32s(f, &controller->pmm[i].ma); | |
345 | + qemu_get_be32s(f, &controller->pmm[i].pcila); | |
346 | + qemu_get_be32s(f, &controller->pmm[i].pciha); | |
347 | + } | |
348 | + | |
349 | + for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) { | |
350 | + qemu_get_be32s(f, &controller->ptm[i].ms); | |
351 | + qemu_get_be32s(f, &controller->ptm[i].la); | |
352 | + } | |
353 | + | |
354 | + return 0; | |
355 | +} | |
356 | + | |
357 | +/* XXX Interrupt acknowledge cycles not supported. */ | |
358 | +PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4], | |
359 | + target_phys_addr_t config_space, | |
360 | + target_phys_addr_t int_ack, | |
361 | + target_phys_addr_t special_cycle, | |
362 | + target_phys_addr_t registers) | |
363 | +{ | |
364 | + PPC4xxPCIState *controller; | |
365 | + int index; | |
366 | + static int ppc4xx_pci_id; | |
367 | + | |
368 | + controller = qemu_mallocz(sizeof(PPC4xxPCIState)); | |
369 | + if (!controller) | |
370 | + return NULL; | |
371 | + | |
372 | + controller->pci_state.bus = pci_register_bus(ppc4xx_pci_set_irq, | |
373 | + ppc4xx_pci_map_irq, | |
374 | + pci_irqs, 0, 4); | |
375 | + | |
376 | + controller->pci_dev = pci_register_device(controller->pci_state.bus, | |
377 | + "host bridge", sizeof(PCIDevice), | |
378 | + 0, NULL, NULL); | |
379 | + controller->pci_dev->config[0x00] = 0x14; // vendor_id | |
380 | + controller->pci_dev->config[0x01] = 0x10; | |
381 | + controller->pci_dev->config[0x02] = 0x7f; // device_id | |
382 | + controller->pci_dev->config[0x03] = 0x02; | |
383 | + controller->pci_dev->config[0x0a] = 0x80; // class_sub = other bridge type | |
384 | + controller->pci_dev->config[0x0b] = 0x06; // class_base = PCI_bridge | |
385 | + | |
386 | + /* CFGADDR */ | |
387 | + index = cpu_register_io_memory(0, pci4xx_cfgaddr_read, | |
388 | + pci4xx_cfgaddr_write, controller); | |
389 | + if (index < 0) | |
390 | + goto free; | |
391 | + cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index); | |
392 | + | |
393 | + /* CFGDATA */ | |
394 | + index = cpu_register_io_memory(0, pci4xx_cfgdata_read, | |
395 | + pci4xx_cfgdata_write, | |
396 | + &controller->pci_state); | |
397 | + if (index < 0) | |
398 | + goto free; | |
399 | + cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index); | |
400 | + | |
401 | + /* Internal registers */ | |
402 | + index = cpu_register_io_memory(0, pci_reg_read, pci_reg_write, controller); | |
403 | + if (index < 0) | |
404 | + goto free; | |
405 | + cpu_register_physical_memory(registers, PCI_REG_SIZE, index); | |
406 | + | |
407 | + qemu_register_reset(ppc4xx_pci_reset, controller); | |
408 | + | |
409 | + /* XXX load/save code not tested. */ | |
410 | + register_savevm("ppc4xx_pci", ppc4xx_pci_id++, 1, | |
411 | + ppc4xx_pci_save, ppc4xx_pci_load, controller); | |
412 | + | |
413 | + return controller->pci_state.bus; | |
414 | + | |
415 | +free: | |
416 | + printf("%s error\n", __func__); | |
417 | + qemu_free(controller); | |
418 | + return NULL; | |
419 | +} | ... | ... |