| 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, see <http://www.gnu.org/licenses/>. |
| 13 | * |
| 14 | * Copyright IBM Corp. 2008 |
| 15 | * |
| 16 | * Authors: Hollis Blanchard <hollisb@us.ibm.com> |
| 17 | */ |
| 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 | |
| 24 | #include "qemu/osdep.h" |
| 25 | #include "qemu/log.h" |
| 26 | #include "hw/core/irq.h" |
| 27 | #include "hw/pci-host/ppc4xx.h" |
| 28 | #include "migration/vmstate.h" |
| 29 | #include "qemu/module.h" |
| 30 | #include "system/reset.h" |
| 31 | #include "hw/pci/pci_device.h" |
| 32 | #include "hw/pci/pci_host.h" |
| 33 | #include "trace.h" |
| 34 | #include "qom/object.h" |
| 35 | |
| 36 | struct PCIMasterMap { |
| 37 | uint32_t la; |
| 38 | uint32_t ma; |
| 39 | uint32_t pcila; |
| 40 | uint32_t pciha; |
| 41 | }; |
| 42 | |
| 43 | struct PCITargetMap { |
| 44 | uint32_t ms; |
| 45 | uint32_t la; |
| 46 | }; |
| 47 | |
| 48 | OBJECT_DECLARE_SIMPLE_TYPE(PPC4xxPCIState, PPC4xx_PCI_HOST) |
| 49 | |
| 50 | #define PPC4xx_PCI_NR_PMMS 3 |
| 51 | #define PPC4xx_PCI_NR_PTMS 2 |
| 52 | |
| 53 | #define PPC4xx_PCI_NUM_DEVS 5 |
| 54 | |
| 55 | struct PPC4xxPCIState { |
| 56 | PCIHostState parent_obj; |
| 57 | |
| 58 | struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS]; |
| 59 | struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS]; |
| 60 | qemu_irq irq[PPC4xx_PCI_NUM_DEVS]; |
| 61 | |
| 62 | MemoryRegion container; |
| 63 | MemoryRegion iomem; |
| 64 | }; |
| 65 | |
| 66 | #define PCIC0_CFGADDR 0x0 |
| 67 | #define PCIC0_CFGDATA 0x4 |
| 68 | |
| 69 | /* |
| 70 | * PLB Memory Map (PMM) registers specify which PLB addresses are translated to |
| 71 | * PCI accesses. |
| 72 | */ |
| 73 | #define PCIL0_PMM0LA 0x0 |
| 74 | #define PCIL0_PMM0MA 0x4 |
| 75 | #define PCIL0_PMM0PCILA 0x8 |
| 76 | #define PCIL0_PMM0PCIHA 0xc |
| 77 | #define PCIL0_PMM1LA 0x10 |
| 78 | #define PCIL0_PMM1MA 0x14 |
| 79 | #define PCIL0_PMM1PCILA 0x18 |
| 80 | #define PCIL0_PMM1PCIHA 0x1c |
| 81 | #define PCIL0_PMM2LA 0x20 |
| 82 | #define PCIL0_PMM2MA 0x24 |
| 83 | #define PCIL0_PMM2PCILA 0x28 |
| 84 | #define PCIL0_PMM2PCIHA 0x2c |
| 85 | |
| 86 | /* |
| 87 | * PCI Target Map (PTM) registers specify which PCI addresses are translated to |
| 88 | * PLB accesses. |
| 89 | */ |
| 90 | #define PCIL0_PTM1MS 0x30 |
| 91 | #define PCIL0_PTM1LA 0x34 |
| 92 | #define PCIL0_PTM2MS 0x38 |
| 93 | #define PCIL0_PTM2LA 0x3c |
| 94 | #define PCI_REG_BASE 0x800000 |
| 95 | #define PCI_REG_SIZE 0x40 |
| 96 | |
| 97 | #define PCI_ALL_SIZE (PCI_REG_BASE + PCI_REG_SIZE) |
| 98 | |
| 99 | static void ppc4xx_pci_reg_write4(void *opaque, hwaddr offset, |
| 100 | uint64_t value, unsigned size) |
| 101 | { |
| 102 | struct PPC4xxPCIState *pci = opaque; |
| 103 | |
| 104 | /* |
| 105 | * We ignore all target attempts at PCI configuration, effectively |
| 106 | * assuming a bidirectional 1:1 mapping of PLB and PCI space. |
| 107 | */ |
| 108 | switch (offset) { |
| 109 | case PCIL0_PMM0LA: |
| 110 | pci->pmm[0].la = value; |
| 111 | break; |
| 112 | case PCIL0_PMM0MA: |
| 113 | pci->pmm[0].ma = value; |
| 114 | break; |
| 115 | case PCIL0_PMM0PCIHA: |
| 116 | pci->pmm[0].pciha = value; |
| 117 | break; |
| 118 | case PCIL0_PMM0PCILA: |
| 119 | pci->pmm[0].pcila = value; |
| 120 | break; |
| 121 | |
| 122 | case PCIL0_PMM1LA: |
| 123 | pci->pmm[1].la = value; |
| 124 | break; |
| 125 | case PCIL0_PMM1MA: |
| 126 | pci->pmm[1].ma = value; |
| 127 | break; |
| 128 | case PCIL0_PMM1PCIHA: |
| 129 | pci->pmm[1].pciha = value; |
| 130 | break; |
| 131 | case PCIL0_PMM1PCILA: |
| 132 | pci->pmm[1].pcila = value; |
| 133 | break; |
| 134 | |
| 135 | case PCIL0_PMM2LA: |
| 136 | pci->pmm[2].la = value; |
| 137 | break; |
| 138 | case PCIL0_PMM2MA: |
| 139 | pci->pmm[2].ma = value; |
| 140 | break; |
| 141 | case PCIL0_PMM2PCIHA: |
| 142 | pci->pmm[2].pciha = value; |
| 143 | break; |
| 144 | case PCIL0_PMM2PCILA: |
| 145 | pci->pmm[2].pcila = value; |
| 146 | break; |
| 147 | |
| 148 | case PCIL0_PTM1MS: |
| 149 | pci->ptm[0].ms = value; |
| 150 | break; |
| 151 | case PCIL0_PTM1LA: |
| 152 | pci->ptm[0].la = value; |
| 153 | break; |
| 154 | case PCIL0_PTM2MS: |
| 155 | pci->ptm[1].ms = value; |
| 156 | break; |
| 157 | case PCIL0_PTM2LA: |
| 158 | pci->ptm[1].la = value; |
| 159 | break; |
| 160 | |
| 161 | default: |
| 162 | qemu_log_mask(LOG_GUEST_ERROR, |
| 163 | "%s: unhandled PCI internal register 0x%" HWADDR_PRIx "\n", |
| 164 | __func__, offset); |
| 165 | break; |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | static uint64_t ppc4xx_pci_reg_read4(void *opaque, hwaddr offset, |
| 170 | unsigned size) |
| 171 | { |
| 172 | struct PPC4xxPCIState *pci = opaque; |
| 173 | uint32_t value; |
| 174 | |
| 175 | switch (offset) { |
| 176 | case PCIL0_PMM0LA: |
| 177 | value = pci->pmm[0].la; |
| 178 | break; |
| 179 | case PCIL0_PMM0MA: |
| 180 | value = pci->pmm[0].ma; |
| 181 | break; |
| 182 | case PCIL0_PMM0PCIHA: |
| 183 | value = pci->pmm[0].pciha; |
| 184 | break; |
| 185 | case PCIL0_PMM0PCILA: |
| 186 | value = pci->pmm[0].pcila; |
| 187 | break; |
| 188 | |
| 189 | case PCIL0_PMM1LA: |
| 190 | value = pci->pmm[1].la; |
| 191 | break; |
| 192 | case PCIL0_PMM1MA: |
| 193 | value = pci->pmm[1].ma; |
| 194 | break; |
| 195 | case PCIL0_PMM1PCIHA: |
| 196 | value = pci->pmm[1].pciha; |
| 197 | break; |
| 198 | case PCIL0_PMM1PCILA: |
| 199 | value = pci->pmm[1].pcila; |
| 200 | break; |
| 201 | |
| 202 | case PCIL0_PMM2LA: |
| 203 | value = pci->pmm[2].la; |
| 204 | break; |
| 205 | case PCIL0_PMM2MA: |
| 206 | value = pci->pmm[2].ma; |
| 207 | break; |
| 208 | case PCIL0_PMM2PCIHA: |
| 209 | value = pci->pmm[2].pciha; |
| 210 | break; |
| 211 | case PCIL0_PMM2PCILA: |
| 212 | value = pci->pmm[2].pcila; |
| 213 | break; |
| 214 | |
| 215 | case PCIL0_PTM1MS: |
| 216 | value = pci->ptm[0].ms; |
| 217 | break; |
| 218 | case PCIL0_PTM1LA: |
| 219 | value = pci->ptm[0].la; |
| 220 | break; |
| 221 | case PCIL0_PTM2MS: |
| 222 | value = pci->ptm[1].ms; |
| 223 | break; |
| 224 | case PCIL0_PTM2LA: |
| 225 | value = pci->ptm[1].la; |
| 226 | break; |
| 227 | |
| 228 | default: |
| 229 | qemu_log_mask(LOG_GUEST_ERROR, |
| 230 | "%s: invalid PCI internal register 0x%" HWADDR_PRIx "\n", |
| 231 | __func__, offset); |
| 232 | value = 0; |
| 233 | } |
| 234 | |
| 235 | return value; |
| 236 | } |
| 237 | |
| 238 | static const MemoryRegionOps pci_reg_ops = { |
| 239 | .read = ppc4xx_pci_reg_read4, |
| 240 | .write = ppc4xx_pci_reg_write4, |
| 241 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 242 | }; |
| 243 | |
| 244 | static void ppc4xx_pci_reset(void *opaque) |
| 245 | { |
| 246 | struct PPC4xxPCIState *pci = opaque; |
| 247 | |
| 248 | memset(pci->pmm, 0, sizeof(pci->pmm)); |
| 249 | memset(pci->ptm, 0, sizeof(pci->ptm)); |
| 250 | } |
| 251 | |
| 252 | /* |
| 253 | * On Bamboo, all pins from each slot are tied to a single board IRQ. |
| 254 | * This may need further refactoring for other boards. |
| 255 | */ |
| 256 | static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num) |
| 257 | { |
| 258 | int slot = PCI_SLOT(pci_dev->devfn); |
| 259 | |
| 260 | trace_ppc4xx_pci_map_irq(pci_dev->devfn, irq_num, slot); |
| 261 | |
| 262 | return slot > 0 ? slot - 1 : PPC4xx_PCI_NUM_DEVS - 1; |
| 263 | } |
| 264 | |
| 265 | static void ppc4xx_pci_set_irq(void *opaque, int irq_num, int level) |
| 266 | { |
| 267 | qemu_irq *pci_irqs = opaque; |
| 268 | |
| 269 | trace_ppc4xx_pci_set_irq(irq_num); |
| 270 | assert(irq_num >= 0 && irq_num < PPC4xx_PCI_NUM_DEVS); |
| 271 | qemu_set_irq(pci_irqs[irq_num], level); |
| 272 | } |
| 273 | |
| 274 | static const VMStateDescription vmstate_pci_master_map = { |
| 275 | .name = "pci_master_map", |
| 276 | .version_id = 0, |
| 277 | .minimum_version_id = 0, |
| 278 | .fields = (const VMStateField[]) { |
| 279 | VMSTATE_UINT32(la, struct PCIMasterMap), |
| 280 | VMSTATE_UINT32(ma, struct PCIMasterMap), |
| 281 | VMSTATE_UINT32(pcila, struct PCIMasterMap), |
| 282 | VMSTATE_UINT32(pciha, struct PCIMasterMap), |
| 283 | VMSTATE_END_OF_LIST() |
| 284 | } |
| 285 | }; |
| 286 | |
| 287 | static const VMStateDescription vmstate_pci_target_map = { |
| 288 | .name = "pci_target_map", |
| 289 | .version_id = 0, |
| 290 | .minimum_version_id = 0, |
| 291 | .fields = (const VMStateField[]) { |
| 292 | VMSTATE_UINT32(ms, struct PCITargetMap), |
| 293 | VMSTATE_UINT32(la, struct PCITargetMap), |
| 294 | VMSTATE_END_OF_LIST() |
| 295 | } |
| 296 | }; |
| 297 | |
| 298 | static const VMStateDescription vmstate_ppc4xx_pci = { |
| 299 | .name = "ppc4xx_pci", |
| 300 | .version_id = 1, |
| 301 | .minimum_version_id = 1, |
| 302 | .fields = (const VMStateField[]) { |
| 303 | VMSTATE_STRUCT_ARRAY(pmm, PPC4xxPCIState, PPC4xx_PCI_NR_PMMS, 1, |
| 304 | vmstate_pci_master_map, |
| 305 | struct PCIMasterMap), |
| 306 | VMSTATE_STRUCT_ARRAY(ptm, PPC4xxPCIState, PPC4xx_PCI_NR_PTMS, 1, |
| 307 | vmstate_pci_target_map, |
| 308 | struct PCITargetMap), |
| 309 | VMSTATE_END_OF_LIST() |
| 310 | } |
| 311 | }; |
| 312 | |
| 313 | /* XXX Interrupt acknowledge cycles not supported. */ |
| 314 | static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) |
| 315 | { |
| 316 | SysBusDevice *sbd = SYS_BUS_DEVICE(dev); |
| 317 | PPC4xxPCIState *s; |
| 318 | PCIHostState *h; |
| 319 | PCIBus *b; |
| 320 | int i; |
| 321 | |
| 322 | h = PCI_HOST_BRIDGE(dev); |
| 323 | s = PPC4xx_PCI_HOST(dev); |
| 324 | |
| 325 | for (i = 0; i < ARRAY_SIZE(s->irq); i++) { |
| 326 | sysbus_init_irq(sbd, &s->irq[i]); |
| 327 | } |
| 328 | |
| 329 | b = pci_register_root_bus(dev, NULL, ppc4xx_pci_set_irq, |
| 330 | ppc4xx_pci_map_irq, s->irq, get_system_memory(), |
| 331 | get_system_io(), 0, ARRAY_SIZE(s->irq), |
| 332 | TYPE_PCI_BUS); |
| 333 | h->bus = b; |
| 334 | |
| 335 | pci_create_simple(b, 0, TYPE_PPC4xx_HOST_BRIDGE); |
| 336 | |
| 337 | /* XXX split into 2 memory regions, one for config space, one for regs */ |
| 338 | memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE); |
| 339 | memory_region_init_io(&h->conf_mem, OBJECT(s), &pci_host_conf_le_ops, h, |
| 340 | "pci-conf-idx", 4); |
| 341 | memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops, h, |
| 342 | "pci-conf-data", 4); |
| 343 | memory_region_init_io(&s->iomem, OBJECT(s), &pci_reg_ops, s, |
| 344 | "pci.reg", PCI_REG_SIZE); |
| 345 | memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); |
| 346 | memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); |
| 347 | memory_region_add_subregion(&s->container, PCI_REG_BASE, &s->iomem); |
| 348 | sysbus_init_mmio(sbd, &s->container); |
| 349 | qemu_register_reset(ppc4xx_pci_reset, s); |
| 350 | } |
| 351 | |
| 352 | static void ppc4xx_host_bridge_class_init(ObjectClass *klass, const void *data) |
| 353 | { |
| 354 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| 355 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 356 | |
| 357 | dc->desc = "Host bridge"; |
| 358 | k->vendor_id = PCI_VENDOR_ID_IBM; |
| 359 | k->device_id = PCI_DEVICE_ID_IBM_440GX; |
| 360 | k->class_id = PCI_CLASS_BRIDGE_OTHER; |
| 361 | /* |
| 362 | * PCI-facing part of the host bridge, not usable without the |
| 363 | * host-facing part, which can't be device_add'ed, yet. |
| 364 | */ |
| 365 | dc->user_creatable = false; |
| 366 | } |
| 367 | |
| 368 | static const TypeInfo ppc4xx_host_bridge_info = { |
| 369 | .name = TYPE_PPC4xx_HOST_BRIDGE, |
| 370 | .parent = TYPE_PCI_DEVICE, |
| 371 | .instance_size = sizeof(PCIDevice), |
| 372 | .class_init = ppc4xx_host_bridge_class_init, |
| 373 | .interfaces = (const InterfaceInfo[]) { |
| 374 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
| 375 | { }, |
| 376 | }, |
| 377 | }; |
| 378 | |
| 379 | static void ppc4xx_pcihost_class_init(ObjectClass *klass, const void *data) |
| 380 | { |
| 381 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 382 | |
| 383 | dc->realize = ppc4xx_pcihost_realize; |
| 384 | dc->vmsd = &vmstate_ppc4xx_pci; |
| 385 | } |
| 386 | |
| 387 | static const TypeInfo ppc4xx_pcihost_info = { |
| 388 | .name = TYPE_PPC4xx_PCI_HOST, |
| 389 | .parent = TYPE_PCI_HOST_BRIDGE, |
| 390 | .instance_size = sizeof(PPC4xxPCIState), |
| 391 | .class_init = ppc4xx_pcihost_class_init, |
| 392 | }; |
| 393 | |
| 394 | static void ppc4xx_pci_register_types(void) |
| 395 | { |
| 396 | type_register_static(&ppc4xx_pcihost_info); |
| 397 | type_register_static(&ppc4xx_host_bridge_info); |
| 398 | } |
| 399 | |
| 400 | type_init(ppc4xx_pci_register_types) |