All of lore.kernel.org
 help / color / mirror / Atom feed
From: Havard Skinnemoen <hskinnemoen@google.com>
To: peter.maydell@linaro.org
Cc: qemu-arm@nongnu.org, qemu-devel@nongnu.org,
	Avi.Fishman@nuvoton.com,  kfting@nuvoton.com,
	Havard Skinnemoen <hskinnemoen@google.com>
Subject: [PATCH 3/6] hw/misc: Add NPCM7xx Clock Controller device model
Date: Thu, 21 May 2020 12:21:30 -0700	[thread overview]
Message-ID: <20200521192133.127559-4-hskinnemoen@google.com> (raw)
In-Reply-To: <20200521192133.127559-1-hskinnemoen@google.com>

Enough functionality to boot the Linux kernel has been implemented. This
includes:

  - Correct power-on reset values so the various clock rates can be
    accurately calculated.
  - Clock enables stick around when written.

In addition, a best effort attempt to implement SECCNT and CNTR25M was
made even though I don't think the kernel needs them.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 hw/misc/Makefile.objs         |   1 +
 hw/misc/npcm7xx_clk.c         | 210 ++++++++++++++++++++++++++++++++++
 hw/misc/trace-events          |   4 +
 include/hw/misc/npcm7xx_clk.h |  65 +++++++++++
 4 files changed, 280 insertions(+)
 create mode 100644 hw/misc/npcm7xx_clk.c
 create mode 100644 include/hw/misc/npcm7xx_clk.h

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 4d83fa337b..1096a76457 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -51,6 +51,7 @@ common-obj-$(CONFIG_IMX) += imx_rngc.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-hpdmc.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-pfpu.o
 common-obj-$(CONFIG_MAINSTONE) += mst_fpga.o
+common-obj-$(CONFIG_NPCM7XX) += npcm7xx_clk.o
 common-obj-$(CONFIG_NPCM7XX) += npcm7xx_gcr.o
 common-obj-$(CONFIG_OMAP) += omap_clk.o
 common-obj-$(CONFIG_OMAP) += omap_gpmc.o
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
new file mode 100644
index 0000000000..b4ff85e8c2
--- /dev/null
+++ b/hw/misc/npcm7xx_clk.c
@@ -0,0 +1,210 @@
+/*
+ * Nuvoton NPCM7xx Clock Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * 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.
+ */
+
+#include "qemu/osdep.h"
+
+#include "hw/misc/npcm7xx_clk.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+#define PLLCON_LOKI     BIT(31)
+#define PLLCON_LOKS     BIT(30)
+#define PLLCON_PWDEN    BIT(12)
+
+static const uint32_t cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
+    [NPCM7XX_CLK_CLKEN1]        = 0xffffffff,
+    [NPCM7XX_CLK_CLKSEL]        = 0x004aaaaa,
+    [NPCM7XX_CLK_CLKDIV1]       = 0x5413f855,
+    [NPCM7XX_CLK_PLLCON0]       = 0x00222101 | PLLCON_LOKI,
+    [NPCM7XX_CLK_PLLCON1]       = 0x00202101 | PLLCON_LOKI,
+    [NPCM7XX_CLK_IPSRST1]       = 0x00001000,
+    [NPCM7XX_CLK_IPSRST2]       = 0x80000000,
+    [NPCM7XX_CLK_CLKEN2]        = 0xffffffff,
+    [NPCM7XX_CLK_CLKDIV2]       = 0xaa4f8f9f,
+    [NPCM7XX_CLK_CLKEN3]        = 0xffffffff,
+    [NPCM7XX_CLK_IPSRST3]       = 0x03000000,
+    [NPCM7XX_CLK_WD0RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_WD1RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_WD2RCR]        = 0xffffffff,
+    [NPCM7XX_CLK_SWRSTC1]       = 0x00000003,
+    [NPCM7XX_CLK_PLLCON2]       = 0x00c02105 | PLLCON_LOKI,
+    [NPCM7XX_CLK_CORSTC]        = 0x04000003,
+    [NPCM7XX_CLK_PLLCONG]       = 0x01228606 | PLLCON_LOKI,
+    [NPCM7XX_CLK_AHBCKFI]       = 0x000000c8,
+};
+
+static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxCLKState *s = opaque;
+    int64_t now_ns;
+    uint32_t value = 0;
+
+    if (reg >= NPCM7XX_CLK_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
+                      __func__, (unsigned int)offset);
+        return 0;
+    }
+
+    switch (reg) {
+    case NPCM7XX_CLK_SWRSTR:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is write-only\n",
+                      __func__, (unsigned int)offset);
+        break;
+
+    case NPCM7XX_CLK_SECCNT:
+        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        value = (now_ns - s->ref_ns) / NANOSECONDS_PER_SECOND;
+        break;
+
+    case NPCM7XX_CLK_CNTR25M:
+        now_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        /*
+         * This register counts 25 MHz cycles, updating every 640 ns. It rolls
+         * over to zero every second.
+         *
+         * The 4 LSBs are always zero: (1e9 / 640) << 4 = 25000000.
+         */
+        value = (((now_ns - s->ref_ns) / 640) << 4) % 25000000;
+        break;
+
+    default:
+        value = s->regs[reg];
+        break;
+    };
+
+    trace_npcm7xx_clk_read(offset, value);
+
+    return value;
+}
+
+static void npcm7xx_clk_write(void *opaque, hwaddr offset,
+                              uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxCLKState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_clk_write(offset, value);
+
+    if (reg >= NPCM7XX_CLK_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
+                      __func__, (unsigned int)offset);
+        return;
+    }
+
+    switch (reg) {
+    case NPCM7XX_CLK_SWRSTR:
+        qemu_log_mask(LOG_UNIMP, "%s: SW reset not implemented: 0x%02x\n",
+                      __func__, value);
+        value = 0;
+        break;
+
+    case NPCM7XX_CLK_PLLCON0:
+    case NPCM7XX_CLK_PLLCON1:
+    case NPCM7XX_CLK_PLLCON2:
+    case NPCM7XX_CLK_PLLCONG:
+        if (value & PLLCON_PWDEN) {
+            /* Power down -- clear lock and indicate loss of lock */
+            value &= ~PLLCON_LOKI;
+            value |= PLLCON_LOKS;
+        } else {
+            /* Normal mode -- assume always locked */
+            value |= PLLCON_LOKI;
+            /* Keep LOKS unchanged unless cleared by writing 1 */
+            if (value & PLLCON_LOKS) {
+                value &= ~PLLCON_LOKS;
+            } else {
+                value |= (value & PLLCON_LOKS);
+            }
+        }
+        break;
+
+    case NPCM7XX_CLK_CNTR25M:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
+                      __func__, (unsigned int)offset);
+        return;
+    }
+
+    s->regs[reg] = value;
+}
+
+static const struct MemoryRegionOps npcm7xx_clk_ops = {
+    .read       = npcm7xx_clk_read,
+    .write      = npcm7xx_clk_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_clk_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+
+    QEMU_BUILD_BUG_ON(sizeof(s->regs) != sizeof(cold_reset_values));
+
+    switch (type) {
+    case RESET_TYPE_COLD:
+        memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values));
+        s->ref_ns = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+        return;
+    }
+
+    /*
+     * A small number of registers need to be reset on a core domain reset,
+     * but no such reset type exists yet.
+     */
+    qemu_log_mask(LOG_UNIMP, "%s: reset type %d not implemented.",
+                  __func__, type);
+}
+
+static void npcm7xx_clk_init(Object *obj)
+{
+    NPCM7xxCLKState *s = NPCM7XX_CLK(obj);
+
+    memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s,
+                          TYPE_NPCM7XX_CLK, 4 * KiB);
+    sysbus_init_mmio(&s->parent, &s->iomem);
+}
+
+static void npcm7xx_clk_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx Clock Control Registers";
+    rc->phases.enter = npcm7xx_clk_enter_reset;
+}
+
+static const TypeInfo npcm7xx_clk_info = {
+    .name               = TYPE_NPCM7XX_CLK,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxCLKState),
+    .instance_init      = npcm7xx_clk_init,
+    .class_init         = npcm7xx_clk_class_init,
+};
+
+static void npcm7xx_clk_register_type(void)
+{
+    type_register_static(&npcm7xx_clk_info);
+}
+type_init(npcm7xx_clk_register_type);
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index d200438dbf..ae97e8b091 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -103,6 +103,10 @@ mos6522_set_sr_int(void) "set sr_int"
 mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64
 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x"
 
+# npcm7xx_clk.c
+npcm7xx_clk_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+npcm7xx_clk_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
+
 # npcm7xx_gcr.c
 npcm7xx_gcr_read(uint64_t offset, uint32_t value) " offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
 npcm7xx_gcr_write(uint64_t offset, uint32_t value) "offset: 0x%04" PRIx64 " value: 0x%08" PRIx32
diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
new file mode 100644
index 0000000000..a678b50ede
--- /dev/null
+++ b/include/hw/misc/npcm7xx_clk.h
@@ -0,0 +1,65 @@
+/*
+ * Nuvoton NPCM7xx Clock Control Registers.
+ *
+ * Copyright 2020 Google LLC
+ *
+ * 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.
+ */
+#ifndef NPCM7XX_CLK_H
+#define NPCM7XX_CLK_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+enum NPCM7xxCLKRegisters {
+    NPCM7XX_CLK_CLKEN1,
+    NPCM7XX_CLK_CLKSEL,
+    NPCM7XX_CLK_CLKDIV1,
+    NPCM7XX_CLK_PLLCON0,
+    NPCM7XX_CLK_PLLCON1,
+    NPCM7XX_CLK_SWRSTR,
+    NPCM7XX_CLK_IPSRST1         = 0x20 / sizeof(uint32_t),
+    NPCM7XX_CLK_IPSRST2,
+    NPCM7XX_CLK_CLKEN2,
+    NPCM7XX_CLK_CLKDIV2,
+    NPCM7XX_CLK_CLKEN3,
+    NPCM7XX_CLK_IPSRST3,
+    NPCM7XX_CLK_WD0RCR,
+    NPCM7XX_CLK_WD1RCR,
+    NPCM7XX_CLK_WD2RCR,
+    NPCM7XX_CLK_SWRSTC1,
+    NPCM7XX_CLK_SWRSTC2,
+    NPCM7XX_CLK_SWRSTC3,
+    NPCM7XX_CLK_SWRSTC4,
+    NPCM7XX_CLK_PLLCON2,
+    NPCM7XX_CLK_CLKDIV3,
+    NPCM7XX_CLK_CORSTC,
+    NPCM7XX_CLK_PLLCONG,
+    NPCM7XX_CLK_AHBCKFI,
+    NPCM7XX_CLK_SECCNT,
+    NPCM7XX_CLK_CNTR25M,
+    NPCM7XX_CLK_NR_REGS,
+};
+
+typedef struct NPCM7xxCLKState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t regs[NPCM7XX_CLK_NR_REGS];
+
+    /* Time reference for SECCNT and CNTR25M, initialized by power on reset */
+    int64_t ref_ns;
+} NPCM7xxCLKState;
+
+#define TYPE_NPCM7XX_CLK "npcm7xx-clk"
+#define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX_CLK)
+
+#endif /* NPCM7XX_CLK_H */
-- 
2.27.0.rc0.183.gde8f92d652-goog



  parent reply	other threads:[~2020-05-21 20:39 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-21 19:21 [PATCH 0/6] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
2020-05-21 19:21 ` [PATCH 1/6] npcm7xx: Add config symbol Havard Skinnemoen
2020-06-09  1:36   ` Joel Stanley
2020-05-21 19:21 ` [PATCH 2/6] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
2020-06-09  1:36   ` Joel Stanley
2020-06-09  7:24   ` Cédric Le Goater
2020-05-21 19:21 ` Havard Skinnemoen [this message]
2020-06-09  1:36   ` [PATCH 3/6] hw/misc: Add NPCM7xx Clock Controller " Joel Stanley
2020-06-09  7:24   ` Cédric Le Goater
2020-05-21 19:21 ` [PATCH 4/6] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen
2020-06-09  1:36   ` Joel Stanley
2020-05-21 19:21 ` [PATCH 5/6] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
2020-06-09  1:36   ` Joel Stanley
2020-06-09  7:24   ` Cédric Le Goater
2020-06-09 23:06     ` Havard Skinnemoen
2020-06-11  6:12       ` Cédric Le Goater
2020-06-11  9:38         ` Peter Maydell
2020-06-17 15:24           ` Cédric Le Goater
2020-05-21 19:21 ` [PATCH 6/6] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen
2020-06-09  1:36   ` Joel Stanley
2020-06-09  7:24   ` Cédric Le Goater
2020-05-22  4:57 ` [PATCH 0/6] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines no-reply
2020-06-08 22:14 ` Havard Skinnemoen
2020-06-09  1:39   ` Joel Stanley
2020-06-09 16:36     ` Havard Skinnemoen
2020-06-09  7:31   ` Cédric Le Goater
2020-06-09  9:38     ` Peter Maydell
2020-06-09 16:30       ` Havard Skinnemoen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200521192133.127559-4-hskinnemoen@google.com \
    --to=hskinnemoen@google.com \
    --cc=Avi.Fishman@nuvoton.com \
    --cc=kfting@nuvoton.com \
    --cc=peter.maydell@linaro.org \
    --cc=qemu-arm@nongnu.org \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.