122dc8a47SPhilippe Mathieu-Daudé /*
222dc8a47SPhilippe Mathieu-Daudé * Emulation of the ibm,plb-pcix PCI controller
322dc8a47SPhilippe Mathieu-Daudé * This is found in some 440 SoCs e.g. the 460EX.
422dc8a47SPhilippe Mathieu-Daudé *
522dc8a47SPhilippe Mathieu-Daudé * Copyright (c) 2016-2018 BALATON Zoltan
622dc8a47SPhilippe Mathieu-Daudé *
722dc8a47SPhilippe Mathieu-Daudé * Derived from ppc4xx_pci.c and pci-host/ppce500.c
822dc8a47SPhilippe Mathieu-Daudé *
922dc8a47SPhilippe Mathieu-Daudé * This program is free software; you can redistribute it and/or modify
1022dc8a47SPhilippe Mathieu-Daudé * it under the terms of the GNU General Public License, version 2, as
1122dc8a47SPhilippe Mathieu-Daudé * published by the Free Software Foundation.
1222dc8a47SPhilippe Mathieu-Daudé *
1322dc8a47SPhilippe Mathieu-Daudé * This program is distributed in the hope that it will be useful,
1422dc8a47SPhilippe Mathieu-Daudé * but WITHOUT ANY WARRANTY; without even the implied warranty of
1522dc8a47SPhilippe Mathieu-Daudé * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1622dc8a47SPhilippe Mathieu-Daudé * GNU General Public License for more details.
1722dc8a47SPhilippe Mathieu-Daudé *
1822dc8a47SPhilippe Mathieu-Daudé * You should have received a copy of the GNU General Public License
1922dc8a47SPhilippe Mathieu-Daudé * along with this program; if not, see <http://www.gnu.org/licenses/>.
2022dc8a47SPhilippe Mathieu-Daudé */
2122dc8a47SPhilippe Mathieu-Daudé
2222dc8a47SPhilippe Mathieu-Daudé #include "qemu/osdep.h"
2322dc8a47SPhilippe Mathieu-Daudé #include "qemu/error-report.h"
2422dc8a47SPhilippe Mathieu-Daudé #include "qemu/log.h"
2522dc8a47SPhilippe Mathieu-Daudé #include "qemu/module.h"
2622dc8a47SPhilippe Mathieu-Daudé #include "qemu/units.h"
2722dc8a47SPhilippe Mathieu-Daudé #include "hw/irq.h"
2822dc8a47SPhilippe Mathieu-Daudé #include "hw/pci-host/ppc4xx.h"
2922dc8a47SPhilippe Mathieu-Daudé #include "hw/pci/pci_device.h"
3022dc8a47SPhilippe Mathieu-Daudé #include "hw/pci/pci_host.h"
3122dc8a47SPhilippe Mathieu-Daudé #include "trace.h"
3222dc8a47SPhilippe Mathieu-Daudé #include "qom/object.h"
3322dc8a47SPhilippe Mathieu-Daudé
3422dc8a47SPhilippe Mathieu-Daudé struct PLBOutMap {
3522dc8a47SPhilippe Mathieu-Daudé uint64_t la;
3622dc8a47SPhilippe Mathieu-Daudé uint64_t pcia;
3722dc8a47SPhilippe Mathieu-Daudé uint32_t sa;
3822dc8a47SPhilippe Mathieu-Daudé MemoryRegion mr;
3922dc8a47SPhilippe Mathieu-Daudé };
4022dc8a47SPhilippe Mathieu-Daudé
4122dc8a47SPhilippe Mathieu-Daudé struct PLBInMap {
4222dc8a47SPhilippe Mathieu-Daudé uint64_t sa;
4322dc8a47SPhilippe Mathieu-Daudé uint64_t la;
4422dc8a47SPhilippe Mathieu-Daudé MemoryRegion mr;
4522dc8a47SPhilippe Mathieu-Daudé };
4622dc8a47SPhilippe Mathieu-Daudé
4722dc8a47SPhilippe Mathieu-Daudé OBJECT_DECLARE_SIMPLE_TYPE(PPC440PCIXState, PPC440_PCIX_HOST)
4822dc8a47SPhilippe Mathieu-Daudé
4922dc8a47SPhilippe Mathieu-Daudé #define PPC440_PCIX_NR_POMS 3
5022dc8a47SPhilippe Mathieu-Daudé #define PPC440_PCIX_NR_PIMS 3
5122dc8a47SPhilippe Mathieu-Daudé
5222dc8a47SPhilippe Mathieu-Daudé struct PPC440PCIXState {
5322dc8a47SPhilippe Mathieu-Daudé PCIHostState parent_obj;
5422dc8a47SPhilippe Mathieu-Daudé
556e4acebaSBALATON Zoltan uint8_t config[PCI_CONFIG_SPACE_SIZE];
5622dc8a47SPhilippe Mathieu-Daudé struct PLBOutMap pom[PPC440_PCIX_NR_POMS];
5722dc8a47SPhilippe Mathieu-Daudé struct PLBInMap pim[PPC440_PCIX_NR_PIMS];
5822dc8a47SPhilippe Mathieu-Daudé uint32_t sts;
5922dc8a47SPhilippe Mathieu-Daudé qemu_irq irq;
6022dc8a47SPhilippe Mathieu-Daudé AddressSpace bm_as;
6122dc8a47SPhilippe Mathieu-Daudé MemoryRegion bm;
6222dc8a47SPhilippe Mathieu-Daudé
6322dc8a47SPhilippe Mathieu-Daudé MemoryRegion container;
6422dc8a47SPhilippe Mathieu-Daudé MemoryRegion iomem;
6522dc8a47SPhilippe Mathieu-Daudé MemoryRegion busmem;
6622dc8a47SPhilippe Mathieu-Daudé MemoryRegion regs;
6722dc8a47SPhilippe Mathieu-Daudé };
6822dc8a47SPhilippe Mathieu-Daudé
6922dc8a47SPhilippe Mathieu-Daudé #define PPC440_REG_BASE 0x80000
7022dc8a47SPhilippe Mathieu-Daudé #define PPC440_REG_SIZE 0xff
7122dc8a47SPhilippe Mathieu-Daudé
7222dc8a47SPhilippe Mathieu-Daudé #define PCIC0_CFGADDR 0x0
7322dc8a47SPhilippe Mathieu-Daudé #define PCIC0_CFGDATA 0x4
7422dc8a47SPhilippe Mathieu-Daudé
7522dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM0LAL 0x68
7622dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM0LAH 0x6c
7722dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM0SA 0x70
7822dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM0PCIAL 0x74
7922dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM0PCIAH 0x78
8022dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM1LAL 0x7c
8122dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM1LAH 0x80
8222dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM1SA 0x84
8322dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM1PCIAL 0x88
8422dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM1PCIAH 0x8c
8522dc8a47SPhilippe Mathieu-Daudé #define PCIX0_POM2SA 0x90
8622dc8a47SPhilippe Mathieu-Daudé
8722dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM0SAL 0x98
8822dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM0LAL 0x9c
8922dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM0LAH 0xa0
9022dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM1SA 0xa4
9122dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM1LAL 0xa8
9222dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM1LAH 0xac
9322dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM2SAL 0xb0
9422dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM2LAL 0xb4
9522dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM2LAH 0xb8
9622dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM0SAH 0xf8
9722dc8a47SPhilippe Mathieu-Daudé #define PCIX0_PIM2SAH 0xfc
9822dc8a47SPhilippe Mathieu-Daudé
9922dc8a47SPhilippe Mathieu-Daudé #define PCIX0_STS 0xe0
10022dc8a47SPhilippe Mathieu-Daudé
10122dc8a47SPhilippe Mathieu-Daudé #define PCI_ALL_SIZE (PPC440_REG_BASE + PPC440_REG_SIZE)
10222dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_clear_region(MemoryRegion * parent,MemoryRegion * mem)10322dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_clear_region(MemoryRegion *parent,
10422dc8a47SPhilippe Mathieu-Daudé MemoryRegion *mem)
10522dc8a47SPhilippe Mathieu-Daudé {
10622dc8a47SPhilippe Mathieu-Daudé if (memory_region_is_mapped(mem)) {
10722dc8a47SPhilippe Mathieu-Daudé memory_region_del_subregion(parent, mem);
10822dc8a47SPhilippe Mathieu-Daudé object_unparent(OBJECT(mem));
10922dc8a47SPhilippe Mathieu-Daudé }
11022dc8a47SPhilippe Mathieu-Daudé }
11122dc8a47SPhilippe Mathieu-Daudé
11222dc8a47SPhilippe Mathieu-Daudé /* DMA mapping */
ppc440_pcix_update_pim(PPC440PCIXState * s,int idx)11322dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_update_pim(PPC440PCIXState *s, int idx)
11422dc8a47SPhilippe Mathieu-Daudé {
11522dc8a47SPhilippe Mathieu-Daudé MemoryRegion *mem = &s->pim[idx].mr;
11622dc8a47SPhilippe Mathieu-Daudé char *name;
11722dc8a47SPhilippe Mathieu-Daudé uint64_t size;
11822dc8a47SPhilippe Mathieu-Daudé
11922dc8a47SPhilippe Mathieu-Daudé /* Before we modify anything, unmap and destroy the region */
12022dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_clear_region(&s->bm, mem);
12122dc8a47SPhilippe Mathieu-Daudé
12222dc8a47SPhilippe Mathieu-Daudé if (!(s->pim[idx].sa & 1)) {
12322dc8a47SPhilippe Mathieu-Daudé /* Not enabled, nothing to do */
12422dc8a47SPhilippe Mathieu-Daudé return;
12522dc8a47SPhilippe Mathieu-Daudé }
12622dc8a47SPhilippe Mathieu-Daudé
12722dc8a47SPhilippe Mathieu-Daudé name = g_strdup_printf("PCI Inbound Window %d", idx);
12822dc8a47SPhilippe Mathieu-Daudé size = ~(s->pim[idx].sa & ~7ULL) + 1;
12922dc8a47SPhilippe Mathieu-Daudé memory_region_init_alias(mem, OBJECT(s), name, get_system_memory(),
13022dc8a47SPhilippe Mathieu-Daudé s->pim[idx].la, size);
13122dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion_overlap(&s->bm, 0, mem, -1);
13222dc8a47SPhilippe Mathieu-Daudé g_free(name);
13322dc8a47SPhilippe Mathieu-Daudé
13422dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_update_pim(idx, size, s->pim[idx].la);
13522dc8a47SPhilippe Mathieu-Daudé }
13622dc8a47SPhilippe Mathieu-Daudé
13722dc8a47SPhilippe Mathieu-Daudé /* BAR mapping */
ppc440_pcix_update_pom(PPC440PCIXState * s,int idx)13822dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_update_pom(PPC440PCIXState *s, int idx)
13922dc8a47SPhilippe Mathieu-Daudé {
14022dc8a47SPhilippe Mathieu-Daudé MemoryRegion *mem = &s->pom[idx].mr;
14122dc8a47SPhilippe Mathieu-Daudé MemoryRegion *address_space_mem = get_system_memory();
14222dc8a47SPhilippe Mathieu-Daudé char *name;
14322dc8a47SPhilippe Mathieu-Daudé uint32_t size;
14422dc8a47SPhilippe Mathieu-Daudé
14522dc8a47SPhilippe Mathieu-Daudé /* Before we modify anything, unmap and destroy the region */
14622dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_clear_region(address_space_mem, mem);
14722dc8a47SPhilippe Mathieu-Daudé
14822dc8a47SPhilippe Mathieu-Daudé if (!(s->pom[idx].sa & 1)) {
14922dc8a47SPhilippe Mathieu-Daudé /* Not enabled, nothing to do */
15022dc8a47SPhilippe Mathieu-Daudé return;
15122dc8a47SPhilippe Mathieu-Daudé }
15222dc8a47SPhilippe Mathieu-Daudé
15322dc8a47SPhilippe Mathieu-Daudé name = g_strdup_printf("PCI Outbound Window %d", idx);
15422dc8a47SPhilippe Mathieu-Daudé size = ~(s->pom[idx].sa & 0xfffffffe) + 1;
15522dc8a47SPhilippe Mathieu-Daudé if (!size) {
15622dc8a47SPhilippe Mathieu-Daudé size = 0xffffffff;
15722dc8a47SPhilippe Mathieu-Daudé }
15822dc8a47SPhilippe Mathieu-Daudé memory_region_init_alias(mem, OBJECT(s), name, &s->busmem,
15922dc8a47SPhilippe Mathieu-Daudé s->pom[idx].pcia, size);
16022dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion(address_space_mem, s->pom[idx].la, mem);
16122dc8a47SPhilippe Mathieu-Daudé g_free(name);
16222dc8a47SPhilippe Mathieu-Daudé
16322dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_update_pom(idx, size, s->pom[idx].la, s->pom[idx].pcia);
16422dc8a47SPhilippe Mathieu-Daudé }
16522dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_reg_write4(void * opaque,hwaddr addr,uint64_t val,unsigned size)16622dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_reg_write4(void *opaque, hwaddr addr,
16722dc8a47SPhilippe Mathieu-Daudé uint64_t val, unsigned size)
16822dc8a47SPhilippe Mathieu-Daudé {
16922dc8a47SPhilippe Mathieu-Daudé struct PPC440PCIXState *s = opaque;
17022dc8a47SPhilippe Mathieu-Daudé
17122dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_reg_write(addr, val, size);
17222dc8a47SPhilippe Mathieu-Daudé switch (addr) {
17322dc8a47SPhilippe Mathieu-Daudé case PCI_VENDOR_ID ... PCI_MAX_LAT:
1746e4acebaSBALATON Zoltan stl_le_p(s->config + addr, val);
17522dc8a47SPhilippe Mathieu-Daudé break;
17622dc8a47SPhilippe Mathieu-Daudé
17722dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0LAL:
17822dc8a47SPhilippe Mathieu-Daudé s->pom[0].la &= 0xffffffff00000000ULL;
17922dc8a47SPhilippe Mathieu-Daudé s->pom[0].la |= val;
18022dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 0);
18122dc8a47SPhilippe Mathieu-Daudé break;
18222dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0LAH:
18322dc8a47SPhilippe Mathieu-Daudé s->pom[0].la &= 0xffffffffULL;
18422dc8a47SPhilippe Mathieu-Daudé s->pom[0].la |= val << 32;
18522dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 0);
18622dc8a47SPhilippe Mathieu-Daudé break;
18722dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0SA:
18822dc8a47SPhilippe Mathieu-Daudé s->pom[0].sa = val;
18922dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 0);
19022dc8a47SPhilippe Mathieu-Daudé break;
19122dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0PCIAL:
19222dc8a47SPhilippe Mathieu-Daudé s->pom[0].pcia &= 0xffffffff00000000ULL;
19322dc8a47SPhilippe Mathieu-Daudé s->pom[0].pcia |= val;
19422dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 0);
19522dc8a47SPhilippe Mathieu-Daudé break;
19622dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0PCIAH:
19722dc8a47SPhilippe Mathieu-Daudé s->pom[0].pcia &= 0xffffffffULL;
19822dc8a47SPhilippe Mathieu-Daudé s->pom[0].pcia |= val << 32;
19922dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 0);
20022dc8a47SPhilippe Mathieu-Daudé break;
20122dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1LAL:
20222dc8a47SPhilippe Mathieu-Daudé s->pom[1].la &= 0xffffffff00000000ULL;
20322dc8a47SPhilippe Mathieu-Daudé s->pom[1].la |= val;
20422dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 1);
20522dc8a47SPhilippe Mathieu-Daudé break;
20622dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1LAH:
20722dc8a47SPhilippe Mathieu-Daudé s->pom[1].la &= 0xffffffffULL;
20822dc8a47SPhilippe Mathieu-Daudé s->pom[1].la |= val << 32;
20922dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 1);
21022dc8a47SPhilippe Mathieu-Daudé break;
21122dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1SA:
21222dc8a47SPhilippe Mathieu-Daudé s->pom[1].sa = val;
21322dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 1);
21422dc8a47SPhilippe Mathieu-Daudé break;
21522dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1PCIAL:
21622dc8a47SPhilippe Mathieu-Daudé s->pom[1].pcia &= 0xffffffff00000000ULL;
21722dc8a47SPhilippe Mathieu-Daudé s->pom[1].pcia |= val;
21822dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 1);
21922dc8a47SPhilippe Mathieu-Daudé break;
22022dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1PCIAH:
22122dc8a47SPhilippe Mathieu-Daudé s->pom[1].pcia &= 0xffffffffULL;
22222dc8a47SPhilippe Mathieu-Daudé s->pom[1].pcia |= val << 32;
22322dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pom(s, 1);
22422dc8a47SPhilippe Mathieu-Daudé break;
22522dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM2SA:
22622dc8a47SPhilippe Mathieu-Daudé s->pom[2].sa = val;
22722dc8a47SPhilippe Mathieu-Daudé break;
22822dc8a47SPhilippe Mathieu-Daudé
22922dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0SAL:
23022dc8a47SPhilippe Mathieu-Daudé s->pim[0].sa &= 0xffffffff00000000ULL;
23122dc8a47SPhilippe Mathieu-Daudé s->pim[0].sa |= val;
23222dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 0);
23322dc8a47SPhilippe Mathieu-Daudé break;
23422dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0LAL:
23522dc8a47SPhilippe Mathieu-Daudé s->pim[0].la &= 0xffffffff00000000ULL;
23622dc8a47SPhilippe Mathieu-Daudé s->pim[0].la |= val;
23722dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 0);
23822dc8a47SPhilippe Mathieu-Daudé break;
23922dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0LAH:
24022dc8a47SPhilippe Mathieu-Daudé s->pim[0].la &= 0xffffffffULL;
24122dc8a47SPhilippe Mathieu-Daudé s->pim[0].la |= val << 32;
24222dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 0);
24322dc8a47SPhilippe Mathieu-Daudé break;
24422dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1SA:
24522dc8a47SPhilippe Mathieu-Daudé s->pim[1].sa = val;
24622dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 1);
24722dc8a47SPhilippe Mathieu-Daudé break;
24822dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1LAL:
24922dc8a47SPhilippe Mathieu-Daudé s->pim[1].la &= 0xffffffff00000000ULL;
25022dc8a47SPhilippe Mathieu-Daudé s->pim[1].la |= val;
25122dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 1);
25222dc8a47SPhilippe Mathieu-Daudé break;
25322dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1LAH:
25422dc8a47SPhilippe Mathieu-Daudé s->pim[1].la &= 0xffffffffULL;
25522dc8a47SPhilippe Mathieu-Daudé s->pim[1].la |= val << 32;
25622dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 1);
25722dc8a47SPhilippe Mathieu-Daudé break;
25822dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2SAL:
25922dc8a47SPhilippe Mathieu-Daudé s->pim[2].sa &= 0xffffffff00000000ULL;
26022dc8a47SPhilippe Mathieu-Daudé s->pim[2].sa |= val;
26122dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 2);
26222dc8a47SPhilippe Mathieu-Daudé break;
26322dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2LAL:
26422dc8a47SPhilippe Mathieu-Daudé s->pim[2].la &= 0xffffffff00000000ULL;
26522dc8a47SPhilippe Mathieu-Daudé s->pim[2].la |= val;
26622dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 2);
26722dc8a47SPhilippe Mathieu-Daudé break;
26822dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2LAH:
26922dc8a47SPhilippe Mathieu-Daudé s->pim[2].la &= 0xffffffffULL;
27022dc8a47SPhilippe Mathieu-Daudé s->pim[2].la |= val << 32;
27122dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 2);
27222dc8a47SPhilippe Mathieu-Daudé break;
27322dc8a47SPhilippe Mathieu-Daudé
27422dc8a47SPhilippe Mathieu-Daudé case PCIX0_STS:
27522dc8a47SPhilippe Mathieu-Daudé s->sts = val;
27622dc8a47SPhilippe Mathieu-Daudé break;
27722dc8a47SPhilippe Mathieu-Daudé
27822dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0SAH:
27922dc8a47SPhilippe Mathieu-Daudé s->pim[0].sa &= 0xffffffffULL;
28022dc8a47SPhilippe Mathieu-Daudé s->pim[0].sa |= val << 32;
28122dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 0);
28222dc8a47SPhilippe Mathieu-Daudé break;
28322dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2SAH:
28422dc8a47SPhilippe Mathieu-Daudé s->pim[2].sa &= 0xffffffffULL;
28522dc8a47SPhilippe Mathieu-Daudé s->pim[2].sa |= val << 32;
28622dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_update_pim(s, 2);
28722dc8a47SPhilippe Mathieu-Daudé break;
28822dc8a47SPhilippe Mathieu-Daudé
28922dc8a47SPhilippe Mathieu-Daudé default:
29022dc8a47SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP,
29122dc8a47SPhilippe Mathieu-Daudé "%s: unhandled PCI internal register 0x%"HWADDR_PRIx"\n",
29222dc8a47SPhilippe Mathieu-Daudé __func__, addr);
29322dc8a47SPhilippe Mathieu-Daudé break;
29422dc8a47SPhilippe Mathieu-Daudé }
29522dc8a47SPhilippe Mathieu-Daudé }
29622dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_reg_read4(void * opaque,hwaddr addr,unsigned size)29722dc8a47SPhilippe Mathieu-Daudé static uint64_t ppc440_pcix_reg_read4(void *opaque, hwaddr addr,
29822dc8a47SPhilippe Mathieu-Daudé unsigned size)
29922dc8a47SPhilippe Mathieu-Daudé {
30022dc8a47SPhilippe Mathieu-Daudé struct PPC440PCIXState *s = opaque;
30122dc8a47SPhilippe Mathieu-Daudé uint32_t val;
30222dc8a47SPhilippe Mathieu-Daudé
30322dc8a47SPhilippe Mathieu-Daudé switch (addr) {
30422dc8a47SPhilippe Mathieu-Daudé case PCI_VENDOR_ID ... PCI_MAX_LAT:
3056e4acebaSBALATON Zoltan val = ldl_le_p(s->config + addr);
30622dc8a47SPhilippe Mathieu-Daudé break;
30722dc8a47SPhilippe Mathieu-Daudé
30822dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0LAL:
30922dc8a47SPhilippe Mathieu-Daudé val = s->pom[0].la;
31022dc8a47SPhilippe Mathieu-Daudé break;
31122dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0LAH:
31222dc8a47SPhilippe Mathieu-Daudé val = s->pom[0].la >> 32;
31322dc8a47SPhilippe Mathieu-Daudé break;
31422dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0SA:
31522dc8a47SPhilippe Mathieu-Daudé val = s->pom[0].sa;
31622dc8a47SPhilippe Mathieu-Daudé break;
31722dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0PCIAL:
31822dc8a47SPhilippe Mathieu-Daudé val = s->pom[0].pcia;
31922dc8a47SPhilippe Mathieu-Daudé break;
32022dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM0PCIAH:
32122dc8a47SPhilippe Mathieu-Daudé val = s->pom[0].pcia >> 32;
32222dc8a47SPhilippe Mathieu-Daudé break;
32322dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1LAL:
32422dc8a47SPhilippe Mathieu-Daudé val = s->pom[1].la;
32522dc8a47SPhilippe Mathieu-Daudé break;
32622dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1LAH:
32722dc8a47SPhilippe Mathieu-Daudé val = s->pom[1].la >> 32;
32822dc8a47SPhilippe Mathieu-Daudé break;
32922dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1SA:
33022dc8a47SPhilippe Mathieu-Daudé val = s->pom[1].sa;
33122dc8a47SPhilippe Mathieu-Daudé break;
33222dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1PCIAL:
33322dc8a47SPhilippe Mathieu-Daudé val = s->pom[1].pcia;
33422dc8a47SPhilippe Mathieu-Daudé break;
33522dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM1PCIAH:
33622dc8a47SPhilippe Mathieu-Daudé val = s->pom[1].pcia >> 32;
33722dc8a47SPhilippe Mathieu-Daudé break;
33822dc8a47SPhilippe Mathieu-Daudé case PCIX0_POM2SA:
33922dc8a47SPhilippe Mathieu-Daudé val = s->pom[2].sa;
34022dc8a47SPhilippe Mathieu-Daudé break;
34122dc8a47SPhilippe Mathieu-Daudé
34222dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0SAL:
34322dc8a47SPhilippe Mathieu-Daudé val = s->pim[0].sa;
34422dc8a47SPhilippe Mathieu-Daudé break;
34522dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0LAL:
34622dc8a47SPhilippe Mathieu-Daudé val = s->pim[0].la;
34722dc8a47SPhilippe Mathieu-Daudé break;
34822dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0LAH:
34922dc8a47SPhilippe Mathieu-Daudé val = s->pim[0].la >> 32;
35022dc8a47SPhilippe Mathieu-Daudé break;
35122dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1SA:
35222dc8a47SPhilippe Mathieu-Daudé val = s->pim[1].sa;
35322dc8a47SPhilippe Mathieu-Daudé break;
35422dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1LAL:
35522dc8a47SPhilippe Mathieu-Daudé val = s->pim[1].la;
35622dc8a47SPhilippe Mathieu-Daudé break;
35722dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM1LAH:
35822dc8a47SPhilippe Mathieu-Daudé val = s->pim[1].la >> 32;
35922dc8a47SPhilippe Mathieu-Daudé break;
36022dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2SAL:
36122dc8a47SPhilippe Mathieu-Daudé val = s->pim[2].sa;
36222dc8a47SPhilippe Mathieu-Daudé break;
36322dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2LAL:
36422dc8a47SPhilippe Mathieu-Daudé val = s->pim[2].la;
36522dc8a47SPhilippe Mathieu-Daudé break;
36622dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2LAH:
36722dc8a47SPhilippe Mathieu-Daudé val = s->pim[2].la >> 32;
36822dc8a47SPhilippe Mathieu-Daudé break;
36922dc8a47SPhilippe Mathieu-Daudé
37022dc8a47SPhilippe Mathieu-Daudé case PCIX0_STS:
37122dc8a47SPhilippe Mathieu-Daudé val = s->sts;
37222dc8a47SPhilippe Mathieu-Daudé break;
37322dc8a47SPhilippe Mathieu-Daudé
37422dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM0SAH:
37522dc8a47SPhilippe Mathieu-Daudé val = s->pim[0].sa >> 32;
37622dc8a47SPhilippe Mathieu-Daudé break;
37722dc8a47SPhilippe Mathieu-Daudé case PCIX0_PIM2SAH:
37822dc8a47SPhilippe Mathieu-Daudé val = s->pim[2].sa >> 32;
37922dc8a47SPhilippe Mathieu-Daudé break;
38022dc8a47SPhilippe Mathieu-Daudé
38122dc8a47SPhilippe Mathieu-Daudé default:
38222dc8a47SPhilippe Mathieu-Daudé qemu_log_mask(LOG_UNIMP,
38322dc8a47SPhilippe Mathieu-Daudé "%s: invalid PCI internal register 0x%" HWADDR_PRIx "\n",
38422dc8a47SPhilippe Mathieu-Daudé __func__, addr);
38522dc8a47SPhilippe Mathieu-Daudé val = 0;
38622dc8a47SPhilippe Mathieu-Daudé }
38722dc8a47SPhilippe Mathieu-Daudé
38822dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_reg_read(addr, val);
38922dc8a47SPhilippe Mathieu-Daudé return val;
39022dc8a47SPhilippe Mathieu-Daudé }
39122dc8a47SPhilippe Mathieu-Daudé
39222dc8a47SPhilippe Mathieu-Daudé static const MemoryRegionOps pci_reg_ops = {
39322dc8a47SPhilippe Mathieu-Daudé .read = ppc440_pcix_reg_read4,
39422dc8a47SPhilippe Mathieu-Daudé .write = ppc440_pcix_reg_write4,
39522dc8a47SPhilippe Mathieu-Daudé .endianness = DEVICE_LITTLE_ENDIAN,
39622dc8a47SPhilippe Mathieu-Daudé };
39722dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_reset(DeviceState * dev)39822dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_reset(DeviceState *dev)
39922dc8a47SPhilippe Mathieu-Daudé {
40022dc8a47SPhilippe Mathieu-Daudé struct PPC440PCIXState *s = PPC440_PCIX_HOST(dev);
40122dc8a47SPhilippe Mathieu-Daudé int i;
40222dc8a47SPhilippe Mathieu-Daudé
40322dc8a47SPhilippe Mathieu-Daudé for (i = 0; i < PPC440_PCIX_NR_POMS; i++) {
40422dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_clear_region(get_system_memory(), &s->pom[i].mr);
40522dc8a47SPhilippe Mathieu-Daudé }
40622dc8a47SPhilippe Mathieu-Daudé for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) {
40722dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_clear_region(&s->bm, &s->pim[i].mr);
40822dc8a47SPhilippe Mathieu-Daudé }
40922dc8a47SPhilippe Mathieu-Daudé memset(s->pom, 0, sizeof(s->pom));
41022dc8a47SPhilippe Mathieu-Daudé memset(s->pim, 0, sizeof(s->pim));
41122dc8a47SPhilippe Mathieu-Daudé for (i = 0; i < PPC440_PCIX_NR_PIMS; i++) {
41222dc8a47SPhilippe Mathieu-Daudé s->pim[i].sa = 0xffffffff00000000ULL;
41322dc8a47SPhilippe Mathieu-Daudé }
41422dc8a47SPhilippe Mathieu-Daudé s->sts = 0;
41522dc8a47SPhilippe Mathieu-Daudé }
41622dc8a47SPhilippe Mathieu-Daudé
41722dc8a47SPhilippe Mathieu-Daudé /*
41822dc8a47SPhilippe Mathieu-Daudé * All four IRQ[ABCD] pins from all slots are tied to a single board
41922dc8a47SPhilippe Mathieu-Daudé * IRQ, so our mapping function here maps everything to IRQ 0.
42022dc8a47SPhilippe Mathieu-Daudé * The code in pci_change_irq_level() tracks the number of times
42122dc8a47SPhilippe Mathieu-Daudé * the mapped IRQ is asserted and deasserted, so if multiple devices
42222dc8a47SPhilippe Mathieu-Daudé * assert an IRQ at the same time the behaviour is correct.
42322dc8a47SPhilippe Mathieu-Daudé *
42422dc8a47SPhilippe Mathieu-Daudé * This may need further refactoring for boards that use multiple IRQ lines.
42522dc8a47SPhilippe Mathieu-Daudé */
ppc440_pcix_map_irq(PCIDevice * pci_dev,int irq_num)42622dc8a47SPhilippe Mathieu-Daudé static int ppc440_pcix_map_irq(PCIDevice *pci_dev, int irq_num)
42722dc8a47SPhilippe Mathieu-Daudé {
42822dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_map_irq(pci_dev->devfn, irq_num, 0);
42922dc8a47SPhilippe Mathieu-Daudé return 0;
43022dc8a47SPhilippe Mathieu-Daudé }
43122dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_set_irq(void * opaque,int irq_num,int level)43222dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_set_irq(void *opaque, int irq_num, int level)
43322dc8a47SPhilippe Mathieu-Daudé {
43422dc8a47SPhilippe Mathieu-Daudé qemu_irq *pci_irq = opaque;
43522dc8a47SPhilippe Mathieu-Daudé
43622dc8a47SPhilippe Mathieu-Daudé trace_ppc440_pcix_set_irq(irq_num);
43722dc8a47SPhilippe Mathieu-Daudé if (irq_num < 0) {
43822dc8a47SPhilippe Mathieu-Daudé error_report("%s: PCI irq %d", __func__, irq_num);
43922dc8a47SPhilippe Mathieu-Daudé return;
44022dc8a47SPhilippe Mathieu-Daudé }
44122dc8a47SPhilippe Mathieu-Daudé qemu_set_irq(*pci_irq, level);
44222dc8a47SPhilippe Mathieu-Daudé }
44322dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_set_iommu(PCIBus * b,void * opaque,int devfn)44422dc8a47SPhilippe Mathieu-Daudé static AddressSpace *ppc440_pcix_set_iommu(PCIBus *b, void *opaque, int devfn)
44522dc8a47SPhilippe Mathieu-Daudé {
44622dc8a47SPhilippe Mathieu-Daudé PPC440PCIXState *s = opaque;
44722dc8a47SPhilippe Mathieu-Daudé
44822dc8a47SPhilippe Mathieu-Daudé return &s->bm_as;
44922dc8a47SPhilippe Mathieu-Daudé }
45022dc8a47SPhilippe Mathieu-Daudé
45122dc8a47SPhilippe Mathieu-Daudé static const PCIIOMMUOps ppc440_iommu_ops = {
45222dc8a47SPhilippe Mathieu-Daudé .get_address_space = ppc440_pcix_set_iommu,
45322dc8a47SPhilippe Mathieu-Daudé };
45422dc8a47SPhilippe Mathieu-Daudé
45522dc8a47SPhilippe Mathieu-Daudé /*
45622dc8a47SPhilippe Mathieu-Daudé * Some guests on sam460ex write all kinds of garbage here such as
45722dc8a47SPhilippe Mathieu-Daudé * missing enable bit and low bits set and still expect this to work
45822dc8a47SPhilippe Mathieu-Daudé * (apparently it does on real hardware because these boot there) so
45922dc8a47SPhilippe Mathieu-Daudé * we have to override these ops here and fix it up
46022dc8a47SPhilippe Mathieu-Daudé */
pci_host_config_write(void * opaque,hwaddr addr,uint64_t val,unsigned len)46122dc8a47SPhilippe Mathieu-Daudé static void pci_host_config_write(void *opaque, hwaddr addr,
46222dc8a47SPhilippe Mathieu-Daudé uint64_t val, unsigned len)
46322dc8a47SPhilippe Mathieu-Daudé {
46422dc8a47SPhilippe Mathieu-Daudé PCIHostState *s = opaque;
46522dc8a47SPhilippe Mathieu-Daudé
46622dc8a47SPhilippe Mathieu-Daudé if (addr != 0 || len != 4) {
46722dc8a47SPhilippe Mathieu-Daudé return;
46822dc8a47SPhilippe Mathieu-Daudé }
46922dc8a47SPhilippe Mathieu-Daudé s->config_reg = (val & 0xfffffffcULL) | (1UL << 31);
47022dc8a47SPhilippe Mathieu-Daudé }
47122dc8a47SPhilippe Mathieu-Daudé
pci_host_config_read(void * opaque,hwaddr addr,unsigned len)47222dc8a47SPhilippe Mathieu-Daudé static uint64_t pci_host_config_read(void *opaque, hwaddr addr,
47322dc8a47SPhilippe Mathieu-Daudé unsigned len)
47422dc8a47SPhilippe Mathieu-Daudé {
47522dc8a47SPhilippe Mathieu-Daudé PCIHostState *s = opaque;
47622dc8a47SPhilippe Mathieu-Daudé uint32_t val = s->config_reg;
47722dc8a47SPhilippe Mathieu-Daudé
47822dc8a47SPhilippe Mathieu-Daudé return val;
47922dc8a47SPhilippe Mathieu-Daudé }
48022dc8a47SPhilippe Mathieu-Daudé
48122dc8a47SPhilippe Mathieu-Daudé const MemoryRegionOps ppc440_pcix_host_conf_ops = {
48222dc8a47SPhilippe Mathieu-Daudé .read = pci_host_config_read,
48322dc8a47SPhilippe Mathieu-Daudé .write = pci_host_config_write,
48422dc8a47SPhilippe Mathieu-Daudé .endianness = DEVICE_LITTLE_ENDIAN,
48522dc8a47SPhilippe Mathieu-Daudé };
48622dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_realize(DeviceState * dev,Error ** errp)48722dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_realize(DeviceState *dev, Error **errp)
48822dc8a47SPhilippe Mathieu-Daudé {
48922dc8a47SPhilippe Mathieu-Daudé SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
49022dc8a47SPhilippe Mathieu-Daudé PPC440PCIXState *s;
49122dc8a47SPhilippe Mathieu-Daudé PCIHostState *h;
49222dc8a47SPhilippe Mathieu-Daudé
49322dc8a47SPhilippe Mathieu-Daudé h = PCI_HOST_BRIDGE(dev);
49422dc8a47SPhilippe Mathieu-Daudé s = PPC440_PCIX_HOST(dev);
49522dc8a47SPhilippe Mathieu-Daudé
49622dc8a47SPhilippe Mathieu-Daudé sysbus_init_irq(sbd, &s->irq);
49722dc8a47SPhilippe Mathieu-Daudé memory_region_init(&s->busmem, OBJECT(dev), "pci-mem", UINT64_MAX);
49822dc8a47SPhilippe Mathieu-Daudé memory_region_init(&s->iomem, OBJECT(dev), "pci-io", 64 * KiB);
49922dc8a47SPhilippe Mathieu-Daudé h->bus = pci_register_root_bus(dev, NULL, ppc440_pcix_set_irq,
50022dc8a47SPhilippe Mathieu-Daudé ppc440_pcix_map_irq, &s->irq, &s->busmem, &s->iomem,
5016e4acebaSBALATON Zoltan PCI_DEVFN(1, 0), 1, TYPE_PCI_BUS);
50222dc8a47SPhilippe Mathieu-Daudé
50322dc8a47SPhilippe Mathieu-Daudé memory_region_init(&s->bm, OBJECT(s), "bm-ppc440-pcix", UINT64_MAX);
50422dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->bm, 0x0, &s->busmem);
50522dc8a47SPhilippe Mathieu-Daudé address_space_init(&s->bm_as, &s->bm, "pci-bm");
50622dc8a47SPhilippe Mathieu-Daudé pci_setup_iommu(h->bus, &ppc440_iommu_ops, s);
50722dc8a47SPhilippe Mathieu-Daudé
50822dc8a47SPhilippe Mathieu-Daudé memory_region_init(&s->container, OBJECT(s), "pci-container", PCI_ALL_SIZE);
50922dc8a47SPhilippe Mathieu-Daudé memory_region_init_io(&h->conf_mem, OBJECT(s), &ppc440_pcix_host_conf_ops,
51022dc8a47SPhilippe Mathieu-Daudé h, "pci-conf-idx", 4);
51122dc8a47SPhilippe Mathieu-Daudé memory_region_init_io(&h->data_mem, OBJECT(s), &pci_host_data_le_ops,
51222dc8a47SPhilippe Mathieu-Daudé h, "pci-conf-data", 4);
51322dc8a47SPhilippe Mathieu-Daudé memory_region_init_io(&s->regs, OBJECT(s), &pci_reg_ops, s, "pci-reg",
51422dc8a47SPhilippe Mathieu-Daudé PPC440_REG_SIZE);
51522dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem);
51622dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem);
51722dc8a47SPhilippe Mathieu-Daudé memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->regs);
51822dc8a47SPhilippe Mathieu-Daudé sysbus_init_mmio(sbd, &s->container);
51922dc8a47SPhilippe Mathieu-Daudé sysbus_init_mmio(sbd, &s->iomem);
52022dc8a47SPhilippe Mathieu-Daudé }
52122dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_class_init(ObjectClass * klass,void * data)52222dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_class_init(ObjectClass *klass, void *data)
52322dc8a47SPhilippe Mathieu-Daudé {
52422dc8a47SPhilippe Mathieu-Daudé DeviceClass *dc = DEVICE_CLASS(klass);
52522dc8a47SPhilippe Mathieu-Daudé
52622dc8a47SPhilippe Mathieu-Daudé dc->realize = ppc440_pcix_realize;
527*e3d08143SPeter Maydell device_class_set_legacy_reset(dc, ppc440_pcix_reset);
52822dc8a47SPhilippe Mathieu-Daudé }
52922dc8a47SPhilippe Mathieu-Daudé
53022dc8a47SPhilippe Mathieu-Daudé static const TypeInfo ppc440_pcix_info = {
53122dc8a47SPhilippe Mathieu-Daudé .name = TYPE_PPC440_PCIX_HOST,
53222dc8a47SPhilippe Mathieu-Daudé .parent = TYPE_PCI_HOST_BRIDGE,
53322dc8a47SPhilippe Mathieu-Daudé .instance_size = sizeof(PPC440PCIXState),
53422dc8a47SPhilippe Mathieu-Daudé .class_init = ppc440_pcix_class_init,
53522dc8a47SPhilippe Mathieu-Daudé };
53622dc8a47SPhilippe Mathieu-Daudé
ppc440_pcix_register_types(void)53722dc8a47SPhilippe Mathieu-Daudé static void ppc440_pcix_register_types(void)
53822dc8a47SPhilippe Mathieu-Daudé {
53922dc8a47SPhilippe Mathieu-Daudé type_register_static(&ppc440_pcix_info);
54022dc8a47SPhilippe Mathieu-Daudé }
54122dc8a47SPhilippe Mathieu-Daudé
54222dc8a47SPhilippe Mathieu-Daudé type_init(ppc440_pcix_register_types)
543