On Fri, 2009-01-09 at 15:56 +0800, Liu Yu wrote:
> Signed-off-by: Liu Yu <yu....@freescale.com>
> ---
>  hw/mpic.c        |  903 
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ppce500_pci.c |  322 +++++++++++++++++++
>  2 files changed, 1225 insertions(+), 0 deletions(-)
>  create mode 100644 hw/mpic.c
>  create mode 100644 hw/ppce500_pci.c
> 
> diff --git a/hw/mpic.c b/hw/mpic.c
> new file mode 100644
> index 0000000..a68948a
> --- /dev/null
> +++ b/hw/mpic.c
> @@ -0,0 +1,903 @@
> +/*
> + * MPIC emulation
> + *
> + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * Author: Yu Liu,     <yu....@freescale.com>
> + *
> + * This file is derived from hw/openpic.c,
> + * the copyright for that material belongs to the original owners.

The Linux drivers for MPIC and OpenPIC share a lot of code. Can't you
modify hw/openpic.c instead of copy/paste/hack?

The MPIC patch should be separate from the PCI patch.

> diff --git a/hw/ppce500_pci.c b/hw/ppce500_pci.c
> new file mode 100644
> index 0000000..9242639
> --- /dev/null
> +++ b/hw/ppce500_pci.c
> @@ -0,0 +1,322 @@
> +/*
> + * QEMU PowerPC E500 embedded processors pci controller emulation
> + *
> + * Copyright (C) 2009 Freescale Semiconductor, Inc. All rights reserved.
> + *
> + * Author: Yu Liu,     <yu....@freescale.com>
> + *
> + * This file is derived from hw/ppc4xx_pci.c,
> + * the copyright for that material belongs to the original owners.
> + *
> + * This is free software; you can redistribute it and/or modify
> + * it under the terms of  the GNU General  Public License as published by
> + * the Free Software Foundation;  either version 2 of the  License, or
> + * (at your option) any later version.
> + */
> +
> +#include "hw.h"
> +#include "ppc.h"
> +#include "ppce500.h"
> +#include "sysemu.h"
> +#include "pci.h"
> +#include "bswap.h"
> +#include "qemu-log.h"
> +
> +#ifdef DEBUG_PCI
> +#define pci_debug(fmt, arg...) fprintf(stderr, fmt, ##arg)
> +#else
> +#define pci_debug(fmt, arg...)
> +#endif
> +
> +#define PPCE500_PCI_REG_SIZE         0x1000
> +
> +#define PPCE500_PCI_CONFIG_ADDR              0x0
> +#define PPCE500_PCI_CONFIG_DATA              0x4
> +#define PPCE500_PCI_INTACK           0x8
> +
> +#define PPCE500_PCI_OW1                      0xC20
> +#define PPCE500_PCI_OW2                      0xC40
> +#define PPCE500_PCI_OW3                      0xC60
> +#define PPCE500_PCI_OW4                      0xC80
> +#define PPCE500_PCI_IW3                      0xDA0
> +#define PPCE500_PCI_IW2                      0xDC0
> +#define PPCE500_PCI_IW1                      0xDE0
> +
> +#define PPCE500_PCI_GAS_TIMR         0xE20
> +
> +#define PCI_POTAR            0x0
> +#define PCI_POTEAR           0x4
> +#define PCI_POWBAR           0x8
> +#define PCI_POWAR            0x10
> +
> +#define PCI_PITAR            0x0
> +#define PCI_PIWBAR           0x8
> +#define PCI_PIWBEAR          0xC
> +#define PCI_PIWAR            0x10
> +
> +#define PPCE500_PCI_NR_POBS 5
> +#define PPCE500_PCI_NR_PIBS 3
> +
> +struct ppce500_pci_t {
> +    target_phys_addr_t registers;
> +    struct pci_outbound pob[PPCE500_PCI_NR_POBS];
> +    struct pci_inbound pib[PPCE500_PCI_NR_PIBS];
> +    uint32_t gas_time;
> +
> +    qemu_irq *pic;
> +
> +    uint32_t pcic0_cfgaddr;
> +    PCIBus *bus;
> +};
> +
> +typedef struct ppce500_pci_t ppce500_pci_t;
> +
> +static uint32_t pci_reg_read(void *opaque, target_phys_addr_t addr, int size)
> +{
> +    ppce500_pci_t *pci = opaque;
> +    unsigned long offset = addr - pci->registers, win;

I don't think this could possibly work now, since a patch went in a few
weeks back that passes "offset" to MMIO callbacks, instead of "addr".

> +    uint32_t value = 0;
> +
> +    win = offset & 0xfe0;
> +
> +    switch (win) {
> +    case 0:
> +     switch(offset & 0xC) {
> +     case PPCE500_PCI_CONFIG_ADDR:
> +         value = pci->pcic0_cfgaddr;
> +         break;
> +
> +     case PPCE500_PCI_CONFIG_DATA: {
> +         uint32_t cfgaddr = pci->pcic0_cfgaddr;
> +
> +         if (!(cfgaddr & (1<<31)))
> +             return 0xffffffff;
> +     
> +         value = pci_data_read(pci->bus, cfgaddr | (offset & 0x3), size);
> +
> +         if (size == 2)
> +             value = cpu_to_le16(value);
> +         else if (size == 4)
> +             value = cpu_to_le32(value);
> +
> +         break;
> +     }
> +     default:;
> +     }
> +     break;
> +    
> +    case PPCE500_PCI_OW1:
> +    case PPCE500_PCI_OW2:
> +    case PPCE500_PCI_OW3:
> +    case PPCE500_PCI_OW4:
> +     switch (offset & 0xC) {
> +     case PCI_POTAR: value = pci->pob[(offset >> 5) & 0x7].potar; break;
> +     case PCI_POTEAR: value = pci->pob[(offset >> 5) & 0x7].potear; break;
> +     case PCI_POWBAR: value = pci->pob[(offset >> 5) & 0x7].powbar; break;
> +     case PCI_POWAR: value = pci->pob[(offset >> 5) & 0x7].powar; break;
> +     default: break;
> +     }
> +     break;
> +
> +    case PPCE500_PCI_IW3:
> +    case PPCE500_PCI_IW2:
> +    case PPCE500_PCI_IW1:
> +     switch (offset & 0xC) {
> +     case PCI_PITAR: value = pci->pib[(offset >> 5) & 0x3].pitar; break;
> +     case PCI_PIWBAR: value = pci->pib[(offset >> 5) & 0x3].piwbar; break;
> +     case PCI_PIWBEAR: value = pci->pib[(offset >> 5) & 0x3].piwbear; break;
> +     case PCI_PIWAR: value = pci->pib[(offset >> 5) & 0x3].piwar; break;
> +     default: break;
> +     };
> +     break;
> +
> +    case PPCE500_PCI_GAS_TIMR:
> +     value = pci->gas_time;
> +     break;
> +
> +    default:
> +     break;
> +    }
> +
> +    pci_debug("%s: offset:%p -> value:%x\n", __func__, offset, value);
> +    return value;
> +}
> +
> +static uint32_t pci_reg_read1(void *opaque, target_phys_addr_t addr)
> +{
> +    return pci_reg_read(opaque, addr, 1);
> +}
> +
> +static uint32_t pci_reg_read2(void *opaque, target_phys_addr_t addr)
> +{
> +    return pci_reg_read(opaque, addr, 2);
> +}
> +
> +static uint32_t pci_reg_read4(void *opaque, target_phys_addr_t addr)
> +{
> +    return pci_reg_read(opaque, addr, 4);
> +}
> +
> +static CPUReadMemoryFunc *e500_pci_reg_read[] = {
> +    &pci_reg_read1,
> +    &pci_reg_read2,
> +    &pci_reg_read4,
> +};
> +
> +static void pci_reg_write(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value, int size)
> +{
> +    ppce500_pci_t *pci = opaque;
> +    unsigned long offset = addr - pci->registers, win;
> +
> +    pci_debug("%s: value:%x -> offset:%p(addr:%Lx - base:%Lx)\n", __func__, 
> value, offset, addr, pci->registers);
> +
> +    win = offset & 0xfe0;
> +
> +    switch (win) {
> +    case 0:
> +     switch(offset & 0xC) {
> +     case PPCE500_PCI_CONFIG_ADDR:
> +         pci->pcic0_cfgaddr = value & ~0x3;
> +         break;
> +
> +     case PPCE500_PCI_CONFIG_DATA:
> +         if (size == 2)
> +             value = le16_to_cpu(value);
> +         else if (size == 4)
> +             value = le32_to_cpu(value);
> +
> +         pci_data_write(pci->bus, pci->pcic0_cfgaddr | (offset & 0x3),
> +                         value, size);
> +         break;
> +     default:
> +         break;
> +     };
> +     break;
> +    
> +    case PPCE500_PCI_OW1:
> +    case PPCE500_PCI_OW2:
> +    case PPCE500_PCI_OW3:
> +    case PPCE500_PCI_OW4:
> +     switch (offset & 0xC) {
> +     case PCI_POTAR: pci->pob[(offset >> 5) & 0x7].potar = value; break;
> +     case PCI_POTEAR: pci->pob[(offset >> 5) & 0x7].potear = value; break;
> +     case PCI_POWBAR: pci->pob[(offset >> 5) & 0x7].powbar = value; break;
> +     case PCI_POWAR: pci->pob[(offset >> 5) & 0x7].powar = value; break;
> +     default: break;
> +     };
> +     break;
> +
> +    case PPCE500_PCI_IW3:
> +    case PPCE500_PCI_IW2:
> +    case PPCE500_PCI_IW1:
> +     switch (offset & 0xC) {
> +     case PCI_PITAR: pci->pib[(offset >> 5) & 0x3].pitar = value; break;
> +     case PCI_PIWBAR: pci->pib[(offset >> 5) & 0x3].piwbar = value; break;
> +     case PCI_PIWBEAR: pci->pib[(offset >> 5) & 0x3].piwbear = value; break;
> +     case PCI_PIWAR: pci->pib[(offset >> 5) & 0x3].piwar = value; break;
> +     default: break;
> +     };
> +     break;
> +
> +    case PPCE500_PCI_GAS_TIMR:
> +     pci->gas_time = value;
> +     break;
> +
> +    default:
> +     break;
> +    };
> +}
> +
> +static void pci_reg_write1(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    pci_reg_write(opaque, addr, value, 1);
> +}
> +
> +static void pci_reg_write2(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    pci_reg_write(opaque, addr, value, 2);
> +}
> +
> +static void pci_reg_write4(void *opaque, target_phys_addr_t addr,
> +                               uint32_t value)
> +{
> +    pci_reg_write(opaque, addr, value, 4);
> +}
> +
> +static CPUWriteMemoryFunc *e500_pci_reg_write[] = {
> +    &pci_reg_write1,
> +    &pci_reg_write2,
> +    &pci_reg_write4,
> +};
> +
> +static int mpc85xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> +    int devno = pci_dev->devfn >> 3, ret = 0;
> +
> +    switch (devno) {
> +     /* Two PCI slot */
> +     case 0x11:
> +     case 0x12:
> +         ret = (irq_num + devno - 0x10) % 4;
> +         break;
> +     default:
> +         printf("Error:%s:unknow dev number\n", __func__);
> +    }
> +
> +    pci_debug("%s: devfn %x irq %d -> %d  devno:%x\n", __func__,
> +           pci_dev->devfn, irq_num, ret, devno);
> +
> +    return ret;
> +}
> +
> +static void mpc85xx_pci_set_irq(qemu_irq *pic, int irq_num, int level)
> +{
> +    pci_debug("%s: PCI irq %d, level:%d\n", __func__, irq_num, level);
> +
> +    qemu_set_irq(pic[irq_num], level);
> +}
> +
> +PCIBus *ppce500_pci_init(qemu_irq pci_irqs[4], target_phys_addr_t registers)
> +{
> +    ppce500_pci_t *pci;
> +    PCIDevice *d;
> +    int index;
> +
> +    pci = qemu_mallocz(sizeof(ppce500_pci_t));
> +    if (!pci)
> +     return NULL;
> +
> +    pci->registers = registers;
> +    pci->pic = pci_irqs;
> +
> +    pci->bus = pci_register_bus(mpc85xx_pci_set_irq, mpc85xx_pci_map_irq,
> +                             pci_irqs, 0x88, 4);
> +    d = pci_register_device(pci->bus, "host bridge", sizeof(PCIDevice),
> +             0, NULL, NULL);
> +
> +    d->config[0x00] = 0x57; // vendor_id
> +    d->config[0x01] = 0x19;
> +    d->config[0x02] = 0x30; // device_id
> +    d->config[0x03] = 0x00;
> +    d->config[0x0a] = 0x20; // class_sub = other bridge type
> +    d->config[0x0b] = 0x0B; // class_base = PCI_bridge
> +
> +    index = cpu_register_io_memory(0, e500_pci_reg_read,
> +                 e500_pci_reg_write, pci);
> +    if (index < 0)
> +     goto free;
> +    cpu_register_physical_memory(registers, PPCE500_PCI_REG_SIZE, index);
> +
> +    /* XXX register_savevm() */

savevm() isn't hard to implement, even if you can't test it.

> +    return pci->bus;
> +
> +free:
> +    printf("%s error\n", __func__);
> +    qemu_free(pci);
> +    return NULL;
> +}
> +

I suspect the byteswapping isn't correct here, so it wouldn't work on an
LE host.

Is this PCI controller shared with any chips qemu currently implements?
If so, you should add it to those platforms (and you could test it with
plain qemu that way, since the e500 MMU isn't emulated).

-- 
Hollis Blanchard
IBM Linux Technology Center

--
To unsubscribe from this list: send the line "unsubscribe kvm-ppc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to