| /* |
| * QEMU PowerPC PowerNV Emulation of a few HOMER related registers |
| * |
| * Copyright (c) 2019, IBM Corporation. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License, version 2, as |
| * published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "qapi/error.h" |
| #include "exec/hwaddr.h" |
| #include "exec/memory.h" |
| #include "sysemu/cpus.h" |
| #include "hw/qdev-core.h" |
| #include "hw/ppc/pnv.h" |
| #include "hw/ppc/pnv_homer.h" |
| |
| |
| static bool core_max_array(PnvHomer *homer, hwaddr addr) |
| { |
| int i; |
| PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); |
| |
| for (i = 0; i <= homer->chip->nr_cores; i++) { |
| if (addr == (hmrc->core_max_base + i)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /* P8 Pstate table */ |
| |
| #define PNV8_OCC_PSTATE_VERSION 0x1f8001 |
| #define PNV8_OCC_PSTATE_MIN 0x1f8003 |
| #define PNV8_OCC_PSTATE_VALID 0x1f8000 |
| #define PNV8_OCC_PSTATE_THROTTLE 0x1f8002 |
| #define PNV8_OCC_PSTATE_NOM 0x1f8004 |
| #define PNV8_OCC_PSTATE_TURBO 0x1f8005 |
| #define PNV8_OCC_PSTATE_ULTRA_TURBO 0x1f8006 |
| #define PNV8_OCC_PSTATE_DATA 0x1f8008 |
| #define PNV8_OCC_PSTATE_ID_ZERO 0x1f8010 |
| #define PNV8_OCC_PSTATE_ID_ONE 0x1f8018 |
| #define PNV8_OCC_PSTATE_ID_TWO 0x1f8020 |
| #define PNV8_OCC_VDD_VOLTAGE_IDENTIFIER 0x1f8012 |
| #define PNV8_OCC_VCS_VOLTAGE_IDENTIFIER 0x1f8013 |
| #define PNV8_OCC_PSTATE_ZERO_FREQUENCY 0x1f8014 |
| #define PNV8_OCC_PSTATE_ONE_FREQUENCY 0x1f801c |
| #define PNV8_OCC_PSTATE_TWO_FREQUENCY 0x1f8024 |
| #define PNV8_CORE_MAX_BASE 0x1f8810 |
| |
| |
| static uint64_t pnv_power8_homer_read(void *opaque, hwaddr addr, |
| unsigned size) |
| { |
| PnvHomer *homer = PNV_HOMER(opaque); |
| |
| switch (addr) { |
| case PNV8_OCC_PSTATE_VERSION: |
| case PNV8_OCC_PSTATE_MIN: |
| case PNV8_OCC_PSTATE_ID_ZERO: |
| return 0; |
| case PNV8_OCC_PSTATE_VALID: |
| case PNV8_OCC_PSTATE_THROTTLE: |
| case PNV8_OCC_PSTATE_NOM: |
| case PNV8_OCC_PSTATE_TURBO: |
| case PNV8_OCC_PSTATE_ID_ONE: |
| case PNV8_OCC_VDD_VOLTAGE_IDENTIFIER: |
| case PNV8_OCC_VCS_VOLTAGE_IDENTIFIER: |
| return 1; |
| case PNV8_OCC_PSTATE_ULTRA_TURBO: |
| case PNV8_OCC_PSTATE_ID_TWO: |
| return 2; |
| case PNV8_OCC_PSTATE_DATA: |
| return 0x1000000000000000; |
| /* P8 frequency for 0, 1, and 2 pstates */ |
| case PNV8_OCC_PSTATE_ZERO_FREQUENCY: |
| case PNV8_OCC_PSTATE_ONE_FREQUENCY: |
| case PNV8_OCC_PSTATE_TWO_FREQUENCY: |
| return 3000; |
| } |
| /* pstate table core max array */ |
| if (core_max_array(homer, addr)) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void pnv_power8_homer_write(void *opaque, hwaddr addr, |
| uint64_t val, unsigned size) |
| { |
| /* callback function defined to homer write */ |
| return; |
| } |
| |
| static const MemoryRegionOps pnv_power8_homer_ops = { |
| .read = pnv_power8_homer_read, |
| .write = pnv_power8_homer_write, |
| .valid.min_access_size = 1, |
| .valid.max_access_size = 8, |
| .impl.min_access_size = 1, |
| .impl.max_access_size = 8, |
| .endianness = DEVICE_BIG_ENDIAN, |
| }; |
| |
| static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) |
| { |
| PnvHomerClass *homer = PNV_HOMER_CLASS(klass); |
| |
| homer->homer_size = PNV_HOMER_SIZE; |
| homer->homer_ops = &pnv_power8_homer_ops; |
| homer->core_max_base = PNV8_CORE_MAX_BASE; |
| } |
| |
| static const TypeInfo pnv_homer_power8_type_info = { |
| .name = TYPE_PNV8_HOMER, |
| .parent = TYPE_PNV_HOMER, |
| .instance_size = sizeof(PnvHomer), |
| .class_init = pnv_homer_power8_class_init, |
| }; |
| |
| /* P9 Pstate table */ |
| |
| #define PNV9_OCC_PSTATE_ID_ZERO 0xe2018 |
| #define PNV9_OCC_PSTATE_ID_ONE 0xe2020 |
| #define PNV9_OCC_PSTATE_ID_TWO 0xe2028 |
| #define PNV9_OCC_PSTATE_DATA 0xe2000 |
| #define PNV9_OCC_PSTATE_DATA_AREA 0xe2008 |
| #define PNV9_OCC_PSTATE_MIN 0xe2003 |
| #define PNV9_OCC_PSTATE_NOM 0xe2004 |
| #define PNV9_OCC_PSTATE_TURBO 0xe2005 |
| #define PNV9_OCC_PSTATE_ULTRA_TURBO 0xe2818 |
| #define PNV9_OCC_MAX_PSTATE_ULTRA_TURBO 0xe2006 |
| #define PNV9_OCC_PSTATE_MAJOR_VERSION 0xe2001 |
| #define PNV9_OCC_OPAL_RUNTIME_DATA 0xe2b85 |
| #define PNV9_CHIP_HOMER_IMAGE_POINTER 0x200008 |
| #define PNV9_CHIP_HOMER_BASE 0x0 |
| #define PNV9_OCC_PSTATE_ZERO_FREQUENCY 0xe201c |
| #define PNV9_OCC_PSTATE_ONE_FREQUENCY 0xe2024 |
| #define PNV9_OCC_PSTATE_TWO_FREQUENCY 0xe202c |
| #define PNV9_OCC_ROLE_MASTER_OR_SLAVE 0xe2002 |
| #define PNV9_CORE_MAX_BASE 0xe2819 |
| |
| |
| static uint64_t pnv_power9_homer_read(void *opaque, hwaddr addr, |
| unsigned size) |
| { |
| PnvHomer *homer = PNV_HOMER(opaque); |
| |
| switch (addr) { |
| case PNV9_OCC_MAX_PSTATE_ULTRA_TURBO: |
| case PNV9_OCC_PSTATE_ID_ZERO: |
| return 0; |
| case PNV9_OCC_PSTATE_DATA: |
| case PNV9_OCC_ROLE_MASTER_OR_SLAVE: |
| case PNV9_OCC_PSTATE_NOM: |
| case PNV9_OCC_PSTATE_TURBO: |
| case PNV9_OCC_PSTATE_ID_ONE: |
| case PNV9_OCC_PSTATE_ULTRA_TURBO: |
| case PNV9_OCC_OPAL_RUNTIME_DATA: |
| return 1; |
| case PNV9_OCC_PSTATE_MIN: |
| case PNV9_OCC_PSTATE_ID_TWO: |
| return 2; |
| |
| /* 3000 khz frequency for 0, 1, and 2 pstates */ |
| case PNV9_OCC_PSTATE_ZERO_FREQUENCY: |
| case PNV9_OCC_PSTATE_ONE_FREQUENCY: |
| case PNV9_OCC_PSTATE_TWO_FREQUENCY: |
| return 3000; |
| case PNV9_OCC_PSTATE_MAJOR_VERSION: |
| return 0x90; |
| case PNV9_CHIP_HOMER_BASE: |
| case PNV9_OCC_PSTATE_DATA_AREA: |
| case PNV9_CHIP_HOMER_IMAGE_POINTER: |
| return 0x1000000000000000; |
| } |
| /* pstate table core max array */ |
| if (core_max_array(homer, addr)) { |
| return 1; |
| } |
| return 0; |
| } |
| |
| static void pnv_power9_homer_write(void *opaque, hwaddr addr, |
| uint64_t val, unsigned size) |
| { |
| /* callback function defined to homer write */ |
| return; |
| } |
| |
| static const MemoryRegionOps pnv_power9_homer_ops = { |
| .read = pnv_power9_homer_read, |
| .write = pnv_power9_homer_write, |
| .valid.min_access_size = 1, |
| .valid.max_access_size = 8, |
| .impl.min_access_size = 1, |
| .impl.max_access_size = 8, |
| .endianness = DEVICE_BIG_ENDIAN, |
| }; |
| |
| static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) |
| { |
| PnvHomerClass *homer = PNV_HOMER_CLASS(klass); |
| |
| homer->homer_size = PNV9_HOMER_SIZE; |
| homer->homer_ops = &pnv_power9_homer_ops; |
| homer->core_max_base = PNV9_CORE_MAX_BASE; |
| } |
| |
| static const TypeInfo pnv_homer_power9_type_info = { |
| .name = TYPE_PNV9_HOMER, |
| .parent = TYPE_PNV_HOMER, |
| .instance_size = sizeof(PnvHomer), |
| .class_init = pnv_homer_power9_class_init, |
| }; |
| |
| static void pnv_homer_realize(DeviceState *dev, Error **errp) |
| { |
| PnvHomer *homer = PNV_HOMER(dev); |
| PnvHomerClass *hmrc = PNV_HOMER_GET_CLASS(homer); |
| Object *obj; |
| Error *local_err = NULL; |
| |
| obj = object_property_get_link(OBJECT(dev), "chip", &local_err); |
| if (!obj) { |
| error_propagate(errp, local_err); |
| error_prepend(errp, "required link 'chip' not found: "); |
| return; |
| } |
| homer->chip = PNV_CHIP(obj); |
| /* homer region */ |
| memory_region_init_io(&homer->regs, OBJECT(dev), |
| hmrc->homer_ops, homer, "homer-main-memory", |
| hmrc->homer_size); |
| } |
| |
| static void pnv_homer_class_init(ObjectClass *klass, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(klass); |
| |
| dc->realize = pnv_homer_realize; |
| dc->desc = "PowerNV HOMER Memory"; |
| } |
| |
| static const TypeInfo pnv_homer_type_info = { |
| .name = TYPE_PNV_HOMER, |
| .parent = TYPE_DEVICE, |
| .instance_size = sizeof(PnvHomer), |
| .class_init = pnv_homer_class_init, |
| .class_size = sizeof(PnvHomerClass), |
| .abstract = true, |
| }; |
| |
| static void pnv_homer_register_types(void) |
| { |
| type_register_static(&pnv_homer_type_info); |
| type_register_static(&pnv_homer_power8_type_info); |
| type_register_static(&pnv_homer_power9_type_info); |
| } |
| |
| type_init(pnv_homer_register_types); |