unin_pci.c 11.7 KB
Newer Older
P
pbrook 已提交
1 2 3 4
/*
 * QEMU Uninorth PCI host (for all Mac99 and newer machines)
 *
 * 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 25 26
#include "hw.h"
#include "ppc_mac.h"
#include "pci.h"
27
#include "pci_host.h"
P
pbrook 已提交
28

29 30 31 32
/* debug UniNorth */
//#define DEBUG_UNIN

#ifdef DEBUG_UNIN
33 34
#define UNIN_DPRINTF(fmt, ...)                                  \
    do { printf("UNIN: " fmt , ## __VA_ARGS__); } while (0)
35
#else
36
#define UNIN_DPRINTF(fmt, ...)
37 38
#endif

A
Alexander Graf 已提交
39 40
static const int unin_irq_line[] = { 0x1b, 0x1c, 0x1d, 0x1e };

41 42 43
typedef struct UNINState {
    SysBusDevice busdev;
    PCIHostState host_state;
44
    ReadWriteHandler data_handler;
45
} UNINState;
P
pbrook 已提交
46

47
static int pci_unin_map_irq(PCIDevice *pci_dev, int irq_num)
P
pbrook 已提交
48
{
A
Alexander Graf 已提交
49 50 51 52 53 54
    int retval;
    int devfn = pci_dev->devfn & 0x00FFFFFF;

    retval = (((devfn >> 11) & 0x1F) + irq_num) & 3;

    return retval;
55 56
}

57
static void pci_unin_set_irq(void *opaque, int irq_num, int level)
58
{
59 60
    qemu_irq *pic = opaque;

A
Alexander Graf 已提交
61 62 63
    UNIN_DPRINTF("%s: setting INT %d = %d\n", __func__,
                 unin_irq_line[irq_num], level);
    qemu_set_irq(pic[unin_irq_line[irq_num]], level);
P
pbrook 已提交
64 65
}

66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
static void pci_unin_save(QEMUFile* f, void *opaque)
{
    PCIDevice *d = opaque;

    pci_device_save(d, f);
}

static int pci_unin_load(QEMUFile* f, void *opaque, int version_id)
{
    PCIDevice *d = opaque;

    if (version_id != 1)
        return -EINVAL;

    return pci_device_load(d, f);
}

static void pci_unin_reset(void *opaque)
{
}

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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
static uint32_t unin_get_config_reg(uint32_t reg, uint32_t addr)
{
    uint32_t retval;

    if (reg & (1u << 31)) {
        /* XXX OpenBIOS compatibility hack */
        retval = reg | (addr & 3);
    } else if (reg & 1) {
        /* CFA1 style */
        retval = (reg & ~7u) | (addr & 7);
    } else {
        uint32_t slot, func;

        /* Grab CFA0 style values */
        slot = ffs(reg & 0xfffff800) - 1;
        func = (reg >> 8) & 7;

        /* ... and then convert them to x86 format */
        /* config pointer */
        retval = (reg & (0xff - 7)) | (addr & 7);
        /* slot */
        retval |= slot << 11;
        /* fn */
        retval |= func << 8;
    }


    UNIN_DPRINTF("Converted config space accessor %08x/%08x -> %08x\n",
                 reg, addr, retval);

    return retval;
}

static void unin_data_write(ReadWriteHandler *handler,
                            pcibus_t addr, uint32_t val, int len)
{
    UNINState *s = container_of(handler, UNINState, data_handler);
    val = qemu_bswap_len(val, len);
    UNIN_DPRINTF("write addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
    pci_data_write(s->host_state.bus,
                   unin_get_config_reg(s->host_state.config_reg, addr),
                   val, len);
}

static uint32_t unin_data_read(ReadWriteHandler *handler,
                               pcibus_t addr, int len)
{
    UNINState *s = container_of(handler, UNINState, data_handler);
    uint32_t val;

    val = pci_data_read(s->host_state.bus,
                        unin_get_config_reg(s->host_state.config_reg, addr),
                        len);
    UNIN_DPRINTF("read addr %" FMT_PCIBUS " len %d val %x\n", addr, len, val);
    val = qemu_bswap_len(val, len);
    return val;
}

145
static int pci_unin_main_init_device(SysBusDevice *dev)
P
pbrook 已提交
146 147 148 149 150 151
{
    UNINState *s;
    int pci_mem_config, pci_mem_data;

    /* Use values found on a real PowerMac */
    /* Uninorth main bus */
152
    s = FROM_SYSBUS(UNINState, dev);
P
pbrook 已提交
153

B
Blue Swirl 已提交
154
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state, 1);
155 156
    s->data_handler.read = unin_data_read;
    s->data_handler.write = unin_data_write;
157 158
    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler,
                                                 DEVICE_NATIVE_ENDIAN);
159 160 161
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);

A
Alex Williamson 已提交
162 163
    register_savevm(&dev->qdev, "uninorth", 0, 1,
                    pci_unin_save, pci_unin_load, &s->host_state);
164
    qemu_register_reset(pci_unin_reset, &s->host_state);
165
    return 0;
166 167
}

A
Alexander Graf 已提交
168 169 170 171 172 173 174 175
static int pci_u3_agp_init_device(SysBusDevice *dev)
{
    UNINState *s;
    int pci_mem_config, pci_mem_data;

    /* Uninorth U3 AGP bus */
    s = FROM_SYSBUS(UNINState, dev);

B
Blue Swirl 已提交
176
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state, 1);
A
Alexander Graf 已提交
177 178
    s->data_handler.read = unin_data_read;
    s->data_handler.write = unin_data_write;
179 180
    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler,
                                                 DEVICE_NATIVE_ENDIAN);
A
Alexander Graf 已提交
181 182 183
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);

A
Alex Williamson 已提交
184 185
    register_savevm(&dev->qdev, "uninorth", 0, 1,
                    pci_unin_save, pci_unin_load, &s->host_state);
A
Alexander Graf 已提交
186 187 188 189 190
    qemu_register_reset(pci_unin_reset, &s->host_state);

    return 0;
}

191
static int pci_unin_agp_init_device(SysBusDevice *dev)
192 193 194 195 196 197 198
{
    UNINState *s;
    int pci_mem_config, pci_mem_data;

    /* Uninorth AGP bus */
    s = FROM_SYSBUS(UNINState, dev);

B
Blue Swirl 已提交
199 200
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state, 0);
    pci_mem_data = pci_host_data_register_mmio(&s->host_state, 1);
201 202
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);
203
    return 0;
204 205
}

206
static int pci_unin_internal_init_device(SysBusDevice *dev)
207 208 209 210 211 212 213
{
    UNINState *s;
    int pci_mem_config, pci_mem_data;

    /* Uninorth internal bus */
    s = FROM_SYSBUS(UNINState, dev);

B
Blue Swirl 已提交
214 215
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state, 0);
    pci_mem_data = pci_host_data_register_mmio(&s->host_state, 1);
216 217
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);
218
    return 0;
219 220 221 222 223 224 225 226 227 228
}

PCIBus *pci_pmac_init(qemu_irq *pic)
{
    DeviceState *dev;
    SysBusDevice *s;
    UNINState *d;

    /* Use values found on a real PowerMac */
    /* Uninorth main bus */
229
    dev = qdev_create(NULL, "uni-north");
M
Markus Armbruster 已提交
230
    qdev_init_nofail(dev);
231 232
    s = sysbus_from_qdev(dev);
    d = FROM_SYSBUS(UNINState, s);
233
    d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
234
                                         pci_unin_set_irq, pci_unin_map_irq,
235
                                         pic, PCI_DEVFN(11, 0), 4);
236

237
#if 0
238
    pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
239
#endif
240 241 242 243 244 245 246

    sysbus_mmio_map(s, 0, 0xf2800000);
    sysbus_mmio_map(s, 1, 0xf2c00000);

    /* DEC 21154 bridge */
#if 0
    /* XXX: not activated as PPC BIOS doesn't handle multiple buses properly */
247
    pci_create_simple(d->host_state.bus, PCI_DEVFN(12, 0), "dec-21154");
248 249 250
#endif

    /* Uninorth AGP bus */
251
    pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north-agp");
252
    dev = qdev_create(NULL, "uni-north-agp");
253 254 255 256
    qdev_init_nofail(dev);
    s = sysbus_from_qdev(dev);
    sysbus_mmio_map(s, 0, 0xf0800000);
    sysbus_mmio_map(s, 1, 0xf0c00000);
257 258 259 260

    /* Uninorth internal bus */
#if 0
    /* XXX: not needed for now */
261
    pci_create_simple(d->host_state.bus, PCI_DEVFN(14, 0), "uni-north-pci");
262
    dev = qdev_create(NULL, "uni-north-pci");
263 264 265 266
    qdev_init_nofail(dev);
    s = sysbus_from_qdev(dev);
    sysbus_mmio_map(s, 0, 0xf4800000);
    sysbus_mmio_map(s, 1, 0xf4c00000);
267 268 269 270 271
#endif

    return d->host_state.bus;
}

A
Alexander Graf 已提交
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
PCIBus *pci_pmac_u3_init(qemu_irq *pic)
{
    DeviceState *dev;
    SysBusDevice *s;
    UNINState *d;

    /* Uninorth AGP bus */

    dev = qdev_create(NULL, "u3-agp");
    qdev_init_nofail(dev);
    s = sysbus_from_qdev(dev);
    d = FROM_SYSBUS(UNINState, s);

    d->host_state.bus = pci_register_bus(&d->busdev.qdev, "pci",
                                         pci_unin_set_irq, pci_unin_map_irq,
287
                                         pic, PCI_DEVFN(11, 0), 4);
A
Alexander Graf 已提交
288 289 290 291 292 293 294 295 296

    sysbus_mmio_map(s, 0, 0xf0800000);
    sysbus_mmio_map(s, 1, 0xf0c00000);

    pci_create_simple(d->host_state.bus, 11 << 3, "u3-agp");

    return d->host_state.bus;
}

297
static int unin_main_pci_host_init(PCIDevice *d)
298
{
299
    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
300
    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_PCI);
P
pbrook 已提交
301
    d->config[0x08] = 0x00; // revision
302
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
P
pbrook 已提交
303 304 305
    d->config[0x0C] = 0x08; // cache_line_size
    d->config[0x0D] = 0x10; // latency_timer
    d->config[0x34] = 0x00; // capabilities_pointer
306
    return 0;
307
}
P
pbrook 已提交
308

309
static int unin_agp_pci_host_init(PCIDevice *d)
310
{
311 312
    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_AGP);
P
pbrook 已提交
313
    d->config[0x08] = 0x00; // revision
314
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
P
pbrook 已提交
315 316 317
    d->config[0x0C] = 0x08; // cache_line_size
    d->config[0x0D] = 0x10; // latency_timer
    //    d->config[0x34] = 0x80; // capabilities_pointer
318
    return 0;
319
}
P
pbrook 已提交
320

A
Alexander Graf 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334
static int u3_agp_pci_host_init(PCIDevice *d)
{
    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_U3_AGP);
    /* revision */
    d->config[0x08] = 0x00;
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
    /* cache line size */
    d->config[0x0C] = 0x08;
    /* latency timer */
    d->config[0x0D] = 0x10;
    return 0;
}

335
static int unin_internal_pci_host_init(PCIDevice *d)
336
{
337
    pci_config_set_vendor_id(d->config, PCI_VENDOR_ID_APPLE);
338
    pci_config_set_device_id(d->config, PCI_DEVICE_ID_APPLE_UNI_N_I_PCI);
P
pbrook 已提交
339
    d->config[0x08] = 0x00; // revision
340
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
P
pbrook 已提交
341 342 343
    d->config[0x0C] = 0x08; // cache_line_size
    d->config[0x0D] = 0x10; // latency_timer
    d->config[0x34] = 0x00; // capabilities_pointer
344
    return 0;
345 346 347
}

static PCIDeviceInfo unin_main_pci_host_info = {
348
    .qdev.name = "uni-north",
349 350 351 352
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_main_pci_host_init,
};

A
Alexander Graf 已提交
353 354 355 356 357 358
static PCIDeviceInfo u3_agp_pci_host_info = {
    .qdev.name = "u3-agp",
    .qdev.size = sizeof(PCIDevice),
    .init      = u3_agp_pci_host_init,
};

359
static PCIDeviceInfo unin_agp_pci_host_info = {
360
    .qdev.name = "uni-north-agp",
361 362 363 364 365
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_agp_pci_host_init,
};

static PCIDeviceInfo unin_internal_pci_host_info = {
366
    .qdev.name = "uni-north-pci",
367 368 369 370 371 372
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_internal_pci_host_init,
};

static void unin_register_devices(void)
{
373
    sysbus_register_dev("uni-north", sizeof(UNINState),
374 375
                        pci_unin_main_init_device);
    pci_qdev_register(&unin_main_pci_host_info);
A
Alexander Graf 已提交
376 377 378
    sysbus_register_dev("u3-agp", sizeof(UNINState),
                        pci_u3_agp_init_device);
    pci_qdev_register(&u3_agp_pci_host_info);
379
    sysbus_register_dev("uni-north-agp", sizeof(UNINState),
380 381
                        pci_unin_agp_init_device);
    pci_qdev_register(&unin_agp_pci_host_info);
382
    sysbus_register_dev("uni-north-pci", sizeof(UNINState),
383 384
                        pci_unin_internal_init_device);
    pci_qdev_register(&unin_internal_pci_host_info);
P
pbrook 已提交
385
}
386 387

device_init(unin_register_devices)