apb_pci.c 13.9 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 "rwhandler.h"
35
#include "apb_pci.h"
36
#include "sysemu.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;
72
    ReadWriteHandler pci_config_handler;
73 74 75 76 77 78
    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 已提交
79
    unsigned int nr_resets;
B
Blue Swirl 已提交
80
} APBState;
P
pbrook 已提交
81

A
Anthony Liguori 已提交
82
static void apb_config_writel (void *opaque, target_phys_addr_t addr,
B
blueswir1 已提交
83
                               uint32_t val)
P
pbrook 已提交
84
{
85 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
    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 已提交
113
                s->nr_resets = 0;
114 115 116 117 118 119 120 121 122 123 124
                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 已提交
125
    default:
B
blueswir1 已提交
126
        break;
P
pbrook 已提交
127 128 129 130
    }
}

static uint32_t apb_config_readl (void *opaque,
A
Anthony Liguori 已提交
131
                                  target_phys_addr_t addr)
P
pbrook 已提交
132
{
133
    APBState *s = opaque;
P
pbrook 已提交
134 135
    uint32_t val;

136 137 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
    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 已提交
169
    default:
B
blueswir1 已提交
170 171
        val = 0;
        break;
P
pbrook 已提交
172
    }
173 174
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, val);

P
pbrook 已提交
175 176 177
    return val;
}

178
static CPUWriteMemoryFunc * const apb_config_write[] = {
P
pbrook 已提交
179 180 181 182 183
    &apb_config_writel,
    &apb_config_writel,
    &apb_config_writel,
};

184
static CPUReadMemoryFunc * const apb_config_read[] = {
P
pbrook 已提交
185 186 187 188 189
    &apb_config_readl,
    &apb_config_readl,
    &apb_config_readl,
};

190
static void apb_pci_config_write(ReadWriteHandler *h, pcibus_t addr,
191 192
                                 uint32_t val, int size)
{
193 194 195
    APBState *s = container_of(h, APBState, pci_config_handler);

    val = qemu_bswap_len(val, size);
196
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
197
    pci_data_write(s->bus, addr, val, size);
198 199
}

200
static uint32_t apb_pci_config_read(ReadWriteHandler *h, pcibus_t addr,
201 202 203
                                    int size)
{
    uint32_t ret;
204
    APBState *s = container_of(h, APBState, pci_config_handler);
205

206
    ret = pci_data_read(s->bus, addr, size);
207
    ret = qemu_bswap_len(ret, size);
208 209 210 211
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret);
    return ret;
}

A
Anthony Liguori 已提交
212
static void pci_apb_iowriteb (void *opaque, target_phys_addr_t addr,
P
pbrook 已提交
213 214
                                  uint32_t val)
{
215
    cpu_outb(addr & IOPORTS_MASK, val);
P
pbrook 已提交
216 217
}

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

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

A
Anthony Liguori 已提交
230
static uint32_t pci_apb_ioreadb (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
231 232 233
{
    uint32_t val;

234
    val = cpu_inb(addr & IOPORTS_MASK);
P
pbrook 已提交
235 236 237
    return val;
}

A
Anthony Liguori 已提交
238
static uint32_t pci_apb_ioreadw (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
239 240 241
{
    uint32_t val;

B
Blue Swirl 已提交
242
    val = bswap16(cpu_inw(addr & IOPORTS_MASK));
P
pbrook 已提交
243 244 245
    return val;
}

A
Anthony Liguori 已提交
246
static uint32_t pci_apb_ioreadl (void *opaque, target_phys_addr_t addr)
P
pbrook 已提交
247 248 249
{
    uint32_t val;

B
Blue Swirl 已提交
250
    val = bswap32(cpu_inl(addr & IOPORTS_MASK));
P
pbrook 已提交
251 252 253
    return val;
}

254
static CPUWriteMemoryFunc * const pci_apb_iowrite[] = {
P
pbrook 已提交
255 256 257 258 259
    &pci_apb_iowriteb,
    &pci_apb_iowritew,
    &pci_apb_iowritel,
};

260
static CPUReadMemoryFunc * const pci_apb_ioread[] = {
P
pbrook 已提交
261 262 263 264 265
    &pci_apb_ioreadb,
    &pci_apb_ioreadw,
    &pci_apb_ioreadl,
};

P
pbrook 已提交
266
/* The APB host has an IRQ line for each IRQ line of each slot.  */
267
static int pci_apb_map_irq(PCIDevice *pci_dev, int irq_num)
P
pbrook 已提交
268
{
P
pbrook 已提交
269 270 271 272 273 274 275 276 277 278 279
    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;
280 281
}

282
static void pci_apb_set_irq(void *opaque, int irq_num, int level)
283
{
284
    APBState *s = opaque;
285

P
pbrook 已提交
286
    /* PCI IRQ map onto the first 32 INO.  */
287 288 289 290 291 292 293 294 295
    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 已提交
296 297
}

298
static int apb_pci_bridge_initfn(PCIDevice *dev)
299
{
300 301 302 303 304 305 306
    int rc;

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

307 308 309 310 311 312 313 314 315 316
    /*
     * 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,
317 318 319 320
                 PCI_COMMAND_MEMORY);
    pci_set_word(dev->config + PCI_STATUS,
                 PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                 PCI_STATUS_DEVSEL_MEDIUM);
321
    return 0;
322 323
}

A
Anthony Liguori 已提交
324 325
PCIBus *pci_apb_init(target_phys_addr_t special_base,
                     target_phys_addr_t mem_base,
B
blueswir1 已提交
326
                     qemu_irq *pic, PCIBus **bus2, PCIBus **bus3)
P
pbrook 已提交
327
{
B
Blue Swirl 已提交
328 329 330
    DeviceState *dev;
    SysBusDevice *s;
    APBState *d;
331
    unsigned int i;
332 333
    PCIDevice *pci_dev;
    PCIBridge *br;
P
pbrook 已提交
334

P
pbrook 已提交
335
    /* Ultrasparc PBM main bus */
B
Blue Swirl 已提交
336
    dev = qdev_create(NULL, "pbm");
M
Markus Armbruster 已提交
337
    qdev_init_nofail(dev);
B
Blue Swirl 已提交
338 339
    s = sysbus_from_qdev(dev);
    /* apb_config */
340
    sysbus_mmio_map(s, 0, special_base);
341 342
    /* PCI configuration space */
    sysbus_mmio_map(s, 1, special_base + 0x1000000ULL);
B
Blue Swirl 已提交
343
    /* pci_ioport */
344
    sysbus_mmio_map(s, 2, special_base + 0x2000000ULL);
B
Blue Swirl 已提交
345
    d = FROM_SYSBUS(APBState, s);
346 347

    d->bus = pci_register_bus(&d->busdev.qdev, "pci",
348
                                         pci_apb_set_irq, pci_pbm_map_irq, d,
B
Blue Swirl 已提交
349
                                         0, 32);
350
    pci_bus_set_mem_base(d->bus, mem_base);
351

352 353 354 355
    for (i = 0; i < 32; i++) {
        sysbus_connect_irq(s, i, pic[i]);
    }

356 357
    pci_create_simple(d->bus, 0, "pbm");

B
Blue Swirl 已提交
358
    /* APB secondary busses */
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    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 已提交
374

375
    return d->bus;
B
Blue Swirl 已提交
376 377
}

378
static void pci_pbm_reset(DeviceState *d)
B
Blue Swirl 已提交
379
{
380 381
    unsigned int i;
    APBState *s = container_of(d, APBState, busdev.qdev);
B
Blue Swirl 已提交
382

383 384 385 386
    for (i = 0; i < 8; i++) {
        s->pci_irq_map[i] &= PBM_PCI_IMR_MASK;
    }

B
Blue Swirl 已提交
387
    if (s->nr_resets++ == 0) {
388 389 390 391 392 393 394
        /* Power on reset */
        s->reset_control = POR;
    }
}

static int pci_pbm_init_device(SysBusDevice *dev)
{
B
Blue Swirl 已提交
395
    APBState *s;
396
    int pci_config, apb_config, pci_ioport;
397
    unsigned int i;
B
Blue Swirl 已提交
398 399

    s = FROM_SYSBUS(APBState, dev);
400 401 402 403 404 405 406
    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 已提交
407
    /* apb_config */
408
    apb_config = cpu_register_io_memory(apb_config_read,
409 410
                                        apb_config_write, s,
                                        DEVICE_NATIVE_ENDIAN);
411
    /* at region 0 */
412
    sysbus_init_mmio(dev, 0x10000ULL, apb_config);
413 414

    /* PCI configuration space */
415 416
    s->pci_config_handler.read = apb_pci_config_read;
    s->pci_config_handler.write = apb_pci_config_write;
417 418
    pci_config = cpu_register_io_memory_simple(&s->pci_config_handler,
                                               DEVICE_NATIVE_ENDIAN);
419
    assert(pci_config >= 0);
420
    /* at region 1 */
421
    sysbus_init_mmio(dev, 0x1000000ULL, pci_config);
422 423 424

    /* pci_ioport */
    pci_ioport = cpu_register_io_memory(pci_apb_ioread,
425 426
                                        pci_apb_iowrite, s,
                                        DEVICE_NATIVE_ENDIAN);
427 428 429
    /* at region 2 */
    sysbus_init_mmio(dev, 0x10000ULL, pci_ioport);

430
    return 0;
B
Blue Swirl 已提交
431
}
P
pbrook 已提交
432

433
static int pbm_pci_host_init(PCIDevice *d)
B
Blue Swirl 已提交
434
{
435 436 437 438 439
    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);
440
    return 0;
B
Blue Swirl 已提交
441
}
P
pbrook 已提交
442

B
Blue Swirl 已提交
443 444 445 446
static PCIDeviceInfo pbm_pci_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(PCIDevice),
    .init      = pbm_pci_host_init,
447 448 449
    .vendor_id = PCI_VENDOR_ID_SUN,
    .device_id = PCI_DEVICE_ID_SUN_SABRE,
    .class_id  = PCI_CLASS_BRIDGE_HOST,
450
    .is_bridge = 1,
B
Blue Swirl 已提交
451 452
};

453 454 455 456 457 458
static SysBusDeviceInfo pbm_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(APBState),
    .qdev.reset = pci_pbm_reset,
    .init = pci_pbm_init_device,
};
459 460 461 462 463 464 465 466

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,
467 468 469
    .vendor_id = PCI_VENDOR_ID_SUN,
    .device_id = PCI_DEVICE_ID_SUN_SIMBA,
    .revision = 0x11,
470 471 472 473
    .config_write = pci_bridge_write_config,
    .is_bridge = 1,
};

B
Blue Swirl 已提交
474 475
static void pbm_register_devices(void)
{
476
    sysbus_register_withprop(&pbm_host_info);
B
Blue Swirl 已提交
477
    pci_qdev_register(&pbm_pci_host_info);
478
    pci_qdev_register(&pbm_pci_bridge_info);
P
pbrook 已提交
479
}
B
Blue Swirl 已提交
480 481

device_init(pbm_register_devices)