| 1 | /* |
| 2 | * QEMU Ultrasparc Sabre PCI host (PBM) |
| 3 | * |
| 4 | * Copyright (c) 2006 Fabrice Bellard |
| 5 | * Copyright (c) 2012,2013 Artyom Tarasenko |
| 6 | * Copyright (c) 2018 Mark Cave-Ayland |
| 7 | * |
| 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 9 | * of this software and associated documentation files (the "Software"), to deal |
| 10 | * in the Software without restriction, including without limitation the rights |
| 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 12 | * copies of the Software, and to permit persons to whom the Software is |
| 13 | * furnished to do so, subject to the following conditions: |
| 14 | * |
| 15 | * The above copyright notice and this permission notice shall be included in |
| 16 | * all copies or substantial portions of the Software. |
| 17 | * |
| 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 21 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 24 | * THE SOFTWARE. |
| 25 | */ |
| 26 | |
| 27 | #include "qemu/osdep.h" |
| 28 | #include "hw/core/sysbus.h" |
| 29 | #include "hw/pci/pci.h" |
| 30 | #include "hw/pci/pci_host.h" |
| 31 | #include "hw/core/qdev-properties.h" |
| 32 | #include "hw/pci/pci_bridge.h" |
| 33 | #include "hw/pci/pci_bus.h" |
| 34 | #include "hw/core/irq.h" |
| 35 | #include "hw/pci-bridge/simba.h" |
| 36 | #include "hw/pci-host/sabre.h" |
| 37 | #include "qapi/error.h" |
| 38 | #include "qemu/log.h" |
| 39 | #include "qemu/module.h" |
| 40 | #include "system/runstate.h" |
| 41 | #include "trace.h" |
| 42 | |
| 43 | /* |
| 44 | * Chipset docs: |
| 45 | * PBM: "UltraSPARC IIi User's Manual", |
| 46 | * https://web.archive.org/web/20030403110020/http://www.sun.com/processors/manuals/805-0087.pdf |
| 47 | */ |
| 48 | |
| 49 | #define PBM_PCI_IMR_MASK 0x7fffffff |
| 50 | #define PBM_PCI_IMR_ENABLED 0x80000000 |
| 51 | |
| 52 | #define POR (1U << 31) |
| 53 | #define SOFT_POR (1U << 30) |
| 54 | #define SOFT_XIR (1U << 29) |
| 55 | #define BTN_POR (1U << 28) |
| 56 | #define BTN_XIR (1U << 27) |
| 57 | #define RESET_MASK 0xf8000000 |
| 58 | #define RESET_WCMASK 0x98000000 |
| 59 | #define RESET_WMASK 0x60000000 |
| 60 | |
| 61 | #define NO_IRQ_REQUEST (MAX_IVEC + 1) |
| 62 | |
| 63 | static inline void sabre_set_request(SabreState *s, unsigned int irq_num) |
| 64 | { |
| 65 | trace_sabre_set_request(irq_num); |
| 66 | s->irq_request = irq_num; |
| 67 | qemu_set_irq(s->ivec_irqs[irq_num], 1); |
| 68 | } |
| 69 | |
| 70 | static inline void sabre_check_irqs(SabreState *s) |
| 71 | { |
| 72 | unsigned int i; |
| 73 | |
| 74 | /* Previous request is not acknowledged, resubmit */ |
| 75 | if (s->irq_request != NO_IRQ_REQUEST) { |
| 76 | sabre_set_request(s, s->irq_request); |
| 77 | return; |
| 78 | } |
| 79 | /* no request pending */ |
| 80 | if (s->pci_irq_in == 0ULL) { |
| 81 | return; |
| 82 | } |
| 83 | for (i = 0; i < 32; i++) { |
| 84 | if (s->pci_irq_in & (1ULL << i)) { |
| 85 | if (s->pci_irq_map[i >> 2] & PBM_PCI_IMR_ENABLED) { |
| 86 | sabre_set_request(s, i); |
| 87 | return; |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | for (i = 32; i < 64; i++) { |
| 92 | if (s->pci_irq_in & (1ULL << i)) { |
| 93 | if (s->obio_irq_map[i - 32] & PBM_PCI_IMR_ENABLED) { |
| 94 | sabre_set_request(s, i); |
| 95 | break; |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | static inline void sabre_clear_request(SabreState *s, unsigned int irq_num) |
| 102 | { |
| 103 | trace_sabre_clear_request(irq_num); |
| 104 | qemu_set_irq(s->ivec_irqs[irq_num], 0); |
| 105 | s->irq_request = NO_IRQ_REQUEST; |
| 106 | } |
| 107 | |
| 108 | static AddressSpace *sabre_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) |
| 109 | { |
| 110 | IOMMUState *is = opaque; |
| 111 | |
| 112 | return &is->iommu_as; |
| 113 | } |
| 114 | |
| 115 | static const PCIIOMMUOps sabre_iommu_ops = { |
| 116 | .get_address_space = sabre_pci_dma_iommu, |
| 117 | }; |
| 118 | |
| 119 | static void sabre_config_write(void *opaque, hwaddr addr, |
| 120 | uint64_t val, unsigned size) |
| 121 | { |
| 122 | SabreState *s = opaque; |
| 123 | |
| 124 | trace_sabre_config_write(addr, val); |
| 125 | |
| 126 | switch (addr) { |
| 127 | case 0x30 ... 0x4f: /* DMA error registers */ |
| 128 | /* XXX: not implemented yet */ |
| 129 | break; |
| 130 | case 0xc00 ... 0xc3f: /* PCI interrupt control */ |
| 131 | if (addr & 4) { |
| 132 | unsigned int ino = (addr & 0x3f) >> 3; |
| 133 | s->pci_irq_map[ino] &= PBM_PCI_IMR_MASK; |
| 134 | s->pci_irq_map[ino] |= val & ~PBM_PCI_IMR_MASK; |
| 135 | if ((s->irq_request == ino) && !(val & ~PBM_PCI_IMR_MASK)) { |
| 136 | sabre_clear_request(s, ino); |
| 137 | } |
| 138 | sabre_check_irqs(s); |
| 139 | } |
| 140 | break; |
| 141 | case 0x1000 ... 0x107f: /* OBIO interrupt control */ |
| 142 | if (addr & 4) { |
| 143 | unsigned int ino = ((addr & 0xff) >> 3); |
| 144 | s->obio_irq_map[ino] &= PBM_PCI_IMR_MASK; |
| 145 | s->obio_irq_map[ino] |= val & ~PBM_PCI_IMR_MASK; |
| 146 | if ((s->irq_request == (ino | 0x20)) |
| 147 | && !(val & ~PBM_PCI_IMR_MASK)) { |
| 148 | sabre_clear_request(s, ino | 0x20); |
| 149 | } |
| 150 | sabre_check_irqs(s); |
| 151 | } |
| 152 | break; |
| 153 | case 0x1400 ... 0x14ff: /* PCI interrupt clear */ |
| 154 | if (addr & 4) { |
| 155 | unsigned int ino = (addr & 0xff) >> 5; |
| 156 | if ((s->irq_request / 4) == ino) { |
| 157 | sabre_clear_request(s, s->irq_request); |
| 158 | sabre_check_irqs(s); |
| 159 | } |
| 160 | } |
| 161 | break; |
| 162 | case 0x1800 ... 0x1860: /* OBIO interrupt clear */ |
| 163 | if (addr & 4) { |
| 164 | unsigned int ino = ((addr & 0xff) >> 3) | 0x20; |
| 165 | if (s->irq_request == ino) { |
| 166 | sabre_clear_request(s, ino); |
| 167 | sabre_check_irqs(s); |
| 168 | } |
| 169 | } |
| 170 | break; |
| 171 | case 0x2000 ... 0x202f: /* PCI control */ |
| 172 | s->pci_control[(addr & 0x3f) >> 2] = val; |
| 173 | break; |
| 174 | case 0xf020 ... 0xf027: /* Reset control */ |
| 175 | if (addr & 4) { |
| 176 | val &= RESET_MASK; |
| 177 | s->reset_control &= ~(val & RESET_WCMASK); |
| 178 | s->reset_control |= val & RESET_WMASK; |
| 179 | if (val & SOFT_POR) { |
| 180 | s->nr_resets = 0; |
| 181 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 182 | } else if (val & SOFT_XIR) { |
| 183 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 184 | } |
| 185 | } |
| 186 | break; |
| 187 | case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ |
| 188 | case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ |
| 189 | case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ |
| 190 | case 0xf000 ... 0xf01f: /* FFB config, memory control */ |
| 191 | /* we don't care */ |
| 192 | default: |
| 193 | break; |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | static uint64_t sabre_config_read(void *opaque, |
| 198 | hwaddr addr, unsigned size) |
| 199 | { |
| 200 | SabreState *s = opaque; |
| 201 | uint32_t val = 0; |
| 202 | |
| 203 | switch (addr) { |
| 204 | case 0x30 ... 0x4f: /* DMA error registers */ |
| 205 | /* XXX: not implemented yet */ |
| 206 | break; |
| 207 | case 0xc00 ... 0xc3f: /* PCI interrupt control */ |
| 208 | if (addr & 4) { |
| 209 | val = s->pci_irq_map[(addr & 0x3f) >> 3]; |
| 210 | } |
| 211 | break; |
| 212 | case 0x1000 ... 0x107f: /* OBIO interrupt control */ |
| 213 | if (addr & 4) { |
| 214 | val = s->obio_irq_map[(addr & 0xff) >> 3]; |
| 215 | } |
| 216 | break; |
| 217 | case 0x1080 ... 0x108f: /* PCI bus error */ |
| 218 | if (addr & 4) { |
| 219 | val = s->pci_err_irq_map[(addr & 0xf) >> 3]; |
| 220 | } |
| 221 | break; |
| 222 | case 0x2000 ... 0x202f: /* PCI control */ |
| 223 | val = s->pci_control[(addr & 0x3f) >> 2]; |
| 224 | break; |
| 225 | case 0xf020 ... 0xf027: /* Reset control */ |
| 226 | if (addr & 4) { |
| 227 | val = s->reset_control; |
| 228 | } |
| 229 | break; |
| 230 | case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */ |
| 231 | case 0xa400 ... 0xa67f: /* IOMMU diagnostics */ |
| 232 | case 0xa800 ... 0xa80f: /* Interrupt diagnostics */ |
| 233 | case 0xf000 ... 0xf01f: /* FFB config, memory control */ |
| 234 | /* we don't care */ |
| 235 | default: |
| 236 | break; |
| 237 | } |
| 238 | trace_sabre_config_read(addr, val); |
| 239 | |
| 240 | return val; |
| 241 | } |
| 242 | |
| 243 | static const MemoryRegionOps sabre_config_ops = { |
| 244 | .read = sabre_config_read, |
| 245 | .write = sabre_config_write, |
| 246 | .endianness = DEVICE_BIG_ENDIAN, |
| 247 | }; |
| 248 | |
| 249 | static void sabre_pci_config_write(void *opaque, hwaddr addr, |
| 250 | uint64_t val, unsigned size) |
| 251 | { |
| 252 | SabreState *s = opaque; |
| 253 | PCIHostState *phb = PCI_HOST_BRIDGE(s); |
| 254 | |
| 255 | trace_sabre_pci_config_write(addr, val); |
| 256 | pci_data_write(phb->bus, addr, val, size); |
| 257 | } |
| 258 | |
| 259 | static uint64_t sabre_pci_config_read(void *opaque, hwaddr addr, |
| 260 | unsigned size) |
| 261 | { |
| 262 | uint32_t ret; |
| 263 | SabreState *s = opaque; |
| 264 | PCIHostState *phb = PCI_HOST_BRIDGE(s); |
| 265 | |
| 266 | ret = pci_data_read(phb->bus, addr, size); |
| 267 | trace_sabre_pci_config_read(addr, ret); |
| 268 | return ret; |
| 269 | } |
| 270 | |
| 271 | /* The sabre host has an IRQ line for each IRQ line of each slot. */ |
| 272 | static int pci_sabre_map_irq(PCIDevice *pci_dev, int irq_num) |
| 273 | { |
| 274 | /* Return the irq as swizzled by the PBM */ |
| 275 | return irq_num; |
| 276 | } |
| 277 | |
| 278 | static int pci_simbaA_map_irq(PCIDevice *pci_dev, int irq_num) |
| 279 | { |
| 280 | /* The on-board devices have fixed (legacy) OBIO intnos */ |
| 281 | switch (PCI_SLOT(pci_dev->devfn)) { |
| 282 | case 1: |
| 283 | /* Onboard NIC */ |
| 284 | return OBIO_NIC_IRQ; |
| 285 | case 3: |
| 286 | /* Onboard IDE */ |
| 287 | return OBIO_HDD_IRQ; |
| 288 | default: |
| 289 | /* Normal intno, fall through */ |
| 290 | break; |
| 291 | } |
| 292 | |
| 293 | return ((PCI_SLOT(pci_dev->devfn) << 2) + irq_num) & 0x1f; |
| 294 | } |
| 295 | |
| 296 | static int pci_simbaB_map_irq(PCIDevice *pci_dev, int irq_num) |
| 297 | { |
| 298 | return (0x10 + (PCI_SLOT(pci_dev->devfn) << 2) + irq_num) & 0x1f; |
| 299 | } |
| 300 | |
| 301 | static void pci_sabre_set_irq(void *opaque, int irq_num, int level) |
| 302 | { |
| 303 | SabreState *s = opaque; |
| 304 | |
| 305 | trace_sabre_pci_set_irq(irq_num, level); |
| 306 | |
| 307 | /* PCI IRQ map onto the first 32 INO. */ |
| 308 | if (irq_num < 32) { |
| 309 | if (level) { |
| 310 | s->pci_irq_in |= 1ULL << irq_num; |
| 311 | if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) { |
| 312 | sabre_set_request(s, irq_num); |
| 313 | } |
| 314 | } else { |
| 315 | s->pci_irq_in &= ~(1ULL << irq_num); |
| 316 | } |
| 317 | } else { |
| 318 | /* OBIO IRQ map onto the next 32 INO. */ |
| 319 | if (level) { |
| 320 | trace_sabre_pci_set_obio_irq(irq_num, level); |
| 321 | s->pci_irq_in |= 1ULL << irq_num; |
| 322 | if ((s->irq_request == NO_IRQ_REQUEST) |
| 323 | && (s->obio_irq_map[irq_num - 32] & PBM_PCI_IMR_ENABLED)) { |
| 324 | sabre_set_request(s, irq_num); |
| 325 | } |
| 326 | } else { |
| 327 | s->pci_irq_in &= ~(1ULL << irq_num); |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | static void sabre_reset(DeviceState *d) |
| 333 | { |
| 334 | SabreState *s = SABRE(d); |
| 335 | PCIDevice *pci_dev; |
| 336 | unsigned int i; |
| 337 | uint16_t cmd; |
| 338 | |
| 339 | for (i = 0; i < 8; i++) { |
| 340 | s->pci_irq_map[i] &= PBM_PCI_IMR_MASK; |
| 341 | } |
| 342 | for (i = 0; i < 32; i++) { |
| 343 | s->obio_irq_map[i] &= PBM_PCI_IMR_MASK; |
| 344 | } |
| 345 | |
| 346 | s->irq_request = NO_IRQ_REQUEST; |
| 347 | s->pci_irq_in = 0ULL; |
| 348 | |
| 349 | if (s->nr_resets++ == 0) { |
| 350 | /* Power on reset */ |
| 351 | s->reset_control = POR; |
| 352 | } |
| 353 | |
| 354 | /* As this is the busA PCI bridge which contains the on-board devices |
| 355 | * attached to the ebus, ensure that we initially allow IO transactions |
| 356 | * so that we get the early serial console until OpenBIOS can properly |
| 357 | * configure the PCI bridge itself */ |
| 358 | pci_dev = PCI_DEVICE(s->bridgeA); |
| 359 | cmd = pci_get_word(pci_dev->config + PCI_COMMAND); |
| 360 | pci_set_word(pci_dev->config + PCI_COMMAND, cmd | PCI_COMMAND_IO); |
| 361 | pci_bridge_update_mappings(PCI_BRIDGE(pci_dev)); |
| 362 | } |
| 363 | |
| 364 | static const MemoryRegionOps pci_config_ops = { |
| 365 | .read = sabre_pci_config_read, |
| 366 | .write = sabre_pci_config_write, |
| 367 | .endianness = DEVICE_LITTLE_ENDIAN, |
| 368 | }; |
| 369 | |
| 370 | static void sabre_realize(DeviceState *dev, Error **errp) |
| 371 | { |
| 372 | SabreState *s = SABRE(dev); |
| 373 | PCIHostState *phb = PCI_HOST_BRIDGE(dev); |
| 374 | PCIDevice *pci_dev; |
| 375 | |
| 376 | memory_region_init(&s->pci_mmio, OBJECT(s), "pci-mmio", 0x100000000ULL); |
| 377 | memory_region_add_subregion(get_system_memory(), s->mem_base, |
| 378 | &s->pci_mmio); |
| 379 | |
| 380 | phb->bus = pci_register_root_bus(dev, "pci", |
| 381 | pci_sabre_set_irq, pci_sabre_map_irq, s, |
| 382 | &s->pci_mmio, |
| 383 | &s->pci_ioport, |
| 384 | 0, 0x40, TYPE_PCI_BUS); |
| 385 | |
| 386 | pci_create_simple(phb->bus, 0, TYPE_SABRE_PCI_DEVICE); |
| 387 | |
| 388 | /* IOMMU */ |
| 389 | memory_region_add_subregion_overlap(&s->sabre_config, 0x200, |
| 390 | sysbus_mmio_get_region(SYS_BUS_DEVICE(s->iommu), 0), 1); |
| 391 | pci_setup_iommu(phb->bus, &sabre_iommu_ops, s->iommu); |
| 392 | |
| 393 | /* APB secondary busses */ |
| 394 | pci_dev = pci_new_multifunction(PCI_DEVFN(1, 0), TYPE_SIMBA_PCI_BRIDGE); |
| 395 | s->bridgeB = PCI_BRIDGE(pci_dev); |
| 396 | pci_bridge_map_irq(s->bridgeB, "pciB", pci_simbaB_map_irq); |
| 397 | pci_realize_and_unref(pci_dev, phb->bus, &error_fatal); |
| 398 | |
| 399 | pci_dev = pci_new_multifunction(PCI_DEVFN(1, 1), TYPE_SIMBA_PCI_BRIDGE); |
| 400 | s->bridgeA = PCI_BRIDGE(pci_dev); |
| 401 | pci_bridge_map_irq(s->bridgeA, "pciA", pci_simbaA_map_irq); |
| 402 | pci_realize_and_unref(pci_dev, phb->bus, &error_fatal); |
| 403 | } |
| 404 | |
| 405 | static void sabre_init(Object *obj) |
| 406 | { |
| 407 | SabreState *s = SABRE(obj); |
| 408 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
| 409 | unsigned int i; |
| 410 | |
| 411 | for (i = 0; i < 8; i++) { |
| 412 | s->pci_irq_map[i] = (0x1f << 6) | (i << 2); |
| 413 | } |
| 414 | for (i = 0; i < 2; i++) { |
| 415 | s->pci_err_irq_map[i] = (0x1f << 6) | 0x30; |
| 416 | } |
| 417 | for (i = 0; i < 32; i++) { |
| 418 | s->obio_irq_map[i] = ((0x1f << 6) | 0x20) + i; |
| 419 | } |
| 420 | qdev_init_gpio_in_named(DEVICE(s), pci_sabre_set_irq, "pbm-irq", MAX_IVEC); |
| 421 | qdev_init_gpio_out_named(DEVICE(s), s->ivec_irqs, "ivec-irq", MAX_IVEC); |
| 422 | s->irq_request = NO_IRQ_REQUEST; |
| 423 | s->pci_irq_in = 0ULL; |
| 424 | |
| 425 | /* IOMMU */ |
| 426 | object_property_add_link(obj, "iommu", TYPE_SUN4U_IOMMU, |
| 427 | (Object **) &s->iommu, |
| 428 | qdev_prop_allow_set_link_before_realize, |
| 429 | 0); |
| 430 | |
| 431 | /* sabre_config */ |
| 432 | memory_region_init_io(&s->sabre_config, OBJECT(s), &sabre_config_ops, s, |
| 433 | "sabre-config", 0x10000); |
| 434 | /* at region 0 */ |
| 435 | sysbus_init_mmio(sbd, &s->sabre_config); |
| 436 | |
| 437 | memory_region_init_io(&s->pci_config, OBJECT(s), &pci_config_ops, s, |
| 438 | "sabre-pci-config", 0x1000000); |
| 439 | /* at region 1 */ |
| 440 | sysbus_init_mmio(sbd, &s->pci_config); |
| 441 | |
| 442 | /* pci_ioport */ |
| 443 | memory_region_init(&s->pci_ioport, OBJECT(s), "sabre-pci-ioport", |
| 444 | 0x1000000); |
| 445 | |
| 446 | /* at region 2 */ |
| 447 | sysbus_init_mmio(sbd, &s->pci_ioport); |
| 448 | } |
| 449 | |
| 450 | static void sabre_pci_realize(PCIDevice *d, Error **errp) |
| 451 | { |
| 452 | pci_set_word(d->config + PCI_COMMAND, |
| 453 | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); |
| 454 | pci_set_word(d->config + PCI_STATUS, |
| 455 | PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ | |
| 456 | PCI_STATUS_DEVSEL_MEDIUM); |
| 457 | } |
| 458 | |
| 459 | static void sabre_pci_class_init(ObjectClass *klass, const void *data) |
| 460 | { |
| 461 | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| 462 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 463 | |
| 464 | k->realize = sabre_pci_realize; |
| 465 | k->vendor_id = PCI_VENDOR_ID_SUN; |
| 466 | k->device_id = PCI_DEVICE_ID_SUN_SABRE; |
| 467 | k->class_id = PCI_CLASS_BRIDGE_HOST; |
| 468 | /* |
| 469 | * PCI-facing part of the host bridge, not usable without the |
| 470 | * host-facing part, which can't be device_add'ed, yet. |
| 471 | */ |
| 472 | dc->user_creatable = false; |
| 473 | } |
| 474 | |
| 475 | static const TypeInfo sabre_pci_info = { |
| 476 | .name = TYPE_SABRE_PCI_DEVICE, |
| 477 | .parent = TYPE_PCI_DEVICE, |
| 478 | .instance_size = sizeof(SabrePCIState), |
| 479 | .class_init = sabre_pci_class_init, |
| 480 | .interfaces = (const InterfaceInfo[]) { |
| 481 | { INTERFACE_CONVENTIONAL_PCI_DEVICE }, |
| 482 | { }, |
| 483 | }, |
| 484 | }; |
| 485 | |
| 486 | static char *sabre_ofw_unit_address(const SysBusDevice *dev) |
| 487 | { |
| 488 | SabreState *s = SABRE(dev); |
| 489 | |
| 490 | return g_strdup_printf("%x,%x", |
| 491 | (uint32_t)((s->special_base >> 32) & 0xffffffff), |
| 492 | (uint32_t)(s->special_base & 0xffffffff)); |
| 493 | } |
| 494 | |
| 495 | static const Property sabre_properties[] = { |
| 496 | DEFINE_PROP_UINT64("special-base", SabreState, special_base, 0), |
| 497 | DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), |
| 498 | }; |
| 499 | |
| 500 | static void sabre_class_init(ObjectClass *klass, const void *data) |
| 501 | { |
| 502 | DeviceClass *dc = DEVICE_CLASS(klass); |
| 503 | SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); |
| 504 | |
| 505 | dc->realize = sabre_realize; |
| 506 | device_class_set_legacy_reset(dc, sabre_reset); |
| 507 | device_class_set_props(dc, sabre_properties); |
| 508 | dc->fw_name = "pci"; |
| 509 | sbc->explicit_ofw_unit_address = sabre_ofw_unit_address; |
| 510 | } |
| 511 | |
| 512 | static const TypeInfo sabre_info = { |
| 513 | .name = TYPE_SABRE, |
| 514 | .parent = TYPE_PCI_HOST_BRIDGE, |
| 515 | .instance_size = sizeof(SabreState), |
| 516 | .instance_init = sabre_init, |
| 517 | .class_init = sabre_class_init, |
| 518 | }; |
| 519 | |
| 520 | static void sabre_register_types(void) |
| 521 | { |
| 522 | type_register_static(&sabre_info); |
| 523 | type_register_static(&sabre_pci_info); |
| 524 | } |
| 525 | |
| 526 | type_init(sabre_register_types) |