Stub the PLL, oscillator and clock control registers that
firmware reads during boot.  PLL always reports locked so
startup code does not time out.

Originally-by: David Brenken <[email protected]>
Originally-by: Christoph Seitz <[email protected]>
Signed-off-by: Parthiban Nallathambi <[email protected]>
---
 hw/tricore/meson.build           |   2 +
 hw/tricore/tricore_scu.c         | 240 +++++++++++++++++++++++++++++++++++++++
 include/hw/tricore/tricore_scu.h | 103 +++++++++++++++++
 3 files changed, 345 insertions(+)

diff --git a/hw/tricore/meson.build b/hw/tricore/meson.build
index 7e3585daf8..96e367f996 100644
--- a/hw/tricore/meson.build
+++ b/hw/tricore/meson.build
@@ -3,5 +3,7 @@ tricore_ss.add(when: 'CONFIG_TRICORE_TESTBOARD', if_true: 
files('tricore_testboa
 tricore_ss.add(when: 'CONFIG_TRICORE_TESTBOARD', if_true: 
files('tricore_testdevice.c'))
 tricore_ss.add(when: 'CONFIG_TRIBOARD', if_true: files('triboard.c'))
 tricore_ss.add(when: 'CONFIG_TC27X_SOC', if_true: files('tc27x_soc.c'))
+tricore_ss.add(when: 'CONFIG_TC39X_SOC', if_true: files('tc39xb_soc.c'))
+tricore_ss.add(when: 'CONFIG_TRICORE_SCU', if_true: files('tricore_scu.c'))
 
 hw_arch += {'tricore': tricore_ss}
diff --git a/hw/tricore/tricore_scu.c b/hw/tricore/tricore_scu.c
new file mode 100644
index 0000000000..4baabc7d82
--- /dev/null
+++ b/hw/tricore/tricore_scu.c
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU TriCore System Control Unit (SCU)
+ *
+ * Copyright (c) 2017 David Brenken <[email protected]>
+ * Copyright (c) 2024 Infineon Technologies AG
+ */
+
+#include "qemu/osdep.h"
+#include "hw/tricore/tricore_scu.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+
+/* Register offsets */
+enum {
+    A_OSCCON        = 0x10,
+    A_PLLSTAT       = 0x14,
+    A_PLLCON0       = 0x18,
+    A_PLLCON1       = 0x1C,
+    A_PLLCON2       = 0x20,
+    A_PLLERAYSTAT   = 0x24,
+    A_CCUCON0       = 0x30,
+    A_CCUCON1       = 0x34,
+    A_CCUCON2       = 0x40,
+    A_CCUCON3       = 0x44,
+    A_CCUCON4       = 0x48,
+    A_CCUCON5       = 0x4C,
+    A_WDTSCON0      = 0xF0,
+    A_WDTCPU0CON0   = 0x100,
+    A_CHIPID        = 0x140,
+};
+
+static void tricore_scu_update_ccucon(TriCoreSCUState *s)
+{
+    if ((s->ccucon[0] & MASK_CCUCON0_UP) ||
+        (s->ccucon[1] & MASK_CCUCON1_UP) ||
+        (s->ccucon[5] & MASK_CCUCON5_UP)) {
+        s->ccucon[0] &= ~(MASK_CCUCON0_UP | (1U << 31));
+        s->ccucon[1] &= ~(MASK_CCUCON1_UP | (1U << 31));
+        s->ccucon[5] &= ~(MASK_CCUCON5_UP | (1U << 31));
+    }
+}
+
+static uint64_t tricore_scu_read(void *opaque, hwaddr offset,
+                                 unsigned size)
+{
+    TriCoreSCUState *s = TRICORE_SCU(opaque);
+
+    switch (offset) {
+    case A_OSCCON:
+        return s->osccon;
+    case A_PLLSTAT:
+        /*
+         * Report PLL as always locked: VCOLOCK=1, K1RDY=1, K2RDY=1.
+         * Firmware polls these bits during clock initialisation.
+         */
+        return 0x00000077;
+    case A_PLLCON0:
+        return s->pllcon[0];
+    case A_PLLCON1:
+        return s->pllcon[1];
+    case A_PLLCON2:
+        return s->pllcon[2];
+    case A_PLLERAYSTAT:
+        return 0x00000077;
+    case A_CCUCON0:
+        return s->ccucon[0];
+    case A_CCUCON1:
+        return s->ccucon[1];
+    case A_CCUCON2:
+        return s->ccucon[2];
+    case A_CCUCON3:
+        return s->ccucon[3];
+    case A_CCUCON4:
+        return s->ccucon[4];
+    case A_CCUCON5:
+        return s->ccucon[5];
+    case A_WDTSCON0:
+        return s->wdtscon1;
+    case A_WDTCPU0CON0:
+        return s->wdtcpu0con0;
+    case A_CHIPID:
+        return 0x47477172 | (1U << 31);
+    default:
+        if (offset < SCU_REG_SIZE) {
+            return s->regs[offset / 4];
+        }
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "tricore_scu: read at bad offset 0x%" HWADDR_PRIx "\n",
+                      offset);
+        return 0;
+    }
+}
+
+static void tricore_scu_write(void *opaque, hwaddr offset,
+                              uint64_t value, unsigned size)
+{
+    TriCoreSCUState *s = TRICORE_SCU(opaque);
+
+    switch (offset) {
+    case A_OSCCON:
+        s->osccon = (uint32_t)value;
+        /*
+         * Real silicon raises PLLHV and PLLLV once the external
+         * oscillator is stable.  Mark stable immediately.
+         */
+        s->osccon |= MASK_OSCCON_PLLHV | MASK_OSCCON_PLLLV;
+        break;
+    case A_PLLCON0:
+        s->pllcon[0] = (uint32_t)value;
+        if (s->pllcon[0] & MASK_PLLCON0_SETFINDIS) {
+            s->pllstat |= MASK_PLLSTAT_FINDIS;
+        }
+        if (s->pllcon[0] & MASK_PLLCON0_CLRFINDIS) {
+            s->pllstat &= ~MASK_PLLSTAT_FINDIS;
+        }
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_PLLCON1:
+        s->pllcon[1] = (uint32_t)value;
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_PLLCON2:
+        s->pllcon[2] = (uint32_t)value;
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON0:
+        s->ccucon[0] = (uint32_t)value;
+        tricore_scu_update_ccucon(s);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON1:
+        s->ccucon[1] = (uint32_t)value;
+        tricore_scu_update_ccucon(s);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON2:
+        s->ccucon[2] = (uint32_t)value & ~(1U << 31);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON3:
+        s->ccucon[3] = (uint32_t)value & ~(1U << 31);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON4:
+        s->ccucon[4] = (uint32_t)value & ~(1U << 31);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_CCUCON5:
+        s->ccucon[5] = (uint32_t)value;
+        tricore_scu_update_ccucon(s);
+        s->pllstat |= MASK_PLLSTAT_VCOLOCK;
+        break;
+    case A_WDTSCON0:
+        s->wdtscon1 = (uint32_t)value;
+        break;
+    case A_WDTCPU0CON0:
+        s->wdtcpu0con0 = (uint32_t)value;
+        break;
+    default:
+        if (offset < SCU_REG_SIZE) {
+            s->regs[offset / 4] = (uint32_t)value & ~(1U << 31);
+            return;
+        }
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "tricore_scu: write at bad offset 0x%"
+                      HWADDR_PRIx "\n", offset);
+        break;
+    }
+}
+
+static const MemoryRegionOps tricore_scu_ops = {
+    .read = tricore_scu_read,
+    .write = tricore_scu_write,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+    .endianness = DEVICE_LITTLE_ENDIAN,
+};
+
+static void tricore_scu_reset_hold(Object *obj, ResetType type)
+{
+    TriCoreSCUState *s = TRICORE_SCU(obj);
+
+    s->ccucon[0] = RESET_TRICORE_CCUCON0;
+    s->ccucon[1] = RESET_TRICORE_CCUCON1;
+    s->ccucon[2] = RESET_TRICORE_CCUCON2;
+    s->ccucon[3] = RESET_TRICORE_CCUCON3;
+    s->ccucon[4] = RESET_TRICORE_CCUCON4;
+    s->ccucon[5] = RESET_TRICORE_CCUCON5;
+    s->fdr = RESET_TRICORE_FDR;
+    s->extcon = RESET_TRICORE_EXTCON;
+
+    s->osccon = RESET_TRICORE_OSCCON | MASK_OSCCON_PLLHV | MASK_OSCCON_PLLLV;
+
+    s->pllcon[0] = RESET_TRICORE_PLLCON0;
+    s->pllcon[1] = RESET_TRICORE_PLLCON1;
+    s->pllcon[2] = RESET_TRICORE_PLLCON2;
+    s->plleraycon[0] = RESET_TRICORE_PLLERAYCON0;
+    s->plleraycon[1] = RESET_TRICORE_PLLERAYCON1;
+    s->plleraystat = RESET_TRICORE_PLLERAYSTAT;
+    s->pllstat = RESET_TRICORE_PLLSTAT | MASK_PLLSTAT_VCOLOCK;
+
+    s->wdtcpu0con0 = RESET_TRICORE_WDTCPU0CON0;
+    s->wdtscon0 = RESET_TRICORE_WDTSCON0;
+    s->wdtscon1 = RESET_TRICORE_WDTSCON1;
+}
+
+static void tricore_scu_init(Object *obj)
+{
+    TriCoreSCUState *s = TRICORE_SCU(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+    memory_region_init_io(&s->iomem, obj, &tricore_scu_ops, s,
+                          TYPE_TRICORE_SCU, SCU_REG_SIZE);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static void tricore_scu_class_init(ObjectClass *klass, const void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+
+    rc->phases.hold = tricore_scu_reset_hold;
+}
+
+static const TypeInfo tricore_scu_info = {
+    .name = TYPE_TRICORE_SCU,
+    .parent = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(TriCoreSCUState),
+    .instance_init = tricore_scu_init,
+    .class_init = tricore_scu_class_init,
+};
+
+static void tricore_scu_register_types(void)
+{
+    type_register_static(&tricore_scu_info);
+}
+
+type_init(tricore_scu_register_types)
diff --git a/include/hw/tricore/tricore_scu.h b/include/hw/tricore/tricore_scu.h
new file mode 100644
index 0000000000..9cb7c916d0
--- /dev/null
+++ b/include/hw/tricore/tricore_scu.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * QEMU TriCore System Control Unit (SCU)
+ *
+ * Copyright (c) 2017 David Brenken <[email protected]>
+ * Copyright (c) 2024 Infineon Technologies AG
+ */
+
+#ifndef HW_TRICORE_SCU_H
+#define HW_TRICORE_SCU_H
+
+#include "hw/core/sysbus.h"
+#include "qom/object.h"
+
+#define TYPE_TRICORE_SCU "tricore_scu"
+OBJECT_DECLARE_SIMPLE_TYPE(TriCoreSCUState, TRICORE_SCU)
+
+/* OSCCON bit positions (iLLD IfxScu_bf.h) */
+#define MASK_OSCCON_PLLLV               0x00000002  /* bit 1 */
+#define MASK_OSCCON_PLLHV               0x00000100  /* bit 8 */
+
+/* PLLCON0 fields */
+#define MASK_PLLCON0_VCOBYP             0x00000001
+#define MASK_PLLCON0_SETFINDIS          0x00000010
+#define MASK_PLLCON0_CLRFINDIS          0x00000020
+#define MASK_PLLCON0_NDIV               0x0000FE00
+#define MASK_PLLCON0_PDIV               0x0F000000
+
+/* PLLCON1 fields */
+#define MASK_PLLCON1_K1DIV              0x007F0000
+#define MASK_PLLCON1_K2DIV              0x0000003F
+
+/* PLLSTAT fields */
+#define MASK_PLLSTAT_VCOBYST            0x00000001
+#define MASK_PLLSTAT_VCOLOCK            0x00000004
+#define MASK_PLLSTAT_FINDIS             0x00000008
+
+/* CCUCON fields */
+#define MASK_CCUCON0_SRIDIV             0x00000F00
+#define MASK_CCUCON0_SPBDIV             0x000F0000
+#define MASK_CCUCON0_UP                 0x40000000
+#define MASK_CCUCON1_STMDIV             0x00000F00
+#define MASK_CCUCON1_INSEL              0x30000000
+#define MASK_CCUCON1_INSEL_BACKUP       0x00000000
+#define MASK_CCUCON1_INSEL_OSC0         0x10000000
+#define MASK_CCUCON1_UP                 0x40000000
+#define MASK_CCUCON5_UP                 0x40000000
+
+/* Reset values */
+#define RESET_TRICORE_OSCCON            0x00000112
+#define RESET_TRICORE_PLLSTAT           0x00000038
+#define RESET_TRICORE_PLLCON0           0x0001C600
+#define RESET_TRICORE_PLLCON1           0x0002020F
+#define RESET_TRICORE_PLLCON2           0x00000000
+#define RESET_TRICORE_PLLERAYSTAT       0x00000038
+#define RESET_TRICORE_PLLERAYCON0       0x00012E00
+#define RESET_TRICORE_PLLERAYCON1       0x000F020F
+#define RESET_TRICORE_CCUCON0           0x01120148
+#define RESET_TRICORE_CCUCON1           0x00002211
+#define RESET_TRICORE_CCUCON2           0x00000002
+#define RESET_TRICORE_CCUCON3           0x00000000
+#define RESET_TRICORE_CCUCON4           0x00000000
+#define RESET_TRICORE_CCUCON5           0x00000041
+#define RESET_TRICORE_FDR               0x00000000
+#define RESET_TRICORE_EXTCON            0x00000000
+#define RESET_TRICORE_WDTSCON0          0xFFFC000E
+#define RESET_TRICORE_WDTSCON1          0x00000000
+#define RESET_TRICORE_WDTCPU0CON0      0xFFFC000E
+
+/* Clock frequencies */
+#define SCU_FBACKUP                     100000000
+#define SCU_XTAL1                       20000000
+
+#define SCU_REG_SIZE                    0x1000
+#define SCU_NUM_REGS                    (SCU_REG_SIZE / 4)
+
+struct TriCoreSCUState {
+    /* private */
+    SysBusDevice parent_obj;
+
+    /* public */
+    MemoryRegion iomem;
+
+    /* CCU registers */
+    uint32_t osccon;
+    uint32_t pllstat;
+    uint32_t pllcon[3];
+    uint32_t plleraystat;
+    uint32_t plleraycon[2];
+    uint32_t ccucon[6];
+    uint32_t fdr;
+    uint32_t extcon;
+
+    /* Watchdog stub registers */
+    uint32_t wdtscon0;
+    uint32_t wdtscon1;
+    uint32_t wdtcpu0con0;
+
+    /* Backing store for remaining offsets */
+    uint32_t regs[SCU_NUM_REGS];
+};
+
+#endif

-- 
2.47.3


Reply via email to