apb_pci.c 13.7 KB
Newer Older
P
pbrook 已提交
1 2 3 4
/*
 * QEMU Ultrasparc APB PCI host
 *
 * Copyright (c) 2006 Fabrice Bellard
5
 *
P
pbrook 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
P
pbrook 已提交
24

B
Fix APB  
blueswir1 已提交
25
/* XXX This file and most of its contents are somewhat misnamed.  The
P
pbrook 已提交
26 27 28
   Ultrasparc PCI host is called the PCI Bus Module (PBM).  The APB is
   the secondary PCI bridge.  */

B
Blue Swirl 已提交
29
#include "sysbus.h"
P
pbrook 已提交
30
#include "pci.h"
31
#include "pci_host.h"
32
#include "pci_bridge.h"
33
#include "pci_internals.h"
34
#include "apb_pci.h"
35
#include "sysemu.h"
36
#include "exec-memory.h"
B
Fix APB  
blueswir1 已提交
37 38 39 40 41

/* debug APB */
//#define DEBUG_APB

#ifdef DEBUG_APB
42 43
#define APB_DPRINTF(fmt, ...) \
do { printf("APB: " fmt , ## __VA_ARGS__); } while (0)
B
Fix APB  
blueswir1 已提交
44
#else
45
#define APB_DPRINTF(fmt, ...)
B
Fix APB  
blueswir1 已提交
46 47
#endif

B
Blue Swirl 已提交
48 49 50 51 52 53 54 55 56
/*
 * Chipset docs:
 * PBM: "UltraSPARC IIi User's Manual",
 * http://www.sun.com/processors/manuals/805-0087.pdf
 *
 * APB: "Advanced PCI Bridge (APB) User's Manual",
 * http://www.sun.com/processors/manuals/805-1251.pdf
 */

57 58 59 60 61 62 63 64 65 66 67 68
#define PBM_PCI_IMR_MASK    0x7fffffff
#define PBM_PCI_IMR_ENABLED 0x80000000

#define POR          (1 << 31)
#define SOFT_POR     (1 << 30)
#define SOFT_XIR     (1 << 29)
#define BTN_POR      (1 << 28)
#define BTN_XIR      (1 << 27)
#define RESET_MASK   0xf8000000
#define RESET_WCMASK 0x98000000
#define RESET_WMASK  0x60000000

B
Blue Swirl 已提交
69 70
typedef struct APBState {
    SysBusDevice busdev;
71
    PCIBus      *bus;
A
Avi Kivity 已提交
72 73 74
    MemoryRegion apb_config;
    MemoryRegion pci_config;
    MemoryRegion pci_ioport;
75 76 77 78 79 80
    uint32_t iommu[4];
    uint32_t pci_control[16];
    uint32_t pci_irq_map[8];
    uint32_t obio_irq_map[32];
    qemu_irq pci_irqs[32];
    uint32_t reset_control;
B
Blue Swirl 已提交
81
    unsigned int nr_resets;
B
Blue Swirl 已提交
82
} APBState;
P
pbrook 已提交
83

A
Anthony Liguori 已提交
84
static void apb_config_writel (void *opaque, target_phys_addr_t addr,
A
Avi Kivity 已提交
85
                               uint64_t val, unsigned size)
P
pbrook 已提交
86
{
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
    APBState *s = opaque;

    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);

    switch (addr & 0xffff) {
    case 0x30 ... 0x4f: /* DMA error registers */
        /* XXX: not implemented yet */
        break;
    case 0x200 ... 0x20b: /* IOMMU */
        s->iommu[(addr & 0xf) >> 2] = val;
        break;
    case 0x20c ... 0x3ff: /* IOMMU flush */
        break;
    case 0xc00 ... 0xc3f: /* PCI interrupt control */
        if (addr & 4) {
            s->pci_irq_map[(addr & 0x3f) >> 3] &= PBM_PCI_IMR_MASK;
            s->pci_irq_map[(addr & 0x3f) >> 3] |= val & ~PBM_PCI_IMR_MASK;
        }
        break;
    case 0x2000 ... 0x202f: /* PCI control */
        s->pci_control[(addr & 0x3f) >> 2] = val;
        break;
    case 0xf020 ... 0xf027: /* Reset control */
        if (addr & 4) {
            val &= RESET_MASK;
            s->reset_control &= ~(val & RESET_WCMASK);
            s->reset_control |= val & RESET_WMASK;
            if (val & SOFT_POR) {
B
Blue Swirl 已提交
115
                s->nr_resets = 0;
116 117 118 119 120 121 122 123 124 125 126
                qemu_system_reset_request();
            } else if (val & SOFT_XIR) {
                qemu_system_reset_request();
            }
        }
        break;
    case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */
    case 0xa400 ... 0xa67f: /* IOMMU diagnostics */
    case 0xa800 ... 0xa80f: /* Interrupt diagnostics */
    case 0xf000 ... 0xf01f: /* FFB config, memory control */
        /* we don't care */
P
pbrook 已提交
127
    default:
B
blueswir1 已提交
128
        break;
P
pbrook 已提交
129 130 131
    }
}

A
Avi Kivity 已提交
132 133
static uint64_t apb_config_readl (void *opaque,
                                  target_phys_addr_t addr, unsigned size)
P
pbrook 已提交
134
{
135
    APBState *s = opaque;
P
pbrook 已提交
136 137
    uint32_t val;

138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
    switch (addr & 0xffff) {
    case 0x30 ... 0x4f: /* DMA error registers */
        val = 0;
        /* XXX: not implemented yet */
        break;
    case 0x200 ... 0x20b: /* IOMMU */
        val = s->iommu[(addr & 0xf) >> 2];
        break;
    case 0x20c ... 0x3ff: /* IOMMU flush */
        val = 0;
        break;
    case 0xc00 ... 0xc3f: /* PCI interrupt control */
        if (addr & 4) {
            val = s->pci_irq_map[(addr & 0x3f) >> 3];
        } else {
            val = 0;
        }
        break;
    case 0x2000 ... 0x202f: /* PCI control */
        val = s->pci_control[(addr & 0x3f) >> 2];
        break;
    case 0xf020 ... 0xf027: /* Reset control */
        if (addr & 4) {
            val = s->reset_control;
        } else {
            val = 0;
        }
        break;
    case 0x5000 ... 0x51cf: /* PIO/DMA diagnostics */
    case 0xa400 ... 0xa67f: /* IOMMU diagnostics */
    case 0xa800 ... 0xa80f: /* Interrupt diagnostics */
    case 0xf000 ... 0xf01f: /* FFB config, memory control */
        /* we don't care */
P
pbrook 已提交
171
    default:
B
blueswir1 已提交
172 173
        val = 0;
        break;
P
pbrook 已提交
174
    }
175 176
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, val);

P
pbrook 已提交
177 178 179
    return val;
}

A
Avi Kivity 已提交
180 181 182 183
static const MemoryRegionOps apb_config_ops = {
    .read = apb_config_readl,
    .write = apb_config_writel,
    .endianness = DEVICE_NATIVE_ENDIAN,
P
pbrook 已提交
184 185
};

A
Avi Kivity 已提交
186 187
static void apb_pci_config_write(void *opaque, target_phys_addr_t addr,
                                 uint64_t val, unsigned size)
188
{
A
Avi Kivity 已提交
189
    APBState *s = opaque;
190 191

    val = qemu_bswap_len(val, size);
192
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
193
    pci_data_write(s->bus, addr, val, size);
194 195
}

A
Avi Kivity 已提交
196 197
static uint64_t apb_pci_config_read(void *opaque, target_phys_addr_t addr,
                                    unsigned size)
198 199
{
    uint32_t ret;
A
Avi Kivity 已提交
200
    APBState *s = opaque;
201

202
    ret = pci_data_read(s->bus, addr, size);
203
    ret = qemu_bswap_len(ret, size);
204 205 206 207
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret);
    return ret;
}

A
Anthony Liguori 已提交
208
static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr,
P
pbrook 已提交
209 210
                                  uint32_t val)
{
211
    cpu_outb(addr & IOPORTS_MASK, val);
P
pbrook 已提交
212 213
}

A
Anthony Liguori 已提交
214
static void pci_apb_iowritew (void *opaque, target_phys_addr_t addr,
P
pbrook 已提交
215 216
                                  uint32_t val)
{
B
Blue Swirl 已提交
217
    cpu_outw(addr & IOPORTS_MASK, bswap16(val));
P
pbrook 已提交
218 219
}

A
Anthony Liguori 已提交
220
static void pci_apb_iowritel (void *opaque, target_phys_addr_t addr,
P
pbrook 已提交
221 222
                                uint32_t val)
{
B
Blue Swirl 已提交
223
    cpu_outl(addr & IOPORTS_MASK, bswap32(val));
P
pbrook 已提交
224 225
}

A
Anthony Liguori 已提交
226
static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
227 228 229
{
    uint32_t val;

230
    val = cpu_inb(addr & IOPORTS_MASK);
P
pbrook 已提交
231 232 233
    return val;
}

A
Anthony Liguori 已提交
234
static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
235 236 237
{
    uint32_t val;

B
Blue Swirl 已提交
238
    val = bswap16(cpu_inw(addr & IOPORTS_MASK));
P
pbrook 已提交
239 240 241
    return val;
}

A
Anthony Liguori 已提交
242
static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
243 244 245
{
    uint32_t val;

B
Blue Swirl 已提交
246
    val = bswap32(cpu_inl(addr & IOPORTS_MASK));
P
pbrook 已提交
247 248 249
    return val;
}

A
Avi Kivity 已提交
250 251 252 253 254 255
static const MemoryRegionOps pci_ioport_ops = {
    .old_mmio = {
        .read = { pci_apb_ioreadb, pci_apb_ioreadw, pci_apb_ioreadl },
        .write = { pci_apb_iowriteb, pci_apb_iowritew, pci_apb_iowritel, },
    },
    .endianness = DEVICE_NATIVE_ENDIAN,
P
pbrook 已提交
256 257
};

P
pbrook 已提交
258
/* The APB host has an IRQ line for each IRQ line of each slot.  */
259
static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num)
P
pbrook 已提交
260
{
P
pbrook 已提交
261 262 263 264 265 266 267 268 269 270 271
    return ((pci_dev->devfn & 0x18) >> 1) + irq_num;
}

static int pci_pbm_map_irq(PCIDevice *pci_dev, int irq_num)
{
    int bus_offset;
    if (pci_dev->devfn & 1)
        bus_offset = 16;
    else
        bus_offset = 0;
    return bus_offset + irq_num;
272 273
}

274
static void pci_apb_set_irq(void *opaque, int irq_num, int level)
275
{
276
    APBState *s = opaque;
277

P
pbrook 已提交
278
    /* PCI IRQ map onto the first 32 INO.  */
279 280 281 282 283 284 285 286 287
    if (irq_num < 32) {
        if (s->pci_irq_map[irq_num >> 2] & PBM_PCI_IMR_ENABLED) {
            APB_DPRINTF("%s: set irq %d level %d\n", __func__, irq_num, level);
            qemu_set_irq(s->pci_irqs[irq_num], level);
        } else {
            APB_DPRINTF("%s: not enabled: lower irq %d\n", __func__, irq_num);
            qemu_irq_lower(s->pci_irqs[irq_num]);
        }
    }
P
pbrook 已提交
288 289
}

290
static int apb_pci_bridge_initfn(PCIDevice *dev)
291
{
292 293 294 295 296 297 298
    int rc;

    rc = pci_bridge_initfn(dev);
    if (rc < 0) {
        return rc;
    }

299 300 301 302 303 304 305 306 307 308
    /*
     * command register:
     * According to PCI bridge spec, after reset
     *   bus master bit is off
     *   memory space enable bit is off
     * According to manual (805-1251.pdf).
     *   the reset value should be zero unless the boot pin is tied high
     *   (which is true) and thus it should be PCI_COMMAND_MEMORY.
     */
    pci_set_word(dev->config + PCI_COMMAND,
309 310 311 312
                 PCI_COMMAND_MEMORY);
    pci_set_word(dev->config + PCI_STATUS,
                 PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                 PCI_STATUS_DEVSEL_MEDIUM);
313
    return 0;
314 315
}

A
Anthony Liguori 已提交
316 317
PCIBus *pci_apb_init(target_phys_addr_t special_base,
                     target_phys_addr_t mem_base,
B
blueswir1 已提交
318
                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
P
pbrook 已提交
319
{
B
Blue Swirl 已提交
320 321 322
    DeviceState *dev;
    SysBusDevice *s;
    APBState *d;
323
    unsigned int i;
324 325
    PCIDevice *pci_dev;
    PCIBridge *br;
P
pbrook 已提交
326

P
pbrook 已提交
327
    /* Ultrasparc PBM main bus */
B
Blue Swirl 已提交
328
    dev = qdev_create(NULL, "pbm");
M
Markus Armbruster 已提交
329
    qdev_init_nofail(dev);
B
Blue Swirl 已提交
330 331
    s = sysbus_from_qdev(dev);
    /* apb_config */
332
    sysbus_mmio_map(s, 0, special_base);
333 334
    /* PCI configuration space */
    sysbus_mmio_map(s, 1, special_base + 0x1000000ULL);
B
Blue Swirl 已提交
335
    /* pci_ioport */
336
    sysbus_mmio_map(s, 2, special_base + 0x2000000ULL);
B
Blue Swirl 已提交
337
    d = FROM_SYSBUS(APBState, s);
338 339

    d->bus = pci_register_bus(&d->busdev.qdev, "pci",
340
                                         pci_apb_set_irq, pci_pbm_map_irq, d,
341
                                         get_system_memory(),
342
                                         get_system_io(),
B
Blue Swirl 已提交
343
                                         0, 32);
344
    pci_bus_set_mem_base(d->bus, mem_base);
345

346 347 348 349
    for (i = 0; i < 32; i++) {
        sysbus_connect_irq(s, i, pic[i]);
    }

350 351
    pci_create_simple(d->bus, 0, "pbm");

B
Blue Swirl 已提交
352
    /* APB secondary busses */
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
    pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 0), true,
                                   "pbm-bridge");
    br = DO_UPCAST(PCIBridge, dev, pci_dev);
    pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 1",
                       pci_apb_map_irq);
    qdev_init_nofail(&pci_dev->qdev);
    *bus2 = pci_bridge_get_sec_bus(br);

    pci_dev = pci_create_multifunction(d->bus, PCI_DEVFN(1, 1), true,
                                   "pbm-bridge");
    br = DO_UPCAST(PCIBridge, dev, pci_dev);
    pci_bridge_map_irq(br, "Advanced PCI Bus secondary bridge 2",
                       pci_apb_map_irq);
    qdev_init_nofail(&pci_dev->qdev);
    *bus3 = pci_bridge_get_sec_bus(br);
P
pbrook 已提交
368

369
    return d->bus;
B
Blue Swirl 已提交
370 371
}

372
static void pci_pbm_reset(DeviceState *d)
B
Blue Swirl 已提交
373
{
374 375
    unsigned int i;
    APBState *s = container_of(d, APBState, busdev.qdev);
B
Blue Swirl 已提交
376

377 378 379 380
    for (i = 0; i < 8; i++) {
        s->pci_irq_map[i] &= PBM_PCI_IMR_MASK;
    }

B
Blue Swirl 已提交
381
    if (s->nr_resets++ == 0) {
382 383 384 385 386
        /* Power on reset */
        s->reset_control = POR;
    }
}

A
Avi Kivity 已提交
387 388 389 390 391 392
static const MemoryRegionOps pci_config_ops = {
    .read = apb_pci_config_read,
    .write = apb_pci_config_write,
    .endianness = DEVICE_NATIVE_ENDIAN,
};

393 394
static int pci_pbm_init_device(SysBusDevice *dev)
{
B
Blue Swirl 已提交
395
    APBState *s;
396
    unsigned int i;
B
Blue Swirl 已提交
397 398

    s = FROM_SYSBUS(APBState, dev);
399 400 401 402 403 404 405
    for (i = 0; i < 8; i++) {
        s->pci_irq_map[i] = (0x1f << 6) | (i << 2);
    }
    for (i = 0; i < 32; i++) {
        sysbus_init_irq(dev, &s->pci_irqs[i]);
    }

B
Blue Swirl 已提交
406
    /* apb_config */
A
Avi Kivity 已提交
407 408
    memory_region_init_io(&s->apb_config, &apb_config_ops, s, "apb-config",
                          0x10000);
409
    /* at region 0 */
A
Avi Kivity 已提交
410
    sysbus_init_mmio_region(dev, &s->apb_config);
411

A
Avi Kivity 已提交
412 413
    memory_region_init_io(&s->pci_config, &pci_config_ops, s, "apb-pci-config",
                          0x1000000);
414
    /* at region 1 */
A
Avi Kivity 已提交
415
    sysbus_init_mmio_region(dev, &s->pci_config);
416 417

    /* pci_ioport */
A
Avi Kivity 已提交
418 419
    memory_region_init_io(&s->pci_ioport, &pci_ioport_ops, s,
                          "apb-pci-ioport", 0x10000);
420
    /* at region 2 */
A
Avi Kivity 已提交
421
    sysbus_init_mmio_region(dev, &s->pci_ioport);
422

423
    return 0;
B
Blue Swirl 已提交
424
}
P
pbrook 已提交
425

426
static int pbm_pci_host_init(PCIDevice *d)
B
Blue Swirl 已提交
427
{
428 429 430 431 432
    pci_set_word(d->config + PCI_COMMAND,
                 PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
    pci_set_word(d->config + PCI_STATUS,
                 PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                 PCI_STATUS_DEVSEL_MEDIUM);
433
    return 0;
B
Blue Swirl 已提交
434
}
P
pbrook 已提交
435

B
Blue Swirl 已提交
436 437 438 439
static PCIDeviceInfo pbm_pci_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(PCIDevice),
    .init      = pbm_pci_host_init,
440 441 442
    .vendor_id = PCI_VENDOR_ID_SUN,
    .device_id = PCI_DEVICE_ID_SUN_SABRE,
    .class_id  = PCI_CLASS_BRIDGE_HOST,
443
    .is_bridge = 1,
B
Blue Swirl 已提交
444 445
};

446 447 448 449 450 451
static SysBusDeviceInfo pbm_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(APBState),
    .qdev.reset = pci_pbm_reset,
    .init = pci_pbm_init_device,
};
452 453 454 455 456 457 458 459

static PCIDeviceInfo pbm_pci_bridge_info = {
    .qdev.name = "pbm-bridge",
    .qdev.size = sizeof(PCIBridge),
    .qdev.vmsd = &vmstate_pci_device,
    .qdev.reset = pci_bridge_reset,
    .init = apb_pci_bridge_initfn,
    .exit = pci_bridge_exitfn,
460 461 462
    .vendor_id = PCI_VENDOR_ID_SUN,
    .device_id = PCI_DEVICE_ID_SUN_SIMBA,
    .revision = 0x11,
463 464 465 466
    .config_write = pci_bridge_write_config,
    .is_bridge = 1,
};

B
Blue Swirl 已提交
467 468
static void pbm_register_devices(void)
{
469
    sysbus_register_withprop(&pbm_host_info);
B
Blue Swirl 已提交
470
    pci_qdev_register(&pbm_pci_host_info);
471
    pci_qdev_register(&pbm_pci_bridge_info);
P
pbrook 已提交
472
}
B
Blue Swirl 已提交
473 474

device_init(pbm_register_devices)