* [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
@ 2008-11-26 19:22 Hollis Blanchard
2008-12-01 16:13 ` [Qemu-devel] " Hollis Blanchard
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
0 siblings, 2 replies; 13+ messages in thread
From: Hollis Blanchard @ 2008-11-26 19:22 UTC (permalink / raw)
To: qemu-devel; +Cc: Hollis Blanchard
This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
---
This isn't yet used by the ppc405 boards qemu emulates, but it could be if
someone has a 405 firmware/kernel they're able to test with.
This patch is required for the ppc440 KVM support I have queued up, and has
been tested with that (big-endian host, big-endian guest).
---
Makefile.target | 2 +-
hw/ppc4xx.h | 7 +
hw/ppc4xx_pci.c | 370 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 378 insertions(+), 1 deletions(-)
create mode 100644 hw/ppc4xx_pci.c
diff --git a/Makefile.target b/Makefile.target
index 3cdf7db..b22c306 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -679,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
# NewWorld PowerMac
OBJS+= unin_pci.o ppc_chrp.o
# PowerPC 4xx boards
-OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
+OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
endif
ifeq ($(TARGET_BASE_ARCH), mips)
OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
index df959f8..98de2d4 100644
--- a/hw/ppc4xx.h
+++ b/hw/ppc4xx.h
@@ -25,6 +25,7 @@
#if !defined(PPC_4XX_H)
#define PPC_4XX_H
+#include "pci.h"
#include "ppc.h"
/* PowerPC 4xx core initialization */
@@ -48,4 +49,10 @@ enum {
qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
uint32_t dcr_base, int has_ssr, int has_vr);
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq *pic,
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers);
+
#endif /* !defined(PPC_4XX_H) */
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
new file mode 100644
index 0000000..c9c832a
--- /dev/null
+++ b/hw/ppc4xx_pci.c
@@ -0,0 +1,370 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+/* This file implements emulation of the 32-bit PCI controller found in some
+ * 440 SoCs, such as the 440EP. */
+
+#include "hw.h"
+
+typedef target_phys_addr_t pci_addr_t;
+#include "pci.h"
+#include "pci_host.h"
+#include "bswap.h"
+
+struct pci_master_map {
+ uint32_t la;
+ uint32_t ma;
+ uint32_t pcila;
+ uint32_t pciha;
+};
+
+struct pci_target_map {
+ uint32_t ms;
+ uint32_t la;
+ uint32_t bar;
+};
+
+#define PPC44x_PCI_NR_PMMS 3
+#define PPC44x_PCI_NR_PTMS 2
+
+struct ppc4xx_pci_t {
+ target_phys_addr_t config_space;
+ target_phys_addr_t registers;
+ struct pci_master_map pmm[PPC44x_PCI_NR_PMMS];
+ struct pci_target_map ptm[PPC44x_PCI_NR_PTMS];
+
+ unsigned int pmm_offset_flags;
+ qemu_irq *pic;
+
+ PCIHostState pci_state;
+};
+typedef struct ppc4xx_pci_t ppc4xx_pci_t;
+
+#define PCIC0_CFGADDR 0x0
+#define PCIC0_CFGDATA 0x4
+
+/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
+ * PCI accesses. */
+#define PCIL0_PMM0LA 0x0
+#define PCIL0_PMM0MA 0x4
+#define PCIL0_PMM0PCILA 0x8
+#define PCIL0_PMM0PCIHA 0xc
+#define PCIL0_PMM1LA 0x10
+#define PCIL0_PMM1MA 0x14
+#define PCIL0_PMM1PCILA 0x18
+#define PCIL0_PMM1PCIHA 0x1c
+#define PCIL0_PMM2LA 0x20
+#define PCIL0_PMM2MA 0x24
+#define PCIL0_PMM2PCILA 0x28
+#define PCIL0_PMM2PCIHA 0x2c
+
+/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
+ * PLB accesses. */
+#define PCIL0_PTM1MS 0x30
+#define PCIL0_PTM1LA 0x34
+#define PCIL0_PTM2MS 0x38
+#define PCIL0_PTM2LA 0x3c
+#define PCI_REG_SIZE 0x40
+
+
+static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
+{
+ ppc4xx_pci_t *ppc4xx_pci = opaque;
+
+ return ppc4xx_pci->pci_state.config_reg;
+}
+
+static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = {
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+};
+
+static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ ppc4xx_pci_t *ppc4xx_pci = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ ppc4xx_pci->pci_state.config_reg = value & ~0x3;
+}
+
+static CPUWriteMemoryFunc *pci4xx_cfgaddr_write[] = {
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+};
+
+static CPUReadMemoryFunc *pci4xx_cfgdata_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+static CPUWriteMemoryFunc *pci4xx_cfgdata_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ struct ppc4xx_pci_t *pci = opaque;
+ unsigned long offset = addr - pci->registers;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ /* We ignore all target attempts at PCI configuration, effectively
+ * assuming a bidirectional 1:1 mapping of PLB and PCI space. */
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ pci->pmm[0].la = value;
+ break;
+ case PCIL0_PMM0MA:
+ pci->pmm[0].ma = value;
+ break;
+ case PCIL0_PMM0PCIHA:
+ pci->pmm[0].pciha = value;
+ break;
+ case PCIL0_PMM0PCILA:
+ pci->pmm[0].pcila = value;
+ break;
+
+ case PCIL0_PMM1LA:
+ pci->pmm[1].la = value;
+ break;
+ case PCIL0_PMM1MA:
+ pci->pmm[1].ma = value;
+ break;
+ case PCIL0_PMM1PCIHA:
+ pci->pmm[1].pciha = value;
+ break;
+ case PCIL0_PMM1PCILA:
+ pci->pmm[1].pcila = value;
+ break;
+
+ case PCIL0_PMM2LA:
+ pci->pmm[2].la = value;
+ break;
+ case PCIL0_PMM2MA:
+ pci->pmm[2].ma = value;
+ break;
+ case PCIL0_PMM2PCIHA:
+ pci->pmm[2].pciha = value;
+ break;
+ case PCIL0_PMM2PCILA:
+ pci->pmm[2].pcila = value;
+ break;
+
+ case PCIL0_PTM1MS:
+ pci->ptm[0].ms = value;
+ break;
+ case PCIL0_PTM1LA:
+ pci->ptm[0].la = value;
+ break;
+ case PCIL0_PTM2MS:
+ pci->ptm[1].ms = value;
+ break;
+ case PCIL0_PTM2LA:
+ pci->ptm[1].la = value;
+ break;
+
+ default:
+ printf(" write to unhandled PCI internal register 0x%lx\n", offset);
+ break;
+ }
+}
+
+static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t addr)
+{
+ struct ppc4xx_pci_t *pci = opaque;
+ unsigned long offset = addr - pci->registers;
+ uint32_t value;
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ value = pci->pmm[0].la;
+ break;
+ case PCIL0_PMM0MA:
+ value = pci->pmm[0].ma;
+ break;
+ case PCIL0_PMM0PCIHA:
+ value = pci->pmm[0].pciha;
+ break;
+ case PCIL0_PMM0PCILA:
+ value = pci->pmm[0].pcila;
+ break;
+
+ case PCIL0_PMM1LA:
+ value = pci->pmm[1].la;
+ break;
+ case PCIL0_PMM1MA:
+ value = pci->pmm[1].ma;
+ break;
+ case PCIL0_PMM1PCIHA:
+ value = pci->pmm[1].pciha;
+ break;
+ case PCIL0_PMM1PCILA:
+ value = pci->pmm[1].pcila;
+ break;
+
+ case PCIL0_PMM2LA:
+ value = pci->pmm[2].la;
+ break;
+ case PCIL0_PMM2MA:
+ value = pci->pmm[2].ma;
+ break;
+ case PCIL0_PMM2PCIHA:
+ value = pci->pmm[2].pciha;
+ break;
+ case PCIL0_PMM2PCILA:
+ value = pci->pmm[2].pcila;
+ break;
+
+ case PCIL0_PTM1MS:
+ value = pci->ptm[0].ms;
+ break;
+ case PCIL0_PTM1LA:
+ value = pci->ptm[0].la;
+ break;
+ case PCIL0_PTM2MS:
+ value = pci->ptm[1].ms;
+ break;
+ case PCIL0_PTM2LA:
+ value = pci->ptm[1].la;
+ break;
+
+ default:
+ printf(" read from invalid PCI internal register 0x%lx\n", offset);
+ value = 0;
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ return value;
+}
+
+static CPUReadMemoryFunc *pci_reg_read[] = {
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+};
+
+static CPUWriteMemoryFunc *pci_reg_write[] = {
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+};
+
+static int bamboo_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot = pci_dev->devfn >> 3;
+
+#if 0
+ printf("### %s: devfn %x irq %d -> %d\n", __func__,
+ pci_dev->devfn, irq_num, slot+1);
+#endif
+
+ /* All pins from each slot are tied to a single board IRQ (2-5) */
+ /* XXX Needs some abstracting for boards other than Bamboo. */
+ return slot + 1;
+}
+
+static void bamboo_pci_set_irq(qemu_irq *pic, int irq_num, int level)
+{
+#if 0
+ printf("### %s: PCI irq %d, UIC irq %d\n", __func__, irq_num, 30 - irq_num);
+#endif
+
+ /* Board IRQs 2-5 are connected to UIC IRQs 28-25 */
+ /* XXX Needs some abstracting for boards other than Bamboo. */
+ qemu_set_irq(pic[30-irq_num], level);
+}
+
+/* XXX Interrupt acknowledge cycles not supported. */
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq *pic,
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers)
+{
+ ppc4xx_pci_t *controller;
+ PCIDevice *d;
+ int index;
+
+ controller = qemu_mallocz(sizeof(ppc4xx_pci_t));
+ if (!controller)
+ return NULL;
+
+ controller->config_space = config_space;
+ controller->registers = registers;
+ controller->pic = pic;
+
+ controller->pci_state.bus = pci_register_bus(bamboo_pci_set_irq,
+ bamboo_pci_map_irq,
+ pic, 0, 4);
+
+ d = pci_register_device(controller->pci_state.bus, "host bridge",
+ sizeof(PCIDevice), 0, NULL, NULL);
+ d->config[0x00] = 0x14; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x7f; // device_id
+ d->config[0x03] = 0x02;
+ d->config[0x0a] = 0x80; // class_sub = other bridge type
+ d->config[0x0b] = 0x06; // class_base = PCI_bridge
+
+ /* CFGADDR */
+ index = cpu_register_io_memory(0, pci4xx_cfgaddr_read,
+ pci4xx_cfgaddr_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index);
+
+ /* CFGDATA */
+ index = cpu_register_io_memory(0, pci4xx_cfgdata_read,
+ pci4xx_cfgdata_write,
+ &controller->pci_state);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index);
+
+ /* Internal registers */
+ index = cpu_register_io_memory(0, pci_reg_read, pci_reg_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers, PCI_REG_SIZE, index);
+
+ /* XXX register_savevm() */
+
+ return controller->pci_state.bus;
+
+free:
+ printf("%s error\n", __func__);
+ qemu_free(controller);
+ return NULL;
+}
--
1.5.6.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [Qemu-devel] Re: [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-11-26 19:22 [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation Hollis Blanchard
@ 2008-12-01 16:13 ` Hollis Blanchard
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
1 sibling, 0 replies; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-01 16:13 UTC (permalink / raw)
To: qemu-devel; +Cc: Jean-Christophe PLAGNIOL-VILLARD, Aurelien Jarno
On Wed, 2008-11-26 at 13:22 -0600, Hollis Blanchard wrote:
> This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
>
> Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
> ---
> This isn't yet used by the ppc405 boards qemu emulates, but it could be if
> someone has a 405 firmware/kernel they're able to test with.
>
> This patch is required for the ppc440 KVM support I have queued up, and has
> been tested with that (big-endian host, big-endian guest).
Hi, any comments on this patch?
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-11-26 19:22 [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation Hollis Blanchard
2008-12-01 16:13 ` [Qemu-devel] " Hollis Blanchard
@ 2008-12-01 18:15 ` Blue Swirl
2008-12-01 19:17 ` Hollis Blanchard
` (2 more replies)
1 sibling, 3 replies; 13+ messages in thread
From: Blue Swirl @ 2008-12-01 18:15 UTC (permalink / raw)
To: qemu-devel; +Cc: Hollis Blanchard
On 11/26/08, Hollis Blanchard <hollisb@us.ibm.com> wrote:
> This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
>
> Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
> ---
> This isn't yet used by the ppc405 boards qemu emulates, but it could be if
> someone has a 405 firmware/kernel they're able to test with.
The device can't be tested unless it's used by some board.
> +#if 0
> + printf("### %s: devfn %x irq %d -> %d\n", __func__,
> + pci_dev->devfn, irq_num, slot+1);
> +#endif
You could introduce a DPRINTF macro, like for example in slavio_intctl.c.
> + /* Board IRQs 2-5 are connected to UIC IRQs 28-25 */
> + /* XXX Needs some abstracting for boards other than Bamboo. */
> + qemu_set_irq(pic[30-irq_num], level);
> +}
The IRQs should be set up at the board level and then passed to the device.
> + /* XXX register_savevm() */
And register_reset?
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
@ 2008-12-01 19:17 ` Hollis Blanchard
2008-12-01 19:59 ` Hollis Blanchard
2008-12-02 20:02 ` [Qemu-devel] [PATCH 1/1] " Hollis Blanchard
2 siblings, 0 replies; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-01 19:17 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel
Hi Blue, thanks for your comments.
On Mon, 2008-12-01 at 20:15 +0200, Blue Swirl wrote:
> On 11/26/08, Hollis Blanchard <hollisb@us.ibm.com> wrote:
> > This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
> >
> > Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
> > ---
> > This isn't yet used by the ppc405 boards qemu emulates, but it could be if
> > someone has a 405 firmware/kernel they're able to test with.
>
> The device can't be tested unless it's used by some board.
I have tested the device using KVM. Since PCI support is a
self-contained patch that could be very useful to other qemu users, I
posted it first.
The problem is that none of the 405 boards emulated by qemu are
functional enough for me to run a kernel, so I can't test those.
However, Jean-Christophe Plagniol-Villard is planning to test this
(though I'm not sure how).
If you'd prefer, I can post this patch only after the PowerPC KVM
support has been merged.
> > +#if 0
> > + printf("### %s: devfn %x irq %d -> %d\n", __func__,
> > + pci_dev->devfn, irq_num, slot+1);
> > +#endif
>
> You could introduce a DPRINTF macro, like for example in slavio_intctl.c.
Sure.
> > + /* Board IRQs 2-5 are connected to UIC IRQs 28-25 */
> > + /* XXX Needs some abstracting for boards other than Bamboo. */
> > + qemu_set_irq(pic[30-irq_num], level);
> > +}
>
> The IRQs should be set up at the board level and then passed to the device.
OK, I'll give that a try.
> > + /* XXX register_savevm() */
>
> And register_reset?
I didn't know about that one. Looks easy enough.
I will send an updated patch once I've made these changes.
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
2008-12-01 19:17 ` Hollis Blanchard
@ 2008-12-01 19:59 ` Hollis Blanchard
2008-12-01 20:51 ` Blue Swirl
2008-12-02 20:02 ` [Qemu-devel] [PATCH 1/1] " Hollis Blanchard
2 siblings, 1 reply; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-01 19:59 UTC (permalink / raw)
To: Blue Swirl; +Cc: qemu-devel
On Mon, 2008-12-01 at 20:15 +0200, Blue Swirl wrote:
>
> > + /* Board IRQs 2-5 are connected to UIC IRQs 28-25 */
> > + /* XXX Needs some abstracting for boards other than Bamboo. */
> > + qemu_set_irq(pic[30-irq_num], level);
> > +}
>
> The IRQs should be set up at the board level and then passed to the
> device.
As far as I can see, this isn't going to work. The functions that need
access to this information are the driver's map_irq() and set_irq(), and
neither of these callbacks is provided a pointer to the driver-specific
structure where I could store the board irqs.
Can you elaborate on your idea, or point me to existing code that
implements it?
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-01 19:59 ` Hollis Blanchard
@ 2008-12-01 20:51 ` Blue Swirl
0 siblings, 0 replies; 13+ messages in thread
From: Blue Swirl @ 2008-12-01 20:51 UTC (permalink / raw)
To: Hollis Blanchard; +Cc: qemu-devel
On 12/1/08, Hollis Blanchard <hollisb@us.ibm.com> wrote:
> On Mon, 2008-12-01 at 20:15 +0200, Blue Swirl wrote:
> >
>
> > > + /* Board IRQs 2-5 are connected to UIC IRQs 28-25 */
> > > + /* XXX Needs some abstracting for boards other than Bamboo. */
> > > + qemu_set_irq(pic[30-irq_num], level);
> > > +}
> >
> > The IRQs should be set up at the board level and then passed to the
> > device.
>
>
> As far as I can see, this isn't going to work. The functions that need
> access to this information are the driver's map_irq() and set_irq(), and
> neither of these callbacks is provided a pointer to the driver-specific
> structure where I could store the board irqs.
>
> Can you elaborate on your idea, or point me to existing code that
> implements it?
The board creates four IRQs which it passes to the device. These are
not directly connected to PIC. Instead the request function of the IRQ
(defined in the board file) raises whatever PIC IRQs the board wants.
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH 1/1] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
2008-12-01 19:17 ` Hollis Blanchard
2008-12-01 19:59 ` Hollis Blanchard
@ 2008-12-02 20:02 ` Hollis Blanchard
2008-12-02 20:22 ` Anthony Liguori
2 siblings, 1 reply; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-02 20:02 UTC (permalink / raw)
To: qemu-devel; +Cc: plagnioj, Hollis Blanchard, aurelien
This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
---
This isn't yet used by the ppc405 boards qemu emulates, but it could be if
someone has a 405 firmware/kernel they're able to test with.
This patch is required for the ppc440 KVM support I have queued up, and has
been tested with that (big-endian host, big-endian guest).
Changes from v1: implement suggestions from Blue Swirl
- Create and use a DPRINTF macro.
- Register a reset function.
- Refactor the PCI interrupt handling so that the caller passes in a qemu_irq
array. Not all boards will be able to use the simple mapping implemented
here, but 405EP (which is currently emulated by qemu) can.
---
Makefile.target | 2 +-
hw/ppc4xx.h | 7 +
hw/ppc4xx_pci.c | 371 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 379 insertions(+), 1 deletions(-)
create mode 100644 hw/ppc4xx_pci.c
diff --git a/Makefile.target b/Makefile.target
index 3cdf7db..b22c306 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -679,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
# NewWorld PowerMac
OBJS+= unin_pci.o ppc_chrp.o
# PowerPC 4xx boards
-OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
+OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
endif
ifeq ($(TARGET_BASE_ARCH), mips)
OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
index df959f8..ef143ef 100644
--- a/hw/ppc4xx.h
+++ b/hw/ppc4xx.h
@@ -25,6 +25,7 @@
#if !defined(PPC_4XX_H)
#define PPC_4XX_H
+#include "pci.h"
#include "ppc.h"
/* PowerPC 4xx core initialization */
@@ -48,4 +49,10 @@ enum {
qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
uint32_t dcr_base, int has_ssr, int has_vr);
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers);
+
#endif /* !defined(PPC_4XX_H) */
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
new file mode 100644
index 0000000..f8493ed
--- /dev/null
+++ b/hw/ppc4xx_pci.c
@@ -0,0 +1,371 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+/* This file implements emulation of the 32-bit PCI controller found in some
+ * 440 SoCs, such as the 440EP. */
+
+#include "hw.h"
+
+typedef target_phys_addr_t pci_addr_t;
+#include "pci.h"
+#include "pci_host.h"
+#include "bswap.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTF(fmt, args...) do { printf(fmt, ##args); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif /* DEBUG */
+
+struct pci_master_map {
+ uint32_t la;
+ uint32_t ma;
+ uint32_t pcila;
+ uint32_t pciha;
+};
+
+struct pci_target_map {
+ uint32_t ms;
+ uint32_t la;
+ uint32_t bar;
+};
+
+#define PPC44x_PCI_NR_PMMS 3
+#define PPC44x_PCI_NR_PTMS 2
+
+struct ppc4xx_pci_t {
+ struct pci_master_map pmm[PPC44x_PCI_NR_PMMS];
+ struct pci_target_map ptm[PPC44x_PCI_NR_PTMS];
+
+ PCIHostState pci_state;
+};
+typedef struct ppc4xx_pci_t ppc4xx_pci_t;
+
+#define PCIC0_CFGADDR 0x0
+#define PCIC0_CFGDATA 0x4
+
+/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
+ * PCI accesses. */
+#define PCIL0_PMM0LA 0x0
+#define PCIL0_PMM0MA 0x4
+#define PCIL0_PMM0PCILA 0x8
+#define PCIL0_PMM0PCIHA 0xc
+#define PCIL0_PMM1LA 0x10
+#define PCIL0_PMM1MA 0x14
+#define PCIL0_PMM1PCILA 0x18
+#define PCIL0_PMM1PCIHA 0x1c
+#define PCIL0_PMM2LA 0x20
+#define PCIL0_PMM2MA 0x24
+#define PCIL0_PMM2PCILA 0x28
+#define PCIL0_PMM2PCIHA 0x2c
+
+/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
+ * PLB accesses. */
+#define PCIL0_PTM1MS 0x30
+#define PCIL0_PTM1LA 0x34
+#define PCIL0_PTM2MS 0x38
+#define PCIL0_PTM2LA 0x3c
+#define PCI_REG_SIZE 0x40
+
+
+static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
+{
+ ppc4xx_pci_t *ppc4xx_pci = opaque;
+
+ return ppc4xx_pci->pci_state.config_reg;
+}
+
+static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = {
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+};
+
+static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ ppc4xx_pci_t *ppc4xx_pci = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ ppc4xx_pci->pci_state.config_reg = value & ~0x3;
+}
+
+static CPUWriteMemoryFunc *pci4xx_cfgaddr_write[] = {
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+};
+
+static CPUReadMemoryFunc *pci4xx_cfgdata_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+static CPUWriteMemoryFunc *pci4xx_cfgdata_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ struct ppc4xx_pci_t *pci = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ /* We ignore all target attempts at PCI configuration, effectively
+ * assuming a bidirectional 1:1 mapping of PLB and PCI space. */
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ pci->pmm[0].la = value;
+ break;
+ case PCIL0_PMM0MA:
+ pci->pmm[0].ma = value;
+ break;
+ case PCIL0_PMM0PCIHA:
+ pci->pmm[0].pciha = value;
+ break;
+ case PCIL0_PMM0PCILA:
+ pci->pmm[0].pcila = value;
+ break;
+
+ case PCIL0_PMM1LA:
+ pci->pmm[1].la = value;
+ break;
+ case PCIL0_PMM1MA:
+ pci->pmm[1].ma = value;
+ break;
+ case PCIL0_PMM1PCIHA:
+ pci->pmm[1].pciha = value;
+ break;
+ case PCIL0_PMM1PCILA:
+ pci->pmm[1].pcila = value;
+ break;
+
+ case PCIL0_PMM2LA:
+ pci->pmm[2].la = value;
+ break;
+ case PCIL0_PMM2MA:
+ pci->pmm[2].ma = value;
+ break;
+ case PCIL0_PMM2PCIHA:
+ pci->pmm[2].pciha = value;
+ break;
+ case PCIL0_PMM2PCILA:
+ pci->pmm[2].pcila = value;
+ break;
+
+ case PCIL0_PTM1MS:
+ pci->ptm[0].ms = value;
+ break;
+ case PCIL0_PTM1LA:
+ pci->ptm[0].la = value;
+ break;
+ case PCIL0_PTM2MS:
+ pci->ptm[1].ms = value;
+ break;
+ case PCIL0_PTM2LA:
+ pci->ptm[1].la = value;
+ break;
+
+ default:
+ printf("%s: unhandled PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ break;
+ }
+}
+
+static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t offset)
+{
+ struct ppc4xx_pci_t *pci = opaque;
+ uint32_t value;
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ value = pci->pmm[0].la;
+ break;
+ case PCIL0_PMM0MA:
+ value = pci->pmm[0].ma;
+ break;
+ case PCIL0_PMM0PCIHA:
+ value = pci->pmm[0].pciha;
+ break;
+ case PCIL0_PMM0PCILA:
+ value = pci->pmm[0].pcila;
+ break;
+
+ case PCIL0_PMM1LA:
+ value = pci->pmm[1].la;
+ break;
+ case PCIL0_PMM1MA:
+ value = pci->pmm[1].ma;
+ break;
+ case PCIL0_PMM1PCIHA:
+ value = pci->pmm[1].pciha;
+ break;
+ case PCIL0_PMM1PCILA:
+ value = pci->pmm[1].pcila;
+ break;
+
+ case PCIL0_PMM2LA:
+ value = pci->pmm[2].la;
+ break;
+ case PCIL0_PMM2MA:
+ value = pci->pmm[2].ma;
+ break;
+ case PCIL0_PMM2PCIHA:
+ value = pci->pmm[2].pciha;
+ break;
+ case PCIL0_PMM2PCILA:
+ value = pci->pmm[2].pcila;
+ break;
+
+ case PCIL0_PTM1MS:
+ value = pci->ptm[0].ms;
+ break;
+ case PCIL0_PTM1LA:
+ value = pci->ptm[0].la;
+ break;
+ case PCIL0_PTM2MS:
+ value = pci->ptm[1].ms;
+ break;
+ case PCIL0_PTM2LA:
+ value = pci->ptm[1].la;
+ break;
+
+ default:
+ printf("%s: invalid PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ value = 0;
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ return value;
+}
+
+static CPUReadMemoryFunc *pci_reg_read[] = {
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+};
+
+static CPUWriteMemoryFunc *pci_reg_write[] = {
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+};
+
+static void ppc4xx_pci_reset(void *opaque)
+{
+ struct ppc4xx_pci_t *pci = opaque;
+
+ memset(pci->pmm, 0, sizeof(pci->pmm));
+ memset(pci->ptm, 0, sizeof(pci->ptm));
+}
+
+/* On Bamboo, all pins from each slot are tied to a single board IRQ. This
+ * may need further refactoring for other boards. */
+static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot = pci_dev->devfn >> 3;
+
+ DPRINTF("%s: devfn %x irq %d -> %d\n", __func__,
+ pci_dev->devfn, irq_num, slot);
+
+ return slot - 1;
+}
+
+static void ppc4xx_pci_set_irq(qemu_irq *pci_irqs, int irq_num, int level)
+{
+ DPRINTF("%s: PCI irq %d\n", __func__, irq_num);
+ qemu_set_irq(pci_irqs[irq_num], level);
+}
+
+/* XXX Interrupt acknowledge cycles not supported. */
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers)
+{
+ ppc4xx_pci_t *controller;
+ PCIDevice *d;
+ int index;
+
+ controller = qemu_mallocz(sizeof(ppc4xx_pci_t));
+ if (!controller)
+ return NULL;
+
+ controller->pci_state.bus = pci_register_bus(ppc4xx_pci_set_irq,
+ ppc4xx_pci_map_irq,
+ pci_irqs, 0, 4);
+
+ d = pci_register_device(controller->pci_state.bus, "host bridge",
+ sizeof(PCIDevice), 0, NULL, NULL);
+ d->config[0x00] = 0x14; // vendor_id
+ d->config[0x01] = 0x10;
+ d->config[0x02] = 0x7f; // device_id
+ d->config[0x03] = 0x02;
+ d->config[0x0a] = 0x80; // class_sub = other bridge type
+ d->config[0x0b] = 0x06; // class_base = PCI_bridge
+
+ /* CFGADDR */
+ index = cpu_register_io_memory(0, pci4xx_cfgaddr_read,
+ pci4xx_cfgaddr_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index);
+
+ /* CFGDATA */
+ index = cpu_register_io_memory(0, pci4xx_cfgdata_read,
+ pci4xx_cfgdata_write,
+ &controller->pci_state);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index);
+
+ /* Internal registers */
+ index = cpu_register_io_memory(0, pci_reg_read, pci_reg_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers, PCI_REG_SIZE, index);
+
+ /* XXX register_savevm() */
+
+ qemu_register_reset(ppc4xx_pci_reset, controller);
+
+ return controller->pci_state.bus;
+
+free:
+ printf("%s error\n", __func__);
+ qemu_free(controller);
+ return NULL;
+}
--
1.5.6.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH 1/1] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 20:02 ` [Qemu-devel] [PATCH 1/1] " Hollis Blanchard
@ 2008-12-02 20:22 ` Anthony Liguori
2008-12-02 20:43 ` Hollis Blanchard
2008-12-02 21:30 ` [Qemu-devel] [PATCH] [v3] " Hollis Blanchard
0 siblings, 2 replies; 13+ messages in thread
From: Anthony Liguori @ 2008-12-02 20:22 UTC (permalink / raw)
To: qemu-devel; +Cc: plagnioj, aurelien, Hollis Blanchard
Hollis Blanchard wrote:
> This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
>
> Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
>
> --- /dev/null
> +++ b/hw/ppc4xx_pci.c
> @@ -0,0 +1,371 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright IBM Corp. 2008
> + *
> + * Authors: Hollis Blanchard <hollisb@us.ibm.com>
> + */
> +
> +/* This file implements emulation of the 32-bit PCI controller found in some
> + * 440 SoCs, such as the 440EP. */
> +
> +#include "hw.h"
> +
> +typedef target_phys_addr_t pci_addr_t;
>
Nice :-)
> +#include "pci.h"
> +#include "pci_host.h"
> +#include "bswap.h"
> +
> +#undef DEBUG
> +#ifdef DEBUG
> +#define DPRINTF(fmt, args...) do { printf(fmt, ##args); } while (0)
> +#else
> +#define DPRINTF(fmt, args...)
> +#endif /* DEBUG */
>
This is a GCC-ism that's deprecated. The proper syntax (C99) is:
#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
> +struct pci_master_map {
> + uint32_t la;
> + uint32_t ma;
> + uint32_t pcila;
> + uint32_t pciha;
> +};
> +
> +struct pci_target_map {
> + uint32_t ms;
> + uint32_t la;
> + uint32_t bar;
> +};
> +
> +#define PPC44x_PCI_NR_PMMS 3
> +#define PPC44x_PCI_NR_PTMS 2
> +
> +struct ppc4xx_pci_t {
> + struct pci_master_map pmm[PPC44x_PCI_NR_PMMS];
> + struct pci_target_map ptm[PPC44x_PCI_NR_PTMS];
> +
> + PCIHostState pci_state;
> +};
> +typedef struct ppc4xx_pci_t ppc4xx_pci_t;
>
It would be better to use QEMU style type naming.
> +#define PCIC0_CFGADDR 0x0
> +#define PCIC0_CFGDATA 0x4
> +
> +/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
> + * PCI accesses. */
> +#define PCIL0_PMM0LA 0x0
> +#define PCIL0_PMM0MA 0x4
> +#define PCIL0_PMM0PCILA 0x8
> +#define PCIL0_PMM0PCIHA 0xc
> +#define PCIL0_PMM1LA 0x10
> +#define PCIL0_PMM1MA 0x14
> +#define PCIL0_PMM1PCILA 0x18
> +#define PCIL0_PMM1PCIHA 0x1c
> +#define PCIL0_PMM2LA 0x20
> +#define PCIL0_PMM2MA 0x24
> +#define PCIL0_PMM2PCILA 0x28
> +#define PCIL0_PMM2PCIHA 0x2c
> +
> +/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
> + * PLB accesses. */
> +#define PCIL0_PTM1MS 0x30
> +#define PCIL0_PTM1LA 0x34
> +#define PCIL0_PTM2MS 0x38
> +#define PCIL0_PTM2LA 0x3c
> +#define PCI_REG_SIZE 0x40
> +
> +
> +static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
> +{
> + ppc4xx_pci_t *ppc4xx_pci = opaque;
> +
> + return ppc4xx_pci->pci_state.config_reg;
> +}
> +
> +static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = {
> + &pci4xx_cfgaddr_readl,
> + &pci4xx_cfgaddr_readl,
> + &pci4xx_cfgaddr_readl,
> +};
> +
> +static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + ppc4xx_pci_t *ppc4xx_pci = opaque;
> +
> +#ifdef TARGET_WORDS_BIGENDIAN
> + value = bswap32(value);
> +#endif
>
>
Is this byte swapping correct?
> +
> + /* XXX register_savevm() */
>
Should be easy enough to add a register_savevm() function, no?
Regards,
Anthony Liguori
> + qemu_register_reset(ppc4xx_pci_reset, controller);
> +
> + return controller->pci_state.bus;
> +
> +free:
> + printf("%s error\n", __func__);
> + qemu_free(controller);
> + return NULL;
> +}
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH 1/1] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 20:22 ` Anthony Liguori
@ 2008-12-02 20:43 ` Hollis Blanchard
2008-12-02 22:13 ` Aurelien Jarno
2008-12-02 21:30 ` [Qemu-devel] [PATCH] [v3] " Hollis Blanchard
1 sibling, 1 reply; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-02 20:43 UTC (permalink / raw)
To: Anthony Liguori; +Cc: plagnioj, qemu-devel, aurelien
On Tue, 2008-12-02 at 14:22 -0600, Anthony Liguori wrote:
> Hollis Blanchard wrote:
>
> > +#include "pci.h"
> > +#include "pci_host.h"
> > +#include "bswap.h"
> > +
> > +#undef DEBUG
> > +#ifdef DEBUG
> > +#define DPRINTF(fmt, args...) do { printf(fmt, ##args); } while (0)
> > +#else
> > +#define DPRINTF(fmt, args...)
> > +#endif /* DEBUG */
> >
>
> This is a GCC-ism that's deprecated. The proper syntax (C99) is:
>
> #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
Will do.
> > +struct ppc4xx_pci_t {
> > + struct pci_master_map pmm[PPC44x_PCI_NR_PMMS];
> > + struct pci_target_map ptm[PPC44x_PCI_NR_PTMS];
> > +
> > + PCIHostState pci_state;
> > +};
> > +typedef struct ppc4xx_pci_t ppc4xx_pci_t;
> >
>
> It would be better to use QEMU style type naming.
I copied this style from ppc405_uc.c, but I will ChangeItLikeThis.
> > +static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
> > + uint32_t value)
> > +{
> > + ppc4xx_pci_t *ppc4xx_pci = opaque;
> > +
> > +#ifdef TARGET_WORDS_BIGENDIAN
> > + value = bswap32(value);
> > +#endif
> >
> >
>
> Is this byte swapping correct?
I hate this byte swapping, because as we've discussed at great length in
the past, "TARGET_WORDS_BIGENDIAN" never should have existed.
That said, as far as I can tell it is correct for the current state of
qemu. I changed it to this style due to Aurelien's previous request. As
I noted in the patch description, I can only test on
big-endian/big-endian guest/host.
> > +
> > + /* XXX register_savevm() */
> >
>
> Should be easy enough to add a register_savevm() function, no?
It should be, but since exactly 0 other PPC 4xx devices implement this,
I won't be able to test it. Should I implement it and leave a comment in
there that says "untested"?
--
Hollis Blanchard
IBM Linux Technology Center
^ permalink raw reply [flat|nested] 13+ messages in thread
* [Qemu-devel] [PATCH] [v3] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 20:22 ` Anthony Liguori
2008-12-02 20:43 ` Hollis Blanchard
@ 2008-12-02 21:30 ` Hollis Blanchard
2008-12-02 22:01 ` Anthony Liguori
2008-12-02 23:53 ` Aurelien Jarno
1 sibling, 2 replies; 13+ messages in thread
From: Hollis Blanchard @ 2008-12-02 21:30 UTC (permalink / raw)
To: qemu-devel; +Cc: plagnioj, Hollis Blanchard, aurelien
This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
---
This isn't yet used by the ppc405 boards qemu emulates, but it could be if
someone has a 405 firmware/kernel they're able to test with.
This patch is required for the ppc440 KVM support I have queued up, and has
been tested with that (big-endian host, big-endian guest).
Changes from v2: implement suggestions from Anthony Liguori
- Use C99 style for DPRINTF.
- Register (untested) load and save functions.
- Cosmetic changes and renaming.
---
Makefile.target | 2 +-
hw/ppc4xx.h | 7 +
hw/ppc4xx_pci.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 427 insertions(+), 1 deletions(-)
create mode 100644 hw/ppc4xx_pci.c
diff --git a/Makefile.target b/Makefile.target
index 3cdf7db..b22c306 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -679,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
# NewWorld PowerMac
OBJS+= unin_pci.o ppc_chrp.o
# PowerPC 4xx boards
-OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
+OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
endif
ifeq ($(TARGET_BASE_ARCH), mips)
OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
index df959f8..ef143ef 100644
--- a/hw/ppc4xx.h
+++ b/hw/ppc4xx.h
@@ -25,6 +25,7 @@
#if !defined(PPC_4XX_H)
#define PPC_4XX_H
+#include "pci.h"
#include "ppc.h"
/* PowerPC 4xx core initialization */
@@ -48,4 +49,10 @@ enum {
qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
uint32_t dcr_base, int has_ssr, int has_vr);
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers);
+
#endif /* !defined(PPC_4XX_H) */
diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
new file mode 100644
index 0000000..c76ec19
--- /dev/null
+++ b/hw/ppc4xx_pci.c
@@ -0,0 +1,419 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Copyright IBM Corp. 2008
+ *
+ * Authors: Hollis Blanchard <hollisb@us.ibm.com>
+ */
+
+/* This file implements emulation of the 32-bit PCI controller found in some
+ * 4xx SoCs, such as the 440EP. */
+
+#include "hw.h"
+
+typedef target_phys_addr_t pci_addr_t;
+#include "pci.h"
+#include "pci_host.h"
+#include "bswap.h"
+
+#undef DEBUG
+#ifdef DEBUG
+#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, args...)
+#endif /* DEBUG */
+
+struct PCIMasterMap {
+ uint32_t la;
+ uint32_t ma;
+ uint32_t pcila;
+ uint32_t pciha;
+};
+
+struct PCITargetMap {
+ uint32_t ms;
+ uint32_t la;
+};
+
+#define PPC4xx_PCI_NR_PMMS 3
+#define PPC4xx_PCI_NR_PTMS 2
+
+struct PPC4xxPCIState {
+ struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS];
+ struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS];
+
+ PCIHostState pci_state;
+ PCIDevice *pci_dev;
+};
+typedef struct PPC4xxPCIState PPC4xxPCIState;
+
+#define PCIC0_CFGADDR 0x0
+#define PCIC0_CFGDATA 0x4
+
+/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
+ * PCI accesses. */
+#define PCIL0_PMM0LA 0x0
+#define PCIL0_PMM0MA 0x4
+#define PCIL0_PMM0PCILA 0x8
+#define PCIL0_PMM0PCIHA 0xc
+#define PCIL0_PMM1LA 0x10
+#define PCIL0_PMM1MA 0x14
+#define PCIL0_PMM1PCILA 0x18
+#define PCIL0_PMM1PCIHA 0x1c
+#define PCIL0_PMM2LA 0x20
+#define PCIL0_PMM2MA 0x24
+#define PCIL0_PMM2PCILA 0x28
+#define PCIL0_PMM2PCIHA 0x2c
+
+/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
+ * PLB accesses. */
+#define PCIL0_PTM1MS 0x30
+#define PCIL0_PTM1LA 0x34
+#define PCIL0_PTM2MS 0x38
+#define PCIL0_PTM2LA 0x3c
+#define PCI_REG_SIZE 0x40
+
+
+static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
+{
+ PPC4xxPCIState *ppc4xx_pci = opaque;
+
+ return ppc4xx_pci->pci_state.config_reg;
+}
+
+static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = {
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+ &pci4xx_cfgaddr_readl,
+};
+
+static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
+ uint32_t value)
+{
+ PPC4xxPCIState *ppc4xx_pci = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ ppc4xx_pci->pci_state.config_reg = value & ~0x3;
+}
+
+static CPUWriteMemoryFunc *pci4xx_cfgaddr_write[] = {
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+ &pci4xx_cfgaddr_writel,
+};
+
+static CPUReadMemoryFunc *pci4xx_cfgdata_read[] = {
+ &pci_host_data_readb,
+ &pci_host_data_readw,
+ &pci_host_data_readl,
+};
+
+static CPUWriteMemoryFunc *pci4xx_cfgdata_write[] = {
+ &pci_host_data_writeb,
+ &pci_host_data_writew,
+ &pci_host_data_writel,
+};
+
+static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t offset,
+ uint32_t value)
+{
+ struct PPC4xxPCIState *pci = opaque;
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ /* We ignore all target attempts at PCI configuration, effectively
+ * assuming a bidirectional 1:1 mapping of PLB and PCI space. */
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ pci->pmm[0].la = value;
+ break;
+ case PCIL0_PMM0MA:
+ pci->pmm[0].ma = value;
+ break;
+ case PCIL0_PMM0PCIHA:
+ pci->pmm[0].pciha = value;
+ break;
+ case PCIL0_PMM0PCILA:
+ pci->pmm[0].pcila = value;
+ break;
+
+ case PCIL0_PMM1LA:
+ pci->pmm[1].la = value;
+ break;
+ case PCIL0_PMM1MA:
+ pci->pmm[1].ma = value;
+ break;
+ case PCIL0_PMM1PCIHA:
+ pci->pmm[1].pciha = value;
+ break;
+ case PCIL0_PMM1PCILA:
+ pci->pmm[1].pcila = value;
+ break;
+
+ case PCIL0_PMM2LA:
+ pci->pmm[2].la = value;
+ break;
+ case PCIL0_PMM2MA:
+ pci->pmm[2].ma = value;
+ break;
+ case PCIL0_PMM2PCIHA:
+ pci->pmm[2].pciha = value;
+ break;
+ case PCIL0_PMM2PCILA:
+ pci->pmm[2].pcila = value;
+ break;
+
+ case PCIL0_PTM1MS:
+ pci->ptm[0].ms = value;
+ break;
+ case PCIL0_PTM1LA:
+ pci->ptm[0].la = value;
+ break;
+ case PCIL0_PTM2MS:
+ pci->ptm[1].ms = value;
+ break;
+ case PCIL0_PTM2LA:
+ pci->ptm[1].la = value;
+ break;
+
+ default:
+ printf("%s: unhandled PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ break;
+ }
+}
+
+static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t offset)
+{
+ struct PPC4xxPCIState *pci = opaque;
+ uint32_t value;
+
+ switch (offset) {
+ case PCIL0_PMM0LA:
+ value = pci->pmm[0].la;
+ break;
+ case PCIL0_PMM0MA:
+ value = pci->pmm[0].ma;
+ break;
+ case PCIL0_PMM0PCIHA:
+ value = pci->pmm[0].pciha;
+ break;
+ case PCIL0_PMM0PCILA:
+ value = pci->pmm[0].pcila;
+ break;
+
+ case PCIL0_PMM1LA:
+ value = pci->pmm[1].la;
+ break;
+ case PCIL0_PMM1MA:
+ value = pci->pmm[1].ma;
+ break;
+ case PCIL0_PMM1PCIHA:
+ value = pci->pmm[1].pciha;
+ break;
+ case PCIL0_PMM1PCILA:
+ value = pci->pmm[1].pcila;
+ break;
+
+ case PCIL0_PMM2LA:
+ value = pci->pmm[2].la;
+ break;
+ case PCIL0_PMM2MA:
+ value = pci->pmm[2].ma;
+ break;
+ case PCIL0_PMM2PCIHA:
+ value = pci->pmm[2].pciha;
+ break;
+ case PCIL0_PMM2PCILA:
+ value = pci->pmm[2].pcila;
+ break;
+
+ case PCIL0_PTM1MS:
+ value = pci->ptm[0].ms;
+ break;
+ case PCIL0_PTM1LA:
+ value = pci->ptm[0].la;
+ break;
+ case PCIL0_PTM2MS:
+ value = pci->ptm[1].ms;
+ break;
+ case PCIL0_PTM2LA:
+ value = pci->ptm[1].la;
+ break;
+
+ default:
+ printf("%s: invalid PCI internal register 0x%lx\n", __func__,
+ (unsigned long)offset);
+ value = 0;
+ }
+
+#ifdef TARGET_WORDS_BIGENDIAN
+ value = bswap32(value);
+#endif
+
+ return value;
+}
+
+static CPUReadMemoryFunc *pci_reg_read[] = {
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+ &ppc4xx_pci_reg_read4,
+};
+
+static CPUWriteMemoryFunc *pci_reg_write[] = {
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+ &ppc4xx_pci_reg_write4,
+};
+
+static void ppc4xx_pci_reset(void *opaque)
+{
+ struct PPC4xxPCIState *pci = opaque;
+
+ memset(pci->pmm, 0, sizeof(pci->pmm));
+ memset(pci->ptm, 0, sizeof(pci->ptm));
+}
+
+/* On Bamboo, all pins from each slot are tied to a single board IRQ. This
+ * may need further refactoring for other boards. */
+static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
+{
+ int slot = pci_dev->devfn >> 3;
+
+ DPRINTF("%s: devfn %x irq %d -> %d\n", __func__,
+ pci_dev->devfn, irq_num, slot);
+
+ return slot - 1;
+}
+
+static void ppc4xx_pci_set_irq(qemu_irq *pci_irqs, int irq_num, int level)
+{
+ DPRINTF("%s: PCI irq %d\n", __func__, irq_num);
+ qemu_set_irq(pci_irqs[irq_num], level);
+}
+
+static void ppc4xx_pci_save(QEMUFile *f, void *opaque)
+{
+ PPC4xxPCIState *controller = opaque;
+ int i;
+
+ pci_device_save(&controller->pci_dev, f);
+
+ for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
+ qemu_put_be32s(f, &controller->pmm[i].la);
+ qemu_put_be32s(f, &controller->pmm[i].ma);
+ qemu_put_be32s(f, &controller->pmm[i].pcila);
+ qemu_put_be32s(f, &controller->pmm[i].pciha);
+ }
+
+ for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
+ qemu_put_be32s(f, &controller->ptm[i].ms);
+ qemu_put_be32s(f, &controller->ptm[i].la);
+ }
+}
+
+static int ppc4xx_pci_load(QEMUFile *f, void *opaque, int version_id)
+{
+ PPC4xxPCIState *controller = opaque;
+ int i;
+
+ if (version_id != 1)
+ return -EINVAL;
+
+ pci_device_load(&controller->pci_dev, f);
+
+ for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
+ qemu_get_be32s(f, &controller->pmm[i].la);
+ qemu_get_be32s(f, &controller->pmm[i].ma);
+ qemu_get_be32s(f, &controller->pmm[i].pcila);
+ qemu_get_be32s(f, &controller->pmm[i].pciha);
+ }
+
+ for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
+ qemu_get_be32s(f, &controller->ptm[i].ms);
+ qemu_get_be32s(f, &controller->ptm[i].la);
+ }
+
+ return 0;
+}
+
+/* XXX Interrupt acknowledge cycles not supported. */
+PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
+ target_phys_addr_t config_space,
+ target_phys_addr_t int_ack,
+ target_phys_addr_t special_cycle,
+ target_phys_addr_t registers)
+{
+ PPC4xxPCIState *controller;
+ int index;
+ static int ppc4xx_pci_id;
+
+ controller = qemu_mallocz(sizeof(PPC4xxPCIState));
+ if (!controller)
+ return NULL;
+
+ controller->pci_state.bus = pci_register_bus(ppc4xx_pci_set_irq,
+ ppc4xx_pci_map_irq,
+ pci_irqs, 0, 4);
+
+ controller->pci_dev = pci_register_device(controller->pci_state.bus,
+ "host bridge", sizeof(PCIDevice),
+ 0, NULL, NULL);
+ controller->pci_dev->config[0x00] = 0x14; // vendor_id
+ controller->pci_dev->config[0x01] = 0x10;
+ controller->pci_dev->config[0x02] = 0x7f; // device_id
+ controller->pci_dev->config[0x03] = 0x02;
+ controller->pci_dev->config[0x0a] = 0x80; // class_sub = other bridge type
+ controller->pci_dev->config[0x0b] = 0x06; // class_base = PCI_bridge
+
+ /* CFGADDR */
+ index = cpu_register_io_memory(0, pci4xx_cfgaddr_read,
+ pci4xx_cfgaddr_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index);
+
+ /* CFGDATA */
+ index = cpu_register_io_memory(0, pci4xx_cfgdata_read,
+ pci4xx_cfgdata_write,
+ &controller->pci_state);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index);
+
+ /* Internal registers */
+ index = cpu_register_io_memory(0, pci_reg_read, pci_reg_write, controller);
+ if (index < 0)
+ goto free;
+ cpu_register_physical_memory(registers, PCI_REG_SIZE, index);
+
+ qemu_register_reset(ppc4xx_pci_reset, controller);
+
+ /* XXX load/save code not tested. */
+ register_savevm("ppc4xx_pci", ppc4xx_pci_id++, 1,
+ ppc4xx_pci_save, ppc4xx_pci_load, controller);
+
+ return controller->pci_state.bus;
+
+free:
+ printf("%s error\n", __func__);
+ qemu_free(controller);
+ return NULL;
+}
--
1.5.6.5
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] [v3] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 21:30 ` [Qemu-devel] [PATCH] [v3] " Hollis Blanchard
@ 2008-12-02 22:01 ` Anthony Liguori
2008-12-02 23:53 ` Aurelien Jarno
1 sibling, 0 replies; 13+ messages in thread
From: Anthony Liguori @ 2008-12-02 22:01 UTC (permalink / raw)
To: qemu-devel; +Cc: plagnioj, aurelien, Hollis Blanchard
Hollis Blanchard wrote:
> This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
>
> Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
> ---
>
>
Acked-by: Anthony Liguori <aliguori@us.ibm.com>
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH 1/1] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 20:43 ` Hollis Blanchard
@ 2008-12-02 22:13 ` Aurelien Jarno
0 siblings, 0 replies; 13+ messages in thread
From: Aurelien Jarno @ 2008-12-02 22:13 UTC (permalink / raw)
To: Hollis Blanchard; +Cc: plagnioj, qemu-devel
On Tue, Dec 02, 2008 at 02:43:38PM -0600, Hollis Blanchard wrote:
> On Tue, 2008-12-02 at 14:22 -0600, Anthony Liguori wrote:
> > Hollis Blanchard wrote:
> >
> > > +#include "pci.h"
> > > +#include "pci_host.h"
> > > +#include "bswap.h"
> > > +
> > > +#undef DEBUG
> > > +#ifdef DEBUG
> > > +#define DPRINTF(fmt, args...) do { printf(fmt, ##args); } while (0)
> > > +#else
> > > +#define DPRINTF(fmt, args...)
> > > +#endif /* DEBUG */
> > >
> >
> > This is a GCC-ism that's deprecated. The proper syntax (C99) is:
> >
> > #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
>
> Will do.
>
> > > +struct ppc4xx_pci_t {
> > > + struct pci_master_map pmm[PPC44x_PCI_NR_PMMS];
> > > + struct pci_target_map ptm[PPC44x_PCI_NR_PTMS];
> > > +
> > > + PCIHostState pci_state;
> > > +};
> > > +typedef struct ppc4xx_pci_t ppc4xx_pci_t;
> > >
> >
> > It would be better to use QEMU style type naming.
>
> I copied this style from ppc405_uc.c, but I will ChangeItLikeThis.
>
> > > +static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
> > > + uint32_t value)
> > > +{
> > > + ppc4xx_pci_t *ppc4xx_pci = opaque;
> > > +
> > > +#ifdef TARGET_WORDS_BIGENDIAN
> > > + value = bswap32(value);
> > > +#endif
> > >
> > >
> >
> > Is this byte swapping correct?
>
> I hate this byte swapping, because as we've discussed at great length in
> the past, "TARGET_WORDS_BIGENDIAN" never should have existed.
>
> That said, as far as I can tell it is correct for the current state of
> qemu. I changed it to this style due to Aurelien's previous request. As
> I noted in the patch description, I can only test on
> big-endian/big-endian guest/host.
>
To summarize the discussion we had last time, this is actually needed
depending on how the PCI controller is connected to the CPU, it's not
dependent on the CPU itself. But we put it here in QEMU, as we currently
don't model buses.
It reflects all byte swapping that is done between the CPU and the
controller, either by the chipset (which can be dependent on the device
or address range), by the bus being wired reversed, or whatever.
If your controller works with byte-swapping enabled, it means that
byte-swapping is necessary for a big-endian guest. It may or may not be
necessary for a little endian guest, but I guess we have no way to try.
The other solution to find the answer it looking at the docs, get a
headache, and maybe no answer. Not sure we want that.
So I think this code is ok, at least until we get more details or
until a bus model is developed in QEMU.
--
.''`. Aurelien Jarno | GPG: 1024D/F1BCDB73
: :' : Debian developer | Electrical Engineer
`. `' aurel32@debian.org | aurelien@aurel32.net
`- people.debian.org/~aurel32 | www.aurel32.net
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [Qemu-devel] [PATCH] [v3] IBM PowerPC 4xx 32-bit PCI controller emulation
2008-12-02 21:30 ` [Qemu-devel] [PATCH] [v3] " Hollis Blanchard
2008-12-02 22:01 ` Anthony Liguori
@ 2008-12-02 23:53 ` Aurelien Jarno
1 sibling, 0 replies; 13+ messages in thread
From: Aurelien Jarno @ 2008-12-02 23:53 UTC (permalink / raw)
To: qemu-devel; +Cc: plagnioj, Hollis Blanchard
On Tue, Dec 02, 2008 at 03:30:37PM -0600, Hollis Blanchard wrote:
> This PCI controller can be found on a number of 4xx SoCs, including the 440EP.
>
> Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
> ---
> This isn't yet used by the ppc405 boards qemu emulates, but it could be if
> someone has a 405 firmware/kernel they're able to test with.
>
> This patch is required for the ppc440 KVM support I have queued up, and has
> been tested with that (big-endian host, big-endian guest).
>
> Changes from v2: implement suggestions from Anthony Liguori
> - Use C99 style for DPRINTF.
> - Register (untested) load and save functions.
> - Cosmetic changes and renaming.
Thanks, applied.
> ---
> Makefile.target | 2 +-
> hw/ppc4xx.h | 7 +
> hw/ppc4xx_pci.c | 419 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 427 insertions(+), 1 deletions(-)
> create mode 100644 hw/ppc4xx_pci.c
>
> diff --git a/Makefile.target b/Makefile.target
> index 3cdf7db..b22c306 100644
> --- a/Makefile.target
> +++ b/Makefile.target
> @@ -679,7 +679,7 @@ OBJS+= heathrow_pic.o grackle_pci.o ppc_oldworld.o
> # NewWorld PowerMac
> OBJS+= unin_pci.o ppc_chrp.o
> # PowerPC 4xx boards
> -OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc405_uc.o ppc405_boards.o
> +OBJS+= pflash_cfi02.o ppc4xx_devs.o ppc4xx_pci.o ppc405_uc.o ppc405_boards.o
> endif
> ifeq ($(TARGET_BASE_ARCH), mips)
> OBJS+= mips_r4k.o mips_jazz.o mips_malta.o mips_mipssim.o
> diff --git a/hw/ppc4xx.h b/hw/ppc4xx.h
> index df959f8..ef143ef 100644
> --- a/hw/ppc4xx.h
> +++ b/hw/ppc4xx.h
> @@ -25,6 +25,7 @@
> #if !defined(PPC_4XX_H)
> #define PPC_4XX_H
>
> +#include "pci.h"
> #include "ppc.h"
>
> /* PowerPC 4xx core initialization */
> @@ -48,4 +49,10 @@ enum {
> qemu_irq *ppcuic_init (CPUState *env, qemu_irq *irqs,
> uint32_t dcr_base, int has_ssr, int has_vr);
>
> +PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
> + target_phys_addr_t config_space,
> + target_phys_addr_t int_ack,
> + target_phys_addr_t special_cycle,
> + target_phys_addr_t registers);
> +
> #endif /* !defined(PPC_4XX_H) */
> diff --git a/hw/ppc4xx_pci.c b/hw/ppc4xx_pci.c
> new file mode 100644
> index 0000000..c76ec19
> --- /dev/null
> +++ b/hw/ppc4xx_pci.c
> @@ -0,0 +1,419 @@
> +/*
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License, version 2, as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> + *
> + * Copyright IBM Corp. 2008
> + *
> + * Authors: Hollis Blanchard <hollisb@us.ibm.com>
> + */
> +
> +/* This file implements emulation of the 32-bit PCI controller found in some
> + * 4xx SoCs, such as the 440EP. */
> +
> +#include "hw.h"
> +
> +typedef target_phys_addr_t pci_addr_t;
> +#include "pci.h"
> +#include "pci_host.h"
> +#include "bswap.h"
> +
> +#undef DEBUG
> +#ifdef DEBUG
> +#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
> +#else
> +#define DPRINTF(fmt, args...)
> +#endif /* DEBUG */
> +
> +struct PCIMasterMap {
> + uint32_t la;
> + uint32_t ma;
> + uint32_t pcila;
> + uint32_t pciha;
> +};
> +
> +struct PCITargetMap {
> + uint32_t ms;
> + uint32_t la;
> +};
> +
> +#define PPC4xx_PCI_NR_PMMS 3
> +#define PPC4xx_PCI_NR_PTMS 2
> +
> +struct PPC4xxPCIState {
> + struct PCIMasterMap pmm[PPC4xx_PCI_NR_PMMS];
> + struct PCITargetMap ptm[PPC4xx_PCI_NR_PTMS];
> +
> + PCIHostState pci_state;
> + PCIDevice *pci_dev;
> +};
> +typedef struct PPC4xxPCIState PPC4xxPCIState;
> +
> +#define PCIC0_CFGADDR 0x0
> +#define PCIC0_CFGDATA 0x4
> +
> +/* PLB Memory Map (PMM) registers specify which PLB addresses are translated to
> + * PCI accesses. */
> +#define PCIL0_PMM0LA 0x0
> +#define PCIL0_PMM0MA 0x4
> +#define PCIL0_PMM0PCILA 0x8
> +#define PCIL0_PMM0PCIHA 0xc
> +#define PCIL0_PMM1LA 0x10
> +#define PCIL0_PMM1MA 0x14
> +#define PCIL0_PMM1PCILA 0x18
> +#define PCIL0_PMM1PCIHA 0x1c
> +#define PCIL0_PMM2LA 0x20
> +#define PCIL0_PMM2MA 0x24
> +#define PCIL0_PMM2PCILA 0x28
> +#define PCIL0_PMM2PCIHA 0x2c
> +
> +/* PCI Target Map (PTM) registers specify which PCI addresses are translated to
> + * PLB accesses. */
> +#define PCIL0_PTM1MS 0x30
> +#define PCIL0_PTM1LA 0x34
> +#define PCIL0_PTM2MS 0x38
> +#define PCIL0_PTM2LA 0x3c
> +#define PCI_REG_SIZE 0x40
> +
> +
> +static uint32_t pci4xx_cfgaddr_readl(void *opaque, target_phys_addr_t addr)
> +{
> + PPC4xxPCIState *ppc4xx_pci = opaque;
> +
> + return ppc4xx_pci->pci_state.config_reg;
> +}
> +
> +static CPUReadMemoryFunc *pci4xx_cfgaddr_read[] = {
> + &pci4xx_cfgaddr_readl,
> + &pci4xx_cfgaddr_readl,
> + &pci4xx_cfgaddr_readl,
> +};
> +
> +static void pci4xx_cfgaddr_writel(void *opaque, target_phys_addr_t addr,
> + uint32_t value)
> +{
> + PPC4xxPCIState *ppc4xx_pci = opaque;
> +
> +#ifdef TARGET_WORDS_BIGENDIAN
> + value = bswap32(value);
> +#endif
> +
> + ppc4xx_pci->pci_state.config_reg = value & ~0x3;
> +}
> +
> +static CPUWriteMemoryFunc *pci4xx_cfgaddr_write[] = {
> + &pci4xx_cfgaddr_writel,
> + &pci4xx_cfgaddr_writel,
> + &pci4xx_cfgaddr_writel,
> +};
> +
> +static CPUReadMemoryFunc *pci4xx_cfgdata_read[] = {
> + &pci_host_data_readb,
> + &pci_host_data_readw,
> + &pci_host_data_readl,
> +};
> +
> +static CPUWriteMemoryFunc *pci4xx_cfgdata_write[] = {
> + &pci_host_data_writeb,
> + &pci_host_data_writew,
> + &pci_host_data_writel,
> +};
> +
> +static void ppc4xx_pci_reg_write4(void *opaque, target_phys_addr_t offset,
> + uint32_t value)
> +{
> + struct PPC4xxPCIState *pci = opaque;
> +
> +#ifdef TARGET_WORDS_BIGENDIAN
> + value = bswap32(value);
> +#endif
> +
> + /* We ignore all target attempts at PCI configuration, effectively
> + * assuming a bidirectional 1:1 mapping of PLB and PCI space. */
> +
> + switch (offset) {
> + case PCIL0_PMM0LA:
> + pci->pmm[0].la = value;
> + break;
> + case PCIL0_PMM0MA:
> + pci->pmm[0].ma = value;
> + break;
> + case PCIL0_PMM0PCIHA:
> + pci->pmm[0].pciha = value;
> + break;
> + case PCIL0_PMM0PCILA:
> + pci->pmm[0].pcila = value;
> + break;
> +
> + case PCIL0_PMM1LA:
> + pci->pmm[1].la = value;
> + break;
> + case PCIL0_PMM1MA:
> + pci->pmm[1].ma = value;
> + break;
> + case PCIL0_PMM1PCIHA:
> + pci->pmm[1].pciha = value;
> + break;
> + case PCIL0_PMM1PCILA:
> + pci->pmm[1].pcila = value;
> + break;
> +
> + case PCIL0_PMM2LA:
> + pci->pmm[2].la = value;
> + break;
> + case PCIL0_PMM2MA:
> + pci->pmm[2].ma = value;
> + break;
> + case PCIL0_PMM2PCIHA:
> + pci->pmm[2].pciha = value;
> + break;
> + case PCIL0_PMM2PCILA:
> + pci->pmm[2].pcila = value;
> + break;
> +
> + case PCIL0_PTM1MS:
> + pci->ptm[0].ms = value;
> + break;
> + case PCIL0_PTM1LA:
> + pci->ptm[0].la = value;
> + break;
> + case PCIL0_PTM2MS:
> + pci->ptm[1].ms = value;
> + break;
> + case PCIL0_PTM2LA:
> + pci->ptm[1].la = value;
> + break;
> +
> + default:
> + printf("%s: unhandled PCI internal register 0x%lx\n", __func__,
> + (unsigned long)offset);
> + break;
> + }
> +}
> +
> +static uint32_t ppc4xx_pci_reg_read4(void *opaque, target_phys_addr_t offset)
> +{
> + struct PPC4xxPCIState *pci = opaque;
> + uint32_t value;
> +
> + switch (offset) {
> + case PCIL0_PMM0LA:
> + value = pci->pmm[0].la;
> + break;
> + case PCIL0_PMM0MA:
> + value = pci->pmm[0].ma;
> + break;
> + case PCIL0_PMM0PCIHA:
> + value = pci->pmm[0].pciha;
> + break;
> + case PCIL0_PMM0PCILA:
> + value = pci->pmm[0].pcila;
> + break;
> +
> + case PCIL0_PMM1LA:
> + value = pci->pmm[1].la;
> + break;
> + case PCIL0_PMM1MA:
> + value = pci->pmm[1].ma;
> + break;
> + case PCIL0_PMM1PCIHA:
> + value = pci->pmm[1].pciha;
> + break;
> + case PCIL0_PMM1PCILA:
> + value = pci->pmm[1].pcila;
> + break;
> +
> + case PCIL0_PMM2LA:
> + value = pci->pmm[2].la;
> + break;
> + case PCIL0_PMM2MA:
> + value = pci->pmm[2].ma;
> + break;
> + case PCIL0_PMM2PCIHA:
> + value = pci->pmm[2].pciha;
> + break;
> + case PCIL0_PMM2PCILA:
> + value = pci->pmm[2].pcila;
> + break;
> +
> + case PCIL0_PTM1MS:
> + value = pci->ptm[0].ms;
> + break;
> + case PCIL0_PTM1LA:
> + value = pci->ptm[0].la;
> + break;
> + case PCIL0_PTM2MS:
> + value = pci->ptm[1].ms;
> + break;
> + case PCIL0_PTM2LA:
> + value = pci->ptm[1].la;
> + break;
> +
> + default:
> + printf("%s: invalid PCI internal register 0x%lx\n", __func__,
> + (unsigned long)offset);
> + value = 0;
> + }
> +
> +#ifdef TARGET_WORDS_BIGENDIAN
> + value = bswap32(value);
> +#endif
> +
> + return value;
> +}
> +
> +static CPUReadMemoryFunc *pci_reg_read[] = {
> + &ppc4xx_pci_reg_read4,
> + &ppc4xx_pci_reg_read4,
> + &ppc4xx_pci_reg_read4,
> +};
> +
> +static CPUWriteMemoryFunc *pci_reg_write[] = {
> + &ppc4xx_pci_reg_write4,
> + &ppc4xx_pci_reg_write4,
> + &ppc4xx_pci_reg_write4,
> +};
> +
> +static void ppc4xx_pci_reset(void *opaque)
> +{
> + struct PPC4xxPCIState *pci = opaque;
> +
> + memset(pci->pmm, 0, sizeof(pci->pmm));
> + memset(pci->ptm, 0, sizeof(pci->ptm));
> +}
> +
> +/* On Bamboo, all pins from each slot are tied to a single board IRQ. This
> + * may need further refactoring for other boards. */
> +static int ppc4xx_pci_map_irq(PCIDevice *pci_dev, int irq_num)
> +{
> + int slot = pci_dev->devfn >> 3;
> +
> + DPRINTF("%s: devfn %x irq %d -> %d\n", __func__,
> + pci_dev->devfn, irq_num, slot);
> +
> + return slot - 1;
> +}
> +
> +static void ppc4xx_pci_set_irq(qemu_irq *pci_irqs, int irq_num, int level)
> +{
> + DPRINTF("%s: PCI irq %d\n", __func__, irq_num);
> + qemu_set_irq(pci_irqs[irq_num], level);
> +}
> +
> +static void ppc4xx_pci_save(QEMUFile *f, void *opaque)
> +{
> + PPC4xxPCIState *controller = opaque;
> + int i;
> +
> + pci_device_save(&controller->pci_dev, f);
> +
> + for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
> + qemu_put_be32s(f, &controller->pmm[i].la);
> + qemu_put_be32s(f, &controller->pmm[i].ma);
> + qemu_put_be32s(f, &controller->pmm[i].pcila);
> + qemu_put_be32s(f, &controller->pmm[i].pciha);
> + }
> +
> + for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
> + qemu_put_be32s(f, &controller->ptm[i].ms);
> + qemu_put_be32s(f, &controller->ptm[i].la);
> + }
> +}
> +
> +static int ppc4xx_pci_load(QEMUFile *f, void *opaque, int version_id)
> +{
> + PPC4xxPCIState *controller = opaque;
> + int i;
> +
> + if (version_id != 1)
> + return -EINVAL;
> +
> + pci_device_load(&controller->pci_dev, f);
> +
> + for (i = 0; i < PPC4xx_PCI_NR_PMMS; i++) {
> + qemu_get_be32s(f, &controller->pmm[i].la);
> + qemu_get_be32s(f, &controller->pmm[i].ma);
> + qemu_get_be32s(f, &controller->pmm[i].pcila);
> + qemu_get_be32s(f, &controller->pmm[i].pciha);
> + }
> +
> + for (i = 0; i < PPC4xx_PCI_NR_PTMS; i++) {
> + qemu_get_be32s(f, &controller->ptm[i].ms);
> + qemu_get_be32s(f, &controller->ptm[i].la);
> + }
> +
> + return 0;
> +}
> +
> +/* XXX Interrupt acknowledge cycles not supported. */
> +PCIBus *ppc4xx_pci_init(CPUState *env, qemu_irq pci_irqs[4],
> + target_phys_addr_t config_space,
> + target_phys_addr_t int_ack,
> + target_phys_addr_t special_cycle,
> + target_phys_addr_t registers)
> +{
> + PPC4xxPCIState *controller;
> + int index;
> + static int ppc4xx_pci_id;
> +
> + controller = qemu_mallocz(sizeof(PPC4xxPCIState));
> + if (!controller)
> + return NULL;
> +
> + controller->pci_state.bus = pci_register_bus(ppc4xx_pci_set_irq,
> + ppc4xx_pci_map_irq,
> + pci_irqs, 0, 4);
> +
> + controller->pci_dev = pci_register_device(controller->pci_state.bus,
> + "host bridge", sizeof(PCIDevice),
> + 0, NULL, NULL);
> + controller->pci_dev->config[0x00] = 0x14; // vendor_id
> + controller->pci_dev->config[0x01] = 0x10;
> + controller->pci_dev->config[0x02] = 0x7f; // device_id
> + controller->pci_dev->config[0x03] = 0x02;
> + controller->pci_dev->config[0x0a] = 0x80; // class_sub = other bridge type
> + controller->pci_dev->config[0x0b] = 0x06; // class_base = PCI_bridge
> +
> + /* CFGADDR */
> + index = cpu_register_io_memory(0, pci4xx_cfgaddr_read,
> + pci4xx_cfgaddr_write, controller);
> + if (index < 0)
> + goto free;
> + cpu_register_physical_memory(config_space + PCIC0_CFGADDR, 4, index);
> +
> + /* CFGDATA */
> + index = cpu_register_io_memory(0, pci4xx_cfgdata_read,
> + pci4xx_cfgdata_write,
> + &controller->pci_state);
> + if (index < 0)
> + goto free;
> + cpu_register_physical_memory(config_space + PCIC0_CFGDATA, 4, index);
> +
> + /* Internal registers */
> + index = cpu_register_io_memory(0, pci_reg_read, pci_reg_write, controller);
> + if (index < 0)
> + goto free;
> + cpu_register_physical_memory(registers, PCI_REG_SIZE, index);
> +
> + qemu_register_reset(ppc4xx_pci_reset, controller);
> +
> + /* XXX load/save code not tested. */
> + register_savevm("ppc4xx_pci", ppc4xx_pci_id++, 1,
> + ppc4xx_pci_save, ppc4xx_pci_load, controller);
> +
> + return controller->pci_state.bus;
> +
> +free:
> + printf("%s error\n", __func__);
> + qemu_free(controller);
> + return NULL;
> +}
> --
> 1.5.6.5
>
>
>
>
--
.''`. Aurelien Jarno | GPG: 1024D/F1BCDB73
: :' : Debian developer | Electrical Engineer
`. `' aurel32@debian.org | aurelien@aurel32.net
`- people.debian.org/~aurel32 | www.aurel32.net
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2008-12-02 23:53 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-11-26 19:22 [Qemu-devel] [PATCH] IBM PowerPC 4xx 32-bit PCI controller emulation Hollis Blanchard
2008-12-01 16:13 ` [Qemu-devel] " Hollis Blanchard
2008-12-01 18:15 ` [Qemu-devel] " Blue Swirl
2008-12-01 19:17 ` Hollis Blanchard
2008-12-01 19:59 ` Hollis Blanchard
2008-12-01 20:51 ` Blue Swirl
2008-12-02 20:02 ` [Qemu-devel] [PATCH 1/1] " Hollis Blanchard
2008-12-02 20:22 ` Anthony Liguori
2008-12-02 20:43 ` Hollis Blanchard
2008-12-02 22:13 ` Aurelien Jarno
2008-12-02 21:30 ` [Qemu-devel] [PATCH] [v3] " Hollis Blanchard
2008-12-02 22:01 ` Anthony Liguori
2008-12-02 23:53 ` Aurelien Jarno
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).