Re: [PATCH 3/5] qemu/kvm: MPIC and E500 PCI controller emualtion

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 2009-01-09 at 15:56 +0800, Liu Yu wrote:
> Signed-off-by: Liu Yu <yu.liu@xxxxxxxxxxxxx>
> ---
>  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.liu@xxxxxxxxxxxxx>
> + *
> + * 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.liu@xxxxxxxxxxxxx>
> + *
> + * 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 majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [KVM Development]     [KVM ARM]     [KVM ia64]     [Linux Virtualization]     [Linux USB Devel]     [Linux Video]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux