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,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o | ||
| 679 | # NewWorld PowerMac | 679 | # NewWorld PowerMac |
| 680 | OBJS+= unin_pci.o ppc_chrp.o | 680 | OBJS+= unin_pci.o ppc_chrp.o |
| 681 | # PowerPC 4xx boards | 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 | endif | 683 | endif |
| 684 | ifeq ($(TARGET_BASE_ARCH), mips) | 684 | ifeq ($(TARGET_BASE_ARCH), mips) |
| 685 | OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o | 685 | OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o |
hw/ppc4xx.h
| @@ -25,6 +25,8 @@ | @@ -25,6 +25,8 @@ | ||
| 25 | #if !defined(PPC_4XX_H) | 25 | #if !defined(PPC_4XX_H) |
| 26 | #define PPC_4XX_H | 26 | #define PPC_4XX_H |
| 27 | 27 | ||
| 28 | +#include "pci.h" | ||
| 29 | + | ||
| 28 | /* PowerPC 4xx core initialization */ | 30 | /* PowerPC 4xx core initialization */ |
| 29 | CPUState *ppc4xx_init (const char *cpu_model, | 31 | CPUState *ppc4xx_init (const char *cpu_model, |
| 30 | clk_setup_t *cpu_clk, clk_setup_t *tb_clk, | 32 | clk_setup_t *cpu_clk, clk_setup_t *tb_clk, |
| @@ -46,4 +48,10 @@ enum { | @@ -46,4 +48,10 @@ enum { | ||
| 46 | qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, | 48 | qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs, |
| 47 | uint32_t dcr_base, int has_ssr, int has_vr); | 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 | #endif /* !defined(PPC_4XX_H) */ | 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 | +} |