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é 55*6e4acebaSBALATON 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é 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 */ 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 */ 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é 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: 174*6e4acebaSBALATON 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é 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: 305*6e4acebaSBALATON 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é 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é */ 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é 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é 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é */ 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é 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é 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, 501*6e4acebaSBALATON 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é 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; 52722dc8a47SPhilippe Mathieu-Daudé dc->reset = 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é 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