apb_pci.c 13.5 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 "rwhandler.h"
33
#include "apb_pci.h"
B
Fix APB  
blueswir1 已提交
34 35 36 37 38

/* debug APB */
//#define DEBUG_APB

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

B
Blue Swirl 已提交
45 46 47 48 49 50 51 52 53
/*
 * 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
 */

54 55 56 57 58 59 60 61 62 63 64 65
#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 已提交
66 67 68
typedef struct APBState {
    SysBusDevice busdev;
    PCIHostState host_state;
69
    ReadWriteHandler pci_config_handler;
70 71 72 73 74 75
    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 已提交
76
} APBState;
P
pbrook 已提交
77

78 79
static unsigned int nr_resets;

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

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

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

P
pbrook 已提交
173 174 175
    return val;
}

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

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

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

    val = qemu_bswap_len(val, size);
194
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " val %x\n", __func__, addr, val);
M
Michael S. Tsirkin 已提交
195
    pci_data_write(s->host_state.bus, addr, val, size);
196 197
}

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

M
Michael S. Tsirkin 已提交
204
    ret = pci_data_read(s->host_state.bus, addr, size);
205
    ret = qemu_bswap_len(ret, size);
206 207 208 209
    APB_DPRINTF("%s: addr " TARGET_FMT_lx " -> %x\n", __func__, addr, ret);
    return ret;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

296 297 298 299 300 301 302 303 304 305 306 307 308 309
static void apb_pci_bridge_init(PCIBus *b)
{
    PCIDevice *dev = pci_bridge_get_device(b);

    /*
     * 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,
310 311 312 313 314 315 316 317
                 PCI_COMMAND_MEMORY);
    pci_set_word(dev->config + PCI_STATUS,
                 PCI_STATUS_FAST_BACK | PCI_STATUS_66MHZ |
                 PCI_STATUS_DEVSEL_MEDIUM);
    pci_set_byte(dev->config + PCI_REVISION_ID, 0x11);
    pci_set_byte(dev->config + PCI_HEADER_TYPE,
                 pci_get_byte(dev->config + PCI_HEADER_TYPE) |
                 PCI_HEADER_TYPE_MULTI_FUNCTION);
318 319
}

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

P
pbrook 已提交
329
    /* Ultrasparc PBM main bus */
B
Blue Swirl 已提交
330
    dev = qdev_create(NULL, "pbm");
M
Markus Armbruster 已提交
331
    qdev_init_nofail(dev);
B
Blue Swirl 已提交
332 333
    s = sysbus_from_qdev(dev);
    /* apb_config */
334
    sysbus_mmio_map(s, 0, special_base);
B
Blue Swirl 已提交
335 336
    /* pci_ioport */
    sysbus_mmio_map(s, 1, special_base + 0x2000000ULL);
B
Blue Swirl 已提交
337
    /* pci_config */
B
Blue Swirl 已提交
338 339
    sysbus_mmio_map(s, 2, special_base + 0x1000000ULL);
    /* mem_data */
B
Blue Swirl 已提交
340
    sysbus_mmio_map(s, 3, mem_base);
B
Blue Swirl 已提交
341
    d = FROM_SYSBUS(APBState, s);
342
    d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
343
                                         pci_apb_set_irq, pci_pbm_map_irq, d,
B
Blue Swirl 已提交
344
                                         0, 32);
345 346
    pci_bus_set_mem_base(d->host_state.bus, mem_base);

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

B
Blue Swirl 已提交
351 352
    pci_create_simple(d->host_state.bus, 0, "pbm");
    /* APB secondary busses */
353 354 355
    *bus2 = pci_bridge_init(d->host_state.bus, PCI_DEVFN(1, 0),
                            PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA,
                            pci_apb_map_irq,
B
Blue Swirl 已提交
356
                            "Advanced PCI Bus secondary bridge 1");
357 358
    apb_pci_bridge_init(*bus2);

359 360 361
    *bus3 = pci_bridge_init(d->host_state.bus, PCI_DEVFN(1, 1),
                            PCI_VENDOR_ID_SUN, PCI_DEVICE_ID_SUN_SIMBA,
                            pci_apb_map_irq,
B
Blue Swirl 已提交
362
                            "Advanced PCI Bus secondary bridge 2");
363
    apb_pci_bridge_init(*bus3);
P
pbrook 已提交
364

B
Blue Swirl 已提交
365 366 367
    return d->host_state.bus;
}

368
static void pci_pbm_reset(DeviceState *d)
B
Blue Swirl 已提交
369
{
370 371
    unsigned int i;
    APBState *s = container_of(d, APBState, busdev.qdev);
B
Blue Swirl 已提交
372

373 374 375 376 377 378 379 380 381 382 383 384
    for (i = 0; i < 8; i++) {
        s->pci_irq_map[i] &= PBM_PCI_IMR_MASK;
    }

    if (nr_resets++ == 0) {
        /* Power on reset */
        s->reset_control = POR;
    }
}

static int pci_pbm_init_device(SysBusDevice *dev)
{
B
Blue Swirl 已提交
385
    APBState *s;
B
Blue Swirl 已提交
386
    int pci_mem_data, apb_config, pci_ioport, pci_config;
387
    unsigned int i;
B
Blue Swirl 已提交
388 389

    s = FROM_SYSBUS(APBState, dev);
390 391 392 393 394 395 396
    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 已提交
397
    /* apb_config */
398
    apb_config = cpu_register_io_memory(apb_config_read,
B
blueswir1 已提交
399
                                        apb_config_write, s);
400
    sysbus_init_mmio(dev, 0x10000ULL, apb_config);
B
Blue Swirl 已提交
401
    /* pci_ioport */
402
    pci_ioport = cpu_register_io_memory(pci_apb_ioread,
P
pbrook 已提交
403
                                          pci_apb_iowrite, s);
B
Blue Swirl 已提交
404
    sysbus_init_mmio(dev, 0x10000ULL, pci_ioport);
405
    /* pci_config */
406 407 408 409
    s->pci_config_handler.read = apb_pci_config_read;
    s->pci_config_handler.write = apb_pci_config_write;
    pci_config = cpu_register_io_memory_simple(&s->pci_config_handler);
    assert(pci_config >= 0);
410
    sysbus_init_mmio(dev, 0x1000000ULL, pci_config);
B
Blue Swirl 已提交
411
    /* mem_data */
B
Blue Swirl 已提交
412
    pci_mem_data = pci_host_data_register_mmio(&s->host_state, 1);
B
Blue Swirl 已提交
413
    sysbus_init_mmio(dev, 0x10000000ULL, pci_mem_data);
414
    return 0;
B
Blue Swirl 已提交
415
}
P
pbrook 已提交
416

417
static int pbm_pci_host_init(PCIDevice *d)
B
Blue Swirl 已提交
418
{
419 420
    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_SUN);
    pci_config_set_device_id(d->config, PCI_DEVICE_ID_SUN_SABRE);
421 422 423 424 425
    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);
426
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
427 428
    pci_set_byte(d->config + PCI_HEADER_TYPE,
                 PCI_HEADER_TYPE_NORMAL);
429
    return 0;
B
Blue Swirl 已提交
430
}
P
pbrook 已提交
431

B
Blue Swirl 已提交
432 433 434 435
static PCIDeviceInfo pbm_pci_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(PCIDevice),
    .init      = pbm_pci_host_init,
B
Blue Swirl 已提交
436
    .header_type  = PCI_HEADER_TYPE_BRIDGE,
B
Blue Swirl 已提交
437 438
};

439 440 441 442 443 444
static SysBusDeviceInfo pbm_host_info = {
    .qdev.name = "pbm",
    .qdev.size = sizeof(APBState),
    .qdev.reset = pci_pbm_reset,
    .init = pci_pbm_init_device,
};
B
Blue Swirl 已提交
445 446
static void pbm_register_devices(void)
{
447
    sysbus_register_withprop(&pbm_host_info);
B
Blue Swirl 已提交
448
    pci_qdev_register(&pbm_pci_host_info);
P
pbrook 已提交
449
}
B
Blue Swirl 已提交
450 451

device_init(pbm_register_devices)