diff options
author | Stefan Hajnoczi <stefanha@redhat.com> | 2023-11-08 20:35:00 +0800 |
---|---|---|
committer | Stefan Hajnoczi <stefanha@redhat.com> | 2023-11-08 20:35:00 +0800 |
commit | a3c3aaa846ad61b801e7196482dcf4afb8ba34e4 (patch) | |
tree | 49fe3b307f1dacfc6a1b3f85b2175e62019a9f19 | |
parent | f09744ddc2424bc6a76702e1951a8d24917062d6 (diff) | |
parent | 5bf4ceec109289356f50f69bf277c99b045182e7 (diff) |
Merge tag 'pull-ppc-20231107' of https://gitlab.com/danielhb/qemu into staging
ppc patch queue for 2023-11-07:
This queue, the last one before the 8.2 feature freeze, has miscellanous
changes that includes new PowerNV features and the new AmigaONE XE
board.
# -----BEGIN PGP SIGNATURE-----
#
# iIwEABYKADQWIQQX6/+ZI9AYAK8oOBk82cqW3gMxZAUCZUqiORYcZGFuaWVsaGI0
# MTNAZ21haWwuY29tAAoJEDzZypbeAzFkBSUA/2qm8CyrRqY5+tsjtWQqZmPZ3L1F
# CgnXFNqtY2tzbTe5AQCi6FeQBEmXbZYVfryZyA+CQ4DUERc+18pe6hV3bBR9Cg==
# =cnHS
# -----END PGP SIGNATURE-----
# gpg: Signature made Wed 08 Nov 2023 04:46:49 HKT
# gpg: using EDDSA key 17EBFF9923D01800AF2838193CD9CA96DE033164
# gpg: issuer "danielhb413@gmail.com"
# gpg: Good signature from "Daniel Henrique Barboza <danielhb413@gmail.com>" [unknown]
# gpg: WARNING: The key's User ID is not certified with a trusted signature!
# gpg: There is no indication that the signature belongs to the owner.
# Primary key fingerprint: 17EB FF99 23D0 1800 AF28 3819 3CD9 CA96 DE03 3164
* tag 'pull-ppc-20231107' of https://gitlab.com/danielhb/qemu:
ppc: qtest already exports qtest_rtas_call()
hw/pci-host: Update PHB5 XSCOM registers
ppc/pnv: Fix number of I2C engines and ports for power9/10
ppc/pnv: Connect PNV I2C controller to powernv10
ppc/pnv: Connect I2C controller model to powernv9 chip
ppc/pnv: Add an I2C controller model
tests/avocado: Add test for amigaone board
hw/ppc: Add emulation of AmigaOne XE board
hw/pci-host: Add emulation of Mai Logic Articia S
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
-rw-r--r-- | MAINTAINERS | 8 | ||||
-rw-r--r-- | configs/devices/ppc-softmmu/default.mak | 1 | ||||
-rw-r--r-- | hw/pci-host/Kconfig | 5 | ||||
-rw-r--r-- | hw/pci-host/articia.c | 293 | ||||
-rw-r--r-- | hw/pci-host/meson.build | 2 | ||||
-rw-r--r-- | hw/pci-host/pnv_phb4.c | 46 | ||||
-rw-r--r-- | hw/pci-host/pnv_phb4_pec.c | 33 | ||||
-rw-r--r-- | hw/ppc/Kconfig | 7 | ||||
-rw-r--r-- | hw/ppc/amigaone.c | 166 | ||||
-rw-r--r-- | hw/ppc/meson.build | 3 | ||||
-rw-r--r-- | hw/ppc/pnv.c | 61 | ||||
-rw-r--r-- | hw/ppc/pnv_i2c.c | 697 | ||||
-rw-r--r-- | hw/ppc/spapr_rtas.c | 5 | ||||
-rw-r--r-- | include/hw/pci-host/articia.h | 17 | ||||
-rw-r--r-- | include/hw/pci-host/pnv_phb4.h | 2 | ||||
-rw-r--r-- | include/hw/pci-host/pnv_phb4_regs.h | 4 | ||||
-rw-r--r-- | include/hw/ppc/pnv_chip.h | 10 | ||||
-rw-r--r-- | include/hw/ppc/pnv_i2c.h | 38 | ||||
-rw-r--r-- | include/hw/ppc/pnv_xscom.h | 6 | ||||
-rw-r--r-- | include/hw/ppc/spapr_rtas.h | 10 | ||||
-rw-r--r-- | tests/avocado/ppc_amiga.py | 38 |
21 files changed, 1414 insertions, 38 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 934ab90f9c..33bb1ba0ea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1539,6 +1539,14 @@ F: hw/pci-host/mv64361.c F: hw/pci-host/mv643xx.h F: include/hw/pci-host/mv64361.h +amigaone +M: BALATON Zoltan <balaton@eik.bme.hu> +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/amigaone.c +F: hw/pci-host/articia.c +F: include/hw/pci-host/articia.h + Virtual Open Firmware (VOF) M: Alexey Kardashevskiy <aik@ozlabs.ru> R: David Gibson <david@gibson.dropbear.id.au> diff --git a/configs/devices/ppc-softmmu/default.mak b/configs/devices/ppc-softmmu/default.mak index a887f5438b..b85fd2bcd7 100644 --- a/configs/devices/ppc-softmmu/default.mak +++ b/configs/devices/ppc-softmmu/default.mak @@ -14,6 +14,7 @@ CONFIG_SAM460EX=y CONFIG_MAC_OLDWORLD=y CONFIG_MAC_NEWWORLD=y +CONFIG_AMIGAONE=y CONFIG_PEGASOS2=y # For PReP diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 54a609d2ca..f046d76a68 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -73,6 +73,11 @@ config SH_PCI bool select PCI +config ARTICIA + bool + select PCI + select I8259 + config MV64361 bool select PCI diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c new file mode 100644 index 0000000000..f3fcc49f81 --- /dev/null +++ b/hw/pci-host/articia.c @@ -0,0 +1,293 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "hw/pci/pci_device.h" +#include "hw/pci/pci_host.h" +#include "hw/irq.h" +#include "hw/i2c/bitbang_i2c.h" +#include "hw/intc/i8259.h" +#include "hw/pci-host/articia.h" + +/* + * This is a minimal emulation of this chip as used in AmigaOne board. + * Most features are missing but those are not needed by firmware and guests. + */ + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaState, ARTICIA) + +OBJECT_DECLARE_SIMPLE_TYPE(ArticiaHostState, ARTICIA_PCI_HOST) +struct ArticiaHostState { + PCIDevice parent_obj; + + ArticiaState *as; +}; + +/* TYPE_ARTICIA */ + +struct ArticiaState { + PCIHostState parent_obj; + + qemu_irq irq[PCI_NUM_PINS]; + MemoryRegion io; + MemoryRegion mem; + MemoryRegion reg; + + bitbang_i2c_interface smbus; + uint32_t gpio; /* bits 0-7 in, 8-15 out, 16-23 direction (0 in, 1 out) */ + hwaddr gpio_base; + MemoryRegion gpio_reg; +}; + +static uint64_t articia_gpio_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + + return (s->gpio >> (addr * 8)) & 0xff; +} + +static void articia_gpio_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + uint32_t sh = addr * 8; + + if (addr == 0) { + /* in bits read only? */ + return; + } + + if ((s->gpio & (0xff << sh)) != (val & 0xff) << sh) { + s->gpio &= ~(0xff << sh | 0xff); + s->gpio |= (val & 0xff) << sh; + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SDA, + s->gpio & BIT(16) ? + !!(s->gpio & BIT(8)) : 1); + if ((s->gpio & BIT(17))) { + s->gpio &= ~BIT(0); + s->gpio |= bitbang_i2c_set(&s->smbus, BITBANG_I2C_SCL, + !!(s->gpio & BIT(9))); + } + } +} + +static const MemoryRegionOps articia_gpio_ops = { + .read = articia_gpio_read, + .write = articia_gpio_write, + .valid.min_access_size = 1, + .valid.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t articia_reg_read(void *opaque, hwaddr addr, unsigned int size) +{ + ArticiaState *s = opaque; + uint64_t ret = UINT_MAX; + + switch (addr) { + case 0xc00cf8: + ret = pci_host_conf_le_ops.read(PCI_HOST_BRIDGE(s), 0, size); + break; + case 0xe00cfc ... 0xe00cff: + ret = pci_host_data_le_ops.read(PCI_HOST_BRIDGE(s), addr - 0xe00cfc, size); + break; + case 0xf00000: + ret = pic_read_irq(isa_pic); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register read 0x%" + HWADDR_PRIx " %d\n", __func__, addr, size); + break; + } + return ret; +} + +static void articia_reg_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int size) +{ + ArticiaState *s = opaque; + + switch (addr) { + case 0xc00cf8: + pci_host_conf_le_ops.write(PCI_HOST_BRIDGE(s), 0, val, size); + break; + case 0xe00cfc ... 0xe00cff: + pci_host_data_le_ops.write(PCI_HOST_BRIDGE(s), addr, val, size); + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: Unimplemented register write 0x%" + HWADDR_PRIx " %d <- %"PRIx64"\n", __func__, addr, size, val); + break; + } +} + +static const MemoryRegionOps articia_reg_ops = { + .read = articia_reg_read, + .write = articia_reg_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void articia_pcihost_set_irq(void *opaque, int n, int level) +{ + ArticiaState *s = opaque; + qemu_set_irq(s->irq[n], level); +} + +/* + * AmigaOne SE PCI slot to IRQ routing + * + * repository: https://source.denx.de/u-boot/custodians/u-boot-avr32.git + * refspec: v2010.06 + * file: board/MAI/AmigaOneG3SE/articiaS_pci.c + */ +static int amigaone_pcihost_bus0_map_irq(PCIDevice *pdev, int pin) +{ + int devfn_slot = PCI_SLOT(pdev->devfn); + + switch (devfn_slot) { + case 6: /* On board ethernet */ + return 3; + case 7: /* South bridge */ + return pin; + default: /* PCI Slot 1 Devfn slot 8, Slot 2 Devfn 9, Slot 3 Devfn 10 */ + return pci_swizzle(devfn_slot, pin); + } + +} + +static void articia_realize(DeviceState *dev, Error **errp) +{ + ArticiaState *s = ARTICIA(dev); + PCIHostState *h = PCI_HOST_BRIDGE(dev); + PCIDevice *pdev; + + bitbang_i2c_init(&s->smbus, i2c_init_bus(dev, "smbus")); + memory_region_init_io(&s->gpio_reg, OBJECT(s), &articia_gpio_ops, s, + TYPE_ARTICIA, 4); + + memory_region_init(&s->mem, OBJECT(dev), "pci-mem", UINT64_MAX); + memory_region_init(&s->io, OBJECT(dev), "pci-io", 0xc00000); + memory_region_init_io(&s->reg, OBJECT(s), &articia_reg_ops, s, + TYPE_ARTICIA, 0x1000000); + memory_region_add_subregion_overlap(&s->reg, 0, &s->io, 1); + + /* devfn_min is 8 that matches first PCI slot in AmigaOne */ + h->bus = pci_register_root_bus(dev, NULL, articia_pcihost_set_irq, + amigaone_pcihost_bus0_map_irq, dev, &s->mem, + &s->io, PCI_DEVFN(8, 0), 4, TYPE_PCI_BUS); + pdev = pci_create_simple_multifunction(h->bus, PCI_DEVFN(0, 0), + TYPE_ARTICIA_PCI_HOST); + ARTICIA_PCI_HOST(pdev)->as = s; + pci_create_simple(h->bus, PCI_DEVFN(0, 1), TYPE_ARTICIA_PCI_BRIDGE); + + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->reg); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->mem); + qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); +} + +static void articia_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = articia_realize; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +/* TYPE_ARTICIA_PCI_HOST */ + +static void articia_pci_host_cfg_write(PCIDevice *d, uint32_t addr, + uint32_t val, int len) +{ + ArticiaState *s = ARTICIA_PCI_HOST(d)->as; + + pci_default_write_config(d, addr, val, len); + switch (addr) { + case 0x40: + s->gpio_base = val; + break; + case 0x44: + if (val != 0x11) { + /* FIXME what do the bits actually mean? */ + break; + } + if (memory_region_is_mapped(&s->gpio_reg)) { + memory_region_del_subregion(&s->io, &s->gpio_reg); + } + memory_region_add_subregion(&s->io, s->gpio_base + 0x38, &s->gpio_reg); + break; + } +} + +static void articia_pci_host_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->config_write = articia_pci_host_cfg_write; + k->vendor_id = 0x10cc; + k->device_id = 0x0660; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +/* TYPE_ARTICIA_PCI_BRIDGE */ + +static void articia_pci_bridge_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->vendor_id = 0x10cc; + k->device_id = 0x0661; + k->class_id = PCI_CLASS_BRIDGE_HOST; + /* + * PCI-facing part of the host bridge, + * not usable without the host-facing part + */ + dc->user_creatable = false; +} + +static const TypeInfo articia_types[] = { + { + .name = TYPE_ARTICIA, + .parent = TYPE_PCI_HOST_BRIDGE, + .instance_size = sizeof(ArticiaState), + .class_init = articia_class_init, + }, + { + .name = TYPE_ARTICIA_PCI_HOST, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(ArticiaHostState), + .class_init = articia_pci_host_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, + { + .name = TYPE_ARTICIA_PCI_BRIDGE, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(PCIDevice), + .class_init = articia_pci_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + }, +}; + +DEFINE_TYPES(articia_types) diff --git a/hw/pci-host/meson.build b/hw/pci-host/meson.build index f891f026cb..de7bfb5a62 100644 --- a/hw/pci-host/meson.build +++ b/hw/pci-host/meson.build @@ -20,6 +20,8 @@ pci_ss.add(when: 'CONFIG_GRACKLE_PCI', if_true: files('grackle.c')) pci_ss.add(when: 'CONFIG_UNIN_PCI', if_true: files('uninorth.c')) # PowerPC E500 boards pci_ss.add(when: 'CONFIG_PPCE500_PCI', if_true: files('ppce500.c')) +# AmigaOne +pci_ss.add(when: 'CONFIG_ARTICIA', if_true: files('articia.c')) # Pegasos2 pci_ss.add(when: 'CONFIG_MV64361', if_true: files('mv64361.c')) diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 37c7afc18c..075499d36d 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -855,7 +855,7 @@ static uint64_t pnv_pec_stk_nest_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->nest_regs[reg]; } @@ -1000,7 +1000,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_NEST_STK_PCI_NEST_FIR: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_CLR: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] &= val; @@ -1009,7 +1009,8 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR] |= val; break; case PEC_NEST_STK_PCI_NEST_FIR_MSK: - phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val; + phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] = val & + PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_MSKC: phb->nest_regs[PEC_NEST_STK_PCI_NEST_FIR_MSK] &= val; @@ -1019,7 +1020,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, break; case PEC_NEST_STK_PCI_NEST_FIR_ACT0: case PEC_NEST_STK_PCI_NEST_FIR_ACT1: - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_PCI_NEST_FIR_WOF: phb->nest_regs[reg] = 0; @@ -1030,7 +1031,7 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, /* Flag error ? */ break; case PEC_NEST_STK_PBCQ_MODE: - phb->nest_regs[reg] = val & 0xff00000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; case PEC_NEST_STK_MMIO_BAR0: case PEC_NEST_STK_MMIO_BAR0_MASK: @@ -1041,28 +1042,33 @@ static void pnv_pec_stk_nest_xscom_write(void *opaque, hwaddr addr, PEC_NEST_STK_BAR_EN_MMIO1)) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffff000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 39); break; case PEC_NEST_STK_PHB_REGS_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_PHB) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xffffffffffc00000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 41); break; case PEC_NEST_STK_INT_BAR: if (phb->nest_regs[PEC_NEST_STK_BAR_EN] & PEC_NEST_STK_BAR_EN_INT) { phb_pec_error(pec, "Changing enabled BAR unsupported"); } - phb->nest_regs[reg] = val & 0xfffffff000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); break; case PEC_NEST_STK_BAR_EN: - phb->nest_regs[reg] = val & 0xf000000000000000ull; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 3); pnv_pec_phb_update_map(phb); break; case PEC_NEST_STK_DATA_FRZ_TYPE: - case PEC_NEST_STK_PBCQ_TUN_BAR: /* Not used for now */ - phb->nest_regs[reg] = val; + phb->nest_regs[reg] = val & PPC_BITMASK(0, 27); + break; + case PEC_NEST_STK_PBCQ_SPARSE_PAGE: + phb->nest_regs[reg] = val & PPC_BITMASK(3, 5); + break; + case PEC_NEST_STK_PBCQ_CACHE_INJ: + phb->nest_regs[reg] = val & PPC_BITMASK(0, 7); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec: nest_xscom_write 0x%"HWADDR_PRIx @@ -1086,7 +1092,7 @@ static uint64_t pnv_pec_stk_pci_xscom_read(void *opaque, hwaddr addr, PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are read-able */ return phb->pci_regs[reg]; } @@ -1095,10 +1101,9 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, { PnvPHB4 *phb = PNV_PHB4(opaque); uint32_t reg = addr >> 3; - switch (reg) { case PEC_PCI_STK_PCI_FIR: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_CLR: phb->pci_regs[PEC_PCI_STK_PCI_FIR] &= val; @@ -1107,7 +1112,7 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, phb->pci_regs[PEC_PCI_STK_PCI_FIR] |= val; break; case PEC_PCI_STK_PCI_FIR_MSK: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_MSKC: phb->pci_regs[PEC_PCI_STK_PCI_FIR_MSK] &= val; @@ -1117,20 +1122,25 @@ static void pnv_pec_stk_pci_xscom_write(void *opaque, hwaddr addr, break; case PEC_PCI_STK_PCI_FIR_ACT0: case PEC_PCI_STK_PCI_FIR_ACT1: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & PPC_BITMASK(0, 5); break; case PEC_PCI_STK_PCI_FIR_WOF: phb->pci_regs[reg] = 0; break; case PEC_PCI_STK_ETU_RESET: - phb->pci_regs[reg] = val & 0x8000000000000000ull; + phb->pci_regs[reg] = val & PPC_BIT(0); /* TODO: Implement reset */ break; case PEC_PCI_STK_PBAIB_ERR_REPORT: break; case PEC_PCI_STK_PBAIB_TX_CMD_CRED: + phb->pci_regs[reg] = val & + ((PPC_BITMASK(0, 2) | PPC_BITMASK(10, 18) + | PPC_BITMASK(26, 34) | PPC_BITMASK(41, 50) + | PPC_BITMASK(58, 63))); + break; case PEC_PCI_STK_PBAIB_TX_DAT_CRED: - phb->pci_regs[reg] = val; + phb->pci_regs[reg] = val & (PPC_BITMASK(33, 34) | PPC_BITMASK(44, 47)); break; default: qemu_log_mask(LOG_UNIMP, "phb4_pec_stk: pci_xscom_write 0x%"HWADDR_PRIx diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index 3b2850f7a3..ce8e228f98 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -34,7 +34,7 @@ static uint64_t pnv_pec_nest_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->nest_regs[reg]; } @@ -45,18 +45,36 @@ static void pnv_pec_nest_xscom_write(void *opaque, hwaddr addr, uint32_t reg = addr >> 3; switch (reg) { - case PEC_NEST_PBCQ_HW_CONFIG: case PEC_NEST_DROP_PRIO_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 25); + break; case PEC_NEST_PBCQ_ERR_INJECT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 11); + break; case PEC_NEST_PCI_NEST_CLK_TRACE_CTL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 16); + break; case PEC_NEST_PBCQ_PMON_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 37); + break; case PEC_NEST_PBCQ_PBUS_ADDR_EXT: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 6); + break; case PEC_NEST_PBCQ_PRED_VEC_TIMEOUT: - case PEC_NEST_CAPP_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_NEST_PBCQ_READ_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 48); + break; case PEC_NEST_PBCQ_WRITE_STK_OVR: case PEC_NEST_PBCQ_STORE_STK_OVR: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 24); + break; case PEC_NEST_PBCQ_RETRY_BKOFF_CTRL: + pec->nest_regs[reg] = val & PPC_BITMASK(0, 41); + break; + case PEC_NEST_PBCQ_HW_CONFIG: + case PEC_NEST_CAPP_CTRL: pec->nest_regs[reg] = val; break; default: @@ -81,7 +99,7 @@ static uint64_t pnv_pec_pci_xscom_read(void *opaque, hwaddr addr, PnvPhb4PecState *pec = PNV_PHB4_PEC(opaque); uint32_t reg = addr >> 3; - /* TODO: add list of allowed registers and error out if not */ + /* All registers are readable */ return pec->pci_regs[reg]; } @@ -93,8 +111,13 @@ static void pnv_pec_pci_xscom_write(void *opaque, hwaddr addr, switch (reg) { case PEC_PCI_PBAIB_HW_CONFIG: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 42); + break; + case PEC_PCI_PBAIB_HW_OVR: + pec->pci_regs[reg] = val & PPC_BITMASK(0, 15); + break; case PEC_PCI_PBAIB_READ_STK_OVR: - pec->pci_regs[reg] = val; + pec->pci_regs[reg] = val & PPC_BITMASK(0, 48); break; default: phb_pec_error(pec, "%s @0x%"HWADDR_PRIx"=%"PRIx64"\n", __func__, diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig index 5dfbf47ef5..56f0475a8e 100644 --- a/hw/ppc/Kconfig +++ b/hw/ppc/Kconfig @@ -69,6 +69,13 @@ config SAM460EX select USB_OHCI select FDT_PPC +config AMIGAONE + bool + imply ATI_VGA + select ARTICIA + select VT82C686 + select SMBUS_EEPROM + config PEGASOS2 bool imply ATI_VGA diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c new file mode 100644 index 0000000000..992a55e632 --- /dev/null +++ b/hw/ppc/amigaone.c @@ -0,0 +1,166 @@ +/* + * QEMU Eyetech AmigaOne/Mai Logic Teron emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/datadir.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/ppc/ppc.h" +#include "hw/boards.h" +#include "hw/loader.h" +#include "hw/pci-host/articia.h" +#include "hw/isa/vt82c686.h" +#include "hw/ide/pci.h" +#include "hw/i2c/smbus_eeprom.h" +#include "hw/ppc/ppc.h" +#include "sysemu/qtest.h" +#include "sysemu/reset.h" +#include "kvm_ppc.h" + +#define BUS_FREQ_HZ 100000000 + +/* + * Firmware binary available at + * https://www.hyperion-entertainment.com/index.php/downloads?view=files&parent=28 + * then "tail -c 524288 updater.image >u-boot-amigaone.bin" + * + * BIOS emulator in firmware cannot run QEMU vgabios and hangs on it, use + * -device VGA,romfile=VGABIOS-lgpl-latest.bin + * from http://www.nongnu.org/vgabios/ instead. + */ +#define PROM_FILENAME "u-boot-amigaone.bin" +#define PROM_ADDR 0xfff00000 +#define PROM_SIZE (512 * KiB) + +static void amigaone_cpu_reset(void *opaque) +{ + PowerPCCPU *cpu = opaque; + + cpu_reset(CPU(cpu)); + cpu_ppc_tb_reset(&cpu->env); +} + +static void fix_spd_data(uint8_t *spd) +{ + uint32_t bank_size = 4 * MiB * spd[31]; + uint32_t rows = bank_size / spd[13] / spd[17]; + spd[3] = ctz32(rows) - spd[4]; +} + +static void amigaone_init(MachineState *machine) +{ + PowerPCCPU *cpu; + CPUPPCState *env; + MemoryRegion *rom, *pci_mem, *mr; + const char *fwname = machine->firmware ?: PROM_FILENAME; + char *filename; + ssize_t sz; + PCIBus *pci_bus; + Object *via; + DeviceState *dev; + I2CBus *i2c_bus; + uint8_t *spd_data; + int i; + + /* init CPU */ + cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); + env = &cpu->env; + if (PPC_INPUT(env) != PPC_FLAGS_INPUT_6xx) { + error_report("Incompatible CPU, only 6xx bus supported"); + exit(1); + } + cpu_ppc_tb_init(env, BUS_FREQ_HZ / 4); + qemu_register_reset(amigaone_cpu_reset, cpu); + + /* RAM */ + if (machine->ram_size > 2 * GiB) { + error_report("RAM size more than 2 GiB is not supported"); + exit(1); + } + memory_region_add_subregion(get_system_memory(), 0, machine->ram); + if (machine->ram_size < 1 * GiB + 32 * KiB) { + /* Firmware uses this area for startup */ + mr = g_new(MemoryRegion, 1); + memory_region_init_ram(mr, NULL, "init-cache", 32 * KiB, &error_fatal); + memory_region_add_subregion(get_system_memory(), 0x40000000, mr); + } + + /* allocate and load firmware */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, fwname); + if (filename) { + rom = g_new(MemoryRegion, 1); + memory_region_init_rom(rom, NULL, "rom", PROM_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), PROM_ADDR, rom); + sz = load_image_targphys(filename, PROM_ADDR, PROM_SIZE); + if (sz <= 0 || sz > PROM_SIZE) { + error_report("Could not load firmware '%s'", filename); + exit(1); + } + g_free(filename); + } else if (!qtest_enabled()) { + error_report("Could not find firmware '%s'", fwname); + exit(1); + } + + /* Articia S */ + dev = sysbus_create_simple(TYPE_ARTICIA, 0xfe000000, NULL); + + i2c_bus = I2C_BUS(qdev_get_child_bus(dev, "smbus")); + if (machine->ram_size > 512 * MiB) { + spd_data = spd_data_generate(SDR, machine->ram_size / 2); + } else { + spd_data = spd_data_generate(SDR, machine->ram_size); + } + fix_spd_data(spd_data); + smbus_eeprom_init_one(i2c_bus, 0x51, spd_data); + if (machine->ram_size > 512 * MiB) { + smbus_eeprom_init_one(i2c_bus, 0x52, spd_data); + } + + pci_mem = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-low", pci_mem, + 0, 0x1000000); + memory_region_add_subregion(get_system_memory(), 0xfd000000, mr); + mr = g_new(MemoryRegion, 1); + memory_region_init_alias(mr, OBJECT(dev), "pci-mem-high", pci_mem, + 0x80000000, 0x7d000000); + memory_region_add_subregion(get_system_memory(), 0x80000000, mr); + pci_bus = PCI_BUS(qdev_get_child_bus(dev, "pci.0")); + + /* VIA VT82c686B South Bridge (multifunction PCI device) */ + via = OBJECT(pci_create_simple_multifunction(pci_bus, PCI_DEVFN(7, 0), + TYPE_VT82C686B_ISA)); + object_property_add_alias(OBJECT(machine), "rtc-time", + object_resolve_path_component(via, "rtc"), + "date"); + qdev_connect_gpio_out(DEVICE(via), 0, + qdev_get_gpio_in(DEVICE(cpu), PPC6xx_INPUT_INT)); + for (i = 0; i < PCI_NUM_PINS; i++) { + qdev_connect_gpio_out(dev, i, qdev_get_gpio_in_named(DEVICE(via), + "pirq", i)); + } + pci_ide_create_devs(PCI_DEVICE(object_resolve_path_component(via, "ide"))); + pci_vga_init(pci_bus); +} + +static void amigaone_machine_init(MachineClass *mc) +{ + mc->desc = "Eyetech AmigaOne/Mai Logic Teron"; + mc->init = amigaone_init; + mc->block_default_type = IF_IDE; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7457_v1.2"); + mc->default_display = "std"; + mc->default_ram_id = "ram"; + mc->default_ram_size = 512 * MiB; +} + +DEFINE_MACHINE("amigaone", amigaone_machine_init) diff --git a/hw/ppc/meson.build b/hw/ppc/meson.build index 7c2c52434a..ea44856d43 100644 --- a/hw/ppc/meson.build +++ b/hw/ppc/meson.build @@ -43,6 +43,7 @@ ppc_ss.add(when: 'CONFIG_POWERNV', if_true: files( 'pnv.c', 'pnv_xscom.c', 'pnv_core.c', + 'pnv_i2c.c', 'pnv_lpc.c', 'pnv_psi.c', 'pnv_occ.c', @@ -81,6 +82,8 @@ ppc_ss.add(when: 'CONFIG_E500', if_true: files( )) # PowerPC 440 Xilinx ML507 reference board. ppc_ss.add(when: 'CONFIG_VIRTEX', if_true: files('virtex_ml507.c')) +# AmigaOne +ppc_ss.add(when: 'CONFIG_AMIGAONE', if_true: files('amigaone.c')) # Pegasos2 ppc_ss.add(when: 'CONFIG_PEGASOS2', if_true: files('pegasos2.c')) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index c0e34fffbc..9c29727337 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1432,6 +1432,10 @@ static void pnv_chip_power9_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip9->pecs[i], TYPE_PNV_PHB4_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip9->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_quad_realize_one(PnvChip *chip, PnvQuad *eq, @@ -1504,6 +1508,7 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv9Psi *psi9 = &chip9->psi; Error *local_err = NULL; + int i; /* XSCOM bridge is first */ pnv_xscom_init(chip, PNV9_XSCOM_SIZE, PNV9_XSCOM_BASE(chip)); @@ -1602,6 +1607,28 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip9->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_I2CM_BASE + + chip9->i2c[i].engine * PNV9_XSCOM_I2CM_SIZE, + &chip9->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip9->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip9->psi), + PSIHB9_IRQ_SBE_I2C)); + } } static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1614,6 +1641,7 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV9_CHIP_MAX_I2C] = {2, 13, 2, 2}; k->chip_cfam_id = 0x220d104900008000ull; /* P9 Nimbus DD2.0 */ k->cores_mask = POWER9_CORE_MASK; @@ -1629,6 +1657,8 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power9_xscom_pcba; dc->desc = "PowerNV Chip POWER9"; k->num_pecs = PNV9_CHIP_MAX_PEC; + k->i2c_num_engines = PNV9_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power9_realize, &k->parent_realize); @@ -1656,6 +1686,10 @@ static void pnv_chip_power10_instance_init(Object *obj) object_initialize_child(obj, "pec[*]", &chip10->pecs[i], TYPE_PNV_PHB5_PEC); } + + for (i = 0; i < pcc->i2c_num_engines; i++) { + object_initialize_child(obj, "i2c[*]", &chip10->i2c[i], TYPE_PNV_I2C); + } } static void pnv_chip_power10_quad_realize(Pnv10Chip *chip10, Error **errp) @@ -1714,6 +1748,7 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) PnvChip *chip = PNV_CHIP(dev); Pnv10Chip *chip10 = PNV10_CHIP(dev); Error *local_err = NULL; + int i; /* XSCOM bridge is first */ pnv_xscom_init(chip, PNV10_XSCOM_SIZE, PNV10_XSCOM_BASE(chip)); @@ -1819,6 +1854,29 @@ static void pnv_chip_power10_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + + /* + * I2C + */ + for (i = 0; i < pcc->i2c_num_engines; i++) { + Object *obj = OBJECT(&chip10->i2c[i]); + + object_property_set_int(obj, "engine", i + 1, &error_fatal); + object_property_set_int(obj, "num-busses", + pcc->i2c_ports_per_engine[i], + &error_fatal); + object_property_set_link(obj, "chip", OBJECT(chip), &error_abort); + if (!qdev_realize(DEVICE(obj), NULL, errp)) { + return; + } + pnv_xscom_add_subregion(chip, PNV10_XSCOM_I2CM_BASE + + chip10->i2c[i].engine * PNV10_XSCOM_I2CM_SIZE, + &chip10->i2c[i].xscom_regs); + qdev_connect_gpio_out(DEVICE(&chip10->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&chip10->psi), + PSIHB9_IRQ_SBE_I2C)); + } } static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) @@ -1831,6 +1889,7 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); + static const int i2c_ports_per_engine[PNV10_CHIP_MAX_I2C] = {14, 14, 2, 16}; k->chip_cfam_id = 0x120da04900008000ull; /* P10 DD1.0 (with NX) */ k->cores_mask = POWER10_CORE_MASK; @@ -1846,6 +1905,8 @@ static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) k->xscom_pcba = pnv_chip_power10_xscom_pcba; dc->desc = "PowerNV Chip POWER10"; k->num_pecs = PNV10_CHIP_MAX_PEC; + k->i2c_num_engines = PNV10_CHIP_MAX_I2C; + k->i2c_ports_per_engine = i2c_ports_per_engine; device_class_set_parent_realize(dc, pnv_chip_power10_realize, &k->parent_realize); diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c new file mode 100644 index 0000000000..f75e59e709 --- /dev/null +++ b/hw/ppc/pnv_i2c.c @@ -0,0 +1,697 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/module.h" +#include "qemu/log.h" +#include "sysemu/reset.h" + +#include "hw/irq.h" +#include "hw/qdev-properties.h" + +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_chip.h" +#include "hw/ppc/pnv_i2c.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/fdt.h" + +#include <libfdt.h> + +/* I2C FIFO register */ +#define I2C_FIFO_REG 0x4 +#define I2C_FIFO PPC_BITMASK(0, 7) + +/* I2C command register */ +#define I2C_CMD_REG 0x5 +#define I2C_CMD_WITH_START PPC_BIT(0) +#define I2C_CMD_WITH_ADDR PPC_BIT(1) +#define I2C_CMD_READ_CONT PPC_BIT(2) +#define I2C_CMD_WITH_STOP PPC_BIT(3) +#define I2C_CMD_INTR_STEERING PPC_BITMASK(6, 7) /* P9 */ +#define I2C_CMD_INTR_STEER_HOST 1 +#define I2C_CMD_INTR_STEER_OCC 2 +#define I2C_CMD_DEV_ADDR PPC_BITMASK(8, 14) +#define I2C_CMD_READ_NOT_WRITE PPC_BIT(15) +#define I2C_CMD_LEN_BYTES PPC_BITMASK(16, 31) +#define I2C_MAX_TFR_LEN 0xfff0ull + +/* I2C mode register */ +#define I2C_MODE_REG 0x6 +#define I2C_MODE_BIT_RATE_DIV PPC_BITMASK(0, 15) +#define I2C_MODE_PORT_NUM PPC_BITMASK(16, 21) +#define I2C_MODE_ENHANCED PPC_BIT(28) +#define I2C_MODE_DIAGNOSTIC PPC_BIT(29) +#define I2C_MODE_PACING_ALLOW PPC_BIT(30) +#define I2C_MODE_WRAP PPC_BIT(31) + +/* I2C watermark register */ +#define I2C_WATERMARK_REG 0x7 +#define I2C_WATERMARK_HIGH PPC_BITMASK(16, 19) +#define I2C_WATERMARK_LOW PPC_BITMASK(24, 27) + +/* + * I2C interrupt mask and condition registers + * + * NB: The function of 0x9 and 0xa changes depending on whether you're reading + * or writing to them. When read they return the interrupt condition bits + * and on writes they update the interrupt mask register. + * + * The bit definitions are the same for all the interrupt registers. + */ +#define I2C_INTR_MASK_REG 0x8 + +#define I2C_INTR_RAW_COND_REG 0x9 /* read */ +#define I2C_INTR_MASK_OR_REG 0x9 /* write*/ + +#define I2C_INTR_COND_REG 0xa /* read */ +#define I2C_INTR_MASK_AND_REG 0xa /* write */ + +#define I2C_INTR_ALL PPC_BITMASK(16, 31) +#define I2C_INTR_INVALID_CMD PPC_BIT(16) +#define I2C_INTR_LBUS_PARITY_ERR PPC_BIT(17) +#define I2C_INTR_BKEND_OVERRUN_ERR PPC_BIT(18) +#define I2C_INTR_BKEND_ACCESS_ERR PPC_BIT(19) +#define I2C_INTR_ARBT_LOST_ERR PPC_BIT(20) +#define I2C_INTR_NACK_RCVD_ERR PPC_BIT(21) +#define I2C_INTR_DATA_REQ PPC_BIT(22) +#define I2C_INTR_CMD_COMP PPC_BIT(23) +#define I2C_INTR_STOP_ERR PPC_BIT(24) +#define I2C_INTR_I2C_BUSY PPC_BIT(25) +#define I2C_INTR_NOT_I2C_BUSY PPC_BIT(26) +#define I2C_INTR_SCL_EQ_1 PPC_BIT(28) +#define I2C_INTR_SCL_EQ_0 PPC_BIT(29) +#define I2C_INTR_SDA_EQ_1 PPC_BIT(30) +#define I2C_INTR_SDA_EQ_0 PPC_BIT(31) + +/* I2C status register */ +#define I2C_RESET_I2C_REG 0xb /* write */ +#define I2C_RESET_ERRORS 0xc +#define I2C_STAT_REG 0xb /* read */ +#define I2C_STAT_INVALID_CMD PPC_BIT(0) +#define I2C_STAT_LBUS_PARITY_ERR PPC_BIT(1) +#define I2C_STAT_BKEND_OVERRUN_ERR PPC_BIT(2) +#define I2C_STAT_BKEND_ACCESS_ERR PPC_BIT(3) +#define I2C_STAT_ARBT_LOST_ERR PPC_BIT(4) +#define I2C_STAT_NACK_RCVD_ERR PPC_BIT(5) +#define I2C_STAT_DATA_REQ PPC_BIT(6) +#define I2C_STAT_CMD_COMP PPC_BIT(7) +#define I2C_STAT_STOP_ERR PPC_BIT(8) +#define I2C_STAT_UPPER_THRS PPC_BITMASK(9, 15) +#define I2C_STAT_ANY_I2C_INTR PPC_BIT(16) +#define I2C_STAT_PORT_HISTORY_BUSY PPC_BIT(19) +#define I2C_STAT_SCL_INPUT_LEVEL PPC_BIT(20) +#define I2C_STAT_SDA_INPUT_LEVEL PPC_BIT(21) +#define I2C_STAT_PORT_BUSY PPC_BIT(22) +#define I2C_STAT_INTERFACE_BUSY PPC_BIT(23) +#define I2C_STAT_FIFO_ENTRY_COUNT PPC_BITMASK(24, 31) + +#define I2C_STAT_ANY_ERR (I2C_STAT_INVALID_CMD | I2C_STAT_LBUS_PARITY_ERR | \ + I2C_STAT_BKEND_OVERRUN_ERR | \ + I2C_STAT_BKEND_ACCESS_ERR | I2C_STAT_ARBT_LOST_ERR | \ + I2C_STAT_NACK_RCVD_ERR | I2C_STAT_STOP_ERR) + + +#define I2C_INTR_ACTIVE \ + ((I2C_STAT_ANY_ERR >> 16) | I2C_INTR_CMD_COMP | I2C_INTR_DATA_REQ) + +/* Pseudo-status used for timeouts */ +#define I2C_STAT_PSEUDO_TIMEOUT PPC_BIT(63) + +/* I2C extended status register */ +#define I2C_EXTD_STAT_REG 0xc +#define I2C_EXTD_STAT_FIFO_SIZE PPC_BITMASK(0, 7) +#define I2C_EXTD_STAT_MSM_CURSTATE PPC_BITMASK(11, 15) +#define I2C_EXTD_STAT_SCL_IN_SYNC PPC_BIT(16) +#define I2C_EXTD_STAT_SDA_IN_SYNC PPC_BIT(17) +#define I2C_EXTD_STAT_S_SCL PPC_BIT(18) +#define I2C_EXTD_STAT_S_SDA PPC_BIT(19) +#define I2C_EXTD_STAT_M_SCL PPC_BIT(20) +#define I2C_EXTD_STAT_M_SDA PPC_BIT(21) +#define I2C_EXTD_STAT_HIGH_WATER PPC_BIT(22) +#define I2C_EXTD_STAT_LOW_WATER PPC_BIT(23) +#define I2C_EXTD_STAT_I2C_BUSY PPC_BIT(24) +#define I2C_EXTD_STAT_SELF_BUSY PPC_BIT(25) +#define I2C_EXTD_STAT_I2C_VERSION PPC_BITMASK(27, 31) + +/* I2C residual front end/back end length */ +#define I2C_RESIDUAL_LEN_REG 0xd +#define I2C_RESIDUAL_FRONT_END PPC_BITMASK(0, 15) +#define I2C_RESIDUAL_BACK_END PPC_BITMASK(16, 31) + +/* Port busy register */ +#define I2C_PORT_BUSY_REG 0xe +#define I2C_SET_S_SCL_REG 0xd +#define I2C_RESET_S_SCL_REG 0xf +#define I2C_SET_S_SDA_REG 0x10 +#define I2C_RESET_S_SDA_REG 0x11 + +#define PNV_I2C_FIFO_SIZE 8 + +static I2CBus *pnv_i2c_get_bus(PnvI2C *i2c) +{ + uint8_t port = GETFIELD(I2C_MODE_PORT_NUM, i2c->regs[I2C_MODE_REG]); + + if (port >= i2c->num_busses) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid bus number %d/%d\n", port, + i2c->num_busses); + return NULL; + } + return i2c->busses[port]; +} + +static void pnv_i2c_update_irq(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + bool recv = !!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE); + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, + i2c->regs[I2C_RESIDUAL_LEN_REG]); + uint8_t fifo_count = GETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, + i2c->regs[I2C_STAT_REG]); + uint8_t fifo_free = PNV_I2C_FIFO_SIZE - fifo_count; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + + if (recv) { + if (fifo_count >= + GETFIELD(I2C_WATERMARK_HIGH, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_HIGH_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_HIGH_WATER; + } + + if (((i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_HIGH_WATER) && + fifo_count != 0) || front_end == 0) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } else { + if (fifo_count <= + GETFIELD(I2C_WATERMARK_LOW, i2c->regs[I2C_WATERMARK_REG])) { + i2c->regs[I2C_EXTD_STAT_REG] |= I2C_EXTD_STAT_LOW_WATER; + } else { + i2c->regs[I2C_EXTD_STAT_REG] &= ~I2C_EXTD_STAT_LOW_WATER; + } + + if (back_end > 0 && + (fifo_free >= back_end || + (i2c->regs[I2C_EXTD_STAT_REG] & I2C_EXTD_STAT_LOW_WATER))) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_DATA_REQ; + } + } + + if (back_end == 0 && front_end == 0) { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_DATA_REQ; + i2c->regs[I2C_STAT_REG] |= I2C_STAT_CMD_COMP; + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_WITH_STOP) { + i2c_end_transfer(bus); + i2c->regs[I2C_EXTD_STAT_REG] &= + ~(I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + } + } else { + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_CMD_COMP; + } + } + + /* + * Status and interrupt registers have nearly the same layout. + */ + i2c->regs[I2C_INTR_RAW_COND_REG] = i2c->regs[I2C_STAT_REG] >> 16; + i2c->regs[I2C_INTR_COND_REG] = + i2c->regs[I2C_INTR_RAW_COND_REG] & i2c->regs[I2C_INTR_MASK_REG]; + + qemu_set_irq(i2c->psi_irq, i2c->regs[I2C_INTR_COND_REG] != 0); +} + +static void pnv_i2c_fifo_update_count(PnvI2C *i2c) +{ + uint64_t stat = i2c->regs[I2C_STAT_REG]; + + i2c->regs[I2C_STAT_REG] = SETFIELD(I2C_STAT_FIFO_ENTRY_COUNT, stat, + fifo8_num_used(&i2c->fifo)); +} + +static void pnv_i2c_frontend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t front_end = GETFIELD(I2C_RESIDUAL_FRONT_END, residual_end); + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, residual_end, front_end - 1); +} + +static void pnv_i2c_fifo_flush(PnvI2C *i2c) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t data; + int ret; + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (!i2c_bus_busy(bus)) { + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + if (fifo8_is_full(&i2c->fifo)) { + return; + } + + data = i2c_recv(bus); + fifo8_push(&i2c->fifo, data); + } else { + if (fifo8_is_empty(&i2c->fifo)) { + return; + } + + data = fifo8_pop(&i2c->fifo); + ret = i2c_send(bus, data); + if (ret) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + i2c_end_transfer(bus); + } + } + + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_frontend_update(i2c); +} + +static void pnv_i2c_handle_cmd(PnvI2C *i2c, uint64_t val) +{ + I2CBus *bus = pnv_i2c_get_bus(i2c); + uint8_t addr = GETFIELD(I2C_CMD_DEV_ADDR, val); + int recv = !!(val & I2C_CMD_READ_NOT_WRITE); + uint32_t len_bytes = GETFIELD(I2C_CMD_LEN_BYTES, val); + + if (!(val & I2C_CMD_WITH_START) && !(val & I2C_CMD_WITH_ADDR) && + !(val & I2C_CMD_WITH_STOP) && !len_bytes) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid command 0x%"PRIx64"\n", + val); + return; + } + + if (!(i2c->regs[I2C_STAT_REG] & I2C_STAT_CMD_COMP)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + return; + } + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_FRONT_END, 0ull, len_bytes) | + SETFIELD(I2C_RESIDUAL_BACK_END, 0ull, len_bytes); + + if (val & I2C_CMD_WITH_START) { + if (i2c_start_transfer(bus, addr, recv)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_NACK_RCVD_ERR; + } else { + i2c->regs[I2C_EXTD_STAT_REG] |= + (I2C_EXTD_STAT_I2C_BUSY | I2C_EXTD_STAT_SELF_BUSY); + pnv_i2c_fifo_flush(i2c); + } + } +} + +static void pnv_i2c_backend_update(PnvI2C *i2c) +{ + uint64_t residual_end = i2c->regs[I2C_RESIDUAL_LEN_REG]; + uint16_t back_end = GETFIELD(I2C_RESIDUAL_BACK_END, residual_end); + + if (!back_end) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_ACCESS_ERR; + return; + } + + i2c->regs[I2C_RESIDUAL_LEN_REG] = + SETFIELD(I2C_RESIDUAL_BACK_END, residual_end, back_end - 1); +} + +static void pnv_i2c_fifo_in(PnvI2C *i2c) +{ + uint8_t data = GETFIELD(I2C_FIFO, i2c->regs[I2C_FIFO_REG]); + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read command in progress\n"); + return; + } + + if (fifo8_is_full(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + fifo8_push(&i2c->fifo, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); + pnv_i2c_fifo_flush(i2c); +} + +static void pnv_i2c_fifo_out(PnvI2C *i2c) +{ + uint8_t data; + I2CBus *bus = pnv_i2c_get_bus(i2c); + + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + + if (!i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: no command in progress\n"); + return; + } + + if (!(i2c->regs[I2C_CMD_REG] & I2C_CMD_READ_NOT_WRITE)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write command in progress\n"); + return; + } + + if (fifo8_is_empty(&i2c->fifo)) { + if (!(i2c->regs[I2C_MODE_REG] & I2C_MODE_PACING_ALLOW)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_BKEND_OVERRUN_ERR; + } + return; + } + + data = fifo8_pop(&i2c->fifo); + + i2c->regs[I2C_FIFO_REG] = SETFIELD(I2C_FIFO, 0ull, data); + pnv_i2c_fifo_update_count(i2c); + pnv_i2c_backend_update(i2c); +} + +static uint64_t pnv_i2c_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + uint64_t val = -1; + int i; + + switch (offset) { + case I2C_STAT_REG: + val = i2c->regs[offset]; + break; + + case I2C_FIFO_REG: + pnv_i2c_fifo_out(i2c); + val = i2c->regs[offset]; + break; + + case I2C_PORT_BUSY_REG: /* compute busy bit for each port */ + val = 0; + for (i = 0; i < i2c->num_busses; i++) { + val |= i2c_bus_busy(i2c->busses[i]) << i; + } + break; + + case I2C_CMD_REG: + case I2C_MODE_REG: + case I2C_WATERMARK_REG: + case I2C_INTR_MASK_REG: + case I2C_INTR_RAW_COND_REG: + case I2C_INTR_COND_REG: + case I2C_EXTD_STAT_REG: + case I2C_RESIDUAL_LEN_REG: + val = i2c->regs[offset]; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: read at register: 0x%" + HWADDR_PRIx "\n", addr >> 3); + } + + pnv_i2c_update_irq(i2c); + + return val; +} + +static void pnv_i2c_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvI2C *i2c = PNV_I2C(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case I2C_MODE_REG: + { + i2c->regs[offset] = val; + I2CBus *bus = pnv_i2c_get_bus(i2c); + if (!bus) { + qemu_log_mask(LOG_GUEST_ERROR, "I2C: invalid port\n"); + return; + } + if (i2c_bus_busy(bus)) { + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: command in progress\n"); + } + } + break; + + case I2C_CMD_REG: + i2c->regs[offset] = val; + pnv_i2c_handle_cmd(i2c, val); + break; + + case I2C_FIFO_REG: + i2c->regs[offset] = val; + pnv_i2c_fifo_in(i2c); + break; + + case I2C_WATERMARK_REG: + i2c->regs[offset] = val; + break; + + case I2C_RESET_I2C_REG: + i2c->regs[I2C_MODE_REG] = 0; + i2c->regs[I2C_CMD_REG] = 0; + i2c->regs[I2C_WATERMARK_REG] = 0; + i2c->regs[I2C_INTR_MASK_REG] = 0; + i2c->regs[I2C_INTR_COND_REG] = 0; + i2c->regs[I2C_INTR_RAW_COND_REG] = 0; + i2c->regs[I2C_STAT_REG] = 0; + i2c->regs[I2C_RESIDUAL_LEN_REG] = 0; + i2c->regs[I2C_EXTD_STAT_REG] &= + (I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION); + break; + + case I2C_RESET_ERRORS: + i2c->regs[I2C_STAT_REG] &= ~I2C_STAT_ANY_ERR; + i2c->regs[I2C_RESIDUAL_LEN_REG] = 0; + i2c->regs[I2C_EXTD_STAT_REG] &= + (I2C_EXTD_STAT_FIFO_SIZE | I2C_EXTD_STAT_I2C_VERSION); + fifo8_reset(&i2c->fifo); + break; + + case I2C_INTR_MASK_REG: + i2c->regs[offset] = val; + break; + + case I2C_INTR_MASK_OR_REG: + i2c->regs[I2C_INTR_MASK_REG] |= val; + break; + + case I2C_INTR_MASK_AND_REG: + i2c->regs[I2C_INTR_MASK_REG] &= val; + break; + + case I2C_PORT_BUSY_REG: + case I2C_SET_S_SCL_REG: + case I2C_RESET_S_SCL_REG: + case I2C_SET_S_SDA_REG: + case I2C_RESET_S_SDA_REG: + i2c->regs[offset] = val; + break; + default: + i2c->regs[I2C_STAT_REG] |= I2C_STAT_INVALID_CMD; + qemu_log_mask(LOG_GUEST_ERROR, "I2C: write at register: 0x%" + HWADDR_PRIx " val=0x%"PRIx64"\n", addr >> 3, val); + } + + pnv_i2c_update_irq(i2c); +} + +static const MemoryRegionOps pnv_i2c_xscom_ops = { + .read = pnv_i2c_xscom_read, + .write = pnv_i2c_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 int pnv_i2c_bus_dt_xscom(PnvI2C *i2c, void *fdt, + int offset, int index) +{ + int i2c_bus_offset; + const char i2c_compat[] = + "ibm,opal-i2c\0ibm,power8-i2c-port\0ibm,power9-i2c-port"; + g_autofree char *i2c_port_name = NULL; + g_autofree char *name = g_strdup_printf("i2c-bus@%x", index); + + i2c_bus_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_bus_offset); + + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "reg", index))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_bus_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_bus_offset, "bus-frequency", 400000))); + + i2c_port_name = g_strdup_printf("p8_%08x_e%dp%d", i2c->chip->chip_id, + i2c->engine, index); + _FDT(fdt_setprop_string(fdt, i2c_bus_offset, "ibm,port-name", + i2c_port_name)); + return 0; +} + +#define XSCOM_BUS_FREQUENCY 466500000 +#define I2C_CLOCK_FREQUENCY (XSCOM_BUS_FREQUENCY / 4) + +static int pnv_i2c_dt_xscom(PnvXScomInterface *dev, void *fdt, + int offset) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i2c_offset; + const char i2c_compat[] = "ibm,power8-i2cm\0ibm,power9-i2cm"; + uint32_t i2c_pcba = PNV9_XSCOM_I2CM_BASE + + i2c->engine * PNV9_XSCOM_I2CM_SIZE; + uint32_t reg[2] = { + cpu_to_be32(i2c_pcba), + cpu_to_be32(PNV9_XSCOM_I2CM_SIZE) + }; + int i; + g_autofree char *name = g_strdup_printf("i2cm@%x", i2c_pcba); + + i2c_offset = fdt_add_subnode(fdt, offset, name); + _FDT(i2c_offset); + + _FDT(fdt_setprop(fdt, i2c_offset, "reg", reg, sizeof(reg))); + + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "#size-cells", 0))); + _FDT(fdt_setprop(fdt, i2c_offset, "compatible", i2c_compat, + sizeof(i2c_compat))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "chip-engine#", i2c->engine))); + _FDT((fdt_setprop_cell(fdt, i2c_offset, "clock-frequency", + I2C_CLOCK_FREQUENCY))); + + for (i = 0; i < i2c->num_busses; i++) { + pnv_i2c_bus_dt_xscom(i2c, fdt, i2c_offset, i); + } + return 0; +} + +static void pnv_i2c_reset(void *dev) +{ + PnvI2C *i2c = PNV_I2C(dev); + + memset(i2c->regs, 0, sizeof(i2c->regs)); + + i2c->regs[I2C_STAT_REG] = I2C_STAT_CMD_COMP; + i2c->regs[I2C_EXTD_STAT_REG] = + SETFIELD(I2C_EXTD_STAT_FIFO_SIZE, 0ull, PNV_I2C_FIFO_SIZE) | + SETFIELD(I2C_EXTD_STAT_I2C_VERSION, 0ull, 23); /* last version */ + + fifo8_reset(&i2c->fifo); +} + +static void pnv_i2c_realize(DeviceState *dev, Error **errp) +{ + PnvI2C *i2c = PNV_I2C(dev); + int i; + + assert(i2c->chip); + + pnv_xscom_region_init(&i2c->xscom_regs, OBJECT(i2c), &pnv_i2c_xscom_ops, + i2c, "xscom-i2c", PNV9_XSCOM_I2CM_SIZE); + + i2c->busses = g_new(I2CBus *, i2c->num_busses); + for (i = 0; i < i2c->num_busses; i++) { + char name[32]; + + snprintf(name, sizeof(name), TYPE_PNV_I2C ".%d", i); + i2c->busses[i] = i2c_init_bus(dev, name); + } + + fifo8_create(&i2c->fifo, PNV_I2C_FIFO_SIZE); + + qemu_register_reset(pnv_i2c_reset, dev); + + qdev_init_gpio_out(DEVICE(dev), &i2c->psi_irq, 1); +} + +static Property pnv_i2c_properties[] = { + DEFINE_PROP_LINK("chip", PnvI2C, chip, TYPE_PNV_CHIP, PnvChip *), + DEFINE_PROP_UINT32("engine", PnvI2C, engine, 1), + DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); + + xscomc->dt_xscom = pnv_i2c_dt_xscom; + + dc->desc = "PowerNV I2C"; + dc->realize = pnv_i2c_realize; + device_class_set_props(dc, pnv_i2c_properties); +} + +static const TypeInfo pnv_i2c_info = { + .name = TYPE_PNV_I2C, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvI2C), + .class_init = pnv_i2c_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_i2c_register_types(void) +{ + type_register_static(&pnv_i2c_info); +} + +type_init(pnv_i2c_register_types); diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 26c384b261..f329693c55 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -38,7 +38,6 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" -#include "hw/ppc/spapr_rtas.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" @@ -531,8 +530,8 @@ target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *spapr, return H_PARAMETER; } -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets) +static uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, + uint32_t nret, uint64_t rets) { int token; diff --git a/include/hw/pci-host/articia.h b/include/hw/pci-host/articia.h new file mode 100644 index 0000000000..529c240274 --- /dev/null +++ b/include/hw/pci-host/articia.h @@ -0,0 +1,17 @@ +/* + * Mai Logic Articia S emulation + * + * Copyright (c) 2023 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + * + */ + +#ifndef ARTICIA_H +#define ARTICIA_H + +#define TYPE_ARTICIA "articia" +#define TYPE_ARTICIA_PCI_HOST "articia-pci-host" +#define TYPE_ARTICIA_PCI_BRIDGE "articia-pci-bridge" + +#endif diff --git a/include/hw/pci-host/pnv_phb4.h b/include/hw/pci-host/pnv_phb4.h index 2d026db9a3..3212e68160 100644 --- a/include/hw/pci-host/pnv_phb4.h +++ b/include/hw/pci-host/pnv_phb4.h @@ -117,7 +117,7 @@ struct PnvPHB4 { MemoryRegion pci_regs_mr; /* Nest registers */ -#define PHB4_PEC_NEST_STK_REGS_COUNT 0x17 +#define PHB4_PEC_NEST_STK_REGS_COUNT 0x18 uint64_t nest_regs[PHB4_PEC_NEST_STK_REGS_COUNT]; MemoryRegion nest_regs_mr; diff --git a/include/hw/pci-host/pnv_phb4_regs.h b/include/hw/pci-host/pnv_phb4_regs.h index 4a0d3b28ef..bea96f4d91 100644 --- a/include/hw/pci-host/pnv_phb4_regs.h +++ b/include/hw/pci-host/pnv_phb4_regs.h @@ -77,10 +77,12 @@ #define PEC_NEST_STK_BAR_EN_PHB PPC_BIT(2) #define PEC_NEST_STK_BAR_EN_INT PPC_BIT(3) #define PEC_NEST_STK_DATA_FRZ_TYPE 0x15 -#define PEC_NEST_STK_PBCQ_TUN_BAR 0x16 +#define PEC_NEST_STK_PBCQ_SPARSE_PAGE 0x16 /* P10 */ +#define PEC_NEST_STK_PBCQ_CACHE_INJ 0x17 /* P10 */ /* XSCOM PCI global registers */ #define PEC_PCI_PBAIB_HW_CONFIG 0x00 +#define PEC_PCI_PBAIB_HW_OVR 0x01 #define PEC_PCI_PBAIB_READ_STK_OVR 0x02 /* XSCOM PCI per-stack registers */ diff --git a/include/hw/ppc/pnv_chip.h b/include/hw/ppc/pnv_chip.h index 53e1d921d7..0ab5c42308 100644 --- a/include/hw/ppc/pnv_chip.h +++ b/include/hw/ppc/pnv_chip.h @@ -9,6 +9,7 @@ #include "hw/ppc/pnv_psi.h" #include "hw/ppc/pnv_sbe.h" #include "hw/ppc/pnv_xive.h" +#include "hw/ppc/pnv_i2c.h" #include "hw/sysbus.h" OBJECT_DECLARE_TYPE(PnvChip, PnvChipClass, @@ -86,6 +87,9 @@ struct Pnv9Chip { #define PNV9_CHIP_MAX_PEC 3 PnvPhb4PecState pecs[PNV9_CHIP_MAX_PEC]; + +#define PNV9_CHIP_MAX_I2C 4 + PnvI2C i2c[PNV9_CHIP_MAX_I2C]; }; /* @@ -115,6 +119,9 @@ struct Pnv10Chip { #define PNV10_CHIP_MAX_PEC 2 PnvPhb4PecState pecs[PNV10_CHIP_MAX_PEC]; + +#define PNV10_CHIP_MAX_I2C 4 + PnvI2C i2c[PNV10_CHIP_MAX_I2C]; }; #define PNV10_PIR2FUSEDCORE(pir) (((pir) >> 3) & 0xf) @@ -130,6 +137,9 @@ struct PnvChipClass { uint32_t num_pecs; uint32_t num_phbs; + uint32_t i2c_num_engines; + const int *i2c_ports_per_engine; + DeviceRealize parent_realize; uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); diff --git a/include/hw/ppc/pnv_i2c.h b/include/hw/ppc/pnv_i2c.h new file mode 100644 index 0000000000..1a37730f1e --- /dev/null +++ b/include/hw/ppc/pnv_i2c.h @@ -0,0 +1,38 @@ +/* + * QEMU PowerPC PowerNV Processor I2C model + * + * Copyright (c) 2019-2023, IBM Corporation. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PPC_PNV_I2C_H +#define PPC_PNV_I2C_H + +#include "hw/ppc/pnv.h" +#include "hw/i2c/i2c.h" +#include "qemu/fifo8.h" + +#define TYPE_PNV_I2C "pnv-i2c" +#define PNV_I2C(obj) OBJECT_CHECK(PnvI2C, (obj), TYPE_PNV_I2C) + +#define PNV_I2C_REGS 0x20 + +typedef struct PnvI2C { + DeviceState parent; + + struct PnvChip *chip; + + qemu_irq psi_irq; + + uint64_t regs[PNV_I2C_REGS]; + uint32_t engine; + uint32_t num_busses; + I2CBus **busses; + + MemoryRegion xscom_regs; + + Fifo8 fifo; +} PnvI2C; + +#endif /* PPC_PNV_I2C_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 35b19610f7..f5becbab41 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -90,6 +90,9 @@ struct PnvXScomInterfaceClass { ((uint64_t)(((core) & 0x1C) + 0x40) << 22) #define PNV9_XSCOM_EQ_SIZE 0x100000 +#define PNV9_XSCOM_I2CM_BASE 0xa0000 +#define PNV9_XSCOM_I2CM_SIZE 0x1000 + #define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE #define PNV9_XSCOM_OCC_SIZE 0x8000 @@ -149,6 +152,9 @@ struct PnvXScomInterfaceClass { #define PNV10_XSCOM_PSIHB_BASE 0x3011D00 #define PNV10_XSCOM_PSIHB_SIZE 0x100 +#define PNV10_XSCOM_I2CM_BASE PNV9_XSCOM_I2CM_BASE +#define PNV10_XSCOM_I2CM_SIZE PNV9_XSCOM_I2CM_SIZE + #define PNV10_XSCOM_OCC_BASE PNV9_XSCOM_OCC_BASE #define PNV10_XSCOM_OCC_SIZE PNV9_XSCOM_OCC_SIZE diff --git a/include/hw/ppc/spapr_rtas.h b/include/hw/ppc/spapr_rtas.h deleted file mode 100644 index 383611f10f..0000000000 --- a/include/hw/ppc/spapr_rtas.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef HW_SPAPR_RTAS_H -#define HW_SPAPR_RTAS_H -/* - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - */ - -uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, - uint32_t nret, uint64_t rets); -#endif /* HW_SPAPR_RTAS_H */ diff --git a/tests/avocado/ppc_amiga.py b/tests/avocado/ppc_amiga.py new file mode 100644 index 0000000000..b6f866f91d --- /dev/null +++ b/tests/avocado/ppc_amiga.py @@ -0,0 +1,38 @@ +# Test AmigaNG boards +# +# Copyright (c) 2023 BALATON Zoltan +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from avocado.utils import archive +from avocado.utils import process +from avocado_qemu import QemuSystemTest +from avocado_qemu import wait_for_console_pattern + +class AmigaOneMachine(QemuSystemTest): + + timeout = 90 + + def test_ppc_amigaone(self): + """ + :avocado: tags=arch:ppc + :avocado: tags=machine:amigaone + :avocado: tags=device:articia + :avocado: tags=accel:tcg + """ + self.require_accelerator("tcg") + tar_name = 'A1Firmware_Floppy_05-Mar-2005.zip' + tar_url = ('https://www.hyperion-entertainment.com/index.php/' + 'downloads?view=download&format=raw&file=25') + tar_hash = 'c52e59bc73e31d8bcc3cc2106778f7ac84f6c755' + zip_file = self.fetch_asset(tar_name, locations=tar_url, + asset_hash=tar_hash) + archive.extract(zip_file, self.workdir) + cmd = f"tail -c 524288 {self.workdir}/floppy_edition/updater.image >{self.workdir}/u-boot-amigaone.bin" + process.run(cmd, shell=True) + + self.vm.set_console() + self.vm.add_args('-bios', self.workdir + '/u-boot-amigaone.bin') + self.vm.launch() + wait_for_console_pattern(self, 'FLASH:') |