unin_pci.c 12.1 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

154 155
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
                                                 DEVICE_LITTLE_ENDIAN);
156 157
    s->data_handler.read = unin_data_read;
    s->data_handler.write = unin_data_write;
158 159
    pci_mem_data = cpu_register_io_memory_simple(&s->data_handler,
                                                 DEVICE_NATIVE_ENDIAN);
160 161 162
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);

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

A
Alexander Graf 已提交
169 170 171 172 173 174 175 176
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);

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

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

    return 0;
}

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

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

201 202 203 204
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
                                                 DEVICE_LITTLE_ENDIAN);
    pci_mem_data = pci_host_data_register_mmio(&s->host_state,
                                               DEVICE_LITTLE_ENDIAN);
205 206
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);
207
    return 0;
208 209
}

210
static int pci_unin_internal_init_device(SysBusDevice *dev)
211 212 213 214 215 216 217
{
    UNINState *s;
    int pci_mem_config, pci_mem_data;

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

218 219 220 221
    pci_mem_config = pci_host_conf_register_mmio(&s->host_state,
                                                 DEVICE_LITTLE_ENDIAN);
    pci_mem_data = pci_host_data_register_mmio(&s->host_state,
                                               DEVICE_LITTLE_ENDIAN);
222 223
    sysbus_init_mmio(dev, 0x1000, pci_mem_config);
    sysbus_init_mmio(dev, 0x1000, pci_mem_data);
224
    return 0;
225 226 227 228 229 230 231 232 233 234
}

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

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

243
#if 0
244
    pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north");
245
#endif
246 247 248 249 250 251 252

    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 */
253
    pci_create_simple(d->host_state.bus, PCI_DEVFN(12, 0), "dec-21154");
254 255 256
#endif

    /* Uninorth AGP bus */
257
    pci_create_simple(d->host_state.bus, PCI_DEVFN(11, 0), "uni-north-agp");
258
    dev = qdev_create(NULL, "uni-north-agp");
259 260 261 262
    qdev_init_nofail(dev);
    s = sysbus_from_qdev(dev);
    sysbus_mmio_map(s, 0, 0xf0800000);
    sysbus_mmio_map(s, 1, 0xf0c00000);
263 264 265 266

    /* Uninorth internal bus */
#if 0
    /* XXX: not needed for now */
267
    pci_create_simple(d->host_state.bus, PCI_DEVFN(14, 0), "uni-north-pci");
268
    dev = qdev_create(NULL, "uni-north-pci");
269 270 271 272
    qdev_init_nofail(dev);
    s = sysbus_from_qdev(dev);
    sysbus_mmio_map(s, 0, 0xf4800000);
    sysbus_mmio_map(s, 1, 0xf4c00000);
273 274 275 276 277
#endif

    return d->host_state.bus;
}

A
Alexander Graf 已提交
278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
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,
293
                                         pic, PCI_DEVFN(11, 0), 4);
A
Alexander Graf 已提交
294 295 296 297 298 299 300 301 302

    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;
}

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

315
static int unin_agp_pci_host_init(PCIDevice *d)
316
{
317 318
    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 已提交
319
    d->config[0x08] = 0x00; // revision
320
    pci_config_set_class(d->config, PCI_CLASS_BRIDGE_HOST);
P
pbrook 已提交
321 322 323
    d->config[0x0C] = 0x08; // cache_line_size
    d->config[0x0D] = 0x10; // latency_timer
    //    d->config[0x34] = 0x80; // capabilities_pointer
324
    return 0;
325
}
P
pbrook 已提交
326

A
Alexander Graf 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339 340
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;
}

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

static PCIDeviceInfo unin_main_pci_host_info = {
354
    .qdev.name = "uni-north",
355 356 357 358
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_main_pci_host_init,
};

A
Alexander Graf 已提交
359 360 361 362 363 364
static PCIDeviceInfo u3_agp_pci_host_info = {
    .qdev.name = "u3-agp",
    .qdev.size = sizeof(PCIDevice),
    .init      = u3_agp_pci_host_init,
};

365
static PCIDeviceInfo unin_agp_pci_host_info = {
366
    .qdev.name = "uni-north-agp",
367 368 369 370 371
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_agp_pci_host_init,
};

static PCIDeviceInfo unin_internal_pci_host_info = {
372
    .qdev.name = "uni-north-pci",
373 374 375 376 377 378
    .qdev.size = sizeof(PCIDevice),
    .init      = unin_internal_pci_host_init,
};

static void unin_register_devices(void)
{
379
    sysbus_register_dev("uni-north", sizeof(UNINState),
380 381
                        pci_unin_main_init_device);
    pci_qdev_register(&unin_main_pci_host_info);
A
Alexander Graf 已提交
382 383 384
    sysbus_register_dev("u3-agp", sizeof(UNINState),
                        pci_u3_agp_init_device);
    pci_qdev_register(&u3_agp_pci_host_info);
385
    sysbus_register_dev("uni-north-agp", sizeof(UNINState),
386 387
                        pci_unin_agp_init_device);
    pci_qdev_register(&unin_agp_pci_host_info);
388
    sysbus_register_dev("uni-north-pci", sizeof(UNINState),
389 390
                        pci_unin_internal_init_device);
    pci_qdev_register(&unin_internal_pci_host_info);
P
pbrook 已提交
391
}
392 393

device_init(unin_register_devices)