 0f9668e0c1
			
		
	
	
		0f9668e0c1
		
	
	
	
	
		
			
			Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20220323155743.1585078-33-marcandre.lureau@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
		
			
				
	
	
		
			369 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			369 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU PowerPC PowerNV (POWER8) PHB3 model
 | |
|  *
 | |
|  * Copyright (c) 2014-2020, IBM Corporation.
 | |
|  *
 | |
|  * This code is licensed under the GPL version 2 or later. See the
 | |
|  * COPYING file in the top-level directory.
 | |
|  */
 | |
| #include "qemu/osdep.h"
 | |
| #include "qapi/error.h"
 | |
| #include "qemu/log.h"
 | |
| #include "target/ppc/cpu.h"
 | |
| #include "hw/ppc/fdt.h"
 | |
| #include "hw/pci-host/pnv_phb3_regs.h"
 | |
| #include "hw/pci-host/pnv_phb3.h"
 | |
| #include "hw/ppc/pnv.h"
 | |
| #include "hw/ppc/pnv_xscom.h"
 | |
| #include "hw/pci/pci_bridge.h"
 | |
| #include "hw/pci/pci_bus.h"
 | |
| 
 | |
| #include <libfdt.h>
 | |
| 
 | |
| #define phb3_pbcq_error(pbcq, fmt, ...)                                 \
 | |
|     qemu_log_mask(LOG_GUEST_ERROR, "phb3_pbcq[%d:%d]: " fmt "\n",       \
 | |
|                   (pbcq)->phb->chip_id, (pbcq)->phb->phb_id, ## __VA_ARGS__)
 | |
| 
 | |
| static uint64_t pnv_pbcq_nest_xscom_read(void *opaque, hwaddr addr,
 | |
|                                          unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t offset = addr >> 3;
 | |
| 
 | |
|     return pbcq->nest_regs[offset];
 | |
| }
 | |
| 
 | |
| static uint64_t pnv_pbcq_pci_xscom_read(void *opaque, hwaddr addr,
 | |
|                                         unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t offset = addr >> 3;
 | |
| 
 | |
|     return pbcq->pci_regs[offset];
 | |
| }
 | |
| 
 | |
| static uint64_t pnv_pbcq_spci_xscom_read(void *opaque, hwaddr addr,
 | |
|                                          unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t offset = addr >> 3;
 | |
| 
 | |
|     if (offset == PBCQ_SPCI_ASB_DATA) {
 | |
|         return pnv_phb3_reg_read(pbcq->phb,
 | |
|                                  pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR], 8);
 | |
|     }
 | |
|     return pbcq->spci_regs[offset];
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_update_map(PnvPBCQState *pbcq)
 | |
| {
 | |
|     uint64_t bar_en = pbcq->nest_regs[PBCQ_NEST_BAR_EN];
 | |
|     uint64_t bar, mask, size;
 | |
| 
 | |
|     /*
 | |
|      * NOTE: This will really not work well if those are remapped
 | |
|      * after the PHB has created its sub regions. We could do better
 | |
|      * if we had a way to resize regions but we don't really care
 | |
|      * that much in practice as the stuff below really only happens
 | |
|      * once early during boot
 | |
|      */
 | |
| 
 | |
|     /* Handle unmaps */
 | |
|     if (memory_region_is_mapped(&pbcq->mmbar0) &&
 | |
|         !(bar_en & PBCQ_NEST_BAR_EN_MMIO0)) {
 | |
|         memory_region_del_subregion(get_system_memory(), &pbcq->mmbar0);
 | |
|     }
 | |
|     if (memory_region_is_mapped(&pbcq->mmbar1) &&
 | |
|         !(bar_en & PBCQ_NEST_BAR_EN_MMIO1)) {
 | |
|         memory_region_del_subregion(get_system_memory(), &pbcq->mmbar1);
 | |
|     }
 | |
|     if (memory_region_is_mapped(&pbcq->phbbar) &&
 | |
|         !(bar_en & PBCQ_NEST_BAR_EN_PHB)) {
 | |
|         memory_region_del_subregion(get_system_memory(), &pbcq->phbbar);
 | |
|     }
 | |
| 
 | |
|     /* Update PHB */
 | |
|     pnv_phb3_update_regions(pbcq->phb);
 | |
| 
 | |
|     /* Handle maps */
 | |
|     if (!memory_region_is_mapped(&pbcq->mmbar0) &&
 | |
|         (bar_en & PBCQ_NEST_BAR_EN_MMIO0)) {
 | |
|         bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] >> 14;
 | |
|         mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0];
 | |
|         size = ((~mask) >> 14) + 1;
 | |
|         memory_region_init(&pbcq->mmbar0, OBJECT(pbcq), "pbcq-mmio0", size);
 | |
|         memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar0);
 | |
|         pbcq->mmio0_base = bar;
 | |
|         pbcq->mmio0_size = size;
 | |
|     }
 | |
|     if (!memory_region_is_mapped(&pbcq->mmbar1) &&
 | |
|         (bar_en & PBCQ_NEST_BAR_EN_MMIO1)) {
 | |
|         bar = pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] >> 14;
 | |
|         mask = pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1];
 | |
|         size = ((~mask) >> 14) + 1;
 | |
|         memory_region_init(&pbcq->mmbar1, OBJECT(pbcq), "pbcq-mmio1", size);
 | |
|         memory_region_add_subregion(get_system_memory(), bar, &pbcq->mmbar1);
 | |
|         pbcq->mmio1_base = bar;
 | |
|         pbcq->mmio1_size = size;
 | |
|     }
 | |
|     if (!memory_region_is_mapped(&pbcq->phbbar)
 | |
|         && (bar_en & PBCQ_NEST_BAR_EN_PHB)) {
 | |
|         bar = pbcq->nest_regs[PBCQ_NEST_PHB_BAR] >> 14;
 | |
|         size = 0x1000;
 | |
|         memory_region_init(&pbcq->phbbar, OBJECT(pbcq), "pbcq-phb", size);
 | |
|         memory_region_add_subregion(get_system_memory(), bar, &pbcq->phbbar);
 | |
|     }
 | |
| 
 | |
|     /* Update PHB */
 | |
|     pnv_phb3_update_regions(pbcq->phb);
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_nest_xscom_write(void *opaque, hwaddr addr,
 | |
|                                 uint64_t val, unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t reg = addr >> 3;
 | |
| 
 | |
|     switch (reg) {
 | |
|     case PBCQ_NEST_MMIO_BAR0:
 | |
|     case PBCQ_NEST_MMIO_BAR1:
 | |
|     case PBCQ_NEST_MMIO_MASK0:
 | |
|     case PBCQ_NEST_MMIO_MASK1:
 | |
|         if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] &
 | |
|             (PBCQ_NEST_BAR_EN_MMIO0 |
 | |
|              PBCQ_NEST_BAR_EN_MMIO1)) {
 | |
|             phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported");
 | |
|         }
 | |
|         pbcq->nest_regs[reg] = val & 0xffffffffc0000000ull;
 | |
|         break;
 | |
|     case PBCQ_NEST_PHB_BAR:
 | |
|         if (pbcq->nest_regs[PBCQ_NEST_BAR_EN] & PBCQ_NEST_BAR_EN_PHB) {
 | |
|             phb3_pbcq_error(pbcq, "Changing enabled BAR unsupported");
 | |
|         }
 | |
|         pbcq->nest_regs[reg] = val & 0xfffffffffc000000ull;
 | |
|         break;
 | |
|     case PBCQ_NEST_BAR_EN:
 | |
|         pbcq->nest_regs[reg] = val & 0xf800000000000000ull;
 | |
|         pnv_pbcq_update_map(pbcq);
 | |
|         pnv_phb3_remap_irqs(pbcq->phb);
 | |
|         break;
 | |
|     case PBCQ_NEST_IRSN_COMPARE:
 | |
|     case PBCQ_NEST_IRSN_MASK:
 | |
|         pbcq->nest_regs[reg] = val & PBCQ_NEST_IRSN_COMP;
 | |
|         pnv_phb3_remap_irqs(pbcq->phb);
 | |
|         break;
 | |
|     case PBCQ_NEST_LSI_SRC_ID:
 | |
|         pbcq->nest_regs[reg] = val & PBCQ_NEST_LSI_SRC;
 | |
|         pnv_phb3_remap_irqs(pbcq->phb);
 | |
|         break;
 | |
|     default:
 | |
|         phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
 | |
|                         addr, val);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_pci_xscom_write(void *opaque, hwaddr addr,
 | |
|                                      uint64_t val, unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t reg = addr >> 3;
 | |
| 
 | |
|     switch (reg) {
 | |
|     case PBCQ_PCI_BAR2:
 | |
|         pbcq->pci_regs[reg] = val & 0xfffffffffc000000ull;
 | |
|         pnv_pbcq_update_map(pbcq);
 | |
|         break;
 | |
|     default:
 | |
|         phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
 | |
|                         addr, val);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_spci_xscom_write(void *opaque, hwaddr addr,
 | |
|                                 uint64_t val, unsigned size)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(opaque);
 | |
|     uint32_t reg = addr >> 3;
 | |
| 
 | |
|     switch (reg) {
 | |
|     case PBCQ_SPCI_ASB_ADDR:
 | |
|         pbcq->spci_regs[reg] = val & 0xfff;
 | |
|         break;
 | |
|     case PBCQ_SPCI_ASB_STATUS:
 | |
|         pbcq->spci_regs[reg] &= ~val;
 | |
|         break;
 | |
|     case PBCQ_SPCI_ASB_DATA:
 | |
|         pnv_phb3_reg_write(pbcq->phb, pbcq->spci_regs[PBCQ_SPCI_ASB_ADDR],
 | |
|                            val, 8);
 | |
|         break;
 | |
|     case PBCQ_SPCI_AIB_CAPP_EN:
 | |
|     case PBCQ_SPCI_CAPP_SEC_TMR:
 | |
|         break;
 | |
|     default:
 | |
|         phb3_pbcq_error(pbcq, "%s @0x%"HWADDR_PRIx"=%"PRIx64, __func__,
 | |
|                         addr, val);
 | |
|     }
 | |
| }
 | |
| 
 | |
| static const MemoryRegionOps pnv_pbcq_nest_xscom_ops = {
 | |
|     .read = pnv_pbcq_nest_xscom_read,
 | |
|     .write = pnv_pbcq_nest_xscom_write,
 | |
|     .valid.min_access_size = 8,
 | |
|     .valid.max_access_size = 8,
 | |
|     .impl.min_access_size = 8,
 | |
|     .impl.max_access_size = 8,
 | |
|     .endianness = DEVICE_BIG_ENDIAN,
 | |
| };
 | |
| 
 | |
| static const MemoryRegionOps pnv_pbcq_pci_xscom_ops = {
 | |
|     .read = pnv_pbcq_pci_xscom_read,
 | |
|     .write = pnv_pbcq_pci_xscom_write,
 | |
|     .valid.min_access_size = 8,
 | |
|     .valid.max_access_size = 8,
 | |
|     .impl.min_access_size = 8,
 | |
|     .impl.max_access_size = 8,
 | |
|     .endianness = DEVICE_BIG_ENDIAN,
 | |
| };
 | |
| 
 | |
| static const MemoryRegionOps pnv_pbcq_spci_xscom_ops = {
 | |
|     .read = pnv_pbcq_spci_xscom_read,
 | |
|     .write = pnv_pbcq_spci_xscom_write,
 | |
|     .valid.min_access_size = 8,
 | |
|     .valid.max_access_size = 8,
 | |
|     .impl.min_access_size = 8,
 | |
|     .impl.max_access_size = 8,
 | |
|     .endianness = DEVICE_BIG_ENDIAN,
 | |
| };
 | |
| 
 | |
| static void pnv_pbcq_default_bars(PnvPBCQState *pbcq)
 | |
| {
 | |
|     uint64_t mm0, mm1, reg;
 | |
|     PnvPHB3 *phb = pbcq->phb;
 | |
| 
 | |
|     mm0 = 0x3d00000000000ull + 0x4000000000ull * phb->chip_id +
 | |
|             0x1000000000ull * phb->phb_id;
 | |
|     mm1 = 0x3ff8000000000ull + 0x0200000000ull * phb->chip_id +
 | |
|             0x0080000000ull * phb->phb_id;
 | |
|     reg = 0x3fffe40000000ull + 0x0000400000ull * phb->chip_id +
 | |
|             0x0000100000ull * phb->phb_id;
 | |
| 
 | |
|     pbcq->nest_regs[PBCQ_NEST_MMIO_BAR0] = mm0 << 14;
 | |
|     pbcq->nest_regs[PBCQ_NEST_MMIO_BAR1] = mm1 << 14;
 | |
|     pbcq->nest_regs[PBCQ_NEST_PHB_BAR] = reg << 14;
 | |
|     pbcq->nest_regs[PBCQ_NEST_MMIO_MASK0] = 0x3fff000000000ull << 14;
 | |
|     pbcq->nest_regs[PBCQ_NEST_MMIO_MASK1] = 0x3ffff80000000ull << 14;
 | |
|     pbcq->pci_regs[PBCQ_PCI_BAR2] = reg << 14;
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_realize(DeviceState *dev, Error **errp)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(dev);
 | |
|     PnvPHB3 *phb;
 | |
|     char name[32];
 | |
| 
 | |
|     assert(pbcq->phb);
 | |
|     phb = pbcq->phb;
 | |
| 
 | |
|     /* TODO: Fix OPAL to do that: establish default BAR values */
 | |
|     pnv_pbcq_default_bars(pbcq);
 | |
| 
 | |
|     /* Initialize the XSCOM region for the PBCQ registers */
 | |
|     snprintf(name, sizeof(name), "xscom-pbcq-nest-%d.%d",
 | |
|              phb->chip_id, phb->phb_id);
 | |
|     pnv_xscom_region_init(&pbcq->xscom_nest_regs, OBJECT(dev),
 | |
|                           &pnv_pbcq_nest_xscom_ops, pbcq, name,
 | |
|                           PNV_XSCOM_PBCQ_NEST_SIZE);
 | |
|     snprintf(name, sizeof(name), "xscom-pbcq-pci-%d.%d",
 | |
|              phb->chip_id, phb->phb_id);
 | |
|     pnv_xscom_region_init(&pbcq->xscom_pci_regs, OBJECT(dev),
 | |
|                           &pnv_pbcq_pci_xscom_ops, pbcq, name,
 | |
|                           PNV_XSCOM_PBCQ_PCI_SIZE);
 | |
|     snprintf(name, sizeof(name), "xscom-pbcq-spci-%d.%d",
 | |
|              phb->chip_id, phb->phb_id);
 | |
|     pnv_xscom_region_init(&pbcq->xscom_spci_regs, OBJECT(dev),
 | |
|                           &pnv_pbcq_spci_xscom_ops, pbcq, name,
 | |
|                           PNV_XSCOM_PBCQ_SPCI_SIZE);
 | |
| 
 | |
|     /* Populate the XSCOM address space. */
 | |
|     pnv_xscom_add_subregion(phb->chip,
 | |
|                             PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id,
 | |
|                             &pbcq->xscom_nest_regs);
 | |
|     pnv_xscom_add_subregion(phb->chip,
 | |
|                             PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id,
 | |
|                             &pbcq->xscom_pci_regs);
 | |
|     pnv_xscom_add_subregion(phb->chip,
 | |
|                             PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id,
 | |
|                             &pbcq->xscom_spci_regs);
 | |
| }
 | |
| 
 | |
| static int pnv_pbcq_dt_xscom(PnvXScomInterface *dev, void *fdt,
 | |
|                              int xscom_offset)
 | |
| {
 | |
|     const char compat[] = "ibm,power8-pbcq";
 | |
|     PnvPHB3 *phb = PNV_PBCQ(dev)->phb;
 | |
|     char *name;
 | |
|     int offset;
 | |
|     uint32_t lpc_pcba = PNV_XSCOM_PBCQ_NEST_BASE + 0x400 * phb->phb_id;
 | |
|     uint32_t reg[] = {
 | |
|         cpu_to_be32(lpc_pcba),
 | |
|         cpu_to_be32(PNV_XSCOM_PBCQ_NEST_SIZE),
 | |
|         cpu_to_be32(PNV_XSCOM_PBCQ_PCI_BASE + 0x400 * phb->phb_id),
 | |
|         cpu_to_be32(PNV_XSCOM_PBCQ_PCI_SIZE),
 | |
|         cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_BASE + 0x040 * phb->phb_id),
 | |
|         cpu_to_be32(PNV_XSCOM_PBCQ_SPCI_SIZE)
 | |
|     };
 | |
| 
 | |
|     name = g_strdup_printf("pbcq@%x", lpc_pcba);
 | |
|     offset = fdt_add_subnode(fdt, xscom_offset, name);
 | |
|     _FDT(offset);
 | |
|     g_free(name);
 | |
| 
 | |
|     _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))));
 | |
| 
 | |
|     _FDT((fdt_setprop_cell(fdt, offset, "ibm,phb-index", phb->phb_id)));
 | |
|     _FDT((fdt_setprop_cell(fdt, offset, "ibm,chip-id", phb->chip_id)));
 | |
|     _FDT((fdt_setprop(fdt, offset, "compatible", compat,
 | |
|                       sizeof(compat))));
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void phb3_pbcq_instance_init(Object *obj)
 | |
| {
 | |
|     PnvPBCQState *pbcq = PNV_PBCQ(obj);
 | |
| 
 | |
|     object_property_add_link(obj, "phb", TYPE_PNV_PHB3,
 | |
|                              (Object **)&pbcq->phb,
 | |
|                              object_property_allow_set_link,
 | |
|                              OBJ_PROP_LINK_STRONG);
 | |
| }
 | |
| 
 | |
| static void pnv_pbcq_class_init(ObjectClass *klass, void *data)
 | |
| {
 | |
|     DeviceClass *dc = DEVICE_CLASS(klass);
 | |
|     PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass);
 | |
| 
 | |
|     xdc->dt_xscom = pnv_pbcq_dt_xscom;
 | |
| 
 | |
|     dc->realize = pnv_pbcq_realize;
 | |
|     dc->user_creatable = false;
 | |
| }
 | |
| 
 | |
| static const TypeInfo pnv_pbcq_type_info = {
 | |
|     .name          = TYPE_PNV_PBCQ,
 | |
|     .parent        = TYPE_DEVICE,
 | |
|     .instance_size = sizeof(PnvPBCQState),
 | |
|     .instance_init = phb3_pbcq_instance_init,
 | |
|     .class_init    = pnv_pbcq_class_init,
 | |
|     .interfaces    = (InterfaceInfo[]) {
 | |
|         { TYPE_PNV_XSCOM_INTERFACE },
 | |
|         { }
 | |
|     }
 | |
| };
 | |
| 
 | |
| static void pnv_pbcq_register_types(void)
 | |
| {
 | |
|     type_register_static(&pnv_pbcq_type_info);
 | |
| }
 | |
| 
 | |
| type_init(pnv_pbcq_register_types)
 |