All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines
@ 2020-07-09  0:35 Havard Skinnemoen
  2020-07-09  0:35 ` [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
                   ` (10 more replies)
  0 siblings, 11 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:35 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

I also pushed this and the previous two patchsets to my qemu fork on github.
The branches are named npcm7xx-v[1-5].

  https://github.com/hskinnemoen/qemu

This patch series models enough of the Nuvoton NPCM730 and NPCM750 SoCs to boot
an OpenBMC image built for quanta-gsj. This includes device models for:

  - Global Configuration Registers
  - Clock Control
  - Timers
  - Fuses
  - Memory Controller
  - Flash Controller

These modules, along with the existing Cortex A9 CPU cores and built-in
peripherals, are integrated into a NPCM730 or NPCM750 SoC, which in turn form
the foundation for the quanta-gsj and npcm750-evb machines, respectively. The
two SoCs are very similar; the only difference is that NPCM730 is missing some
peripherals that NPCM750 has, and which are not considered essential for
datacenter use (e.g. graphics controllers). For more information, see

https://www.nuvoton.com/products/cloud-computing/ibmc/

Both quanta-gsj and npcm750-evb correspond to real boards supported by OpenBMC.
At the end of the series, qemu can boot an OpenBMC image built for one of these
boards with some minor modifications.

The patches in this series were developed by Google and reviewed by Nuvoton. We
will be maintaining the machine and peripheral support together.

The data sheet for these SoCs is not generally available. Please let me know if
more comments are needed to understand the device behavior.

Changes since v4:
  - OTP cleanups suggested by Philippe Mathieu-Daudé.
      - Added fuse array definitions based on public Nuvoton bootblock code.
      - Moved class structure to .c file since it's only used internally.
      - Readability improvements.
  - Split the first patch and folded parts of it into three other patches so
    that CONFIG_NPCM7XX is only enabled after the initial NPCM7xx machine
    support is added.
  - DRAM init moved to machine init code.
  - Consistently use lower-case hex literals.
  - Switched to fine-grained unimplemented devices, based on public bootblock
    source code. Added a tiny SRAM that got left out previously.
  - Simplified error handling in npcm7xx_realize() since the board code will
    abort anyway, and SoCs are not hot-pluggable.

Changes since v3:

  - License headers are now GPL v2-or-later throughout.
  - Added vmstate throughout (except the memory controller, which doesn't
    really have any state worth saving). Successfully booted a gsj image
    with two stop/savevm/quit/loadvm cycles along the way.
      - JFFS2 really doesn't like it if I let qemu keep running after savevm,
        and then jump back in time with loadvm. I assume this is expected.
  - Fixed an error API violation in npcm7xx_realize, removed pointless error
    check after object_property_set_link().
  - Switched the OTP device to use an embedded array instead of a g_malloc0'd
    one because I couldn't figure out how to set up vmstate for the latter.

Changes since v2:

  - Simplified the MAINTAINERS entry.
  - Added link to OpenPOWER jenkins for gsj BMC images.
  - Reverted the smpboot change, back to byte swapping.
  - Adapted to upstream API changes:
      - sysbus_init_child_obj -> object_initialize_child
      - object_property_set_bool -> qdev_realize / sysbus_realize
      - ssi_create_slave_no_init -> qdev_new
      - qdev_init_nofail -> qdev_realize_and_unref
      - ssi_auto_connect_slaves removed
  - Moved Boot ROM loading from soc to machine init.
  - Plumbed power-on-straps property from GCR to the machine init code so it
    can be properly initialized. Turns out npcm750 memory init doesn't work
    without this. npcm730 is fine either way, though I'm not sure why.
  - Reworked the flash init code so it looks more like aspeed (i.e. the flash
    device gets added even if there's no drive).

Changes since v1 (requested by reviewers):

  - Clarify the source of CLK reset values.
  - Made smpboot a constant byte array, eliinated byte swapping.
  - NPCM7xxState now stores an array of ARMCPUs, not pointers to ARMCPUs.
  - Clarify why EL3 is disabled.
  - Introduce NPCM7XX_NUM_IRQ constant.
  - Set the number of CPUs according to SoC variant, and disallow command line
    overrides (i.e. you can no longer override the number of CPUs with the -smp
    parameter). This is trying to follow the spirit of
    https://patchwork.kernel.org/patch/11595407/.
  - Switch register operations to DEVICE_LITTLE_ENDIAN throughout.
  - Machine documentation added (new patch).

Changes since v1 to support flash booting:

  - GCR reset value changes to get past memory initialization when booting
    from flash (patches 2 and 5):
      - INTCR2 now indicates that the DDR controller is initialized.
      - INTCR3 is initialized according to DDR memory size. A realize()
	method was implemented to achieve this.
  - Refactor the machine initialization a bit to make it easier to drop in
    machine-specific flash initialization (patch 6).
  - Extend the series with additional patches to enable booting from flash:
      - Boot ROM (through the -bios option).
      - OTP (fuse) controller.
      - Memory Controller stub (just enough to skip memory training).
      - Flash controller.
      - Board-specific flash initialization.

Thanks for reviewing,

Havard

Havard Skinnemoen (11):
  hw/misc: Add NPCM7xx System Global Control Registers device model
  hw/misc: Add NPCM7xx Clock Controller device model
  hw/timer: Add NPCM7xx Timer device model
  hw/arm: Add NPCM730 and NPCM750 SoC models
  hw/arm: Add two NPCM7xx-based machines
  hw/arm: Load -bios image as a boot ROM for npcm7xx
  hw/nvram: NPCM7xx OTP device model
  hw/mem: Stubbed out NPCM7xx Memory Controller model
  hw/ssi: NPCM7xx Flash Interface Unit device model
  hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  docs/system: Add Nuvoton machine documentation

 docs/system/arm/nuvoton.rst      |  92 ++++++
 docs/system/target-arm.rst       |   1 +
 default-configs/arm-softmmu.mak  |   1 +
 include/hw/arm/npcm7xx.h         | 112 +++++++
 include/hw/mem/npcm7xx_mc.h      |  36 +++
 include/hw/misc/npcm7xx_clk.h    |  66 ++++
 include/hw/misc/npcm7xx_gcr.h    |  76 +++++
 include/hw/nvram/npcm7xx_otp.h   |  88 ++++++
 include/hw/ssi/npcm7xx_fiu.h     | 100 ++++++
 include/hw/timer/npcm7xx_timer.h |  96 ++++++
 hw/arm/npcm7xx.c                 | 464 ++++++++++++++++++++++++++++
 hw/arm/npcm7xx_boards.c          | 200 ++++++++++++
 hw/mem/npcm7xx_mc.c              |  84 +++++
 hw/misc/npcm7xx_clk.c            | 230 ++++++++++++++
 hw/misc/npcm7xx_gcr.c            | 226 ++++++++++++++
 hw/nvram/npcm7xx_otp.c           | 424 +++++++++++++++++++++++++
 hw/ssi/npcm7xx_fiu.c             | 510 +++++++++++++++++++++++++++++++
 hw/timer/npcm7xx_timer.c         | 468 ++++++++++++++++++++++++++++
 MAINTAINERS                      |   8 +
 hw/arm/Kconfig                   |   9 +
 hw/arm/Makefile.objs             |   1 +
 hw/mem/Makefile.objs             |   1 +
 hw/misc/Makefile.objs            |   2 +
 hw/misc/trace-events             |   8 +
 hw/nvram/Makefile.objs           |   1 +
 hw/ssi/Makefile.objs             |   1 +
 hw/ssi/trace-events              |   9 +
 hw/timer/Makefile.objs           |   1 +
 hw/timer/trace-events            |   5 +
 29 files changed, 3320 insertions(+)
 create mode 100644 docs/system/arm/nuvoton.rst
 create mode 100644 include/hw/arm/npcm7xx.h
 create mode 100644 include/hw/mem/npcm7xx_mc.h
 create mode 100644 include/hw/misc/npcm7xx_clk.h
 create mode 100644 include/hw/misc/npcm7xx_gcr.h
 create mode 100644 include/hw/nvram/npcm7xx_otp.h
 create mode 100644 include/hw/ssi/npcm7xx_fiu.h
 create mode 100644 include/hw/timer/npcm7xx_timer.h
 create mode 100644 hw/arm/npcm7xx.c
 create mode 100644 hw/arm/npcm7xx_boards.c
 create mode 100644 hw/mem/npcm7xx_mc.c
 create mode 100644 hw/misc/npcm7xx_clk.c
 create mode 100644 hw/misc/npcm7xx_gcr.c
 create mode 100644 hw/nvram/npcm7xx_otp.c
 create mode 100644 hw/ssi/npcm7xx_fiu.c
 create mode 100644 hw/timer/npcm7xx_timer.c

-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply	[flat|nested] 64+ messages in thread

* [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
@ 2020-07-09  0:35 ` Havard Skinnemoen
  2020-07-09  6:04   ` Philippe Mathieu-Daudé
  2020-07-09  0:35 ` [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen
                   ` (9 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:35 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

Implement a device model for the System Global Control Registers in the
NPCM730 and NPCM750 BMC SoCs.

This is primarily used to enable SMP boot (the boot ROM spins reading
the SCRPAD register) and DDR memory initialization; other registers are
best effort for now.

The reset values of the MDLR and PWRON registers are determined by the
SoC variant (730 vs 750) and board straps respectively.

Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/misc/npcm7xx_gcr.h |  76 ++++++++++++
 hw/misc/npcm7xx_gcr.c         | 226 ++++++++++++++++++++++++++++++++++
 MAINTAINERS                   |   8 ++
 hw/arm/Kconfig                |   3 +
 hw/misc/Makefile.objs         |   1 +
 hw/misc/trace-events          |   4 +
 6 files changed, 318 insertions(+)
 create mode 100644 include/hw/misc/npcm7xx_gcr.h
 create mode 100644 hw/misc/npcm7xx_gcr.c

diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h
new file mode 100644
index 0000000000..4884676be2
--- /dev/null
+++ b/include/hw/misc/npcm7xx_gcr.h
@@ -0,0 +1,76 @@
+/*
+ * Nuvoton NPCM7xx System Global 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_GCR_H
+#define NPCM7XX_GCR_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+enum NPCM7xxGCRRegisters {
+    NPCM7XX_GCR_PDID,
+    NPCM7XX_GCR_PWRON,
+    NPCM7XX_GCR_MFSEL1          = 0x0c / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL2,
+    NPCM7XX_GCR_MISCPE,
+    NPCM7XX_GCR_SPSWC           = 0x038 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR,
+    NPCM7XX_GCR_INTSR,
+    NPCM7XX_GCR_HIFCR           = 0x050 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR2          = 0x060 / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL3,
+    NPCM7XX_GCR_SRCNT,
+    NPCM7XX_GCR_RESSR,
+    NPCM7XX_GCR_RLOCKR1,
+    NPCM7XX_GCR_FLOCKR1,
+    NPCM7XX_GCR_DSCNT,
+    NPCM7XX_GCR_MDLR,
+    NPCM7XX_GCR_SCRPAD3,
+    NPCM7XX_GCR_SCRPAD2,
+    NPCM7XX_GCR_DAVCLVLR        = 0x098 / sizeof(uint32_t),
+    NPCM7XX_GCR_INTCR3,
+    NPCM7XX_GCR_VSINTR          = 0x0ac / sizeof(uint32_t),
+    NPCM7XX_GCR_MFSEL4,
+    NPCM7XX_GCR_CPBPNTR         = 0x0c4 / sizeof(uint32_t),
+    NPCM7XX_GCR_CPCTL           = 0x0d0 / sizeof(uint32_t),
+    NPCM7XX_GCR_CP2BST,
+    NPCM7XX_GCR_B2CPNT,
+    NPCM7XX_GCR_CPPCTL,
+    NPCM7XX_GCR_I2CSEGSEL,
+    NPCM7XX_GCR_I2CSEGCTL,
+    NPCM7XX_GCR_VSRCR,
+    NPCM7XX_GCR_MLOCKR,
+    NPCM7XX_GCR_SCRPAD          = 0x013c / sizeof(uint32_t),
+    NPCM7XX_GCR_USB1PHYCTL,
+    NPCM7XX_GCR_USB2PHYCTL,
+    NPCM7XX_GCR_NR_REGS,
+};
+
+typedef struct NPCM7xxGCRState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t regs[NPCM7XX_GCR_NR_REGS];
+
+    uint32_t reset_pwron;
+    uint32_t reset_mdlr;
+    uint32_t reset_intcr3;
+} NPCM7xxGCRState;
+
+#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
+#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR)
+
+#endif /* NPCM7XX_GCR_H */
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
new file mode 100644
index 0000000000..9934cd238d
--- /dev/null
+++ b/hw/misc/npcm7xx_gcr.c
@@ -0,0 +1,226 @@
+/*
+ * Nuvoton NPCM7xx System Global 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_gcr.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#include "trace.h"
+
+static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
+    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
+    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
+    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
+    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
+    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
+    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
+    [NPCM7XX_GCR_RESSR]         = 0x80000000,
+    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
+    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
+    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
+    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
+    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
+};
+
+static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxGCRState *s = opaque;
+
+    if (reg >= NPCM7XX_GCR_NR_REGS) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
+                      __func__, (unsigned int)offset);
+        return 0;
+    }
+
+    trace_npcm7xx_gcr_read(offset, s->regs[reg]);
+
+    return s->regs[reg];
+}
+
+static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
+                              uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxGCRState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_gcr_write(offset, value);
+
+    if (reg >= NPCM7XX_GCR_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_GCR_PDID:
+    case NPCM7XX_GCR_PWRON:
+    case NPCM7XX_GCR_INTSR:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
+                      __func__, (unsigned int)offset);
+        return;
+
+    case NPCM7XX_GCR_RESSR:
+    case NPCM7XX_GCR_CP2BST:
+        /* Write 1 to clear */
+        value = s->regs[reg] & ~value;
+        break;
+
+    case NPCM7XX_GCR_RLOCKR1:
+    case NPCM7XX_GCR_MDLR:
+        /* Write 1 to set */
+        value |= s->regs[reg];
+        break;
+    };
+
+    s->regs[reg] = value;
+}
+
+static const struct MemoryRegionOps npcm7xx_gcr_ops = {
+    .read       = npcm7xx_gcr_read,
+    .write      = npcm7xx_gcr_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxGCRState *s = NPCM7XX_GCR(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(s->regs));
+        s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
+        s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
+        s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
+        break;
+    }
+}
+
+static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
+    uint64_t dram_size;
+    Error *err = NULL;
+    Object *obj;
+
+    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
+    if (!obj) {
+        error_setg(errp, "%s: required dram-mr link not found: %s",
+                   __func__, error_get_pretty(err));
+        return;
+    }
+    dram_size = memory_region_size(MEMORY_REGION(obj));
+
+    /* Power-on reset value */
+    s->reset_intcr3 = 0x00001002;
+
+    /*
+     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
+     * DRAM size, and is normally initialized by the boot block as part of DRAM
+     * training. However, since we don't have a complete emulation of the
+     * memory controller and try to make it look like it has already been
+     * initialized, the boot block will skip this initialization, and we need
+     * to make sure this field is set correctly up front.
+     *
+     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
+     * more of DRAM will be interpreted as 128 MiB.
+     *
+     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
+     */
+    if (dram_size >= 2 * GiB) {
+        s->reset_intcr3 |= 4 << 8;
+    } else if (dram_size >= 1 * GiB) {
+        s->reset_intcr3 |= 3 << 8;
+    } else if (dram_size >= 512 * MiB) {
+        s->reset_intcr3 |= 2 << 8;
+    } else if (dram_size >= 256 * MiB) {
+        s->reset_intcr3 |= 1 << 8;
+    } else if (dram_size >= 128 * MiB) {
+        s->reset_intcr3 |= 0 << 8;
+    } else {
+        error_setg(errp,
+                   "npcm7xx_gcr: DRAM size %" PRIu64
+                   " is too small (need 128 MiB minimum)",
+                   dram_size);
+        return;
+    }
+}
+
+static void npcm7xx_gcr_init(Object *obj)
+{
+    NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
+
+    memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
+                          TYPE_NPCM7XX_GCR, 4 * KiB);
+    sysbus_init_mmio(&s->parent, &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm7xx_gcr = {
+    .name = "npcm7xx-gcr",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static Property npcm7xx_gcr_properties[] = {
+    DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
+    DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx System Global Control Registers";
+    dc->realize = npcm7xx_gcr_realize;
+    dc->vmsd = &vmstate_npcm7xx_gcr;
+    rc->phases.enter = npcm7xx_gcr_enter_reset;
+
+    device_class_set_props(dc, npcm7xx_gcr_properties);
+}
+
+static const TypeInfo npcm7xx_gcr_info = {
+    .name               = TYPE_NPCM7XX_GCR,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxGCRState),
+    .instance_init      = npcm7xx_gcr_init,
+    .class_init         = npcm7xx_gcr_class_init,
+};
+
+static void npcm7xx_gcr_register_type(void)
+{
+    type_register_static(&npcm7xx_gcr_info);
+}
+type_init(npcm7xx_gcr_register_type);
diff --git a/MAINTAINERS b/MAINTAINERS
index 42388f1de2..43173a338a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -723,6 +723,14 @@ S: Odd Fixes
 F: hw/arm/musicpal.c
 F: docs/system/arm/musicpal.rst
 
+Nuvoton NPCM7xx
+M: Havard Skinnemoen <hskinnemoen@google.com>
+M: Tyrone Ting <kfting@nuvoton.com>
+L: qemu-arm@nongnu.org
+S: Supported
+F: hw/*/npcm7xx*
+F: include/hw/*/npcm7xx*
+
 nSeries
 M: Andrzej Zaborowski <balrogg@gmail.com>
 M: Peter Maydell <peter.maydell@linaro.org>
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 4a224a6351..192a8dec3b 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -354,6 +354,9 @@ config XLNX_VERSAL
     select VIRTIO_MMIO
     select UNIMP
 
+config NPCM7XX
+    bool
+
 config FSL_IMX25
     bool
     select IMX
diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index 5aaca8a039..40a9d1c01e 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_gcr.o
 common-obj-$(CONFIG_OMAP) += omap_clk.o
 common-obj-$(CONFIG_OMAP) += omap_gpmc.o
 common-obj-$(CONFIG_OMAP) += omap_l4.o
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index ebea53735c..48e2d54c49 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -107,6 +107,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_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
+
 # stm32f4xx_syscfg
 stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
 stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller device model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
  2020-07-09  0:35 ` [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
@ 2020-07-09  0:35 ` Havard Skinnemoen
  2020-07-15  7:18   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen
                   ` (8 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:35 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

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>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/misc/npcm7xx_clk.h |  66 ++++++++++
 hw/misc/npcm7xx_clk.c         | 230 ++++++++++++++++++++++++++++++++++
 hw/misc/Makefile.objs         |   1 +
 hw/misc/trace-events          |   4 +
 4 files changed, 301 insertions(+)
 create mode 100644 include/hw/misc/npcm7xx_clk.h
 create mode 100644 hw/misc/npcm7xx_clk.c

diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h
new file mode 100644
index 0000000000..44afa1b105
--- /dev/null
+++ b/include/hw/misc/npcm7xx_clk.h
@@ -0,0 +1,66 @@
+/*
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 */
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
new file mode 100644
index 0000000000..9c34b12ea9
--- /dev/null
+++ b/hw/misc/npcm7xx_clk.c
@@ -0,0 +1,230 @@
+/*
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "migration/vmstate.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)
+
+/*
+ * These reset values were taken from version 0.91 of the NPCM750R data sheet.
+ *
+ * All are loaded on power-up reset. CLKENx and SWRSTR should also be loaded on
+ * core domain reset, but this reset type is not yet supported by QEMU.
+ */
+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_LITTLE_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 const VMStateDescription vmstate_npcm7xx_clk = {
+    .name = "npcm7xx-clk",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS),
+        VMSTATE_INT64(ref_ns, NPCM7xxCLKState),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+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";
+    dc->vmsd = &vmstate_npcm7xx_clk;
+    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/Makefile.objs b/hw/misc/Makefile.objs
index 40a9d1c01e..2e74803005 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/trace-events b/hw/misc/trace-events
index 48e2d54c49..a531af5b79 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -107,6 +107,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
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer device model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
  2020-07-09  0:35 ` [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
  2020-07-09  0:35 ` [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-15  7:25   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
                   ` (7 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley

The NPCM730 and NPCM750 SoCs have three timer modules each holding five
timers and some shared registers (e.g. interrupt status).

Each timer runs at 25 MHz divided by a prescaler, and counts down from a
configurable initial value to zero. When zero is reached, the interrupt
flag for the timer is set, and the timer is disabled (one-shot mode) or
reloaded from its initial value (periodic mode).

This implementation is sufficient to boot a Linux kernel configured for
NPCM750. Note that the kernel does not seem to actually turn on the
interrupts.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/timer/npcm7xx_timer.h |  96 +++++++
 hw/timer/npcm7xx_timer.c         | 468 +++++++++++++++++++++++++++++++
 hw/timer/Makefile.objs           |   1 +
 hw/timer/trace-events            |   5 +
 4 files changed, 570 insertions(+)
 create mode 100644 include/hw/timer/npcm7xx_timer.h
 create mode 100644 hw/timer/npcm7xx_timer.c

diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h
new file mode 100644
index 0000000000..94900a7877
--- /dev/null
+++ b/include/hw/timer/npcm7xx_timer.h
@@ -0,0 +1,96 @@
+/*
+ * Nuvoton NPCM7xx Timer Controller
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_TIMER_H
+#define NPCM7XX_TIMER_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+#include "qemu/timer.h"
+
+/* Each Timer Module (TIM) instance holds five 25 MHz timers. */
+#define NPCM7XX_TIMERS_PER_CTRL (5)
+
+/**
+ * enum NPCM7xxTimerRegisters - 32-bit register indices.
+ */
+enum NPCM7xxTimerRegisters {
+    NPCM7XX_TIMER_TCSR0,
+    NPCM7XX_TIMER_TCSR1,
+    NPCM7XX_TIMER_TICR0,
+    NPCM7XX_TIMER_TICR1,
+    NPCM7XX_TIMER_TDR0,
+    NPCM7XX_TIMER_TDR1,
+    NPCM7XX_TIMER_TISR,
+    NPCM7XX_TIMER_WTCR,
+    NPCM7XX_TIMER_TCSR2,
+    NPCM7XX_TIMER_TCSR3,
+    NPCM7XX_TIMER_TICR2,
+    NPCM7XX_TIMER_TICR3,
+    NPCM7XX_TIMER_TDR2,
+    NPCM7XX_TIMER_TDR3,
+    NPCM7XX_TIMER_TCSR4         = 0x0040 / sizeof(uint32_t),
+    NPCM7XX_TIMER_TICR4         = 0x0048 / sizeof(uint32_t),
+    NPCM7XX_TIMER_TDR4          = 0x0050 / sizeof(uint32_t),
+    NPCM7XX_TIMER_NR_REGS,
+};
+
+typedef struct NPCM7xxTimerCtrlState NPCM7xxTimerCtrlState;
+
+/**
+ * struct NPCM7xxTimer - Individual timer state.
+ * @irq: GIC interrupt line to fire on expiration (if enabled).
+ * @qtimer: QEMU timer that notifies us on expiration.
+ * @expires_ns: Absolute virtual expiration time.
+ * @remaining_ns: Remaining time until expiration if timer is paused.
+ * @tcsr: The Timer Control and Status Register.
+ * @ticr: The Timer Initial Count Register.
+ */
+typedef struct NPCM7xxTimer {
+    NPCM7xxTimerCtrlState *ctrl;
+
+    qemu_irq    irq;
+    QEMUTimer   qtimer;
+    int64_t     expires_ns;
+    int64_t     remaining_ns;
+
+    uint32_t    tcsr;
+    uint32_t    ticr;
+} NPCM7xxTimer;
+
+/**
+ * struct NPCM7xxTimerCtrlState - Timer Module device state.
+ * @parent: System bus device.
+ * @iomem: Memory region through which registers are accessed.
+ * @tisr: The Timer Interrupt Status Register.
+ * @wtcr: The Watchdog Timer Control Register.
+ * @timer: The five individual timers managed by this module.
+ */
+struct NPCM7xxTimerCtrlState {
+    SysBusDevice parent;
+
+    MemoryRegion iomem;
+
+    uint32_t    tisr;
+    uint32_t    wtcr;
+
+    NPCM7xxTimer timer[NPCM7XX_TIMERS_PER_CTRL];
+};
+
+#define TYPE_NPCM7XX_TIMER "npcm7xx-timer"
+#define NPCM7XX_TIMER(obj)                                              \
+    OBJECT_CHECK(NPCM7xxTimerCtrlState, (obj), TYPE_NPCM7XX_TIMER)
+
+#endif /* NPCM7XX_TIMER_H */
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
new file mode 100644
index 0000000000..b0a72aef75
--- /dev/null
+++ b/hw/timer/npcm7xx_timer.c
@@ -0,0 +1,468 @@
+/*
+ * Nuvoton NPCM7xx Timer Controller
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/irq.h"
+#include "hw/timer/npcm7xx_timer.h"
+#include "migration/vmstate.h"
+#include "qemu/bitops.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"
+
+/* Register field definitions. */
+#define NPCM7XX_TCSR_CEN                BIT(30)
+#define NPCM7XX_TCSR_IE                 BIT(29)
+#define NPCM7XX_TCSR_PERIODIC           BIT(27)
+#define NPCM7XX_TCSR_CRST               BIT(26)
+#define NPCM7XX_TCSR_CACT               BIT(25)
+#define NPCM7XX_TCSR_RSVD               0x21ffff00
+#define NPCM7XX_TCSR_PRESCALE_START     0
+#define NPCM7XX_TCSR_PRESCALE_LEN       8
+
+/* The reference clock frequency is always 25 MHz. */
+#define NPCM7XX_TIMER_REF_HZ            (25000000)
+
+/* Return the value by which to divide the reference clock rate. */
+static uint32_t npcm7xx_timer_prescaler(const NPCM7xxTimer *t)
+{
+    return extract32(t->tcsr, NPCM7XX_TCSR_PRESCALE_START,
+                     NPCM7XX_TCSR_PRESCALE_LEN) + 1;
+}
+
+/* Convert a timer cycle count to a time interval in nanoseconds. */
+static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
+{
+    int64_t ns = count;
+
+    ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
+    ns *= npcm7xx_timer_prescaler(t);
+
+    return ns;
+}
+
+/* Convert a time interval in nanoseconds to a timer cycle count. */
+static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
+{
+    int64_t count;
+
+    count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
+    count /= npcm7xx_timer_prescaler(t);
+
+    return count;
+}
+
+/*
+ * Raise the interrupt line if there's a pending interrupt and interrupts are
+ * enabled for this timer. If not, lower it.
+ */
+static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
+{
+    NPCM7xxTimerCtrlState *tc = t->ctrl;
+    /* Find the array index of this timer. */
+    int index = t - tc->timer;
+
+    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
+
+    if ((t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index))) {
+        qemu_irq_raise(t->irq);
+        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 1);
+    } else {
+        qemu_irq_lower(t->irq);
+        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 0);
+    }
+}
+
+/* Start or resume the timer. */
+static void npcm7xx_timer_start(NPCM7xxTimer *t)
+{
+    int64_t now;
+
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    t->expires_ns = now + t->remaining_ns;
+    timer_mod(&t->qtimer, t->expires_ns);
+}
+
+/*
+ * Called when the counter reaches zero. Sets the interrupt flag, and either
+ * restarts or disables the timer.
+ */
+static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
+{
+    NPCM7xxTimerCtrlState *tc = t->ctrl;
+    int index = t - tc->timer;
+
+    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
+
+    tc->tisr |= BIT(index);
+
+    if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
+        t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
+        if (t->tcsr & NPCM7XX_TCSR_CEN) {
+            npcm7xx_timer_start(t);
+        }
+    } else {
+        t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
+    }
+
+    npcm7xx_timer_check_interrupt(t);
+}
+
+/* Stop counting. Record the time remaining so we can continue later. */
+static void npcm7xx_timer_pause(NPCM7xxTimer *t)
+{
+    int64_t now;
+
+    timer_del(&t->qtimer);
+    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+    t->remaining_ns = t->expires_ns - now;
+    if (t->remaining_ns <= 0) {
+        npcm7xx_timer_reached_zero(t);
+    }
+}
+
+/*
+ * Restart the timer from its initial value. If the timer was enabled and stays
+ * enabled, adjust the QEMU timer according to the new count. If the timer is
+ * transitioning from disabled to enabled, the caller is expected to start the
+ * timer later.
+ */
+static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
+{
+    t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
+
+    if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
+        npcm7xx_timer_start(t);
+    }
+}
+
+/* Register read and write handlers */
+
+static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
+{
+    uint32_t old_tcsr = t->tcsr;
+
+    if (new_tcsr & NPCM7XX_TCSR_RSVD) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
+                      __func__, new_tcsr);
+        new_tcsr &= ~NPCM7XX_TCSR_RSVD;
+    }
+    if (new_tcsr & NPCM7XX_TCSR_CACT) {
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
+                      __func__, new_tcsr);
+        new_tcsr &= ~NPCM7XX_TCSR_CACT;
+    }
+
+    t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
+
+    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
+        npcm7xx_timer_check_interrupt(t);
+    }
+    if (new_tcsr & NPCM7XX_TCSR_CRST) {
+        npcm7xx_timer_restart(t, old_tcsr);
+        t->tcsr &= ~NPCM7XX_TCSR_CRST;
+    }
+    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
+        if (new_tcsr & NPCM7XX_TCSR_CEN) {
+            npcm7xx_timer_start(t);
+        } else {
+            npcm7xx_timer_pause(t);
+        }
+    }
+}
+
+static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
+{
+    t->ticr = new_ticr;
+
+    npcm7xx_timer_restart(t, t->tcsr);
+}
+
+static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
+{
+    if (t->tcsr & NPCM7XX_TCSR_CEN) {
+        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+
+        return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
+    }
+
+    return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
+}
+
+static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
+{
+    NPCM7xxTimerCtrlState *s = opaque;
+    uint64_t value = 0;
+    hwaddr reg;
+
+    reg = offset / sizeof(uint32_t);
+    switch (reg) {
+    case NPCM7XX_TIMER_TCSR0:
+        value = s->timer[0].tcsr;
+        break;
+    case NPCM7XX_TIMER_TCSR1:
+        value = s->timer[1].tcsr;
+        break;
+    case NPCM7XX_TIMER_TCSR2:
+        value = s->timer[2].tcsr;
+        break;
+    case NPCM7XX_TIMER_TCSR3:
+        value = s->timer[3].tcsr;
+        break;
+    case NPCM7XX_TIMER_TCSR4:
+        value = s->timer[4].tcsr;
+        break;
+
+    case NPCM7XX_TIMER_TICR0:
+        value = s->timer[0].ticr;
+        break;
+    case NPCM7XX_TIMER_TICR1:
+        value = s->timer[1].ticr;
+        break;
+    case NPCM7XX_TIMER_TICR2:
+        value = s->timer[2].ticr;
+        break;
+    case NPCM7XX_TIMER_TICR3:
+        value = s->timer[3].ticr;
+        break;
+    case NPCM7XX_TIMER_TICR4:
+        value = s->timer[4].ticr;
+        break;
+
+    case NPCM7XX_TIMER_TDR0:
+        value = npcm7xx_timer_read_tdr(&s->timer[0]);
+        break;
+    case NPCM7XX_TIMER_TDR1:
+        value = npcm7xx_timer_read_tdr(&s->timer[1]);
+        break;
+    case NPCM7XX_TIMER_TDR2:
+        value = npcm7xx_timer_read_tdr(&s->timer[2]);
+        break;
+    case NPCM7XX_TIMER_TDR3:
+        value = npcm7xx_timer_read_tdr(&s->timer[3]);
+        break;
+    case NPCM7XX_TIMER_TDR4:
+        value = npcm7xx_timer_read_tdr(&s->timer[4]);
+        break;
+
+    case NPCM7XX_TIMER_TISR:
+        value = s->tisr;
+        break;
+
+    case NPCM7XX_TIMER_WTCR:
+        value = s->wtcr;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
+                      __func__, (unsigned int)offset);
+        break;
+    }
+
+    trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
+
+    return value;
+}
+
+static void npcm7xx_timer_write(void *opaque, hwaddr offset,
+                                uint64_t v, unsigned size)
+{
+    uint32_t reg = offset / sizeof(uint32_t);
+    NPCM7xxTimerCtrlState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
+
+    switch (reg) {
+    case NPCM7XX_TIMER_TCSR0:
+        npcm7xx_timer_write_tcsr(&s->timer[0], value);
+        return;
+    case NPCM7XX_TIMER_TCSR1:
+        npcm7xx_timer_write_tcsr(&s->timer[1], value);
+        return;
+    case NPCM7XX_TIMER_TCSR2:
+        npcm7xx_timer_write_tcsr(&s->timer[2], value);
+        return;
+    case NPCM7XX_TIMER_TCSR3:
+        npcm7xx_timer_write_tcsr(&s->timer[3], value);
+        return;
+    case NPCM7XX_TIMER_TCSR4:
+        npcm7xx_timer_write_tcsr(&s->timer[4], value);
+        return;
+
+    case NPCM7XX_TIMER_TICR0:
+        npcm7xx_timer_write_ticr(&s->timer[0], value);
+        return;
+    case NPCM7XX_TIMER_TICR1:
+        npcm7xx_timer_write_ticr(&s->timer[1], value);
+        return;
+    case NPCM7XX_TIMER_TICR2:
+        npcm7xx_timer_write_ticr(&s->timer[2], value);
+        return;
+    case NPCM7XX_TIMER_TICR3:
+        npcm7xx_timer_write_ticr(&s->timer[3], value);
+        return;
+    case NPCM7XX_TIMER_TICR4:
+        npcm7xx_timer_write_ticr(&s->timer[4], value);
+        return;
+
+    case NPCM7XX_TIMER_TDR0:
+    case NPCM7XX_TIMER_TDR1:
+    case NPCM7XX_TIMER_TDR2:
+    case NPCM7XX_TIMER_TDR3:
+    case NPCM7XX_TIMER_TDR4:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
+                      __func__, (unsigned int)offset);
+        return;
+
+    case NPCM7XX_TIMER_TISR:
+        s->tisr &= ~value;
+        return;
+
+    case NPCM7XX_TIMER_WTCR:
+        qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
+                      __func__, value);
+        return;
+    }
+
+    qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
+                  __func__, (unsigned int)offset);
+}
+
+static const struct MemoryRegionOps npcm7xx_timer_ops = {
+    .read       = npcm7xx_timer_read,
+    .write      = npcm7xx_timer_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+/* Called when the QEMU timer expires. */
+static void npcm7xx_timer_expired(void *opaque)
+{
+    NPCM7xxTimer *t = opaque;
+
+    if (t->tcsr & NPCM7XX_TCSR_CEN) {
+        npcm7xx_timer_reached_zero(t);
+    }
+}
+
+static void npcm7xx_timer_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        NPCM7xxTimer *t = &s->timer[i];
+
+        timer_del(&t->qtimer);
+        t->expires_ns = 0;
+        t->remaining_ns = 0;
+        t->tcsr = 0x00000005;
+        t->ticr = 0x00000000;
+    }
+
+    s->tisr = 0x00000000;
+    s->wtcr = 0x00000400;
+}
+
+static void npcm7xx_timer_hold_reset(Object *obj)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        qemu_irq_lower(s->timer[i].irq);
+    }
+}
+
+static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
+    SysBusDevice *sbd = &s->parent;
+    int i;
+
+    for (i = 0; i < NPCM7XX_TIMERS_PER_CTRL; i++) {
+        NPCM7xxTimer *t = &s->timer[i];
+        t->ctrl = s;
+        timer_init_ns(&t->qtimer, QEMU_CLOCK_VIRTUAL, npcm7xx_timer_expired, t);
+        sysbus_init_irq(sbd, &t->irq);
+    }
+
+    memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s,
+                          TYPE_NPCM7XX_TIMER, 4 * KiB);
+    sysbus_init_mmio(sbd, &s->iomem);
+}
+
+static const VMStateDescription vmstate_npcm7xx_timer = {
+    .name = "npcm7xx-timer",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_TIMER(qtimer, NPCM7xxTimer),
+        VMSTATE_INT64(expires_ns, NPCM7xxTimer),
+        VMSTATE_INT64(remaining_ns, NPCM7xxTimer),
+        VMSTATE_UINT32(tcsr, NPCM7xxTimer),
+        VMSTATE_UINT32(ticr, NPCM7xxTimer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
+    .name = "npcm7xx-timer-ctrl",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+        VMSTATE_UINT32(wtcr, NPCM7xxTimerCtrlState),
+        VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
+                             NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
+                             NPCM7xxTimer),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_timer_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx Timer Controller";
+    dc->realize = npcm7xx_timer_realize;
+    dc->vmsd = &vmstate_npcm7xx_timer_ctrl;
+    rc->phases.enter = npcm7xx_timer_enter_reset;
+    rc->phases.hold = npcm7xx_timer_hold_reset;
+}
+
+static const TypeInfo npcm7xx_timer_info = {
+    .name               = TYPE_NPCM7XX_TIMER,
+    .parent             = TYPE_SYS_BUS_DEVICE,
+    .instance_size      = sizeof(NPCM7xxTimerCtrlState),
+    .class_init         = npcm7xx_timer_class_init,
+};
+
+static void npcm7xx_timer_register_type(void)
+{
+    type_register_static(&npcm7xx_timer_info);
+}
+type_init(npcm7xx_timer_register_type);
diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
index a39f6ec0c2..84c484dda8 100644
--- a/hw/timer/Makefile.objs
+++ b/hw/timer/Makefile.objs
@@ -14,6 +14,7 @@ common-obj-$(CONFIG_IMX) += imx_epit.o
 common-obj-$(CONFIG_IMX) += imx_gpt.o
 common-obj-$(CONFIG_LM32) += lm32_timer.o
 common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
+common-obj-$(CONFIG_NPCM7XX) += npcm7xx_timer.o
 common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o
 
 common-obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
diff --git a/hw/timer/trace-events b/hw/timer/trace-events
index 866c9f546a..e2434ecf3f 100644
--- a/hw/timer/trace-events
+++ b/hw/timer/trace-events
@@ -66,6 +66,11 @@ cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK A
 cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u"
 cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset"
 
+# npcm7xx_timer.c
+npcm7xx_timer_read(const char *id, uint64_t offset, uint64_t value) " %s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
+npcm7xx_timer_write(const char *id, uint64_t offset, uint64_t value) "%s offset: 0x%04" PRIx64 " value 0x%08" PRIx64
+npcm7xx_timer_irq(const char *id, int timer, int state) "%s timer %d state %d"
+
 # nrf51_timer.c
 nrf51_timer_read(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
 nrf51_timer_write(uint8_t timer_id, uint64_t addr, uint32_t value, unsigned size) "timer %u write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u"
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (2 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-09  6:11   ` Philippe Mathieu-Daudé
  2020-07-13 15:02   ` Cédric Le Goater
  2020-07-09  0:36 ` [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen
                   ` (6 subsequent siblings)
  10 siblings, 2 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley

The Nuvoton NPCM7xx SoC family are used to implement Baseboard
Management Controllers in servers. While the family includes four SoCs,
this patch implements limited support for two of them: NPCM730 (targeted
for Data Center applications) and NPCM750 (targeted for Enterprise
applications).

This patch includes little more than the bare minimum needed to boot a
Linux kernel built with NPCM7xx support in direct-kernel mode:

  - Two Cortex-A9 CPU cores with built-in periperhals.
  - Global Configuration Registers.
  - Clock Management.
  - 3 Timer Modules with 5 timers each.
  - 4 serial ports.

The chips themselves have a lot more features, some of which will be
added to the model at a later stage.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h |  86 +++++++++
 hw/arm/npcm7xx.c         | 376 +++++++++++++++++++++++++++++++++++++++
 hw/arm/Kconfig           |   5 +
 hw/arm/Makefile.objs     |   1 +
 4 files changed, 468 insertions(+)
 create mode 100644 include/hw/arm/npcm7xx.h
 create mode 100644 hw/arm/npcm7xx.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
new file mode 100644
index 0000000000..95d9224f59
--- /dev/null
+++ b/include/hw/arm/npcm7xx.h
@@ -0,0 +1,86 @@
+/*
+ * Nuvoton NPCM7xx SoC family.
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_H
+#define NPCM7XX_H
+
+#include "hw/boards.h"
+#include "hw/cpu/a9mpcore.h"
+#include "hw/misc/npcm7xx_clk.h"
+#include "hw/misc/npcm7xx_gcr.h"
+#include "hw/timer/npcm7xx_timer.h"
+#include "target/arm/cpu.h"
+
+#define NPCM7XX_MAX_NUM_CPUS    (2)
+
+/* The first half of the address space is reserved for DDR4 DRAM. */
+#define NPCM7XX_DRAM_BA         (0x00000000)
+#define NPCM7XX_DRAM_SZ         (2 * GiB)
+
+/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
+#define NPCM7XX_LOADER_START            (0x00000000)  /* Start of SDRAM */
+#define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
+#define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
+#define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
+
+typedef struct NPCM7xxState {
+    DeviceState         parent;
+
+    ARMCPU              cpu[NPCM7XX_MAX_NUM_CPUS];
+    A9MPPrivState       a9mpcore;
+
+    MemoryRegion        sram;
+    MemoryRegion        irom;
+    MemoryRegion        ram3;
+    MemoryRegion        *dram;
+
+    NPCM7xxGCRState     gcr;
+    NPCM7xxCLKState     clk;
+    NPCM7xxTimerCtrlState tim[3];
+} NPCM7xxState;
+
+#define TYPE_NPCM7XX    "npcm7xx"
+#define NPCM7XX(obj)    OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
+
+#define TYPE_NPCM730    "npcm730"
+#define TYPE_NPCM750    "npcm750"
+
+typedef struct NPCM7xxClass {
+    DeviceClass         parent;
+
+    /* Bitmask of modules that are permanently disabled on this chip. */
+    uint32_t            disabled_modules;
+    /* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
+    uint32_t            num_cpus;
+} NPCM7xxClass;
+
+#define NPCM7XX_CLASS(klass)                                            \
+    OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
+#define NPCM7XX_GET_CLASS(obj)                                          \
+    OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
+
+/**
+ * npcm7xx_write_secondary_boot - Write stub for booting secondary CPU.
+ * @cpu: The CPU to be booted.
+ * @info: Boot info structure for the board.
+ *
+ * This will write a short code stub to the internal ROM that will keep the
+ * secondary CPU spinning until the primary CPU writes an address to the SCRPAD
+ * register in the GCR, after which the secondary CPU will jump there.
+ */
+extern void npcm7xx_write_secondary_boot(ARMCPU *cpu,
+                                         const struct arm_boot_info *info);
+
+#endif /* NPCM7XX_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
new file mode 100644
index 0000000000..af45f3c716
--- /dev/null
+++ b/hw/arm/npcm7xx.c
@@ -0,0 +1,376 @@
+/*
+ * Nuvoton NPCM7xx SoC family.
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 "exec/address-spaces.h"
+#include "hw/arm/npcm7xx.h"
+#include "hw/char/serial.h"
+#include "hw/loader.h"
+#include "hw/misc/unimp.h"
+#include "hw/qdev-properties.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+#include "sysemu/sysemu.h"
+
+/*
+ * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
+ * that aren't handled by any device.
+ */
+#define NPCM7XX_MMIO_BA         (0x80000000)
+#define NPCM7XX_MMIO_SZ         (0x7ffd0000)
+
+/* Core system modules. */
+#define NPCM7XX_L2C_BA          (0xf03fc000)
+#define NPCM7XX_CPUP_BA         (0xf03fe000)
+#define NPCM7XX_GCR_BA          (0xf0800000)
+#define NPCM7XX_CLK_BA          (0xf0801000)
+
+/* Internal AHB SRAM */
+#define NPCM7XX_RAM3_BA         (0xc0008000)
+#define NPCM7XX_RAM3_SZ         (4 * KiB)
+
+/* Memory blocks at the end of the address space */
+#define NPCM7XX_RAM2_BA         (0xfffd0000)
+#define NPCM7XX_RAM2_SZ         (128 * KiB)
+#define NPCM7XX_ROM_BA          (0xffff0000)
+#define NPCM7XX_ROM_SZ          (64 * KiB)
+
+/*
+ * Interrupt lines going into the GIC. This does not include internal Cortex-A9
+ * interrupts.
+ */
+enum NPCM7xxInterrupt {
+    NPCM7XX_UART0_IRQ           = 2,
+    NPCM7XX_UART1_IRQ,
+    NPCM7XX_UART2_IRQ,
+    NPCM7XX_UART3_IRQ,
+    NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
+    NPCM7XX_TIMER1_IRQ,
+    NPCM7XX_TIMER2_IRQ,
+    NPCM7XX_TIMER3_IRQ,
+    NPCM7XX_TIMER4_IRQ,
+    NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
+    NPCM7XX_TIMER6_IRQ,
+    NPCM7XX_TIMER7_IRQ,
+    NPCM7XX_TIMER8_IRQ,
+    NPCM7XX_TIMER9_IRQ,
+    NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
+    NPCM7XX_TIMER11_IRQ,
+    NPCM7XX_TIMER12_IRQ,
+    NPCM7XX_TIMER13_IRQ,
+    NPCM7XX_TIMER14_IRQ,
+};
+
+/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
+#define NPCM7XX_NUM_IRQ         (160)
+
+/* Register base address for each Timer Module */
+static const hwaddr npcm7xx_tim_addr[] = {
+    0xf0008000,
+    0xf0009000,
+    0xf000a000,
+};
+
+/* Register base address for each 16550 UART */
+static const hwaddr npcm7xx_uart_addr[] = {
+    0xf0001000,
+    0xf0002000,
+    0xf0003000,
+    0xf0004000,
+};
+
+void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
+{
+    /*
+     * The default smpboot stub halts the secondary CPU with a 'wfi'
+     * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
+     * does not send an IPI to wake it up, so the second CPU fails to boot. So
+     * we need to provide our own smpboot stub that can not use 'wfi', it has
+     * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
+     */
+    uint32_t smpboot[] = {
+        0xe59f2018,     /* ldr r2, bootreg_addr */
+        0xe3a00000,     /* mov r0, #0 */
+        0xe5820000,     /* str r0, [r2] */
+        0xe320f002,     /* wfe */
+        0xe5921000,     /* ldr r1, [r2] */
+        0xe1110001,     /* tst r1, r1 */
+        0x0afffffb,     /* beq <wfe> */
+        0xe12fff11,     /* bx r1 */
+        NPCM7XX_SMP_BOOTREG_ADDR,
+    };
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
+        smpboot[i] = tswap32(smpboot[i]);
+    }
+
+    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
+                       NPCM7XX_SMP_LOADER_START);
+}
+
+static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
+{
+    return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
+}
+
+static void npcm7xx_init(Object *obj)
+{
+    NPCM7xxState *s = NPCM7XX(obj);
+    int i;
+
+    for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
+        object_initialize_child(obj, "cpu[*]", &s->cpu[i],
+                                ARM_CPU_TYPE_NAME("cortex-a9"));
+    }
+
+    object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
+    object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
+    object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
+                              "power-on-straps");
+    object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
+
+    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
+        object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
+    }
+}
+
+static void npcm7xx_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxState *s = NPCM7XX(dev);
+    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
+    int i;
+
+    /* CPUs */
+    for (i = 0; i < nc->num_cpus; i++) {
+        object_property_set_int(OBJECT(&s->cpu[i]),
+                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
+                                "mp-affinity", &error_abort);
+        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
+                                "reset-cbar", &error_abort);
+        object_property_set_bool(OBJECT(&s->cpu[i]), true,
+                                 "reset-hivecs", &error_abort);
+
+        /* Disable security extensions. */
+        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
+                                 &error_abort);
+
+        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
+    }
+
+    /* A9MPCORE peripherals */
+    object_property_set_int(OBJECT(&s->a9mpcore), nc->num_cpus, "num-cpu",
+                            &error_abort);
+    object_property_set_int(OBJECT(&s->a9mpcore), NPCM7XX_NUM_IRQ, "num-irq",
+                            &error_abort);
+    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
+
+    for (i = 0; i < nc->num_cpus; i++) {
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
+                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
+    }
+
+    /* L2 cache controller */
+    sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
+
+    /* System Global Control Registers (GCR) */
+    object_property_set_int(OBJECT(&s->gcr), nc->disabled_modules,
+                            "disabled-modules", &error_abort);
+    object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
+    sysbus_realize(SYS_BUS_DEVICE(&s->gcr), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
+
+    /* Clock Control Registers (CLK) */
+    sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
+
+    /* Timer Modules (TIM) */
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
+    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
+        int first_irq;
+        int j;
+
+        sysbus_realize(sbd, &error_abort);
+        sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
+
+        first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
+        for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
+            qemu_irq irq = npcm7xx_irq(s, first_irq + j);
+            sysbus_connect_irq(sbd, j, irq);
+        }
+    }
+
+    /* UART0..3 (16550 compatible) */
+    for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
+        serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
+                       npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
+                       serial_hd(i), DEVICE_LITTLE_ENDIAN);
+    }
+
+    /* RAM2 (SRAM) */
+    memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
+                           NPCM7XX_RAM2_SZ, &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
+
+    /* RAM3 (SRAM) */
+    memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
+                           NPCM7XX_RAM3_SZ, &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
+
+    /* Internal ROM */
+    memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
+                           &error_abort);
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
+
+    create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.rng",          0xf000b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.adc",          0xf000c000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[0]",      0xf0010000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[1]",      0xf0011000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[2]",      0xf0012000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[3]",      0xf0013000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[4]",      0xf0014000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[5]",      0xf0015000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[6]",      0xf0016000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.gpio[7]",      0xf0017000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[0]",     0xf0080000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[1]",     0xf0081000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[2]",     0xf0082000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[3]",     0xf0083000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[4]",     0xf0084000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[5]",     0xf0085000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[6]",     0xf0086000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[7]",     0xf0087000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[8]",     0xf0088000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[9]",     0xf0089000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[10]",    0xf008a000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[11]",    0xf008b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[12]",    0xf008c000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[13]",    0xf008d000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[14]",    0xf008e000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.smbus[15]",    0xf008f000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pwm[0]",       0xf0103000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pwm[1]",       0xf0104000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
+    create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.ehci",         0xf0806000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.ohci",         0xf0807000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
+    create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.emc1",         0xf0825000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.emc2",         0xf0826000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
+    create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
+    create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
+    create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
+    create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
+    create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);
+}
+
+static Property npcm7xx_properties[] = {
+    DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
+                     MemoryRegion *),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_class_init(ObjectClass *oc, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(oc);
+
+    dc->realize = npcm7xx_realize;
+    dc->user_creatable = false;
+    device_class_set_props(dc, npcm7xx_properties);
+}
+
+static void npcm730_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
+
+    /* NPCM730 is optimized for data center use, so no graphics, etc. */
+    nc->disabled_modules = 0x00300395;
+    nc->num_cpus = 2;
+}
+
+static void npcm750_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
+
+    /* NPCM750 has 2 cores and a full set of peripherals */
+    nc->disabled_modules = 0x00000000;
+    nc->num_cpus = 2;
+}
+
+static const TypeInfo npcm7xx_soc_types[] = {
+    {
+        .name           = TYPE_NPCM7XX,
+        .parent         = TYPE_DEVICE,
+        .instance_size  = sizeof(NPCM7xxState),
+        .instance_init  = npcm7xx_init,
+        .class_size     = sizeof(NPCM7xxClass),
+        .class_init     = npcm7xx_class_init,
+        .abstract       = true,
+    }, {
+        .name           = TYPE_NPCM730,
+        .parent         = TYPE_NPCM7XX,
+        .class_init     = npcm730_class_init,
+    }, {
+        .name           = TYPE_NPCM750,
+        .parent         = TYPE_NPCM7XX,
+        .class_init     = npcm750_class_init,
+    },
+};
+
+DEFINE_TYPES(npcm7xx_soc_types);
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index 192a8dec3b..a31d0d282f 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -356,6 +356,11 @@ config XLNX_VERSAL
 
 config NPCM7XX
     bool
+    select A9MPCORE
+    select ARM_GIC
+    select PL310  # cache controller
+    select SERIAL
+    select UNIMP
 
 config FSL_IMX25
     bool
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 534a6a119e..13d163a599 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -41,6 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
 obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
 obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
+obj-$(CONFIG_NPCM7XX) += npcm7xx.o
 obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
 obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (3 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-09  5:57   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Joel Stanley, Cédric Le Goater

This adds two new machines, both supported by OpenBMC:

  - npcm750-evb: Nuvoton NPCM750 Evaluation Board.
  - quanta-gsj: A board with a NPCM730 chip.

They rely on the NPCM7xx SoC device to do the heavy lifting. They are
almost completely identical at the moment, apart from the SoC type,
which currently only changes the reset contents of one register
(GCR.MDLR), but they might grow apart a bit more as more functionality
is added.

Both machines can boot the Linux kernel into /bin/sh.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 default-configs/arm-softmmu.mak |   1 +
 include/hw/arm/npcm7xx.h        |  19 ++++
 hw/arm/npcm7xx_boards.c         | 156 ++++++++++++++++++++++++++++++++
 hw/arm/Makefile.objs            |   2 +-
 4 files changed, 177 insertions(+), 1 deletion(-)
 create mode 100644 hw/arm/npcm7xx_boards.c

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 8fc09a4a51..9a94ebd0be 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
 CONFIG_SPITZ=y
 CONFIG_TOSA=y
 CONFIG_Z2=y
+CONFIG_NPCM7XX=y
 CONFIG_COLLIE=y
 CONFIG_ASPEED_SOC=y
 CONFIG_NETDUINO2=y
diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 95d9224f59..b8b76bc07b 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -35,6 +35,25 @@
 #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
 #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
 
+typedef struct NPCM7xxMachine {
+    MachineState        parent;
+} NPCM7xxMachine;
+
+#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
+#define NPCM7XX_MACHINE(obj)                                            \
+    OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE)
+
+typedef struct NPCM7xxMachineClass {
+    MachineClass        parent;
+
+    const char          *soc_type;
+} NPCM7xxMachineClass;
+
+#define NPCM7XX_MACHINE_CLASS(klass)                                    \
+    OBJECT_CLASS_CHECK(NPCM7xxMachineClass, (klass), TYPE_NPCM7XX_MACHINE)
+#define NPCM7XX_MACHINE_GET_CLASS(obj)                                  \
+    OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE)
+
 typedef struct NPCM7xxState {
     DeviceState         parent;
 
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
new file mode 100644
index 0000000000..d78d9f991b
--- /dev/null
+++ b/hw/arm/npcm7xx_boards.c
@@ -0,0 +1,156 @@
+/*
+ * Machine definitions for boards featuring an NPCM7xx SoC.
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/arm/boot.h"
+#include "hw/arm/npcm7xx.h"
+#include "hw/core/cpu.h"
+#include "qapi/error.h"
+#include "qemu/units.h"
+
+#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
+#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
+
+static struct arm_boot_info npcm7xx_binfo = {
+    .loader_start       = NPCM7XX_LOADER_START,
+    .smp_loader_start   = NPCM7XX_SMP_LOADER_START,
+    .smp_bootreg_addr   = NPCM7XX_SMP_BOOTREG_ADDR,
+    .gic_cpu_if_addr    = NPCM7XX_GIC_CPU_IF_ADDR,
+    .write_secondary_boot = npcm7xx_write_secondary_boot,
+    .board_id           = -1,
+};
+
+static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
+{
+    NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
+
+    npcm7xx_binfo.ram_size = machine->ram_size;
+    npcm7xx_binfo.nb_cpus = sc->num_cpus;
+
+    arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
+}
+
+static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
+{
+    memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
+
+    object_property_set_link(OBJECT(soc), OBJECT(dram), "dram-mr",
+                             &error_abort);
+}
+
+static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
+                                        uint32_t hw_straps)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
+    Object *obj;
+
+    obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
+                                &error_abort, NULL);
+    object_property_set_uint(obj, hw_straps, "power-on-straps", &error_abort);
+
+    return NPCM7XX(obj);
+}
+
+static void npcm750_evb_init(MachineState *machine)
+{
+    NPCM7xxState *soc;
+
+    soc = npcm7xx_create_soc(machine, NPCM750_EVB_POWER_ON_STRAPS);
+    npcm7xx_connect_dram(soc, machine->ram);
+    qdev_realize(DEVICE(soc), NULL, &error_abort);
+
+    npcm7xx_load_kernel(machine, soc);
+}
+
+static void quanta_gsj_init(MachineState *machine)
+{
+    NPCM7xxState *soc;
+
+    soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
+    npcm7xx_connect_dram(soc, machine->ram);
+    qdev_realize(DEVICE(soc), NULL, &error_abort);
+
+    npcm7xx_load_kernel(machine, soc);
+}
+
+static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type)
+{
+    NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type));
+    MachineClass *mc = MACHINE_CLASS(nmc);
+
+    nmc->soc_type = type;
+    mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
+}
+
+static void npcm7xx_machine_class_init(ObjectClass *oc, void *data)
+{
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    mc->no_floppy       = 1;
+    mc->no_cdrom        = 1;
+    mc->no_parallel     = 1;
+    mc->default_ram_id  = "ram";
+}
+
+/*
+ * Schematics:
+ * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf
+ */
+static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    npcm7xx_set_soc_type(nmc, TYPE_NPCM750);
+
+    mc->desc            = "Nuvoton NPCM750 Evaluation Board (Cortex A9)";
+    mc->init            = npcm750_evb_init;
+    mc->default_ram_size = 512 * MiB;
+};
+
+static void gsj_machine_class_init(ObjectClass *oc, void *data)
+{
+    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
+    MachineClass *mc = MACHINE_CLASS(oc);
+
+    npcm7xx_set_soc_type(nmc, TYPE_NPCM730);
+
+    mc->desc            = "Quanta GSJ (Cortex A9)";
+    mc->init            = quanta_gsj_init;
+    mc->default_ram_size = 512 * MiB;
+};
+
+static const TypeInfo npcm7xx_machine_types[] = {
+    {
+        .name           = TYPE_NPCM7XX_MACHINE,
+        .parent         = TYPE_MACHINE,
+        .instance_size  = sizeof(NPCM7xxMachine),
+        .class_size     = sizeof(NPCM7xxMachineClass),
+        .class_init     = npcm7xx_machine_class_init,
+        .abstract       = true,
+    }, {
+        .name           = MACHINE_TYPE_NAME("npcm750-evb"),
+        .parent         = TYPE_NPCM7XX_MACHINE,
+        .class_init     = npcm750_evb_machine_class_init,
+    }, {
+        .name           = MACHINE_TYPE_NAME("quanta-gsj"),
+        .parent         = TYPE_NPCM7XX_MACHINE,
+        .class_init     = gsj_machine_class_init,
+    },
+};
+
+DEFINE_TYPES(npcm7xx_machine_types)
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 13d163a599..c333548ce1 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -41,7 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
 obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
 obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
 obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
-obj-$(CONFIG_NPCM7XX) += npcm7xx.o
+obj-$(CONFIG_NPCM7XX) += npcm7xx.o npcm7xx_boards.o
 obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
 obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
 obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (4 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-13 17:50   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 07/11] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen
                   ` (4 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

If a -bios option is specified on the command line, load the image into
the internal ROM memory region, which contains the first instructions
run by the CPU after reset.

A minimal Apache-2.0-licensed boot ROM can be found at

https://github.com/google/vbootrom

It is by no means feature complete, but it is enough to launch the
Nuvoton bootblock[1] from offset 0 in the flash, which in turn will
launch u-boot and finally the Linux kernel.

[1] https://github.com/Nuvoton-Israel/bootblock

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 hw/arm/npcm7xx_boards.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index d78d9f991b..80cf1535f1 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -19,8 +19,11 @@
 #include "hw/arm/boot.h"
 #include "hw/arm/npcm7xx.h"
 #include "hw/core/cpu.h"
+#include "hw/loader.h"
 #include "qapi/error.h"
+#include "qemu-common.h"
 #include "qemu/units.h"
+#include "sysemu/sysemu.h"
 
 #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
 #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
@@ -34,6 +37,25 @@ static struct arm_boot_info npcm7xx_binfo = {
     .board_id           = -1,
 };
 
+static void npcm7xx_load_bootrom(NPCM7xxState *soc)
+{
+    if (bios_name) {
+        g_autofree char *filename = NULL;
+        int ret;
+
+        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
+        if (!filename) {
+            error_report("Could not find ROM image '%s'", bios_name);
+            exit(1);
+        }
+        ret = load_image_mr(filename, &soc->irom);
+        if (ret < 0) {
+            error_report("Failed to load ROM image '%s'", filename);
+            exit(1);
+        }
+    }
+}
+
 static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
 {
     NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
@@ -73,6 +95,7 @@ static void npcm750_evb_init(MachineState *machine)
     npcm7xx_connect_dram(soc, machine->ram);
     qdev_realize(DEVICE(soc), NULL, &error_abort);
 
+    npcm7xx_load_bootrom(soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
@@ -84,6 +107,7 @@ static void quanta_gsj_init(MachineState *machine)
     npcm7xx_connect_dram(soc, machine->ram);
     qdev_realize(DEVICE(soc), NULL, &error_abort);
 
+    npcm7xx_load_bootrom(soc);
     npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 07/11] hw/nvram: NPCM7xx OTP device model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (5 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-09  0:36 ` [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Avi Fishman

This supports reading and writing OTP fuses and keys. Only fuse reading
has been tested. Protection is not implemented.

Reviewed-by: Avi Fishman <avi.fishman@nuvoton.com>
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h       |   3 +
 include/hw/nvram/npcm7xx_otp.h |  88 +++++++
 hw/arm/npcm7xx.c               |  29 +++
 hw/nvram/npcm7xx_otp.c         | 424 +++++++++++++++++++++++++++++++++
 hw/nvram/Makefile.objs         |   1 +
 5 files changed, 545 insertions(+)
 create mode 100644 include/hw/nvram/npcm7xx_otp.h
 create mode 100644 hw/nvram/npcm7xx_otp.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index b8b76bc07b..0fd4ae4133 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -20,6 +20,7 @@
 #include "hw/cpu/a9mpcore.h"
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/misc/npcm7xx_gcr.h"
+#include "hw/nvram/npcm7xx_otp.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "target/arm/cpu.h"
 
@@ -68,6 +69,8 @@ typedef struct NPCM7xxState {
     NPCM7xxGCRState     gcr;
     NPCM7xxCLKState     clk;
     NPCM7xxTimerCtrlState tim[3];
+    NPCM7xxOTPState     key_storage;
+    NPCM7xxOTPState     fuse_array;
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h
new file mode 100644
index 0000000000..c4c1b751d4
--- /dev/null
+++ b/include/hw/nvram/npcm7xx_otp.h
@@ -0,0 +1,88 @@
+/*
+ * Nuvoton NPCM7xx OTP (Fuse Array) Interface
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_OTP_H
+#define NPCM7XX_OTP_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/* Each OTP module holds 8192 bits of one-time programmable storage */
+#define NPCM7XX_OTP_ARRAY_BITS (8192)
+#define NPCM7XX_OTP_ARRAY_BYTES (NPCM7XX_OTP_ARRAY_BITS / BITS_PER_BYTE)
+
+/* Fuse array offsets */
+#define NPCM7XX_FUSE_FUSTRAP (0)
+#define NPCM7XX_FUSE_CP_FUSTRAP (12)
+#define NPCM7XX_FUSE_DAC_CALIB (16)
+#define NPCM7XX_FUSE_ADC_CALIB (24)
+#define NPCM7XX_FUSE_DERIVATIVE (64)
+#define NPCM7XX_FUSE_TEST_SIG (72)
+#define NPCM7XX_FUSE_DIE_LOCATION (74)
+#define NPCM7XX_FUSE_GP1 (80)
+#define NPCM7XX_FUSE_GP2 (128)
+
+/**
+ * enum NPCM7xxOTPRegister - 32-bit register indices.
+ */
+typedef enum NPCM7xxOTPRegister {
+    NPCM7XX_OTP_FST,
+    NPCM7XX_OTP_FADDR,
+    NPCM7XX_OTP_FDATA,
+    NPCM7XX_OTP_FCFG,
+    /* Offset 0x10 is FKEYIND in OTP1, FUSTRAP in OTP2 */
+    NPCM7XX_OTP_FKEYIND = 0x0010 / sizeof(uint32_t),
+    NPCM7XX_OTP_FUSTRAP = 0x0010 / sizeof(uint32_t),
+    NPCM7XX_OTP_FCTL,
+    NPCM7XX_OTP_NR_REGS,
+} NPCM7xxOTPRegister;
+
+/**
+ * struct NPCM7xxOTPState - Device state for one OTP module.
+ * @parent: System bus device.
+ * @mmio: Memory region through which registers are accessed.
+ * @regs: Register contents.
+ * @array: OTP storage array.
+ */
+typedef struct NPCM7xxOTPState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+    uint32_t regs[NPCM7XX_OTP_NR_REGS];
+    uint8_t array[NPCM7XX_OTP_ARRAY_BYTES];
+} NPCM7xxOTPState;
+
+#define TYPE_NPCM7XX_OTP "npcm7xx-otp"
+#define NPCM7XX_OTP(obj) OBJECT_CHECK(NPCM7xxOTPState, (obj), TYPE_NPCM7XX_OTP)
+
+#define TYPE_NPCM7XX_KEY_STORAGE "npcm7xx-key-storage"
+#define TYPE_NPCM7XX_FUSE_ARRAY "npcm7xx-fuse-array"
+
+typedef struct NPCM7xxOTPClass NPCM7xxOTPClass;
+
+/**
+ * npcm7xx_otp_array_write - ECC encode and write data to OTP array.
+ * @s: OTP module.
+ * @data: Data to be encoded and written.
+ * @offset: Offset of first byte to be written in the OTP array.
+ * @len: Number of bytes before ECC encoding.
+ *
+ * Each nibble of data is encoded into a byte, so the number of bytes written
+ * to the array will be @len * 2.
+ */
+extern void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
+                                    unsigned int offset, unsigned int len);
+
+#endif /* NPCM7XX_OTP_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index af45f3c716..70eaa2347f 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -33,6 +33,10 @@
 #define NPCM7XX_MMIO_BA         (0x80000000)
 #define NPCM7XX_MMIO_SZ         (0x7ffd0000)
 
+/* OTP key storage and fuse strap array */
+#define NPCM7XX_OTP1_BA         (0xf0189000)
+#define NPCM7XX_OTP2_BA         (0xf018a000)
+
 /* Core system modules. */
 #define NPCM7XX_L2C_BA          (0xf03fc000)
 #define NPCM7XX_CPUP_BA         (0xf03fe000)
@@ -123,6 +127,20 @@ void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
                        NPCM7XX_SMP_LOADER_START);
 }
 
+static void npcm7xx_init_fuses(NPCM7xxState *s)
+{
+    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
+    uint32_t value;
+
+    /*
+     * The initial mask of disabled modules indicates the chip derivative (e.g.
+     * NPCM750 or NPCM730).
+     */
+    value = tswap32(nc->disabled_modules);
+    npcm7xx_otp_array_write(&s->fuse_array, &value, NPCM7XX_FUSE_DERIVATIVE,
+                            sizeof(value));
+}
+
 static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
 {
     return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
@@ -143,6 +161,10 @@ static void npcm7xx_init(Object *obj)
     object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
                               "power-on-straps");
     object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
+    object_initialize_child(obj, "otp1", &s->key_storage,
+                            TYPE_NPCM7XX_KEY_STORAGE);
+    object_initialize_child(obj, "otp2", &s->fuse_array,
+                            TYPE_NPCM7XX_FUSE_ARRAY);
 
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
@@ -201,6 +223,13 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
     sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
 
+    /* OTP key storage and fuse strap array */
+    sysbus_realize(SYS_BUS_DEVICE(&s->key_storage), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->key_storage), 0, NPCM7XX_OTP1_BA);
+    sysbus_realize(SYS_BUS_DEVICE(&s->fuse_array), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
+    npcm7xx_init_fuses(s);
+
     /* Timer Modules (TIM) */
     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
new file mode 100644
index 0000000000..b67d69fdf9
--- /dev/null
+++ b/hw/nvram/npcm7xx_otp.c
@@ -0,0 +1,424 @@
+/*
+ * Nuvoton NPCM7xx OTP (Fuse Array) Interface
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/nvram/npcm7xx_otp.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+/* Each module has 4 KiB of register space. Only a fraction of it is used. */
+#define NPCM7XX_OTP_REGS_SIZE (4 * KiB)
+
+/* Register field definitions. */
+#define FST_RIEN BIT(2)
+#define FST_RDST BIT(1)
+#define FST_RDY BIT(0)
+#define FST_RO_MASK (FST_RDST | FST_RDY)
+
+#define FADDR_BYTEADDR(rv) extract32((rv), 0, 10)
+#define FADDR_BITPOS(rv) extract32((rv), 10, 3)
+
+#define FDATA_CLEAR 0x00000001
+
+#define FCFG_FDIS BIT(31)
+#define FCFG_FCFGLK_MASK 0x00ff0000
+
+#define FCTL_PROG_CMD1 0x00000001
+#define FCTL_PROG_CMD2 0xbf79e5d0
+#define FCTL_READ_CMD 0x00000002
+
+/**
+ * struct NPCM7xxOTPClass - OTP module class.
+ * @parent: System bus device class.
+ * @mmio_ops: MMIO register operations for this type of module.
+ *
+ * The two OTP modules (key-storage and fuse-array) have slightly different
+ * behavior, so we give them different MMIO register operations.
+ */
+struct NPCM7xxOTPClass {
+    SysBusDeviceClass parent;
+
+    const MemoryRegionOps *mmio_ops;
+};
+
+#define NPCM7XX_OTP_CLASS(klass) \
+    OBJECT_CLASS_CHECK(NPCM7xxOTPClass, (klass), TYPE_NPCM7XX_OTP)
+#define NPCM7XX_OTP_GET_CLASS(obj) \
+    OBJECT_GET_CLASS(NPCM7xxOTPClass, (obj), TYPE_NPCM7XX_OTP)
+
+static uint8_t ecc_encode_nibble(uint8_t n)
+{
+    uint8_t result = n;
+
+    result |= (((n >> 0) & 1) ^ ((n >> 1) & 1)) << 4;
+    result |= (((n >> 2) & 1) ^ ((n >> 3) & 1)) << 5;
+    result |= (((n >> 0) & 1) ^ ((n >> 2) & 1)) << 6;
+    result |= (((n >> 1) & 1) ^ ((n >> 3) & 1)) << 7;
+
+    return result;
+}
+
+void npcm7xx_otp_array_write(NPCM7xxOTPState *s, const void *data,
+                             unsigned int offset, unsigned int len)
+{
+    const uint8_t *src = data;
+    uint8_t *dst = &s->array[offset];
+
+    while (len-- > 0) {
+        uint8_t c = *src++;
+
+        *dst++ = ecc_encode_nibble(extract8(c, 0, 4));
+        *dst++ = ecc_encode_nibble(extract8(c, 4, 4));
+    }
+}
+
+/* Common register read handler for both OTP classes. */
+static uint64_t npcm7xx_otp_read(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg)
+{
+    uint32_t value = 0;
+
+    switch (reg) {
+    case NPCM7XX_OTP_FST:
+    case NPCM7XX_OTP_FADDR:
+    case NPCM7XX_OTP_FDATA:
+    case NPCM7XX_OTP_FCFG:
+        value = s->regs[reg];
+        break;
+
+    case NPCM7XX_OTP_FCTL:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: read from write-only FCTL register\n",
+                      DEVICE(s)->canonical_path);
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: read from invalid offset 0x%zx\n",
+                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
+        break;
+    }
+
+    return value;
+}
+
+/* Read a byte from the OTP array into the data register. */
+static void npcm7xx_otp_read_array(NPCM7xxOTPState *s)
+{
+    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
+
+    s->regs[NPCM7XX_OTP_FDATA] = s->array[FADDR_BYTEADDR(faddr)];
+    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
+}
+
+/* Program a byte from the data register into the OTP array. */
+static void npcm7xx_otp_program_array(NPCM7xxOTPState *s)
+{
+    uint32_t faddr = s->regs[NPCM7XX_OTP_FADDR];
+
+    /* Bits can only go 0->1, never 1->0. */
+    s->array[FADDR_BYTEADDR(faddr)] |= (1U << FADDR_BITPOS(faddr));
+    s->regs[NPCM7XX_OTP_FST] |= FST_RDST | FST_RDY;
+}
+
+/* Compute the next value of the FCFG register. */
+static uint32_t npcm7xx_otp_compute_fcfg(uint32_t cur_value, uint32_t new_value)
+{
+    uint32_t lock_mask;
+    uint32_t value;
+
+    /*
+     * FCFGLK holds sticky bits 16..23, indicating which bits in FPRGLK (8..15)
+     * and FRDLK (0..7) that are read-only.
+     */
+    lock_mask = (cur_value & FCFG_FCFGLK_MASK) >> 8;
+    lock_mask |= lock_mask >> 8;
+    /* FDIS and FCFGLK bits are sticky (write 1 to set; can't clear). */
+    value = cur_value & (FCFG_FDIS | FCFG_FCFGLK_MASK);
+    /* Preserve read-only bits in FPRGLK and FRDLK */
+    value |= cur_value & lock_mask;
+    /* Set all bits that aren't read-only. */
+    value |= new_value & ~lock_mask;
+
+    return value;
+}
+
+/* Common register write handler for both OTP classes. */
+static void npcm7xx_otp_write(NPCM7xxOTPState *s, NPCM7xxOTPRegister reg,
+                              uint32_t value)
+{
+    switch (reg) {
+    case NPCM7XX_OTP_FST:
+        /* RDST is cleared by writing 1 to it. */
+        if (value & FST_RDST) {
+            s->regs[NPCM7XX_OTP_FST] &= ~FST_RDST;
+        }
+        /* Preserve read-only and write-one-to-clear bits */
+        value &= ~FST_RO_MASK;
+        value |= s->regs[NPCM7XX_OTP_FST] & FST_RO_MASK;
+        break;
+
+    case NPCM7XX_OTP_FADDR:
+        break;
+
+    case NPCM7XX_OTP_FDATA:
+        /*
+         * This register is cleared by writing a magic value to it; no other
+         * values can be written.
+         */
+        if (value == FDATA_CLEAR) {
+            value = 0;
+        } else {
+            value = s->regs[NPCM7XX_OTP_FDATA];
+        }
+        break;
+
+    case NPCM7XX_OTP_FCFG:
+        value = npcm7xx_otp_compute_fcfg(s->regs[NPCM7XX_OTP_FCFG], value);
+        break;
+
+    case NPCM7XX_OTP_FCTL:
+        switch (value) {
+        case FCTL_READ_CMD:
+            npcm7xx_otp_read_array(s);
+            break;
+
+        case FCTL_PROG_CMD1:
+            /*
+             * Programming requires writing two separate magic values to this
+             * register; this is the first one. Just store it so it can be
+             * verified later when the second magic value is received.
+             */
+            break;
+
+        case FCTL_PROG_CMD2:
+            /*
+             * Only initiate programming if we received the first half of the
+             * command immediately before this one.
+             */
+            if (s->regs[NPCM7XX_OTP_FCTL] == FCTL_PROG_CMD1) {
+                npcm7xx_otp_program_array(s);
+            }
+            break;
+
+        default:
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: unrecognized FCNTL value 0x%" PRIx32 "\n",
+                          DEVICE(s)->canonical_path, value);
+            break;
+        }
+        if (value != FCTL_PROG_CMD1) {
+            value = 0;
+        }
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: write to invalid offset 0x%zx\n",
+                      DEVICE(s)->canonical_path, reg * sizeof(uint32_t));
+        return;
+    }
+
+    s->regs[reg] = value;
+}
+
+/* Register read handler specific to the fuse array OTP module. */
+static uint64_t npcm7xx_fuse_array_read(void *opaque, hwaddr addr,
+                                        unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+    uint32_t value;
+
+    /*
+     * Only the Fuse Strap register needs special handling; all other registers
+     * work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FUSTRAP) {
+        value = npcm7xx_otp_read(s, reg);
+    } else {
+        /* FUSTRAP is stored as three copies in the OTP array. */
+        uint32_t fustrap[3];
+
+        memcpy(fustrap, &s->array[0], sizeof(fustrap));
+
+        /* Determine value by a majority vote on each bit. */
+        value = (fustrap[0] & fustrap[1]) | (fustrap[0] & fustrap[2]) |
+                (fustrap[1] & fustrap[2]);
+    }
+
+    return value;
+}
+
+/* Register write handler specific to the fuse array OTP module. */
+static void npcm7xx_fuse_array_write(void *opaque, hwaddr addr, uint64_t v,
+                                     unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * The Fuse Strap register is read-only. Other registers are handled by
+     * common code.
+     */
+    if (reg != NPCM7XX_OTP_FUSTRAP) {
+        npcm7xx_otp_write(s, reg, v);
+    }
+}
+
+static const MemoryRegionOps npcm7xx_fuse_array_ops = {
+    .read       = npcm7xx_fuse_array_read,
+    .write      = npcm7xx_fuse_array_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+/* Register read handler specific to the key storage OTP module. */
+static uint64_t npcm7xx_key_storage_read(void *opaque, hwaddr addr,
+                                         unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * Only the Fuse Key Index register needs special handling; all other
+     * registers work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FKEYIND) {
+        return npcm7xx_otp_read(s, reg);
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
+
+    return s->regs[NPCM7XX_OTP_FKEYIND];
+}
+
+/* Register write handler specific to the key storage OTP module. */
+static void npcm7xx_key_storage_write(void *opaque, hwaddr addr, uint64_t v,
+                                      unsigned int size)
+{
+    NPCM7xxOTPRegister reg = addr / sizeof(uint32_t);
+    NPCM7xxOTPState *s = opaque;
+
+    /*
+     * Only the Fuse Key Index register needs special handling; all other
+     * registers work the same way for both kinds of OTP modules.
+     */
+    if (reg != NPCM7XX_OTP_FKEYIND) {
+        npcm7xx_otp_write(s, reg, v);
+        return;
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: FKEYIND is not implemented\n", __func__);
+
+    s->regs[NPCM7XX_OTP_FKEYIND] = v;
+}
+
+static const MemoryRegionOps npcm7xx_key_storage_ops = {
+    .read       = npcm7xx_key_storage_read,
+    .write      = npcm7xx_key_storage_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid      = {
+        .min_access_size        = 4,
+        .max_access_size        = 4,
+        .unaligned              = false,
+    },
+};
+
+static void npcm7xx_otp_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxOTPState *s = NPCM7XX_OTP(obj);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    s->regs[NPCM7XX_OTP_FST] = 0x00000001;
+    s->regs[NPCM7XX_OTP_FCFG] = 0x20000000;
+}
+
+static void npcm7xx_otp_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
+    NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
+    SysBusDevice *sbd = &s->parent;
+
+    memset(s->array, 0, sizeof(s->array));
+
+    memory_region_init_io(&s->mmio, OBJECT(s), oc->mmio_ops, s, "regs",
+                          NPCM7XX_OTP_REGS_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+}
+
+static const VMStateDescription vmstate_npcm7xx_otp = {
+    .name = "npcm7xx-otp",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxOTPState, NPCM7XX_OTP_NR_REGS),
+        VMSTATE_UINT8_ARRAY(array, NPCM7xxOTPState, NPCM7XX_OTP_ARRAY_BYTES),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static void npcm7xx_otp_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->realize = npcm7xx_otp_realize;
+    dc->vmsd = &vmstate_npcm7xx_otp;
+    rc->phases.enter = npcm7xx_otp_enter_reset;
+}
+
+static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
+
+    oc->mmio_ops = &npcm7xx_key_storage_ops;
+}
+
+static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data)
+{
+    NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass);
+
+    oc->mmio_ops = &npcm7xx_fuse_array_ops;
+}
+
+static const TypeInfo npcm7xx_otp_types[] = {
+    {
+        .name = TYPE_NPCM7XX_OTP,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxOTPState),
+        .class_init = npcm7xx_otp_class_init,
+        .abstract = true,
+    },
+    {
+        .name = TYPE_NPCM7XX_KEY_STORAGE,
+        .parent = TYPE_NPCM7XX_OTP,
+        .class_init = npcm7xx_key_storage_class_init,
+    },
+    {
+        .name = TYPE_NPCM7XX_FUSE_ARRAY,
+        .parent = TYPE_NPCM7XX_OTP,
+        .class_init = npcm7xx_fuse_array_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_otp_types);
diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs
index f3ad921382..0270f0bbf7 100644
--- a/hw/nvram/Makefile.objs
+++ b/hw/nvram/Makefile.objs
@@ -4,5 +4,6 @@ common-obj-$(CONFIG_AT24C) += eeprom_at24c.o
 common-obj-y += fw_cfg.o
 common-obj-$(CONFIG_CHRP_NVRAM) += chrp_nvram.o
 common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o
+common-obj-$(CONFIG_NPCM7XX) += npcm7xx_otp.o
 common-obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o
 obj-$(CONFIG_PSERIES) += spapr_nvram.o
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (6 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 07/11] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-09 16:29   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen
                   ` (2 subsequent siblings)
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

This just implements the bare minimum to cause the boot block to skip
memory initialization.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h    |  2 +
 include/hw/mem/npcm7xx_mc.h | 36 ++++++++++++++++
 hw/arm/npcm7xx.c            |  6 +++
 hw/mem/npcm7xx_mc.c         | 84 +++++++++++++++++++++++++++++++++++++
 hw/mem/Makefile.objs        |  1 +
 5 files changed, 129 insertions(+)
 create mode 100644 include/hw/mem/npcm7xx_mc.h
 create mode 100644 hw/mem/npcm7xx_mc.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 0fd4ae4133..3ae9e5dca2 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -18,6 +18,7 @@
 
 #include "hw/boards.h"
 #include "hw/cpu/a9mpcore.h"
+#include "hw/mem/npcm7xx_mc.h"
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/misc/npcm7xx_gcr.h"
 #include "hw/nvram/npcm7xx_otp.h"
@@ -71,6 +72,7 @@ typedef struct NPCM7xxState {
     NPCM7xxTimerCtrlState tim[3];
     NPCM7xxOTPState     key_storage;
     NPCM7xxOTPState     fuse_array;
+    NPCM7xxMCState      mc;
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/mem/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h
new file mode 100644
index 0000000000..7ed38be243
--- /dev/null
+++ b/include/hw/mem/npcm7xx_mc.h
@@ -0,0 +1,36 @@
+/*
+ * Nuvoton NPCM7xx Memory Controller stub
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_MC_H
+#define NPCM7XX_MC_H
+
+#include "exec/memory.h"
+#include "hw/sysbus.h"
+
+/**
+ * struct NPCM7xxMCState - Device state for the memory controller.
+ * @parent: System bus device.
+ * @mmio: Memory region through which registers are accessed.
+ */
+typedef struct NPCM7xxMCState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+} NPCM7xxMCState;
+
+#define TYPE_NPCM7XX_MC "npcm7xx-mc"
+#define NPCM7XX_MC(obj) OBJECT_CHECK(NPCM7xxMCState, (obj), TYPE_NPCM7XX_MC)
+
+#endif /* NPCM7XX_MC_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 70eaa2347f..4d227bb74b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -42,6 +42,7 @@
 #define NPCM7XX_CPUP_BA         (0xf03fe000)
 #define NPCM7XX_GCR_BA          (0xf0800000)
 #define NPCM7XX_CLK_BA          (0xf0801000)
+#define NPCM7XX_MC_BA           (0xf0824000)
 
 /* Internal AHB SRAM */
 #define NPCM7XX_RAM3_BA         (0xc0008000)
@@ -165,6 +166,7 @@ static void npcm7xx_init(Object *obj)
                             TYPE_NPCM7XX_KEY_STORAGE);
     object_initialize_child(obj, "otp2", &s->fuse_array,
                             TYPE_NPCM7XX_FUSE_ARRAY);
+    object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
 
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
@@ -230,6 +232,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
     npcm7xx_init_fuses(s);
 
+    /* Fake Memory Controller (MC) */
+    sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
+    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA);
+
     /* Timer Modules (TIM) */
     QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
new file mode 100644
index 0000000000..0435d06ab4
--- /dev/null
+++ b/hw/mem/npcm7xx_mc.c
@@ -0,0 +1,84 @@
+/*
+ * Nuvoton NPCM7xx Memory Controller stub
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/mem/npcm7xx_mc.h"
+#include "qapi/error.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#define NPCM7XX_MC_REGS_SIZE (4 * KiB)
+
+static uint64_t npcm7xx_mc_read(void *opaque, hwaddr addr, unsigned int size)
+{
+    /*
+     * If bits 8..11 @ offset 0 are not zero, the boot block thinks the memory
+     * controller has already been initialized and will skip DDR training.
+     */
+    if (addr == 0) {
+        return 0x100;
+    }
+
+    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
+
+    return 0;
+}
+
+static void npcm7xx_mc_write(void *opaque, hwaddr addr, uint64_t v,
+                             unsigned int size)
+{
+    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
+}
+
+static const MemoryRegionOps npcm7xx_mc_ops = {
+    .read = npcm7xx_mc_read,
+    .write = npcm7xx_mc_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxMCState *s = NPCM7XX_MC(dev);
+
+    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs",
+                          NPCM7XX_MC_REGS_SIZE);
+    sysbus_init_mmio(&s->parent, &s->mmio);
+}
+
+static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx Memory Controller stub";
+    dc->realize = npcm7xx_mc_realize;
+}
+
+static const TypeInfo npcm7xx_mc_types[] = {
+    {
+        .name = TYPE_NPCM7XX_MC,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxMCState),
+        .class_init = npcm7xx_mc_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_mc_types);
diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs
index 56345befd0..9a33ef7b35 100644
--- a/hw/mem/Makefile.objs
+++ b/hw/mem/Makefile.objs
@@ -1,3 +1,4 @@
 common-obj-$(CONFIG_DIMM) += pc-dimm.o
 common-obj-y += memory-device.o
+common-obj-$(CONFIG_NPCM7XX) += npcm7xx_mc.o
 common-obj-$(CONFIG_NVDIMM) += nvdimm.o
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (7 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-09 17:00   ` Philippe Mathieu-Daudé
  2020-07-09  0:36 ` [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen
  2020-07-09  0:36 ` [PATCH v5 11/11] docs/system: Add Nuvoton machine documentation Havard Skinnemoen
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

This implements a device model for the NPCM7xx SPI flash controller.

Direct reads and writes, and user-mode transactions have been tested in
various modes. Protection features are not implemented yet.

All the FIU instances are available in the SoC's address space,
regardless of whether or not they're connected to actual flash chips.

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 include/hw/arm/npcm7xx.h     |   2 +
 include/hw/ssi/npcm7xx_fiu.h | 100 +++++++
 hw/arm/npcm7xx.c             |  53 ++++
 hw/ssi/npcm7xx_fiu.c         | 510 +++++++++++++++++++++++++++++++++++
 hw/arm/Kconfig               |   1 +
 hw/ssi/Makefile.objs         |   1 +
 hw/ssi/trace-events          |   9 +
 7 files changed, 676 insertions(+)
 create mode 100644 include/hw/ssi/npcm7xx_fiu.h
 create mode 100644 hw/ssi/npcm7xx_fiu.c

diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
index 3ae9e5dca2..bc99f47286 100644
--- a/include/hw/arm/npcm7xx.h
+++ b/include/hw/arm/npcm7xx.h
@@ -23,6 +23,7 @@
 #include "hw/misc/npcm7xx_gcr.h"
 #include "hw/nvram/npcm7xx_otp.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/ssi/npcm7xx_fiu.h"
 #include "target/arm/cpu.h"
 
 #define NPCM7XX_MAX_NUM_CPUS    (2)
@@ -73,6 +74,7 @@ typedef struct NPCM7xxState {
     NPCM7xxOTPState     key_storage;
     NPCM7xxOTPState     fuse_array;
     NPCM7xxMCState      mc;
+    NPCM7xxFIUState     fiu[2];
 } NPCM7xxState;
 
 #define TYPE_NPCM7XX    "npcm7xx"
diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h
new file mode 100644
index 0000000000..b867bd0429
--- /dev/null
+++ b/include/hw/ssi/npcm7xx_fiu.h
@@ -0,0 +1,100 @@
+/*
+ * Nuvoton NPCM7xx Flash Interface Unit (FIU)
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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_FIU_H
+#define NPCM7XX_FIU_H
+
+#include "hw/ssi/ssi.h"
+#include "hw/sysbus.h"
+
+/**
+ * enum NPCM7xxFIURegister - 32-bit FIU register indices.
+ */
+enum NPCM7xxFIURegister {
+    NPCM7XX_FIU_DRD_CFG,
+    NPCM7XX_FIU_DWR_CFG,
+    NPCM7XX_FIU_UMA_CFG,
+    NPCM7XX_FIU_UMA_CTS,
+    NPCM7XX_FIU_UMA_CMD,
+    NPCM7XX_FIU_UMA_ADDR,
+    NPCM7XX_FIU_PRT_CFG,
+    NPCM7XX_FIU_UMA_DW0 = 0x0020 / sizeof(uint32_t),
+    NPCM7XX_FIU_UMA_DW1,
+    NPCM7XX_FIU_UMA_DW2,
+    NPCM7XX_FIU_UMA_DW3,
+    NPCM7XX_FIU_UMA_DR0,
+    NPCM7XX_FIU_UMA_DR1,
+    NPCM7XX_FIU_UMA_DR2,
+    NPCM7XX_FIU_UMA_DR3,
+    NPCM7XX_FIU_PRT_CMD0,
+    NPCM7XX_FIU_PRT_CMD1,
+    NPCM7XX_FIU_PRT_CMD2,
+    NPCM7XX_FIU_PRT_CMD3,
+    NPCM7XX_FIU_PRT_CMD4,
+    NPCM7XX_FIU_PRT_CMD5,
+    NPCM7XX_FIU_PRT_CMD6,
+    NPCM7XX_FIU_PRT_CMD7,
+    NPCM7XX_FIU_PRT_CMD8,
+    NPCM7XX_FIU_PRT_CMD9,
+    NPCM7XX_FIU_CFG = 0x78 / sizeof(uint32_t),
+    NPCM7XX_FIU_NR_REGS,
+};
+
+typedef struct NPCM7xxFIUState NPCM7xxFIUState;
+
+/**
+ * struct NPCM7xxFIUFlash - Per-chipselect flash controller state.
+ * @direct_access: Memory region for direct flash access.
+ * @fiu: Pointer to flash controller shared state.
+ */
+typedef struct NPCM7xxFIUFlash {
+    MemoryRegion direct_access;
+    NPCM7xxFIUState *fiu;
+} NPCM7xxFIUFlash;
+
+/**
+ * NPCM7xxFIUState - Device state for one Flash Interface Unit.
+ * @parent: System bus device.
+ * @mmio: Memory region for register access.
+ * @cs_count: Number of flash chips that may be connected to this module.
+ * @active_cs: Currently active chip select, or -1 if no chip is selected.
+ * @cs_lines: GPIO lines that may be wired to flash chips.
+ * @flash: Array of @cs_count per-flash-chip state objects.
+ * @spi: The SPI bus mastered by this controller.
+ * @regs: Register contents.
+ *
+ * Each FIU has a shared bank of registers, and controls up to four chip
+ * selects. Each chip select has a dedicated memory region which may be used to
+ * read and write the flash connected to that chip select as if it were memory.
+ */
+struct NPCM7xxFIUState {
+    SysBusDevice parent;
+
+    MemoryRegion mmio;
+
+    int32_t cs_count;
+    int32_t active_cs;
+    qemu_irq *cs_lines;
+    NPCM7xxFIUFlash *flash;
+
+    SSIBus *spi;
+
+    uint32_t regs[NPCM7XX_FIU_NR_REGS];
+};
+
+#define TYPE_NPCM7XX_FIU "npcm7xx-fiu"
+#define NPCM7XX_FIU(obj) OBJECT_CHECK(NPCM7xxFIUState, (obj), TYPE_NPCM7XX_FIU)
+
+#endif /* NPCM7XX_FIU_H */
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 4d227bb74b..c9ff3dab25 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -98,6 +98,37 @@ static const hwaddr npcm7xx_uart_addr[] = {
     0xf0004000,
 };
 
+static const hwaddr npcm7xx_fiu0_flash_addr[] = {
+    0x80000000,
+    0x88000000,
+};
+
+static const hwaddr npcm7xx_fiu3_flash_addr[] = {
+    0xa0000000,
+    0xa8000000,
+    0xb0000000,
+    0xb8000000,
+};
+
+static const struct {
+    const char *name;
+    hwaddr regs_addr;
+    int cs_count;
+    const hwaddr *flash_addr;
+} npcm7xx_fiu[] = {
+    {
+        .name = "fiu0",
+        .regs_addr = 0xfb000000,
+        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
+        .flash_addr = npcm7xx_fiu0_flash_addr,
+    }, {
+        .name = "fiu3",
+        .regs_addr = 0xc0000000,
+        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
+        .flash_addr = npcm7xx_fiu3_flash_addr,
+    },
+};
+
 void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
 {
     /*
@@ -171,6 +202,12 @@ static void npcm7xx_init(Object *obj)
     for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
         object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
     }
+
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
+    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
+        object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
+                                TYPE_NPCM7XX_FIU);
+    }
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -260,6 +297,22 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
                        serial_hd(i), DEVICE_LITTLE_ENDIAN);
     }
 
+    /* Flash Interface Unit (FIU) */
+    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
+    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
+        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
+        int j;
+
+        object_property_set_int(OBJECT(sbd), npcm7xx_fiu[i].cs_count,
+                                "cs-count", &error_abort);
+        sysbus_realize(sbd, &error_abort);
+
+        sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
+        for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
+            sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
+        }
+    }
+
     /* RAM2 (SRAM) */
     memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
                            NPCM7XX_RAM2_SZ, &error_abort);
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
new file mode 100644
index 0000000000..92ade709e8
--- /dev/null
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -0,0 +1,510 @@
+/*
+ * Nuvoton NPCM7xx Flash Interface Unit (FIU)
+ *
+ * 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 as published by the
+ * Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/irq.h"
+#include "hw/qdev-properties.h"
+#include "hw/ssi/npcm7xx_fiu.h"
+#include "migration/vmstate.h"
+#include "qapi/error.h"
+#include "qemu/error-report.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/units.h"
+
+#include "trace.h"
+
+/* Up to 128 MiB of flash may be accessed directly as memory. */
+#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
+
+/* Each module has 4 KiB of register space. Only a fraction of it is used. */
+#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
+
+/* FIU_{DRD,DWR,UMA,PTR}_CFG cannot be written when this bit is set. */
+#define NPCM7XX_FIU_CFG_LCK BIT(31)
+
+/* Direct Read configuration register fields. */
+#define FIU_DRD_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
+#define FIU_ADDSIZ_3BYTES 0
+#define FIU_ADDSIZ_4BYTES 1
+#define FIU_DRD_CFG_DBW(rv) extract32(rv, 12, 2)
+#define FIU_DRD_CFG_ACCTYPE(rv) extract32(rv, 8, 2)
+#define FIU_DRD_CFG_RDCMD(rv) extract32(rv, 0, 8)
+
+/* Direct Write configuration register fields. */
+#define FIU_DWR_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
+#define FIU_DWR_CFG_WRCMD(rv) extract32(rv, 0, 8)
+
+/* User-Mode Access register fields. */
+
+/* Command Mode Lock and the bits protected by it. */
+#define FIU_UMA_CFG_CMMLCK BIT(30)
+#define FIU_UMA_CFG_CMMLCK_MASK 0x00000403
+
+#define FIU_UMA_CFG_RDATSIZ(rv) extract32(rv, 24, 5)
+#define FIU_UMA_CFG_DBSIZ(rv) extract32(rv, 21, 3)
+#define FIU_UMA_CFG_WDATSIZ(rv) extract32(rv, 16, 5)
+#define FIU_UMA_CFG_ADDSIZ(rv) extract32(rv, 11, 3)
+#define FIU_UMA_CFG_CMDSIZ(rv) extract32(rv, 10, 1)
+#define FIU_UMA_CFG_DBPCK(rv) extract32(rv, 6, 2)
+
+#define FIU_UMA_CTS_RDYIE BIT(25)
+#define FIU_UMA_CTS_RDYST BIT(24)
+#define FIU_UMA_CTS_SW_CS BIT(16)
+#define FIU_UMA_CTS_DEV_NUM(rv) extract32(rv, 8, 2)
+#define FIU_UMA_CTS_EXEC_DONE BIT(0)
+
+/* Direct flash memory read handler. */
+static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
+                                       unsigned int size)
+{
+    NPCM7xxFIUFlash *f = opaque;
+    NPCM7xxFIUState *fiu = f->fiu;
+    int cs_id = f - fiu->flash;
+    uint64_t value = 0;
+    uint32_t drd_cfg;
+    int dummy_cycles;
+    int i;
+
+    drd_cfg = fiu->regs[NPCM7XX_FIU_DRD_CFG];
+
+    qemu_irq_lower(fiu->cs_lines[cs_id]);
+    ssi_transfer(fiu->spi, FIU_DRD_CFG_RDCMD(drd_cfg));
+
+    switch (FIU_DRD_CFG_ADDSIZ(drd_cfg)) {
+    case FIU_ADDSIZ_4BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
+        /* fall through */
+    case FIU_ADDSIZ_3BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
+                      DEVICE(fiu)->canonical_path, FIU_DRD_CFG_ADDSIZ(drd_cfg));
+        break;
+    }
+
+    /* Flash chip model expects one transfer per dummy bit, not byte */
+    dummy_cycles =
+        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
+    for (i = 0; i < dummy_cycles; i++) {
+        ssi_transfer(fiu->spi, 0);
+    }
+
+    for (i = 0; i < size; i++) {
+        value |= ssi_transfer(fiu->spi, 0) << (8 * i);
+    }
+
+    qemu_irq_raise(fiu->cs_lines[cs_id]);
+
+    trace_npcm7xx_fiu_flash_read(DEVICE(fiu)->canonical_path, cs_id, addr, size,
+                                 value);
+
+    return value;
+}
+
+/* Direct flash memory write handler. */
+static void npcm7xx_fiu_flash_write(void *opaque, hwaddr addr, uint64_t v,
+                                    unsigned int size)
+{
+    NPCM7xxFIUFlash *f = opaque;
+    NPCM7xxFIUState *fiu = f->fiu;
+    int cs_id = f - fiu->flash;
+    uint32_t dwr_cfg;
+    int i;
+
+    trace_npcm7xx_fiu_flash_write(DEVICE(fiu)->canonical_path, cs_id, addr,
+                                  size, v);
+
+    dwr_cfg = fiu->regs[NPCM7XX_FIU_DWR_CFG];
+
+    qemu_irq_lower(fiu->cs_lines[cs_id]);
+    ssi_transfer(fiu->spi, FIU_DWR_CFG_WRCMD(dwr_cfg));
+
+    switch (FIU_DWR_CFG_ADDSIZ(dwr_cfg)) {
+    case FIU_ADDSIZ_4BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
+        /* fall through */
+    case FIU_ADDSIZ_3BYTES:
+        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
+        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
+                      DEVICE(fiu)->canonical_path, FIU_DWR_CFG_ADDSIZ(dwr_cfg));
+        break;
+    }
+
+    for (i = 0; i < size; i++) {
+        ssi_transfer(fiu->spi, v & 0xff);
+        v >>= 8;
+    }
+
+    qemu_irq_raise(fiu->cs_lines[cs_id]);
+}
+
+static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
+    .read = npcm7xx_fiu_flash_read,
+    .write = npcm7xx_fiu_flash_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 1,
+        .max_access_size = 8,
+        .unaligned = true,
+    },
+};
+
+/* Control register read handler. */
+static uint64_t npcm7xx_fiu_ctrl_read(void *opaque, hwaddr addr,
+                                      unsigned int size)
+{
+    hwaddr reg = addr / sizeof(uint32_t);
+    NPCM7xxFIUState *s = opaque;
+    uint32_t value;
+
+    if (reg < NPCM7XX_FIU_NR_REGS) {
+        value = s->regs[reg];
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: read from invalid offset 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        value = 0;
+    }
+
+    trace_npcm7xx_fiu_ctrl_read(DEVICE(s)->canonical_path, addr, value);
+
+    return value;
+}
+
+/* Send the specified number of address bytes from the UMA address register. */
+static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
+{
+    /* All cases fall through */
+    switch (addsiz) {
+    case 4:
+        ssi_transfer(spi, extract32(addr, 24, 8));
+    case 3:
+        ssi_transfer(spi, extract32(addr, 16, 8));
+    case 2:
+        ssi_transfer(spi, extract32(addr, 8, 8));
+    case 1:
+        ssi_transfer(spi, extract32(addr, 0, 8));
+    case 0:
+        break;
+    }
+}
+
+/* Send the number of dummy bits specified in the UMA config register. */
+static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
+{
+    unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
+    unsigned int i;
+
+    for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
+        /* Use bytes 0 and 1 first, then keep repeating byte 2 */
+        unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
+        unsigned int j;
+
+        for (j = 0; j < 8; j += bits_per_clock) {
+            ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
+        }
+    }
+}
+
+/* Assert the chip select specified in the UMA Control/Status Register. */
+static void npcm7xx_fiu_select(NPCM7xxFIUState *s)
+{
+    int cs_id;
+
+    cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
+    if (cs_id < s->cs_count) {
+        qemu_irq_lower(s->cs_lines[cs_id]);
+    } else {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: UMA to CS%d; this module has only %d chip selects",
+                      DEVICE(s)->canonical_path, cs_id, s->cs_count);
+        cs_id = -1;
+    }
+
+    s->active_cs = cs_id;
+}
+
+/* Deassert the currently active chip select. */
+static void npcm7xx_fiu_deselect(NPCM7xxFIUState *s)
+{
+    if (s->active_cs < 0) {
+        return;
+    }
+
+    qemu_irq_raise(s->cs_lines[s->active_cs]);
+    s->active_cs = -1;
+}
+
+/* Perform a User-Mode Access transaction. */
+static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
+{
+    uint32_t uma_cts = s->regs[NPCM7XX_FIU_UMA_CTS];
+    uint32_t uma_cfg;
+    unsigned int i;
+
+    /* SW_CS means the CS is already forced low, so don't touch it. */
+    if (uma_cts & FIU_UMA_CTS_SW_CS) {
+        npcm7xx_fiu_select(s);
+    }
+
+    /* Send command, if present. */
+    uma_cfg = s->regs[NPCM7XX_FIU_UMA_CFG];
+    if (FIU_UMA_CFG_CMDSIZ(uma_cfg) > 0) {
+        ssi_transfer(s->spi, extract32(s->regs[NPCM7XX_FIU_UMA_CMD], 0, 8));
+    }
+
+    /* Send address, if present. */
+    send_address(s->spi, FIU_UMA_CFG_ADDSIZ(uma_cfg),
+                 s->regs[NPCM7XX_FIU_UMA_ADDR]);
+
+    /* Write data, if present. */
+    for (i = 0; i < FIU_UMA_CFG_WDATSIZ(uma_cfg); i++) {
+        unsigned int reg =
+            (i < 16) ? (NPCM7XX_FIU_UMA_DW0 + i / 4) : NPCM7XX_FIU_UMA_DW3;
+        unsigned int field = (i % 4) * 8;
+
+        ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
+    }
+
+    /* Send aummy bits, if present. */
+    send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
+
+    /* Read data, if present. */
+    for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
+        unsigned int reg = NPCM7XX_FIU_UMA_DR0 + i / 4;
+        unsigned int field = (i % 4) * 8;
+        uint8_t c;
+
+        c = ssi_transfer(s->spi, 0);
+        if (reg <= NPCM7XX_FIU_UMA_DR3) {
+            s->regs[reg] = deposit32(s->regs[reg], field, 8, c);
+        }
+    }
+
+    /* Again, don't touch CS if the user is forcing it low. */
+    if (uma_cts & FIU_UMA_CTS_SW_CS) {
+        npcm7xx_fiu_deselect(s);
+    }
+
+    /* RDYST means a command has completed since it was cleared. */
+    s->regs[NPCM7XX_FIU_UMA_CTS] |= FIU_UMA_CTS_RDYST;
+    /* EXEC_DONE means Execute Command / Not Done, so clear it here. */
+    s->regs[NPCM7XX_FIU_UMA_CTS] &= ~FIU_UMA_CTS_EXEC_DONE;
+}
+
+/* Control register write handler. */
+static void npcm7xx_fiu_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
+                                   unsigned int size)
+{
+    hwaddr reg = addr / sizeof(uint32_t);
+    NPCM7xxFIUState *s = opaque;
+    uint32_t value = v;
+
+    trace_npcm7xx_fiu_ctrl_write(DEVICE(s)->canonical_path, addr, value);
+
+    switch (reg) {
+    case NPCM7XX_FIU_UMA_CFG:
+        if (s->regs[reg] & FIU_UMA_CFG_CMMLCK) {
+            value &= ~FIU_UMA_CFG_CMMLCK_MASK;
+            value |= (s->regs[reg] & FIU_UMA_CFG_CMMLCK_MASK);
+        }
+        /* fall through */
+    case NPCM7XX_FIU_DRD_CFG:
+    case NPCM7XX_FIU_DWR_CFG:
+        if (s->regs[reg] & NPCM7XX_FIU_CFG_LCK) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "%s: write to locked register @ 0x%" PRIx64 "\n",
+                          DEVICE(s)->canonical_path, addr);
+            return;
+        }
+        s->regs[reg] = value;
+        break;
+
+    case NPCM7XX_FIU_UMA_CTS:
+        if (value & FIU_UMA_CTS_RDYST) {
+            value &= ~FIU_UMA_CTS_RDYST;
+        } else {
+            value |= s->regs[reg] & FIU_UMA_CTS_RDYST;
+        }
+        if ((s->regs[reg] ^ value) & FIU_UMA_CTS_SW_CS) {
+            if (value & FIU_UMA_CTS_SW_CS) {
+                /*
+                 * Don't drop CS if there's a transfer in progress, or we're
+                 * about to start one.
+                 */
+                if (!((value | s->regs[reg]) & FIU_UMA_CTS_EXEC_DONE)) {
+                    npcm7xx_fiu_deselect(s);
+                }
+            } else {
+                npcm7xx_fiu_select(s);
+            }
+        }
+        s->regs[reg] = value | (s->regs[reg] & FIU_UMA_CTS_EXEC_DONE);
+        if (value & FIU_UMA_CTS_EXEC_DONE) {
+            npcm7xx_fiu_uma_transaction(s);
+        }
+        break;
+
+    case NPCM7XX_FIU_UMA_DR0 ... NPCM7XX_FIU_UMA_DR3:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: write to read-only register @ 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        return;
+
+    case NPCM7XX_FIU_PRT_CFG:
+    case NPCM7XX_FIU_PRT_CMD0 ... NPCM7XX_FIU_PRT_CMD9:
+        qemu_log_mask(LOG_UNIMP, "%s: PRT is not implemented\n", __func__);
+        break;
+
+    case NPCM7XX_FIU_UMA_CMD:
+    case NPCM7XX_FIU_UMA_ADDR:
+    case NPCM7XX_FIU_UMA_DW0 ... NPCM7XX_FIU_UMA_DW3:
+    case NPCM7XX_FIU_CFG:
+        s->regs[reg] = value;
+        break;
+
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "%s: write to invalid offset 0x%" PRIx64 "\n",
+                      DEVICE(s)->canonical_path, addr);
+        return;
+    }
+}
+
+static const MemoryRegionOps npcm7xx_fiu_ctrl_ops = {
+    .read = npcm7xx_fiu_ctrl_read,
+    .write = npcm7xx_fiu_ctrl_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+        .unaligned = false,
+    },
+};
+
+static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
+
+    trace_npcm7xx_fiu_enter_reset(DEVICE(obj)->canonical_path, type);
+
+    memset(s->regs, 0, sizeof(s->regs));
+
+    s->regs[NPCM7XX_FIU_DRD_CFG] = 0x0300100b;
+    s->regs[NPCM7XX_FIU_DWR_CFG] = 0x03000002;
+    s->regs[NPCM7XX_FIU_UMA_CFG] = 0x00000400;
+    s->regs[NPCM7XX_FIU_UMA_CTS] = 0x00010000;
+    s->regs[NPCM7XX_FIU_UMA_CMD] = 0x0000000b;
+    s->regs[NPCM7XX_FIU_PRT_CFG] = 0x00000400;
+    s->regs[NPCM7XX_FIU_CFG] = 0x0000000b;
+}
+
+static void npcm7xx_fiu_hold_reset(Object *obj)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
+    int i;
+
+    trace_npcm7xx_fiu_hold_reset(DEVICE(obj)->canonical_path);
+
+    for (i = 0; i < s->cs_count; i++) {
+        qemu_irq_raise(s->cs_lines[i]);
+    }
+}
+
+static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
+{
+    NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
+    SysBusDevice *sbd = &s->parent;
+    int i;
+
+    if (s->cs_count <= 0) {
+        error_setg(errp, "%s: %d chip selects specified, need at least one",
+                   dev->canonical_path, s->cs_count);
+        return;
+    }
+
+    s->spi = ssi_create_bus(dev, "spi");
+    s->cs_lines = g_new0(qemu_irq, s->cs_count);
+    s->flash = g_new0(NPCM7xxFIUFlash, s->cs_count);
+
+    /*
+     * Register the control registers region first. It may be followed by one
+     * or more direct flash access regions.
+     */
+    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_fiu_ctrl_ops, s, "ctrl",
+                          NPCM7XX_FIU_CTRL_REGS_SIZE);
+    sysbus_init_mmio(sbd, &s->mmio);
+
+    for (i = 0; i < s->cs_count; i++) {
+        NPCM7xxFIUFlash *flash = &s->flash[i];
+        sysbus_init_irq(sbd, &s->cs_lines[i]);
+        flash->fiu = s;
+        memory_region_init_io(&flash->direct_access, OBJECT(s),
+                              &npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
+                              NPCM7XX_FIU_FLASH_WINDOW_SIZE);
+        sysbus_init_mmio(sbd, &flash->direct_access);
+    }
+}
+
+static const VMStateDescription vmstate_npcm7xx_fiu = {
+    .name = "npcm7xx-fiu",
+    .version_id = 0,
+    .minimum_version_id = 0,
+    .fields = (VMStateField[]) {
+        VMSTATE_INT32(active_cs, NPCM7xxFIUState),
+        VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS),
+        VMSTATE_END_OF_LIST(),
+    },
+};
+
+static Property npcm7xx_fiu_properties[] = {
+    DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
+{
+    ResettableClass *rc = RESETTABLE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->desc = "NPCM7xx Flash Interface Unit";
+    dc->realize = npcm7xx_fiu_realize;
+    dc->vmsd = &vmstate_npcm7xx_fiu;
+    rc->phases.enter = npcm7xx_fiu_enter_reset;
+    rc->phases.hold = npcm7xx_fiu_hold_reset;
+    device_class_set_props(dc, npcm7xx_fiu_properties);
+}
+
+static const TypeInfo npcm7xx_fiu_types[] = {
+    {
+        .name = TYPE_NPCM7XX_FIU,
+        .parent = TYPE_SYS_BUS_DEVICE,
+        .instance_size = sizeof(NPCM7xxFIUState),
+        .class_init = npcm7xx_fiu_class_init,
+    },
+};
+DEFINE_TYPES(npcm7xx_fiu_types);
diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index a31d0d282f..8d0ef0593b 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -360,6 +360,7 @@ config NPCM7XX
     select ARM_GIC
     select PL310  # cache controller
     select SERIAL
+    select SSI
     select UNIMP
 
 config FSL_IMX25
diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
index 07a85f1967..cab48e72c9 100644
--- a/hw/ssi/Makefile.objs
+++ b/hw/ssi/Makefile.objs
@@ -5,6 +5,7 @@ common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
 common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o
 common-obj-$(CONFIG_MSF2) += mss-spi.o
+common-obj-$(CONFIG_NPCM7XX) += npcm7xx_fiu.o
 
 common-obj-$(CONFIG_OMAP) += omap_spi.o
 common-obj-$(CONFIG_IMX) += imx_spi.o
diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
index 0ea498de91..8024253c1f 100644
--- a/hw/ssi/trace-events
+++ b/hw/ssi/trace-events
@@ -9,3 +9,12 @@ aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
 aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
 aspeed_smc_write(uint64_t addr,  uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
 aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
+
+# npcm7xx_fiu.c
+
+npcm7xx_fiu_enter_reset(const char *id, int reset_type) "%s reset type: %d"
+npcm7xx_fiu_hold_reset(const char *id) "%s"
+npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"
+npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"
+npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
+npcm7xx_fiu_flash_write(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (8 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  2020-07-13 14:57   ` Cédric Le Goater
  2020-07-09  0:36 ` [PATCH v5 11/11] docs/system: Add Nuvoton machine documentation Havard Skinnemoen
  10 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen

This allows these NPCM7xx-based boards to boot from a flash image, e.g.
one built with OpenBMC. For example like this:

IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
qemu-system-arm -machine quanta-gsj -nographic \
	-bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
	-drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on

Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 hw/arm/npcm7xx_boards.c | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 80cf1535f1..cfb31ce6f5 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -20,6 +20,7 @@
 #include "hw/arm/npcm7xx.h"
 #include "hw/core/cpu.h"
 #include "hw/loader.h"
+#include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
 #include "qemu/units.h"
@@ -66,6 +67,22 @@ static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
     arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
 }
 
+static void npcm7xx_connect_flash(NPCM7xxFIUState *fiu, int cs_no,
+                                  const char *flash_type, DriveInfo *dinfo)
+{
+    DeviceState *flash;
+    qemu_irq flash_cs;
+
+    flash = qdev_new(flash_type);
+    if (dinfo) {
+        qdev_prop_set_drive(flash, "drive", blk_by_legacy_dinfo(dinfo));
+    }
+    qdev_realize_and_unref(flash, BUS(fiu->spi), &error_fatal);
+
+    flash_cs = qdev_get_gpio_in_named(flash, SSI_GPIO_CS, 0);
+    sysbus_connect_irq(SYS_BUS_DEVICE(fiu), cs_no, flash_cs);
+}
+
 static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
 {
     memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
@@ -96,6 +113,7 @@ static void npcm750_evb_init(MachineState *machine)
     qdev_realize(DEVICE(soc), NULL, &error_abort);
 
     npcm7xx_load_bootrom(soc);
+    npcm7xx_connect_flash(&soc->fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
     npcm7xx_load_kernel(machine, soc);
 }
 
@@ -108,6 +126,8 @@ static void quanta_gsj_init(MachineState *machine)
     qdev_realize(DEVICE(soc), NULL, &error_abort);
 
     npcm7xx_load_bootrom(soc);
+    npcm7xx_connect_flash(&soc->fiu[0], 0, "mx25l25635e",
+                          drive_get(IF_MTD, 0, 0));
     npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* [PATCH v5 11/11] docs/system: Add Nuvoton machine documentation
  2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
                   ` (9 preceding siblings ...)
  2020-07-09  0:36 ` [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen
@ 2020-07-09  0:36 ` Havard Skinnemoen
  10 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  0:36 UTC (permalink / raw)
  To: peter.maydell, f4bug
  Cc: qemu-arm, qemu-devel, Avi.Fishman, kfting, Havard Skinnemoen,
	Cédric Le Goater

Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
---
 docs/system/arm/nuvoton.rst | 92 +++++++++++++++++++++++++++++++++++++
 docs/system/target-arm.rst  |  1 +
 2 files changed, 93 insertions(+)
 create mode 100644 docs/system/arm/nuvoton.rst

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
new file mode 100644
index 0000000000..7384662d58
--- /dev/null
+++ b/docs/system/arm/nuvoton.rst
@@ -0,0 +1,92 @@
+Nuvoton iBMC boards (``npcm750-evb``, ``quanta-gsj``)
+=====================================================
+
+The `Nuvoton iBMC`_ chips (NPCM7xx) are a family of ARM-based SoCs that are
+designed to be used as Baseboard Management Controllers (BMCs) in various
+servers. They all feature one or two ARM Cortex A9 CPU cores, as well as an
+assortment of peripherals targeted for either Enterprise or Data Center /
+Hyperscale applications. The former is a superset of the latter, so NPCM750 has
+all the peripherals of NPCM730 and more.
+
+.. _Nuvoton iBMC: https://www.nuvoton.com/products/cloud-computing/ibmc/
+
+The NPCM750 SoC has two Cortex A9 cores and is targeted for the Enterprise
+segment. The following machines are based on this chip :
+
+- ``npcm750-evb``       Nuvoton NPCM750 Evaluation board
+
+The NPCM730 SoC has two Cortex A9 cores and is targeted for Data Center and
+Hyperscale applications. The following machines are based on this chip :
+
+- ``quanta-gsj``        Quanta GSJ server BMC
+
+There are also two more SoCs, NPCM710 and NPCM705, which are single-core
+variants of NPCM750 and NPCM730, respectively. These are currently not
+supported by QEMU.
+
+Supported devices
+-----------------
+
+ * SMP (Dual Core Cortex-A9)
+ * Cortex-A9MPCore built-in peripherals: SCU, GIC, Global Timer, Private Timer
+   and Watchdog.
+ * SRAM, ROM and DRAM mappings
+ * System Global Control Registers (GCR)
+ * Clock and reset controller (CLK)
+ * Timer controller (TIM)
+ * Serial ports (16550-based)
+ * DDR4 memory controller (dummy interface indicating memory training is done)
+ * OTP controllers (no protection features)
+ * Flash Interface Unit (FIU; no protection features)
+
+Missing devices
+---------------
+
+ * GPIO controller
+ * LPC/eSPI host-to-BMC interface, including
+
+   * Keyboard and mouse controller interface (KBCI)
+   * Keyboard Controller Style (KCS) channels
+   * BIOS POST code FIFO
+   * System Wake-up Control (SWC)
+   * Shared memory (SHM)
+   * eSPI slave interface
+
+ * Ethernet controllers (GMAC and EMC)
+ * USB host (USBH)
+ * USB device (USBD)
+ * SMBus controller (SMBF)
+ * Peripheral SPI controller (PSPI)
+ * Analog to Digital Converter (ADC)
+ * SD/MMC host
+ * Random Number Generator (RNG)
+ * PECI interface
+ * Pulse Width Modulation (PWM)
+ * Tachometer
+ * PCI and PCIe root complex and bridges
+ * VDM and MCTP support
+ * Serial I/O expansion
+ * LPC/eSPI host
+ * Coprocessor
+ * Graphics
+ * Video capture
+ * Encoding compression engine
+ * Security features
+
+Boot options
+------------
+
+The Nuvoton machines can boot from an OpenBMC firmware image, or directly into
+a kernel using the ``-kernel`` option. OpenBMC images for `quanta-gsj` and
+possibly others can be downloaded from the OpenPOWER jenkins :
+
+   https://openpower.xyz/
+
+Booting a full firmware image requires a Boot ROM specified via the ``-bios``
+option to QEMU. The firmware image should be attached as an MTD drive. Example :
+
+.. code-block:: bash
+
+  $ qemu-system-arm -machine quanta-gsj -nographic \
+      -bios npcm7xx_bootrom.bin \
+      -drive file=image-bmc,if=mtd,bus=0,unit=0,format=raw
diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst
index 1bd477a293..38a9daa9b9 100644
--- a/docs/system/target-arm.rst
+++ b/docs/system/target-arm.rst
@@ -84,6 +84,7 @@ undocumented; you can get a complete list by running
    arm/aspeed
    arm/musicpal
    arm/nseries
+   arm/nuvoton
    arm/orangepi
    arm/palm
    arm/xscale
-- 
2.27.0.383.g050319c2ae-goog



^ permalink raw reply related	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines
  2020-07-09  0:36 ` [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen
@ 2020-07-09  5:57   ` Philippe Mathieu-Daudé
  2020-07-09  6:09     ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09  5:57 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: Avi.Fishman, qemu-devel, kfting, qemu-arm, Cédric Le Goater,
	Joel Stanley

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> This adds two new machines, both supported by OpenBMC:
> 
>   - npcm750-evb: Nuvoton NPCM750 Evaluation Board.
>   - quanta-gsj: A board with a NPCM730 chip.
> 
> They rely on the NPCM7xx SoC device to do the heavy lifting. They are
> almost completely identical at the moment, apart from the SoC type,
> which currently only changes the reset contents of one register
> (GCR.MDLR), but they might grow apart a bit more as more functionality
> is added.
> 
> Both machines can boot the Linux kernel into /bin/sh.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  default-configs/arm-softmmu.mak |   1 +
>  include/hw/arm/npcm7xx.h        |  19 ++++
>  hw/arm/npcm7xx_boards.c         | 156 ++++++++++++++++++++++++++++++++
>  hw/arm/Makefile.objs            |   2 +-
>  4 files changed, 177 insertions(+), 1 deletion(-)
>  create mode 100644 hw/arm/npcm7xx_boards.c
> 
> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
> index 8fc09a4a51..9a94ebd0be 100644
> --- a/default-configs/arm-softmmu.mak
> +++ b/default-configs/arm-softmmu.mak
> @@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
>  CONFIG_SPITZ=y
>  CONFIG_TOSA=y
>  CONFIG_Z2=y
> +CONFIG_NPCM7XX=y
>  CONFIG_COLLIE=y
>  CONFIG_ASPEED_SOC=y
>  CONFIG_NETDUINO2=y
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> index 95d9224f59..b8b76bc07b 100644
> --- a/include/hw/arm/npcm7xx.h
> +++ b/include/hw/arm/npcm7xx.h
> @@ -35,6 +35,25 @@
>  #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
>  #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
>  
> +typedef struct NPCM7xxMachine {
> +    MachineState        parent;
> +} NPCM7xxMachine;
> +
> +#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
> +#define NPCM7XX_MACHINE(obj)                                            \
> +    OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE)
> +
> +typedef struct NPCM7xxMachineClass {
> +    MachineClass        parent;
> +
> +    const char          *soc_type;
> +} NPCM7xxMachineClass;
> +
> +#define NPCM7XX_MACHINE_CLASS(klass)                                    \
> +    OBJECT_CLASS_CHECK(NPCM7xxMachineClass, (klass), TYPE_NPCM7XX_MACHINE)
> +#define NPCM7XX_MACHINE_GET_CLASS(obj)                                  \
> +    OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE)
> +
>  typedef struct NPCM7xxState {
>      DeviceState         parent;
>  
> diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
> new file mode 100644
> index 0000000000..d78d9f991b
> --- /dev/null
> +++ b/hw/arm/npcm7xx_boards.c
> @@ -0,0 +1,156 @@
> +/*
> + * Machine definitions for boards featuring an NPCM7xx SoC.
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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/arm/boot.h"
> +#include "hw/arm/npcm7xx.h"
> +#include "hw/core/cpu.h"
> +#include "qapi/error.h"
> +#include "qemu/units.h"
> +
> +#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
> +#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
> +
> +static struct arm_boot_info npcm7xx_binfo = {
> +    .loader_start       = NPCM7XX_LOADER_START,
> +    .smp_loader_start   = NPCM7XX_SMP_LOADER_START,
> +    .smp_bootreg_addr   = NPCM7XX_SMP_BOOTREG_ADDR,
> +    .gic_cpu_if_addr    = NPCM7XX_GIC_CPU_IF_ADDR,
> +    .write_secondary_boot = npcm7xx_write_secondary_boot,
> +    .board_id           = -1,
> +};
> +
> +static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
> +{
> +    NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
> +
> +    npcm7xx_binfo.ram_size = machine->ram_size;
> +    npcm7xx_binfo.nb_cpus = sc->num_cpus;
> +
> +    arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
> +}
> +
> +static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
> +{

You might need a check such:

    if (memory_region_size(dram) > NPCM7XX_DRAM_SZ) {
        error_report("Invalid RAM size, maximum allowed is 2GB");
        exit(1);
    }

Or add a npcm7xx_verify_dram_size(MachineState *machine) and use
machine->ram_size.

Hardware don't really care you use more than 2GB, the memory
above 2GB is not addressable by the SoC.

Maybe the check belongs to npcm7xx_gcr_realize() instead?

    if (dram_size > 2 * GiB) {
        warn_report(...)
    }
    if (dram_size >= 2 * GiB) {
        s->reset_intcr3 |= 4 << 8;
    ...

If the GCR is generic to a family of Nuvoton chipsets and expected to
also work when using ARMv8-A cores (able to address >4GB), then the
check belongs to npcm7xx_realize().

With this addressed:
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
> +
> +    object_property_set_link(OBJECT(soc), OBJECT(dram), "dram-mr",
> +                             &error_abort);
> +}
> +
> +static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
> +                                        uint32_t hw_straps)
> +{
> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
> +    Object *obj;
> +
> +    obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
> +                                &error_abort, NULL);
> +    object_property_set_uint(obj, hw_straps, "power-on-straps", &error_abort);
> +
> +    return NPCM7XX(obj);
> +}
> +
> +static void npcm750_evb_init(MachineState *machine)
> +{
> +    NPCM7xxState *soc;
> +
> +    soc = npcm7xx_create_soc(machine, NPCM750_EVB_POWER_ON_STRAPS);
> +    npcm7xx_connect_dram(soc, machine->ram);
> +    qdev_realize(DEVICE(soc), NULL, &error_abort);
> +
> +    npcm7xx_load_kernel(machine, soc);
> +}
> +
> +static void quanta_gsj_init(MachineState *machine)
> +{
> +    NPCM7xxState *soc;
> +
> +    soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
> +    npcm7xx_connect_dram(soc, machine->ram);
> +    qdev_realize(DEVICE(soc), NULL, &error_abort);
> +
> +    npcm7xx_load_kernel(machine, soc);
> +}
> +
> +static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type)
> +{
> +    NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type));
> +    MachineClass *mc = MACHINE_CLASS(nmc);
> +
> +    nmc->soc_type = type;
> +    mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
> +}
> +
> +static void npcm7xx_machine_class_init(ObjectClass *oc, void *data)
> +{
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    mc->no_floppy       = 1;
> +    mc->no_cdrom        = 1;
> +    mc->no_parallel     = 1;
> +    mc->default_ram_id  = "ram";
> +}
> +
> +/*
> + * Schematics:
> + * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf
> + */
> +static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    npcm7xx_set_soc_type(nmc, TYPE_NPCM750);
> +
> +    mc->desc            = "Nuvoton NPCM750 Evaluation Board (Cortex A9)";
> +    mc->init            = npcm750_evb_init;

Either use 'var = value' or pad and align the '=' :)

> +    mc->default_ram_size = 512 * MiB;
> +};
> +
> +static void gsj_machine_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
> +    MachineClass *mc = MACHINE_CLASS(oc);
> +
> +    npcm7xx_set_soc_type(nmc, TYPE_NPCM730);
> +
> +    mc->desc            = "Quanta GSJ (Cortex A9)";
> +    mc->init            = quanta_gsj_init;
> +    mc->default_ram_size = 512 * MiB;
> +};
> +
> +static const TypeInfo npcm7xx_machine_types[] = {
> +    {
> +        .name           = TYPE_NPCM7XX_MACHINE,
> +        .parent         = TYPE_MACHINE,
> +        .instance_size  = sizeof(NPCM7xxMachine),
> +        .class_size     = sizeof(NPCM7xxMachineClass),
> +        .class_init     = npcm7xx_machine_class_init,
> +        .abstract       = true,
> +    }, {
> +        .name           = MACHINE_TYPE_NAME("npcm750-evb"),
> +        .parent         = TYPE_NPCM7XX_MACHINE,
> +        .class_init     = npcm750_evb_machine_class_init,
> +    }, {
> +        .name           = MACHINE_TYPE_NAME("quanta-gsj"),
> +        .parent         = TYPE_NPCM7XX_MACHINE,
> +        .class_init     = gsj_machine_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(npcm7xx_machine_types)
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 13d163a599..c333548ce1 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -41,7 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
>  obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
>  obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
> -obj-$(CONFIG_NPCM7XX) += npcm7xx.o
> +obj-$(CONFIG_NPCM7XX) += npcm7xx.o npcm7xx_boards.o
>  obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
>  obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
>  obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09  0:35 ` [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
@ 2020-07-09  6:04   ` Philippe Mathieu-Daudé
  2020-07-09  6:43     ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09  6:04 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: Avi.Fishman, qemu-devel, kfting, qemu-arm, Cédric Le Goater,
	Joel Stanley

On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> Implement a device model for the System Global Control Registers in the
> NPCM730 and NPCM750 BMC SoCs.
> 
> This is primarily used to enable SMP boot (the boot ROM spins reading
> the SCRPAD register) and DDR memory initialization; other registers are
> best effort for now.
> 
> The reset values of the MDLR and PWRON registers are determined by the
> SoC variant (730 vs 750) and board straps respectively.
> 
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/misc/npcm7xx_gcr.h |  76 ++++++++++++
>  hw/misc/npcm7xx_gcr.c         | 226 ++++++++++++++++++++++++++++++++++
>  MAINTAINERS                   |   8 ++
>  hw/arm/Kconfig                |   3 +
>  hw/misc/Makefile.objs         |   1 +
>  hw/misc/trace-events          |   4 +
>  6 files changed, 318 insertions(+)
>  create mode 100644 include/hw/misc/npcm7xx_gcr.h
>  create mode 100644 hw/misc/npcm7xx_gcr.c
> 
> diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h
> new file mode 100644
> index 0000000000..4884676be2
> --- /dev/null
> +++ b/include/hw/misc/npcm7xx_gcr.h
> @@ -0,0 +1,76 @@
> +/*
> + * Nuvoton NPCM7xx System Global 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_GCR_H
> +#define NPCM7XX_GCR_H
> +
> +#include "exec/memory.h"
> +#include "hw/sysbus.h"
> +
> +enum NPCM7xxGCRRegisters {
> +    NPCM7XX_GCR_PDID,
> +    NPCM7XX_GCR_PWRON,
> +    NPCM7XX_GCR_MFSEL1          = 0x0c / sizeof(uint32_t),
> +    NPCM7XX_GCR_MFSEL2,
> +    NPCM7XX_GCR_MISCPE,
> +    NPCM7XX_GCR_SPSWC           = 0x038 / sizeof(uint32_t),
> +    NPCM7XX_GCR_INTCR,
> +    NPCM7XX_GCR_INTSR,
> +    NPCM7XX_GCR_HIFCR           = 0x050 / sizeof(uint32_t),
> +    NPCM7XX_GCR_INTCR2          = 0x060 / sizeof(uint32_t),
> +    NPCM7XX_GCR_MFSEL3,
> +    NPCM7XX_GCR_SRCNT,
> +    NPCM7XX_GCR_RESSR,
> +    NPCM7XX_GCR_RLOCKR1,
> +    NPCM7XX_GCR_FLOCKR1,
> +    NPCM7XX_GCR_DSCNT,
> +    NPCM7XX_GCR_MDLR,
> +    NPCM7XX_GCR_SCRPAD3,
> +    NPCM7XX_GCR_SCRPAD2,
> +    NPCM7XX_GCR_DAVCLVLR        = 0x098 / sizeof(uint32_t),
> +    NPCM7XX_GCR_INTCR3,
> +    NPCM7XX_GCR_VSINTR          = 0x0ac / sizeof(uint32_t),
> +    NPCM7XX_GCR_MFSEL4,
> +    NPCM7XX_GCR_CPBPNTR         = 0x0c4 / sizeof(uint32_t),
> +    NPCM7XX_GCR_CPCTL           = 0x0d0 / sizeof(uint32_t),
> +    NPCM7XX_GCR_CP2BST,
> +    NPCM7XX_GCR_B2CPNT,
> +    NPCM7XX_GCR_CPPCTL,
> +    NPCM7XX_GCR_I2CSEGSEL,
> +    NPCM7XX_GCR_I2CSEGCTL,
> +    NPCM7XX_GCR_VSRCR,
> +    NPCM7XX_GCR_MLOCKR,
> +    NPCM7XX_GCR_SCRPAD          = 0x013c / sizeof(uint32_t),
> +    NPCM7XX_GCR_USB1PHYCTL,
> +    NPCM7XX_GCR_USB2PHYCTL,
> +    NPCM7XX_GCR_NR_REGS,
> +};
> +
> +typedef struct NPCM7xxGCRState {
> +    SysBusDevice parent;
> +
> +    MemoryRegion iomem;
> +
> +    uint32_t regs[NPCM7XX_GCR_NR_REGS];
> +
> +    uint32_t reset_pwron;
> +    uint32_t reset_mdlr;
> +    uint32_t reset_intcr3;
> +} NPCM7xxGCRState;
> +
> +#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
> +#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR)
> +
> +#endif /* NPCM7XX_GCR_H */
> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
> new file mode 100644
> index 0000000000..9934cd238d
> --- /dev/null
> +++ b/hw/misc/npcm7xx_gcr.c
> @@ -0,0 +1,226 @@
> +/*
> + * Nuvoton NPCM7xx System Global 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_gcr.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +
> +#include "trace.h"
> +
> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
> +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
> +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
> +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
> +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
> +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
> +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
> +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
> +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
> +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
> +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
> +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
> +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
> +};
> +
> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    uint32_t reg = offset / sizeof(uint32_t);
> +    NPCM7xxGCRState *s = opaque;
> +
> +    if (reg >= NPCM7XX_GCR_NR_REGS) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
> +                      __func__, (unsigned int)offset);

Maybe use HWADDR_PRIx instead of casting to int?

> +        return 0;
> +    }
> +
> +    trace_npcm7xx_gcr_read(offset, s->regs[reg]);
> +
> +    return s->regs[reg];
> +}
> +
> +static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
> +                              uint64_t v, unsigned size)
> +{
> +    uint32_t reg = offset / sizeof(uint32_t);
> +    NPCM7xxGCRState *s = opaque;
> +    uint32_t value = v;
> +
> +    trace_npcm7xx_gcr_write(offset, value);
> +
> +    if (reg >= NPCM7XX_GCR_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_GCR_PDID:
> +    case NPCM7XX_GCR_PWRON:
> +    case NPCM7XX_GCR_INTSR:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
> +                      __func__, (unsigned int)offset);
> +        return;
> +
> +    case NPCM7XX_GCR_RESSR:
> +    case NPCM7XX_GCR_CP2BST:
> +        /* Write 1 to clear */
> +        value = s->regs[reg] & ~value;
> +        break;
> +
> +    case NPCM7XX_GCR_RLOCKR1:
> +    case NPCM7XX_GCR_MDLR:
> +        /* Write 1 to set */
> +        value |= s->regs[reg];
> +        break;
> +    };
> +
> +    s->regs[reg] = value;
> +}
> +
> +static const struct MemoryRegionOps npcm7xx_gcr_ops = {
> +    .read       = npcm7xx_gcr_read,
> +    .write      = npcm7xx_gcr_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,
> +        .unaligned              = false,
> +    },
> +};
> +
> +static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)

2nd user of this new API, nice :)

> +{
> +    NPCM7xxGCRState *s = NPCM7XX_GCR(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(s->regs));
> +        s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
> +        s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
> +        s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
> +        break;
> +    }
> +}
> +
> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
> +    uint64_t dram_size;
> +    Error *err = NULL;
> +    Object *obj;
> +
> +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
> +    if (!obj) {
> +        error_setg(errp, "%s: required dram-mr link not found: %s",
> +                   __func__, error_get_pretty(err));
> +        return;
> +    }
> +    dram_size = memory_region_size(MEMORY_REGION(obj));
> +
> +    /* Power-on reset value */
> +    s->reset_intcr3 = 0x00001002;
> +
> +    /*
> +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
> +     * DRAM size, and is normally initialized by the boot block as part of DRAM
> +     * training. However, since we don't have a complete emulation of the
> +     * memory controller and try to make it look like it has already been
> +     * initialized, the boot block will skip this initialization, and we need
> +     * to make sure this field is set correctly up front.
> +     *
> +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
> +     * more of DRAM will be interpreted as 128 MiB.

I'd say u-boot is right in wrapping at 2GB, as more DRAM is
un-addressable.

> +     *
> +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
> +     */
> +    if (dram_size >= 2 * GiB) {

See my comment in this series on the machine patch.

> +        s->reset_intcr3 |= 4 << 8;
> +    } else if (dram_size >= 1 * GiB) {
> +        s->reset_intcr3 |= 3 << 8;
> +    } else if (dram_size >= 512 * MiB) {
> +        s->reset_intcr3 |= 2 << 8;
> +    } else if (dram_size >= 256 * MiB) {
> +        s->reset_intcr3 |= 1 << 8;
> +    } else if (dram_size >= 128 * MiB) {
> +        s->reset_intcr3 |= 0 << 8;
> +    } else {
> +        error_setg(errp,
> +                   "npcm7xx_gcr: DRAM size %" PRIu64
> +                   " is too small (need 128 MiB minimum)",
> +                   dram_size);

Ah, so you could add the same error for >2GB. Easier.

Preferably using HWADDR_PRIx, and similar error for >2GB:
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +        return;
> +    }
> +}
> +
> +static void npcm7xx_gcr_init(Object *obj)
> +{
> +    NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
> +
> +    memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
> +                          TYPE_NPCM7XX_GCR, 4 * KiB);
> +    sysbus_init_mmio(&s->parent, &s->iomem);
> +}
> +
> +static const VMStateDescription vmstate_npcm7xx_gcr = {
> +    .name = "npcm7xx-gcr",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static Property npcm7xx_gcr_properties[] = {
> +    DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
> +    DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
> +{
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc = "NPCM7xx System Global Control Registers";
> +    dc->realize = npcm7xx_gcr_realize;
> +    dc->vmsd = &vmstate_npcm7xx_gcr;
> +    rc->phases.enter = npcm7xx_gcr_enter_reset;
> +
> +    device_class_set_props(dc, npcm7xx_gcr_properties);
> +}
> +
> +static const TypeInfo npcm7xx_gcr_info = {
> +    .name               = TYPE_NPCM7XX_GCR,
> +    .parent             = TYPE_SYS_BUS_DEVICE,
> +    .instance_size      = sizeof(NPCM7xxGCRState),
> +    .instance_init      = npcm7xx_gcr_init,
> +    .class_init         = npcm7xx_gcr_class_init,
> +};
> +
> +static void npcm7xx_gcr_register_type(void)
> +{
> +    type_register_static(&npcm7xx_gcr_info);
> +}
> +type_init(npcm7xx_gcr_register_type);
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 42388f1de2..43173a338a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -723,6 +723,14 @@ S: Odd Fixes
>  F: hw/arm/musicpal.c
>  F: docs/system/arm/musicpal.rst
>  
> +Nuvoton NPCM7xx
> +M: Havard Skinnemoen <hskinnemoen@google.com>
> +M: Tyrone Ting <kfting@nuvoton.com>
> +L: qemu-arm@nongnu.org
> +S: Supported
> +F: hw/*/npcm7xx*
> +F: include/hw/*/npcm7xx*
> +
>  nSeries
>  M: Andrzej Zaborowski <balrogg@gmail.com>
>  M: Peter Maydell <peter.maydell@linaro.org>
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 4a224a6351..192a8dec3b 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -354,6 +354,9 @@ config XLNX_VERSAL
>      select VIRTIO_MMIO
>      select UNIMP
>  
> +config NPCM7XX
> +    bool
> +
>  config FSL_IMX25
>      bool
>      select IMX
> diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> index 5aaca8a039..40a9d1c01e 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_gcr.o
>  common-obj-$(CONFIG_OMAP) += omap_clk.o
>  common-obj-$(CONFIG_OMAP) += omap_gpmc.o
>  common-obj-$(CONFIG_OMAP) += omap_l4.o
> diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> index ebea53735c..48e2d54c49 100644
> --- a/hw/misc/trace-events
> +++ b/hw/misc/trace-events
> @@ -107,6 +107,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_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
> +
>  # stm32f4xx_syscfg
>  stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
>  stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines
  2020-07-09  5:57   ` Philippe Mathieu-Daudé
@ 2020-07-09  6:09     ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09  6:09 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: Avi.Fishman, qemu-devel, kfting, qemu-arm, Cédric Le Goater,
	Joel Stanley

On 7/9/20 7:57 AM, Philippe Mathieu-Daudé wrote:
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>> This adds two new machines, both supported by OpenBMC:
>>
>>   - npcm750-evb: Nuvoton NPCM750 Evaluation Board.
>>   - quanta-gsj: A board with a NPCM730 chip.
>>
>> They rely on the NPCM7xx SoC device to do the heavy lifting. They are
>> almost completely identical at the moment, apart from the SoC type,
>> which currently only changes the reset contents of one register
>> (GCR.MDLR), but they might grow apart a bit more as more functionality
>> is added.
>>
>> Both machines can boot the Linux kernel into /bin/sh.
>>
>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>> ---
>>  default-configs/arm-softmmu.mak |   1 +
>>  include/hw/arm/npcm7xx.h        |  19 ++++
>>  hw/arm/npcm7xx_boards.c         | 156 ++++++++++++++++++++++++++++++++
>>  hw/arm/Makefile.objs            |   2 +-
>>  4 files changed, 177 insertions(+), 1 deletion(-)
>>  create mode 100644 hw/arm/npcm7xx_boards.c
>>
>> diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
>> index 8fc09a4a51..9a94ebd0be 100644
>> --- a/default-configs/arm-softmmu.mak
>> +++ b/default-configs/arm-softmmu.mak
>> @@ -27,6 +27,7 @@ CONFIG_GUMSTIX=y
>>  CONFIG_SPITZ=y
>>  CONFIG_TOSA=y
>>  CONFIG_Z2=y
>> +CONFIG_NPCM7XX=y
>>  CONFIG_COLLIE=y
>>  CONFIG_ASPEED_SOC=y
>>  CONFIG_NETDUINO2=y
>> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
>> index 95d9224f59..b8b76bc07b 100644
>> --- a/include/hw/arm/npcm7xx.h
>> +++ b/include/hw/arm/npcm7xx.h
>> @@ -35,6 +35,25 @@
>>  #define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
>>  #define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
>>  
>> +typedef struct NPCM7xxMachine {
>> +    MachineState        parent;
>> +} NPCM7xxMachine;
>> +
>> +#define TYPE_NPCM7XX_MACHINE MACHINE_TYPE_NAME("npcm7xx")
>> +#define NPCM7XX_MACHINE(obj)                                            \
>> +    OBJECT_CHECK(NPCM7xxMachine, (obj), TYPE_NPCM7XX_MACHINE)
>> +
>> +typedef struct NPCM7xxMachineClass {
>> +    MachineClass        parent;
>> +
>> +    const char          *soc_type;
>> +} NPCM7xxMachineClass;
>> +
>> +#define NPCM7XX_MACHINE_CLASS(klass)                                    \
>> +    OBJECT_CLASS_CHECK(NPCM7xxMachineClass, (klass), TYPE_NPCM7XX_MACHINE)
>> +#define NPCM7XX_MACHINE_GET_CLASS(obj)                                  \
>> +    OBJECT_GET_CLASS(NPCM7xxMachineClass, (obj), TYPE_NPCM7XX_MACHINE)
>> +
>>  typedef struct NPCM7xxState {
>>      DeviceState         parent;
>>  
>> diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
>> new file mode 100644
>> index 0000000000..d78d9f991b
>> --- /dev/null
>> +++ b/hw/arm/npcm7xx_boards.c
>> @@ -0,0 +1,156 @@
>> +/*
>> + * Machine definitions for boards featuring an NPCM7xx SoC.
>> + *
>> + * 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 as published by the
>> + * Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + * 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/arm/boot.h"
>> +#include "hw/arm/npcm7xx.h"
>> +#include "hw/core/cpu.h"
>> +#include "qapi/error.h"
>> +#include "qemu/units.h"
>> +
>> +#define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
>> +#define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
>> +
>> +static struct arm_boot_info npcm7xx_binfo = {
>> +    .loader_start       = NPCM7XX_LOADER_START,
>> +    .smp_loader_start   = NPCM7XX_SMP_LOADER_START,
>> +    .smp_bootreg_addr   = NPCM7XX_SMP_BOOTREG_ADDR,
>> +    .gic_cpu_if_addr    = NPCM7XX_GIC_CPU_IF_ADDR,
>> +    .write_secondary_boot = npcm7xx_write_secondary_boot,
>> +    .board_id           = -1,
>> +};
>> +
>> +static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
>> +{
>> +    NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
>> +
>> +    npcm7xx_binfo.ram_size = machine->ram_size;
>> +    npcm7xx_binfo.nb_cpus = sc->num_cpus;
>> +
>> +    arm_load_kernel(&soc->cpu[0], machine, &npcm7xx_binfo);
>> +}
>> +
>> +static void npcm7xx_connect_dram(NPCM7xxState *soc, MemoryRegion *dram)
>> +{
> 
> You might need a check such:
> 
>     if (memory_region_size(dram) > NPCM7XX_DRAM_SZ) {
>         error_report("Invalid RAM size, maximum allowed is 2GB");
>         exit(1);
>     }
> 
> Or add a npcm7xx_verify_dram_size(MachineState *machine) and use
> machine->ram_size.
> 
> Hardware don't really care you use more than 2GB, the memory
> above 2GB is not addressable by the SoC.
> 
> Maybe the check belongs to npcm7xx_gcr_realize() instead?
> 
>     if (dram_size > 2 * GiB) {
>         warn_report(...)
>     }
>     if (dram_size >= 2 * GiB) {
>         s->reset_intcr3 |= 4 << 8;
>     ...
> 
> If the GCR is generic to a family of Nuvoton chipsets and expected to
> also work when using ARMv8-A cores (able to address >4GB), then the
> check belongs to npcm7xx_realize().
> 
> With this addressed:
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> 
>> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_DRAM_BA, dram);
>> +
>> +    object_property_set_link(OBJECT(soc), OBJECT(dram), "dram-mr",
>> +                             &error_abort);
>> +}
>> +
>> +static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
>> +                                        uint32_t hw_straps)
>> +{
>> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
>> +    Object *obj;
>> +

Oh I forgot, you also need:

        if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
            error_report("This board can only be used with CPU %s",
                         mc->default_cpu_type);
            exit(1);
        }

With ...

>> +    obj = object_new_with_props(nmc->soc_type, OBJECT(machine), "soc",
>> +                                &error_abort, NULL);
>> +    object_property_set_uint(obj, hw_straps, "power-on-straps", &error_abort);
>> +
>> +    return NPCM7XX(obj);
>> +}
>> +
>> +static void npcm750_evb_init(MachineState *machine)
>> +{
>> +    NPCM7xxState *soc;
>> +
>> +    soc = npcm7xx_create_soc(machine, NPCM750_EVB_POWER_ON_STRAPS);
>> +    npcm7xx_connect_dram(soc, machine->ram);
>> +    qdev_realize(DEVICE(soc), NULL, &error_abort);
>> +
>> +    npcm7xx_load_kernel(machine, soc);
>> +}
>> +
>> +static void quanta_gsj_init(MachineState *machine)
>> +{
>> +    NPCM7xxState *soc;
>> +
>> +    soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
>> +    npcm7xx_connect_dram(soc, machine->ram);
>> +    qdev_realize(DEVICE(soc), NULL, &error_abort);
>> +
>> +    npcm7xx_load_kernel(machine, soc);
>> +}
>> +
>> +static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type)
>> +{
>> +    NPCM7xxClass *sc = NPCM7XX_CLASS(object_class_by_name(type));
>> +    MachineClass *mc = MACHINE_CLASS(nmc);
>> +
>> +    nmc->soc_type = type;
>> +    mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus;
>> +}
>> +
>> +static void npcm7xx_machine_class_init(ObjectClass *oc, void *data)
>> +{
>> +    MachineClass *mc = MACHINE_CLASS(oc);
>> +
>> +    mc->no_floppy       = 1;
>> +    mc->no_cdrom        = 1;
>> +    mc->no_parallel     = 1;
>> +    mc->default_ram_id  = "ram";

... this here:

        mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a9");

>> +}
>> +
>> +/*
>> + * Schematics:
>> + * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf
>> + */
>> +static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data)
>> +{
>> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
>> +    MachineClass *mc = MACHINE_CLASS(oc);
>> +
>> +    npcm7xx_set_soc_type(nmc, TYPE_NPCM750);
>> +
>> +    mc->desc            = "Nuvoton NPCM750 Evaluation Board (Cortex A9)";
>> +    mc->init            = npcm750_evb_init;
> 
> Either use 'var = value' or pad and align the '=' :)
> 
>> +    mc->default_ram_size = 512 * MiB;
>> +};
>> +
>> +static void gsj_machine_class_init(ObjectClass *oc, void *data)
>> +{
>> +    NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc);
>> +    MachineClass *mc = MACHINE_CLASS(oc);
>> +
>> +    npcm7xx_set_soc_type(nmc, TYPE_NPCM730);
>> +
>> +    mc->desc            = "Quanta GSJ (Cortex A9)";
>> +    mc->init            = quanta_gsj_init;
>> +    mc->default_ram_size = 512 * MiB;
>> +};
>> +
>> +static const TypeInfo npcm7xx_machine_types[] = {
>> +    {
>> +        .name           = TYPE_NPCM7XX_MACHINE,
>> +        .parent         = TYPE_MACHINE,
>> +        .instance_size  = sizeof(NPCM7xxMachine),
>> +        .class_size     = sizeof(NPCM7xxMachineClass),
>> +        .class_init     = npcm7xx_machine_class_init,
>> +        .abstract       = true,
>> +    }, {
>> +        .name           = MACHINE_TYPE_NAME("npcm750-evb"),
>> +        .parent         = TYPE_NPCM7XX_MACHINE,
>> +        .class_init     = npcm750_evb_machine_class_init,
>> +    }, {
>> +        .name           = MACHINE_TYPE_NAME("quanta-gsj"),
>> +        .parent         = TYPE_NPCM7XX_MACHINE,
>> +        .class_init     = gsj_machine_class_init,
>> +    },
>> +};
>> +
>> +DEFINE_TYPES(npcm7xx_machine_types)
>> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
>> index 13d163a599..c333548ce1 100644
>> --- a/hw/arm/Makefile.objs
>> +++ b/hw/arm/Makefile.objs
>> @@ -41,7 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
>>  obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
>>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
>>  obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
>> -obj-$(CONFIG_NPCM7XX) += npcm7xx.o
>> +obj-$(CONFIG_NPCM7XX) += npcm7xx.o npcm7xx_boards.o
>>  obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
>>  obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
>>  obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
>>
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-09  0:36 ` [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
@ 2020-07-09  6:11   ` Philippe Mathieu-Daudé
  2020-07-13 15:02   ` Cédric Le Goater
  1 sibling, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09  6:11 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, Joel Stanley, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
> Management Controllers in servers. While the family includes four SoCs,
> this patch implements limited support for two of them: NPCM730 (targeted
> for Data Center applications) and NPCM750 (targeted for Enterprise
> applications).
> 
> This patch includes little more than the bare minimum needed to boot a
> Linux kernel built with NPCM7xx support in direct-kernel mode:
> 
>   - Two Cortex-A9 CPU cores with built-in periperhals.
>   - Global Configuration Registers.
>   - Clock Management.
>   - 3 Timer Modules with 5 timers each.
>   - 4 serial ports.
> 
> The chips themselves have a lot more features, some of which will be
> added to the model at a later stage.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h |  86 +++++++++
>  hw/arm/npcm7xx.c         | 376 +++++++++++++++++++++++++++++++++++++++
>  hw/arm/Kconfig           |   5 +
>  hw/arm/Makefile.objs     |   1 +
>  4 files changed, 468 insertions(+)
>  create mode 100644 include/hw/arm/npcm7xx.h
>  create mode 100644 hw/arm/npcm7xx.c
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> new file mode 100644
> index 0000000000..95d9224f59
> --- /dev/null
> +++ b/include/hw/arm/npcm7xx.h
> @@ -0,0 +1,86 @@
> +/*
> + * Nuvoton NPCM7xx SoC family.
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_H
> +#define NPCM7XX_H
> +
> +#include "hw/boards.h"
> +#include "hw/cpu/a9mpcore.h"
> +#include "hw/misc/npcm7xx_clk.h"
> +#include "hw/misc/npcm7xx_gcr.h"
> +#include "hw/timer/npcm7xx_timer.h"
> +#include "target/arm/cpu.h"
> +
> +#define NPCM7XX_MAX_NUM_CPUS    (2)
> +
> +/* The first half of the address space is reserved for DDR4 DRAM. */
> +#define NPCM7XX_DRAM_BA         (0x00000000)
> +#define NPCM7XX_DRAM_SZ         (2 * GiB)
> +
> +/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
> +#define NPCM7XX_LOADER_START            (0x00000000)  /* Start of SDRAM */
> +#define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
> +#define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
> +#define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
> +
> +typedef struct NPCM7xxState {
> +    DeviceState         parent;
> +
> +    ARMCPU              cpu[NPCM7XX_MAX_NUM_CPUS];
> +    A9MPPrivState       a9mpcore;
> +
> +    MemoryRegion        sram;
> +    MemoryRegion        irom;
> +    MemoryRegion        ram3;
> +    MemoryRegion        *dram;
> +
> +    NPCM7xxGCRState     gcr;
> +    NPCM7xxCLKState     clk;
> +    NPCM7xxTimerCtrlState tim[3];
> +} NPCM7xxState;
> +
> +#define TYPE_NPCM7XX    "npcm7xx"
> +#define NPCM7XX(obj)    OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
> +
> +#define TYPE_NPCM730    "npcm730"
> +#define TYPE_NPCM750    "npcm750"
> +
> +typedef struct NPCM7xxClass {
> +    DeviceClass         parent;
> +
> +    /* Bitmask of modules that are permanently disabled on this chip. */
> +    uint32_t            disabled_modules;
> +    /* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
> +    uint32_t            num_cpus;
> +} NPCM7xxClass;
> +
> +#define NPCM7XX_CLASS(klass)                                            \
> +    OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
> +#define NPCM7XX_GET_CLASS(obj)                                          \
> +    OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
> +
> +/**
> + * npcm7xx_write_secondary_boot - Write stub for booting secondary CPU.
> + * @cpu: The CPU to be booted.
> + * @info: Boot info structure for the board.
> + *
> + * This will write a short code stub to the internal ROM that will keep the
> + * secondary CPU spinning until the primary CPU writes an address to the SCRPAD
> + * register in the GCR, after which the secondary CPU will jump there.
> + */
> +extern void npcm7xx_write_secondary_boot(ARMCPU *cpu,
> +                                         const struct arm_boot_info *info);

extern?! :)

> +
> +#endif /* NPCM7XX_H */
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> new file mode 100644
> index 0000000000..af45f3c716
> --- /dev/null
> +++ b/hw/arm/npcm7xx.c
> @@ -0,0 +1,376 @@
> +/*
> + * Nuvoton NPCM7xx SoC family.
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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 "exec/address-spaces.h"
> +#include "hw/arm/npcm7xx.h"
> +#include "hw/char/serial.h"
> +#include "hw/loader.h"
> +#include "hw/misc/unimp.h"
> +#include "hw/qdev-properties.h"
> +#include "qapi/error.h"
> +#include "qemu/units.h"
> +#include "sysemu/sysemu.h"
> +
> +/*
> + * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
> + * that aren't handled by any device.
> + */
> +#define NPCM7XX_MMIO_BA         (0x80000000)
> +#define NPCM7XX_MMIO_SZ         (0x7ffd0000)
> +
> +/* Core system modules. */
> +#define NPCM7XX_L2C_BA          (0xf03fc000)
> +#define NPCM7XX_CPUP_BA         (0xf03fe000)
> +#define NPCM7XX_GCR_BA          (0xf0800000)
> +#define NPCM7XX_CLK_BA          (0xf0801000)
> +
> +/* Internal AHB SRAM */
> +#define NPCM7XX_RAM3_BA         (0xc0008000)
> +#define NPCM7XX_RAM3_SZ         (4 * KiB)
> +
> +/* Memory blocks at the end of the address space */
> +#define NPCM7XX_RAM2_BA         (0xfffd0000)
> +#define NPCM7XX_RAM2_SZ         (128 * KiB)
> +#define NPCM7XX_ROM_BA          (0xffff0000)
> +#define NPCM7XX_ROM_SZ          (64 * KiB)
> +
> +/*
> + * Interrupt lines going into the GIC. This does not include internal Cortex-A9
> + * interrupts.
> + */
> +enum NPCM7xxInterrupt {
> +    NPCM7XX_UART0_IRQ           = 2,
> +    NPCM7XX_UART1_IRQ,
> +    NPCM7XX_UART2_IRQ,
> +    NPCM7XX_UART3_IRQ,
> +    NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
> +    NPCM7XX_TIMER1_IRQ,
> +    NPCM7XX_TIMER2_IRQ,
> +    NPCM7XX_TIMER3_IRQ,
> +    NPCM7XX_TIMER4_IRQ,
> +    NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
> +    NPCM7XX_TIMER6_IRQ,
> +    NPCM7XX_TIMER7_IRQ,
> +    NPCM7XX_TIMER8_IRQ,
> +    NPCM7XX_TIMER9_IRQ,
> +    NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
> +    NPCM7XX_TIMER11_IRQ,
> +    NPCM7XX_TIMER12_IRQ,
> +    NPCM7XX_TIMER13_IRQ,
> +    NPCM7XX_TIMER14_IRQ,
> +};
> +
> +/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
> +#define NPCM7XX_NUM_IRQ         (160)
> +
> +/* Register base address for each Timer Module */
> +static const hwaddr npcm7xx_tim_addr[] = {
> +    0xf0008000,
> +    0xf0009000,
> +    0xf000a000,
> +};
> +
> +/* Register base address for each 16550 UART */
> +static const hwaddr npcm7xx_uart_addr[] = {
> +    0xf0001000,
> +    0xf0002000,
> +    0xf0003000,
> +    0xf0004000,
> +};
> +
> +void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)

So this confirms my comment from v4, instead I'd keep it static, and add
npcm7xx_load_kernel().

> +{
> +    /*
> +     * The default smpboot stub halts the secondary CPU with a 'wfi'
> +     * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
> +     * does not send an IPI to wake it up, so the second CPU fails to boot. So
> +     * we need to provide our own smpboot stub that can not use 'wfi', it has
> +     * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
> +     */
> +    uint32_t smpboot[] = {
> +        0xe59f2018,     /* ldr r2, bootreg_addr */
> +        0xe3a00000,     /* mov r0, #0 */
> +        0xe5820000,     /* str r0, [r2] */
> +        0xe320f002,     /* wfe */
> +        0xe5921000,     /* ldr r1, [r2] */
> +        0xe1110001,     /* tst r1, r1 */
> +        0x0afffffb,     /* beq <wfe> */
> +        0xe12fff11,     /* bx r1 */
> +        NPCM7XX_SMP_BOOTREG_ADDR,
> +    };
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
> +        smpboot[i] = tswap32(smpboot[i]);
> +    }
> +
> +    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
> +                       NPCM7XX_SMP_LOADER_START);
> +}
> +
> +static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
> +{
> +    return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> +}
> +
> +static void npcm7xx_init(Object *obj)
> +{
> +    NPCM7xxState *s = NPCM7XX(obj);
> +    int i;
> +
> +    for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
> +        object_initialize_child(obj, "cpu[*]", &s->cpu[i],
> +                                ARM_CPU_TYPE_NAME("cortex-a9"));
> +    }
> +
> +    object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
> +    object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
> +    object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
> +                              "power-on-straps");
> +    object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> +
> +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> +        object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> +    }
> +}
> +
> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxState *s = NPCM7XX(dev);
> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> +    int i;
> +
> +    /* CPUs */
> +    for (i = 0; i < nc->num_cpus; i++) {
> +        object_property_set_int(OBJECT(&s->cpu[i]),
> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
> +                                "mp-affinity", &error_abort);
> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
> +                                "reset-cbar", &error_abort);
> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
> +                                 "reset-hivecs", &error_abort);
> +
> +        /* Disable security extensions. */
> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
> +                                 &error_abort);
> +
> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
> +    }
> +
> +    /* A9MPCORE peripherals */
> +    object_property_set_int(OBJECT(&s->a9mpcore), nc->num_cpus, "num-cpu",
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->a9mpcore), NPCM7XX_NUM_IRQ, "num-irq",
> +                            &error_abort);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
> +
> +    for (i = 0; i < nc->num_cpus; i++) {
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
> +    }
> +
> +    /* L2 cache controller */
> +    sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
> +
> +    /* System Global Control Registers (GCR) */
> +    object_property_set_int(OBJECT(&s->gcr), nc->disabled_modules,
> +                            "disabled-modules", &error_abort);
> +    object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
> +    sysbus_realize(SYS_BUS_DEVICE(&s->gcr), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
> +
> +    /* Clock Control Registers (CLK) */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
> +
> +    /* Timer Modules (TIM) */
> +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
> +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> +        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
> +        int first_irq;
> +        int j;
> +
> +        sysbus_realize(sbd, &error_abort);
> +        sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
> +
> +        first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
> +        for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
> +            qemu_irq irq = npcm7xx_irq(s, first_irq + j);
> +            sysbus_connect_irq(sbd, j, irq);
> +        }
> +    }
> +
> +    /* UART0..3 (16550 compatible) */
> +    for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
> +        serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
> +                       npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
> +                       serial_hd(i), DEVICE_LITTLE_ENDIAN);
> +    }
> +
> +    /* RAM2 (SRAM) */
> +    memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
> +                           NPCM7XX_RAM2_SZ, &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
> +
> +    /* RAM3 (SRAM) */
> +    memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
> +                           NPCM7XX_RAM3_SZ, &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
> +
> +    /* Internal ROM */
> +    memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
> +                           &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
> +
> +    create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.rng",          0xf000b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.adc",          0xf000c000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[0]",      0xf0010000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[1]",      0xf0011000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[2]",      0xf0012000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[3]",      0xf0013000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[4]",      0xf0014000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[5]",      0xf0015000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[6]",      0xf0016000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[7]",      0xf0017000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[0]",     0xf0080000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[1]",     0xf0081000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[2]",     0xf0082000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[3]",     0xf0083000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[4]",     0xf0084000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[5]",     0xf0085000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[6]",     0xf0086000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[7]",     0xf0087000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[8]",     0xf0088000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[9]",     0xf0089000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[10]",    0xf008a000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[11]",    0xf008b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[12]",    0xf008c000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[13]",    0xf008d000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[14]",    0xf008e000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[15]",    0xf008f000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pwm[0]",       0xf0103000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pwm[1]",       0xf0104000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
> +    create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.ehci",         0xf0806000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.ohci",         0xf0807000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.emc1",         0xf0825000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.emc2",         0xf0826000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
> +    create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
> +    create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
> +    create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);

Thanks a lot for this!

Without 'extern':
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +}
> +
> +static Property npcm7xx_properties[] = {
> +    DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
> +                     MemoryRegion *),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void npcm7xx_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = npcm7xx_realize;
> +    dc->user_creatable = false;
> +    device_class_set_props(dc, npcm7xx_properties);
> +}
> +
> +static void npcm730_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> +
> +    /* NPCM730 is optimized for data center use, so no graphics, etc. */
> +    nc->disabled_modules = 0x00300395;
> +    nc->num_cpus = 2;
> +}
> +
> +static void npcm750_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> +
> +    /* NPCM750 has 2 cores and a full set of peripherals */
> +    nc->disabled_modules = 0x00000000;
> +    nc->num_cpus = 2;
> +}
> +
> +static const TypeInfo npcm7xx_soc_types[] = {
> +    {
> +        .name           = TYPE_NPCM7XX,
> +        .parent         = TYPE_DEVICE,
> +        .instance_size  = sizeof(NPCM7xxState),
> +        .instance_init  = npcm7xx_init,
> +        .class_size     = sizeof(NPCM7xxClass),
> +        .class_init     = npcm7xx_class_init,
> +        .abstract       = true,
> +    }, {
> +        .name           = TYPE_NPCM730,
> +        .parent         = TYPE_NPCM7XX,
> +        .class_init     = npcm730_class_init,
> +    }, {
> +        .name           = TYPE_NPCM750,
> +        .parent         = TYPE_NPCM7XX,
> +        .class_init     = npcm750_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(npcm7xx_soc_types);
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 192a8dec3b..a31d0d282f 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -356,6 +356,11 @@ config XLNX_VERSAL
>  
>  config NPCM7XX
>      bool
> +    select A9MPCORE
> +    select ARM_GIC
> +    select PL310  # cache controller
> +    select SERIAL
> +    select UNIMP
>  
>  config FSL_IMX25
>      bool
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 534a6a119e..13d163a599 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -41,6 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
>  obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
>  obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
> +obj-$(CONFIG_NPCM7XX) += npcm7xx.o
>  obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
>  obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
>  obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09  6:04   ` Philippe Mathieu-Daudé
@ 2020-07-09  6:43     ` Havard Skinnemoen
  2020-07-09 16:23       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09  6:43 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> > Implement a device model for the System Global Control Registers in the
> > NPCM730 and NPCM750 BMC SoCs.
> >
> > This is primarily used to enable SMP boot (the boot ROM spins reading
> > the SCRPAD register) and DDR memory initialization; other registers are
> > best effort for now.
> >
> > The reset values of the MDLR and PWRON registers are determined by the
> > SoC variant (730 vs 750) and board straps respectively.
> >
> > Reviewed-by: Joel Stanley <joel@jms.id.au>
> > Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/misc/npcm7xx_gcr.h |  76 ++++++++++++
> >  hw/misc/npcm7xx_gcr.c         | 226 ++++++++++++++++++++++++++++++++++
> >  MAINTAINERS                   |   8 ++
> >  hw/arm/Kconfig                |   3 +
> >  hw/misc/Makefile.objs         |   1 +
> >  hw/misc/trace-events          |   4 +
> >  6 files changed, 318 insertions(+)
> >  create mode 100644 include/hw/misc/npcm7xx_gcr.h
> >  create mode 100644 hw/misc/npcm7xx_gcr.c
> >
> > diff --git a/include/hw/misc/npcm7xx_gcr.h b/include/hw/misc/npcm7xx_gcr.h
> > new file mode 100644
> > index 0000000000..4884676be2
> > --- /dev/null
> > +++ b/include/hw/misc/npcm7xx_gcr.h
> > @@ -0,0 +1,76 @@
> > +/*
> > + * Nuvoton NPCM7xx System Global 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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_GCR_H
> > +#define NPCM7XX_GCR_H
> > +
> > +#include "exec/memory.h"
> > +#include "hw/sysbus.h"
> > +
> > +enum NPCM7xxGCRRegisters {
> > +    NPCM7XX_GCR_PDID,
> > +    NPCM7XX_GCR_PWRON,
> > +    NPCM7XX_GCR_MFSEL1          = 0x0c / sizeof(uint32_t),
> > +    NPCM7XX_GCR_MFSEL2,
> > +    NPCM7XX_GCR_MISCPE,
> > +    NPCM7XX_GCR_SPSWC           = 0x038 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_INTCR,
> > +    NPCM7XX_GCR_INTSR,
> > +    NPCM7XX_GCR_HIFCR           = 0x050 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_INTCR2          = 0x060 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_MFSEL3,
> > +    NPCM7XX_GCR_SRCNT,
> > +    NPCM7XX_GCR_RESSR,
> > +    NPCM7XX_GCR_RLOCKR1,
> > +    NPCM7XX_GCR_FLOCKR1,
> > +    NPCM7XX_GCR_DSCNT,
> > +    NPCM7XX_GCR_MDLR,
> > +    NPCM7XX_GCR_SCRPAD3,
> > +    NPCM7XX_GCR_SCRPAD2,
> > +    NPCM7XX_GCR_DAVCLVLR        = 0x098 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_INTCR3,
> > +    NPCM7XX_GCR_VSINTR          = 0x0ac / sizeof(uint32_t),
> > +    NPCM7XX_GCR_MFSEL4,
> > +    NPCM7XX_GCR_CPBPNTR         = 0x0c4 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_CPCTL           = 0x0d0 / sizeof(uint32_t),
> > +    NPCM7XX_GCR_CP2BST,
> > +    NPCM7XX_GCR_B2CPNT,
> > +    NPCM7XX_GCR_CPPCTL,
> > +    NPCM7XX_GCR_I2CSEGSEL,
> > +    NPCM7XX_GCR_I2CSEGCTL,
> > +    NPCM7XX_GCR_VSRCR,
> > +    NPCM7XX_GCR_MLOCKR,
> > +    NPCM7XX_GCR_SCRPAD          = 0x013c / sizeof(uint32_t),
> > +    NPCM7XX_GCR_USB1PHYCTL,
> > +    NPCM7XX_GCR_USB2PHYCTL,
> > +    NPCM7XX_GCR_NR_REGS,
> > +};
> > +
> > +typedef struct NPCM7xxGCRState {
> > +    SysBusDevice parent;
> > +
> > +    MemoryRegion iomem;
> > +
> > +    uint32_t regs[NPCM7XX_GCR_NR_REGS];
> > +
> > +    uint32_t reset_pwron;
> > +    uint32_t reset_mdlr;
> > +    uint32_t reset_intcr3;
> > +} NPCM7xxGCRState;
> > +
> > +#define TYPE_NPCM7XX_GCR "npcm7xx-gcr"
> > +#define NPCM7XX_GCR(obj) OBJECT_CHECK(NPCM7xxGCRState, (obj), TYPE_NPCM7XX_GCR)
> > +
> > +#endif /* NPCM7XX_GCR_H */
> > diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
> > new file mode 100644
> > index 0000000000..9934cd238d
> > --- /dev/null
> > +++ b/hw/misc/npcm7xx_gcr.c
> > @@ -0,0 +1,226 @@
> > +/*
> > + * Nuvoton NPCM7xx System Global 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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_gcr.h"
> > +#include "hw/qdev-properties.h"
> > +#include "migration/vmstate.h"
> > +#include "qapi/error.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/units.h"
> > +
> > +#include "trace.h"
> > +
> > +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
> > +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
> > +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
> > +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
> > +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
> > +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
> > +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
> > +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
> > +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
> > +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
> > +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
> > +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
> > +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
> > +};
> > +
> > +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
> > +{
> > +    uint32_t reg = offset / sizeof(uint32_t);
> > +    NPCM7xxGCRState *s = opaque;
> > +
> > +    if (reg >= NPCM7XX_GCR_NR_REGS) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
> > +                      __func__, (unsigned int)offset);
>
> Maybe use HWADDR_PRIx instead of casting to int?

Will do, thanks!

>
> > +        return 0;
> > +    }
> > +
> > +    trace_npcm7xx_gcr_read(offset, s->regs[reg]);
> > +
> > +    return s->regs[reg];
> > +}
> > +
> > +static void npcm7xx_gcr_write(void *opaque, hwaddr offset,
> > +                              uint64_t v, unsigned size)
> > +{
> > +    uint32_t reg = offset / sizeof(uint32_t);
> > +    NPCM7xxGCRState *s = opaque;
> > +    uint32_t value = v;
> > +
> > +    trace_npcm7xx_gcr_write(offset, value);
> > +
> > +    if (reg >= NPCM7XX_GCR_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_GCR_PDID:
> > +    case NPCM7XX_GCR_PWRON:
> > +    case NPCM7XX_GCR_INTSR:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
> > +                      __func__, (unsigned int)offset);
> > +        return;
> > +
> > +    case NPCM7XX_GCR_RESSR:
> > +    case NPCM7XX_GCR_CP2BST:
> > +        /* Write 1 to clear */
> > +        value = s->regs[reg] & ~value;
> > +        break;
> > +
> > +    case NPCM7XX_GCR_RLOCKR1:
> > +    case NPCM7XX_GCR_MDLR:
> > +        /* Write 1 to set */
> > +        value |= s->regs[reg];
> > +        break;
> > +    };
> > +
> > +    s->regs[reg] = value;
> > +}
> > +
> > +static const struct MemoryRegionOps npcm7xx_gcr_ops = {
> > +    .read       = npcm7xx_gcr_read,
> > +    .write      = npcm7xx_gcr_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +static void npcm7xx_gcr_enter_reset(Object *obj, ResetType type)
>
> 2nd user of this new API, nice :)
>
> > +{
> > +    NPCM7xxGCRState *s = NPCM7XX_GCR(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(s->regs));
> > +        s->regs[NPCM7XX_GCR_PWRON] = s->reset_pwron;
> > +        s->regs[NPCM7XX_GCR_MDLR] = s->reset_mdlr;
> > +        s->regs[NPCM7XX_GCR_INTCR3] = s->reset_intcr3;
> > +        break;
> > +    }
> > +}
> > +
> > +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
> > +{
> > +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
> > +    uint64_t dram_size;
> > +    Error *err = NULL;
> > +    Object *obj;
> > +
> > +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
> > +    if (!obj) {
> > +        error_setg(errp, "%s: required dram-mr link not found: %s",
> > +                   __func__, error_get_pretty(err));
> > +        return;
> > +    }
> > +    dram_size = memory_region_size(MEMORY_REGION(obj));
> > +
> > +    /* Power-on reset value */
> > +    s->reset_intcr3 = 0x00001002;
> > +
> > +    /*
> > +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
> > +     * DRAM size, and is normally initialized by the boot block as part of DRAM
> > +     * training. However, since we don't have a complete emulation of the
> > +     * memory controller and try to make it look like it has already been
> > +     * initialized, the boot block will skip this initialization, and we need
> > +     * to make sure this field is set correctly up front.
> > +     *
> > +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
> > +     * more of DRAM will be interpreted as 128 MiB.
>
> I'd say u-boot is right in wrapping at 2GB, as more DRAM is
> un-addressable.

Ah, maybe I shouldn't have said "or more". The bug is that if you
specify _exactly_ 2GiB, u-boot will see 128 MiB.

But I agree more than 2GiB doesn't make sense, so I'll add a check for that.

Not sure if I agree that the check should be here. > 2 GiB is an
addressing limitation, and the GCR module shouldn't really know what
the SoC's address space looks like. The lower limit is because the GCR
module can't encode anything less than 128 MiB.

> > +     *
> > +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
> > +     */
> > +    if (dram_size >= 2 * GiB) {
>
> See my comment in this series on the machine patch.
>
> > +        s->reset_intcr3 |= 4 << 8;
> > +    } else if (dram_size >= 1 * GiB) {
> > +        s->reset_intcr3 |= 3 << 8;
> > +    } else if (dram_size >= 512 * MiB) {
> > +        s->reset_intcr3 |= 2 << 8;
> > +    } else if (dram_size >= 256 * MiB) {
> > +        s->reset_intcr3 |= 1 << 8;
> > +    } else if (dram_size >= 128 * MiB) {
> > +        s->reset_intcr3 |= 0 << 8;
> > +    } else {
> > +        error_setg(errp,
> > +                   "npcm7xx_gcr: DRAM size %" PRIu64
> > +                   " is too small (need 128 MiB minimum)",
> > +                   dram_size);
>
> Ah, so you could add the same error for >2GB. Easier.
>
> Preferably using HWADDR_PRIx, and similar error for >2GB:
> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
>
> > +        return;
> > +    }
> > +}
> > +
> > +static void npcm7xx_gcr_init(Object *obj)
> > +{
> > +    NPCM7xxGCRState *s = NPCM7XX_GCR(obj);
> > +
> > +    memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s,
> > +                          TYPE_NPCM7XX_GCR, 4 * KiB);
> > +    sysbus_init_mmio(&s->parent, &s->iomem);
> > +}
> > +
> > +static const VMStateDescription vmstate_npcm7xx_gcr = {
> > +    .name = "npcm7xx-gcr",
> > +    .version_id = 0,
> > +    .minimum_version_id = 0,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxGCRState, NPCM7XX_GCR_NR_REGS),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static Property npcm7xx_gcr_properties[] = {
> > +    DEFINE_PROP_UINT32("disabled-modules", NPCM7xxGCRState, reset_mdlr, 0),
> > +    DEFINE_PROP_UINT32("power-on-straps", NPCM7xxGCRState, reset_pwron, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data)
> > +{
> > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->desc = "NPCM7xx System Global Control Registers";
> > +    dc->realize = npcm7xx_gcr_realize;
> > +    dc->vmsd = &vmstate_npcm7xx_gcr;
> > +    rc->phases.enter = npcm7xx_gcr_enter_reset;
> > +
> > +    device_class_set_props(dc, npcm7xx_gcr_properties);
> > +}
> > +
> > +static const TypeInfo npcm7xx_gcr_info = {
> > +    .name               = TYPE_NPCM7XX_GCR,
> > +    .parent             = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size      = sizeof(NPCM7xxGCRState),
> > +    .instance_init      = npcm7xx_gcr_init,
> > +    .class_init         = npcm7xx_gcr_class_init,
> > +};
> > +
> > +static void npcm7xx_gcr_register_type(void)
> > +{
> > +    type_register_static(&npcm7xx_gcr_info);
> > +}
> > +type_init(npcm7xx_gcr_register_type);
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 42388f1de2..43173a338a 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -723,6 +723,14 @@ S: Odd Fixes
> >  F: hw/arm/musicpal.c
> >  F: docs/system/arm/musicpal.rst
> >
> > +Nuvoton NPCM7xx
> > +M: Havard Skinnemoen <hskinnemoen@google.com>
> > +M: Tyrone Ting <kfting@nuvoton.com>
> > +L: qemu-arm@nongnu.org
> > +S: Supported
> > +F: hw/*/npcm7xx*
> > +F: include/hw/*/npcm7xx*
> > +
> >  nSeries
> >  M: Andrzej Zaborowski <balrogg@gmail.com>
> >  M: Peter Maydell <peter.maydell@linaro.org>
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index 4a224a6351..192a8dec3b 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -354,6 +354,9 @@ config XLNX_VERSAL
> >      select VIRTIO_MMIO
> >      select UNIMP
> >
> > +config NPCM7XX
> > +    bool
> > +
> >  config FSL_IMX25
> >      bool
> >      select IMX
> > diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
> > index 5aaca8a039..40a9d1c01e 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_gcr.o
> >  common-obj-$(CONFIG_OMAP) += omap_clk.o
> >  common-obj-$(CONFIG_OMAP) += omap_gpmc.o
> >  common-obj-$(CONFIG_OMAP) += omap_l4.o
> > diff --git a/hw/misc/trace-events b/hw/misc/trace-events
> > index ebea53735c..48e2d54c49 100644
> > --- a/hw/misc/trace-events
> > +++ b/hw/misc/trace-events
> > @@ -107,6 +107,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_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
> > +
> >  # stm32f4xx_syscfg
> >  stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %d, Line: %d; Level: %d"
> >  stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d"
> >


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09  6:43     ` Havard Skinnemoen
@ 2020-07-09 16:23       ` Philippe Mathieu-Daudé
  2020-07-09 17:09         ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09 16:23 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing,
	Cédric Le Goater, qemu-arm, Joel Stanley, IS20 Avi Fishman

On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
>>> Implement a device model for the System Global Control Registers in the
>>> NPCM730 and NPCM750 BMC SoCs.
>>>
>>> This is primarily used to enable SMP boot (the boot ROM spins reading
>>> the SCRPAD register) and DDR memory initialization; other registers are
>>> best effort for now.
>>>
>>> The reset values of the MDLR and PWRON registers are determined by the
>>> SoC variant (730 vs 750) and board straps respectively.
>>>
>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>> ---
[...]
>>> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
>>> new file mode 100644
>>> index 0000000000..9934cd238d
>>> --- /dev/null
>>> +++ b/hw/misc/npcm7xx_gcr.c
>>> @@ -0,0 +1,226 @@
>>> +/*
>>> + * Nuvoton NPCM7xx System Global 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 as published by the
>>> + * Free Software Foundation; either version 2 of the License, or
>>> + * (at your option) any later version.
>>> + *
>>> + * 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_gcr.h"
>>> +#include "hw/qdev-properties.h"
>>> +#include "migration/vmstate.h"
>>> +#include "qapi/error.h"
>>> +#include "qemu/log.h"
>>> +#include "qemu/module.h"
>>> +#include "qemu/units.h"
>>> +
>>> +#include "trace.h"
>>> +
>>> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
>>> +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
>>> +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
>>> +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
>>> +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
>>> +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
>>> +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
>>> +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
>>> +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
>>> +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
>>> +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
>>> +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
>>> +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
>>> +};
>>> +
>>> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
>>> +{
>>> +    uint32_t reg = offset / sizeof(uint32_t);
>>> +    NPCM7xxGCRState *s = opaque;
>>> +
>>> +    if (reg >= NPCM7XX_GCR_NR_REGS) {
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
>>> +                      __func__, (unsigned int)offset);
>>
>> Maybe use HWADDR_PRIx instead of casting to int?
> 
> Will do, thanks!

Glad you are not annoyed by my review comments.

FYI your series quality is in my top-4 "adding new machine to QEMU".
I'd like to use it as example to follow.

I am slowly reviewing because this is not part of my work duties,
so I do that in my free time before/after work, which is why I can
barely do more that 2 per day, as I have to learn the SoC and
cross check documentation (or in this case, existing firmware code
since there is no public doc).

[...]
>>> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
>>> +    uint64_t dram_size;
>>> +    Error *err = NULL;
>>> +    Object *obj;
>>> +
>>> +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
>>> +    if (!obj) {
>>> +        error_setg(errp, "%s: required dram-mr link not found: %s",
>>> +                   __func__, error_get_pretty(err));
>>> +        return;
>>> +    }
>>> +    dram_size = memory_region_size(MEMORY_REGION(obj));
>>> +
>>> +    /* Power-on reset value */
>>> +    s->reset_intcr3 = 0x00001002;
>>> +
>>> +    /*
>>> +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
>>> +     * DRAM size, and is normally initialized by the boot block as part of DRAM
>>> +     * training. However, since we don't have a complete emulation of the
>>> +     * memory controller and try to make it look like it has already been
>>> +     * initialized, the boot block will skip this initialization, and we need
>>> +     * to make sure this field is set correctly up front.
>>> +     *
>>> +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
>>> +     * more of DRAM will be interpreted as 128 MiB.
>>
>> I'd say u-boot is right in wrapping at 2GB, as more DRAM is
>> un-addressable.
> 
> Ah, maybe I shouldn't have said "or more". The bug is that if you
> specify _exactly_ 2GiB, u-boot will see 128 MiB.
> 
> But I agree more than 2GiB doesn't make sense, so I'll add a check for that.
> 
> Not sure if I agree that the check should be here. > 2 GiB is an
> addressing limitation, and the GCR module shouldn't really know what
> the SoC's address space looks like. The lower limit is because the GCR
> module can't encode anything less than 128 MiB.
> 
>>> +     *
>>> +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
>>> +     */
>>> +    if (dram_size >= 2 * GiB) {
>>
>> See my comment in this series on the machine patch.
>>
>>> +        s->reset_intcr3 |= 4 << 8;
>>> +    } else if (dram_size >= 1 * GiB) {
>>> +        s->reset_intcr3 |= 3 << 8;
>>> +    } else if (dram_size >= 512 * MiB) {
>>> +        s->reset_intcr3 |= 2 << 8;
>>> +    } else if (dram_size >= 256 * MiB) {
>>> +        s->reset_intcr3 |= 1 << 8;
>>> +    } else if (dram_size >= 128 * MiB) {
>>> +        s->reset_intcr3 |= 0 << 8;

I think this can be simplified as:

         s->reset_intcr3 = (4 - clz32(dram_size)) << 8;

Which implicitly truncate to power of 2 (which is what this
block does, as you can have only 1 bank managed per this SGC).

But checking is_power_of_2(dram_size) and reporting an error
else, is probably even better.

>>> +    } else {
>>> +        error_setg(errp,
>>> +                   "npcm7xx_gcr: DRAM size %" PRIu64
>>> +                   " is too small (need 128 MiB minimum)",
>>> +                   dram_size);
>>
>> Ah, so you could add the same error for >2GB. Easier.
>>
>> Preferably using HWADDR_PRIx, and similar error for >2GB:
>> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model
  2020-07-09  0:36 ` [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen
@ 2020-07-09 16:29   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09 16:29 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, Cédric Le Goater, qemu-arm, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> This just implements the bare minimum to cause the boot block to skip
> memory initialization.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h    |  2 +
>  include/hw/mem/npcm7xx_mc.h | 36 ++++++++++++++++
>  hw/arm/npcm7xx.c            |  6 +++
>  hw/mem/npcm7xx_mc.c         | 84 +++++++++++++++++++++++++++++++++++++
>  hw/mem/Makefile.objs        |  1 +
>  5 files changed, 129 insertions(+)
>  create mode 100644 include/hw/mem/npcm7xx_mc.h
>  create mode 100644 hw/mem/npcm7xx_mc.c
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> index 0fd4ae4133..3ae9e5dca2 100644
> --- a/include/hw/arm/npcm7xx.h
> +++ b/include/hw/arm/npcm7xx.h
> @@ -18,6 +18,7 @@
>  
>  #include "hw/boards.h"
>  #include "hw/cpu/a9mpcore.h"
> +#include "hw/mem/npcm7xx_mc.h"
>  #include "hw/misc/npcm7xx_clk.h"
>  #include "hw/misc/npcm7xx_gcr.h"
>  #include "hw/nvram/npcm7xx_otp.h"
> @@ -71,6 +72,7 @@ typedef struct NPCM7xxState {
>      NPCM7xxTimerCtrlState tim[3];
>      NPCM7xxOTPState     key_storage;
>      NPCM7xxOTPState     fuse_array;
> +    NPCM7xxMCState      mc;
>  } NPCM7xxState;
>  
>  #define TYPE_NPCM7XX    "npcm7xx"
> diff --git a/include/hw/mem/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h
> new file mode 100644
> index 0000000000..7ed38be243
> --- /dev/null
> +++ b/include/hw/mem/npcm7xx_mc.h
> @@ -0,0 +1,36 @@
> +/*
> + * Nuvoton NPCM7xx Memory Controller stub
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_MC_H
> +#define NPCM7XX_MC_H
> +
> +#include "exec/memory.h"
> +#include "hw/sysbus.h"
> +
> +/**
> + * struct NPCM7xxMCState - Device state for the memory controller.
> + * @parent: System bus device.
> + * @mmio: Memory region through which registers are accessed.
> + */
> +typedef struct NPCM7xxMCState {
> +    SysBusDevice parent;
> +
> +    MemoryRegion mmio;
> +} NPCM7xxMCState;
> +
> +#define TYPE_NPCM7XX_MC "npcm7xx-mc"
> +#define NPCM7XX_MC(obj) OBJECT_CHECK(NPCM7xxMCState, (obj), TYPE_NPCM7XX_MC)
> +
> +#endif /* NPCM7XX_MC_H */
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index 70eaa2347f..4d227bb74b 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -42,6 +42,7 @@
>  #define NPCM7XX_CPUP_BA         (0xf03fe000)
>  #define NPCM7XX_GCR_BA          (0xf0800000)
>  #define NPCM7XX_CLK_BA          (0xf0801000)
> +#define NPCM7XX_MC_BA           (0xf0824000)
>  
>  /* Internal AHB SRAM */
>  #define NPCM7XX_RAM3_BA         (0xc0008000)
> @@ -165,6 +166,7 @@ static void npcm7xx_init(Object *obj)
>                              TYPE_NPCM7XX_KEY_STORAGE);
>      object_initialize_child(obj, "otp2", &s->fuse_array,
>                              TYPE_NPCM7XX_FUSE_ARRAY);
> +    object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC);
>  
>      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
>          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> @@ -230,6 +232,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
>      sysbus_mmio_map(SYS_BUS_DEVICE(&s->fuse_array), 0, NPCM7XX_OTP2_BA);
>      npcm7xx_init_fuses(s);
>  
> +    /* Fake Memory Controller (MC) */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA);
> +
>      /* Timer Modules (TIM) */
>      QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
>      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
> new file mode 100644
> index 0000000000..0435d06ab4
> --- /dev/null
> +++ b/hw/mem/npcm7xx_mc.c
> @@ -0,0 +1,84 @@
> +/*
> + * Nuvoton NPCM7xx Memory Controller stub
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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/mem/npcm7xx_mc.h"
> +#include "qapi/error.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +
> +#define NPCM7XX_MC_REGS_SIZE (4 * KiB)
> +
> +static uint64_t npcm7xx_mc_read(void *opaque, hwaddr addr, unsigned int size)
> +{
> +    /*
> +     * If bits 8..11 @ offset 0 are not zero, the boot block thinks the memory
> +     * controller has already been initialized and will skip DDR training.
> +     */
> +    if (addr == 0) {
> +        return 0x100;

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +    }
> +
> +    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
> +
> +    return 0;
> +}
> +
> +static void npcm7xx_mc_write(void *opaque, hwaddr addr, uint64_t v,
> +                             unsigned int size)
> +{
> +    qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
> +}
> +
> +static const MemoryRegionOps npcm7xx_mc_ops = {
> +    .read = npcm7xx_mc_read,
> +    .write = npcm7xx_mc_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +        .unaligned = false,
> +    },
> +};
> +
> +static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxMCState *s = NPCM7XX_MC(dev);
> +
> +    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs",
> +                          NPCM7XX_MC_REGS_SIZE);
> +    sysbus_init_mmio(&s->parent, &s->mmio);
> +}
> +
> +static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc = "NPCM7xx Memory Controller stub";
> +    dc->realize = npcm7xx_mc_realize;
> +}
> +
> +static const TypeInfo npcm7xx_mc_types[] = {
> +    {
> +        .name = TYPE_NPCM7XX_MC,
> +        .parent = TYPE_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(NPCM7xxMCState),
> +        .class_init = npcm7xx_mc_class_init,
> +    },
> +};
> +DEFINE_TYPES(npcm7xx_mc_types);
> diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs
> index 56345befd0..9a33ef7b35 100644
> --- a/hw/mem/Makefile.objs
> +++ b/hw/mem/Makefile.objs
> @@ -1,3 +1,4 @@
>  common-obj-$(CONFIG_DIMM) += pc-dimm.o
>  common-obj-y += memory-device.o
> +common-obj-$(CONFIG_NPCM7XX) += npcm7xx_mc.o
>  common-obj-$(CONFIG_NVDIMM) += nvdimm.o
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-07-09  0:36 ` [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen
@ 2020-07-09 17:00   ` Philippe Mathieu-Daudé
  2020-07-12  5:42     ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09 17:00 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, Cédric Le Goater, qemu-arm, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> This implements a device model for the NPCM7xx SPI flash controller.
> 
> Direct reads and writes, and user-mode transactions have been tested in
> various modes. Protection features are not implemented yet.
> 
> All the FIU instances are available in the SoC's address space,
> regardless of whether or not they're connected to actual flash chips.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h     |   2 +
>  include/hw/ssi/npcm7xx_fiu.h | 100 +++++++
>  hw/arm/npcm7xx.c             |  53 ++++
>  hw/ssi/npcm7xx_fiu.c         | 510 +++++++++++++++++++++++++++++++++++
>  hw/arm/Kconfig               |   1 +
>  hw/ssi/Makefile.objs         |   1 +
>  hw/ssi/trace-events          |   9 +
>  7 files changed, 676 insertions(+)
>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
>  create mode 100644 hw/ssi/npcm7xx_fiu.c
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> index 3ae9e5dca2..bc99f47286 100644
> --- a/include/hw/arm/npcm7xx.h
> +++ b/include/hw/arm/npcm7xx.h
> @@ -23,6 +23,7 @@
>  #include "hw/misc/npcm7xx_gcr.h"
>  #include "hw/nvram/npcm7xx_otp.h"
>  #include "hw/timer/npcm7xx_timer.h"
> +#include "hw/ssi/npcm7xx_fiu.h"
>  #include "target/arm/cpu.h"
>  
>  #define NPCM7XX_MAX_NUM_CPUS    (2)
> @@ -73,6 +74,7 @@ typedef struct NPCM7xxState {
>      NPCM7xxOTPState     key_storage;
>      NPCM7xxOTPState     fuse_array;
>      NPCM7xxMCState      mc;
> +    NPCM7xxFIUState     fiu[2];
>  } NPCM7xxState;
>  
>  #define TYPE_NPCM7XX    "npcm7xx"
> diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h
> new file mode 100644
> index 0000000000..b867bd0429
> --- /dev/null
> +++ b/include/hw/ssi/npcm7xx_fiu.h
> @@ -0,0 +1,100 @@
> +/*
> + * Nuvoton NPCM7xx Flash Interface Unit (FIU)
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_FIU_H
> +#define NPCM7XX_FIU_H
> +
> +#include "hw/ssi/ssi.h"
> +#include "hw/sysbus.h"
> +
> +/**
> + * enum NPCM7xxFIURegister - 32-bit FIU register indices.
> + */
> +enum NPCM7xxFIURegister {
> +    NPCM7XX_FIU_DRD_CFG,
> +    NPCM7XX_FIU_DWR_CFG,
> +    NPCM7XX_FIU_UMA_CFG,
> +    NPCM7XX_FIU_UMA_CTS,
> +    NPCM7XX_FIU_UMA_CMD,
> +    NPCM7XX_FIU_UMA_ADDR,
> +    NPCM7XX_FIU_PRT_CFG,
> +    NPCM7XX_FIU_UMA_DW0 = 0x0020 / sizeof(uint32_t),
> +    NPCM7XX_FIU_UMA_DW1,
> +    NPCM7XX_FIU_UMA_DW2,
> +    NPCM7XX_FIU_UMA_DW3,
> +    NPCM7XX_FIU_UMA_DR0,
> +    NPCM7XX_FIU_UMA_DR1,
> +    NPCM7XX_FIU_UMA_DR2,
> +    NPCM7XX_FIU_UMA_DR3,
> +    NPCM7XX_FIU_PRT_CMD0,
> +    NPCM7XX_FIU_PRT_CMD1,
> +    NPCM7XX_FIU_PRT_CMD2,
> +    NPCM7XX_FIU_PRT_CMD3,
> +    NPCM7XX_FIU_PRT_CMD4,
> +    NPCM7XX_FIU_PRT_CMD5,
> +    NPCM7XX_FIU_PRT_CMD6,
> +    NPCM7XX_FIU_PRT_CMD7,
> +    NPCM7XX_FIU_PRT_CMD8,
> +    NPCM7XX_FIU_PRT_CMD9,
> +    NPCM7XX_FIU_CFG = 0x78 / sizeof(uint32_t),
> +    NPCM7XX_FIU_NR_REGS,
> +};
> +
> +typedef struct NPCM7xxFIUState NPCM7xxFIUState;
> +
> +/**
> + * struct NPCM7xxFIUFlash - Per-chipselect flash controller state.
> + * @direct_access: Memory region for direct flash access.
> + * @fiu: Pointer to flash controller shared state.
> + */
> +typedef struct NPCM7xxFIUFlash {
> +    MemoryRegion direct_access;
> +    NPCM7xxFIUState *fiu;
> +} NPCM7xxFIUFlash;
> +
> +/**
> + * NPCM7xxFIUState - Device state for one Flash Interface Unit.
> + * @parent: System bus device.
> + * @mmio: Memory region for register access.
> + * @cs_count: Number of flash chips that may be connected to this module.
> + * @active_cs: Currently active chip select, or -1 if no chip is selected.
> + * @cs_lines: GPIO lines that may be wired to flash chips.
> + * @flash: Array of @cs_count per-flash-chip state objects.
> + * @spi: The SPI bus mastered by this controller.
> + * @regs: Register contents.
> + *
> + * Each FIU has a shared bank of registers, and controls up to four chip
> + * selects. Each chip select has a dedicated memory region which may be used to
> + * read and write the flash connected to that chip select as if it were memory.
> + */
> +struct NPCM7xxFIUState {
> +    SysBusDevice parent;
> +
> +    MemoryRegion mmio;
> +
> +    int32_t cs_count;
> +    int32_t active_cs;
> +    qemu_irq *cs_lines;
> +    NPCM7xxFIUFlash *flash;
> +
> +    SSIBus *spi;
> +
> +    uint32_t regs[NPCM7XX_FIU_NR_REGS];
> +};
> +
> +#define TYPE_NPCM7XX_FIU "npcm7xx-fiu"
> +#define NPCM7XX_FIU(obj) OBJECT_CHECK(NPCM7xxFIUState, (obj), TYPE_NPCM7XX_FIU)
> +
> +#endif /* NPCM7XX_FIU_H */
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index 4d227bb74b..c9ff3dab25 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -98,6 +98,37 @@ static const hwaddr npcm7xx_uart_addr[] = {
>      0xf0004000,
>  };
>  
> +static const hwaddr npcm7xx_fiu0_flash_addr[] = {

So per
https://github.com/Nuvoton-Israel/bootblock/blob/master/SWC_HAL/Chips/npcm750/npcm750.h
this is SPI0 on AHB18,

> +    0x80000000,
> +    0x88000000,

CS0 & CS1,

also listed:

0x90000000, // CS2
0x98000000, // CS3

> +};
> +
> +static const hwaddr npcm7xx_fiu3_flash_addr[] = {

Ditto SPI3 on AHB3, and CS0 to CS3.

> +    0xa0000000,
> +    0xa8000000,
> +    0xb0000000,
> +    0xb8000000,
> +};
> +
> +static const struct {
> +    const char *name;
> +    hwaddr regs_addr;
> +    int cs_count;
> +    const hwaddr *flash_addr;
> +} npcm7xx_fiu[] = {
> +    {
> +        .name = "fiu0",
> +        .regs_addr = 0xfb000000,
> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),

Hmm without the datasheet, can't tell, but I'd expect 4 CS
regardless.

> +        .flash_addr = npcm7xx_fiu0_flash_addr,
> +    }, {
> +        .name = "fiu3",
> +        .regs_addr = 0xc0000000,
> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
> +        .flash_addr = npcm7xx_fiu3_flash_addr,
> +    },
> +};
> +
>  void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
>  {
>      /*
> @@ -171,6 +202,12 @@ static void npcm7xx_init(Object *obj)
>      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
>          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
>      }
> +
> +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
> +    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
> +        object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
> +                                TYPE_NPCM7XX_FIU);
> +    }
>  }
>  
>  static void npcm7xx_realize(DeviceState *dev, Error **errp)
> @@ -260,6 +297,22 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
>                         serial_hd(i), DEVICE_LITTLE_ENDIAN);
>      }
>  
> +    /* Flash Interface Unit (FIU) */
> +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
> +    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
> +        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
> +        int j;
> +
> +        object_property_set_int(OBJECT(sbd), npcm7xx_fiu[i].cs_count,
> +                                "cs-count", &error_abort);
> +        sysbus_realize(sbd, &error_abort);
> +
> +        sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
> +        for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
> +            sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
> +        }
> +    }
> +
>      /* RAM2 (SRAM) */
>      memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
>                             NPCM7XX_RAM2_SZ, &error_abort);
> diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
> new file mode 100644
> index 0000000000..92ade709e8
> --- /dev/null
> +++ b/hw/ssi/npcm7xx_fiu.c
> @@ -0,0 +1,510 @@
> +/*
> + * Nuvoton NPCM7xx Flash Interface Unit (FIU)
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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/irq.h"
> +#include "hw/qdev-properties.h"
> +#include "hw/ssi/npcm7xx_fiu.h"
> +#include "migration/vmstate.h"
> +#include "qapi/error.h"
> +#include "qemu/error-report.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/units.h"
> +
> +#include "trace.h"
> +
> +/* Up to 128 MiB of flash may be accessed directly as memory. */
> +#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
> +
> +/* Each module has 4 KiB of register space. Only a fraction of it is used. */
> +#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
> +
> +/* FIU_{DRD,DWR,UMA,PTR}_CFG cannot be written when this bit is set. */
> +#define NPCM7XX_FIU_CFG_LCK BIT(31)
> +
> +/* Direct Read configuration register fields. */
> +#define FIU_DRD_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
> +#define FIU_ADDSIZ_3BYTES 0
> +#define FIU_ADDSIZ_4BYTES 1
> +#define FIU_DRD_CFG_DBW(rv) extract32(rv, 12, 2)
> +#define FIU_DRD_CFG_ACCTYPE(rv) extract32(rv, 8, 2)
> +#define FIU_DRD_CFG_RDCMD(rv) extract32(rv, 0, 8)
> +
> +/* Direct Write configuration register fields. */
> +#define FIU_DWR_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
> +#define FIU_DWR_CFG_WRCMD(rv) extract32(rv, 0, 8)
> +
> +/* User-Mode Access register fields. */
> +
> +/* Command Mode Lock and the bits protected by it. */
> +#define FIU_UMA_CFG_CMMLCK BIT(30)
> +#define FIU_UMA_CFG_CMMLCK_MASK 0x00000403
> +
> +#define FIU_UMA_CFG_RDATSIZ(rv) extract32(rv, 24, 5)
> +#define FIU_UMA_CFG_DBSIZ(rv) extract32(rv, 21, 3)
> +#define FIU_UMA_CFG_WDATSIZ(rv) extract32(rv, 16, 5)
> +#define FIU_UMA_CFG_ADDSIZ(rv) extract32(rv, 11, 3)
> +#define FIU_UMA_CFG_CMDSIZ(rv) extract32(rv, 10, 1)
> +#define FIU_UMA_CFG_DBPCK(rv) extract32(rv, 6, 2)
> +
> +#define FIU_UMA_CTS_RDYIE BIT(25)
> +#define FIU_UMA_CTS_RDYST BIT(24)
> +#define FIU_UMA_CTS_SW_CS BIT(16)
> +#define FIU_UMA_CTS_DEV_NUM(rv) extract32(rv, 8, 2)
> +#define FIU_UMA_CTS_EXEC_DONE BIT(0)
> +
> +/* Direct flash memory read handler. */
> +static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
> +                                       unsigned int size)
> +{
> +    NPCM7xxFIUFlash *f = opaque;
> +    NPCM7xxFIUState *fiu = f->fiu;
> +    int cs_id = f - fiu->flash;

Hmmm? Not obvious.

> +    uint64_t value = 0;
> +    uint32_t drd_cfg;
> +    int dummy_cycles;
> +    int i;
> +
> +    drd_cfg = fiu->regs[NPCM7XX_FIU_DRD_CFG];
> +
> +    qemu_irq_lower(fiu->cs_lines[cs_id]);

Isn't it cleaner to use npcm7xx_fiu_select() here?

When can we have 'fio->active_cs != cs_id'?

BTW I'd add a trace event in select/deselect().

> +    ssi_transfer(fiu->spi, FIU_DRD_CFG_RDCMD(drd_cfg));
> +
> +    switch (FIU_DRD_CFG_ADDSIZ(drd_cfg)) {
> +    case FIU_ADDSIZ_4BYTES:
> +        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
> +        /* fall through */
> +    case FIU_ADDSIZ_3BYTES:
> +        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
> +        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
> +        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
> +                      DEVICE(fiu)->canonical_path, FIU_DRD_CFG_ADDSIZ(drd_cfg));
> +        break;
> +    }
> +
> +    /* Flash chip model expects one transfer per dummy bit, not byte */
> +    dummy_cycles =
> +        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
> +    for (i = 0; i < dummy_cycles; i++) {
> +        ssi_transfer(fiu->spi, 0);

Note to self, might worth a ssi_shift_dummy(count) generic method.

> +    }
> +
> +    for (i = 0; i < size; i++) {
> +        value |= ssi_transfer(fiu->spi, 0) << (8 * i);
> +    }
> +

Ditto npcm7xx_fiu_deselect().

> +    qemu_irq_raise(fiu->cs_lines[cs_id]);
> +
> +    trace_npcm7xx_fiu_flash_read(DEVICE(fiu)->canonical_path, cs_id, addr, size,
> +                                 value);
> +
> +    return value;
> +}
> +
> +/* Direct flash memory write handler. */
> +static void npcm7xx_fiu_flash_write(void *opaque, hwaddr addr, uint64_t v,
> +                                    unsigned int size)
> +{
> +    NPCM7xxFIUFlash *f = opaque;
> +    NPCM7xxFIUState *fiu = f->fiu;
> +    int cs_id = f - fiu->flash;
> +    uint32_t dwr_cfg;
> +    int i;
> +
> +    trace_npcm7xx_fiu_flash_write(DEVICE(fiu)->canonical_path, cs_id, addr,
> +                                  size, v);
> +
> +    dwr_cfg = fiu->regs[NPCM7XX_FIU_DWR_CFG];
> +
> +    qemu_irq_lower(fiu->cs_lines[cs_id]);
> +    ssi_transfer(fiu->spi, FIU_DWR_CFG_WRCMD(dwr_cfg));
> +
> +    switch (FIU_DWR_CFG_ADDSIZ(dwr_cfg)) {
> +    case FIU_ADDSIZ_4BYTES:
> +        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
> +        /* fall through */
> +    case FIU_ADDSIZ_3BYTES:
> +        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
> +        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
> +        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
> +                      DEVICE(fiu)->canonical_path, FIU_DWR_CFG_ADDSIZ(dwr_cfg));
> +        break;
> +    }
> +
> +    for (i = 0; i < size; i++) {
> +        ssi_transfer(fiu->spi, v & 0xff);
> +        v >>= 8;

Or since you use deposit32() in transaction():

           extract32(v, 8 * i, 8);

> +    }
> +
> +    qemu_irq_raise(fiu->cs_lines[cs_id]);
> +}
> +
> +static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
> +    .read = npcm7xx_fiu_flash_read,
> +    .write = npcm7xx_fiu_flash_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 1,
> +        .max_access_size = 8,

Are you sure? Maybe, I can' tell.

> +        .unaligned = true,
> +    },
> +};
> +
> +/* Control register read handler. */
> +static uint64_t npcm7xx_fiu_ctrl_read(void *opaque, hwaddr addr,
> +                                      unsigned int size)
> +{
> +    hwaddr reg = addr / sizeof(uint32_t);
> +    NPCM7xxFIUState *s = opaque;
> +    uint32_t value;
> +
> +    if (reg < NPCM7XX_FIU_NR_REGS) {
> +        value = s->regs[reg];
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: read from invalid offset 0x%" PRIx64 "\n",
> +                      DEVICE(s)->canonical_path, addr);
> +        value = 0;
> +    }
> +
> +    trace_npcm7xx_fiu_ctrl_read(DEVICE(s)->canonical_path, addr, value);
> +
> +    return value;
> +}
> +
> +/* Send the specified number of address bytes from the UMA address register. */
> +static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
> +{
> +    /* All cases fall through */

Is this comment recognized by -Wimplicit-fallthrough?

> +    switch (addsiz) {
> +    case 4:
> +        ssi_transfer(spi, extract32(addr, 24, 8));
> +    case 3:
> +        ssi_transfer(spi, extract32(addr, 16, 8));
> +    case 2:
> +        ssi_transfer(spi, extract32(addr, 8, 8));
> +    case 1:
> +        ssi_transfer(spi, extract32(addr, 0, 8));
> +    case 0:
> +        break;
> +    }
> +}
> +
> +/* Send the number of dummy bits specified in the UMA config register. */
> +static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
> +{
> +    unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
> +    unsigned int i;
> +
> +    for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
> +        /* Use bytes 0 and 1 first, then keep repeating byte 2 */
> +        unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
> +        unsigned int j;
> +
> +        for (j = 0; j < 8; j += bits_per_clock) {
> +            ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
> +        }
> +    }
> +}
> +
> +/* Assert the chip select specified in the UMA Control/Status Register. */
> +static void npcm7xx_fiu_select(NPCM7xxFIUState *s)
> +{
> +    int cs_id;
> +
> +    cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
> +    if (cs_id < s->cs_count) {
> +        qemu_irq_lower(s->cs_lines[cs_id]);
> +    } else {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: UMA to CS%d; this module has only %d chip selects",
> +                      DEVICE(s)->canonical_path, cs_id, s->cs_count);
> +        cs_id = -1;
> +    }
> +
> +    s->active_cs = cs_id;
> +}
> +
> +/* Deassert the currently active chip select. */
> +static void npcm7xx_fiu_deselect(NPCM7xxFIUState *s)
> +{
> +    if (s->active_cs < 0) {
> +        return;
> +    }
> +
> +    qemu_irq_raise(s->cs_lines[s->active_cs]);
> +    s->active_cs = -1;
> +}
> +
> +/* Perform a User-Mode Access transaction. */
> +static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
> +{
> +    uint32_t uma_cts = s->regs[NPCM7XX_FIU_UMA_CTS];
> +    uint32_t uma_cfg;
> +    unsigned int i;
> +
> +    /* SW_CS means the CS is already forced low, so don't touch it. */
> +    if (uma_cts & FIU_UMA_CTS_SW_CS) {
> +        npcm7xx_fiu_select(s);
> +    }
> +
> +    /* Send command, if present. */
> +    uma_cfg = s->regs[NPCM7XX_FIU_UMA_CFG];
> +    if (FIU_UMA_CFG_CMDSIZ(uma_cfg) > 0) {
> +        ssi_transfer(s->spi, extract32(s->regs[NPCM7XX_FIU_UMA_CMD], 0, 8));
> +    }
> +
> +    /* Send address, if present. */
> +    send_address(s->spi, FIU_UMA_CFG_ADDSIZ(uma_cfg),
> +                 s->regs[NPCM7XX_FIU_UMA_ADDR]);
> +
> +    /* Write data, if present. */
> +    for (i = 0; i < FIU_UMA_CFG_WDATSIZ(uma_cfg); i++) {
> +        unsigned int reg =
> +            (i < 16) ? (NPCM7XX_FIU_UMA_DW0 + i / 4) : NPCM7XX_FIU_UMA_DW3;
> +        unsigned int field = (i % 4) * 8;
> +
> +        ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
> +    }
> +
> +    /* Send aummy bits, if present. */

Typo "dummy" ;)

> +    send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
> +
> +    /* Read data, if present. */
> +    for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
> +        unsigned int reg = NPCM7XX_FIU_UMA_DR0 + i / 4;
> +        unsigned int field = (i % 4) * 8;
> +        uint8_t c;
> +
> +        c = ssi_transfer(s->spi, 0);
> +        if (reg <= NPCM7XX_FIU_UMA_DR3) {
> +            s->regs[reg] = deposit32(s->regs[reg], field, 8, c);
> +        }
> +    }
> +
> +    /* Again, don't touch CS if the user is forcing it low. */
> +    if (uma_cts & FIU_UMA_CTS_SW_CS) {
> +        npcm7xx_fiu_deselect(s);
> +    }
> +
> +    /* RDYST means a command has completed since it was cleared. */
> +    s->regs[NPCM7XX_FIU_UMA_CTS] |= FIU_UMA_CTS_RDYST;
> +    /* EXEC_DONE means Execute Command / Not Done, so clear it here. */
> +    s->regs[NPCM7XX_FIU_UMA_CTS] &= ~FIU_UMA_CTS_EXEC_DONE;
> +}
> +
> +/* Control register write handler. */
> +static void npcm7xx_fiu_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
> +                                   unsigned int size)
> +{
> +    hwaddr reg = addr / sizeof(uint32_t);
> +    NPCM7xxFIUState *s = opaque;
> +    uint32_t value = v;
> +
> +    trace_npcm7xx_fiu_ctrl_write(DEVICE(s)->canonical_path, addr, value);
> +
> +    switch (reg) {
> +    case NPCM7XX_FIU_UMA_CFG:
> +        if (s->regs[reg] & FIU_UMA_CFG_CMMLCK) {
> +            value &= ~FIU_UMA_CFG_CMMLCK_MASK;
> +            value |= (s->regs[reg] & FIU_UMA_CFG_CMMLCK_MASK);
> +        }
> +        /* fall through */
> +    case NPCM7XX_FIU_DRD_CFG:
> +    case NPCM7XX_FIU_DWR_CFG:
> +        if (s->regs[reg] & NPCM7XX_FIU_CFG_LCK) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "%s: write to locked register @ 0x%" PRIx64 "\n",
> +                          DEVICE(s)->canonical_path, addr);
> +            return;
> +        }
> +        s->regs[reg] = value;
> +        break;
> +
> +    case NPCM7XX_FIU_UMA_CTS:
> +        if (value & FIU_UMA_CTS_RDYST) {
> +            value &= ~FIU_UMA_CTS_RDYST;
> +        } else {
> +            value |= s->regs[reg] & FIU_UMA_CTS_RDYST;
> +        }
> +        if ((s->regs[reg] ^ value) & FIU_UMA_CTS_SW_CS) {
> +            if (value & FIU_UMA_CTS_SW_CS) {
> +                /*
> +                 * Don't drop CS if there's a transfer in progress, or we're
> +                 * about to start one.
> +                 */
> +                if (!((value | s->regs[reg]) & FIU_UMA_CTS_EXEC_DONE)) {
> +                    npcm7xx_fiu_deselect(s);
> +                }
> +            } else {
> +                npcm7xx_fiu_select(s);
> +            }
> +        }
> +        s->regs[reg] = value | (s->regs[reg] & FIU_UMA_CTS_EXEC_DONE);
> +        if (value & FIU_UMA_CTS_EXEC_DONE) {
> +            npcm7xx_fiu_uma_transaction(s);
> +        }
> +        break;
> +
> +    case NPCM7XX_FIU_UMA_DR0 ... NPCM7XX_FIU_UMA_DR3:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: write to read-only register @ 0x%" PRIx64 "\n",
> +                      DEVICE(s)->canonical_path, addr);
> +        return;
> +
> +    case NPCM7XX_FIU_PRT_CFG:
> +    case NPCM7XX_FIU_PRT_CMD0 ... NPCM7XX_FIU_PRT_CMD9:
> +        qemu_log_mask(LOG_UNIMP, "%s: PRT is not implemented\n", __func__);
> +        break;
> +
> +    case NPCM7XX_FIU_UMA_CMD:
> +    case NPCM7XX_FIU_UMA_ADDR:
> +    case NPCM7XX_FIU_UMA_DW0 ... NPCM7XX_FIU_UMA_DW3:
> +    case NPCM7XX_FIU_CFG:
> +        s->regs[reg] = value;
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "%s: write to invalid offset 0x%" PRIx64 "\n",
> +                      DEVICE(s)->canonical_path, addr);
> +        return;
> +    }
> +}
> +
> +static const MemoryRegionOps npcm7xx_fiu_ctrl_ops = {
> +    .read = npcm7xx_fiu_ctrl_read,
> +    .write = npcm7xx_fiu_ctrl_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +        .unaligned = false,
> +    },
> +};
> +
> +static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type)
> +{
> +    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
> +
> +    trace_npcm7xx_fiu_enter_reset(DEVICE(obj)->canonical_path, type);
> +
> +    memset(s->regs, 0, sizeof(s->regs));
> +
> +    s->regs[NPCM7XX_FIU_DRD_CFG] = 0x0300100b;
> +    s->regs[NPCM7XX_FIU_DWR_CFG] = 0x03000002;
> +    s->regs[NPCM7XX_FIU_UMA_CFG] = 0x00000400;
> +    s->regs[NPCM7XX_FIU_UMA_CTS] = 0x00010000;
> +    s->regs[NPCM7XX_FIU_UMA_CMD] = 0x0000000b;
> +    s->regs[NPCM7XX_FIU_PRT_CFG] = 0x00000400;
> +    s->regs[NPCM7XX_FIU_CFG] = 0x0000000b;
> +}
> +
> +static void npcm7xx_fiu_hold_reset(Object *obj)
> +{
> +    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
> +    int i;
> +
> +    trace_npcm7xx_fiu_hold_reset(DEVICE(obj)->canonical_path);
> +
> +    for (i = 0; i < s->cs_count; i++) {
> +        qemu_irq_raise(s->cs_lines[i]);
> +    }
> +}
> +
> +static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
> +    SysBusDevice *sbd = &s->parent;
> +    int i;
> +
> +    if (s->cs_count <= 0) {
> +        error_setg(errp, "%s: %d chip selects specified, need at least one",
> +                   dev->canonical_path, s->cs_count);
> +        return;
> +    }
> +
> +    s->spi = ssi_create_bus(dev, "spi");
> +    s->cs_lines = g_new0(qemu_irq, s->cs_count);

More recent API:

       qdev_init_gpio_out_named(dev, &s->cs_count, "cs", s->cs_count);

> +    s->flash = g_new0(NPCM7xxFIUFlash, s->cs_count);
> +
> +    /*
> +     * Register the control registers region first. It may be followed by one
> +     * or more direct flash access regions.
> +     */
> +    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_fiu_ctrl_ops, s, "ctrl",
> +                          NPCM7XX_FIU_CTRL_REGS_SIZE);
> +    sysbus_init_mmio(sbd, &s->mmio);
> +
> +    for (i = 0; i < s->cs_count; i++) {
> +        NPCM7xxFIUFlash *flash = &s->flash[i];
> +        sysbus_init_irq(sbd, &s->cs_lines[i]);
> +        flash->fiu = s;
> +        memory_region_init_io(&flash->direct_access, OBJECT(s),
> +                              &npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
> +                              NPCM7XX_FIU_FLASH_WINDOW_SIZE);
> +        sysbus_init_mmio(sbd, &flash->direct_access);
> +    }
> +}
> +
> +static const VMStateDescription vmstate_npcm7xx_fiu = {
> +    .name = "npcm7xx-fiu",
> +    .version_id = 0,
> +    .minimum_version_id = 0,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_INT32(active_cs, NPCM7xxFIUState),
> +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS),
> +        VMSTATE_END_OF_LIST(),
> +    },
> +};
> +
> +static Property npcm7xx_fiu_properties[] = {
> +    DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
> +{
> +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->desc = "NPCM7xx Flash Interface Unit";
> +    dc->realize = npcm7xx_fiu_realize;
> +    dc->vmsd = &vmstate_npcm7xx_fiu;
> +    rc->phases.enter = npcm7xx_fiu_enter_reset;
> +    rc->phases.hold = npcm7xx_fiu_hold_reset;
> +    device_class_set_props(dc, npcm7xx_fiu_properties);
> +}
> +
> +static const TypeInfo npcm7xx_fiu_types[] = {
> +    {
> +        .name = TYPE_NPCM7XX_FIU,
> +        .parent = TYPE_SYS_BUS_DEVICE,
> +        .instance_size = sizeof(NPCM7xxFIUState),
> +        .class_init = npcm7xx_fiu_class_init,
> +    },
> +};
> +DEFINE_TYPES(npcm7xx_fiu_types);
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index a31d0d282f..8d0ef0593b 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -360,6 +360,7 @@ config NPCM7XX
>      select ARM_GIC
>      select PL310  # cache controller
>      select SERIAL
> +    select SSI
>      select UNIMP
>  
>  config FSL_IMX25
> diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
> index 07a85f1967..cab48e72c9 100644
> --- a/hw/ssi/Makefile.objs
> +++ b/hw/ssi/Makefile.objs
> @@ -5,6 +5,7 @@ common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
>  common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
>  common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o
>  common-obj-$(CONFIG_MSF2) += mss-spi.o
> +common-obj-$(CONFIG_NPCM7XX) += npcm7xx_fiu.o
>  
>  common-obj-$(CONFIG_OMAP) += omap_spi.o
>  common-obj-$(CONFIG_IMX) += imx_spi.o
> diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
> index 0ea498de91..8024253c1f 100644
> --- a/hw/ssi/trace-events
> +++ b/hw/ssi/trace-events
> @@ -9,3 +9,12 @@ aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
>  aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
>  aspeed_smc_write(uint64_t addr,  uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
>  aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
> +
> +# npcm7xx_fiu.c
> +
> +npcm7xx_fiu_enter_reset(const char *id, int reset_type) "%s reset type: %d"
> +npcm7xx_fiu_hold_reset(const char *id) "%s"
> +npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"
> +npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"

Nitpicking again: PRIx32 :p

Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

> +npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
> +npcm7xx_fiu_flash_write(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09 16:23       ` Philippe Mathieu-Daudé
@ 2020-07-09 17:09         ` Havard Skinnemoen
  2020-07-09 17:24           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09 17:09 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
> > On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> >>> Implement a device model for the System Global Control Registers in the
> >>> NPCM730 and NPCM750 BMC SoCs.
> >>>
> >>> This is primarily used to enable SMP boot (the boot ROM spins reading
> >>> the SCRPAD register) and DDR memory initialization; other registers are
> >>> best effort for now.
> >>>
> >>> The reset values of the MDLR and PWRON registers are determined by the
> >>> SoC variant (730 vs 750) and board straps respectively.
> >>>
> >>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> >>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>> ---
> [...]
> >>> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
> >>> new file mode 100644
> >>> index 0000000000..9934cd238d
> >>> --- /dev/null
> >>> +++ b/hw/misc/npcm7xx_gcr.c
> >>> @@ -0,0 +1,226 @@
> >>> +/*
> >>> + * Nuvoton NPCM7xx System Global 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 as published by the
> >>> + * Free Software Foundation; either version 2 of the License, or
> >>> + * (at your option) any later version.
> >>> + *
> >>> + * 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_gcr.h"
> >>> +#include "hw/qdev-properties.h"
> >>> +#include "migration/vmstate.h"
> >>> +#include "qapi/error.h"
> >>> +#include "qemu/log.h"
> >>> +#include "qemu/module.h"
> >>> +#include "qemu/units.h"
> >>> +
> >>> +#include "trace.h"
> >>> +
> >>> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
> >>> +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
> >>> +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
> >>> +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
> >>> +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
> >>> +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
> >>> +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
> >>> +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
> >>> +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
> >>> +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
> >>> +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
> >>> +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
> >>> +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
> >>> +};
> >>> +
> >>> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
> >>> +{
> >>> +    uint32_t reg = offset / sizeof(uint32_t);
> >>> +    NPCM7xxGCRState *s = opaque;
> >>> +
> >>> +    if (reg >= NPCM7XX_GCR_NR_REGS) {
> >>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
> >>> +                      __func__, (unsigned int)offset);
> >>
> >> Maybe use HWADDR_PRIx instead of casting to int?
> >
> > Will do, thanks!
>
> Glad you are not annoyed by my review comments.
>
> FYI your series quality is in my top-4 "adding new machine to QEMU".
> I'd like to use it as example to follow.
>
> I am slowly reviewing because this is not part of my work duties,
> so I do that in my free time before/after work, which is why I can
> barely do more that 2 per day, as I have to learn the SoC and
> cross check documentation (or in this case, existing firmware code
> since there is no public doc).

Your feedback is super valuable, thanks a lot. I really appreciate it.

>
> [...]
> >>> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
> >>> +{
> >>> +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
> >>> +    uint64_t dram_size;
> >>> +    Error *err = NULL;
> >>> +    Object *obj;
> >>> +
> >>> +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
> >>> +    if (!obj) {
> >>> +        error_setg(errp, "%s: required dram-mr link not found: %s",
> >>> +                   __func__, error_get_pretty(err));
> >>> +        return;
> >>> +    }
> >>> +    dram_size = memory_region_size(MEMORY_REGION(obj));
> >>> +
> >>> +    /* Power-on reset value */
> >>> +    s->reset_intcr3 = 0x00001002;
> >>> +
> >>> +    /*
> >>> +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
> >>> +     * DRAM size, and is normally initialized by the boot block as part of DRAM
> >>> +     * training. However, since we don't have a complete emulation of the
> >>> +     * memory controller and try to make it look like it has already been
> >>> +     * initialized, the boot block will skip this initialization, and we need
> >>> +     * to make sure this field is set correctly up front.
> >>> +     *
> >>> +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
> >>> +     * more of DRAM will be interpreted as 128 MiB.
> >>
> >> I'd say u-boot is right in wrapping at 2GB, as more DRAM is
> >> un-addressable.
> >
> > Ah, maybe I shouldn't have said "or more". The bug is that if you
> > specify _exactly_ 2GiB, u-boot will see 128 MiB.
> >
> > But I agree more than 2GiB doesn't make sense, so I'll add a check for that.
> >
> > Not sure if I agree that the check should be here. > 2 GiB is an
> > addressing limitation, and the GCR module shouldn't really know what
> > the SoC's address space looks like. The lower limit is because the GCR
> > module can't encode anything less than 128 MiB.
> >
> >>> +     *
> >>> +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
> >>> +     */
> >>> +    if (dram_size >= 2 * GiB) {
> >>
> >> See my comment in this series on the machine patch.
> >>
> >>> +        s->reset_intcr3 |= 4 << 8;
> >>> +    } else if (dram_size >= 1 * GiB) {
> >>> +        s->reset_intcr3 |= 3 << 8;
> >>> +    } else if (dram_size >= 512 * MiB) {
> >>> +        s->reset_intcr3 |= 2 << 8;
> >>> +    } else if (dram_size >= 256 * MiB) {
> >>> +        s->reset_intcr3 |= 1 << 8;
> >>> +    } else if (dram_size >= 128 * MiB) {
> >>> +        s->reset_intcr3 |= 0 << 8;
>
> I think this can be simplified as:
>
>          s->reset_intcr3 = (4 - clz32(dram_size)) << 8;
>
> Which implicitly truncate to power of 2 (which is what this
> block does, as you can have only 1 bank managed per this SGC).

Good idea. I find this a little easier to understand though:

#define NPCM7XX_GCR_MIN_DRAM_SIZE   (128 * MiB)

    s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;

> But checking is_power_of_2(dram_size) and reporting an error
> else, is probably even better.

OK, I'll add a check for this, and also explicit checks for too large
and too small. The former is currently redundant with the DRAM size
check I'm adding to npcm7xx_realize(), but I kind of like the idea of
checking for encoding limitations and addressing limitations
separately, as they may not necessarily stay the same forever.

> >>> +    } else {
> >>> +        error_setg(errp,
> >>> +                   "npcm7xx_gcr: DRAM size %" PRIu64
> >>> +                   " is too small (need 128 MiB minimum)",
> >>> +                   dram_size);
> >>
> >> Ah, so you could add the same error for >2GB. Easier.
> >>
> >> Preferably using HWADDR_PRIx, and similar error for >2GB:
> >> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09 17:09         ` Havard Skinnemoen
@ 2020-07-09 17:24           ` Philippe Mathieu-Daudé
  2020-07-09 17:42             ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-09 17:24 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing,
	Cédric Le Goater, qemu-arm, Joel Stanley, IS20 Avi Fishman

On 7/9/20 7:09 PM, Havard Skinnemoen wrote:
> On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
>>> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
>>>>> Implement a device model for the System Global Control Registers in the
>>>>> NPCM730 and NPCM750 BMC SoCs.
>>>>>
>>>>> This is primarily used to enable SMP boot (the boot ROM spins reading
>>>>> the SCRPAD register) and DDR memory initialization; other registers are
>>>>> best effort for now.
>>>>>
>>>>> The reset values of the MDLR and PWRON registers are determined by the
>>>>> SoC variant (730 vs 750) and board straps respectively.
>>>>>
>>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>> ---
>> [...]
>>>>> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
>>>>> new file mode 100644
>>>>> index 0000000000..9934cd238d
>>>>> --- /dev/null
>>>>> +++ b/hw/misc/npcm7xx_gcr.c
>>>>> @@ -0,0 +1,226 @@
>>>>> +/*
>>>>> + * Nuvoton NPCM7xx System Global 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 as published by the
>>>>> + * Free Software Foundation; either version 2 of the License, or
>>>>> + * (at your option) any later version.
>>>>> + *
>>>>> + * 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_gcr.h"
>>>>> +#include "hw/qdev-properties.h"
>>>>> +#include "migration/vmstate.h"
>>>>> +#include "qapi/error.h"
>>>>> +#include "qemu/log.h"
>>>>> +#include "qemu/module.h"
>>>>> +#include "qemu/units.h"
>>>>> +
>>>>> +#include "trace.h"
>>>>> +
>>>>> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
>>>>> +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
>>>>> +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
>>>>> +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
>>>>> +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
>>>>> +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
>>>>> +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
>>>>> +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
>>>>> +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
>>>>> +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
>>>>> +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
>>>>> +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
>>>>> +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
>>>>> +};
>>>>> +
>>>>> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
>>>>> +{
>>>>> +    uint32_t reg = offset / sizeof(uint32_t);
>>>>> +    NPCM7xxGCRState *s = opaque;
>>>>> +
>>>>> +    if (reg >= NPCM7XX_GCR_NR_REGS) {
>>>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
>>>>> +                      __func__, (unsigned int)offset);
>>>>
>>>> Maybe use HWADDR_PRIx instead of casting to int?
>>>
>>> Will do, thanks!
>>
>> Glad you are not annoyed by my review comments.
>>
>> FYI your series quality is in my top-4 "adding new machine to QEMU".
>> I'd like to use it as example to follow.
>>
>> I am slowly reviewing because this is not part of my work duties,
>> so I do that in my free time before/after work, which is why I can
>> barely do more that 2 per day, as I have to learn the SoC and
>> cross check documentation (or in this case, existing firmware code
>> since there is no public doc).
> 
> Your feedback is super valuable, thanks a lot. I really appreciate it.

OK I'll continue, but might not have time until next week.
After I plan to test too.

What would be useful is an acceptance test (see examples in
tests/acceptance/), using the artefacts from the link you shared
elsewhere:
https://openpower.xyz/job/run-meta-ci/distro=ubuntu,label=builder,target=gsj/lastSuccessfulBuild/artifact/openbmc/build/tmp/deploy/images/gsj/

But these are apparently not stable links (expire after 30 days?).

>> [...]
>>>>> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
>>>>> +{
>>>>> +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
>>>>> +    uint64_t dram_size;
>>>>> +    Error *err = NULL;
>>>>> +    Object *obj;
>>>>> +
>>>>> +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
>>>>> +    if (!obj) {
>>>>> +        error_setg(errp, "%s: required dram-mr link not found: %s",
>>>>> +                   __func__, error_get_pretty(err));
>>>>> +        return;
>>>>> +    }
>>>>> +    dram_size = memory_region_size(MEMORY_REGION(obj));
>>>>> +
>>>>> +    /* Power-on reset value */
>>>>> +    s->reset_intcr3 = 0x00001002;
>>>>> +
>>>>> +    /*
>>>>> +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
>>>>> +     * DRAM size, and is normally initialized by the boot block as part of DRAM
>>>>> +     * training. However, since we don't have a complete emulation of the
>>>>> +     * memory controller and try to make it look like it has already been
>>>>> +     * initialized, the boot block will skip this initialization, and we need
>>>>> +     * to make sure this field is set correctly up front.
>>>>> +     *
>>>>> +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
>>>>> +     * more of DRAM will be interpreted as 128 MiB.
>>>>
>>>> I'd say u-boot is right in wrapping at 2GB, as more DRAM is
>>>> un-addressable.
>>>
>>> Ah, maybe I shouldn't have said "or more". The bug is that if you
>>> specify _exactly_ 2GiB, u-boot will see 128 MiB.
>>>
>>> But I agree more than 2GiB doesn't make sense, so I'll add a check for that.
>>>
>>> Not sure if I agree that the check should be here. > 2 GiB is an
>>> addressing limitation, and the GCR module shouldn't really know what
>>> the SoC's address space looks like. The lower limit is because the GCR
>>> module can't encode anything less than 128 MiB.
>>>
>>>>> +     *
>>>>> +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
>>>>> +     */
>>>>> +    if (dram_size >= 2 * GiB) {
>>>>
>>>> See my comment in this series on the machine patch.
>>>>
>>>>> +        s->reset_intcr3 |= 4 << 8;
>>>>> +    } else if (dram_size >= 1 * GiB) {
>>>>> +        s->reset_intcr3 |= 3 << 8;
>>>>> +    } else if (dram_size >= 512 * MiB) {
>>>>> +        s->reset_intcr3 |= 2 << 8;
>>>>> +    } else if (dram_size >= 256 * MiB) {
>>>>> +        s->reset_intcr3 |= 1 << 8;
>>>>> +    } else if (dram_size >= 128 * MiB) {
>>>>> +        s->reset_intcr3 |= 0 << 8;
>>
>> I think this can be simplified as:
>>
>>          s->reset_intcr3 = (4 - clz32(dram_size)) << 8;
>>
>> Which implicitly truncate to power of 2 (which is what this
>> block does, as you can have only 1 bank managed per this SGC).
> 
> Good idea. I find this a little easier to understand though:
> 
> #define NPCM7XX_GCR_MIN_DRAM_SIZE   (128 * MiB)
> 
>     s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;

I like it, furthermore it will work with >4GB if this model is
ever reused on an ARMv8-A SoC.

>> But checking is_power_of_2(dram_size) and reporting an error
>> else, is probably even better.
> 
> OK, I'll add a check for this, and also explicit checks for too large
> and too small. The former is currently redundant with the DRAM size
> check I'm adding to npcm7xx_realize(), but I kind of like the idea of
> checking for encoding limitations and addressing limitations
> separately, as they may not necessarily stay the same forever.
> 
>>>>> +    } else {
>>>>> +        error_setg(errp,
>>>>> +                   "npcm7xx_gcr: DRAM size %" PRIu64
>>>>> +                   " is too small (need 128 MiB minimum)",
>>>>> +                   dram_size);
>>>>
>>>> Ah, so you could add the same error for >2GB. Easier.
>>>>
>>>> Preferably using HWADDR_PRIx, and similar error for >2GB:
>>>> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09 17:24           ` Philippe Mathieu-Daudé
@ 2020-07-09 17:42             ` Havard Skinnemoen
  2020-07-10  9:31               ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-09 17:42 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Thu, Jul 9, 2020 at 10:24 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 7:09 PM, Havard Skinnemoen wrote:
> > On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
> >>> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> >>>>> Implement a device model for the System Global Control Registers in the
> >>>>> NPCM730 and NPCM750 BMC SoCs.
> >>>>>
> >>>>> This is primarily used to enable SMP boot (the boot ROM spins reading
> >>>>> the SCRPAD register) and DDR memory initialization; other registers are
> >>>>> best effort for now.
> >>>>>
> >>>>> The reset values of the MDLR and PWRON registers are determined by the
> >>>>> SoC variant (730 vs 750) and board straps respectively.
> >>>>>
> >>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> >>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>>>> ---
> >> [...]
> >>>>> diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
> >>>>> new file mode 100644
> >>>>> index 0000000000..9934cd238d
> >>>>> --- /dev/null
> >>>>> +++ b/hw/misc/npcm7xx_gcr.c
> >>>>> @@ -0,0 +1,226 @@
> >>>>> +/*
> >>>>> + * Nuvoton NPCM7xx System Global 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 as published by the
> >>>>> + * Free Software Foundation; either version 2 of the License, or
> >>>>> + * (at your option) any later version.
> >>>>> + *
> >>>>> + * 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_gcr.h"
> >>>>> +#include "hw/qdev-properties.h"
> >>>>> +#include "migration/vmstate.h"
> >>>>> +#include "qapi/error.h"
> >>>>> +#include "qemu/log.h"
> >>>>> +#include "qemu/module.h"
> >>>>> +#include "qemu/units.h"
> >>>>> +
> >>>>> +#include "trace.h"
> >>>>> +
> >>>>> +static const uint32_t cold_reset_values[NPCM7XX_GCR_NR_REGS] = {
> >>>>> +    [NPCM7XX_GCR_PDID]          = 0x04a92750,   /* Poleg A1 */
> >>>>> +    [NPCM7XX_GCR_MISCPE]        = 0x0000ffff,
> >>>>> +    [NPCM7XX_GCR_SPSWC]         = 0x00000003,
> >>>>> +    [NPCM7XX_GCR_INTCR]         = 0x0000035e,
> >>>>> +    [NPCM7XX_GCR_HIFCR]         = 0x0000004e,
> >>>>> +    [NPCM7XX_GCR_INTCR2]        = (1U << 19),   /* DDR initialized */
> >>>>> +    [NPCM7XX_GCR_RESSR]         = 0x80000000,
> >>>>> +    [NPCM7XX_GCR_DSCNT]         = 0x000000c0,
> >>>>> +    [NPCM7XX_GCR_DAVCLVLR]      = 0x5a00f3cf,
> >>>>> +    [NPCM7XX_GCR_SCRPAD]        = 0x00000008,
> >>>>> +    [NPCM7XX_GCR_USB1PHYCTL]    = 0x034730e4,
> >>>>> +    [NPCM7XX_GCR_USB2PHYCTL]    = 0x034730e4,
> >>>>> +};
> >>>>> +
> >>>>> +static uint64_t npcm7xx_gcr_read(void *opaque, hwaddr offset, unsigned size)
> >>>>> +{
> >>>>> +    uint32_t reg = offset / sizeof(uint32_t);
> >>>>> +    NPCM7xxGCRState *s = opaque;
> >>>>> +
> >>>>> +    if (reg >= NPCM7XX_GCR_NR_REGS) {
> >>>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: offset 0x%04x out of range\n",
> >>>>> +                      __func__, (unsigned int)offset);
> >>>>
> >>>> Maybe use HWADDR_PRIx instead of casting to int?
> >>>
> >>> Will do, thanks!
> >>
> >> Glad you are not annoyed by my review comments.
> >>
> >> FYI your series quality is in my top-4 "adding new machine to QEMU".
> >> I'd like to use it as example to follow.
> >>
> >> I am slowly reviewing because this is not part of my work duties,
> >> so I do that in my free time before/after work, which is why I can
> >> barely do more that 2 per day, as I have to learn the SoC and
> >> cross check documentation (or in this case, existing firmware code
> >> since there is no public doc).
> >
> > Your feedback is super valuable, thanks a lot. I really appreciate it.
>
> OK I'll continue, but might not have time until next week.
> After I plan to test too.

OK, I'll try to post a v6 before the weekend, unless you prefer that I
hold off until you've reviewed the whole series.

> What would be useful is an acceptance test (see examples in
> tests/acceptance/), using the artefacts from the link you shared
> elsewhere:
> https://openpower.xyz/job/run-meta-ci/distro=ubuntu,label=builder,target=gsj/lastSuccessfulBuild/artifact/openbmc/build/tmp/deploy/images/gsj/

Yes, tests are definitely on my radar. I'm working on SPI flash
qtests. I haven't had much success with avocado yet, but I'll keep
trying (perhaps using docker to control the environment more tightly).

Since the 5.1 release is frozen now, I might post some followup
patches before this series is merged, e.g. tests, bootrom
submodule+blob, more peripherals, etc. Is it preferable to keep this
series frozen (modulo API updates) since you spent a lot of time
reviewing it, and post the new stuff separately, or is it better to
add new patches to the end of the series and resend the whole thing?

> But these are apparently not stable links (expire after 30 days?).

Sorry, I'm too ignorant about Jenkins to know. I'll see if I can
figure something out.

> >> [...]
> >>>>> +static void npcm7xx_gcr_realize(DeviceState *dev, Error **errp)
> >>>>> +{
> >>>>> +    NPCM7xxGCRState *s = NPCM7XX_GCR(dev);
> >>>>> +    uint64_t dram_size;
> >>>>> +    Error *err = NULL;
> >>>>> +    Object *obj;
> >>>>> +
> >>>>> +    obj = object_property_get_link(OBJECT(dev), "dram-mr", &err);
> >>>>> +    if (!obj) {
> >>>>> +        error_setg(errp, "%s: required dram-mr link not found: %s",
> >>>>> +                   __func__, error_get_pretty(err));
> >>>>> +        return;
> >>>>> +    }
> >>>>> +    dram_size = memory_region_size(MEMORY_REGION(obj));
> >>>>> +
> >>>>> +    /* Power-on reset value */
> >>>>> +    s->reset_intcr3 = 0x00001002;
> >>>>> +
> >>>>> +    /*
> >>>>> +     * The GMMAP (Graphics Memory Map) field is used by u-boot to detect the
> >>>>> +     * DRAM size, and is normally initialized by the boot block as part of DRAM
> >>>>> +     * training. However, since we don't have a complete emulation of the
> >>>>> +     * memory controller and try to make it look like it has already been
> >>>>> +     * initialized, the boot block will skip this initialization, and we need
> >>>>> +     * to make sure this field is set correctly up front.
> >>>>> +     *
> >>>>> +     * WARNING: some versions of u-boot only looks at bits 8 and 9, so 2 GiB or
> >>>>> +     * more of DRAM will be interpreted as 128 MiB.
> >>>>
> >>>> I'd say u-boot is right in wrapping at 2GB, as more DRAM is
> >>>> un-addressable.
> >>>
> >>> Ah, maybe I shouldn't have said "or more". The bug is that if you
> >>> specify _exactly_ 2GiB, u-boot will see 128 MiB.
> >>>
> >>> But I agree more than 2GiB doesn't make sense, so I'll add a check for that.
> >>>
> >>> Not sure if I agree that the check should be here. > 2 GiB is an
> >>> addressing limitation, and the GCR module shouldn't really know what
> >>> the SoC's address space looks like. The lower limit is because the GCR
> >>> module can't encode anything less than 128 MiB.
> >>>
> >>>>> +     *
> >>>>> +     * https://github.com/Nuvoton-Israel/u-boot/blob/2aef993bd2aafeb5408dbaad0f3ce099ee40c4aa/board/nuvoton/poleg/poleg.c#L244
> >>>>> +     */
> >>>>> +    if (dram_size >= 2 * GiB) {
> >>>>
> >>>> See my comment in this series on the machine patch.
> >>>>
> >>>>> +        s->reset_intcr3 |= 4 << 8;
> >>>>> +    } else if (dram_size >= 1 * GiB) {
> >>>>> +        s->reset_intcr3 |= 3 << 8;
> >>>>> +    } else if (dram_size >= 512 * MiB) {
> >>>>> +        s->reset_intcr3 |= 2 << 8;
> >>>>> +    } else if (dram_size >= 256 * MiB) {
> >>>>> +        s->reset_intcr3 |= 1 << 8;
> >>>>> +    } else if (dram_size >= 128 * MiB) {
> >>>>> +        s->reset_intcr3 |= 0 << 8;
> >>
> >> I think this can be simplified as:
> >>
> >>          s->reset_intcr3 = (4 - clz32(dram_size)) << 8;
> >>
> >> Which implicitly truncate to power of 2 (which is what this
> >> block does, as you can have only 1 bank managed per this SGC).
> >
> > Good idea. I find this a little easier to understand though:
> >
> > #define NPCM7XX_GCR_MIN_DRAM_SIZE   (128 * MiB)
> >
> >     s->reset_intcr3 |= ctz64(dram_size / NPCM7XX_GCR_MIN_DRAM_SIZE) << 8;
>
> I like it, furthermore it will work with >4GB if this model is
> ever reused on an ARMv8-A SoC.
>
> >> But checking is_power_of_2(dram_size) and reporting an error
> >> else, is probably even better.
> >
> > OK, I'll add a check for this, and also explicit checks for too large
> > and too small. The former is currently redundant with the DRAM size
> > check I'm adding to npcm7xx_realize(), but I kind of like the idea of
> > checking for encoding limitations and addressing limitations
> > separately, as they may not necessarily stay the same forever.
> >
> >>>>> +    } else {
> >>>>> +        error_setg(errp,
> >>>>> +                   "npcm7xx_gcr: DRAM size %" PRIu64
> >>>>> +                   " is too small (need 128 MiB minimum)",
> >>>>> +                   dram_size);
> >>>>
> >>>> Ah, so you could add the same error for >2GB. Easier.
> >>>>
> >>>> Preferably using HWADDR_PRIx, and similar error for >2GB:
> >>>> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
> >


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-09 17:42             ` Havard Skinnemoen
@ 2020-07-10  9:31               ` Philippe Mathieu-Daudé
  2020-07-11  6:46                 ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-10  9:31 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing,
	Cédric Le Goater, qemu-arm, Joel Stanley, IS20 Avi Fishman

On 7/9/20 7:42 PM, Havard Skinnemoen wrote:
> On Thu, Jul 9, 2020 at 10:24 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> On 7/9/20 7:09 PM, Havard Skinnemoen wrote:
>>> On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
>>>>> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
>>>>>>> Implement a device model for the System Global Control Registers in the
>>>>>>> NPCM730 and NPCM750 BMC SoCs.
>>>>>>>
>>>>>>> This is primarily used to enable SMP boot (the boot ROM spins reading
>>>>>>> the SCRPAD register) and DDR memory initialization; other registers are
>>>>>>> best effort for now.
>>>>>>>
>>>>>>> The reset values of the MDLR and PWRON registers are determined by the
>>>>>>> SoC variant (730 vs 750) and board straps respectively.
>>>>>>>
>>>>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>>>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>>>> ---
[...]
>>>>>> Maybe use HWADDR_PRIx instead of casting to int?
>>>>>
>>>>> Will do, thanks!
>>>>
>>>> Glad you are not annoyed by my review comments.
>>>>
>>>> FYI your series quality is in my top-4 "adding new machine to QEMU".
>>>> I'd like to use it as example to follow.
>>>>
>>>> I am slowly reviewing because this is not part of my work duties,
>>>> so I do that in my free time before/after work, which is why I can
>>>> barely do more that 2 per day, as I have to learn the SoC and
>>>> cross check documentation (or in this case, existing firmware code
>>>> since there is no public doc).
>>>
>>> Your feedback is super valuable, thanks a lot. I really appreciate it.
>>
>> OK I'll continue, but might not have time until next week.
>> After I plan to test too.
> 
> OK, I'll try to post a v6 before the weekend, unless you prefer that I
> hold off until you've reviewed the whole series.

I don't mind, if it is not too much overhead on your side.

What I noticed on the QEMU community is:

- If a series reviewed the same day and is almost done except
  a single bugfix, it is OK to repost the same day, so the
  maintainer is likely to queue it directly.

- If there are various reviews, and you do gradual improvements,
  it is OK to repost every 3 days. Else reviewers tend to skip/
  postpone your series for later.

- Pinging for review before 1 week passed is stressing reviewers,
  except in case of critical security bug (CVE) or during freeze.

- Series with integrated test provided are usually reviewed first.

>> What would be useful is an acceptance test (see examples in
>> tests/acceptance/), using the artefacts from the link you shared
>> elsewhere:
>> https://openpower.xyz/job/run-meta-ci/distro=ubuntu,label=builder,target=gsj/lastSuccessfulBuild/artifact/openbmc/build/tmp/deploy/images/gsj/
> 
> Yes, tests are definitely on my radar. I'm working on SPI flash
> qtests. I haven't had much success with avocado yet, but I'll keep
> trying (perhaps using docker to control the environment more tightly).

This might help:
https://www.mail-archive.com/qemu-devel@nongnu.org/msg704907.html

> Since the 5.1 release is frozen now, I might post some followup
> patches before this series is merged, e.g. tests, bootrom
> submodule+blob, more peripherals, etc. Is it preferable to keep this
> series frozen (modulo API updates) since you spent a lot of time
> reviewing it, and post the new stuff separately, or is it better to
> add new patches to the end of the series and resend the whole thing?

If you rework a peripheral, you need to reset the Reviewed-by/Tested-by
tags. If you add new peripherals, you only need to reset these tags on
the SoC patch. I'm fine either way, I use git-backport-diff to see the
SoC changes easily:

https://github.com/codyprime/git-scripts/blob/master/git-backport-diff

> 
>> But these are apparently not stable links (expire after 30 days?).
> 
> Sorry, I'm too ignorant about Jenkins to know. I'll see if I can
> figure something out.

What I do in that case is take the binary used for the test,
write a comment and push it in a stable branch to my own repo:
https://github.com/philmd/qemu-testing-blob/ and use the stable
url in the test.

We know QEMU emulation worked with this particular binary at some
point. We want to avoid regressions in QEMU, so let's keep testing
what we know worked. We don't want to track 2 bugs at a time (one
in the updated guest and one in QEMU).

Regards,

Phil.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-10  9:31               ` Philippe Mathieu-Daudé
@ 2020-07-11  6:46                 ` Havard Skinnemoen
  2020-07-12  5:49                   ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-11  6:46 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Fri, Jul 10, 2020 at 2:31 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 7:42 PM, Havard Skinnemoen wrote:
> > On Thu, Jul 9, 2020 at 10:24 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >> On 7/9/20 7:09 PM, Havard Skinnemoen wrote:
> >>> On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
> >>>>> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>>>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> >>>>>>> Implement a device model for the System Global Control Registers in the
> >>>>>>> NPCM730 and NPCM750 BMC SoCs.
> >>>>>>>
> >>>>>>> This is primarily used to enable SMP boot (the boot ROM spins reading
> >>>>>>> the SCRPAD register) and DDR memory initialization; other registers are
> >>>>>>> best effort for now.
> >>>>>>>
> >>>>>>> The reset values of the MDLR and PWRON registers are determined by the
> >>>>>>> SoC variant (730 vs 750) and board straps respectively.
> >>>>>>>
> >>>>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> >>>>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>>>>>> ---
> [...]
> >>>>>> Maybe use HWADDR_PRIx instead of casting to int?
> >>>>>
> >>>>> Will do, thanks!
> >>>>
> >>>> Glad you are not annoyed by my review comments.
> >>>>
> >>>> FYI your series quality is in my top-4 "adding new machine to QEMU".
> >>>> I'd like to use it as example to follow.
> >>>>
> >>>> I am slowly reviewing because this is not part of my work duties,
> >>>> so I do that in my free time before/after work, which is why I can
> >>>> barely do more that 2 per day, as I have to learn the SoC and
> >>>> cross check documentation (or in this case, existing firmware code
> >>>> since there is no public doc).
> >>>
> >>> Your feedback is super valuable, thanks a lot. I really appreciate it.
> >>
> >> OK I'll continue, but might not have time until next week.
> >> After I plan to test too.
> >
> > OK, I'll try to post a v6 before the weekend, unless you prefer that I
> > hold off until you've reviewed the whole series.
>
> I don't mind, if it is not too much overhead on your side.
>
> What I noticed on the QEMU community is:
>
> - If a series reviewed the same day and is almost done except
>   a single bugfix, it is OK to repost the same day, so the
>   maintainer is likely to queue it directly.
>
> - If there are various reviews, and you do gradual improvements,
>   it is OK to repost every 3 days. Else reviewers tend to skip/
>   postpone your series for later.
>
> - Pinging for review before 1 week passed is stressing reviewers,
>   except in case of critical security bug (CVE) or during freeze.
>
> - Series with integrated test provided are usually reviewed first.

This is very helpful, thanks.

> >> What would be useful is an acceptance test (see examples in
> >> tests/acceptance/), using the artefacts from the link you shared
> >> elsewhere:
> >> https://openpower.xyz/job/run-meta-ci/distro=ubuntu,label=builder,target=gsj/lastSuccessfulBuild/artifact/openbmc/build/tmp/deploy/images/gsj/
> >
> > Yes, tests are definitely on my radar. I'm working on SPI flash
> > qtests. I haven't had much success with avocado yet, but I'll keep
> > trying (perhaps using docker to control the environment more tightly).
>
> This might help:
> https://www.mail-archive.com/qemu-devel@nongnu.org/msg704907.html

Thanks, I got it working now.

> > Since the 5.1 release is frozen now, I might post some followup
> > patches before this series is merged, e.g. tests, bootrom
> > submodule+blob, more peripherals, etc. Is it preferable to keep this
> > series frozen (modulo API updates) since you spent a lot of time
> > reviewing it, and post the new stuff separately, or is it better to
> > add new patches to the end of the series and resend the whole thing?
>
> If you rework a peripheral, you need to reset the Reviewed-by/Tested-by
> tags. If you add new peripherals, you only need to reset these tags on
> the SoC patch. I'm fine either way, I use git-backport-diff to see the
> SoC changes easily:
>
> https://github.com/codyprime/git-scripts/blob/master/git-backport-diff

I've been adding new peripherals incrementally after the basic SoC
support patch. Is that OK to do without resetting the tags?

But it's more likely that I'll add other things than peripherals next,
i.e. bootrom and tests.

> >
> >> But these are apparently not stable links (expire after 30 days?).
> >
> > Sorry, I'm too ignorant about Jenkins to know. I'll see if I can
> > figure something out.
>
> What I do in that case is take the binary used for the test,
> write a comment and push it in a stable branch to my own repo:
> https://github.com/philmd/qemu-testing-blob/ and use the stable
> url in the test.
>
> We know QEMU emulation worked with this particular binary at some
> point. We want to avoid regressions in QEMU, so let's keep testing
> what we know worked. We don't want to track 2 bugs at a time (one
> in the updated guest and one in QEMU).

Good point. I'll see if I can upload images to github. I might fork
the openbmc repository and attach binaries to a github release, to
make it clear where the binaries came from.

I accidentally broke my test image and had some trouble recreating it,
so I ended up reworking my various hacks a bit. The good news is that
I got most of them turned into proper bug fixes that I can send
upstream.

It might take a little longer than I said previously, but I'll try to
include acceptance tests in the next series.

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-07-09 17:00   ` Philippe Mathieu-Daudé
@ 2020-07-12  5:42     ` Havard Skinnemoen
  2020-07-13 17:38       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-12  5:42 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Cédric Le Goater

On Thu, Jul 9, 2020 at 10:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> > This implements a device model for the NPCM7xx SPI flash controller.
> >
> > Direct reads and writes, and user-mode transactions have been tested in
> > various modes. Protection features are not implemented yet.
> >
> > All the FIU instances are available in the SoC's address space,
> > regardless of whether or not they're connected to actual flash chips.
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/arm/npcm7xx.h     |   2 +
> >  include/hw/ssi/npcm7xx_fiu.h | 100 +++++++
> >  hw/arm/npcm7xx.c             |  53 ++++
> >  hw/ssi/npcm7xx_fiu.c         | 510 +++++++++++++++++++++++++++++++++++
> >  hw/arm/Kconfig               |   1 +
> >  hw/ssi/Makefile.objs         |   1 +
> >  hw/ssi/trace-events          |   9 +
> >  7 files changed, 676 insertions(+)
> >  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
> >  create mode 100644 hw/ssi/npcm7xx_fiu.c
> >
> > diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> > index 3ae9e5dca2..bc99f47286 100644
> > --- a/include/hw/arm/npcm7xx.h
> > +++ b/include/hw/arm/npcm7xx.h
> > @@ -23,6 +23,7 @@
> >  #include "hw/misc/npcm7xx_gcr.h"
> >  #include "hw/nvram/npcm7xx_otp.h"
> >  #include "hw/timer/npcm7xx_timer.h"
> > +#include "hw/ssi/npcm7xx_fiu.h"
> >  #include "target/arm/cpu.h"
> >
> >  #define NPCM7XX_MAX_NUM_CPUS    (2)
> > @@ -73,6 +74,7 @@ typedef struct NPCM7xxState {
> >      NPCM7xxOTPState     key_storage;
> >      NPCM7xxOTPState     fuse_array;
> >      NPCM7xxMCState      mc;
> > +    NPCM7xxFIUState     fiu[2];
> >  } NPCM7xxState;
> >
> >  #define TYPE_NPCM7XX    "npcm7xx"
> > diff --git a/include/hw/ssi/npcm7xx_fiu.h b/include/hw/ssi/npcm7xx_fiu.h
> > new file mode 100644
> > index 0000000000..b867bd0429
> > --- /dev/null
> > +++ b/include/hw/ssi/npcm7xx_fiu.h
> > @@ -0,0 +1,100 @@
> > +/*
> > + * Nuvoton NPCM7xx Flash Interface Unit (FIU)
> > + *
> > + * 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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_FIU_H
> > +#define NPCM7XX_FIU_H
> > +
> > +#include "hw/ssi/ssi.h"
> > +#include "hw/sysbus.h"
> > +
> > +/**
> > + * enum NPCM7xxFIURegister - 32-bit FIU register indices.
> > + */
> > +enum NPCM7xxFIURegister {
> > +    NPCM7XX_FIU_DRD_CFG,
> > +    NPCM7XX_FIU_DWR_CFG,
> > +    NPCM7XX_FIU_UMA_CFG,
> > +    NPCM7XX_FIU_UMA_CTS,
> > +    NPCM7XX_FIU_UMA_CMD,
> > +    NPCM7XX_FIU_UMA_ADDR,
> > +    NPCM7XX_FIU_PRT_CFG,
> > +    NPCM7XX_FIU_UMA_DW0 = 0x0020 / sizeof(uint32_t),
> > +    NPCM7XX_FIU_UMA_DW1,
> > +    NPCM7XX_FIU_UMA_DW2,
> > +    NPCM7XX_FIU_UMA_DW3,
> > +    NPCM7XX_FIU_UMA_DR0,
> > +    NPCM7XX_FIU_UMA_DR1,
> > +    NPCM7XX_FIU_UMA_DR2,
> > +    NPCM7XX_FIU_UMA_DR3,
> > +    NPCM7XX_FIU_PRT_CMD0,
> > +    NPCM7XX_FIU_PRT_CMD1,
> > +    NPCM7XX_FIU_PRT_CMD2,
> > +    NPCM7XX_FIU_PRT_CMD3,
> > +    NPCM7XX_FIU_PRT_CMD4,
> > +    NPCM7XX_FIU_PRT_CMD5,
> > +    NPCM7XX_FIU_PRT_CMD6,
> > +    NPCM7XX_FIU_PRT_CMD7,
> > +    NPCM7XX_FIU_PRT_CMD8,
> > +    NPCM7XX_FIU_PRT_CMD9,
> > +    NPCM7XX_FIU_CFG = 0x78 / sizeof(uint32_t),
> > +    NPCM7XX_FIU_NR_REGS,
> > +};
> > +
> > +typedef struct NPCM7xxFIUState NPCM7xxFIUState;
> > +
> > +/**
> > + * struct NPCM7xxFIUFlash - Per-chipselect flash controller state.
> > + * @direct_access: Memory region for direct flash access.
> > + * @fiu: Pointer to flash controller shared state.
> > + */
> > +typedef struct NPCM7xxFIUFlash {
> > +    MemoryRegion direct_access;
> > +    NPCM7xxFIUState *fiu;
> > +} NPCM7xxFIUFlash;
> > +
> > +/**
> > + * NPCM7xxFIUState - Device state for one Flash Interface Unit.
> > + * @parent: System bus device.
> > + * @mmio: Memory region for register access.
> > + * @cs_count: Number of flash chips that may be connected to this module.
> > + * @active_cs: Currently active chip select, or -1 if no chip is selected.
> > + * @cs_lines: GPIO lines that may be wired to flash chips.
> > + * @flash: Array of @cs_count per-flash-chip state objects.
> > + * @spi: The SPI bus mastered by this controller.
> > + * @regs: Register contents.
> > + *
> > + * Each FIU has a shared bank of registers, and controls up to four chip
> > + * selects. Each chip select has a dedicated memory region which may be used to
> > + * read and write the flash connected to that chip select as if it were memory.
> > + */
> > +struct NPCM7xxFIUState {
> > +    SysBusDevice parent;
> > +
> > +    MemoryRegion mmio;
> > +
> > +    int32_t cs_count;
> > +    int32_t active_cs;
> > +    qemu_irq *cs_lines;
> > +    NPCM7xxFIUFlash *flash;
> > +
> > +    SSIBus *spi;
> > +
> > +    uint32_t regs[NPCM7XX_FIU_NR_REGS];
> > +};
> > +
> > +#define TYPE_NPCM7XX_FIU "npcm7xx-fiu"
> > +#define NPCM7XX_FIU(obj) OBJECT_CHECK(NPCM7xxFIUState, (obj), TYPE_NPCM7XX_FIU)
> > +
> > +#endif /* NPCM7XX_FIU_H */
> > diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> > index 4d227bb74b..c9ff3dab25 100644
> > --- a/hw/arm/npcm7xx.c
> > +++ b/hw/arm/npcm7xx.c
> > @@ -98,6 +98,37 @@ static const hwaddr npcm7xx_uart_addr[] = {
> >      0xf0004000,
> >  };
> >
> > +static const hwaddr npcm7xx_fiu0_flash_addr[] = {
>
> So per
> https://github.com/Nuvoton-Israel/bootblock/blob/master/SWC_HAL/Chips/npcm750/npcm750.h
> this is SPI0 on AHB18,
>
> > +    0x80000000,
> > +    0x88000000,
>
> CS0 & CS1,
>
> also listed:
>
> 0x90000000, // CS2
> 0x98000000, // CS3

Confirmed with Nuvoton off-list that these do not exist. SPI0 only
supports two chip-selects for direct access.

I'll add comments.

> > +};
> > +
> > +static const hwaddr npcm7xx_fiu3_flash_addr[] = {
>
> Ditto SPI3 on AHB3, and CS0 to CS3.
>
> > +    0xa0000000,
> > +    0xa8000000,
> > +    0xb0000000,
> > +    0xb8000000,
> > +};
> > +
> > +static const struct {
> > +    const char *name;
> > +    hwaddr regs_addr;
> > +    int cs_count;
> > +    const hwaddr *flash_addr;
> > +} npcm7xx_fiu[] = {
> > +    {
> > +        .name = "fiu0",
> > +        .regs_addr = 0xfb000000,
> > +        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
>
> Hmm without the datasheet, can't tell, but I'd expect 4 CS
> regardless.

FIU0/SPI0 only has 2 chip selects.

> > +        .flash_addr = npcm7xx_fiu0_flash_addr,
> > +    }, {
> > +        .name = "fiu3",
> > +        .regs_addr = 0xc0000000,
> > +        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
> > +        .flash_addr = npcm7xx_fiu3_flash_addr,
> > +    },
> > +};
> > +
> >  void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
> >  {
> >      /*
> > @@ -171,6 +202,12 @@ static void npcm7xx_init(Object *obj)
> >      for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> >          object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> >      }
> > +
> > +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
> > +    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
> > +        object_initialize_child(obj, npcm7xx_fiu[i].name, &s->fiu[i],
> > +                                TYPE_NPCM7XX_FIU);
> > +    }
> >  }
> >
> >  static void npcm7xx_realize(DeviceState *dev, Error **errp)
> > @@ -260,6 +297,22 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
> >                         serial_hd(i), DEVICE_LITTLE_ENDIAN);
> >      }
> >
> > +    /* Flash Interface Unit (FIU) */
> > +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_fiu) != ARRAY_SIZE(s->fiu));
> > +    for (i = 0; i < ARRAY_SIZE(s->fiu); i++) {
> > +        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->fiu[i]);
> > +        int j;
> > +
> > +        object_property_set_int(OBJECT(sbd), npcm7xx_fiu[i].cs_count,
> > +                                "cs-count", &error_abort);
> > +        sysbus_realize(sbd, &error_abort);
> > +
> > +        sysbus_mmio_map(sbd, 0, npcm7xx_fiu[i].regs_addr);
> > +        for (j = 0; j < npcm7xx_fiu[i].cs_count; j++) {
> > +            sysbus_mmio_map(sbd, j + 1, npcm7xx_fiu[i].flash_addr[j]);
> > +        }
> > +    }
> > +
> >      /* RAM2 (SRAM) */
> >      memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
> >                             NPCM7XX_RAM2_SZ, &error_abort);
> > diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
> > new file mode 100644
> > index 0000000000..92ade709e8
> > --- /dev/null
> > +++ b/hw/ssi/npcm7xx_fiu.c
> > @@ -0,0 +1,510 @@
> > +/*
> > + * Nuvoton NPCM7xx Flash Interface Unit (FIU)
> > + *
> > + * 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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/irq.h"
> > +#include "hw/qdev-properties.h"
> > +#include "hw/ssi/npcm7xx_fiu.h"
> > +#include "migration/vmstate.h"
> > +#include "qapi/error.h"
> > +#include "qemu/error-report.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/units.h"
> > +
> > +#include "trace.h"
> > +
> > +/* Up to 128 MiB of flash may be accessed directly as memory. */
> > +#define NPCM7XX_FIU_FLASH_WINDOW_SIZE (128 * MiB)
> > +
> > +/* Each module has 4 KiB of register space. Only a fraction of it is used. */
> > +#define NPCM7XX_FIU_CTRL_REGS_SIZE (4 * KiB)
> > +
> > +/* FIU_{DRD,DWR,UMA,PTR}_CFG cannot be written when this bit is set. */
> > +#define NPCM7XX_FIU_CFG_LCK BIT(31)
> > +
> > +/* Direct Read configuration register fields. */
> > +#define FIU_DRD_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
> > +#define FIU_ADDSIZ_3BYTES 0
> > +#define FIU_ADDSIZ_4BYTES 1
> > +#define FIU_DRD_CFG_DBW(rv) extract32(rv, 12, 2)
> > +#define FIU_DRD_CFG_ACCTYPE(rv) extract32(rv, 8, 2)
> > +#define FIU_DRD_CFG_RDCMD(rv) extract32(rv, 0, 8)
> > +
> > +/* Direct Write configuration register fields. */
> > +#define FIU_DWR_CFG_ADDSIZ(rv) extract32(rv, 16, 2)
> > +#define FIU_DWR_CFG_WRCMD(rv) extract32(rv, 0, 8)
> > +
> > +/* User-Mode Access register fields. */
> > +
> > +/* Command Mode Lock and the bits protected by it. */
> > +#define FIU_UMA_CFG_CMMLCK BIT(30)
> > +#define FIU_UMA_CFG_CMMLCK_MASK 0x00000403
> > +
> > +#define FIU_UMA_CFG_RDATSIZ(rv) extract32(rv, 24, 5)
> > +#define FIU_UMA_CFG_DBSIZ(rv) extract32(rv, 21, 3)
> > +#define FIU_UMA_CFG_WDATSIZ(rv) extract32(rv, 16, 5)
> > +#define FIU_UMA_CFG_ADDSIZ(rv) extract32(rv, 11, 3)
> > +#define FIU_UMA_CFG_CMDSIZ(rv) extract32(rv, 10, 1)
> > +#define FIU_UMA_CFG_DBPCK(rv) extract32(rv, 6, 2)
> > +
> > +#define FIU_UMA_CTS_RDYIE BIT(25)
> > +#define FIU_UMA_CTS_RDYST BIT(24)
> > +#define FIU_UMA_CTS_SW_CS BIT(16)
> > +#define FIU_UMA_CTS_DEV_NUM(rv) extract32(rv, 8, 2)
> > +#define FIU_UMA_CTS_EXEC_DONE BIT(0)
> > +
> > +/* Direct flash memory read handler. */
> > +static uint64_t npcm7xx_fiu_flash_read(void *opaque, hwaddr addr,
> > +                                       unsigned int size)
> > +{
> > +    NPCM7xxFIUFlash *f = opaque;
> > +    NPCM7xxFIUState *fiu = f->fiu;
> > +    int cs_id = f - fiu->flash;
>
> Hmmm? Not obvious.

I'll add a helper function.


> > +    uint64_t value = 0;
> > +    uint32_t drd_cfg;
> > +    int dummy_cycles;
> > +    int i;
> > +
> > +    drd_cfg = fiu->regs[NPCM7XX_FIU_DRD_CFG];
> > +
> > +    qemu_irq_lower(fiu->cs_lines[cs_id]);
>
> Isn't it cleaner to use npcm7xx_fiu_select() here?

Yes, you're right.

> When can we have 'fio->active_cs != cs_id'?

There's a set of user-mode registers that also control CS. I'll log an
error if CS is already active when we get here (indicating a guest
software error).

> BTW I'd add a trace event in select/deselect().

Will do.

> > +    ssi_transfer(fiu->spi, FIU_DRD_CFG_RDCMD(drd_cfg));
> > +
> > +    switch (FIU_DRD_CFG_ADDSIZ(drd_cfg)) {
> > +    case FIU_ADDSIZ_4BYTES:
> > +        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
> > +        /* fall through */
> > +    case FIU_ADDSIZ_3BYTES:
> > +        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
> > +        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
> > +        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
> > +                      DEVICE(fiu)->canonical_path, FIU_DRD_CFG_ADDSIZ(drd_cfg));
> > +        break;
> > +    }
> > +
> > +    /* Flash chip model expects one transfer per dummy bit, not byte */
> > +    dummy_cycles =
> > +        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
> > +    for (i = 0; i < dummy_cycles; i++) {
> > +        ssi_transfer(fiu->spi, 0);
>
> Note to self, might worth a ssi_shift_dummy(count) generic method.

I'm not a huge fan of this interface to be honest. It requires the
flash controller to have intimate knowledge of the flash chip, and if
it doesn't predict the dummy phase correctly, or the guest programs
the wrong number of dummy cycles, the end result is very different
from what you'll see on a real system. I'd love to see something like
a number-of-bits parameter to ssi_transfer instead.

> > +    }
> > +
> > +    for (i = 0; i < size; i++) {
> > +        value |= ssi_transfer(fiu->spi, 0) << (8 * i);
> > +    }
> > +
>
> Ditto npcm7xx_fiu_deselect().

Will do.

> > +    qemu_irq_raise(fiu->cs_lines[cs_id]);
> > +
> > +    trace_npcm7xx_fiu_flash_read(DEVICE(fiu)->canonical_path, cs_id, addr, size,
> > +                                 value);
> > +
> > +    return value;
> > +}
> > +
> > +/* Direct flash memory write handler. */
> > +static void npcm7xx_fiu_flash_write(void *opaque, hwaddr addr, uint64_t v,
> > +                                    unsigned int size)
> > +{
> > +    NPCM7xxFIUFlash *f = opaque;
> > +    NPCM7xxFIUState *fiu = f->fiu;
> > +    int cs_id = f - fiu->flash;
> > +    uint32_t dwr_cfg;
> > +    int i;
> > +
> > +    trace_npcm7xx_fiu_flash_write(DEVICE(fiu)->canonical_path, cs_id, addr,
> > +                                  size, v);
> > +
> > +    dwr_cfg = fiu->regs[NPCM7XX_FIU_DWR_CFG];
> > +
> > +    qemu_irq_lower(fiu->cs_lines[cs_id]);
> > +    ssi_transfer(fiu->spi, FIU_DWR_CFG_WRCMD(dwr_cfg));
> > +
> > +    switch (FIU_DWR_CFG_ADDSIZ(dwr_cfg)) {
> > +    case FIU_ADDSIZ_4BYTES:
> > +        ssi_transfer(fiu->spi, extract32(addr, 24, 8));
> > +        /* fall through */
> > +    case FIU_ADDSIZ_3BYTES:
> > +        ssi_transfer(fiu->spi, extract32(addr, 16, 8));
> > +        ssi_transfer(fiu->spi, extract32(addr, 8, 8));
> > +        ssi_transfer(fiu->spi, extract32(addr, 0, 8));
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: bad address size %d\n",
> > +                      DEVICE(fiu)->canonical_path, FIU_DWR_CFG_ADDSIZ(dwr_cfg));
> > +        break;
> > +    }
> > +
> > +    for (i = 0; i < size; i++) {
> > +        ssi_transfer(fiu->spi, v & 0xff);
> > +        v >>= 8;
>
> Or since you use deposit32() in transaction():
>
>            extract32(v, 8 * i, 8);

Needs to be extract64(), but done. Also, deposit64() in flash_read().

> > +    }
> > +
> > +    qemu_irq_raise(fiu->cs_lines[cs_id]);
> > +}
> > +
> > +static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
> > +    .read = npcm7xx_fiu_flash_read,
> > +    .write = npcm7xx_fiu_flash_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 1,
> > +        .max_access_size = 8,
>
> Are you sure? Maybe, I can' tell.

Real hardware supports 16 bytes, but there's no way to do more than 8
in emulation, I think?

> > +        .unaligned = true,
> > +    },
> > +};
> > +
> > +/* Control register read handler. */
> > +static uint64_t npcm7xx_fiu_ctrl_read(void *opaque, hwaddr addr,
> > +                                      unsigned int size)
> > +{
> > +    hwaddr reg = addr / sizeof(uint32_t);
> > +    NPCM7xxFIUState *s = opaque;
> > +    uint32_t value;
> > +
> > +    if (reg < NPCM7XX_FIU_NR_REGS) {
> > +        value = s->regs[reg];
> > +    } else {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: read from invalid offset 0x%" PRIx64 "\n",
> > +                      DEVICE(s)->canonical_path, addr);
> > +        value = 0;
> > +    }
> > +
> > +    trace_npcm7xx_fiu_ctrl_read(DEVICE(s)->canonical_path, addr, value);
> > +
> > +    return value;
> > +}
> > +
> > +/* Send the specified number of address bytes from the UMA address register. */
> > +static void send_address(SSIBus *spi, unsigned int addsiz, uint32_t addr)
> > +{
> > +    /* All cases fall through */
>
> Is this comment recognized by -Wimplicit-fallthrough?

Nope. I'll use proper fallthrough comments.

> > +    switch (addsiz) {
> > +    case 4:
> > +        ssi_transfer(spi, extract32(addr, 24, 8));
> > +    case 3:
> > +        ssi_transfer(spi, extract32(addr, 16, 8));
> > +    case 2:
> > +        ssi_transfer(spi, extract32(addr, 8, 8));
> > +    case 1:
> > +        ssi_transfer(spi, extract32(addr, 0, 8));
> > +    case 0:
> > +        break;
> > +    }
> > +}
> > +
> > +/* Send the number of dummy bits specified in the UMA config register. */
> > +static void send_dummy_bits(SSIBus *spi, uint32_t uma_cfg, uint32_t uma_cmd)
> > +{
> > +    unsigned int bits_per_clock = 1U << FIU_UMA_CFG_DBPCK(uma_cfg);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < FIU_UMA_CFG_DBSIZ(uma_cfg); i++) {
> > +        /* Use bytes 0 and 1 first, then keep repeating byte 2 */
> > +        unsigned int field = (i < 2) ? ((i + 1) * 8) : 24;
> > +        unsigned int j;
> > +
> > +        for (j = 0; j < 8; j += bits_per_clock) {
> > +            ssi_transfer(spi, extract32(uma_cmd, field + j, bits_per_clock));
> > +        }
> > +    }
> > +}
> > +
> > +/* Assert the chip select specified in the UMA Control/Status Register. */
> > +static void npcm7xx_fiu_select(NPCM7xxFIUState *s)
> > +{
> > +    int cs_id;
> > +
> > +    cs_id = FIU_UMA_CTS_DEV_NUM(s->regs[NPCM7XX_FIU_UMA_CTS]);
> > +    if (cs_id < s->cs_count) {
> > +        qemu_irq_lower(s->cs_lines[cs_id]);
> > +    } else {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: UMA to CS%d; this module has only %d chip selects",
> > +                      DEVICE(s)->canonical_path, cs_id, s->cs_count);
> > +        cs_id = -1;
> > +    }
> > +
> > +    s->active_cs = cs_id;
> > +}
> > +
> > +/* Deassert the currently active chip select. */
> > +static void npcm7xx_fiu_deselect(NPCM7xxFIUState *s)
> > +{
> > +    if (s->active_cs < 0) {
> > +        return;
> > +    }
> > +
> > +    qemu_irq_raise(s->cs_lines[s->active_cs]);
> > +    s->active_cs = -1;
> > +}
> > +
> > +/* Perform a User-Mode Access transaction. */
> > +static void npcm7xx_fiu_uma_transaction(NPCM7xxFIUState *s)
> > +{
> > +    uint32_t uma_cts = s->regs[NPCM7XX_FIU_UMA_CTS];
> > +    uint32_t uma_cfg;
> > +    unsigned int i;
> > +
> > +    /* SW_CS means the CS is already forced low, so don't touch it. */
> > +    if (uma_cts & FIU_UMA_CTS_SW_CS) {
> > +        npcm7xx_fiu_select(s);
> > +    }
> > +
> > +    /* Send command, if present. */
> > +    uma_cfg = s->regs[NPCM7XX_FIU_UMA_CFG];
> > +    if (FIU_UMA_CFG_CMDSIZ(uma_cfg) > 0) {
> > +        ssi_transfer(s->spi, extract32(s->regs[NPCM7XX_FIU_UMA_CMD], 0, 8));
> > +    }
> > +
> > +    /* Send address, if present. */
> > +    send_address(s->spi, FIU_UMA_CFG_ADDSIZ(uma_cfg),
> > +                 s->regs[NPCM7XX_FIU_UMA_ADDR]);
> > +
> > +    /* Write data, if present. */
> > +    for (i = 0; i < FIU_UMA_CFG_WDATSIZ(uma_cfg); i++) {
> > +        unsigned int reg =
> > +            (i < 16) ? (NPCM7XX_FIU_UMA_DW0 + i / 4) : NPCM7XX_FIU_UMA_DW3;
> > +        unsigned int field = (i % 4) * 8;
> > +
> > +        ssi_transfer(s->spi, extract32(s->regs[reg], field, 8));
> > +    }
> > +
> > +    /* Send aummy bits, if present. */
>
> Typo "dummy" ;)
>
> > +    send_dummy_bits(s->spi, uma_cfg, s->regs[NPCM7XX_FIU_UMA_CMD]);
> > +
> > +    /* Read data, if present. */
> > +    for (i = 0; i < FIU_UMA_CFG_RDATSIZ(uma_cfg); i++) {
> > +        unsigned int reg = NPCM7XX_FIU_UMA_DR0 + i / 4;
> > +        unsigned int field = (i % 4) * 8;
> > +        uint8_t c;
> > +
> > +        c = ssi_transfer(s->spi, 0);
> > +        if (reg <= NPCM7XX_FIU_UMA_DR3) {
> > +            s->regs[reg] = deposit32(s->regs[reg], field, 8, c);
> > +        }
> > +    }
> > +
> > +    /* Again, don't touch CS if the user is forcing it low. */
> > +    if (uma_cts & FIU_UMA_CTS_SW_CS) {
> > +        npcm7xx_fiu_deselect(s);
> > +    }
> > +
> > +    /* RDYST means a command has completed since it was cleared. */
> > +    s->regs[NPCM7XX_FIU_UMA_CTS] |= FIU_UMA_CTS_RDYST;
> > +    /* EXEC_DONE means Execute Command / Not Done, so clear it here. */
> > +    s->regs[NPCM7XX_FIU_UMA_CTS] &= ~FIU_UMA_CTS_EXEC_DONE;
> > +}
> > +
> > +/* Control register write handler. */
> > +static void npcm7xx_fiu_ctrl_write(void *opaque, hwaddr addr, uint64_t v,
> > +                                   unsigned int size)
> > +{
> > +    hwaddr reg = addr / sizeof(uint32_t);
> > +    NPCM7xxFIUState *s = opaque;
> > +    uint32_t value = v;
> > +
> > +    trace_npcm7xx_fiu_ctrl_write(DEVICE(s)->canonical_path, addr, value);
> > +
> > +    switch (reg) {
> > +    case NPCM7XX_FIU_UMA_CFG:
> > +        if (s->regs[reg] & FIU_UMA_CFG_CMMLCK) {
> > +            value &= ~FIU_UMA_CFG_CMMLCK_MASK;
> > +            value |= (s->regs[reg] & FIU_UMA_CFG_CMMLCK_MASK);
> > +        }
> > +        /* fall through */
> > +    case NPCM7XX_FIU_DRD_CFG:
> > +    case NPCM7XX_FIU_DWR_CFG:
> > +        if (s->regs[reg] & NPCM7XX_FIU_CFG_LCK) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "%s: write to locked register @ 0x%" PRIx64 "\n",
> > +                          DEVICE(s)->canonical_path, addr);
> > +            return;
> > +        }
> > +        s->regs[reg] = value;
> > +        break;
> > +
> > +    case NPCM7XX_FIU_UMA_CTS:
> > +        if (value & FIU_UMA_CTS_RDYST) {
> > +            value &= ~FIU_UMA_CTS_RDYST;
> > +        } else {
> > +            value |= s->regs[reg] & FIU_UMA_CTS_RDYST;
> > +        }
> > +        if ((s->regs[reg] ^ value) & FIU_UMA_CTS_SW_CS) {
> > +            if (value & FIU_UMA_CTS_SW_CS) {
> > +                /*
> > +                 * Don't drop CS if there's a transfer in progress, or we're
> > +                 * about to start one.
> > +                 */
> > +                if (!((value | s->regs[reg]) & FIU_UMA_CTS_EXEC_DONE)) {
> > +                    npcm7xx_fiu_deselect(s);
> > +                }
> > +            } else {
> > +                npcm7xx_fiu_select(s);
> > +            }
> > +        }
> > +        s->regs[reg] = value | (s->regs[reg] & FIU_UMA_CTS_EXEC_DONE);
> > +        if (value & FIU_UMA_CTS_EXEC_DONE) {
> > +            npcm7xx_fiu_uma_transaction(s);
> > +        }
> > +        break;
> > +
> > +    case NPCM7XX_FIU_UMA_DR0 ... NPCM7XX_FIU_UMA_DR3:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: write to read-only register @ 0x%" PRIx64 "\n",
> > +                      DEVICE(s)->canonical_path, addr);
> > +        return;
> > +
> > +    case NPCM7XX_FIU_PRT_CFG:
> > +    case NPCM7XX_FIU_PRT_CMD0 ... NPCM7XX_FIU_PRT_CMD9:
> > +        qemu_log_mask(LOG_UNIMP, "%s: PRT is not implemented\n", __func__);
> > +        break;
> > +
> > +    case NPCM7XX_FIU_UMA_CMD:
> > +    case NPCM7XX_FIU_UMA_ADDR:
> > +    case NPCM7XX_FIU_UMA_DW0 ... NPCM7XX_FIU_UMA_DW3:
> > +    case NPCM7XX_FIU_CFG:
> > +        s->regs[reg] = value;
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "%s: write to invalid offset 0x%" PRIx64 "\n",
> > +                      DEVICE(s)->canonical_path, addr);
> > +        return;
> > +    }
> > +}
> > +
> > +static const MemoryRegionOps npcm7xx_fiu_ctrl_ops = {
> > +    .read = npcm7xx_fiu_ctrl_read,
> > +    .write = npcm7xx_fiu_ctrl_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +        .unaligned = false,
> > +    },
> > +};
> > +
> > +static void npcm7xx_fiu_enter_reset(Object *obj, ResetType type)
> > +{
> > +    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
> > +
> > +    trace_npcm7xx_fiu_enter_reset(DEVICE(obj)->canonical_path, type);
> > +
> > +    memset(s->regs, 0, sizeof(s->regs));
> > +
> > +    s->regs[NPCM7XX_FIU_DRD_CFG] = 0x0300100b;
> > +    s->regs[NPCM7XX_FIU_DWR_CFG] = 0x03000002;
> > +    s->regs[NPCM7XX_FIU_UMA_CFG] = 0x00000400;
> > +    s->regs[NPCM7XX_FIU_UMA_CTS] = 0x00010000;
> > +    s->regs[NPCM7XX_FIU_UMA_CMD] = 0x0000000b;
> > +    s->regs[NPCM7XX_FIU_PRT_CFG] = 0x00000400;
> > +    s->regs[NPCM7XX_FIU_CFG] = 0x0000000b;
> > +}
> > +
> > +static void npcm7xx_fiu_hold_reset(Object *obj)
> > +{
> > +    NPCM7xxFIUState *s = NPCM7XX_FIU(obj);
> > +    int i;
> > +
> > +    trace_npcm7xx_fiu_hold_reset(DEVICE(obj)->canonical_path);
> > +
> > +    for (i = 0; i < s->cs_count; i++) {
> > +        qemu_irq_raise(s->cs_lines[i]);
> > +    }
> > +}
> > +
> > +static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
> > +{
> > +    NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
> > +    SysBusDevice *sbd = &s->parent;
> > +    int i;
> > +
> > +    if (s->cs_count <= 0) {
> > +        error_setg(errp, "%s: %d chip selects specified, need at least one",
> > +                   dev->canonical_path, s->cs_count);
> > +        return;
> > +    }
> > +
> > +    s->spi = ssi_create_bus(dev, "spi");
> > +    s->cs_lines = g_new0(qemu_irq, s->cs_count);
>
> More recent API:
>
>        qdev_init_gpio_out_named(dev, &s->cs_count, "cs", s->cs_count);

Cool. Also made a matching change in npcm7xx_boards.c.

> > +    s->flash = g_new0(NPCM7xxFIUFlash, s->cs_count);
> > +
> > +    /*
> > +     * Register the control registers region first. It may be followed by one
> > +     * or more direct flash access regions.
> > +     */
> > +    memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_fiu_ctrl_ops, s, "ctrl",
> > +                          NPCM7XX_FIU_CTRL_REGS_SIZE);
> > +    sysbus_init_mmio(sbd, &s->mmio);
> > +
> > +    for (i = 0; i < s->cs_count; i++) {
> > +        NPCM7xxFIUFlash *flash = &s->flash[i];
> > +        sysbus_init_irq(sbd, &s->cs_lines[i]);
> > +        flash->fiu = s;
> > +        memory_region_init_io(&flash->direct_access, OBJECT(s),
> > +                              &npcm7xx_fiu_flash_ops, &s->flash[i], "flash",
> > +                              NPCM7XX_FIU_FLASH_WINDOW_SIZE);
> > +        sysbus_init_mmio(sbd, &flash->direct_access);
> > +    }
> > +}
> > +
> > +static const VMStateDescription vmstate_npcm7xx_fiu = {
> > +    .name = "npcm7xx-fiu",
> > +    .version_id = 0,
> > +    .minimum_version_id = 0,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_INT32(active_cs, NPCM7xxFIUState),
> > +        VMSTATE_UINT32_ARRAY(regs, NPCM7xxFIUState, NPCM7XX_FIU_NR_REGS),
> > +        VMSTATE_END_OF_LIST(),
> > +    },
> > +};
> > +
> > +static Property npcm7xx_fiu_properties[] = {
> > +    DEFINE_PROP_INT32("cs-count", NPCM7xxFIUState, cs_count, 0),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data)
> > +{
> > +    ResettableClass *rc = RESETTABLE_CLASS(klass);
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->desc = "NPCM7xx Flash Interface Unit";
> > +    dc->realize = npcm7xx_fiu_realize;
> > +    dc->vmsd = &vmstate_npcm7xx_fiu;
> > +    rc->phases.enter = npcm7xx_fiu_enter_reset;
> > +    rc->phases.hold = npcm7xx_fiu_hold_reset;
> > +    device_class_set_props(dc, npcm7xx_fiu_properties);
> > +}
> > +
> > +static const TypeInfo npcm7xx_fiu_types[] = {
> > +    {
> > +        .name = TYPE_NPCM7XX_FIU,
> > +        .parent = TYPE_SYS_BUS_DEVICE,
> > +        .instance_size = sizeof(NPCM7xxFIUState),
> > +        .class_init = npcm7xx_fiu_class_init,
> > +    },
> > +};
> > +DEFINE_TYPES(npcm7xx_fiu_types);
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index a31d0d282f..8d0ef0593b 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -360,6 +360,7 @@ config NPCM7XX
> >      select ARM_GIC
> >      select PL310  # cache controller
> >      select SERIAL
> > +    select SSI
> >      select UNIMP
> >
> >  config FSL_IMX25
> > diff --git a/hw/ssi/Makefile.objs b/hw/ssi/Makefile.objs
> > index 07a85f1967..cab48e72c9 100644
> > --- a/hw/ssi/Makefile.objs
> > +++ b/hw/ssi/Makefile.objs
> > @@ -5,6 +5,7 @@ common-obj-$(CONFIG_XILINX_SPIPS) += xilinx_spips.o
> >  common-obj-$(CONFIG_ASPEED_SOC) += aspeed_smc.o
> >  common-obj-$(CONFIG_STM32F2XX_SPI) += stm32f2xx_spi.o
> >  common-obj-$(CONFIG_MSF2) += mss-spi.o
> > +common-obj-$(CONFIG_NPCM7XX) += npcm7xx_fiu.o
> >
> >  common-obj-$(CONFIG_OMAP) += omap_spi.o
> >  common-obj-$(CONFIG_IMX) += imx_spi.o
> > diff --git a/hw/ssi/trace-events b/hw/ssi/trace-events
> > index 0ea498de91..8024253c1f 100644
> > --- a/hw/ssi/trace-events
> > +++ b/hw/ssi/trace-events
> > @@ -9,3 +9,12 @@ aspeed_smc_dma_checksum(uint32_t addr, uint32_t data) "0x%08x: 0x%08x"
> >  aspeed_smc_dma_rw(const char *dir, uint32_t flash_addr, uint32_t dram_addr, uint32_t size) "%s flash:@0x%08x dram:@0x%08x size:0x%08x"
> >  aspeed_smc_write(uint64_t addr,  uint32_t size, uint64_t data) "@0x%" PRIx64 " size %u: 0x%" PRIx64
> >  aspeed_smc_flash_select(int cs, const char *prefix) "CS%d %sselect"
> > +
> > +# npcm7xx_fiu.c
> > +
> > +npcm7xx_fiu_enter_reset(const char *id, int reset_type) "%s reset type: %d"
> > +npcm7xx_fiu_hold_reset(const char *id) "%s"
> > +npcm7xx_fiu_ctrl_read(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"
> > +npcm7xx_fiu_ctrl_write(const char *id, uint64_t addr, uint32_t data) "%s offset: 0x%04" PRIx64 " value: 0x%08x"
>
> Nitpicking again: PRIx32 :p

Will do.

> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>

Thanks!

> > +npcm7xx_fiu_flash_read(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
> > +npcm7xx_fiu_flash_write(const char *id, int cs, uint64_t addr, unsigned int size, uint64_t value) "%s[%d] offset: 0x%08" PRIx64 " size: %u value: 0x%" PRIx64
> >


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model
  2020-07-11  6:46                 ` Havard Skinnemoen
@ 2020-07-12  5:49                   ` Havard Skinnemoen
  0 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-12  5:49 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley, Cédric Le Goater

On Fri, Jul 10, 2020 at 11:46 PM Havard Skinnemoen
<hskinnemoen@google.com> wrote:
>
> On Fri, Jul 10, 2020 at 2:31 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >
> > On 7/9/20 7:42 PM, Havard Skinnemoen wrote:
> > > On Thu, Jul 9, 2020 at 10:24 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> > >> On 7/9/20 7:09 PM, Havard Skinnemoen wrote:
> > >>> On Thu, Jul 9, 2020 at 9:23 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> > >>>> On 7/9/20 8:43 AM, Havard Skinnemoen wrote:
> > >>>>> On Wed, Jul 8, 2020 at 11:04 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> > >>>>>> On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> > >>>>>>> Implement a device model for the System Global Control Registers in the
> > >>>>>>> NPCM730 and NPCM750 BMC SoCs.
> > >>>>>>>
> > >>>>>>> This is primarily used to enable SMP boot (the boot ROM spins reading
> > >>>>>>> the SCRPAD register) and DDR memory initialization; other registers are
> > >>>>>>> best effort for now.
> > >>>>>>>
> > >>>>>>> The reset values of the MDLR and PWRON registers are determined by the
> > >>>>>>> SoC variant (730 vs 750) and board straps respectively.
> > >>>>>>>
> > >>>>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> > >>>>>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> > >>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > >>>>>>> ---
> > [...]
> > >>>>>> Maybe use HWADDR_PRIx instead of casting to int?
> > >>>>>
> > >>>>> Will do, thanks!
> > >>>>
> > >>>> Glad you are not annoyed by my review comments.
> > >>>>
> > >>>> FYI your series quality is in my top-4 "adding new machine to QEMU".
> > >>>> I'd like to use it as example to follow.
> > >>>>
> > >>>> I am slowly reviewing because this is not part of my work duties,
> > >>>> so I do that in my free time before/after work, which is why I can
> > >>>> barely do more that 2 per day, as I have to learn the SoC and
> > >>>> cross check documentation (or in this case, existing firmware code
> > >>>> since there is no public doc).
> > >>>
> > >>> Your feedback is super valuable, thanks a lot. I really appreciate it.
> > >>
> > >> OK I'll continue, but might not have time until next week.
> > >> After I plan to test too.
> > >
> > > OK, I'll try to post a v6 before the weekend, unless you prefer that I
> > > hold off until you've reviewed the whole series.
> >
> > I don't mind, if it is not too much overhead on your side.
> >
> > What I noticed on the QEMU community is:
> >
> > - If a series reviewed the same day and is almost done except
> >   a single bugfix, it is OK to repost the same day, so the
> >   maintainer is likely to queue it directly.
> >
> > - If there are various reviews, and you do gradual improvements,
> >   it is OK to repost every 3 days. Else reviewers tend to skip/
> >   postpone your series for later.
> >
> > - Pinging for review before 1 week passed is stressing reviewers,
> >   except in case of critical security bug (CVE) or during freeze.
> >
> > - Series with integrated test provided are usually reviewed first.
>
> This is very helpful, thanks.
>
> > >> What would be useful is an acceptance test (see examples in
> > >> tests/acceptance/), using the artefacts from the link you shared
> > >> elsewhere:
> > >> https://openpower.xyz/job/run-meta-ci/distro=ubuntu,label=builder,target=gsj/lastSuccessfulBuild/artifact/openbmc/build/tmp/deploy/images/gsj/
> > >
> > > Yes, tests are definitely on my radar. I'm working on SPI flash
> > > qtests. I haven't had much success with avocado yet, but I'll keep
> > > trying (perhaps using docker to control the environment more tightly).
> >
> > This might help:
> > https://www.mail-archive.com/qemu-devel@nongnu.org/msg704907.html
>
> Thanks, I got it working now.
>
> > > Since the 5.1 release is frozen now, I might post some followup
> > > patches before this series is merged, e.g. tests, bootrom
> > > submodule+blob, more peripherals, etc. Is it preferable to keep this
> > > series frozen (modulo API updates) since you spent a lot of time
> > > reviewing it, and post the new stuff separately, or is it better to
> > > add new patches to the end of the series and resend the whole thing?
> >
> > If you rework a peripheral, you need to reset the Reviewed-by/Tested-by
> > tags. If you add new peripherals, you only need to reset these tags on
> > the SoC patch. I'm fine either way, I use git-backport-diff to see the
> > SoC changes easily:
> >
> > https://github.com/codyprime/git-scripts/blob/master/git-backport-diff
>
> I've been adding new peripherals incrementally after the basic SoC
> support patch. Is that OK to do without resetting the tags?
>
> But it's more likely that I'll add other things than peripherals next,
> i.e. bootrom and tests.
>
> > >
> > >> But these are apparently not stable links (expire after 30 days?).
> > >
> > > Sorry, I'm too ignorant about Jenkins to know. I'll see if I can
> > > figure something out.
> >
> > What I do in that case is take the binary used for the test,
> > write a comment and push it in a stable branch to my own repo:
> > https://github.com/philmd/qemu-testing-blob/ and use the stable
> > url in the test.
> >
> > We know QEMU emulation worked with this particular binary at some
> > point. We want to avoid regressions in QEMU, so let's keep testing
> > what we know worked. We don't want to track 2 bugs at a time (one
> > in the updated guest and one in QEMU).
>
> Good point. I'll see if I can upload images to github. I might fork
> the openbmc repository and attach binaries to a github release, to
> make it clear where the binaries came from.
>
> I accidentally broke my test image and had some trouble recreating it,
> so I ended up reworking my various hacks a bit. The good news is that
> I got most of them turned into proper bug fixes that I can send
> upstream.
>
> It might take a little longer than I said previously, but I'll try to
> include acceptance tests in the next series.

I uploaded the images here:

https://github.com/hskinnemoen/openbmc/releases/tag/20200711-gsj-qemu-0

I used them to implement an acceptance test that I'll include in v6.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-09  0:36 ` [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen
@ 2020-07-13 14:57   ` Cédric Le Goater
  2020-07-13 17:59     ` Philippe Mathieu-Daudé
  2020-07-14  2:56     ` Havard Skinnemoen
  0 siblings, 2 replies; 64+ messages in thread
From: Cédric Le Goater @ 2020-07-13 14:57 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell, f4bug
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
> one built with OpenBMC. For example like this:
> 
> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
> qemu-system-arm -machine quanta-gsj -nographic \
> 	-bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
> 	-drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>

May be we don't need to create the flash object if dinfo is NULL.


Reviewed-by: Cédric Le Goater <clg@kaod.org>
Tested-by: Cédric Le Goater <clg@kaod.org>

Nice ! 

We need a SPI controller model and a network device model now. 

npcm7xx_bootrom.bin is a bit of a pain. Could we include it in 
the QEMU roms ? 

C.





>================================================
> BootBlock by Nuvoton Technology Corp. Ver 10.10.9
>================================================

BB Basic
Oct 27 2019
10:10:49
 
 


>CORSTC         = 0x04000003=>0x4fff9f9d
>WD0RCR         = 0xffffffff=>0x4fff9f9d
>WD1RCR         = 0xffffffff=>0x4fff9f9d
>WD2RCR         = 0xffffffff=>0x4fff9f9d
>SWRSTC1        = 0x00000003=>0x4fff9f9d
>SWRSTC2        = 0x00000000=>0x4fff9f9d
>SWRSTC3        = 0x00000000=>0x4fff9f9d
>SWRSTC4        = 0x00000000=>0x4fff9f9d
>CLKEN1         = 0xffffffff
>CLKEN2         = 0xffffffff
>CLKEN3         = 0xffffffff
>CLKSEL         = 0x004aaaaa=>0x01f18fc9
>CLKDIV1        = 0x5413f855=>0x5413fa55
>CLKDIV2        = 0xaa4f8f9f=>0xeb4f8f9f
>CLKDIV3        = 0x00000100=>0x0000027e
>PLLCON0        = 0x80222101=>0x80202101
>PLLCON1        = 0x80202101=>0x80402101
>PLLCON2        = 0x80c02105
>VSRCR          = 0x00000000
>INTCR2         = 0x00080000=>0x000c0000
>RESSR          = 0x80000000
>MFSEL1         = 0x00000000=>0x00000a00
>MFSEL2         = 0x00000000
>MFSEL3         = 0x00000000
>MFSEL4         = 0x00000000
>DSCNT          = 0x000000c0
>SMC_CTL        = 0x00000000
>GPnDOUT(7)     = 0x00000000
>GPnOE(7)       = 0x00000000
>FIU_DRD_CFG    = 0x030011bb
>FIU_DWR_CFG    = 0x03000002
>FIU_CFG(0)     = 0x0000000b
>FIU_CFG(3)     = 0x0000000b
>FLOCKR1        = 0x00000000
>RLOCKR1        = 0x00000000
>FCFG1          = 0x20000000=>0x1a000000
>FCFG2          = 0x20000000=>0x1a000000
>MDLR           = 0x00300395
>SPSWC          = 0x00000003=>0x00000000
>LCR(2)         = 0x00000000
>LCR(3)         = 0x00000000
>IPSRST2        = 0x80000000
>PWRON          = 0x00001fff
>ETSR           = 0x00000000
>FUSTRAP        = 0x00000000
>SCRPAD         = 0x00000000

>OTP values:
>FINAL_TEST_SIGNATURE (Product OTP)   =  0x00000000 0x00000000
>FUSTRAP                              =  0x00000000
>CP_FUSTRAP                           =  0x00000000
>DAC_CALIB                            =  0x00000000
>ADC_CALIB                            =  0x00000000
>DIE_LOCATION                         =  0x00000000
>DERIVATIVE                           =  0x00300395

>Device: Poleg BMC NPCM730

>set INTCR3_GFXRDEN and INTCR3_GFXRSTDLY
>INTCR          = 0x0000035e=>0x0004831e
>INTCR2         = 0x00080000
>INTCR3         = 0x00001202=>0x06001252
>EB
>Board manufacturer: Nuvoton 

> CPU CLK is 800000000 Hz   (header val is 800000000 Hz )
> MC  CLK is 800000000 Hz   (header val is 800000000 Hz )

>PLL0 = 800MHz 
>PLL1 = 800MHz 
>PLL2 = 480MHz 
>APB1 = 25MHz  
>APB2 = 50MHz 
>APB3 = 50MHz 
>APB4 = 25MHz 
>APB5 = 100MHz 
>SPI0 = 20MHz 
>SPI3 = 20MHz 
>ADC  = 25MHz 
>CP   = 200MHz 
>TMC  = 25MHz 
>GFX  = 160MHz 
>PCI  = 133MHz 


> PLLs were not reconfigured by BB
>Last reset was PORST 
>vgaioen=0 and don't mux gspi.

>HOST IF: LPC
>Host LPC Released>MC already configured.

>Skip DDR init.
>Basic mode


bootblock at 0x80000000, uboot at 0x8000e000, image num 0
>set FIU0 FIU_DRD_CFG to  0x30111bc 

>copied uboot image to 0x8000, size 0x620f0, from 0x8000e000 

>ROM mailbox status :  ST_ROM_NO_STATUS                            

>imageState: 		img0=  IMAGE_NOT_TESTED             img1=  IMAGE_NOT_TESTED             img2= IMAGE_NOT_TESTED             

>BB mailbox status :  ST_ROM_NO_STATUS                            
>imageState: 		img0=  IMAGE_NOT_TESTED             img1=  IMAGE_NOT_TESTED             img2= IMAGE_NOT_TESTED             
>Boot block run for 277403 us.

>Jump to uboot at 0x8000


U-Boot 2019.01 (Jun 15 2020 - 08:23:29 +0000)

CPU: NPCM7XX A1 @ Model: Nuvoton npcm750 Development Board
Board: Nuvoton npcm750 Development Board
DRAM:  464 MiB
l2_pl310_init
OTP: NPCM750 module bind OK
RNG: NPCM750 RNG module bind OK
AES: NPCM750 AES module bind OK
SHA: NPCM750 SHA module bind OK
MMC:   sdhci_setup_cfg: Hardware doesn't specify base clock frequency
sdhci1@f0842000 - probe failed: -22
sdhci_setup_cfg: Hardware doesn't specify base clock frequency

Loading Environment from SPI Flash... SF: Detected mx25l25635f with page size 256 Bytes, erase size 64 KiB, total 32 MiB, mapped at 80000000
*** Warning - bad CRC, using default environment

In:    serial0@f0001000
Out:   serial0@f0001000
Err:   serial0@f0001000
Net:   
Error: gmac0@f0802000 address not set.
eth-1: gmac0@f0802000
Error: gmac1@f0804000 address not set.
, eth-1: gmac1@f0804000
Error: emc0@f0825000 address not set.
, eth-1: emc0@f0825000
Error: emc1@f0825000 address not set.
, eth-1: emc1@f0825000
Security is NOT enabled
SF: Detected mx25l25635f with page size 256 Bytes, erase size 64 KiB, total 32 MiB, mapped at 80000000
Hit any key to stop autoboot:  0 
Booting Kernel from flash
+++ uimage at 0x80200000
Using bootargs: earlycon=uart8250,mmio32,0xf0001000 root=/dev/ram console=ttyS0,115200n8 mem=464M ramdisk_size=48000 basemac=
## Loading kernel from FIT Image at 80200000 ...
   Using 'conf@nuvoton-npcm730-gsj.dtb' configuration
   Trying 'kernel@1' kernel subimage
     Description:  Linux kernel
     Type:         Kernel Image
     Compression:  uncompressed
     Data Start:   0x80200124
     Data Size:    3049504 Bytes = 2.9 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: 0x00008000
     Entry Point:  0x00008000
     Hash algo:    sha256
     Hash value:   2c3b4ad63288300ab380774501a930b5a55683df90f1f23b9329393187194865
   Verifying Hash Integrity ... sha256+ OK
## Loading ramdisk from FIT Image at 80200000 ...
   Using 'conf@nuvoton-npcm730-gsj.dtb' configuration
   Trying 'ramdisk@1' ramdisk subimage
     Description:  obmc-phosphor-initramfs
     Type:         RAMDisk Image
     Compression:  uncompressed
     Data Start:   0x804f1814
     Data Size:    1097672 Bytes = 1 MiB
     Architecture: ARM
     OS:           Linux
     Load Address: unavailable
     Entry Point:  unavailable
     Hash algo:    sha256
     Hash value:   7a8ac1c81726efb38ec4ee2db0c6291e9788b7a13c4de158644ad912537fe008
   Verifying Hash Integrity ... sha256+ OK
## Loading fdt from FIT Image at 80200000 ...
   Using 'conf@nuvoton-npcm730-gsj.dtb' configuration
   Trying 'fdt@nuvoton-npcm730-gsj.dtb' fdt subimage
     Description:  Flattened Device Tree blob
     Type:         Flat Device Tree
     Compression:  uncompressed
     Data Start:   0x804e8a58
     Data Size:    36079 Bytes = 35.2 KiB
     Architecture: ARM
     Hash algo:    sha256
     Hash value:   cbc6ffb46a6f195e59ed86e9596424df5dc022c807f221487f970faa321f2cd6
   Verifying Hash Integrity ... sha256+ OK
   Booting using the fdt blob at 0x804e8a58
   Loading Kernel Image ... OK
   Loading Ramdisk to 012f4000, end 013fffc8 ... OK
   Loading Device Tree to 012e8000, end 012f3cee ... OK

Starting kernel ...

Booting Linux on physical CPU 0x0
Linux version 5.4.32-7dc9442 (oe-user@oe-host) (gcc version 10.1.0 (GCC)) #1 SMP Thu Jun 25 06:55:32 UTC 2020
CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
OF: fdt: Machine model: Quanta GSJ Board (Device Tree v12)
earlycon: uart8250 at MMIO32 0xf0001000 (options '')
printk: bootconsole [uart8250] enabled
Memory policy: Data cache writeback
CPU: All CPU(s) started in SVC mode.
percpu: Embedded 18 pages/cpu s43724 r8192 d21812 u73728
Built 1 zonelists, mobility grouping on.  Total pages: 117856
Kernel command line: earlycon=uart8250,mmio32,0xf0001000 root=/dev/ram console=ttyS0,115200n8 mem=464M ramdisk_size=48000 basemac=
Dentry cache hash table entries: 65536 (order: 6, 262144 bytes, linear)
Inode-cache hash table entries: 32768 (order: 5, 131072 bytes, linear)
mem auto-init: stack:off, heap alloc:off, heap free:off
Memory: 456772K/475136K available (7168K kernel code, 457K rwdata, 1780K rodata, 1024K init, 2151K bss, 18364K reserved, 0K cma-reserved)
ftrace: allocating 25188 entries in 50 pages
rcu: Hierarchical RCU implementation.
rcu: 	RCU event tracing is enabled.
rcu: 	RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2.
rcu: RCU calculated value of scheduler-enlistment delay is 10 jiffies.
rcu: Adjusting geometry for rcu_fanout_leaf=16, nr_cpu_ids=2
NR_IRQS: 16, nr_irqs: 16, preallocated irqs: 16
L2C-310 enabling early BRESP for Cortex-A9
L2C-310 full line of zeros enabled for Cortex-A9
L2C-310 ID prefetch enabled, offset 1 lines
L2C-310 dynamic clock gating disabled, standby mode disabled
L2C-310 cache controller enabled, 16 ways, 512 kB
L2C-310: CACHE_ID 0x410000c8, AUX_CTRL 0x76450001
random: get_random_bytes called from start_kernel+0x2bc/0x458 with crng_init=0
clocksource: npcm7xx-timer1: mask: 0xffffff max_cycles: 0xffffff, max_idle_ns: 597268854 ns
Enabling NPCM7xx clocksource timer base: cd80a000, IRQ: 17 
Console: colour dummy device 80x30
sched_clock: 32 bits at 100 Hz, resolution 10000000ns, wraps every 21474836475000000ns
Calibrating delay loop... 1515.11 BogoMIPS (lpj=7575552)
pid_max: default: 32768 minimum: 301
Mount-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
Mountpoint-cache hash table entries: 1024 (order: 0, 4096 bytes, linear)
CPU: Testing write buffer coherency: ok
CPU0: Spectre v2: using BPIALL workaround
CPU0: thread -1, cpu 0, socket 0, mpidr 80000000
Setting up static identity map for 0x100000 - 0x100060
rcu: Hierarchical SRCU implementation.
smp: Bringing up secondary CPUs ...
CPU1: thread -1, cpu 1, socket 0, mpidr 80000001
CPU1: Spectre v2: using BPIALL workaround
smp: Brought up 1 node, 2 CPUs
SMP: Total of 2 processors activated (3174.80 BogoMIPS).
CPU: All CPU(s) started in SVC mode.
devtmpfs: initialized
clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 19112604462750000 ns
futex hash table entries: 512 (order: 3, 32768 bytes, linear)
pinctrl core: initialized pinctrl subsystem
NET: Registered protocol family 16
DMA: preallocated 256 KiB pool for atomic coherent allocations
NPCM7xx Pinctrl driver probed
SCSI subsystem initialized
usbcore: registered new interface driver usbfs
usbcore: registered new interface driver hub
usbcore: registered new device driver usb
pps_core: LinuxPPS API ver. 1 registered
pps_core: Software ver. 5.3.6 - Copyright 2005-2007 Rodolfo Giometti <giometti@linux.it>
PTP clock support registered
EDAC MC: Ver: 3.0.0
clocksource: Switched to clocksource npcm7xx-timer1
thermal_sys: Registered thermal governor 'step_wise'
NET: Registered protocol family 2
tcp_listen_portaddr_hash hash table entries: 512 (order: 0, 6144 bytes, linear)
TCP established hash table entries: 4096 (order: 2, 16384 bytes, linear)
TCP bind hash table entries: 4096 (order: 3, 32768 bytes, linear)
TCP: Hash tables configured (established 4096 bind 4096)
UDP hash table entries: 256 (order: 1, 8192 bytes, linear)
UDP-Lite hash table entries: 256 (order: 1, 8192 bytes, linear)
NET: Registered protocol family 1
RPC: Registered named UNIX socket transport module.
RPC: Registered udp transport module.
RPC: Registered tcp transport module.
RPC: Registered tcp NFSv4.1 backchannel transport module.
Trying to unpack rootfs image as initramfs...
Freeing initrd memory: 1072K
workingset: timestamp_bits=30 max_order=17 bucket_order=0
squashfs: version 4.0 (2009/01/31) Phillip Lougher
Key type cifs.idmap registered
jffs2: version 2.2. (NAND) (SUMMARY)  © 2001-2006 Red Hat, Inc.
romfs: ROMFS MTD (C) 2007 Red Hat, Inc.
NET: Registered protocol family 38
Block layer SCSI generic (bsg) driver version 0.4 loaded (major 249)
io scheduler mq-deadline registered
io scheduler kyber registered
Serial: 8250/16550 driver, 4 ports, IRQ sharing disabled
printk: console [ttyS0] disabled
f0001000.serial: ttyS0 at MMIO 0xf0001000 (irq = 29, base_baud = 1500000) is a Nuvoton 16550
printk: console [ttyS0] enabled
printk: console [ttyS0] enabled
printk: bootconsole [uart8250] disabled
printk: bootconsole [uart8250] disabled
f0002000.serial: ttyS1 at MMIO 0xf0002000 (irq = 30, base_baud = 1500000) is a Nuvoton 16550
f0003000.serial: ttyS2 at MMIO 0xf0003000 (irq = 31, base_baud = 1500000) is a Nuvoton 16550
f0004000.serial: ttyS3 at MMIO 0xf0004000 (irq = 32, base_baud = 1500000) is a Nuvoton 16550
brd: module loaded
loop: module loaded
NPCM7xx PCI Mailbox probed
spi_master spi0: /ahb/fiu@fb000000/spi-nor@0 has no valid 'spi-max-frequency' property (-22)
spi_master spi0: Failed to create SPI device for /ahb/fiu@fb000000/spi-nor@0
libphy: Fixed MDIO Bus: probed
8<--- cut here ---
Unable to handle kernel paging request at virtual address fffffffe
pgd = (ptrval)
[fffffffe] *pgd=1cfff841, *pte=00000000, *ppte=00000000
Internal error: Oops: 27 [#1] SMP ARM
Modules linked in:
CPU: 0 PID: 1 Comm: swapper/0 Not tainted 5.4.32-7dc9442 #1
Hardware name: NPCM7XX Chip family
PC is at get_mac_address.constprop.0+0x30/0xb8
LR is at 0x4
pc : [<b0521614>]    lr : [<00000004>]    psr: a0000013
sp : cc0bbd08  ip : 00000000  fp : cc0bbd24
r10: 00000001  r9 : cc218888  r8 : cc218500
r7 : cc1aa410  r6 : cc1aa400  r5 : 00000000  r4 : cc218000
r3 : cc44d108  r2 : 00000000  r1 : cc1aa42c  r0 : fffffffe
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment none
Control: 10c5387d  Table: 00004059  DAC: 00000051
Process swapper/0 (pid: 1, stack limit = 0x(ptrval))
Stack: (0xcc0bbd08 to 0xcc0bc000)
bd00:                   cc218000 00000000 cc1aa400 cc1aa410 cc0bbd64 cc0bbd28
bd20: b0522964 b05215f0 00000000 00000000 00000000 b0d854b4 b0b44a94 00000000
bd40: cc1aa410 b0b44a94 00000000 b0d854b4 b0b44a94 b0b683a0 cc0bbd84 cc0bbd68
bd60: b04b5cb4 b0522638 cc1aa410 b0d854b0 00000000 00000000 cc0bbdc4 cc0bbd88
bd80: b04b309c b04b5c68 b0b44a94 cc1aa410 cc1aa504 00000000 cc0bbdc4 cc1aa410
bda0: b0b44a94 cc1aa454 b0b44a94 b0a00510 b0a53854 b0a78aa8 cc0bbdf4 cc0bbdc8
bdc0: b04b3860 b04b2e90 cc1aa410 b0b44a94 cc1aa410 cc1aa410 00000000 cc1aa454
bde0: b0b44a94 b0a00510 cc0bbe14 cc0bbdf8 b04b3bac b04b3758 00000000 b0b44a94
be00: cc1aa410 b0b3dd20 cc0bbe34 cc0bbe18 b04b3d38 b04b3b40 00000000 b0b44a94
be20: b04b3be8 b0b3dd20 cc0bbe64 cc0bbe38 b04b0c14 b04b3bf4 cc45bb80 cc08ac58
be40: cc1abcb4 b0b03e08 cc0bbe74 b0b44a94 cc45bb80 00000000 cc0bbe74 cc0bbe68
be60: b04b2748 b04b0bb0 cc0bbe9c cc0bbe78 b04b213c b04b2728 b093d1b0 b078a5a8
be80: b0b44a94 00000000 ffffe000 00000000 cc0bbeb4 cc0bbea0 b04b4940 b04b2020
bea0: b0b3dd20 b0a2dd84 cc0bbecc cc0bbeb8 b04b5c04 b04b4870 b0b5eb00 b0a2dd84
bec0: cc0bbedc cc0bbed0 b0a2dda8 b04b5bc0 cc0bbf4c cc0bbee0 b0102d80 b0a2dd90
bee0: 00000006 00000000 b0a00510 b0b03e00 b08e51fc b08e521c b08e5248 b0a00510
bf00: 00000000 00000006 00000006 000000ba cc0bbf34 ccc3d465 ccc3d46d b0b03e08
bf20: b09bbac4 00000007 b0b72700 b0b03e08 00000007 b0b72700 b0a53834 b09bbac4
bf40: cc0bbf94 cc0bbf50 b0a01280 b0102cbc 00000006 00000006 00000000 b0a00510
bf60: ffffe000 000000ba cc0bbf8c 00000000 b0794eb4 00000000 00000000 00000000
bf80: 00000000 00000000 cc0bbfac cc0bbf98 b0794ecc b0a01130 00000000 b0794eb4
bfa0: 00000000 cc0bbfb0 b01010e8 b0794ec0 00000000 00000000 00000000 00000000
bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bfe0: 00000000 00000000 00000000 00000000 00000013 00000000 00000000 00000000
Backtrace: 
[<b05215e4>] (get_mac_address.constprop.0) from [<b0522964>] (npcm7xx_ether_probe+0x338/0x4d0)
 r7:cc1aa410 r6:cc1aa400 r5:00000000 r4:cc218000
[<b052262c>] (npcm7xx_ether_probe) from [<b04b5cb4>] (platform_drv_probe+0x58/0xa4)
 r10:b0b683a0 r9:b0b44a94 r8:b0d854b4 r7:00000000 r6:b0b44a94 r5:cc1aa410
 r4:00000000
[<b04b5c5c>] (platform_drv_probe) from [<b04b309c>] (really_probe+0x218/0x4c8)
 r7:00000000 r6:00000000 r5:b0d854b0 r4:cc1aa410
[<b04b2e84>] (really_probe) from [<b04b3860>] (driver_probe_device+0x114/0x15c)
 r10:b0a78aa8 r9:b0a53854 r8:b0a00510 r7:b0b44a94 r6:cc1aa454 r5:b0b44a94
 r4:cc1aa410
[<b04b374c>] (driver_probe_device) from [<b04b3bac>] (device_driver_attach+0x78/0xb4)
 r8:b0a00510 r7:b0b44a94 r6:cc1aa454 r5:00000000 r4:cc1aa410
[<b04b3b34>] (device_driver_attach) from [<b04b3d38>] (__driver_attach+0x150/0x158)
 r7:b0b3dd20 r6:cc1aa410 r5:b0b44a94 r4:00000000
[<b04b3be8>] (__driver_attach) from [<b04b0c14>] (bus_for_each_dev+0x70/0xd0)
 r7:b0b3dd20 r6:b04b3be8 r5:b0b44a94 r4:00000000
[<b04b0ba4>] (bus_for_each_dev) from [<b04b2748>] (driver_attach+0x2c/0x30)
 r6:00000000 r5:cc45bb80 r4:b0b44a94
[<b04b271c>] (driver_attach) from [<b04b213c>] (bus_add_driver+0x128/0x214)
[<b04b2014>] (bus_add_driver) from [<b04b4940>] (driver_register+0xdc/0x118)
 r7:00000000 r6:ffffe000 r5:00000000 r4:b0b44a94
[<b04b4864>] (driver_register) from [<b04b5c04>] (__platform_driver_register+0x50/0x58)
 r5:b0a2dd84 r4:b0b3dd20
[<b04b5bb4>] (__platform_driver_register) from [<b0a2dda8>] (npcm7xx_ether_driver_init+0x24/0x28)
 r5:b0a2dd84 r4:b0b5eb00
[<b0a2dd84>] (npcm7xx_ether_driver_init) from [<b0102d80>] (do_one_initcall+0xd0/0x260)
[<b0102cb0>] (do_one_initcall) from [<b0a01280>] (kernel_init_freeable+0x15c/0x1f4)
 r7:b09bbac4 r6:b0a53834 r5:b0b72700 r4:00000007
[<b0a01124>] (kernel_init_freeable) from [<b0794ecc>] (kernel_init+0x18/0x120)
 r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:b0794eb4
 r4:00000000
[<b0794eb4>] (kernel_init) from [<b01010e8>] (ret_from_fork+0x14/0x2c)
Exception stack(0xcc0bbfb0 to 0xcc0bbff8)
bfa0:                                     00000000 00000000 00000000 00000000
bfc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
bfe0: 00000000 00000000 00000000 00000000 00000013 00000000
 r5:b0794eb4 r4:00000000
Code: e5960190 eb0399cc e3500000 159431c8 (15902000) 
---[ end trace a0f295d64d5e2753 ]---
Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b
CPU1: stopping
CPU: 1 PID: 0 Comm: swapper/1 Tainted: G      D           5.4.32-7dc9442 #1
Hardware name: NPCM7XX Chip family
Backtrace: 
[<b0107c4c>] (dump_backtrace) from [<b010823c>] (show_stack+0x20/0x24)
 r7:00000000 r6:600f0193 r5:00000000 r4:b0b5b1b8
[<b010821c>] (show_stack) from [<b077b170>] (dump_stack+0x94/0xa8)
[<b077b0dc>] (dump_stack) from [<b010a20c>] (handle_IPI+0x1f0/0x38c)
 r7:00000000 r6:00000004 r5:b0b5eb64 r4:b0b729e0
[<b010a01c>] (handle_IPI) from [<b0102264>] (gic_handle_irq+0x9c/0xa0)
 r10:00000000 r9:cc0f5f48 r8:cd803100 r7:cd802100 r6:cd80210c r5:b0b37db4
 r4:b0b04504
[<b01021c8>] (gic_handle_irq) from [<b0101a8c>] (__irq_svc+0x6c/0x90)
Exception stack(0xcc0f5f48 to 0xcc0f5f90)
5f40:                   00000000 00001764 ccc33774 b01112e0 cc0f4000 00000001
5f60: b0b03e28 b0b03e6c b0b5e72c b08e5e98 00000000 cc0f5fa4 cc0f5fa8 cc0f5f98
5f80: b0104114 b0104118 600f0013 ffffffff
 r9:cc0f4000 r8:b0b5e72c r7:cc0f5f7c r6:ffffffff r5:600f0013 r4:b0104118
[<b01040d4>] (arch_cpu_idle) from [<b014b240>] (do_idle+0xec/0x140)
[<b014b154>] (do_idle) from [<b014b56c>] (cpu_startup_entry+0x28/0x2c)
 r9:410fc090 r8:00004059 r7:b0b729e8 r6:10c0387d r5:00000001 r4:0000008a
[<b014b544>] (cpu_startup_entry) from [<b0109d7c>] (secondary_start_kernel+0x184/0x18c)
[<b0109bf8>] (secondary_start_kernel) from [<001026ec>] (0x1026ec)
 r5:00000051 r4:1c0cc059
---[ end Kernel panic - not syncing: Attempted to kill init! exitcode=0x0000000b ]---



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-09  0:36 ` [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
  2020-07-09  6:11   ` Philippe Mathieu-Daudé
@ 2020-07-13 15:02   ` Cédric Le Goater
  2020-07-14  0:44     ` Havard Skinnemoen
  1 sibling, 1 reply; 64+ messages in thread
From: Cédric Le Goater @ 2020-07-13 15:02 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell, f4bug
  Cc: kfting, qemu-arm, Joel Stanley, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
> Management Controllers in servers. While the family includes four SoCs,
> this patch implements limited support for two of them: NPCM730 (targeted
> for Data Center applications) and NPCM750 (targeted for Enterprise
> applications).
> 
> This patch includes little more than the bare minimum needed to boot a
> Linux kernel built with NPCM7xx support in direct-kernel mode:
> 
>   - Two Cortex-A9 CPU cores with built-in periperhals.
>   - Global Configuration Registers.
>   - Clock Management.
>   - 3 Timer Modules with 5 timers each.
>   - 4 serial ports.
> 
> The chips themselves have a lot more features, some of which will be
> added to the model at a later stage.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/arm/npcm7xx.h |  86 +++++++++
>  hw/arm/npcm7xx.c         | 376 +++++++++++++++++++++++++++++++++++++++
>  hw/arm/Kconfig           |   5 +
>  hw/arm/Makefile.objs     |   1 +
>  4 files changed, 468 insertions(+)
>  create mode 100644 include/hw/arm/npcm7xx.h
>  create mode 100644 hw/arm/npcm7xx.c
> 
> diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> new file mode 100644
> index 0000000000..95d9224f59
> --- /dev/null
> +++ b/include/hw/arm/npcm7xx.h
> @@ -0,0 +1,86 @@
> +/*
> + * Nuvoton NPCM7xx SoC family.
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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_H
> +#define NPCM7XX_H
> +
> +#include "hw/boards.h"
> +#include "hw/cpu/a9mpcore.h"
> +#include "hw/misc/npcm7xx_clk.h"
> +#include "hw/misc/npcm7xx_gcr.h"
> +#include "hw/timer/npcm7xx_timer.h"
> +#include "target/arm/cpu.h"
> +
> +#define NPCM7XX_MAX_NUM_CPUS    (2)
> +
> +/* The first half of the address space is reserved for DDR4 DRAM. */
> +#define NPCM7XX_DRAM_BA         (0x00000000)
> +#define NPCM7XX_DRAM_SZ         (2 * GiB)
> +
> +/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
> +#define NPCM7XX_LOADER_START            (0x00000000)  /* Start of SDRAM */
> +#define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
> +#define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
> +#define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
> +
> +typedef struct NPCM7xxState {
> +    DeviceState         parent;
> +
> +    ARMCPU              cpu[NPCM7XX_MAX_NUM_CPUS];
> +    A9MPPrivState       a9mpcore;
> +
> +    MemoryRegion        sram;
> +    MemoryRegion        irom;
> +    MemoryRegion        ram3;
> +    MemoryRegion        *dram;
> +
> +    NPCM7xxGCRState     gcr;
> +    NPCM7xxCLKState     clk;
> +    NPCM7xxTimerCtrlState tim[3];
> +} NPCM7xxState;
> +
> +#define TYPE_NPCM7XX    "npcm7xx"
> +#define NPCM7XX(obj)    OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
> +
> +#define TYPE_NPCM730    "npcm730"
> +#define TYPE_NPCM750    "npcm750"
> +
> +typedef struct NPCM7xxClass {
> +    DeviceClass         parent;
> +
> +    /* Bitmask of modules that are permanently disabled on this chip. */
> +    uint32_t            disabled_modules;
> +    /* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
> +    uint32_t            num_cpus;
> +} NPCM7xxClass;
> +
> +#define NPCM7XX_CLASS(klass)                                            \
> +    OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
> +#define NPCM7XX_GET_CLASS(obj)                                          \
> +    OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
> +
> +/**
> + * npcm7xx_write_secondary_boot - Write stub for booting secondary CPU.
> + * @cpu: The CPU to be booted.
> + * @info: Boot info structure for the board.
> + *
> + * This will write a short code stub to the internal ROM that will keep the
> + * secondary CPU spinning until the primary CPU writes an address to the SCRPAD
> + * register in the GCR, after which the secondary CPU will jump there.
> + */
> +extern void npcm7xx_write_secondary_boot(ARMCPU *cpu,
> +                                         const struct arm_boot_info *info);
> +
> +#endif /* NPCM7XX_H */
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> new file mode 100644
> index 0000000000..af45f3c716
> --- /dev/null
> +++ b/hw/arm/npcm7xx.c
> @@ -0,0 +1,376 @@
> +/*
> + * Nuvoton NPCM7xx SoC family.
> + *
> + * 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 as published by the
> + * Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * 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 "exec/address-spaces.h"
> +#include "hw/arm/npcm7xx.h"
> +#include "hw/char/serial.h"
> +#include "hw/loader.h"
> +#include "hw/misc/unimp.h"
> +#include "hw/qdev-properties.h"
> +#include "qapi/error.h"
> +#include "qemu/units.h"
> +#include "sysemu/sysemu.h"
> +
> +/*
> + * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
> + * that aren't handled by any device.
> + */
> +#define NPCM7XX_MMIO_BA         (0x80000000)
> +#define NPCM7XX_MMIO_SZ         (0x7ffd0000)
> +
> +/* Core system modules. */
> +#define NPCM7XX_L2C_BA          (0xf03fc000)
> +#define NPCM7XX_CPUP_BA         (0xf03fe000)
> +#define NPCM7XX_GCR_BA          (0xf0800000)
> +#define NPCM7XX_CLK_BA          (0xf0801000)
> +
> +/* Internal AHB SRAM */
> +#define NPCM7XX_RAM3_BA         (0xc0008000)
> +#define NPCM7XX_RAM3_SZ         (4 * KiB)
> +
> +/* Memory blocks at the end of the address space */
> +#define NPCM7XX_RAM2_BA         (0xfffd0000)
> +#define NPCM7XX_RAM2_SZ         (128 * KiB)
> +#define NPCM7XX_ROM_BA          (0xffff0000)
> +#define NPCM7XX_ROM_SZ          (64 * KiB)
> +
> +/*
> + * Interrupt lines going into the GIC. This does not include internal Cortex-A9
> + * interrupts.
> + */
> +enum NPCM7xxInterrupt {
> +    NPCM7XX_UART0_IRQ           = 2,
> +    NPCM7XX_UART1_IRQ,
> +    NPCM7XX_UART2_IRQ,
> +    NPCM7XX_UART3_IRQ,
> +    NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
> +    NPCM7XX_TIMER1_IRQ,
> +    NPCM7XX_TIMER2_IRQ,
> +    NPCM7XX_TIMER3_IRQ,
> +    NPCM7XX_TIMER4_IRQ,
> +    NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
> +    NPCM7XX_TIMER6_IRQ,
> +    NPCM7XX_TIMER7_IRQ,
> +    NPCM7XX_TIMER8_IRQ,
> +    NPCM7XX_TIMER9_IRQ,
> +    NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
> +    NPCM7XX_TIMER11_IRQ,
> +    NPCM7XX_TIMER12_IRQ,
> +    NPCM7XX_TIMER13_IRQ,
> +    NPCM7XX_TIMER14_IRQ,
> +};
> +
> +/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
> +#define NPCM7XX_NUM_IRQ         (160)
> +
> +/* Register base address for each Timer Module */
> +static const hwaddr npcm7xx_tim_addr[] = {
> +    0xf0008000,
> +    0xf0009000,
> +    0xf000a000,
> +};
> +
> +/* Register base address for each 16550 UART */
> +static const hwaddr npcm7xx_uart_addr[] = {
> +    0xf0001000,
> +    0xf0002000,
> +    0xf0003000,
> +    0xf0004000,
> +};
> +
> +void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
> +{
> +    /*
> +     * The default smpboot stub halts the secondary CPU with a 'wfi'
> +     * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
> +     * does not send an IPI to wake it up, so the second CPU fails to boot. So
> +     * we need to provide our own smpboot stub that can not use 'wfi', it has
> +     * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
> +     */
> +    uint32_t smpboot[] = {
> +        0xe59f2018,     /* ldr r2, bootreg_addr */
> +        0xe3a00000,     /* mov r0, #0 */
> +        0xe5820000,     /* str r0, [r2] */
> +        0xe320f002,     /* wfe */
> +        0xe5921000,     /* ldr r1, [r2] */
> +        0xe1110001,     /* tst r1, r1 */
> +        0x0afffffb,     /* beq <wfe> */
> +        0xe12fff11,     /* bx r1 */
> +        NPCM7XX_SMP_BOOTREG_ADDR,
> +    };
> +    int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
> +        smpboot[i] = tswap32(smpboot[i]);
> +    }
> +
> +    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
> +                       NPCM7XX_SMP_LOADER_START);
> +}
> +
> +static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
> +{
> +    return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> +}
> +
> +static void npcm7xx_init(Object *obj)
> +{
> +    NPCM7xxState *s = NPCM7XX(obj);
> +    int i;
> +
> +    for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
> +        object_initialize_child(obj, "cpu[*]", &s->cpu[i],
> +                                ARM_CPU_TYPE_NAME("cortex-a9"));
> +    }
> +
> +    object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
> +    object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
> +    object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
> +                              "power-on-straps");
> +    object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> +
> +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> +        object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> +    }
> +}
> +
> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
> +{
> +    NPCM7xxState *s = NPCM7XX(dev);
> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> +    int i;
> +
> +    /* CPUs */
> +    for (i = 0; i < nc->num_cpus; i++) {
> +        object_property_set_int(OBJECT(&s->cpu[i]),
> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
> +                                "mp-affinity", &error_abort);
> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
> +                                "reset-cbar", &error_abort);
> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
> +                                 "reset-hivecs", &error_abort);
> +
> +        /* Disable security extensions. */
> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
> +                                 &error_abort);
> +
> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);

I would check the error:
 
        if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
            return;
        }

same for the sysbus_realize() below.

> +    }
> +
> +    /* A9MPCORE peripherals */
> +    object_property_set_int(OBJECT(&s->a9mpcore), nc->num_cpus, "num-cpu",
> +                            &error_abort);
> +    object_property_set_int(OBJECT(&s->a9mpcore), NPCM7XX_NUM_IRQ, "num-irq",
> +                            &error_abort);
> +    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
> +
> +    for (i = 0; i < nc->num_cpus; i++) {
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
> +    }
> +
> +    /* L2 cache controller */
> +    sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
> +
> +    /* System Global Control Registers (GCR) */
> +    object_property_set_int(OBJECT(&s->gcr), nc->disabled_modules,
> +                            "disabled-modules", &error_abort);
> +    object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
> +    sysbus_realize(SYS_BUS_DEVICE(&s->gcr), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
> +
> +    /* Clock Control Registers (CLK) */
> +    sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
> +
> +    /* Timer Modules (TIM) */
> +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
> +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> +        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
> +        int first_irq;
> +        int j;
> +
> +        sysbus_realize(sbd, &error_abort);
> +        sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
> +
> +        first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
> +        for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
> +            qemu_irq irq = npcm7xx_irq(s, first_irq + j);
> +            sysbus_connect_irq(sbd, j, irq);
> +        }
> +    }
> +
> +    /* UART0..3 (16550 compatible) */
> +    for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
> +        serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
> +                       npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
> +                       serial_hd(i), DEVICE_LITTLE_ENDIAN);
> +    }
> +
> +    /* RAM2 (SRAM) */
> +    memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
> +                           NPCM7XX_RAM2_SZ, &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
> +
> +    /* RAM3 (SRAM) */
> +    memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
> +                           NPCM7XX_RAM3_SZ, &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
> +
> +    /* Internal ROM */
> +    memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
> +                           &error_abort);
> +    memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
> +
> +    create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.rng",          0xf000b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.adc",          0xf000c000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[0]",      0xf0010000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[1]",      0xf0011000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[2]",      0xf0012000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[3]",      0xf0013000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[4]",      0xf0014000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[5]",      0xf0015000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[6]",      0xf0016000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.gpio[7]",      0xf0017000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[0]",     0xf0080000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[1]",     0xf0081000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[2]",     0xf0082000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[3]",     0xf0083000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[4]",     0xf0084000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[5]",     0xf0085000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[6]",     0xf0086000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[7]",     0xf0087000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[8]",     0xf0088000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[9]",     0xf0089000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[10]",    0xf008a000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[11]",    0xf008b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[12]",    0xf008c000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[13]",    0xf008d000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[14]",    0xf008e000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.smbus[15]",    0xf008f000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pwm[0]",       0xf0103000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pwm[1]",       0xf0104000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
> +    create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.ehci",         0xf0806000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.ohci",         0xf0807000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
> +    create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.emc1",         0xf0825000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.emc2",         0xf0826000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
> +    create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
> +    create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
> +    create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
> +    create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
> +    create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);
> +}
> +
> +static Property npcm7xx_properties[] = {
> +    DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
> +                     MemoryRegion *),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void npcm7xx_class_init(ObjectClass *oc, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(oc);
> +
> +    dc->realize = npcm7xx_realize;
> +    dc->user_creatable = false;
> +    device_class_set_props(dc, npcm7xx_properties);
> +}
> +
> +static void npcm730_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> +
> +    /* NPCM730 is optimized for data center use, so no graphics, etc. */
> +    nc->disabled_modules = 0x00300395;
> +    nc->num_cpus = 2;
> +}
> +
> +static void npcm750_class_init(ObjectClass *oc, void *data)
> +{
> +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> +
> +    /* NPCM750 has 2 cores and a full set of peripherals */
> +    nc->disabled_modules = 0x00000000;
> +    nc->num_cpus = 2;
> +}
> +
> +static const TypeInfo npcm7xx_soc_types[] = {
> +    {
> +        .name           = TYPE_NPCM7XX,
> +        .parent         = TYPE_DEVICE,
> +        .instance_size  = sizeof(NPCM7xxState),
> +        .instance_init  = npcm7xx_init,
> +        .class_size     = sizeof(NPCM7xxClass),
> +        .class_init     = npcm7xx_class_init,
> +        .abstract       = true,
> +    }, {
> +        .name           = TYPE_NPCM730,
> +        .parent         = TYPE_NPCM7XX,
> +        .class_init     = npcm730_class_init,
> +    }, {
> +        .name           = TYPE_NPCM750,
> +        .parent         = TYPE_NPCM7XX,
> +        .class_init     = npcm750_class_init,
> +    },
> +};
> +
> +DEFINE_TYPES(npcm7xx_soc_types);
> diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> index 192a8dec3b..a31d0d282f 100644
> --- a/hw/arm/Kconfig
> +++ b/hw/arm/Kconfig
> @@ -356,6 +356,11 @@ config XLNX_VERSAL
>  
>  config NPCM7XX
>      bool
> +    select A9MPCORE
> +    select ARM_GIC
> +    select PL310  # cache controller
> +    select SERIAL
> +    select UNIMP
>  
>  config FSL_IMX25
>      bool
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 534a6a119e..13d163a599 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -41,6 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
>  obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
>  obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
> +obj-$(CONFIG_NPCM7XX) += npcm7xx.o
>  obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
>  obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
>  obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
> 



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-07-12  5:42     ` Havard Skinnemoen
@ 2020-07-13 17:38       ` Philippe Mathieu-Daudé
  2020-07-14  2:39         ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-13 17:38 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

On 7/12/20 7:42 AM, Havard Skinnemoen wrote:
> On Thu, Jul 9, 2020 at 10:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>> This implements a device model for the NPCM7xx SPI flash controller.
>>>
>>> Direct reads and writes, and user-mode transactions have been tested in
>>> various modes. Protection features are not implemented yet.
>>>
>>> All the FIU instances are available in the SoC's address space,
>>> regardless of whether or not they're connected to actual flash chips.
>>>
>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>> ---
>>>  include/hw/arm/npcm7xx.h     |   2 +
>>>  include/hw/ssi/npcm7xx_fiu.h | 100 +++++++
>>>  hw/arm/npcm7xx.c             |  53 ++++
>>>  hw/ssi/npcm7xx_fiu.c         | 510 +++++++++++++++++++++++++++++++++++
>>>  hw/arm/Kconfig               |   1 +
>>>  hw/ssi/Makefile.objs         |   1 +
>>>  hw/ssi/trace-events          |   9 +
>>>  7 files changed, 676 insertions(+)
>>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
>>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
[...]

>>> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
>>> index 4d227bb74b..c9ff3dab25 100644
>>> --- a/hw/arm/npcm7xx.c
>>> +++ b/hw/arm/npcm7xx.c
>>> @@ -98,6 +98,37 @@ static const hwaddr npcm7xx_uart_addr[] = {
>>>      0xf0004000,
>>>  };
>>>
>>> +static const hwaddr npcm7xx_fiu0_flash_addr[] = {
>>
>> So per
>> https://github.com/Nuvoton-Israel/bootblock/blob/master/SWC_HAL/Chips/npcm750/npcm750.h
>> this is SPI0 on AHB18,
>>
>>> +    0x80000000,
>>> +    0x88000000,
>>
>> CS0 & CS1,
>>
>> also listed:
>>
>> 0x90000000, // CS2
>> 0x98000000, // CS3
> 
> Confirmed with Nuvoton off-list that these do not exist. SPI0 only
> supports two chip-selects for direct access.

I suppose Novoton confirmed for the particular npcm750, but you aim
to model the npcm7xx family. I doubt 2 similar IP blocks are that
different ;) Anyway with a comment this is good.

> 
> I'll add comments.
> 
>>> +};
>>> +
>>> +static const hwaddr npcm7xx_fiu3_flash_addr[] = {
>>
>> Ditto SPI3 on AHB3, and CS0 to CS3.
>>
>>> +    0xa0000000,
>>> +    0xa8000000,
>>> +    0xb0000000,
>>> +    0xb8000000,
>>> +};
>>> +
>>> +static const struct {
>>> +    const char *name;
>>> +    hwaddr regs_addr;
>>> +    int cs_count;
>>> +    const hwaddr *flash_addr;
>>> +} npcm7xx_fiu[] = {
>>> +    {
>>> +        .name = "fiu0",
>>> +        .regs_addr = 0xfb000000,
>>> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
>>
>> Hmm without the datasheet, can't tell, but I'd expect 4 CS
>> regardless.
> 
> FIU0/SPI0 only has 2 chip selects.
> 
>>> +        .flash_addr = npcm7xx_fiu0_flash_addr,
>>> +    }, {
>>> +        .name = "fiu3",
>>> +        .regs_addr = 0xc0000000,
>>> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
>>> +        .flash_addr = npcm7xx_fiu3_flash_addr,
>>> +    },
>>> +};
[...]

>>> +
>>> +    /* Flash chip model expects one transfer per dummy bit, not byte */
>>> +    dummy_cycles =
>>> +        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
>>> +    for (i = 0; i < dummy_cycles; i++) {
>>> +        ssi_transfer(fiu->spi, 0);
>>
>> Note to self, might worth a ssi_shift_dummy(count) generic method.
> 
> I'm not a huge fan of this interface to be honest. It requires the
> flash controller to have intimate knowledge of the flash chip, and if
> it doesn't predict the dummy phase correctly, or the guest programs
> the wrong number of dummy cycles, the end result is very different
> from what you'll see on a real system. I'd love to see something like
> a number-of-bits parameter to ssi_transfer instead.

Do you mean like these?

- ssi_transfer_bit(bool value);
- ssi_shift_dummy_bits(size_t bits);

Some interfaces allow bit shifting. SPI doesn't simply because
nobody had the use :)

> 
>>> +    }
>>> +
>>> +    for (i = 0; i < size; i++) {
>>> +        value |= ssi_transfer(fiu->spi, 0) << (8 * i);
>>> +    }
>>> +
[...]

>>> +static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
>>> +    .read = npcm7xx_fiu_flash_read,
>>> +    .write = npcm7xx_fiu_flash_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid = {
>>> +        .min_access_size = 1,
>>> +        .max_access_size = 8,
>>
>> Are you sure? Maybe, I can' tell.
> 
> Real hardware supports 16 bytes, but there's no way to do more than 8
> in emulation, I think?

That would mean you can plug this device on a 128-bit wide bus,
and you can transfer 128-bit in a single CPU operation.

>>> +        .unaligned = true,
>>> +    },
>>> +};
>>> +


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx
  2020-07-09  0:36 ` [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen
@ 2020-07-13 17:50   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-13 17:50 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> If a -bios option is specified on the command line, load the image into
> the internal ROM memory region, which contains the first instructions
> run by the CPU after reset.
> 
> A minimal Apache-2.0-licensed boot ROM can be found at
> 
> https://github.com/google/vbootrom
> 
> It is by no means feature complete, but it is enough to launch the
> Nuvoton bootblock[1] from offset 0 in the flash, which in turn will
> launch u-boot and finally the Linux kernel.

Peter, should we include it as submodule with the compiled blob in
pc-bios/?

> 
> [1] https://github.com/Nuvoton-Israel/bootblock
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  hw/arm/npcm7xx_boards.c | 24 ++++++++++++++++++++++++
>  1 file changed, 24 insertions(+)
> 
> diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
> index d78d9f991b..80cf1535f1 100644
> --- a/hw/arm/npcm7xx_boards.c
> +++ b/hw/arm/npcm7xx_boards.c
> @@ -19,8 +19,11 @@
>  #include "hw/arm/boot.h"
>  #include "hw/arm/npcm7xx.h"
>  #include "hw/core/cpu.h"
> +#include "hw/loader.h"
>  #include "qapi/error.h"
> +#include "qemu-common.h"
>  #include "qemu/units.h"
> +#include "sysemu/sysemu.h"
>  
>  #define NPCM750_EVB_POWER_ON_STRAPS 0x00001ff7
>  #define QUANTA_GSJ_POWER_ON_STRAPS 0x00001fff
> @@ -34,6 +37,25 @@ static struct arm_boot_info npcm7xx_binfo = {
>      .board_id           = -1,
>  };
>  
> +static void npcm7xx_load_bootrom(NPCM7xxState *soc)
> +{
> +    if (bios_name) {
> +        g_autofree char *filename = NULL;
> +        int ret;
> +
> +        filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
> +        if (!filename) {
> +            error_report("Could not find ROM image '%s'", bios_name);
> +            exit(1);
> +        }
> +        ret = load_image_mr(filename, &soc->irom);
> +        if (ret < 0) {
> +            error_report("Failed to load ROM image '%s'", filename);
> +            exit(1);
> +        }
> +    }
> +}
> +
>  static void npcm7xx_load_kernel(MachineState *machine, NPCM7xxState *soc)
>  {
>      NPCM7xxClass *sc = NPCM7XX_GET_CLASS(soc);
> @@ -73,6 +95,7 @@ static void npcm750_evb_init(MachineState *machine)
>      npcm7xx_connect_dram(soc, machine->ram);
>      qdev_realize(DEVICE(soc), NULL, &error_abort);
>  
> +    npcm7xx_load_bootrom(soc);
>      npcm7xx_load_kernel(machine, soc);
>  }
>  
> @@ -84,6 +107,7 @@ static void quanta_gsj_init(MachineState *machine)
>      npcm7xx_connect_dram(soc, machine->ram);
>      qdev_realize(DEVICE(soc), NULL, &error_abort);
>  
> +    npcm7xx_load_bootrom(soc);
>      npcm7xx_load_kernel(machine, soc);
>  }
>  
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-13 14:57   ` Cédric Le Goater
@ 2020-07-13 17:59     ` Philippe Mathieu-Daudé
  2020-07-13 18:02       ` Philippe Mathieu-Daudé
  2020-07-14  2:56     ` Havard Skinnemoen
  1 sibling, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-13 17:59 UTC (permalink / raw)
  To: Cédric Le Goater, Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 7/13/20 4:57 PM, Cédric Le Goater wrote:
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>> one built with OpenBMC. For example like this:
>>
>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>> qemu-system-arm -machine quanta-gsj -nographic \
>> 	-bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>> 	-drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>
>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> 
> May be we don't need to create the flash object if dinfo is NULL.

Well, this is not wrong since m25p80_realize() check for the 'drive'
property, but I'd rather avoid using fake block, and instead force
users wanting an empty flash to use '-drive driver=null-co,...'.

So I prefer Cédric suggestion too.

> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Tested-by: Cédric Le Goater <clg@kaod.org>
> 
> Nice ! 
> 
> We need a SPI controller model and a network device model now. 
> 
> npcm7xx_bootrom.bin is a bit of a pain. Could we include it in 
> the QEMU roms ? 

Ah, this is what I asked on patch #6 ;)


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-13 17:59     ` Philippe Mathieu-Daudé
@ 2020-07-13 18:02       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-13 18:02 UTC (permalink / raw)
  To: Cédric Le Goater, Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, qemu-devel, Avi.Fishman

On 7/13/20 7:59 PM, Philippe Mathieu-Daudé wrote:
> On 7/13/20 4:57 PM, Cédric Le Goater wrote:
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>> one built with OpenBMC. For example like this:
>>>
>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>> qemu-system-arm -machine quanta-gsj -nographic \
>>> 	-bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>> 	-drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>
>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>
>> May be we don't need to create the flash object if dinfo is NULL.
> 
> Well, this is not wrong since m25p80_realize() check for the 'drive'
> property, but I'd rather avoid using fake block, and instead force
> users wanting an empty flash to use '-drive driver=null-co,...'.
> 
> So I prefer Cédric suggestion too.

Ah I see the model is fixed, is this SPI flash soldered on the
board?

> 
>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>> Tested-by: Cédric Le Goater <clg@kaod.org>
>>
>> Nice ! 
>>
>> We need a SPI controller model and a network device model now. 
>>
>> npcm7xx_bootrom.bin is a bit of a pain. Could we include it in 
>> the QEMU roms ? 
> 
> Ah, this is what I asked on patch #6 ;)
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-13 15:02   ` Cédric Le Goater
@ 2020-07-14  0:44     ` Havard Skinnemoen
  2020-07-14 11:37       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-14  0:44 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Peter Maydell, Philippe Mathieu-Daudé,
	qemu-arm, QEMU Developers, IS20 Avi Fishman, CS20 KFTing,
	Joel Stanley

On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
>
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> > The Nuvoton NPCM7xx SoC family are used to implement Baseboard
> > Management Controllers in servers. While the family includes four SoCs,
> > this patch implements limited support for two of them: NPCM730 (targeted
> > for Data Center applications) and NPCM750 (targeted for Enterprise
> > applications).
> >
> > This patch includes little more than the bare minimum needed to boot a
> > Linux kernel built with NPCM7xx support in direct-kernel mode:
> >
> >   - Two Cortex-A9 CPU cores with built-in periperhals.
> >   - Global Configuration Registers.
> >   - Clock Management.
> >   - 3 Timer Modules with 5 timers each.
> >   - 4 serial ports.
> >
> > The chips themselves have a lot more features, some of which will be
> > added to the model at a later stage.
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Reviewed-by: Joel Stanley <joel@jms.id.au>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/arm/npcm7xx.h |  86 +++++++++
> >  hw/arm/npcm7xx.c         | 376 +++++++++++++++++++++++++++++++++++++++
> >  hw/arm/Kconfig           |   5 +
> >  hw/arm/Makefile.objs     |   1 +
> >  4 files changed, 468 insertions(+)
> >  create mode 100644 include/hw/arm/npcm7xx.h
> >  create mode 100644 hw/arm/npcm7xx.c
> >
> > diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h
> > new file mode 100644
> > index 0000000000..95d9224f59
> > --- /dev/null
> > +++ b/include/hw/arm/npcm7xx.h
> > @@ -0,0 +1,86 @@
> > +/*
> > + * Nuvoton NPCM7xx SoC family.
> > + *
> > + * 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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_H
> > +#define NPCM7XX_H
> > +
> > +#include "hw/boards.h"
> > +#include "hw/cpu/a9mpcore.h"
> > +#include "hw/misc/npcm7xx_clk.h"
> > +#include "hw/misc/npcm7xx_gcr.h"
> > +#include "hw/timer/npcm7xx_timer.h"
> > +#include "target/arm/cpu.h"
> > +
> > +#define NPCM7XX_MAX_NUM_CPUS    (2)
> > +
> > +/* The first half of the address space is reserved for DDR4 DRAM. */
> > +#define NPCM7XX_DRAM_BA         (0x00000000)
> > +#define NPCM7XX_DRAM_SZ         (2 * GiB)
> > +
> > +/* Magic addresses for setting up direct kernel booting and SMP boot stubs. */
> > +#define NPCM7XX_LOADER_START            (0x00000000)  /* Start of SDRAM */
> > +#define NPCM7XX_SMP_LOADER_START        (0xffff0000)  /* Boot ROM */
> > +#define NPCM7XX_SMP_BOOTREG_ADDR        (0xf080013c)  /* GCR.SCRPAD */
> > +#define NPCM7XX_GIC_CPU_IF_ADDR         (0xf03fe100)  /* GIC within A9 */
> > +
> > +typedef struct NPCM7xxState {
> > +    DeviceState         parent;
> > +
> > +    ARMCPU              cpu[NPCM7XX_MAX_NUM_CPUS];
> > +    A9MPPrivState       a9mpcore;
> > +
> > +    MemoryRegion        sram;
> > +    MemoryRegion        irom;
> > +    MemoryRegion        ram3;
> > +    MemoryRegion        *dram;
> > +
> > +    NPCM7xxGCRState     gcr;
> > +    NPCM7xxCLKState     clk;
> > +    NPCM7xxTimerCtrlState tim[3];
> > +} NPCM7xxState;
> > +
> > +#define TYPE_NPCM7XX    "npcm7xx"
> > +#define NPCM7XX(obj)    OBJECT_CHECK(NPCM7xxState, (obj), TYPE_NPCM7XX)
> > +
> > +#define TYPE_NPCM730    "npcm730"
> > +#define TYPE_NPCM750    "npcm750"
> > +
> > +typedef struct NPCM7xxClass {
> > +    DeviceClass         parent;
> > +
> > +    /* Bitmask of modules that are permanently disabled on this chip. */
> > +    uint32_t            disabled_modules;
> > +    /* Number of CPU cores enabled in this SoC class (may be 1 or 2). */
> > +    uint32_t            num_cpus;
> > +} NPCM7xxClass;
> > +
> > +#define NPCM7XX_CLASS(klass)                                            \
> > +    OBJECT_CLASS_CHECK(NPCM7xxClass, (klass), TYPE_NPCM7XX)
> > +#define NPCM7XX_GET_CLASS(obj)                                          \
> > +    OBJECT_GET_CLASS(NPCM7xxClass, (obj), TYPE_NPCM7XX)
> > +
> > +/**
> > + * npcm7xx_write_secondary_boot - Write stub for booting secondary CPU.
> > + * @cpu: The CPU to be booted.
> > + * @info: Boot info structure for the board.
> > + *
> > + * This will write a short code stub to the internal ROM that will keep the
> > + * secondary CPU spinning until the primary CPU writes an address to the SCRPAD
> > + * register in the GCR, after which the secondary CPU will jump there.
> > + */
> > +extern void npcm7xx_write_secondary_boot(ARMCPU *cpu,
> > +                                         const struct arm_boot_info *info);
> > +
> > +#endif /* NPCM7XX_H */
> > diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> > new file mode 100644
> > index 0000000000..af45f3c716
> > --- /dev/null
> > +++ b/hw/arm/npcm7xx.c
> > @@ -0,0 +1,376 @@
> > +/*
> > + * Nuvoton NPCM7xx SoC family.
> > + *
> > + * 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 as published by the
> > + * Free Software Foundation; either version 2 of the License, or
> > + * (at your option) any later version.
> > + *
> > + * 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 "exec/address-spaces.h"
> > +#include "hw/arm/npcm7xx.h"
> > +#include "hw/char/serial.h"
> > +#include "hw/loader.h"
> > +#include "hw/misc/unimp.h"
> > +#include "hw/qdev-properties.h"
> > +#include "qapi/error.h"
> > +#include "qemu/units.h"
> > +#include "sysemu/sysemu.h"
> > +
> > +/*
> > + * This covers the whole MMIO space. We'll use this to catch any MMIO accesses
> > + * that aren't handled by any device.
> > + */
> > +#define NPCM7XX_MMIO_BA         (0x80000000)
> > +#define NPCM7XX_MMIO_SZ         (0x7ffd0000)
> > +
> > +/* Core system modules. */
> > +#define NPCM7XX_L2C_BA          (0xf03fc000)
> > +#define NPCM7XX_CPUP_BA         (0xf03fe000)
> > +#define NPCM7XX_GCR_BA          (0xf0800000)
> > +#define NPCM7XX_CLK_BA          (0xf0801000)
> > +
> > +/* Internal AHB SRAM */
> > +#define NPCM7XX_RAM3_BA         (0xc0008000)
> > +#define NPCM7XX_RAM3_SZ         (4 * KiB)
> > +
> > +/* Memory blocks at the end of the address space */
> > +#define NPCM7XX_RAM2_BA         (0xfffd0000)
> > +#define NPCM7XX_RAM2_SZ         (128 * KiB)
> > +#define NPCM7XX_ROM_BA          (0xffff0000)
> > +#define NPCM7XX_ROM_SZ          (64 * KiB)
> > +
> > +/*
> > + * Interrupt lines going into the GIC. This does not include internal Cortex-A9
> > + * interrupts.
> > + */
> > +enum NPCM7xxInterrupt {
> > +    NPCM7XX_UART0_IRQ           = 2,
> > +    NPCM7XX_UART1_IRQ,
> > +    NPCM7XX_UART2_IRQ,
> > +    NPCM7XX_UART3_IRQ,
> > +    NPCM7XX_TIMER0_IRQ          = 32,   /* Timer Module 0 */
> > +    NPCM7XX_TIMER1_IRQ,
> > +    NPCM7XX_TIMER2_IRQ,
> > +    NPCM7XX_TIMER3_IRQ,
> > +    NPCM7XX_TIMER4_IRQ,
> > +    NPCM7XX_TIMER5_IRQ,                 /* Timer Module 1 */
> > +    NPCM7XX_TIMER6_IRQ,
> > +    NPCM7XX_TIMER7_IRQ,
> > +    NPCM7XX_TIMER8_IRQ,
> > +    NPCM7XX_TIMER9_IRQ,
> > +    NPCM7XX_TIMER10_IRQ,                /* Timer Module 2 */
> > +    NPCM7XX_TIMER11_IRQ,
> > +    NPCM7XX_TIMER12_IRQ,
> > +    NPCM7XX_TIMER13_IRQ,
> > +    NPCM7XX_TIMER14_IRQ,
> > +};
> > +
> > +/* Total number of GIC interrupts, including internal Cortex-A9 interrupts. */
> > +#define NPCM7XX_NUM_IRQ         (160)
> > +
> > +/* Register base address for each Timer Module */
> > +static const hwaddr npcm7xx_tim_addr[] = {
> > +    0xf0008000,
> > +    0xf0009000,
> > +    0xf000a000,
> > +};
> > +
> > +/* Register base address for each 16550 UART */
> > +static const hwaddr npcm7xx_uart_addr[] = {
> > +    0xf0001000,
> > +    0xf0002000,
> > +    0xf0003000,
> > +    0xf0004000,
> > +};
> > +
> > +void npcm7xx_write_secondary_boot(ARMCPU *cpu, const struct arm_boot_info *info)
> > +{
> > +    /*
> > +     * The default smpboot stub halts the secondary CPU with a 'wfi'
> > +     * instruction, but the arch/arm/mach-npcm/platsmp.c in the Linux kernel
> > +     * does not send an IPI to wake it up, so the second CPU fails to boot. So
> > +     * we need to provide our own smpboot stub that can not use 'wfi', it has
> > +     * to spin the secondary CPU until the first CPU writes to the SCRPAD reg.
> > +     */
> > +    uint32_t smpboot[] = {
> > +        0xe59f2018,     /* ldr r2, bootreg_addr */
> > +        0xe3a00000,     /* mov r0, #0 */
> > +        0xe5820000,     /* str r0, [r2] */
> > +        0xe320f002,     /* wfe */
> > +        0xe5921000,     /* ldr r1, [r2] */
> > +        0xe1110001,     /* tst r1, r1 */
> > +        0x0afffffb,     /* beq <wfe> */
> > +        0xe12fff11,     /* bx r1 */
> > +        NPCM7XX_SMP_BOOTREG_ADDR,
> > +    };
> > +    int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(smpboot); i++) {
> > +        smpboot[i] = tswap32(smpboot[i]);
> > +    }
> > +
> > +    rom_add_blob_fixed("smpboot", smpboot, sizeof(smpboot),
> > +                       NPCM7XX_SMP_LOADER_START);
> > +}
> > +
> > +static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n)
> > +{
> > +    return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n);
> > +}
> > +
> > +static void npcm7xx_init(Object *obj)
> > +{
> > +    NPCM7xxState *s = NPCM7XX(obj);
> > +    int i;
> > +
> > +    for (i = 0; i < NPCM7XX_MAX_NUM_CPUS; i++) {
> > +        object_initialize_child(obj, "cpu[*]", &s->cpu[i],
> > +                                ARM_CPU_TYPE_NAME("cortex-a9"));
> > +    }
> > +
> > +    object_initialize_child(obj, "a9mpcore", &s->a9mpcore, TYPE_A9MPCORE_PRIV);
> > +    object_initialize_child(obj, "gcr", &s->gcr, TYPE_NPCM7XX_GCR);
> > +    object_property_add_alias(obj, "power-on-straps", OBJECT(&s->gcr),
> > +                              "power-on-straps");
> > +    object_initialize_child(obj, "clk", &s->clk, TYPE_NPCM7XX_CLK);
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> > +        object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TIMER);
> > +    }
> > +}
> > +
> > +static void npcm7xx_realize(DeviceState *dev, Error **errp)
> > +{
> > +    NPCM7xxState *s = NPCM7XX(dev);
> > +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> > +    int i;
> > +
> > +    /* CPUs */
> > +    for (i = 0; i < nc->num_cpus; i++) {
> > +        object_property_set_int(OBJECT(&s->cpu[i]),
> > +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
> > +                                "mp-affinity", &error_abort);
> > +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
> > +                                "reset-cbar", &error_abort);
> > +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
> > +                                 "reset-hivecs", &error_abort);
> > +
> > +        /* Disable security extensions. */
> > +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
> > +                                 &error_abort);
> > +
> > +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>
> I would check the error:
>
>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
>             return;
>         }
>
> same for the sysbus_realize() below.

Hmm, I used to propagate these errors until Philippe told me not to
(or at least that's how I understood it). I'll be happy to do it
either way (and the new API makes it really easy to propagate errors),
but I worry that I don't fully understand when to propagate errors and
when not to.

It makes sense to me to propagate errors from *_realize() and
error_abort on failure to set simple properties, but I'd like to know
if Philippe is on board with that.

> > +    }
> > +
> > +    /* A9MPCORE peripherals */
> > +    object_property_set_int(OBJECT(&s->a9mpcore), nc->num_cpus, "num-cpu",
> > +                            &error_abort);
> > +    object_property_set_int(OBJECT(&s->a9mpcore), NPCM7XX_NUM_IRQ, "num-irq",
> > +                            &error_abort);
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
> > +
> > +    for (i = 0; i < nc->num_cpus; i++) {
> > +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
> > +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
> > +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
> > +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
> > +    }
> > +
> > +    /* L2 cache controller */
> > +    sysbus_create_simple("l2x0", NPCM7XX_L2C_BA, NULL);
> > +
> > +    /* System Global Control Registers (GCR) */
> > +    object_property_set_int(OBJECT(&s->gcr), nc->disabled_modules,
> > +                            "disabled-modules", &error_abort);
> > +    object_property_add_const_link(OBJECT(&s->gcr), "dram-mr", OBJECT(s->dram));
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->gcr), &error_abort);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->gcr), 0, NPCM7XX_GCR_BA);
> > +
> > +    /* Clock Control Registers (CLK) */
> > +    sysbus_realize(SYS_BUS_DEVICE(&s->clk), &error_abort);
> > +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->clk), 0, NPCM7XX_CLK_BA);
> > +
> > +    /* Timer Modules (TIM) */
> > +    QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) != ARRAY_SIZE(s->tim));
> > +    for (i = 0; i < ARRAY_SIZE(s->tim); i++) {
> > +        SysBusDevice *sbd = SYS_BUS_DEVICE(&s->tim[i]);
> > +        int first_irq;
> > +        int j;
> > +
> > +        sysbus_realize(sbd, &error_abort);
> > +        sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
> > +
> > +        first_irq = NPCM7XX_TIMER0_IRQ + i * NPCM7XX_TIMERS_PER_CTRL;
> > +        for (j = 0; j < NPCM7XX_TIMERS_PER_CTRL; j++) {
> > +            qemu_irq irq = npcm7xx_irq(s, first_irq + j);
> > +            sysbus_connect_irq(sbd, j, irq);
> > +        }
> > +    }
> > +
> > +    /* UART0..3 (16550 compatible) */
> > +    for (i = 0; i < ARRAY_SIZE(npcm7xx_uart_addr); i++) {
> > +        serial_mm_init(get_system_memory(), npcm7xx_uart_addr[i], 2,
> > +                       npcm7xx_irq(s, NPCM7XX_UART0_IRQ + i), 115200,
> > +                       serial_hd(i), DEVICE_LITTLE_ENDIAN);
> > +    }
> > +
> > +    /* RAM2 (SRAM) */
> > +    memory_region_init_ram(&s->sram, OBJECT(dev), "ram2",
> > +                           NPCM7XX_RAM2_SZ, &error_abort);
> > +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM2_BA, &s->sram);
> > +
> > +    /* RAM3 (SRAM) */
> > +    memory_region_init_ram(&s->ram3, OBJECT(dev), "ram3",
> > +                           NPCM7XX_RAM3_SZ, &error_abort);
> > +    memory_region_add_subregion(get_system_memory(), NPCM7XX_RAM3_BA, &s->ram3);
> > +
> > +    /* Internal ROM */
> > +    memory_region_init_rom(&s->irom, OBJECT(dev), "irom", NPCM7XX_ROM_SZ,
> > +                           &error_abort);
> > +    memory_region_add_subregion(get_system_memory(), NPCM7XX_ROM_BA, &s->irom);
> > +
> > +    create_unimplemented_device("npcm7xx.shm",          0xc0001000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.vdmx",         0xe0800000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.pcierc",       0xe1000000,  64 * KiB);
> > +    create_unimplemented_device("npcm7xx.kcs",          0xf0007000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.rng",          0xf000b000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.adc",          0xf000c000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gfxi",         0xf000e000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[0]",      0xf0010000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[1]",      0xf0011000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[2]",      0xf0012000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[3]",      0xf0013000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[4]",      0xf0014000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[5]",      0xf0015000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[6]",      0xf0016000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.gpio[7]",      0xf0017000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[0]",     0xf0080000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[1]",     0xf0081000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[2]",     0xf0082000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[3]",     0xf0083000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[4]",     0xf0084000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[5]",     0xf0085000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[6]",     0xf0086000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[7]",     0xf0087000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[8]",     0xf0088000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[9]",     0xf0089000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[10]",    0xf008a000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[11]",    0xf008b000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[12]",    0xf008c000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[13]",    0xf008d000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[14]",    0xf008e000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.smbus[15]",    0xf008f000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.espi",         0xf009f000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.peci",         0xf0100000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.siox[1]",      0xf0101000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.siox[2]",      0xf0102000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.pwm[0]",       0xf0103000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.pwm[1]",       0xf0104000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[0]",       0xf0180000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[1]",       0xf0181000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[2]",       0xf0182000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[3]",       0xf0183000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[4]",       0xf0184000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[5]",       0xf0185000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[6]",       0xf0186000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.mft[7]",       0xf0187000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.pspi1",        0xf0200000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.pspi2",        0xf0201000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.ahbpci",       0xf0400000,   1 * MiB);
> > +    create_unimplemented_device("npcm7xx.mcphy",        0xf05f0000,  64 * KiB);
> > +    create_unimplemented_device("npcm7xx.gmac1",        0xf0802000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.gmac2",        0xf0804000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.ehci",         0xf0806000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.ohci",         0xf0807000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.vcd",          0xf0810000,  64 * KiB);
> > +    create_unimplemented_device("npcm7xx.ece",          0xf0820000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.vdma",         0xf0822000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.emc1",         0xf0825000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.emc2",         0xf0826000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[0]",      0xf0830000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[1]",      0xf0831000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[2]",      0xf0832000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[3]",      0xf0833000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[4]",      0xf0834000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[5]",      0xf0835000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[6]",      0xf0836000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[7]",      0xf0837000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[8]",      0xf0838000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.usbd[9]",      0xf0839000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.sd",           0xf0840000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.mmc",          0xf0842000,   8 * KiB);
> > +    create_unimplemented_device("npcm7xx.pcimbx",       0xf0848000, 512 * KiB);
> > +    create_unimplemented_device("npcm7xx.aes",          0xf0858000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.des",          0xf0859000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.sha",          0xf085a000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.secacc",       0xf085b000,   4 * KiB);
> > +    create_unimplemented_device("npcm7xx.spixcs0",      0xf8000000,  16 * MiB);
> > +    create_unimplemented_device("npcm7xx.spixcs1",      0xf9000000,  16 * MiB);
> > +    create_unimplemented_device("npcm7xx.spix",         0xfb001000,   4 * KiB);
> > +}
> > +
> > +static Property npcm7xx_properties[] = {
> > +    DEFINE_PROP_LINK("dram-mr", NPCM7xxState, dram, TYPE_MEMORY_REGION,
> > +                     MemoryRegion *),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void npcm7xx_class_init(ObjectClass *oc, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(oc);
> > +
> > +    dc->realize = npcm7xx_realize;
> > +    dc->user_creatable = false;
> > +    device_class_set_props(dc, npcm7xx_properties);
> > +}
> > +
> > +static void npcm730_class_init(ObjectClass *oc, void *data)
> > +{
> > +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> > +
> > +    /* NPCM730 is optimized for data center use, so no graphics, etc. */
> > +    nc->disabled_modules = 0x00300395;
> > +    nc->num_cpus = 2;
> > +}
> > +
> > +static void npcm750_class_init(ObjectClass *oc, void *data)
> > +{
> > +    NPCM7xxClass *nc = NPCM7XX_CLASS(oc);
> > +
> > +    /* NPCM750 has 2 cores and a full set of peripherals */
> > +    nc->disabled_modules = 0x00000000;
> > +    nc->num_cpus = 2;
> > +}
> > +
> > +static const TypeInfo npcm7xx_soc_types[] = {
> > +    {
> > +        .name           = TYPE_NPCM7XX,
> > +        .parent         = TYPE_DEVICE,
> > +        .instance_size  = sizeof(NPCM7xxState),
> > +        .instance_init  = npcm7xx_init,
> > +        .class_size     = sizeof(NPCM7xxClass),
> > +        .class_init     = npcm7xx_class_init,
> > +        .abstract       = true,
> > +    }, {
> > +        .name           = TYPE_NPCM730,
> > +        .parent         = TYPE_NPCM7XX,
> > +        .class_init     = npcm730_class_init,
> > +    }, {
> > +        .name           = TYPE_NPCM750,
> > +        .parent         = TYPE_NPCM7XX,
> > +        .class_init     = npcm750_class_init,
> > +    },
> > +};
> > +
> > +DEFINE_TYPES(npcm7xx_soc_types);
> > diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
> > index 192a8dec3b..a31d0d282f 100644
> > --- a/hw/arm/Kconfig
> > +++ b/hw/arm/Kconfig
> > @@ -356,6 +356,11 @@ config XLNX_VERSAL
> >
> >  config NPCM7XX
> >      bool
> > +    select A9MPCORE
> > +    select ARM_GIC
> > +    select PL310  # cache controller
> > +    select SERIAL
> > +    select UNIMP
> >
> >  config FSL_IMX25
> >      bool
> > diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> > index 534a6a119e..13d163a599 100644
> > --- a/hw/arm/Makefile.objs
> > +++ b/hw/arm/Makefile.objs
> > @@ -41,6 +41,7 @@ obj-$(CONFIG_STM32F205_SOC) += stm32f205_soc.o
> >  obj-$(CONFIG_STM32F405_SOC) += stm32f405_soc.o
> >  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zynqmp.o xlnx-zcu102.o
> >  obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o
> > +obj-$(CONFIG_NPCM7XX) += npcm7xx.o
> >  obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o
> >  obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o
> >  obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o
> >
>


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model
  2020-07-13 17:38       ` Philippe Mathieu-Daudé
@ 2020-07-14  2:39         ` Havard Skinnemoen
  0 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-14  2:39 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Cédric Le Goater

On Mon, Jul 13, 2020 at 10:39 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/12/20 7:42 AM, Havard Skinnemoen wrote:
> > On Thu, Jul 9, 2020 at 10:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> >>> This implements a device model for the NPCM7xx SPI flash controller.
> >>>
> >>> Direct reads and writes, and user-mode transactions have been tested in
> >>> various modes. Protection features are not implemented yet.
> >>>
> >>> All the FIU instances are available in the SoC's address space,
> >>> regardless of whether or not they're connected to actual flash chips.
> >>>
> >>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> >>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>> ---
> >>>  include/hw/arm/npcm7xx.h     |   2 +
> >>>  include/hw/ssi/npcm7xx_fiu.h | 100 +++++++
> >>>  hw/arm/npcm7xx.c             |  53 ++++
> >>>  hw/ssi/npcm7xx_fiu.c         | 510 +++++++++++++++++++++++++++++++++++
> >>>  hw/arm/Kconfig               |   1 +
> >>>  hw/ssi/Makefile.objs         |   1 +
> >>>  hw/ssi/trace-events          |   9 +
> >>>  7 files changed, 676 insertions(+)
> >>>  create mode 100644 include/hw/ssi/npcm7xx_fiu.h
> >>>  create mode 100644 hw/ssi/npcm7xx_fiu.c
> [...]
>
> >>> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> >>> index 4d227bb74b..c9ff3dab25 100644
> >>> --- a/hw/arm/npcm7xx.c
> >>> +++ b/hw/arm/npcm7xx.c
> >>> @@ -98,6 +98,37 @@ static const hwaddr npcm7xx_uart_addr[] = {
> >>>      0xf0004000,
> >>>  };
> >>>
> >>> +static const hwaddr npcm7xx_fiu0_flash_addr[] = {
> >>
> >> So per
> >> https://github.com/Nuvoton-Israel/bootblock/blob/master/SWC_HAL/Chips/npcm750/npcm750.h
> >> this is SPI0 on AHB18,
> >>
> >>> +    0x80000000,
> >>> +    0x88000000,
> >>
> >> CS0 & CS1,
> >>
> >> also listed:
> >>
> >> 0x90000000, // CS2
> >> 0x98000000, // CS3
> >
> > Confirmed with Nuvoton off-list that these do not exist. SPI0 only
> > supports two chip-selects for direct access.
>
> I suppose Novoton confirmed for the particular npcm750, but you aim
> to model the npcm7xx family. I doubt 2 similar IP blocks are that
> different ;) Anyway with a comment this is good.

OK. I'm mostly concerned about modeling the chips I know about for
now. If a chip with four AXI endpoints and four chipselects on SPI0
appears, it will be a fairly small change to move these arrays into
the SoC class struct, but I'd rather not do that without evidence that
such a chip exist.

The IP blocks may be identical for all I know, but they are not wired
up identically, and that's really what matters here in the SoC-level
model.

> >
> > I'll add comments.
> >
> >>> +};
> >>> +
> >>> +static const hwaddr npcm7xx_fiu3_flash_addr[] = {
> >>
> >> Ditto SPI3 on AHB3, and CS0 to CS3.
> >>
> >>> +    0xa0000000,
> >>> +    0xa8000000,
> >>> +    0xb0000000,
> >>> +    0xb8000000,
> >>> +};
> >>> +
> >>> +static const struct {
> >>> +    const char *name;
> >>> +    hwaddr regs_addr;
> >>> +    int cs_count;
> >>> +    const hwaddr *flash_addr;
> >>> +} npcm7xx_fiu[] = {
> >>> +    {
> >>> +        .name = "fiu0",
> >>> +        .regs_addr = 0xfb000000,
> >>> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu0_flash_addr),
> >>
> >> Hmm without the datasheet, can't tell, but I'd expect 4 CS
> >> regardless.
> >
> > FIU0/SPI0 only has 2 chip selects.
> >
> >>> +        .flash_addr = npcm7xx_fiu0_flash_addr,
> >>> +    }, {
> >>> +        .name = "fiu3",
> >>> +        .regs_addr = 0xc0000000,
> >>> +        .cs_count = ARRAY_SIZE(npcm7xx_fiu3_flash_addr),
> >>> +        .flash_addr = npcm7xx_fiu3_flash_addr,
> >>> +    },
> >>> +};
> [...]
>
> >>> +
> >>> +    /* Flash chip model expects one transfer per dummy bit, not byte */
> >>> +    dummy_cycles =
> >>> +        (FIU_DRD_CFG_DBW(drd_cfg) * 8) >> FIU_DRD_CFG_ACCTYPE(drd_cfg);
> >>> +    for (i = 0; i < dummy_cycles; i++) {
> >>> +        ssi_transfer(fiu->spi, 0);
> >>
> >> Note to self, might worth a ssi_shift_dummy(count) generic method.
> >
> > I'm not a huge fan of this interface to be honest. It requires the
> > flash controller to have intimate knowledge of the flash chip, and if
> > it doesn't predict the dummy phase correctly, or the guest programs
> > the wrong number of dummy cycles, the end result is very different
> > from what you'll see on a real system. I'd love to see something like
> > a number-of-bits parameter to ssi_transfer instead.
>
> Do you mean like these?
>
> - ssi_transfer_bit(bool value);
> - ssi_shift_dummy_bits(size_t bits);
>
> Some interfaces allow bit shifting. SPI doesn't simply because
> nobody had the use :)

I mean I don't like how the semantics of ssi_transfer changes
implicitly in the middle of the transfer. One call shifts a whole
byte, the next 8 shift individual bits, then it's back to bytes again.
If a mistake is made, the flash controller might end up thinking it's
shifting 16 bits, but if the flash device only expects 8 dummy bits,
it will see 8 bits + 8 bytes for a total of 9 bytes of dummies. This
differs a lot from what would happen on a real device.

For example, I wrote up a test for the aspeed flash controller against
a Winbond flash chip in Dual I/O mode. The data ended up getting
shifted by 15 bytes because of, I think, bugs in the dummy logic on
both sides. I took a lot of head scratching to figure out what's going
on.

Here's where aspeed_smc figures it needs to send 2 dummy bytes (aka 16
dummy bits) for a DIO read:

https://elixir.bootlin.com/qemu/latest/source/hw/ssi/aspeed_smc.c#L803

And here's where the Winbond model decides to expect one dummy _bit_:

https://elixir.bootlin.com/qemu/latest/source/hw/block/m25p80.c#L862

The difference is 15 ssi_transfers, which is what I see in the test.

IMO they're both wrong -- they should both expect one dummy byte, or
four dummy cycles in x2 mode. But the real problem, also IMO, is that
they can't even agree on whether one ssi_transfer means one bit or one
byte.

While ssi_shift_dummy_bits is strictly nicer than open-coded loops of
ssi_transfer, it doesn't fix the underlying problem that the dummy
cycles need to be treated specially by both the flash controller model
and the device model, and if they're not 100% synchronized (which is
particularly interesting when you have flash chips with configurable
dummy cycles), the result is like nothing you'd ever see on a real
system.

> >
> >>> +    }
> >>> +
> >>> +    for (i = 0; i < size; i++) {
> >>> +        value |= ssi_transfer(fiu->spi, 0) << (8 * i);
> >>> +    }
> >>> +
> [...]
>
> >>> +static const MemoryRegionOps npcm7xx_fiu_flash_ops = {
> >>> +    .read = npcm7xx_fiu_flash_read,
> >>> +    .write = npcm7xx_fiu_flash_write,
> >>> +    .endianness = DEVICE_LITTLE_ENDIAN,
> >>> +    .valid = {
> >>> +        .min_access_size = 1,
> >>> +        .max_access_size = 8,
> >>
> >> Are you sure? Maybe, I can' tell.
> >
> > Real hardware supports 16 bytes, but there's no way to do more than 8
> > in emulation, I think?
>
> That would mean you can plug this device on a 128-bit wide bus,
> and you can transfer 128-bit in a single CPU operation.

Hmm, I see what you mean. It can do 16-byte bursts. I'm not sure if
that looks more like a single 16-byte access or four 4-byte accesses
on the SPI bus. I was assuming it's the former, but I could be wrong.

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-13 14:57   ` Cédric Le Goater
  2020-07-13 17:59     ` Philippe Mathieu-Daudé
@ 2020-07-14  2:56     ` Havard Skinnemoen
  2020-07-14  9:16       ` Markus Armbruster
  2020-07-15  7:42       ` Cédric Le Goater
  1 sibling, 2 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-14  2:56 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Peter Maydell, Philippe Mathieu-Daudé,
	qemu-arm, QEMU Developers, IS20 Avi Fishman, CS20 KFTing

On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> > This allows these NPCM7xx-based boards to boot from a flash image, e.g.
> > one built with OpenBMC. For example like this:
> >
> > IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
> > qemu-system-arm -machine quanta-gsj -nographic \
> >       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
> >       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>
> May be we don't need to create the flash object if dinfo is NULL.

It's soldered on the board, so you can't really boot the board without
it. But if you think it's better to remove it altogether if we don't
have an image to load into it, I can do that.

>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Tested-by: Cédric Le Goater <clg@kaod.org>
>
> Nice !
>
> We need a SPI controller model and a network device model now.

Yeah, and i2c, PWM, GPIO, etc., but if you're referring to the kernel
crash, see below.

> npcm7xx_bootrom.bin is a bit of a pain. Could we include it in
> the QEMU roms ?

Yeah, I was planning to include this in v6.

> spi_master spi0: /ahb/fiu@fb000000/spi-nor@0 has no valid 'spi-max-frequency' property (-22)
> spi_master spi0: Failed to create SPI device for /ahb/fiu@fb000000/spi-nor@0

This is a device tree bug:

https://github.com/hskinnemoen/openbmc/commit/99b172f88002f4fac939f85debe1187b9c569871

> libphy: Fixed MDIO Bus: probed
> 8<--- cut here ---
> Unable to handle kernel paging request at virtual address fffffffe

I believe this is a kernel bug:

https://github.com/hskinnemoen/openbmc/commit/77e9f58ba157eabc976f15fa49892128fe2b2382

I needed two additional patches to get all the way to the login prompt:

https://github.com/hskinnemoen/openbmc/commits/20200711-gsj-qemu-0


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14  2:56     ` Havard Skinnemoen
@ 2020-07-14  9:16       ` Markus Armbruster
  2020-07-14 11:29         ` Philippe Mathieu-Daudé
  2020-07-15  7:42       ` Cédric Le Goater
  1 sibling, 1 reply; 64+ messages in thread
From: Markus Armbruster @ 2020-07-14  9:16 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, Philippe Mathieu-Daudé,
	CS20 KFTing, qemu-arm, Cédric Le Goater, IS20 Avi Fishman

Havard Skinnemoen <hskinnemoen@google.com> writes:

> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>> > This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>> > one built with OpenBMC. For example like this:
>> >
>> > IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>> > qemu-system-arm -machine quanta-gsj -nographic \
>> >       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>> >       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>> >
>> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>
>> May be we don't need to create the flash object if dinfo is NULL.
>
> It's soldered on the board, so you can't really boot the board without
> it. But if you think it's better to remove it altogether if we don't
> have an image to load into it, I can do that.

If a device is a fixed part of the physical board, it should be a fixed
part of the virtual board, too.

[...]



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14  9:16       ` Markus Armbruster
@ 2020-07-14 11:29         ` Philippe Mathieu-Daudé
  2020-07-14 16:21           ` Markus Armbruster
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-14 11:29 UTC (permalink / raw)
  To: Markus Armbruster, Havard Skinnemoen
  Cc: Peter Maydell, Qemu-block, QEMU Developers, CS20 KFTing,
	qemu-arm, Cédric Le Goater, IS20 Avi Fishman

+ qemu-block experts.

On 7/14/20 11:16 AM, Markus Armbruster wrote:
> Havard Skinnemoen <hskinnemoen@google.com> writes:
> 
>> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>
>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>>> one built with OpenBMC. For example like this:
>>>>
>>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>>
>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>
>>> May be we don't need to create the flash object if dinfo is NULL.
>>
>> It's soldered on the board, so you can't really boot the board without
>> it. But if you think it's better to remove it altogether if we don't
>> have an image to load into it, I can do that.
> 
> If a device is a fixed part of the physical board, it should be a fixed
> part of the virtual board, too.

We agree so far but ... how to do it?

I never used this API, does that makes sense?

    if (!dinfo) {
        QemuOpts *opts;

        opts = qemu_opts_create(NULL, "spi-flash", 1, &error_abort);
        qdict_put_str(opts, "format", "null-co");
        qdict_put_int(opts, BLOCK_OPT_SIZE, 64 * MiB);
        qdict_put_bool(opts, NULL_OPT_ZEROES, false); // XXX

        dinfo = drive_new(opts, IF_MTD, &error_abort);
        qemu_opts_del(opts);
    }

We should probably add a public helper for that.

'XXX' because NOR flashes erase content is when hardware bit
is set, so it would be more useful to return -1/0xff... rather
than zeroes.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-14  0:44     ` Havard Skinnemoen
@ 2020-07-14 11:37       ` Philippe Mathieu-Daudé
  2020-07-14 16:01         ` Markus Armbruster
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-14 11:37 UTC (permalink / raw)
  To: Havard Skinnemoen, Cédric Le Goater
  Cc: Peter Maydell, QEMU Developers, Markus Armbruster, CS20 KFTing,
	qemu-arm, Joel Stanley, IS20 Avi Fishman

+Markus

On 7/14/20 2:44 AM, Havard Skinnemoen wrote:
> On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
>>
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
>>> Management Controllers in servers. While the family includes four SoCs,
>>> this patch implements limited support for two of them: NPCM730 (targeted
>>> for Data Center applications) and NPCM750 (targeted for Enterprise
>>> applications).
>>>
>>> This patch includes little more than the bare minimum needed to boot a
>>> Linux kernel built with NPCM7xx support in direct-kernel mode:
>>>
>>>   - Two Cortex-A9 CPU cores with built-in periperhals.
>>>   - Global Configuration Registers.
>>>   - Clock Management.
>>>   - 3 Timer Modules with 5 timers each.
>>>   - 4 serial ports.
>>>
>>> The chips themselves have a lot more features, some of which will be
>>> added to the model at a later stage.
>>>
>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>> ---
...

>>> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
>>> +{
>>> +    NPCM7xxState *s = NPCM7XX(dev);
>>> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>>> +    int i;
>>> +
>>> +    /* CPUs */
>>> +    for (i = 0; i < nc->num_cpus; i++) {
>>> +        object_property_set_int(OBJECT(&s->cpu[i]),
>>> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
>>> +                                "mp-affinity", &error_abort);
>>> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>>> +                                "reset-cbar", &error_abort);
>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
>>> +                                 "reset-hivecs", &error_abort);
>>> +
>>> +        /* Disable security extensions. */
>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>>> +                                 &error_abort);
>>> +
>>> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>>
>> I would check the error:
>>
>>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
>>             return;
>>         }
>>
>> same for the sysbus_realize() below.
> 
> Hmm, I used to propagate these errors until Philippe told me not to
> (or at least that's how I understood it).

It was before Markus simplification API were merged, you had to
propagate after each call, since this is a non hot-pluggable SoC
I suggested to use &error_abort to simplify.

> I'll be happy to do it
> either way (and the new API makes it really easy to propagate errors),
> but I worry that I don't fully understand when to propagate errors and
> when not to.

Markus explained it on the mailing list recently (as I found the doc
not obvious). I can't find the thread. I suppose once the work result
after the "Questionable aspects of QEMU Error's design" discussion is
merged, the documentation will be clarified.

My rule of thumb so far is:
- programming error (can't happen) -> &error_abort
- everything triggerable by user or management layer (via QMP command)
  -> &error_fatal, as we can't risk loose the user data, we need to
  shutdown gracefully.

> 
> It makes sense to me to propagate errors from *_realize() and
> error_abort on failure to set simple properties, but I'd like to know
> if Philippe is on board with that.
> 
>>> +    }
>>> +
>>> +    /* A9MPCORE peripherals */
>>> +    object_property_set_int(OBJECT(&s->a9mpcore), nc->num_cpus, "num-cpu",
>>> +                            &error_abort);
>>> +    object_property_set_int(OBJECT(&s->a9mpcore), NPCM7XX_NUM_IRQ, "num-irq",
>>> +                            &error_abort);
>>> +    sysbus_realize(SYS_BUS_DEVICE(&s->a9mpcore), &error_abort);
>>> +    sysbus_mmio_map(SYS_BUS_DEVICE(&s->a9mpcore), 0, NPCM7XX_CPUP_BA);
>>> +
>>> +    for (i = 0; i < nc->num_cpus; i++) {
>>> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i,
>>> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_IRQ));
>>> +        sysbus_connect_irq(SYS_BUS_DEVICE(&s->a9mpcore), i + nc->num_cpus,
>>> +                           qdev_get_gpio_in(DEVICE(&s->cpu[i]), ARM_CPU_FIQ));
>>> +    }
...


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-14 11:37       ` Philippe Mathieu-Daudé
@ 2020-07-14 16:01         ` Markus Armbruster
  2020-07-14 17:11           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Markus Armbruster @ 2020-07-14 16:01 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, IS20 Avi Fishman, QEMU Developers,
	Havard Skinnemoen, CS20 KFTing, qemu-arm, Cédric Le Goater,
	Joel Stanley

Philippe Mathieu-Daudé <f4bug@amsat.org> writes:

> +Markus
>
> On 7/14/20 2:44 AM, Havard Skinnemoen wrote:
>> On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>
>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
>>>> Management Controllers in servers. While the family includes four SoCs,
>>>> this patch implements limited support for two of them: NPCM730 (targeted
>>>> for Data Center applications) and NPCM750 (targeted for Enterprise
>>>> applications).
>>>>
>>>> This patch includes little more than the bare minimum needed to boot a
>>>> Linux kernel built with NPCM7xx support in direct-kernel mode:
>>>>
>>>>   - Two Cortex-A9 CPU cores with built-in periperhals.
>>>>   - Global Configuration Registers.
>>>>   - Clock Management.
>>>>   - 3 Timer Modules with 5 timers each.
>>>>   - 4 serial ports.
>>>>
>>>> The chips themselves have a lot more features, some of which will be
>>>> added to the model at a later stage.
>>>>
>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>> ---
> ...
>
>>>> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
>>>> +{
>>>> +    NPCM7xxState *s = NPCM7XX(dev);
>>>> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>>>> +    int i;
>>>> +
>>>> +    /* CPUs */
>>>> +    for (i = 0; i < nc->num_cpus; i++) {
>>>> +        object_property_set_int(OBJECT(&s->cpu[i]),
>>>> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
>>>> +                                "mp-affinity", &error_abort);
>>>> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>>>> +                                "reset-cbar", &error_abort);
>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
>>>> +                                 "reset-hivecs", &error_abort);
>>>> +
>>>> +        /* Disable security extensions. */
>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>>>> +                                 &error_abort);
>>>> +
>>>> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>>>
>>> I would check the error:
>>>
>>>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
>>>             return;
>>>         }
>>>
>>> same for the sysbus_realize() below.
>> 
>> Hmm, I used to propagate these errors until Philippe told me not to
>> (or at least that's how I understood it).
>
> It was before Markus simplification API were merged, you had to
> propagate after each call, since this is a non hot-pluggable SoC
> I suggested to use &error_abort to simplify.
>
>> I'll be happy to do it
>> either way (and the new API makes it really easy to propagate errors),
>> but I worry that I don't fully understand when to propagate errors and
>> when not to.
>
> Markus explained it on the mailing list recently (as I found the doc
> not obvious). I can't find the thread. I suppose once the work result
> after the "Questionable aspects of QEMU Error's design" discussion is
> merged, the documentation will be clarified.

The Error API evolved recently.  Please peruse the big comment in
include/qapi/error.h.  If still unsure, don't hesitate to ask here.

> My rule of thumb so far is:
> - programming error (can't happen) -> &error_abort

Correct.  Quote the big comment:

 * Call a function aborting on errors:
 *     foo(arg, &error_abort);
 * This is more concise and fails more nicely than
 *     Error *err = NULL;
 *     foo(arg, &err);
 *     assert(!err); // don't do this

> - everything triggerable by user or management layer (via QMP command)
>   -> &error_fatal, as we can't risk loose the user data, we need to
>   shutdown gracefully.

Quote the big comment:

 * Call a function treating errors as fatal:
 *     foo(arg, &error_fatal);
 * This is more concise than
 *     Error *err = NULL;
 *     foo(arg, &err);
 *     if (err) { // don't do this
 *         error_report_err(err);
 *         exit(1);
 *     }

Terminating the process is generally fine during initial startup,
i.e. before the guest runs.

It's generally not fine once the guest runs.  Errors need to be handled
more gracefully then.  A QMP command, for instance, should fail cleanly,
propagating the error to the monitor core, which then sends it to the
QMP client, and loops to process the next command.

>> It makes sense to me to propagate errors from *_realize() and
>> error_abort on failure to set simple properties, but I'd like to know
>> if Philippe is on board with that.

Realize methods must not use &error_fatal.  Instead, they should clean
up and fail.

"Clean up" is the part we often neglect.  The big advantage of
&error_fatal is that you don't have to bother :)

Questions?

[...]



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14 11:29         ` Philippe Mathieu-Daudé
@ 2020-07-14 16:21           ` Markus Armbruster
  2020-07-14 17:16             ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Markus Armbruster @ 2020-07-14 16:21 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, Havard Skinnemoen,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

Philippe Mathieu-Daudé <f4bug@amsat.org> writes:

> + qemu-block experts.
>
> On 7/14/20 11:16 AM, Markus Armbruster wrote:
>> Havard Skinnemoen <hskinnemoen@google.com> writes:
>> 
>>> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>>
>>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>>>> one built with OpenBMC. For example like this:
>>>>>
>>>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>>>
>>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>
>>>> May be we don't need to create the flash object if dinfo is NULL.
>>>
>>> It's soldered on the board, so you can't really boot the board without
>>> it. But if you think it's better to remove it altogether if we don't
>>> have an image to load into it, I can do that.
>> 
>> If a device is a fixed part of the physical board, it should be a fixed
>> part of the virtual board, too.
>
> We agree so far but ... how to do it?
>
> I never used this API, does that makes sense?
>
>     if (!dinfo) {
>         QemuOpts *opts;
>
>         opts = qemu_opts_create(NULL, "spi-flash", 1, &error_abort);
>         qdict_put_str(opts, "format", "null-co");
>         qdict_put_int(opts, BLOCK_OPT_SIZE, 64 * MiB);
>         qdict_put_bool(opts, NULL_OPT_ZEROES, false); // XXX
>
>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>         qemu_opts_del(opts);
>     }

I believe existing code special-cases "no backend" instead of making one
up.

Example: pflash_cfi0?.c

If ->blk is non-null, we read its contents into the memory buffer and
write updates back, else we leave it blank and don't write updates back.

Making one up could be more elegant.  To find out, you have to try.

We make up a few default drives (i.e. drives the user doesn't specify):
floppy, CD-ROM and SD card.  Ancient part of the user interface, uses
DriveInfo.  I doubt we should create more of them.

I believe block backends we make up for internal use should stay away
from DriveInfo.  Kevin, what do you think?  How would you make up a
null-co block backend for a device's internal use?

> We should probably add a public helper for that.

If we decide we want to make up backends, then yes, we should do that in
a helper, not in each device.

> 'XXX' because NOR flashes erase content is when hardware bit
> is set, so it would be more useful to return -1/0xff... rather
> than zeroes.



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-14 16:01         ` Markus Armbruster
@ 2020-07-14 17:11           ` Philippe Mathieu-Daudé
  2020-07-15  1:03             ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-14 17:11 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Peter Maydell, Joel Stanley, Havard Skinnemoen, QEMU Developers,
	CS20 KFTing, qemu-arm, IS20 Avi Fishman, Cédric Le Goater

On 7/14/20 6:01 PM, Markus Armbruster wrote:
> Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
> 
>> +Markus
>>
>> On 7/14/20 2:44 AM, Havard Skinnemoen wrote:
>>> On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>>
>>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>>> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
>>>>> Management Controllers in servers. While the family includes four SoCs,
>>>>> this patch implements limited support for two of them: NPCM730 (targeted
>>>>> for Data Center applications) and NPCM750 (targeted for Enterprise
>>>>> applications).
>>>>>
>>>>> This patch includes little more than the bare minimum needed to boot a
>>>>> Linux kernel built with NPCM7xx support in direct-kernel mode:
>>>>>
>>>>>   - Two Cortex-A9 CPU cores with built-in periperhals.
>>>>>   - Global Configuration Registers.
>>>>>   - Clock Management.
>>>>>   - 3 Timer Modules with 5 timers each.
>>>>>   - 4 serial ports.
>>>>>
>>>>> The chips themselves have a lot more features, some of which will be
>>>>> added to the model at a later stage.
>>>>>
>>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>> ---
>> ...
>>
>>>>> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
>>>>> +{
>>>>> +    NPCM7xxState *s = NPCM7XX(dev);
>>>>> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>>>>> +    int i;
>>>>> +
>>>>> +    /* CPUs */
>>>>> +    for (i = 0; i < nc->num_cpus; i++) {
>>>>> +        object_property_set_int(OBJECT(&s->cpu[i]),
>>>>> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
>>>>> +                                "mp-affinity", &error_abort);
>>>>> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>>>>> +                                "reset-cbar", &error_abort);
>>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
>>>>> +                                 "reset-hivecs", &error_abort);
>>>>> +
>>>>> +        /* Disable security extensions. */
>>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>>>>> +                                 &error_abort);
>>>>> +
>>>>> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>>>>
>>>> I would check the error:
>>>>
>>>>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
>>>>             return;
>>>>         }
>>>>
>>>> same for the sysbus_realize() below.
>>>
>>> Hmm, I used to propagate these errors until Philippe told me not to
>>> (or at least that's how I understood it).
>>
>> It was before Markus simplification API were merged, you had to
>> propagate after each call, since this is a non hot-pluggable SoC
>> I suggested to use &error_abort to simplify.
>>
>>> I'll be happy to do it
>>> either way (and the new API makes it really easy to propagate errors),
>>> but I worry that I don't fully understand when to propagate errors and
>>> when not to.
>>
>> Markus explained it on the mailing list recently (as I found the doc
>> not obvious). I can't find the thread. I suppose once the work result
>> after the "Questionable aspects of QEMU Error's design" discussion is
>> merged, the documentation will be clarified.
> 
> The Error API evolved recently.  Please peruse the big comment in
> include/qapi/error.h.  If still unsure, don't hesitate to ask here.
> 
>> My rule of thumb so far is:
>> - programming error (can't happen) -> &error_abort
> 
> Correct.  Quote the big comment:
> 
>  * Call a function aborting on errors:
>  *     foo(arg, &error_abort);
>  * This is more concise and fails more nicely than
>  *     Error *err = NULL;
>  *     foo(arg, &err);
>  *     assert(!err); // don't do this
> 
>> - everything triggerable by user or management layer (via QMP command)
>>   -> &error_fatal, as we can't risk loose the user data, we need to
>>   shutdown gracefully.
> 
> Quote the big comment:
> 
>  * Call a function treating errors as fatal:
>  *     foo(arg, &error_fatal);
>  * This is more concise than
>  *     Error *err = NULL;
>  *     foo(arg, &err);
>  *     if (err) { // don't do this
>  *         error_report_err(err);
>  *         exit(1);
>  *     }
> 
> Terminating the process is generally fine during initial startup,
> i.e. before the guest runs.
> 
> It's generally not fine once the guest runs.  Errors need to be handled
> more gracefully then.  A QMP command, for instance, should fail cleanly,
> propagating the error to the monitor core, which then sends it to the
> QMP client, and loops to process the next command.
> 
>>> It makes sense to me to propagate errors from *_realize() and
>>> error_abort on failure to set simple properties, but I'd like to know
>>> if Philippe is on board with that.
> 
> Realize methods must not use &error_fatal.  Instead, they should clean
> up and fail.
> 
> "Clean up" is the part we often neglect.  The big advantage of
> &error_fatal is that you don't have to bother :)
> 
> Questions?

One on my side. So in this realize(), all &error_abort uses has
to be replaced by local_err + propagate ...:

static void npcm7xx_realize(DeviceState *dev, Error **errp)
{
    NPCM7xxState *s = NPCM7XX(dev);
    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
    int i;

    /* CPUs */
    for (i = 0; i < nc->num_cpus; i++) {
        object_property_set_int(OBJECT(&s->cpu[i]),
                                arm_cpu_mp_affinity(i,
NPCM7XX_MAX_NUM_CPUS),
                                "mp-affinity", &error_abort);
        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
                                "reset-cbar", &error_abort);
        object_property_set_bool(OBJECT(&s->cpu[i]), true,
                                 "reset-hivecs", &error_abort);

        /* Disable security extensions. */
        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
                                 &error_abort);

        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
    }
    [...]

... but the caller does:

static void quanta_gsj_init(MachineState *machine)
{
    NPCM7xxState *soc;

    soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
    npcm7xx_connect_dram(soc, machine->ram);
    qdev_realize(DEVICE(soc), NULL, &error_abort);
                                    ^^^^^^^^^^^^
    npcm7xx_load_kernel(machine, soc);
}

So we overload the code...

My question: Do you confirm this is worth it to propagate?


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14 16:21           ` Markus Armbruster
@ 2020-07-14 17:16             ` Philippe Mathieu-Daudé
  2020-07-15  9:00               ` Markus Armbruster
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-14 17:16 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, Havard Skinnemoen,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

On 7/14/20 6:21 PM, Markus Armbruster wrote:
> Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
> 
>> + qemu-block experts.
>>
>> On 7/14/20 11:16 AM, Markus Armbruster wrote:
>>> Havard Skinnemoen <hskinnemoen@google.com> writes:
>>>
>>>> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>>>
>>>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>>>>> one built with OpenBMC. For example like this:
>>>>>>
>>>>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>>>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>>>>
>>>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>>
>>>>> May be we don't need to create the flash object if dinfo is NULL.
>>>>
>>>> It's soldered on the board, so you can't really boot the board without
>>>> it. But if you think it's better to remove it altogether if we don't
>>>> have an image to load into it, I can do that.
>>>
>>> If a device is a fixed part of the physical board, it should be a fixed
>>> part of the virtual board, too.
>>
>> We agree so far but ... how to do it?
>>
>> I never used this API, does that makes sense?
>>
>>     if (!dinfo) {
>>         QemuOpts *opts;
>>
>>         opts = qemu_opts_create(NULL, "spi-flash", 1, &error_abort);
>>         qdict_put_str(opts, "format", "null-co");
>>         qdict_put_int(opts, BLOCK_OPT_SIZE, 64 * MiB);
>>         qdict_put_bool(opts, NULL_OPT_ZEROES, false); // XXX
>>
>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>         qemu_opts_del(opts);
>>     }
> 
> I believe existing code special-cases "no backend" instead of making one
> up.
> 
> Example: pflash_cfi0?.c
> 
> If ->blk is non-null, we read its contents into the memory buffer and
> write updates back, else we leave it blank and don't write updates back.
> 
> Making one up could be more elegant.  To find out, you have to try.

I'd rather avoid ad-hoc code in each device. I2C EEPROM do that too,
it is a source of head aches.

From the emulation PoV I'd prefer to always use a block backend,
regardless the user provide a drive.

> 
> We make up a few default drives (i.e. drives the user doesn't specify):
> floppy, CD-ROM and SD card.  Ancient part of the user interface, uses
> DriveInfo.  I doubt we should create more of them.
> 
> I believe block backends we make up for internal use should stay away
> from DriveInfo.  Kevin, what do you think?  How would you make up a
> null-co block backend for a device's internal use?

I read 'DriveInfo' is the legacy interface, but all the code base use it
so it is confusing, I don't understand what is the correct interface to
use.

> 
>> We should probably add a public helper for that.
> 
> If we decide we want to make up backends, then yes, we should do that in
> a helper, not in each device.
> 
>> 'XXX' because NOR flashes erase content is when hardware bit
>> is set, so it would be more useful to return -1/0xff... rather
>> than zeroes.
> 
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-14 17:11           ` Philippe Mathieu-Daudé
@ 2020-07-15  1:03             ` Havard Skinnemoen
  2020-07-15  9:35               ` Markus Armbruster
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-15  1:03 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Markus Armbruster, Peter Maydell, IS20 Avi Fishman,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	Joel Stanley

On Tue, Jul 14, 2020 at 10:11 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/14/20 6:01 PM, Markus Armbruster wrote:
> > Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
> >
> >> +Markus
> >>
> >> On 7/14/20 2:44 AM, Havard Skinnemoen wrote:
> >>> On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
> >>>>
> >>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> >>>>> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
> >>>>> Management Controllers in servers. While the family includes four SoCs,
> >>>>> this patch implements limited support for two of them: NPCM730 (targeted
> >>>>> for Data Center applications) and NPCM750 (targeted for Enterprise
> >>>>> applications).
> >>>>>
> >>>>> This patch includes little more than the bare minimum needed to boot a
> >>>>> Linux kernel built with NPCM7xx support in direct-kernel mode:
> >>>>>
> >>>>>   - Two Cortex-A9 CPU cores with built-in periperhals.
> >>>>>   - Global Configuration Registers.
> >>>>>   - Clock Management.
> >>>>>   - 3 Timer Modules with 5 timers each.
> >>>>>   - 4 serial ports.
> >>>>>
> >>>>> The chips themselves have a lot more features, some of which will be
> >>>>> added to the model at a later stage.
> >>>>>
> >>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> >>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
> >>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>>>> ---
> >> ...
> >>
> >>>>> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
> >>>>> +{
> >>>>> +    NPCM7xxState *s = NPCM7XX(dev);
> >>>>> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
> >>>>> +    int i;
> >>>>> +
> >>>>> +    /* CPUs */
> >>>>> +    for (i = 0; i < nc->num_cpus; i++) {
> >>>>> +        object_property_set_int(OBJECT(&s->cpu[i]),
> >>>>> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
> >>>>> +                                "mp-affinity", &error_abort);
> >>>>> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
> >>>>> +                                "reset-cbar", &error_abort);
> >>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
> >>>>> +                                 "reset-hivecs", &error_abort);
> >>>>> +
> >>>>> +        /* Disable security extensions. */
> >>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
> >>>>> +                                 &error_abort);
> >>>>> +
> >>>>> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
> >>>>
> >>>> I would check the error:
> >>>>
> >>>>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
> >>>>             return;
> >>>>         }
> >>>>
> >>>> same for the sysbus_realize() below.
> >>>
> >>> Hmm, I used to propagate these errors until Philippe told me not to
> >>> (or at least that's how I understood it).
> >>
> >> It was before Markus simplification API were merged, you had to
> >> propagate after each call, since this is a non hot-pluggable SoC
> >> I suggested to use &error_abort to simplify.
> >>
> >>> I'll be happy to do it
> >>> either way (and the new API makes it really easy to propagate errors),
> >>> but I worry that I don't fully understand when to propagate errors and
> >>> when not to.
> >>
> >> Markus explained it on the mailing list recently (as I found the doc
> >> not obvious). I can't find the thread. I suppose once the work result
> >> after the "Questionable aspects of QEMU Error's design" discussion is
> >> merged, the documentation will be clarified.
> >
> > The Error API evolved recently.  Please peruse the big comment in
> > include/qapi/error.h.  If still unsure, don't hesitate to ask here.
> >
> >> My rule of thumb so far is:
> >> - programming error (can't happen) -> &error_abort
> >
> > Correct.  Quote the big comment:
> >
> >  * Call a function aborting on errors:
> >  *     foo(arg, &error_abort);
> >  * This is more concise and fails more nicely than
> >  *     Error *err = NULL;
> >  *     foo(arg, &err);
> >  *     assert(!err); // don't do this
> >
> >> - everything triggerable by user or management layer (via QMP command)
> >>   -> &error_fatal, as we can't risk loose the user data, we need to
> >>   shutdown gracefully.
> >
> > Quote the big comment:
> >
> >  * Call a function treating errors as fatal:
> >  *     foo(arg, &error_fatal);
> >  * This is more concise than
> >  *     Error *err = NULL;
> >  *     foo(arg, &err);
> >  *     if (err) { // don't do this
> >  *         error_report_err(err);
> >  *         exit(1);
> >  *     }
> >
> > Terminating the process is generally fine during initial startup,
> > i.e. before the guest runs.
> >
> > It's generally not fine once the guest runs.  Errors need to be handled
> > more gracefully then.  A QMP command, for instance, should fail cleanly,
> > propagating the error to the monitor core, which then sends it to the
> > QMP client, and loops to process the next command.
> >
> >>> It makes sense to me to propagate errors from *_realize() and
> >>> error_abort on failure to set simple properties, but I'd like to know
> >>> if Philippe is on board with that.
> >
> > Realize methods must not use &error_fatal.  Instead, they should clean
> > up and fail.
> >
> > "Clean up" is the part we often neglect.  The big advantage of
> > &error_fatal is that you don't have to bother :)
> >
> > Questions?
>
> One on my side. So in this realize(), all &error_abort uses has
> to be replaced by local_err + propagate ...:
>
> static void npcm7xx_realize(DeviceState *dev, Error **errp)
> {
>     NPCM7xxState *s = NPCM7XX(dev);
>     NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>     int i;
>
>     /* CPUs */
>     for (i = 0; i < nc->num_cpus; i++) {
>         object_property_set_int(OBJECT(&s->cpu[i]),
>                                 arm_cpu_mp_affinity(i,
> NPCM7XX_MAX_NUM_CPUS),
>                                 "mp-affinity", &error_abort);
>         object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>                                 "reset-cbar", &error_abort);
>         object_property_set_bool(OBJECT(&s->cpu[i]), true,
>                                  "reset-hivecs", &error_abort);
>
>         /* Disable security extensions. */
>         object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>                                  &error_abort);
>
>         qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>     }
>     [...]
>
> ... but the caller does:
>
> static void quanta_gsj_init(MachineState *machine)
> {
>     NPCM7xxState *soc;
>
>     soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
>     npcm7xx_connect_dram(soc, machine->ram);
>     qdev_realize(DEVICE(soc), NULL, &error_abort);
>                                     ^^^^^^^^^^^^
>     npcm7xx_load_kernel(machine, soc);
> }
>
> So we overload the code...
>
> My question: Do you confirm this is worth it to propagate?

Here's my understanding. Please let me know if it sounds right.

1. Internal code failing to set simple properties to predefined values
is a programming error, so error_abort is appropriate.
2. qdev_realize() may fail due to user input, so errors should be propagated.
3. machine init can't propagate errors any further, so all errors are
fatal. But if all realize() functions follow (1) and (2), only user
errors are propagated, so error_fatal should be used to produce a nice
error message rather than "Unexpected error, aborting!"

If any of this can ever be hot-plugged, then it means errors may
propagate somewhere other than the machine init code, so it becomes
extra important not to let bad user input crash the whole qemu
process. I don't know if this is a concern when none of these devices
can currently be hot-plugged.

For example, if the user tries to create a machine with 64 MB RAM, the
gcr device will report an error because it can't represent less than
128 MB of memory. Currently, this is reported as

$ ./arm-softmmu/qemu-system-arm -machine npcm750-evb -nographic -m 64
Unexpected error in npcm7xx_gcr_realize() at
/usr/local/google/home/hskinnemoen/qemu/for-upstream/hw/misc/npcm7xx_gcr.c:151:
qemu-system-arm: npcm7xx_gcr: DRAM size 67108864 is too small (128 MiB minimum)
Aborted

But if I change npcm7xx_realize() to propagate errors from
sysbus_realize(gcr), and change npcm750_evb_init() to use error_fatal
instead of error_abort, I get

$ ./arm-softmmu/qemu-system-arm -machine npcm750-evb -nographic -m 64
qemu-system-arm: npcm7xx_gcr: DRAM size 67108864 is too small (128 MiB minimum)

which seems less scary and more accurate.

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller device model
  2020-07-09  0:35 ` [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen
@ 2020-07-15  7:18   ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-15  7:18 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: Avi.Fishman, qemu-devel, kfting, qemu-arm, Cédric Le Goater,
	Joel Stanley

On 7/9/20 2:35 AM, Havard Skinnemoen wrote:
> 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>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/misc/npcm7xx_clk.h |  66 ++++++++++
>  hw/misc/npcm7xx_clk.c         | 230 ++++++++++++++++++++++++++++++++++
>  hw/misc/Makefile.objs         |   1 +
>  hw/misc/trace-events          |   4 +
>  4 files changed, 301 insertions(+)
>  create mode 100644 include/hw/misc/npcm7xx_clk.h
>  create mode 100644 hw/misc/npcm7xx_clk.c
> 
...

> +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;

Can we declare NPCM7XX_TIMER_REF_HZ in hw/misc/npcm7xx_clk.h and
have the timer device include hw/misc/npcm7xx_clk.h?

> +        break;
> +
> +    default:
> +        value = s->regs[reg];
> +        break;
> +    };
> +
> +    trace_npcm7xx_clk_read(offset, value);
> +
> +    return value;
> +}
...


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer device model
  2020-07-09  0:36 ` [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen
@ 2020-07-15  7:25   ` Philippe Mathieu-Daudé
  2020-07-15 23:04     ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-15  7:25 UTC (permalink / raw)
  To: Havard Skinnemoen, peter.maydell
  Cc: kfting, qemu-arm, Joel Stanley, qemu-devel, Avi.Fishman

On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> The NPCM730 and NPCM750 SoCs have three timer modules each holding five
> timers and some shared registers (e.g. interrupt status).
> 
> Each timer runs at 25 MHz divided by a prescaler, and counts down from a
> configurable initial value to zero. When zero is reached, the interrupt
> flag for the timer is set, and the timer is disabled (one-shot mode) or
> reloaded from its initial value (periodic mode).
> 
> This implementation is sufficient to boot a Linux kernel configured for
> NPCM750. Note that the kernel does not seem to actually turn on the
> interrupts.
> 
> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> Reviewed-by: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> ---
>  include/hw/timer/npcm7xx_timer.h |  96 +++++++
>  hw/timer/npcm7xx_timer.c         | 468 +++++++++++++++++++++++++++++++
>  hw/timer/Makefile.objs           |   1 +
>  hw/timer/trace-events            |   5 +
>  4 files changed, 570 insertions(+)
>  create mode 100644 include/hw/timer/npcm7xx_timer.h
>  create mode 100644 hw/timer/npcm7xx_timer.c
> 
...

> +/* The reference clock frequency is always 25 MHz. */
> +#define NPCM7XX_TIMER_REF_HZ            (25000000)
> +
> +/* Return the value by which to divide the reference clock rate. */
> +static uint32_t npcm7xx_timer_prescaler(const NPCM7xxTimer *t)
> +{
> +    return extract32(t->tcsr, NPCM7XX_TCSR_PRESCALE_START,
> +                     NPCM7XX_TCSR_PRESCALE_LEN) + 1;
> +}
> +
> +/* Convert a timer cycle count to a time interval in nanoseconds. */
> +static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
> +{
> +    int64_t ns = count;
> +
> +    ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
> +    ns *= npcm7xx_timer_prescaler(t);
> +
> +    return ns;
> +}
> +
> +/* Convert a time interval in nanoseconds to a timer cycle count. */
> +static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
> +{
> +    int64_t count;
> +
> +    count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
> +    count /= npcm7xx_timer_prescaler(t);
> +
> +    return count;
> +}
> +
> +/*
> + * Raise the interrupt line if there's a pending interrupt and interrupts are
> + * enabled for this timer. If not, lower it.
> + */
> +static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
> +{
> +    NPCM7xxTimerCtrlState *tc = t->ctrl;
> +    /* Find the array index of this timer. */
> +    int index = t - tc->timer;

As you suggested in another device in this series, using a getter
here is clearer.

> +
> +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
> +
> +    if ((t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index))) {
> +        qemu_irq_raise(t->irq);
> +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 1);
> +    } else {
> +        qemu_irq_lower(t->irq);
> +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 0);
> +    }
> +}
> +
> +/* Start or resume the timer. */
> +static void npcm7xx_timer_start(NPCM7xxTimer *t)
> +{
> +    int64_t now;
> +
> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    t->expires_ns = now + t->remaining_ns;
> +    timer_mod(&t->qtimer, t->expires_ns);
> +}
> +
> +/*
> + * Called when the counter reaches zero. Sets the interrupt flag, and either
> + * restarts or disables the timer.
> + */
> +static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
> +{
> +    NPCM7xxTimerCtrlState *tc = t->ctrl;
> +    int index = t - tc->timer;
> +
> +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
> +
> +    tc->tisr |= BIT(index);
> +
> +    if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
> +        t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
> +        if (t->tcsr & NPCM7XX_TCSR_CEN) {
> +            npcm7xx_timer_start(t);
> +        }
> +    } else {
> +        t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
> +    }
> +
> +    npcm7xx_timer_check_interrupt(t);
> +}
> +
> +/* Stop counting. Record the time remaining so we can continue later. */
> +static void npcm7xx_timer_pause(NPCM7xxTimer *t)
> +{
> +    int64_t now;
> +
> +    timer_del(&t->qtimer);
> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +    t->remaining_ns = t->expires_ns - now;
> +    if (t->remaining_ns <= 0) {

Can this happen? Shouldn't we get npcm7xx_timer_expired() before?

> +        npcm7xx_timer_reached_zero(t);
> +    }
> +}
> +
> +/*
> + * Restart the timer from its initial value. If the timer was enabled and stays
> + * enabled, adjust the QEMU timer according to the new count. If the timer is
> + * transitioning from disabled to enabled, the caller is expected to start the
> + * timer later.
> + */
> +static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
> +{
> +    t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
> +
> +    if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
> +        npcm7xx_timer_start(t);
> +    }
> +}
> +
> +/* Register read and write handlers */
> +
> +static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
> +{
> +    uint32_t old_tcsr = t->tcsr;
> +
> +    if (new_tcsr & NPCM7XX_TCSR_RSVD) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
> +                      __func__, new_tcsr);
> +        new_tcsr &= ~NPCM7XX_TCSR_RSVD;
> +    }
> +    if (new_tcsr & NPCM7XX_TCSR_CACT) {
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
> +                      __func__, new_tcsr);
> +        new_tcsr &= ~NPCM7XX_TCSR_CACT;
> +    }
> +
> +    t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
> +
> +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
> +        npcm7xx_timer_check_interrupt(t);
> +    }
> +    if (new_tcsr & NPCM7XX_TCSR_CRST) {
> +        npcm7xx_timer_restart(t, old_tcsr);
> +        t->tcsr &= ~NPCM7XX_TCSR_CRST;
> +    }
> +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
> +        if (new_tcsr & NPCM7XX_TCSR_CEN) {
> +            npcm7xx_timer_start(t);
> +        } else {
> +            npcm7xx_timer_pause(t);
> +        }
> +    }
> +}
> +
> +static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
> +{
> +    t->ticr = new_ticr;
> +
> +    npcm7xx_timer_restart(t, t->tcsr);
> +}
> +
> +static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
> +{
> +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
> +        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +
> +        return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
> +    }
> +
> +    return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
> +}
> +
> +static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
> +{
> +    NPCM7xxTimerCtrlState *s = opaque;
> +    uint64_t value = 0;
> +    hwaddr reg;
> +
> +    reg = offset / sizeof(uint32_t);
> +    switch (reg) {
> +    case NPCM7XX_TIMER_TCSR0:
> +        value = s->timer[0].tcsr;
> +        break;
> +    case NPCM7XX_TIMER_TCSR1:
> +        value = s->timer[1].tcsr;

Maybe add:

static hwaddr timer_index(hwaddr reg)
{
    return reg - NPCM7XX_TIMER_TCSR0;
}

And use shorter:

    case NPCM7XX_TIMER_TCSR0:
    case NPCM7XX_TIMER_TCSR1:
    case NPCM7XX_TIMER_TCSR2:
    case NPCM7XX_TIMER_TCSR3:
    case NPCM7XX_TIMER_TCSR4:
        value = s->timer[timer_index(reg)].tcsr;
        break;

Similarly with NPCM7XX_TIMER_TDRx and in npcm7xx_timer_write().

> +        break;
> +    case NPCM7XX_TIMER_TCSR2:
> +        value = s->timer[2].tcsr;
> +        break;
> +    case NPCM7XX_TIMER_TCSR3:
> +        value = s->timer[3].tcsr;
> +        break;
> +    case NPCM7XX_TIMER_TCSR4:
> +        value = s->timer[4].tcsr;
> +        break;
> +
> +    case NPCM7XX_TIMER_TICR0:
> +        value = s->timer[0].ticr;
> +        break;
> +    case NPCM7XX_TIMER_TICR1:
> +        value = s->timer[1].ticr;
> +        break;
> +    case NPCM7XX_TIMER_TICR2:
> +        value = s->timer[2].ticr;
> +        break;
> +    case NPCM7XX_TIMER_TICR3:
> +        value = s->timer[3].ticr;
> +        break;
> +    case NPCM7XX_TIMER_TICR4:
> +        value = s->timer[4].ticr;
> +        break;
> +
> +    case NPCM7XX_TIMER_TDR0:
> +        value = npcm7xx_timer_read_tdr(&s->timer[0]);
> +        break;
> +    case NPCM7XX_TIMER_TDR1:
> +        value = npcm7xx_timer_read_tdr(&s->timer[1]);
> +        break;
> +    case NPCM7XX_TIMER_TDR2:
> +        value = npcm7xx_timer_read_tdr(&s->timer[2]);
> +        break;
> +    case NPCM7XX_TIMER_TDR3:
> +        value = npcm7xx_timer_read_tdr(&s->timer[3]);
> +        break;
> +    case NPCM7XX_TIMER_TDR4:
> +        value = npcm7xx_timer_read_tdr(&s->timer[4]);
> +        break;
> +
> +    case NPCM7XX_TIMER_TISR:
> +        value = s->tisr;
> +        break;
> +
> +    case NPCM7XX_TIMER_WTCR:
> +        value = s->wtcr;
> +        break;
> +
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
> +                      __func__, (unsigned int)offset);
> +        break;
> +    }
> +
> +    trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
> +
> +    return value;
> +}
> +
> +static void npcm7xx_timer_write(void *opaque, hwaddr offset,
> +                                uint64_t v, unsigned size)
> +{
> +    uint32_t reg = offset / sizeof(uint32_t);
> +    NPCM7xxTimerCtrlState *s = opaque;
> +    uint32_t value = v;
> +
> +    trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
> +
> +    switch (reg) {
> +    case NPCM7XX_TIMER_TCSR0:
> +        npcm7xx_timer_write_tcsr(&s->timer[0], value);
> +        return;
> +    case NPCM7XX_TIMER_TCSR1:
> +        npcm7xx_timer_write_tcsr(&s->timer[1], value);
> +        return;
> +    case NPCM7XX_TIMER_TCSR2:
> +        npcm7xx_timer_write_tcsr(&s->timer[2], value);
> +        return;
> +    case NPCM7XX_TIMER_TCSR3:
> +        npcm7xx_timer_write_tcsr(&s->timer[3], value);
> +        return;
> +    case NPCM7XX_TIMER_TCSR4:
> +        npcm7xx_timer_write_tcsr(&s->timer[4], value);
> +        return;
> +
> +    case NPCM7XX_TIMER_TICR0:
> +        npcm7xx_timer_write_ticr(&s->timer[0], value);
> +        return;
> +    case NPCM7XX_TIMER_TICR1:
> +        npcm7xx_timer_write_ticr(&s->timer[1], value);
> +        return;
> +    case NPCM7XX_TIMER_TICR2:
> +        npcm7xx_timer_write_ticr(&s->timer[2], value);
> +        return;
> +    case NPCM7XX_TIMER_TICR3:
> +        npcm7xx_timer_write_ticr(&s->timer[3], value);
> +        return;
> +    case NPCM7XX_TIMER_TICR4:
> +        npcm7xx_timer_write_ticr(&s->timer[4], value);
> +        return;
> +
> +    case NPCM7XX_TIMER_TDR0:
> +    case NPCM7XX_TIMER_TDR1:
> +    case NPCM7XX_TIMER_TDR2:
> +    case NPCM7XX_TIMER_TDR3:
> +    case NPCM7XX_TIMER_TDR4:
> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
> +                      __func__, (unsigned int)offset);
> +        return;
> +
> +    case NPCM7XX_TIMER_TISR:
> +        s->tisr &= ~value;
> +        return;
> +
> +    case NPCM7XX_TIMER_WTCR:
> +        qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
> +                      __func__, value);
> +        return;
> +    }
> +
> +    qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
> +                  __func__, (unsigned int)offset);
> +}
> +
> +static const struct MemoryRegionOps npcm7xx_timer_ops = {
> +    .read       = npcm7xx_timer_read,
> +    .write      = npcm7xx_timer_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid      = {
> +        .min_access_size        = 4,
> +        .max_access_size        = 4,
> +        .unaligned              = false,
> +    },
> +};
> +
> +/* Called when the QEMU timer expires. */
> +static void npcm7xx_timer_expired(void *opaque)
> +{
> +    NPCM7xxTimer *t = opaque;
> +
> +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
> +        npcm7xx_timer_reached_zero(t);
> +    }
> +}
...


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14  2:56     ` Havard Skinnemoen
  2020-07-14  9:16       ` Markus Armbruster
@ 2020-07-15  7:42       ` Cédric Le Goater
  2020-07-15 21:19         ` Havard Skinnemoen
  1 sibling, 1 reply; 64+ messages in thread
From: Cédric Le Goater @ 2020-07-15  7:42 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, Philippe Mathieu-Daudé,
	QEMU Developers, CS20 KFTing, qemu-arm, IS20 Avi Fishman,
	Joel Stanley

On 7/14/20 4:56 AM, Havard Skinnemoen wrote:
> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>> one built with OpenBMC. For example like this:
>>>
>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>
>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>
>> May be we don't need to create the flash object if dinfo is NULL.
> 
> It's soldered on the board, so you can't really boot the board without
> it. But if you think it's better to remove it altogether if we don't
> have an image to load into it, I can do that.
> 
>>
>> Reviewed-by: Cédric Le Goater <clg@kaod.org>
>> Tested-by: Cédric Le Goater <clg@kaod.org>
>>
>> Nice !
>>
>> We need a SPI controller model and a network device model now.
> 
> Yeah, and i2c, PWM, GPIO, etc., but if you're referring to the kernel
> crash, see below.

We don't need all device models but fixing the crash would be better. 

>> npcm7xx_bootrom.bin is a bit of a pain. Could we include it in
>> the QEMU roms ?
> 
> Yeah, I was planning to include this in v6.

Good. It will ease CI.

>> spi_master spi0: /ahb/fiu@fb000000/spi-nor@0 has no valid 'spi-max-frequency' property (-22)
>> spi_master spi0: Failed to create SPI device for /ahb/fiu@fb000000/spi-nor@0
> 
> This is a device tree bug:
> 
> https://github.com/hskinnemoen/openbmc/commit/99b172f88002f4fac939f85debe1187b9c569871
> 
>> libphy: Fixed MDIO Bus: probed
>> 8<--- cut here ---
>> Unable to handle kernel paging request at virtual address fffffffe
> 
> I believe this is a kernel bug:
> 
> https://github.com/hskinnemoen/openbmc/commit/77e9f58ba157eabc976f15fa49892128fe2b2382
> 
> I needed two additional patches to get all the way to the login prompt:
> 
> https://github.com/hskinnemoen/openbmc/commits/20200711-gsj-qemu-0
 

Great. So are these for mainline or Joel's openbmc branch ? 

Thanks,

C.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-14 17:16             ` Philippe Mathieu-Daudé
@ 2020-07-15  9:00               ` Markus Armbruster
  2020-07-15 10:57                 ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Markus Armbruster @ 2020-07-15  9:00 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, Havard Skinnemoen,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

Philippe Mathieu-Daudé <f4bug@amsat.org> writes:

> On 7/14/20 6:21 PM, Markus Armbruster wrote:
>> Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
>> 
>>> + qemu-block experts.
>>>
>>> On 7/14/20 11:16 AM, Markus Armbruster wrote:
>>>> Havard Skinnemoen <hskinnemoen@google.com> writes:
>>>>
>>>>> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>>>>
>>>>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>>>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>>>>>> one built with OpenBMC. For example like this:
>>>>>>>
>>>>>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>>>>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>>>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>>>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>>>>>
>>>>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>>>
>>>>>> May be we don't need to create the flash object if dinfo is NULL.
>>>>>
>>>>> It's soldered on the board, so you can't really boot the board without
>>>>> it. But if you think it's better to remove it altogether if we don't
>>>>> have an image to load into it, I can do that.
>>>>
>>>> If a device is a fixed part of the physical board, it should be a fixed
>>>> part of the virtual board, too.
>>>
>>> We agree so far but ... how to do it?
>>>
>>> I never used this API, does that makes sense?
>>>
>>>     if (!dinfo) {
>>>         QemuOpts *opts;
>>>
>>>         opts = qemu_opts_create(NULL, "spi-flash", 1, &error_abort);
>>>         qdict_put_str(opts, "format", "null-co");
>>>         qdict_put_int(opts, BLOCK_OPT_SIZE, 64 * MiB);
>>>         qdict_put_bool(opts, NULL_OPT_ZEROES, false); // XXX
>>>
>>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>>         qemu_opts_del(opts);
>>>     }
>> 
>> I believe existing code special-cases "no backend" instead of making one
>> up.
>> 
>> Example: pflash_cfi0?.c
>> 
>> If ->blk is non-null, we read its contents into the memory buffer and
>> write updates back, else we leave it blank and don't write updates back.
>> 
>> Making one up could be more elegant.  To find out, you have to try.
>
> I'd rather avoid ad-hoc code in each device. I2C EEPROM do that too,
> it is a source of head aches.
>
>>From the emulation PoV I'd prefer to always use a block backend,
> regardless the user provide a drive.
>
>> 
>> We make up a few default drives (i.e. drives the user doesn't specify):
>> floppy, CD-ROM and SD card.  Ancient part of the user interface, uses
>> DriveInfo.  I doubt we should create more of them.
>> 
>> I believe block backends we make up for internal use should stay away
>> from DriveInfo.  Kevin, what do you think?  How would you make up a
>> null-co block backend for a device's internal use?
>
> I read 'DriveInfo' is the legacy interface, but all the code base use it
> so it is confusing, I don't understand what is the correct interface to
> use.

I admit the "legacy" bit is still aspirational.  We still haven't
managed to replace it for configuring certain onboard devices.

The thing being configured is a device's BlockBackend.

To understand the point I'm trying to make, please ignore "legacy", and
focus on the actual purpose of DriveInfo: it's (one kind of) user
configuration for a BlockBackend.

Now let me try to state the problem you're trying to solve.  Instead of
special-casing "no backend" in device code like pflash_cfi0?.c do, you
want to make up a "dummy" backend instead.  You need the dummy to read
some blank value and ignore writes.  One of the null block drivers
should fit the bill.

Now my point.  Why first make up user configuration, then use that to
create a BlockBackend, when you could just go ahead and create the
BlockBackend?

Sadly, I'm not sufficiently familiar with the block API anymore to tell
you exactly how.  blk_new_with_bs() looks promising.  Perhaps Kevin can
advise.

>>> We should probably add a public helper for that.
>> 
>> If we decide we want to make up backends, then yes, we should do that in
>> a helper, not in each device.
>> 
>>> 'XXX' because NOR flashes erase content is when hardware bit
>>> is set, so it would be more useful to return -1/0xff... rather
>>> than zeroes.



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models
  2020-07-15  1:03             ` Havard Skinnemoen
@ 2020-07-15  9:35               ` Markus Armbruster
  0 siblings, 0 replies; 64+ messages in thread
From: Markus Armbruster @ 2020-07-15  9:35 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, Joel Stanley, QEMU Developers,
	Philippe Mathieu-Daudé,
	CS20 KFTing, qemu-arm, IS20 Avi Fishman, Cédric Le Goater

Havard Skinnemoen <hskinnemoen@google.com> writes:

> On Tue, Jul 14, 2020 at 10:11 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>
>> On 7/14/20 6:01 PM, Markus Armbruster wrote:
>> > Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
>> >
>> >> +Markus
>> >>
>> >> On 7/14/20 2:44 AM, Havard Skinnemoen wrote:
>> >>> On Mon, Jul 13, 2020 at 8:02 AM Cédric Le Goater <clg@kaod.org> wrote:
>> >>>>
>> >>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>> >>>>> The Nuvoton NPCM7xx SoC family are used to implement Baseboard
>> >>>>> Management Controllers in servers. While the family includes four SoCs,
>> >>>>> this patch implements limited support for two of them: NPCM730 (targeted
>> >>>>> for Data Center applications) and NPCM750 (targeted for Enterprise
>> >>>>> applications).
>> >>>>>
>> >>>>> This patch includes little more than the bare minimum needed to boot a
>> >>>>> Linux kernel built with NPCM7xx support in direct-kernel mode:
>> >>>>>
>> >>>>>   - Two Cortex-A9 CPU cores with built-in periperhals.
>> >>>>>   - Global Configuration Registers.
>> >>>>>   - Clock Management.
>> >>>>>   - 3 Timer Modules with 5 timers each.
>> >>>>>   - 4 serial ports.
>> >>>>>
>> >>>>> The chips themselves have a lot more features, some of which will be
>> >>>>> added to the model at a later stage.
>> >>>>>
>> >>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>> >>>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>> >>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>> >>>>> ---
>> >> ...
>> >>
>> >>>>> +static void npcm7xx_realize(DeviceState *dev, Error **errp)
>> >>>>> +{
>> >>>>> +    NPCM7xxState *s = NPCM7XX(dev);
>> >>>>> +    NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>> >>>>> +    int i;
>> >>>>> +
>> >>>>> +    /* CPUs */
>> >>>>> +    for (i = 0; i < nc->num_cpus; i++) {
>> >>>>> +        object_property_set_int(OBJECT(&s->cpu[i]),
>> >>>>> +                                arm_cpu_mp_affinity(i, NPCM7XX_MAX_NUM_CPUS),
>> >>>>> +                                "mp-affinity", &error_abort);
>> >>>>> +        object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>> >>>>> +                                "reset-cbar", &error_abort);
>> >>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), true,
>> >>>>> +                                 "reset-hivecs", &error_abort);
>> >>>>> +
>> >>>>> +        /* Disable security extensions. */
>> >>>>> +        object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>> >>>>> +                                 &error_abort);
>> >>>>> +
>> >>>>> +        qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>> >>>>
>> >>>> I would check the error:
>> >>>>
>> >>>>         if (!qdev_realize(DEVICE(&s->cpu[i]), NULL, errp)) {
>> >>>>             return;
>> >>>>         }
>> >>>>
>> >>>> same for the sysbus_realize() below.
>> >>>
>> >>> Hmm, I used to propagate these errors until Philippe told me not to
>> >>> (or at least that's how I understood it).
>> >>
>> >> It was before Markus simplification API were merged, you had to
>> >> propagate after each call, since this is a non hot-pluggable SoC
>> >> I suggested to use &error_abort to simplify.
>> >>
>> >>> I'll be happy to do it
>> >>> either way (and the new API makes it really easy to propagate errors),
>> >>> but I worry that I don't fully understand when to propagate errors and
>> >>> when not to.
>> >>
>> >> Markus explained it on the mailing list recently (as I found the doc
>> >> not obvious). I can't find the thread. I suppose once the work result
>> >> after the "Questionable aspects of QEMU Error's design" discussion is
>> >> merged, the documentation will be clarified.
>> >
>> > The Error API evolved recently.  Please peruse the big comment in
>> > include/qapi/error.h.  If still unsure, don't hesitate to ask here.
>> >
>> >> My rule of thumb so far is:
>> >> - programming error (can't happen) -> &error_abort
>> >
>> > Correct.  Quote the big comment:
>> >
>> >  * Call a function aborting on errors:
>> >  *     foo(arg, &error_abort);
>> >  * This is more concise and fails more nicely than
>> >  *     Error *err = NULL;
>> >  *     foo(arg, &err);
>> >  *     assert(!err); // don't do this
>> >
>> >> - everything triggerable by user or management layer (via QMP command)
>> >>   -> &error_fatal, as we can't risk loose the user data, we need to
>> >>   shutdown gracefully.
>> >
>> > Quote the big comment:
>> >
>> >  * Call a function treating errors as fatal:
>> >  *     foo(arg, &error_fatal);
>> >  * This is more concise than
>> >  *     Error *err = NULL;
>> >  *     foo(arg, &err);
>> >  *     if (err) { // don't do this
>> >  *         error_report_err(err);
>> >  *         exit(1);
>> >  *     }
>> >
>> > Terminating the process is generally fine during initial startup,
>> > i.e. before the guest runs.
>> >
>> > It's generally not fine once the guest runs.  Errors need to be handled
>> > more gracefully then.  A QMP command, for instance, should fail cleanly,
>> > propagating the error to the monitor core, which then sends it to the
>> > QMP client, and loops to process the next command.
>> >
>> >>> It makes sense to me to propagate errors from *_realize() and
>> >>> error_abort on failure to set simple properties, but I'd like to know
>> >>> if Philippe is on board with that.
>> >
>> > Realize methods must not use &error_fatal.  Instead, they should clean
>> > up and fail.
>> >
>> > "Clean up" is the part we often neglect.  The big advantage of
>> > &error_fatal is that you don't have to bother :)
>> >
>> > Questions?
>>
>> One on my side. So in this realize(), all &error_abort uses has
>> to be replaced by local_err + propagate ...:

Except for the ones where failure is a programming error.  For instance,
...

>> static void npcm7xx_realize(DeviceState *dev, Error **errp)
>> {
>>     NPCM7xxState *s = NPCM7XX(dev);
>>     NPCM7xxClass *nc = NPCM7XX_GET_CLASS(s);
>>     int i;
>>
>>     /* CPUs */
>>     for (i = 0; i < nc->num_cpus; i++) {
>>         object_property_set_int(OBJECT(&s->cpu[i]),
>>                                 arm_cpu_mp_affinity(i,
>> NPCM7XX_MAX_NUM_CPUS),
>>                                 "mp-affinity", &error_abort);
>>         object_property_set_int(OBJECT(&s->cpu[i]), NPCM7XX_GIC_CPU_IF_ADDR,
>>                                 "reset-cbar", &error_abort);
>>         object_property_set_bool(OBJECT(&s->cpu[i]), true,
>>                                  "reset-hivecs", &error_abort);

... object_property_set_bool() can fail only when

* No property with that name exists (programming error)

* The property is read-only (programming error)

* Its ->set() method fails

  The method is actually set_bool(), which fails only when

  - the device is already realized (programming errro)
  - visit_type_bool() fails (programming error)

Now, you may prefer not to know all that here, and instead propagate the
error.  I have two issues with that: it clutters the code, and the
impossible error path is untestable.

The common way to limit the clutter is of course skipping the cleanup ;)

You could also aim for the sour spot where the impossible error path is
wrong.  Extra points for making it subtly wrong, and tempting to copy to
a place where it's actually possible.

Bah, I'll take &error_abort, thank you very much.

>>
>>         /* Disable security extensions. */
>>         object_property_set_bool(OBJECT(&s->cpu[i]), false, "has_el3",
>>                                  &error_abort);
>>
>>         qdev_realize(DEVICE(&s->cpu[i]), NULL, &error_abort);
>>     }
>>     [...]
>>
>> ... but the caller does:
>>
>> static void quanta_gsj_init(MachineState *machine)
>> {
>>     NPCM7xxState *soc;
>>
>>     soc = npcm7xx_create_soc(machine, QUANTA_GSJ_POWER_ON_STRAPS);
>>     npcm7xx_connect_dram(soc, machine->ram);
>>     qdev_realize(DEVICE(soc), NULL, &error_abort);
>>                                     ^^^^^^^^^^^^
>>     npcm7xx_load_kernel(machine, soc);
>> }

quanta_gsj_init() states "realizing this device can't fail".

The realize method states "this step can't fail" for a number of steps.

What's wrong with that?

>>
>> So we overload the code...
>>
>> My question: Do you confirm this is worth it to propagate?
>
> Here's my understanding. Please let me know if it sounds right.
>
> 1. Internal code failing to set simple properties to predefined values
> is a programming error, so error_abort is appropriate.

That would be my advice.

> 2. qdev_realize() may fail due to user input, so errors should be propagated.

In general, yes.  For a specific device, you may know it can't fail, and
then &error_abort may be okay.

> 3. machine init can't propagate errors any further, so all errors are
> fatal.

Basically yes.

A machine init may also choose to recover from an error.  Say create an
optional device, and if it doesn't work, just omit it.  Just an example
for illustration; it feels like a bad idea to me.

>        But if all realize() functions follow (1) and (2), only user
> errors are propagated, so error_fatal should be used to produce a nice
> error message rather than "Unexpected error, aborting!"

Yes.

> If any of this can ever be hot-plugged, then it means errors may
> propagate somewhere other than the machine init code, so it becomes
> extra important not to let bad user input crash the whole qemu
> process. I don't know if this is a concern when none of these devices
> can currently be hot-plugged.

Many, many devices neglect to clean up properly on error, and get away
with it only because all callers treat errors as fatal.

If you decide to take cleanup shortcuts, say because the cleanup is
untestable, consider adding a comment at least.

> For example, if the user tries to create a machine with 64 MB RAM, the
> gcr device will report an error because it can't represent less than
> 128 MB of memory. Currently, this is reported as
>
> $ ./arm-softmmu/qemu-system-arm -machine npcm750-evb -nographic -m 64
> Unexpected error in npcm7xx_gcr_realize() at
> /usr/local/google/home/hskinnemoen/qemu/for-upstream/hw/misc/npcm7xx_gcr.c:151:
> qemu-system-arm: npcm7xx_gcr: DRAM size 67108864 is too small (128 MiB minimum)
> Aborted
>
> But if I change npcm7xx_realize() to propagate errors from
> sysbus_realize(gcr), and change npcm750_evb_init() to use error_fatal
> instead of error_abort, I get
>
> $ ./arm-softmmu/qemu-system-arm -machine npcm750-evb -nographic -m 64
> qemu-system-arm: npcm7xx_gcr: DRAM size 67108864 is too small (128 MiB minimum)
>
> which seems less scary and more accurate.

Looks like a bug fix to me :)



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-15  9:00               ` Markus Armbruster
@ 2020-07-15 10:57                 ` Philippe Mathieu-Daudé
  2020-07-15 20:54                   ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-15 10:57 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, Havard Skinnemoen,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

On 7/15/20 11:00 AM, Markus Armbruster wrote:
> Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
> 
>> On 7/14/20 6:21 PM, Markus Armbruster wrote:
>>> Philippe Mathieu-Daudé <f4bug@amsat.org> writes:
>>>
>>>> + qemu-block experts.
>>>>
>>>> On 7/14/20 11:16 AM, Markus Armbruster wrote:
>>>>> Havard Skinnemoen <hskinnemoen@google.com> writes:
>>>>>
>>>>>> On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
>>>>>>>
>>>>>>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>>>>>>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
>>>>>>>> one built with OpenBMC. For example like this:
>>>>>>>>
>>>>>>>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
>>>>>>>> qemu-system-arm -machine quanta-gsj -nographic \
>>>>>>>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
>>>>>>>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
>>>>>>>>
>>>>>>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>>>>>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>>>>>>
>>>>>>> May be we don't need to create the flash object if dinfo is NULL.
>>>>>>
>>>>>> It's soldered on the board, so you can't really boot the board without
>>>>>> it. But if you think it's better to remove it altogether if we don't
>>>>>> have an image to load into it, I can do that.
>>>>>
>>>>> If a device is a fixed part of the physical board, it should be a fixed
>>>>> part of the virtual board, too.
>>>>
>>>> We agree so far but ... how to do it?
>>>>
>>>> I never used this API, does that makes sense?
>>>>
>>>>     if (!dinfo) {
>>>>         QemuOpts *opts;
>>>>
>>>>         opts = qemu_opts_create(NULL, "spi-flash", 1, &error_abort);
>>>>         qdict_put_str(opts, "format", "null-co");
>>>>         qdict_put_int(opts, BLOCK_OPT_SIZE, 64 * MiB);
>>>>         qdict_put_bool(opts, NULL_OPT_ZEROES, false); // XXX
>>>>
>>>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>>>         qemu_opts_del(opts);
>>>>     }
>>>
>>> I believe existing code special-cases "no backend" instead of making one
>>> up.
>>>
>>> Example: pflash_cfi0?.c
>>>
>>> If ->blk is non-null, we read its contents into the memory buffer and
>>> write updates back, else we leave it blank and don't write updates back.
>>>
>>> Making one up could be more elegant.  To find out, you have to try.
>>
>> I'd rather avoid ad-hoc code in each device. I2C EEPROM do that too,
>> it is a source of head aches.
>>
>> >From the emulation PoV I'd prefer to always use a block backend,
>> regardless the user provide a drive.
>>
>>>
>>> We make up a few default drives (i.e. drives the user doesn't specify):
>>> floppy, CD-ROM and SD card.  Ancient part of the user interface, uses
>>> DriveInfo.  I doubt we should create more of them.
>>>
>>> I believe block backends we make up for internal use should stay away
>>> from DriveInfo.  Kevin, what do you think?  How would you make up a
>>> null-co block backend for a device's internal use?
>>
>> I read 'DriveInfo' is the legacy interface, but all the code base use it
>> so it is confusing, I don't understand what is the correct interface to
>> use.
> 
> I admit the "legacy" bit is still aspirational.  We still haven't
> managed to replace it for configuring certain onboard devices.
> 
> The thing being configured is a device's BlockBackend.
> 
> To understand the point I'm trying to make, please ignore "legacy", and
> focus on the actual purpose of DriveInfo: it's (one kind of) user
> configuration for a BlockBackend.
> 
> Now let me try to state the problem you're trying to solve.  Instead of
> special-casing "no backend" in device code like pflash_cfi0?.c do, you
> want to make up a "dummy" backend instead.  You need the dummy to read
> some blank value and ignore writes.  One of the null block drivers
> should fit the bill.
> 
> Now my point.  Why first make up user configuration, then use that to
> create a BlockBackend, when you could just go ahead and create the
> BlockBackend?

CLI issue mostly.

We can solve it similarly to the recent "sdcard: Do not allow invalid SD
card sizes" patch:

 if (!dinfo) {
     error_setg(errp, "Missing SPI flash drive");
     error_append_hint(errp, "You can use a dummy drive using:\n");
     error_append_hint(errp, "-drive if=mtd,driver=null-co,"
                             "read-ones=on,size=64M\n);
     return;
 }

having npcm7xx_connect_flash() taking an Error* argument,
and MachineClass::init() call it with &error_fatal.

> 
> Sadly, I'm not sufficiently familiar with the block API anymore to tell
> you exactly how.  blk_new_with_bs() looks promising.  Perhaps Kevin can
> advise.
> 
>>>> We should probably add a public helper for that.
>>>
>>> If we decide we want to make up backends, then yes, we should do that in
>>> a helper, not in each device.
>>>
>>>> 'XXX' because NOR flashes erase content is when hardware bit
>>>> is set, so it would be more useful to return -1/0xff... rather
>>>> than zeroes.


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-15 10:57                 ` Philippe Mathieu-Daudé
@ 2020-07-15 20:54                   ` Havard Skinnemoen
  2020-07-16 20:56                     ` Havard Skinnemoen
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-15 20:54 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Markus Armbruster, Kevin Wolf, Peter Maydell, Qemu-block,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/15/20 11:00 AM, Markus Armbruster wrote:
> > Now my point.  Why first make up user configuration, then use that to
> > create a BlockBackend, when you could just go ahead and create the
> > BlockBackend?
>
> CLI issue mostly.
>
> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
> card sizes" patch:
>
>  if (!dinfo) {
>      error_setg(errp, "Missing SPI flash drive");
>      error_append_hint(errp, "You can use a dummy drive using:\n");
>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>                              "read-ones=on,size=64M\n);
>      return;
>  }
>
> having npcm7xx_connect_flash() taking an Error* argument,
> and MachineClass::init() call it with &error_fatal.

Erroring out if the user specifies a configuration that can't possibly
boot sounds good to me. Better than trying to come up with defaults
that are still not going to result in a bootable system.

For testing recovery paths, I think it makes sense to explicitly
specify a null device as you suggest.

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-15  7:42       ` Cédric Le Goater
@ 2020-07-15 21:19         ` Havard Skinnemoen
  0 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-15 21:19 UTC (permalink / raw)
  To: Cédric Le Goater
  Cc: Peter Maydell, Philippe Mathieu-Daudé,
	qemu-arm, QEMU Developers, IS20 Avi Fishman, CS20 KFTing,
	Joel Stanley

On Wed, Jul 15, 2020 at 12:42 AM Cédric Le Goater <clg@kaod.org> wrote:
>
> On 7/14/20 4:56 AM, Havard Skinnemoen wrote:
> > On Mon, Jul 13, 2020 at 7:57 AM Cédric Le Goater <clg@kaod.org> wrote:
> >>
> >> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> >>> This allows these NPCM7xx-based boards to boot from a flash image, e.g.
> >>> one built with OpenBMC. For example like this:
> >>>
> >>> IMAGE=${OPENBMC}/build/tmp/deploy/images/gsj/image-bmc
> >>> qemu-system-arm -machine quanta-gsj -nographic \
> >>>       -bios ~/qemu/bootrom/npcm7xx_bootrom.bin \
> >>>       -drive file=${IMAGE},if=mtd,bus=0,unit=0,format=raw,snapshot=on
> >>>
> >>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> >>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> >>
> >> May be we don't need to create the flash object if dinfo is NULL.
> >
> > It's soldered on the board, so you can't really boot the board without
> > it. But if you think it's better to remove it altogether if we don't
> > have an image to load into it, I can do that.
> >
> >>
> >> Reviewed-by: Cédric Le Goater <clg@kaod.org>
> >> Tested-by: Cédric Le Goater <clg@kaod.org>
> >>
> >> Nice !
> >>
> >> We need a SPI controller model and a network device model now.
> >
> > Yeah, and i2c, PWM, GPIO, etc., but if you're referring to the kernel
> > crash, see below.
>
> We don't need all device models but fixing the crash would be better.
>
> >> npcm7xx_bootrom.bin is a bit of a pain. Could we include it in
> >> the QEMU roms ?
> >
> > Yeah, I was planning to include this in v6.
>
> Good. It will ease CI.
>
> >> spi_master spi0: /ahb/fiu@fb000000/spi-nor@0 has no valid 'spi-max-frequency' property (-22)
> >> spi_master spi0: Failed to create SPI device for /ahb/fiu@fb000000/spi-nor@0
> >
> > This is a device tree bug:
> >
> > https://github.com/hskinnemoen/openbmc/commit/99b172f88002f4fac939f85debe1187b9c569871
> >
> >> libphy: Fixed MDIO Bus: probed
> >> 8<--- cut here ---
> >> Unable to handle kernel paging request at virtual address fffffffe
> >
> > I believe this is a kernel bug:
> >
> > https://github.com/hskinnemoen/openbmc/commit/77e9f58ba157eabc976f15fa49892128fe2b2382
> >
> > I needed two additional patches to get all the way to the login prompt:
> >
> > https://github.com/hskinnemoen/openbmc/commits/20200711-gsj-qemu-0
>
>
> Great. So are these for mainline or Joel's openbmc branch ?

I believe they need to go to the openbmc kernel and/or the Nuvoton
vendor kernel. Mainline has none of the things these patches apply to
(gsj device tree and emac driver).

I'll try to send them out within the next day or two.

Thanks for testing!

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer device model
  2020-07-15  7:25   ` Philippe Mathieu-Daudé
@ 2020-07-15 23:04     ` Havard Skinnemoen
  2020-07-16  8:04       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-15 23:04 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Peter Maydell, qemu-arm, QEMU Developers, IS20 Avi Fishman,
	CS20 KFTing, Joel Stanley

On Wed, Jul 15, 2020 at 12:25 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
> > The NPCM730 and NPCM750 SoCs have three timer modules each holding five
> > timers and some shared registers (e.g. interrupt status).
> >
> > Each timer runs at 25 MHz divided by a prescaler, and counts down from a
> > configurable initial value to zero. When zero is reached, the interrupt
> > flag for the timer is set, and the timer is disabled (one-shot mode) or
> > reloaded from its initial value (periodic mode).
> >
> > This implementation is sufficient to boot a Linux kernel configured for
> > NPCM750. Note that the kernel does not seem to actually turn on the
> > interrupts.
> >
> > Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
> > Reviewed-by: Joel Stanley <joel@jms.id.au>
> > Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
> > ---
> >  include/hw/timer/npcm7xx_timer.h |  96 +++++++
> >  hw/timer/npcm7xx_timer.c         | 468 +++++++++++++++++++++++++++++++
> >  hw/timer/Makefile.objs           |   1 +
> >  hw/timer/trace-events            |   5 +
> >  4 files changed, 570 insertions(+)
> >  create mode 100644 include/hw/timer/npcm7xx_timer.h
> >  create mode 100644 hw/timer/npcm7xx_timer.c
> >
> ...
>
> > +/* The reference clock frequency is always 25 MHz. */
> > +#define NPCM7XX_TIMER_REF_HZ            (25000000)
> > +
> > +/* Return the value by which to divide the reference clock rate. */
> > +static uint32_t npcm7xx_timer_prescaler(const NPCM7xxTimer *t)
> > +{
> > +    return extract32(t->tcsr, NPCM7XX_TCSR_PRESCALE_START,
> > +                     NPCM7XX_TCSR_PRESCALE_LEN) + 1;
> > +}
> > +
> > +/* Convert a timer cycle count to a time interval in nanoseconds. */
> > +static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
> > +{
> > +    int64_t ns = count;
> > +
> > +    ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
> > +    ns *= npcm7xx_timer_prescaler(t);
> > +
> > +    return ns;
> > +}
> > +
> > +/* Convert a time interval in nanoseconds to a timer cycle count. */
> > +static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
> > +{
> > +    int64_t count;
> > +
> > +    count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
> > +    count /= npcm7xx_timer_prescaler(t);
> > +
> > +    return count;
> > +}
> > +
> > +/*
> > + * Raise the interrupt line if there's a pending interrupt and interrupts are
> > + * enabled for this timer. If not, lower it.
> > + */
> > +static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
> > +{
> > +    NPCM7xxTimerCtrlState *tc = t->ctrl;
> > +    /* Find the array index of this timer. */
> > +    int index = t - tc->timer;
>
> As you suggested in another device in this series, using a getter
> here is clearer.

Definitely.

> > +
> > +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
> > +
> > +    if ((t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index))) {
> > +        qemu_irq_raise(t->irq);
> > +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 1);
> > +    } else {
> > +        qemu_irq_lower(t->irq);
> > +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 0);
> > +    }
> > +}
> > +
> > +/* Start or resume the timer. */
> > +static void npcm7xx_timer_start(NPCM7xxTimer *t)
> > +{
> > +    int64_t now;
> > +
> > +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +    t->expires_ns = now + t->remaining_ns;
> > +    timer_mod(&t->qtimer, t->expires_ns);
> > +}
> > +
> > +/*
> > + * Called when the counter reaches zero. Sets the interrupt flag, and either
> > + * restarts or disables the timer.
> > + */
> > +static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
> > +{
> > +    NPCM7xxTimerCtrlState *tc = t->ctrl;
> > +    int index = t - tc->timer;
> > +
> > +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
> > +
> > +    tc->tisr |= BIT(index);
> > +
> > +    if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
> > +        t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
> > +        if (t->tcsr & NPCM7XX_TCSR_CEN) {
> > +            npcm7xx_timer_start(t);
> > +        }
> > +    } else {
> > +        t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
> > +    }
> > +
> > +    npcm7xx_timer_check_interrupt(t);
> > +}
> > +
> > +/* Stop counting. Record the time remaining so we can continue later. */
> > +static void npcm7xx_timer_pause(NPCM7xxTimer *t)
> > +{
> > +    int64_t now;
> > +
> > +    timer_del(&t->qtimer);
> > +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +    t->remaining_ns = t->expires_ns - now;
> > +    if (t->remaining_ns <= 0) {
>
> Can this happen? Shouldn't we get npcm7xx_timer_expired() before?

I was thinking the timer might expire right after calling timer_del(),
and handling it before we expire the timer makes bookkeeping easier.
But if QEMU_CLOCK_VIRTUAL is stopped while this code is running (even
on multi-cpu systems?), then I agree it can't happen.

If it can't possibly happen, then it should be appropriate to add

    g_assert(t->remaining_ns > 0);

right?

> > +        npcm7xx_timer_reached_zero(t);
> > +    }
> > +}
> > +
> > +/*
> > + * Restart the timer from its initial value. If the timer was enabled and stays
> > + * enabled, adjust the QEMU timer according to the new count. If the timer is
> > + * transitioning from disabled to enabled, the caller is expected to start the
> > + * timer later.
> > + */
> > +static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
> > +{
> > +    t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
> > +
> > +    if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
> > +        npcm7xx_timer_start(t);
> > +    }
> > +}
> > +
> > +/* Register read and write handlers */
> > +
> > +static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
> > +{
> > +    uint32_t old_tcsr = t->tcsr;
> > +
> > +    if (new_tcsr & NPCM7XX_TCSR_RSVD) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
> > +                      __func__, new_tcsr);
> > +        new_tcsr &= ~NPCM7XX_TCSR_RSVD;
> > +    }
> > +    if (new_tcsr & NPCM7XX_TCSR_CACT) {
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
> > +                      __func__, new_tcsr);
> > +        new_tcsr &= ~NPCM7XX_TCSR_CACT;
> > +    }
> > +
> > +    t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
> > +
> > +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
> > +        npcm7xx_timer_check_interrupt(t);
> > +    }
> > +    if (new_tcsr & NPCM7XX_TCSR_CRST) {
> > +        npcm7xx_timer_restart(t, old_tcsr);
> > +        t->tcsr &= ~NPCM7XX_TCSR_CRST;
> > +    }
> > +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
> > +        if (new_tcsr & NPCM7XX_TCSR_CEN) {
> > +            npcm7xx_timer_start(t);
> > +        } else {
> > +            npcm7xx_timer_pause(t);
> > +        }
> > +    }
> > +}
> > +
> > +static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
> > +{
> > +    t->ticr = new_ticr;
> > +
> > +    npcm7xx_timer_restart(t, t->tcsr);
> > +}
> > +
> > +static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
> > +{
> > +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
> > +        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> > +
> > +        return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
> > +    }
> > +
> > +    return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
> > +}
> > +
> > +static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
> > +{
> > +    NPCM7xxTimerCtrlState *s = opaque;
> > +    uint64_t value = 0;
> > +    hwaddr reg;
> > +
> > +    reg = offset / sizeof(uint32_t);
> > +    switch (reg) {
> > +    case NPCM7XX_TIMER_TCSR0:
> > +        value = s->timer[0].tcsr;
> > +        break;
> > +    case NPCM7XX_TIMER_TCSR1:
> > +        value = s->timer[1].tcsr;
>
> Maybe add:
>
> static hwaddr timer_index(hwaddr reg)
> {
>     return reg - NPCM7XX_TIMER_TCSR0;
> }
>
> And use shorter:
>
>     case NPCM7XX_TIMER_TCSR0:
>     case NPCM7XX_TIMER_TCSR1:
>     case NPCM7XX_TIMER_TCSR2:
>     case NPCM7XX_TIMER_TCSR3:
>     case NPCM7XX_TIMER_TCSR4:
>         value = s->timer[timer_index(reg)].tcsr;
>         break;
>
> Similarly with NPCM7XX_TIMER_TDRx and in npcm7xx_timer_write().

Sorry, that won't work because the registers for the various modules
are not grouped together.

> > +        break;
> > +    case NPCM7XX_TIMER_TCSR2:
> > +        value = s->timer[2].tcsr;
> > +        break;
> > +    case NPCM7XX_TIMER_TCSR3:
> > +        value = s->timer[3].tcsr;
> > +        break;
> > +    case NPCM7XX_TIMER_TCSR4:
> > +        value = s->timer[4].tcsr;
> > +        break;
> > +
> > +    case NPCM7XX_TIMER_TICR0:
> > +        value = s->timer[0].ticr;
> > +        break;
> > +    case NPCM7XX_TIMER_TICR1:
> > +        value = s->timer[1].ticr;
> > +        break;
> > +    case NPCM7XX_TIMER_TICR2:
> > +        value = s->timer[2].ticr;
> > +        break;
> > +    case NPCM7XX_TIMER_TICR3:
> > +        value = s->timer[3].ticr;
> > +        break;
> > +    case NPCM7XX_TIMER_TICR4:
> > +        value = s->timer[4].ticr;
> > +        break;
> > +
> > +    case NPCM7XX_TIMER_TDR0:
> > +        value = npcm7xx_timer_read_tdr(&s->timer[0]);
> > +        break;
> > +    case NPCM7XX_TIMER_TDR1:
> > +        value = npcm7xx_timer_read_tdr(&s->timer[1]);
> > +        break;
> > +    case NPCM7XX_TIMER_TDR2:
> > +        value = npcm7xx_timer_read_tdr(&s->timer[2]);
> > +        break;
> > +    case NPCM7XX_TIMER_TDR3:
> > +        value = npcm7xx_timer_read_tdr(&s->timer[3]);
> > +        break;
> > +    case NPCM7XX_TIMER_TDR4:
> > +        value = npcm7xx_timer_read_tdr(&s->timer[4]);
> > +        break;
> > +
> > +    case NPCM7XX_TIMER_TISR:
> > +        value = s->tisr;
> > +        break;
> > +
> > +    case NPCM7XX_TIMER_WTCR:
> > +        value = s->wtcr;
> > +        break;
> > +
> > +    default:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
> > +                      __func__, (unsigned int)offset);
> > +        break;
> > +    }
> > +
> > +    trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
> > +
> > +    return value;
> > +}
> > +
> > +static void npcm7xx_timer_write(void *opaque, hwaddr offset,
> > +                                uint64_t v, unsigned size)
> > +{
> > +    uint32_t reg = offset / sizeof(uint32_t);
> > +    NPCM7xxTimerCtrlState *s = opaque;
> > +    uint32_t value = v;
> > +
> > +    trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
> > +
> > +    switch (reg) {
> > +    case NPCM7XX_TIMER_TCSR0:
> > +        npcm7xx_timer_write_tcsr(&s->timer[0], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TCSR1:
> > +        npcm7xx_timer_write_tcsr(&s->timer[1], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TCSR2:
> > +        npcm7xx_timer_write_tcsr(&s->timer[2], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TCSR3:
> > +        npcm7xx_timer_write_tcsr(&s->timer[3], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TCSR4:
> > +        npcm7xx_timer_write_tcsr(&s->timer[4], value);
> > +        return;
> > +
> > +    case NPCM7XX_TIMER_TICR0:
> > +        npcm7xx_timer_write_ticr(&s->timer[0], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TICR1:
> > +        npcm7xx_timer_write_ticr(&s->timer[1], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TICR2:
> > +        npcm7xx_timer_write_ticr(&s->timer[2], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TICR3:
> > +        npcm7xx_timer_write_ticr(&s->timer[3], value);
> > +        return;
> > +    case NPCM7XX_TIMER_TICR4:
> > +        npcm7xx_timer_write_ticr(&s->timer[4], value);
> > +        return;
> > +
> > +    case NPCM7XX_TIMER_TDR0:
> > +    case NPCM7XX_TIMER_TDR1:
> > +    case NPCM7XX_TIMER_TDR2:
> > +    case NPCM7XX_TIMER_TDR3:
> > +    case NPCM7XX_TIMER_TDR4:
> > +        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
> > +                      __func__, (unsigned int)offset);
> > +        return;
> > +
> > +    case NPCM7XX_TIMER_TISR:
> > +        s->tisr &= ~value;
> > +        return;
> > +
> > +    case NPCM7XX_TIMER_WTCR:
> > +        qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
> > +                      __func__, value);
> > +        return;
> > +    }
> > +
> > +    qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
> > +                  __func__, (unsigned int)offset);
> > +}
> > +
> > +static const struct MemoryRegionOps npcm7xx_timer_ops = {
> > +    .read       = npcm7xx_timer_read,
> > +    .write      = npcm7xx_timer_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid      = {
> > +        .min_access_size        = 4,
> > +        .max_access_size        = 4,
> > +        .unaligned              = false,
> > +    },
> > +};
> > +
> > +/* Called when the QEMU timer expires. */
> > +static void npcm7xx_timer_expired(void *opaque)
> > +{
> > +    NPCM7xxTimer *t = opaque;
> > +
> > +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
> > +        npcm7xx_timer_reached_zero(t);
> > +    }
> > +}
> ...


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer device model
  2020-07-15 23:04     ` Havard Skinnemoen
@ 2020-07-16  8:04       ` Philippe Mathieu-Daudé
  0 siblings, 0 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-16  8:04 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Peter Maydell, QEMU Developers, CS20 KFTing, qemu-arm,
	Joel Stanley, IS20 Avi Fishman

On 7/16/20 1:04 AM, Havard Skinnemoen wrote:
> On Wed, Jul 15, 2020 at 12:25 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>> On 7/9/20 2:36 AM, Havard Skinnemoen wrote:
>>> The NPCM730 and NPCM750 SoCs have three timer modules each holding five
>>> timers and some shared registers (e.g. interrupt status).
>>>
>>> Each timer runs at 25 MHz divided by a prescaler, and counts down from a
>>> configurable initial value to zero. When zero is reached, the interrupt
>>> flag for the timer is set, and the timer is disabled (one-shot mode) or
>>> reloaded from its initial value (periodic mode).
>>>
>>> This implementation is sufficient to boot a Linux kernel configured for
>>> NPCM750. Note that the kernel does not seem to actually turn on the
>>> interrupts.
>>>
>>> Reviewed-by: Tyrone Ting <kfting@nuvoton.com>
>>> Reviewed-by: Joel Stanley <joel@jms.id.au>
>>> Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com>
>>> ---
>>>  include/hw/timer/npcm7xx_timer.h |  96 +++++++
>>>  hw/timer/npcm7xx_timer.c         | 468 +++++++++++++++++++++++++++++++
>>>  hw/timer/Makefile.objs           |   1 +
>>>  hw/timer/trace-events            |   5 +
>>>  4 files changed, 570 insertions(+)
>>>  create mode 100644 include/hw/timer/npcm7xx_timer.h
>>>  create mode 100644 hw/timer/npcm7xx_timer.c
>>>
>> ...
>>
>>> +/* The reference clock frequency is always 25 MHz. */
>>> +#define NPCM7XX_TIMER_REF_HZ            (25000000)
>>> +
>>> +/* Return the value by which to divide the reference clock rate. */
>>> +static uint32_t npcm7xx_timer_prescaler(const NPCM7xxTimer *t)
>>> +{
>>> +    return extract32(t->tcsr, NPCM7XX_TCSR_PRESCALE_START,
>>> +                     NPCM7XX_TCSR_PRESCALE_LEN) + 1;
>>> +}
>>> +
>>> +/* Convert a timer cycle count to a time interval in nanoseconds. */
>>> +static int64_t npcm7xx_timer_count_to_ns(NPCM7xxTimer *t, uint32_t count)
>>> +{
>>> +    int64_t ns = count;
>>> +
>>> +    ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
>>> +    ns *= npcm7xx_timer_prescaler(t);
>>> +
>>> +    return ns;
>>> +}
>>> +
>>> +/* Convert a time interval in nanoseconds to a timer cycle count. */
>>> +static uint32_t npcm7xx_timer_ns_to_count(NPCM7xxTimer *t, int64_t ns)
>>> +{
>>> +    int64_t count;
>>> +
>>> +    count = ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ);
>>> +    count /= npcm7xx_timer_prescaler(t);
>>> +
>>> +    return count;
>>> +}
>>> +
>>> +/*
>>> + * Raise the interrupt line if there's a pending interrupt and interrupts are
>>> + * enabled for this timer. If not, lower it.
>>> + */
>>> +static void npcm7xx_timer_check_interrupt(NPCM7xxTimer *t)
>>> +{
>>> +    NPCM7xxTimerCtrlState *tc = t->ctrl;
>>> +    /* Find the array index of this timer. */
>>> +    int index = t - tc->timer;
>>
>> As you suggested in another device in this series, using a getter
>> here is clearer.
> 
> Definitely.
> 
>>> +
>>> +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
>>> +
>>> +    if ((t->tcsr & NPCM7XX_TCSR_IE) && (tc->tisr & BIT(index))) {
>>> +        qemu_irq_raise(t->irq);
>>> +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 1);
>>> +    } else {
>>> +        qemu_irq_lower(t->irq);
>>> +        trace_npcm7xx_timer_irq(DEVICE(tc)->canonical_path, index, 0);
>>> +    }
>>> +}
>>> +
>>> +/* Start or resume the timer. */
>>> +static void npcm7xx_timer_start(NPCM7xxTimer *t)
>>> +{
>>> +    int64_t now;
>>> +
>>> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +    t->expires_ns = now + t->remaining_ns;
>>> +    timer_mod(&t->qtimer, t->expires_ns);
>>> +}
>>> +
>>> +/*
>>> + * Called when the counter reaches zero. Sets the interrupt flag, and either
>>> + * restarts or disables the timer.
>>> + */
>>> +static void npcm7xx_timer_reached_zero(NPCM7xxTimer *t)
>>> +{
>>> +    NPCM7xxTimerCtrlState *tc = t->ctrl;
>>> +    int index = t - tc->timer;
>>> +
>>> +    g_assert(index >= 0 && index < NPCM7XX_TIMERS_PER_CTRL);
>>> +
>>> +    tc->tisr |= BIT(index);
>>> +
>>> +    if (t->tcsr & NPCM7XX_TCSR_PERIODIC) {
>>> +        t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
>>> +        if (t->tcsr & NPCM7XX_TCSR_CEN) {
>>> +            npcm7xx_timer_start(t);
>>> +        }
>>> +    } else {
>>> +        t->tcsr &= ~(NPCM7XX_TCSR_CEN | NPCM7XX_TCSR_CACT);
>>> +    }
>>> +
>>> +    npcm7xx_timer_check_interrupt(t);
>>> +}
>>> +
>>> +/* Stop counting. Record the time remaining so we can continue later. */
>>> +static void npcm7xx_timer_pause(NPCM7xxTimer *t)
>>> +{
>>> +    int64_t now;
>>> +
>>> +    timer_del(&t->qtimer);
>>> +    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +    t->remaining_ns = t->expires_ns - now;
>>> +    if (t->remaining_ns <= 0) {
>>
>> Can this happen? Shouldn't we get npcm7xx_timer_expired() before?
> 
> I was thinking the timer might expire right after calling timer_del(),
> and handling it before we expire the timer makes bookkeeping easier.
> But if QEMU_CLOCK_VIRTUAL is stopped while this code is running (even
> on multi-cpu systems?), then I agree it can't happen.
> 
> If it can't possibly happen, then it should be appropriate to add
> 
>     g_assert(t->remaining_ns > 0);
> 
> right?

This is my understanding, but I'd rather see someone more familiar
with timers confirm.

> 
>>> +        npcm7xx_timer_reached_zero(t);
>>> +    }
>>> +}
>>> +
>>> +/*
>>> + * Restart the timer from its initial value. If the timer was enabled and stays
>>> + * enabled, adjust the QEMU timer according to the new count. If the timer is
>>> + * transitioning from disabled to enabled, the caller is expected to start the
>>> + * timer later.
>>> + */
>>> +static void npcm7xx_timer_restart(NPCM7xxTimer *t, uint32_t old_tcsr)
>>> +{
>>> +    t->remaining_ns = npcm7xx_timer_count_to_ns(t, t->ticr);
>>> +
>>> +    if (old_tcsr & t->tcsr & NPCM7XX_TCSR_CEN) {
>>> +        npcm7xx_timer_start(t);
>>> +    }
>>> +}
>>> +
>>> +/* Register read and write handlers */
>>> +
>>> +static void npcm7xx_timer_write_tcsr(NPCM7xxTimer *t, uint32_t new_tcsr)
>>> +{
>>> +    uint32_t old_tcsr = t->tcsr;
>>> +
>>> +    if (new_tcsr & NPCM7XX_TCSR_RSVD) {
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits in 0x%08x ignored\n",
>>> +                      __func__, new_tcsr);
>>> +        new_tcsr &= ~NPCM7XX_TCSR_RSVD;
>>> +    }
>>> +    if (new_tcsr & NPCM7XX_TCSR_CACT) {
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: read-only bits in 0x%08x ignored\n",
>>> +                      __func__, new_tcsr);
>>> +        new_tcsr &= ~NPCM7XX_TCSR_CACT;
>>> +    }
>>> +
>>> +    t->tcsr = (t->tcsr & NPCM7XX_TCSR_CACT) | new_tcsr;
>>> +
>>> +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_IE) {
>>> +        npcm7xx_timer_check_interrupt(t);
>>> +    }
>>> +    if (new_tcsr & NPCM7XX_TCSR_CRST) {
>>> +        npcm7xx_timer_restart(t, old_tcsr);
>>> +        t->tcsr &= ~NPCM7XX_TCSR_CRST;
>>> +    }
>>> +    if ((old_tcsr ^ new_tcsr) & NPCM7XX_TCSR_CEN) {
>>> +        if (new_tcsr & NPCM7XX_TCSR_CEN) {
>>> +            npcm7xx_timer_start(t);
>>> +        } else {
>>> +            npcm7xx_timer_pause(t);
>>> +        }
>>> +    }
>>> +}
>>> +
>>> +static void npcm7xx_timer_write_ticr(NPCM7xxTimer *t, uint32_t new_ticr)
>>> +{
>>> +    t->ticr = new_ticr;
>>> +
>>> +    npcm7xx_timer_restart(t, t->tcsr);
>>> +}
>>> +
>>> +static uint32_t npcm7xx_timer_read_tdr(NPCM7xxTimer *t)
>>> +{
>>> +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
>>> +        int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
>>> +
>>> +        return npcm7xx_timer_ns_to_count(t, t->expires_ns - now);
>>> +    }
>>> +
>>> +    return npcm7xx_timer_ns_to_count(t, t->remaining_ns);
>>> +}
>>> +
>>> +static uint64_t npcm7xx_timer_read(void *opaque, hwaddr offset, unsigned size)
>>> +{
>>> +    NPCM7xxTimerCtrlState *s = opaque;
>>> +    uint64_t value = 0;
>>> +    hwaddr reg;
>>> +
>>> +    reg = offset / sizeof(uint32_t);
>>> +    switch (reg) {
>>> +    case NPCM7XX_TIMER_TCSR0:
>>> +        value = s->timer[0].tcsr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TCSR1:
>>> +        value = s->timer[1].tcsr;
>>
>> Maybe add:
>>
>> static hwaddr timer_index(hwaddr reg)
>> {
>>     return reg - NPCM7XX_TIMER_TCSR0;
>> }
>>
>> And use shorter:
>>
>>     case NPCM7XX_TIMER_TCSR0:
>>     case NPCM7XX_TIMER_TCSR1:
>>     case NPCM7XX_TIMER_TCSR2:
>>     case NPCM7XX_TIMER_TCSR3:
>>     case NPCM7XX_TIMER_TCSR4:
>>         value = s->timer[timer_index(reg)].tcsr;
>>         break;
>>
>> Similarly with NPCM7XX_TIMER_TDRx and in npcm7xx_timer_write().
> 
> Sorry, that won't work because the registers for the various modules
> are not grouped together.

So what about:

static hwaddr timer_index(hwaddr reg)
{
    switch (reg) {
    case NPCM7XX_TIMER_TCSR0:
        return 0;
    case NPCM7XX_TIMER_TCSR1:
        return 1;
    case NPCM7XX_TIMER_TCSR2:
        return 2;
    case NPCM7XX_TIMER_TCSR3:
        return 3;
    case NPCM7XX_TIMER_TCSR4:
        return 4;
    default:
        g_assert_not_reached();
    }
}

This simplifies the read/write handlers.

> 
>>> +        break;
>>> +    case NPCM7XX_TIMER_TCSR2:
>>> +        value = s->timer[2].tcsr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TCSR3:
>>> +        value = s->timer[3].tcsr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TCSR4:
>>> +        value = s->timer[4].tcsr;
>>> +        break;
>>> +
>>> +    case NPCM7XX_TIMER_TICR0:
>>> +        value = s->timer[0].ticr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TICR1:
>>> +        value = s->timer[1].ticr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TICR2:
>>> +        value = s->timer[2].ticr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TICR3:
>>> +        value = s->timer[3].ticr;
>>> +        break;
>>> +    case NPCM7XX_TIMER_TICR4:
>>> +        value = s->timer[4].ticr;
>>> +        break;
>>> +
>>> +    case NPCM7XX_TIMER_TDR0:
>>> +        value = npcm7xx_timer_read_tdr(&s->timer[0]);
>>> +        break;
>>> +    case NPCM7XX_TIMER_TDR1:
>>> +        value = npcm7xx_timer_read_tdr(&s->timer[1]);
>>> +        break;
>>> +    case NPCM7XX_TIMER_TDR2:
>>> +        value = npcm7xx_timer_read_tdr(&s->timer[2]);
>>> +        break;
>>> +    case NPCM7XX_TIMER_TDR3:
>>> +        value = npcm7xx_timer_read_tdr(&s->timer[3]);
>>> +        break;
>>> +    case NPCM7XX_TIMER_TDR4:
>>> +        value = npcm7xx_timer_read_tdr(&s->timer[4]);
>>> +        break;
>>> +
>>> +    case NPCM7XX_TIMER_TISR:
>>> +        value = s->tisr;
>>> +        break;
>>> +
>>> +    case NPCM7XX_TIMER_WTCR:
>>> +        value = s->wtcr;
>>> +        break;
>>> +
>>> +    default:
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
>>> +                      __func__, (unsigned int)offset);
>>> +        break;
>>> +    }
>>> +
>>> +    trace_npcm7xx_timer_read(DEVICE(s)->canonical_path, offset, value);
>>> +
>>> +    return value;
>>> +}
>>> +
>>> +static void npcm7xx_timer_write(void *opaque, hwaddr offset,
>>> +                                uint64_t v, unsigned size)
>>> +{
>>> +    uint32_t reg = offset / sizeof(uint32_t);
>>> +    NPCM7xxTimerCtrlState *s = opaque;
>>> +    uint32_t value = v;
>>> +
>>> +    trace_npcm7xx_timer_write(DEVICE(s)->canonical_path, offset, value);
>>> +
>>> +    switch (reg) {
>>> +    case NPCM7XX_TIMER_TCSR0:
>>> +        npcm7xx_timer_write_tcsr(&s->timer[0], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TCSR1:
>>> +        npcm7xx_timer_write_tcsr(&s->timer[1], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TCSR2:
>>> +        npcm7xx_timer_write_tcsr(&s->timer[2], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TCSR3:
>>> +        npcm7xx_timer_write_tcsr(&s->timer[3], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TCSR4:
>>> +        npcm7xx_timer_write_tcsr(&s->timer[4], value);
>>> +        return;
>>> +
>>> +    case NPCM7XX_TIMER_TICR0:
>>> +        npcm7xx_timer_write_ticr(&s->timer[0], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TICR1:
>>> +        npcm7xx_timer_write_ticr(&s->timer[1], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TICR2:
>>> +        npcm7xx_timer_write_ticr(&s->timer[2], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TICR3:
>>> +        npcm7xx_timer_write_ticr(&s->timer[3], value);
>>> +        return;
>>> +    case NPCM7XX_TIMER_TICR4:
>>> +        npcm7xx_timer_write_ticr(&s->timer[4], value);
>>> +        return;
>>> +
>>> +    case NPCM7XX_TIMER_TDR0:
>>> +    case NPCM7XX_TIMER_TDR1:
>>> +    case NPCM7XX_TIMER_TDR2:
>>> +    case NPCM7XX_TIMER_TDR3:
>>> +    case NPCM7XX_TIMER_TDR4:
>>> +        qemu_log_mask(LOG_GUEST_ERROR, "%s: register @ 0x%04x is read-only\n",
>>> +                      __func__, (unsigned int)offset);
>>> +        return;
>>> +
>>> +    case NPCM7XX_TIMER_TISR:
>>> +        s->tisr &= ~value;
>>> +        return;
>>> +
>>> +    case NPCM7XX_TIMER_WTCR:
>>> +        qemu_log_mask(LOG_UNIMP, "%s: WTCR write not implemented: 0x%08x\n",
>>> +                      __func__, value);
>>> +        return;
>>> +    }
>>> +
>>> +    qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid offset 0x%04x\n",
>>> +                  __func__, (unsigned int)offset);
>>> +}
>>> +
>>> +static const struct MemoryRegionOps npcm7xx_timer_ops = {
>>> +    .read       = npcm7xx_timer_read,
>>> +    .write      = npcm7xx_timer_write,
>>> +    .endianness = DEVICE_LITTLE_ENDIAN,
>>> +    .valid      = {
>>> +        .min_access_size        = 4,
>>> +        .max_access_size        = 4,
>>> +        .unaligned              = false,
>>> +    },
>>> +};
>>> +
>>> +/* Called when the QEMU timer expires. */
>>> +static void npcm7xx_timer_expired(void *opaque)
>>> +{
>>> +    NPCM7xxTimer *t = opaque;
>>> +
>>> +    if (t->tcsr & NPCM7XX_TCSR_CEN) {
>>> +        npcm7xx_timer_reached_zero(t);
>>> +    }
>>> +}
>> ...
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-15 20:54                   ` Havard Skinnemoen
@ 2020-07-16 20:56                     ` Havard Skinnemoen
  2020-07-17  7:48                       ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-16 20:56 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Markus Armbruster, Kevin Wolf, Peter Maydell, Qemu-block,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
<hskinnemoen@google.com> wrote:
>
> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >
> > On 7/15/20 11:00 AM, Markus Armbruster wrote:
> > > Now my point.  Why first make up user configuration, then use that to
> > > create a BlockBackend, when you could just go ahead and create the
> > > BlockBackend?
> >
> > CLI issue mostly.
> >
> > We can solve it similarly to the recent "sdcard: Do not allow invalid SD
> > card sizes" patch:
> >
> >  if (!dinfo) {
> >      error_setg(errp, "Missing SPI flash drive");
> >      error_append_hint(errp, "You can use a dummy drive using:\n");
> >      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
> >                              "read-ones=on,size=64M\n);
> >      return;
> >  }
> >
> > having npcm7xx_connect_flash() taking an Error* argument,
> > and MachineClass::init() call it with &error_fatal.
>
> Erroring out if the user specifies a configuration that can't possibly
> boot sounds good to me. Better than trying to come up with defaults
> that are still not going to result in a bootable system.
>
> For testing recovery paths, I think it makes sense to explicitly
> specify a null device as you suggest.

Hmm, one problem. qom-test fails with

qemu-system-aarch64: Missing SPI flash drive
You can add a dummy drive using:
-drive if=mtd,driver=null-co,read-zeroes=on,size=32M
Broken pipe
/usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
kill_qemu() tried to terminate QEMU process but encountered exit
status 1 (expected 0)
ERROR qom-test - too few tests run (expected 68, got 7)

So it looks like we might need a different solution to this, unless we
want to make generic tests more machine-aware...


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-16 20:56                     ` Havard Skinnemoen
@ 2020-07-17  7:48                       ` Philippe Mathieu-Daudé
  2020-07-17  8:03                         ` Thomas Huth
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-17  7:48 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Kevin Wolf, Peter Maydell, Thomas Huth, Qemu-block,
	Markus Armbruster, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

+Thomas

On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
> <hskinnemoen@google.com> wrote:
>>
>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>
>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>> Now my point.  Why first make up user configuration, then use that to
>>>> create a BlockBackend, when you could just go ahead and create the
>>>> BlockBackend?
>>>
>>> CLI issue mostly.
>>>
>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>> card sizes" patch:
>>>
>>>  if (!dinfo) {
>>>      error_setg(errp, "Missing SPI flash drive");
>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>                              "read-ones=on,size=64M\n);
>>>      return;
>>>  }
>>>
>>> having npcm7xx_connect_flash() taking an Error* argument,
>>> and MachineClass::init() call it with &error_fatal.
>>
>> Erroring out if the user specifies a configuration that can't possibly
>> boot sounds good to me. Better than trying to come up with defaults
>> that are still not going to result in a bootable system.
>>
>> For testing recovery paths, I think it makes sense to explicitly
>> specify a null device as you suggest.
> 
> Hmm, one problem. qom-test fails with
> 
> qemu-system-aarch64: Missing SPI flash drive
> You can add a dummy drive using:
> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
> Broken pipe
> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
> kill_qemu() tried to terminate QEMU process but encountered exit
> status 1 (expected 0)
> ERROR qom-test - too few tests run (expected 68, got 7)
> 
> So it looks like we might need a different solution to this, unless we
> want to make generic tests more machine-aware...
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17  7:48                       ` Philippe Mathieu-Daudé
@ 2020-07-17  8:03                         ` Thomas Huth
  2020-07-17  8:27                           ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Thomas Huth @ 2020-07-17  8:03 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé, Havard Skinnemoen
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, Markus Armbruster,
	QEMU Developers, CS20 KFTing, qemu-arm, Cédric Le Goater,
	IS20 Avi Fishman

On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
> +Thomas

> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>> <hskinnemoen@google.com> wrote:
>>>
>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>
>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>> BlockBackend?
>>>>
>>>> CLI issue mostly.
>>>>
>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>> card sizes" patch:
>>>>
>>>>  if (!dinfo) {
>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>                              "read-ones=on,size=64M\n);
>>>>      return;
>>>>  }
>>>>
>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>> and MachineClass::init() call it with &error_fatal.
>>>
>>> Erroring out if the user specifies a configuration that can't possibly
>>> boot sounds good to me. Better than trying to come up with defaults
>>> that are still not going to result in a bootable system.
>>>
>>> For testing recovery paths, I think it makes sense to explicitly
>>> specify a null device as you suggest.
>>
>> Hmm, one problem. qom-test fails with
>>
>> qemu-system-aarch64: Missing SPI flash drive
>> You can add a dummy drive using:
>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>> Broken pipe
>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>> kill_qemu() tried to terminate QEMU process but encountered exit
>> status 1 (expected 0)
>> ERROR qom-test - too few tests run (expected 68, got 7)
>>
>> So it looks like we might need a different solution to this, unless we
>> want to make generic tests more machine-aware...

I didn't follow the other mails in this thread, but what we usually do
in such a case: Add a "if (qtest_enabled())" check to the device or the
machine to ignore the error if it is running in qtest mode.

 Thomas



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17  8:03                         ` Thomas Huth
@ 2020-07-17  8:27                           ` Philippe Mathieu-Daudé
  2020-07-17  9:00                             ` Philippe Mathieu-Daudé
  0 siblings, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-17  8:27 UTC (permalink / raw)
  To: Thomas Huth, Havard Skinnemoen, Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, QEMU Developers,
	CS20 KFTing, qemu-arm, Cédric Le Goater, IS20 Avi Fishman

On 7/17/20 10:03 AM, Thomas Huth wrote:
> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
>> +Thomas
> 
>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>>> <hskinnemoen@google.com> wrote:
>>>>
>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>
>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>>> BlockBackend?
>>>>>
>>>>> CLI issue mostly.
>>>>>
>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>>> card sizes" patch:
>>>>>
>>>>>  if (!dinfo) {
>>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>>                              "read-ones=on,size=64M\n);
>>>>>      return;
>>>>>  }
>>>>>
>>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>>> and MachineClass::init() call it with &error_fatal.
>>>>
>>>> Erroring out if the user specifies a configuration that can't possibly
>>>> boot sounds good to me. Better than trying to come up with defaults
>>>> that are still not going to result in a bootable system.
>>>>
>>>> For testing recovery paths, I think it makes sense to explicitly
>>>> specify a null device as you suggest.
>>>
>>> Hmm, one problem. qom-test fails with
>>>
>>> qemu-system-aarch64: Missing SPI flash drive
>>> You can add a dummy drive using:
>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>>> Broken pipe
>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>>> kill_qemu() tried to terminate QEMU process but encountered exit
>>> status 1 (expected 0)
>>> ERROR qom-test - too few tests run (expected 68, got 7)
>>>
>>> So it looks like we might need a different solution to this, unless we
>>> want to make generic tests more machine-aware...
> 
> I didn't follow the other mails in this thread, but what we usually do
> in such a case: Add a "if (qtest_enabled())" check to the device or the
> machine to ignore the error if it is running in qtest mode.

Hmm I'm not sure it works in this case. We could do:

  if (!dinfo) {
     if (qtest) {
        /* create null drive for qtest */
        opts = ...;
        dinfo = drive_new(opts, IF_MTD, &error_abort);
     } else {
        /* teach user to use proper CLI */
        error_setg(errp, "Missing SPI flash drive");
        error_append_hint(errp, "You can use a dummy drive using:\n");
        error_append_hint(errp, "-drive if=mtd,driver=null-co,"
                                "read-ones=on,size=64M\n);
     }
  }

But I'm not sure Markus will enjoy it :)

Markus, any better idea about how to handle that with automatic qtests?

Thanks :)

Phil.

> 
>  Thomas
> 
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17  8:27                           ` Philippe Mathieu-Daudé
@ 2020-07-17  9:00                             ` Philippe Mathieu-Daudé
  2020-07-17 19:18                               ` Havard Skinnemoen
  2020-07-20  7:58                               ` Markus Armbruster
  0 siblings, 2 replies; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-17  9:00 UTC (permalink / raw)
  To: Thomas Huth, Havard Skinnemoen, Markus Armbruster
  Cc: Kevin Wolf, Peter Maydell, Qemu-block, QEMU Developers,
	CS20 KFTing, qemu-arm, Cédric Le Goater, IS20 Avi Fishman

On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
> On 7/17/20 10:03 AM, Thomas Huth wrote:
>> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
>>> +Thomas
>>
>>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>>>> <hskinnemoen@google.com> wrote:
>>>>>
>>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>>
>>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>>>> BlockBackend?
>>>>>>
>>>>>> CLI issue mostly.
>>>>>>
>>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>>>> card sizes" patch:
>>>>>>
>>>>>>  if (!dinfo) {
>>>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>>>                              "read-ones=on,size=64M\n);
>>>>>>      return;
>>>>>>  }
>>>>>>
>>>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>>>> and MachineClass::init() call it with &error_fatal.
>>>>>
>>>>> Erroring out if the user specifies a configuration that can't possibly
>>>>> boot sounds good to me. Better than trying to come up with defaults
>>>>> that are still not going to result in a bootable system.
>>>>>
>>>>> For testing recovery paths, I think it makes sense to explicitly
>>>>> specify a null device as you suggest.
>>>>
>>>> Hmm, one problem. qom-test fails with
>>>>
>>>> qemu-system-aarch64: Missing SPI flash drive
>>>> You can add a dummy drive using:
>>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>>>> Broken pipe
>>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>>>> kill_qemu() tried to terminate QEMU process but encountered exit
>>>> status 1 (expected 0)
>>>> ERROR qom-test - too few tests run (expected 68, got 7)
>>>>
>>>> So it looks like we might need a different solution to this, unless we
>>>> want to make generic tests more machine-aware...
>>
>> I didn't follow the other mails in this thread, but what we usually do
>> in such a case: Add a "if (qtest_enabled())" check to the device or the
>> machine to ignore the error if it is running in qtest mode.
> 
> Hmm I'm not sure it works in this case. We could do:
> 
>   if (!dinfo) {
>      if (qtest) {
>         /* create null drive for qtest */
>         opts = ...;
>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>      } else {
>         /* teach user to use proper CLI */
>         error_setg(errp, "Missing SPI flash drive");
>         error_append_hint(errp, "You can use a dummy drive using:\n");
>         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>                                 "read-ones=on,size=64M\n);
>      }
>   }
> 
> But I'm not sure Markus will enjoy it :)
> 
> Markus, any better idea about how to handle that with automatic qtests?

FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
drive":

static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
{
    IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
    IDEState *s = bus->ifs + dev->unit;
    int ret;

    if (!dev->conf.blk) {
        if (kind != IDE_CD) {
            error_setg(errp, "No drive specified");
            return;
        } else {
            /* Anonymous BlockBackend for an empty drive */
            dev->conf.blk = blk_new(qemu_get_aio_context(), 0,
BLK_PERM_ALL);
            ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
            assert(ret == 0);
        }
    }


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17  9:00                             ` Philippe Mathieu-Daudé
@ 2020-07-17 19:18                               ` Havard Skinnemoen
  2020-07-17 20:21                                 ` Cédric Le Goater
  2020-07-17 20:52                                 ` Philippe Mathieu-Daudé
  2020-07-20  7:58                               ` Markus Armbruster
  1 sibling, 2 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-17 19:18 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Thomas Huth, Markus Armbruster, Kevin Wolf, Peter Maydell,
	Qemu-block, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

On Fri, Jul 17, 2020 at 2:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
> > On 7/17/20 10:03 AM, Thomas Huth wrote:
> >> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
> >>> +Thomas
> >>
> >>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
> >>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
> >>>> <hskinnemoen@google.com> wrote:
> >>>>>
> >>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>>>>
> >>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
> >>>>>>> Now my point.  Why first make up user configuration, then use that to
> >>>>>>> create a BlockBackend, when you could just go ahead and create the
> >>>>>>> BlockBackend?
> >>>>>>
> >>>>>> CLI issue mostly.
> >>>>>>
> >>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
> >>>>>> card sizes" patch:
> >>>>>>
> >>>>>>  if (!dinfo) {
> >>>>>>      error_setg(errp, "Missing SPI flash drive");
> >>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
> >>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
> >>>>>>                              "read-ones=on,size=64M\n);
> >>>>>>      return;
> >>>>>>  }
> >>>>>>
> >>>>>> having npcm7xx_connect_flash() taking an Error* argument,
> >>>>>> and MachineClass::init() call it with &error_fatal.
> >>>>>
> >>>>> Erroring out if the user specifies a configuration that can't possibly
> >>>>> boot sounds good to me. Better than trying to come up with defaults
> >>>>> that are still not going to result in a bootable system.
> >>>>>
> >>>>> For testing recovery paths, I think it makes sense to explicitly
> >>>>> specify a null device as you suggest.
> >>>>
> >>>> Hmm, one problem. qom-test fails with
> >>>>
> >>>> qemu-system-aarch64: Missing SPI flash drive
> >>>> You can add a dummy drive using:
> >>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
> >>>> Broken pipe
> >>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
> >>>> kill_qemu() tried to terminate QEMU process but encountered exit
> >>>> status 1 (expected 0)
> >>>> ERROR qom-test - too few tests run (expected 68, got 7)
> >>>>
> >>>> So it looks like we might need a different solution to this, unless we
> >>>> want to make generic tests more machine-aware...
> >>
> >> I didn't follow the other mails in this thread, but what we usually do
> >> in such a case: Add a "if (qtest_enabled())" check to the device or the
> >> machine to ignore the error if it is running in qtest mode.
> >
> > Hmm I'm not sure it works in this case. We could do:
> >
> >   if (!dinfo) {
> >      if (qtest) {
> >         /* create null drive for qtest */
> >         opts = ...;
> >         dinfo = drive_new(opts, IF_MTD, &error_abort);
> >      } else {
> >         /* teach user to use proper CLI */
> >         error_setg(errp, "Missing SPI flash drive");
> >         error_append_hint(errp, "You can use a dummy drive using:\n");
> >         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
> >                                 "read-ones=on,size=64M\n);
> >      }
> >   }
> >
> > But I'm not sure Markus will enjoy it :)
> >
> > Markus, any better idea about how to handle that with automatic qtests?
>
> FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
> drive":
>
> static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
> {
>     IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
>     IDEState *s = bus->ifs + dev->unit;
>     int ret;
>
>     if (!dev->conf.blk) {
>         if (kind != IDE_CD) {
>             error_setg(errp, "No drive specified");
>             return;
>         } else {
>             /* Anonymous BlockBackend for an empty drive */
>             dev->conf.blk = blk_new(qemu_get_aio_context(), 0,
> BLK_PERM_ALL);
>             ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
>             assert(ret == 0);
>         }
>     }

Could someone please remind me what problem we're trying to solve here?

Currently, if the user (or test) doesn't provide a drive, we pass NULL
as the block backend to m25p80. This means we'll take the code path in
m25p_realize() that does

        trace_m25p80_binding_no_bdrv(s);
        s->storage = blk_blockalign(NULL, s->size);
        memset(s->storage, 0xFF, s->size);

which will look like a freshly chip-erased flash chip.

Are we looking for a more elegant way to replace those three lines of
code (+ a couple of conditionals in the writeback paths)?

But we don't even have a dummy device that looks like an erased flash chip...

Havard


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17 19:18                               ` Havard Skinnemoen
@ 2020-07-17 20:21                                 ` Cédric Le Goater
  2020-07-17 20:52                                 ` Philippe Mathieu-Daudé
  1 sibling, 0 replies; 64+ messages in thread
From: Cédric Le Goater @ 2020-07-17 20:21 UTC (permalink / raw)
  To: Havard Skinnemoen, Philippe Mathieu-Daudé
  Cc: Kevin Wolf, Peter Maydell, Thomas Huth, Qemu-block,
	Markus Armbruster, QEMU Developers, CS20 KFTing, qemu-arm,
	IS20 Avi Fishman

On 7/17/20 9:18 PM, Havard Skinnemoen wrote:
> On Fri, Jul 17, 2020 at 2:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>
>> On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
>>> On 7/17/20 10:03 AM, Thomas Huth wrote:
>>>> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
>>>>> +Thomas
>>>>
>>>>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>>>>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>>>>>> <hskinnemoen@google.com> wrote:
>>>>>>>
>>>>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>>>>
>>>>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>>>>>> BlockBackend?
>>>>>>>>
>>>>>>>> CLI issue mostly.
>>>>>>>>
>>>>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>>>>>> card sizes" patch:
>>>>>>>>
>>>>>>>>  if (!dinfo) {
>>>>>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>>>>>                              "read-ones=on,size=64M\n);
>>>>>>>>      return;
>>>>>>>>  }
>>>>>>>>
>>>>>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>>>>>> and MachineClass::init() call it with &error_fatal.
>>>>>>>
>>>>>>> Erroring out if the user specifies a configuration that can't possibly
>>>>>>> boot sounds good to me. Better than trying to come up with defaults
>>>>>>> that are still not going to result in a bootable system.
>>>>>>>
>>>>>>> For testing recovery paths, I think it makes sense to explicitly
>>>>>>> specify a null device as you suggest.
>>>>>>
>>>>>> Hmm, one problem. qom-test fails with
>>>>>>
>>>>>> qemu-system-aarch64: Missing SPI flash drive
>>>>>> You can add a dummy drive using:
>>>>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>>>>>> Broken pipe
>>>>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>>>>>> kill_qemu() tried to terminate QEMU process but encountered exit
>>>>>> status 1 (expected 0)
>>>>>> ERROR qom-test - too few tests run (expected 68, got 7)
>>>>>>
>>>>>> So it looks like we might need a different solution to this, unless we
>>>>>> want to make generic tests more machine-aware...
>>>>
>>>> I didn't follow the other mails in this thread, but what we usually do
>>>> in such a case: Add a "if (qtest_enabled())" check to the device or the
>>>> machine to ignore the error if it is running in qtest mode.
>>>
>>> Hmm I'm not sure it works in this case. We could do:
>>>
>>>   if (!dinfo) {
>>>      if (qtest) {
>>>         /* create null drive for qtest */
>>>         opts = ...;
>>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>>      } else {
>>>         /* teach user to use proper CLI */
>>>         error_setg(errp, "Missing SPI flash drive");
>>>         error_append_hint(errp, "You can use a dummy drive using:\n");
>>>         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>                                 "read-ones=on,size=64M\n);
>>>      }
>>>   }
>>>
>>> But I'm not sure Markus will enjoy it :)
>>>
>>> Markus, any better idea about how to handle that with automatic qtests?
>>
>> FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
>> drive":
>>
>> static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
>> {
>>     IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
>>     IDEState *s = bus->ifs + dev->unit;
>>     int ret;
>>
>>     if (!dev->conf.blk) {
>>         if (kind != IDE_CD) {
>>             error_setg(errp, "No drive specified");
>>             return;
>>         } else {
>>             /* Anonymous BlockBackend for an empty drive */
>>             dev->conf.blk = blk_new(qemu_get_aio_context(), 0,
>> BLK_PERM_ALL);
>>             ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
>>             assert(ret == 0);
>>         }
>>     }
> 
> Could someone please remind me what problem we're trying to solve here?
> 
> Currently, if the user (or test) doesn't provide a drive, we pass NULL
> as the block backend to m25p80. This means we'll take the code path in
> m25p_realize() that does
> 
>         trace_m25p80_binding_no_bdrv(s);
>         s->storage = blk_blockalign(NULL, s->size);
>         memset(s->storage, 0xFF, s->size);
> 
> which will look like a freshly chip-erased flash chip.

which is perfect. 

C. 



^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17 19:18                               ` Havard Skinnemoen
  2020-07-17 20:21                                 ` Cédric Le Goater
@ 2020-07-17 20:52                                 ` Philippe Mathieu-Daudé
  2020-07-17 20:57                                   ` Havard Skinnemoen
  1 sibling, 1 reply; 64+ messages in thread
From: Philippe Mathieu-Daudé @ 2020-07-17 20:52 UTC (permalink / raw)
  To: Havard Skinnemoen
  Cc: Kevin Wolf, Peter Maydell, Thomas Huth, Qemu-block,
	Markus Armbruster, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

On 7/17/20 9:18 PM, Havard Skinnemoen wrote:
> On Fri, Jul 17, 2020 at 2:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>
>> On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
>>> On 7/17/20 10:03 AM, Thomas Huth wrote:
>>>> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
>>>>> +Thomas
>>>>
>>>>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>>>>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>>>>>> <hskinnemoen@google.com> wrote:
>>>>>>>
>>>>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>>>>
>>>>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>>>>>> BlockBackend?
>>>>>>>>
>>>>>>>> CLI issue mostly.
>>>>>>>>
>>>>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>>>>>> card sizes" patch:
>>>>>>>>
>>>>>>>>  if (!dinfo) {
>>>>>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>>>>>                              "read-ones=on,size=64M\n);
>>>>>>>>      return;
>>>>>>>>  }
>>>>>>>>
>>>>>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>>>>>> and MachineClass::init() call it with &error_fatal.
>>>>>>>
>>>>>>> Erroring out if the user specifies a configuration that can't possibly
>>>>>>> boot sounds good to me. Better than trying to come up with defaults
>>>>>>> that are still not going to result in a bootable system.
>>>>>>>
>>>>>>> For testing recovery paths, I think it makes sense to explicitly
>>>>>>> specify a null device as you suggest.
>>>>>>
>>>>>> Hmm, one problem. qom-test fails with
>>>>>>
>>>>>> qemu-system-aarch64: Missing SPI flash drive
>>>>>> You can add a dummy drive using:
>>>>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>>>>>> Broken pipe
>>>>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>>>>>> kill_qemu() tried to terminate QEMU process but encountered exit
>>>>>> status 1 (expected 0)
>>>>>> ERROR qom-test - too few tests run (expected 68, got 7)
>>>>>>
>>>>>> So it looks like we might need a different solution to this, unless we
>>>>>> want to make generic tests more machine-aware...
>>>>
>>>> I didn't follow the other mails in this thread, but what we usually do
>>>> in such a case: Add a "if (qtest_enabled())" check to the device or the
>>>> machine to ignore the error if it is running in qtest mode.
>>>
>>> Hmm I'm not sure it works in this case. We could do:
>>>
>>>   if (!dinfo) {
>>>      if (qtest) {
>>>         /* create null drive for qtest */
>>>         opts = ...;
>>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>>      } else {
>>>         /* teach user to use proper CLI */
>>>         error_setg(errp, "Missing SPI flash drive");
>>>         error_append_hint(errp, "You can use a dummy drive using:\n");
>>>         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>                                 "read-ones=on,size=64M\n);
>>>      }
>>>   }
>>>
>>> But I'm not sure Markus will enjoy it :)
>>>
>>> Markus, any better idea about how to handle that with automatic qtests?
>>
>> FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
>> drive":
>>
>> static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
>> {
>>     IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
>>     IDEState *s = bus->ifs + dev->unit;
>>     int ret;
>>
>>     if (!dev->conf.blk) {
>>         if (kind != IDE_CD) {
>>             error_setg(errp, "No drive specified");
>>             return;
>>         } else {
>>             /* Anonymous BlockBackend for an empty drive */
>>             dev->conf.blk = blk_new(qemu_get_aio_context(), 0,
>> BLK_PERM_ALL);
>>             ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
>>             assert(ret == 0);
>>         }
>>     }
> 
> Could someone please remind me what problem we're trying to solve here?

Sorry, out of the scope of your series, which is fine with the current
code base :)

> Currently, if the user (or test) doesn't provide a drive, we pass NULL
> as the block backend to m25p80. This means we'll take the code path in
> m25p_realize() that does
> 
>         trace_m25p80_binding_no_bdrv(s);
>         s->storage = blk_blockalign(NULL, s->size);
>         memset(s->storage, 0xFF, s->size);
> 
> which will look like a freshly chip-erased flash chip.
> 
> Are we looking for a more elegant way to replace those three lines of
> code (+ a couple of conditionals in the writeback paths)?

Yes, I am. Anyway, unrelated to your work, sorry if it confused you.

> 
> But we don't even have a dummy device that looks like an erased flash chip...

No, this is still the design stage, but your series has a quality that
let us foreseen a bit where we are heading...

> 
> Havard
> 


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17 20:52                                 ` Philippe Mathieu-Daudé
@ 2020-07-17 20:57                                   ` Havard Skinnemoen
  0 siblings, 0 replies; 64+ messages in thread
From: Havard Skinnemoen @ 2020-07-17 20:57 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Thomas Huth, Markus Armbruster, Kevin Wolf, Peter Maydell,
	Qemu-block, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

On Fri, Jul 17, 2020 at 1:52 PM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>
> On 7/17/20 9:18 PM, Havard Skinnemoen wrote:
> > On Fri, Jul 17, 2020 at 2:00 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>
> >> On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
> >>> On 7/17/20 10:03 AM, Thomas Huth wrote:
> >>>> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
> >>>>> +Thomas
> >>>>
> >>>>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
> >>>>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
> >>>>>> <hskinnemoen@google.com> wrote:
> >>>>>>>
> >>>>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
> >>>>>>>>
> >>>>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
> >>>>>>>>> Now my point.  Why first make up user configuration, then use that to
> >>>>>>>>> create a BlockBackend, when you could just go ahead and create the
> >>>>>>>>> BlockBackend?
> >>>>>>>>
> >>>>>>>> CLI issue mostly.
> >>>>>>>>
> >>>>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
> >>>>>>>> card sizes" patch:
> >>>>>>>>
> >>>>>>>>  if (!dinfo) {
> >>>>>>>>      error_setg(errp, "Missing SPI flash drive");
> >>>>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
> >>>>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
> >>>>>>>>                              "read-ones=on,size=64M\n);
> >>>>>>>>      return;
> >>>>>>>>  }
> >>>>>>>>
> >>>>>>>> having npcm7xx_connect_flash() taking an Error* argument,
> >>>>>>>> and MachineClass::init() call it with &error_fatal.
> >>>>>>>
> >>>>>>> Erroring out if the user specifies a configuration that can't possibly
> >>>>>>> boot sounds good to me. Better than trying to come up with defaults
> >>>>>>> that are still not going to result in a bootable system.
> >>>>>>>
> >>>>>>> For testing recovery paths, I think it makes sense to explicitly
> >>>>>>> specify a null device as you suggest.
> >>>>>>
> >>>>>> Hmm, one problem. qom-test fails with
> >>>>>>
> >>>>>> qemu-system-aarch64: Missing SPI flash drive
> >>>>>> You can add a dummy drive using:
> >>>>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
> >>>>>> Broken pipe
> >>>>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
> >>>>>> kill_qemu() tried to terminate QEMU process but encountered exit
> >>>>>> status 1 (expected 0)
> >>>>>> ERROR qom-test - too few tests run (expected 68, got 7)
> >>>>>>
> >>>>>> So it looks like we might need a different solution to this, unless we
> >>>>>> want to make generic tests more machine-aware...
> >>>>
> >>>> I didn't follow the other mails in this thread, but what we usually do
> >>>> in such a case: Add a "if (qtest_enabled())" check to the device or the
> >>>> machine to ignore the error if it is running in qtest mode.
> >>>
> >>> Hmm I'm not sure it works in this case. We could do:
> >>>
> >>>   if (!dinfo) {
> >>>      if (qtest) {
> >>>         /* create null drive for qtest */
> >>>         opts = ...;
> >>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
> >>>      } else {
> >>>         /* teach user to use proper CLI */
> >>>         error_setg(errp, "Missing SPI flash drive");
> >>>         error_append_hint(errp, "You can use a dummy drive using:\n");
> >>>         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
> >>>                                 "read-ones=on,size=64M\n);
> >>>      }
> >>>   }
> >>>
> >>> But I'm not sure Markus will enjoy it :)
> >>>
> >>> Markus, any better idea about how to handle that with automatic qtests?
> >>
> >> FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
> >> drive":
> >>
> >> static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
> >> {
> >>     IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
> >>     IDEState *s = bus->ifs + dev->unit;
> >>     int ret;
> >>
> >>     if (!dev->conf.blk) {
> >>         if (kind != IDE_CD) {
> >>             error_setg(errp, "No drive specified");
> >>             return;
> >>         } else {
> >>             /* Anonymous BlockBackend for an empty drive */
> >>             dev->conf.blk = blk_new(qemu_get_aio_context(), 0,
> >> BLK_PERM_ALL);
> >>             ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
> >>             assert(ret == 0);
> >>         }
> >>     }
> >
> > Could someone please remind me what problem we're trying to solve here?
>
> Sorry, out of the scope of your series, which is fine with the current
> code base :)
>
> > Currently, if the user (or test) doesn't provide a drive, we pass NULL
> > as the block backend to m25p80. This means we'll take the code path in
> > m25p_realize() that does
> >
> >         trace_m25p80_binding_no_bdrv(s);
> >         s->storage = blk_blockalign(NULL, s->size);
> >         memset(s->storage, 0xFF, s->size);
> >
> > which will look like a freshly chip-erased flash chip.
> >
> > Are we looking for a more elegant way to replace those three lines of
> > code (+ a couple of conditionals in the writeback paths)?
>
> Yes, I am. Anyway, unrelated to your work, sorry if it confused you.

OK, great, I'll be happy to contribute to that. I was just a little
worried that my series was getting blocked behind it.

> >
> > But we don't even have a dummy device that looks like an erased flash chip...
>
> No, this is still the design stage, but your series has a quality that
> let us foreseen a bit where we are heading...
>
> >
> > Havard
> >


^ permalink raw reply	[flat|nested] 64+ messages in thread

* Re: [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj
  2020-07-17  9:00                             ` Philippe Mathieu-Daudé
  2020-07-17 19:18                               ` Havard Skinnemoen
@ 2020-07-20  7:58                               ` Markus Armbruster
  1 sibling, 0 replies; 64+ messages in thread
From: Markus Armbruster @ 2020-07-20  7:58 UTC (permalink / raw)
  To: Philippe Mathieu-Daudé
  Cc: Kevin Wolf, Peter Maydell, Thomas Huth, Qemu-block,
	Havard Skinnemoen, QEMU Developers, CS20 KFTing, qemu-arm,
	Cédric Le Goater, IS20 Avi Fishman

Philippe Mathieu-Daudé <f4bug@amsat.org> writes:

> On 7/17/20 10:27 AM, Philippe Mathieu-Daudé wrote:
>> On 7/17/20 10:03 AM, Thomas Huth wrote:
>>> On 17/07/2020 09.48, Philippe Mathieu-Daudé wrote:
>>>> +Thomas
>>>
>>>> On 7/16/20 10:56 PM, Havard Skinnemoen wrote:
>>>>> On Wed, Jul 15, 2020 at 1:54 PM Havard Skinnemoen
>>>>> <hskinnemoen@google.com> wrote:
>>>>>>
>>>>>> On Wed, Jul 15, 2020 at 3:57 AM Philippe Mathieu-Daudé <f4bug@amsat.org> wrote:
>>>>>>>
>>>>>>> On 7/15/20 11:00 AM, Markus Armbruster wrote:
>>>>>>>> Now my point.  Why first make up user configuration, then use that to
>>>>>>>> create a BlockBackend, when you could just go ahead and create the
>>>>>>>> BlockBackend?
>>>>>>>
>>>>>>> CLI issue mostly.
>>>>>>>
>>>>>>> We can solve it similarly to the recent "sdcard: Do not allow invalid SD
>>>>>>> card sizes" patch:
>>>>>>>
>>>>>>>  if (!dinfo) {
>>>>>>>      error_setg(errp, "Missing SPI flash drive");
>>>>>>>      error_append_hint(errp, "You can use a dummy drive using:\n");
>>>>>>>      error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>>>>>>                              "read-ones=on,size=64M\n);
>>>>>>>      return;
>>>>>>>  }
>>>>>>>
>>>>>>> having npcm7xx_connect_flash() taking an Error* argument,
>>>>>>> and MachineClass::init() call it with &error_fatal.
>>>>>>
>>>>>> Erroring out if the user specifies a configuration that can't possibly
>>>>>> boot sounds good to me. Better than trying to come up with defaults
>>>>>> that are still not going to result in a bootable system.
>>>>>>
>>>>>> For testing recovery paths, I think it makes sense to explicitly
>>>>>> specify a null device as you suggest.
>>>>>
>>>>> Hmm, one problem. qom-test fails with
>>>>>
>>>>> qemu-system-aarch64: Missing SPI flash drive
>>>>> You can add a dummy drive using:
>>>>> -drive if=mtd,driver=null-co,read-zeroes=on,size=32M
>>>>> Broken pipe
>>>>> /usr/local/google/home/hskinnemoen/qemu/for-upstream/tests/qtest/libqtest.c:166:
>>>>> kill_qemu() tried to terminate QEMU process but encountered exit
>>>>> status 1 (expected 0)
>>>>> ERROR qom-test - too few tests run (expected 68, got 7)
>>>>>
>>>>> So it looks like we might need a different solution to this, unless we
>>>>> want to make generic tests more machine-aware...
>>>
>>> I didn't follow the other mails in this thread, but what we usually do
>>> in such a case: Add a "if (qtest_enabled())" check to the device or the
>>> machine to ignore the error if it is running in qtest mode.
>> 
>> Hmm I'm not sure it works in this case. We could do:
>> 
>>   if (!dinfo) {
>>      if (qtest) {
>>         /* create null drive for qtest */
>>         opts = ...;
>>         dinfo = drive_new(opts, IF_MTD, &error_abort);
>>      } else {
>>         /* teach user to use proper CLI */
>>         error_setg(errp, "Missing SPI flash drive");
>>         error_append_hint(errp, "You can use a dummy drive using:\n");
>>         error_append_hint(errp, "-drive if=mtd,driver=null-co,"
>>                                 "read-ones=on,size=64M\n);
>>      }
>>   }
>> 
>> But I'm not sure Markus will enjoy it :)

Using drive_new() for creating an internal dummy backend is wrong.

Doing it only when qtest_enabled() doesn't make it less wrong.

>> Markus, any better idea about how to handle that with automatic qtests?
>
> FWIW IDE device has a concept of "Anonymous BlockBackend for an empty
> drive":
>
> static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
> {
>     IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
>     IDEState *s = bus->ifs + dev->unit;
>     int ret;
>
>     if (!dev->conf.blk) {
>         if (kind != IDE_CD) {
>             error_setg(errp, "No drive specified");
>             return;
>         } else {
>             /* Anonymous BlockBackend for an empty drive */
>             dev->conf.blk = blk_new(qemu_get_aio_context(), 0, BLK_PERM_ALL);
>             ret = blk_attach_dev(dev->conf.blk, &dev->qdev);
>             assert(ret == 0);
>         }
>     }

I figure this creates an internal dummy backend the right way, just not
the kind you need.  For a non-empty one, you get to make up a
BlockDriverState, then use blk_new_with_bs().

Is the simplification of device code really worth making up a dummy
backend?



^ permalink raw reply	[flat|nested] 64+ messages in thread

end of thread, other threads:[~2020-07-20  7:59 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-09  0:35 [PATCH v5 00/11] Add Nuvoton NPCM730/NPCM750 SoCs and two BMC machines Havard Skinnemoen
2020-07-09  0:35 ` [PATCH v5 01/11] hw/misc: Add NPCM7xx System Global Control Registers device model Havard Skinnemoen
2020-07-09  6:04   ` Philippe Mathieu-Daudé
2020-07-09  6:43     ` Havard Skinnemoen
2020-07-09 16:23       ` Philippe Mathieu-Daudé
2020-07-09 17:09         ` Havard Skinnemoen
2020-07-09 17:24           ` Philippe Mathieu-Daudé
2020-07-09 17:42             ` Havard Skinnemoen
2020-07-10  9:31               ` Philippe Mathieu-Daudé
2020-07-11  6:46                 ` Havard Skinnemoen
2020-07-12  5:49                   ` Havard Skinnemoen
2020-07-09  0:35 ` [PATCH v5 02/11] hw/misc: Add NPCM7xx Clock Controller " Havard Skinnemoen
2020-07-15  7:18   ` Philippe Mathieu-Daudé
2020-07-09  0:36 ` [PATCH v5 03/11] hw/timer: Add NPCM7xx Timer " Havard Skinnemoen
2020-07-15  7:25   ` Philippe Mathieu-Daudé
2020-07-15 23:04     ` Havard Skinnemoen
2020-07-16  8:04       ` Philippe Mathieu-Daudé
2020-07-09  0:36 ` [PATCH v5 04/11] hw/arm: Add NPCM730 and NPCM750 SoC models Havard Skinnemoen
2020-07-09  6:11   ` Philippe Mathieu-Daudé
2020-07-13 15:02   ` Cédric Le Goater
2020-07-14  0:44     ` Havard Skinnemoen
2020-07-14 11:37       ` Philippe Mathieu-Daudé
2020-07-14 16:01         ` Markus Armbruster
2020-07-14 17:11           ` Philippe Mathieu-Daudé
2020-07-15  1:03             ` Havard Skinnemoen
2020-07-15  9:35               ` Markus Armbruster
2020-07-09  0:36 ` [PATCH v5 05/11] hw/arm: Add two NPCM7xx-based machines Havard Skinnemoen
2020-07-09  5:57   ` Philippe Mathieu-Daudé
2020-07-09  6:09     ` Philippe Mathieu-Daudé
2020-07-09  0:36 ` [PATCH v5 06/11] hw/arm: Load -bios image as a boot ROM for npcm7xx Havard Skinnemoen
2020-07-13 17:50   ` Philippe Mathieu-Daudé
2020-07-09  0:36 ` [PATCH v5 07/11] hw/nvram: NPCM7xx OTP device model Havard Skinnemoen
2020-07-09  0:36 ` [PATCH v5 08/11] hw/mem: Stubbed out NPCM7xx Memory Controller model Havard Skinnemoen
2020-07-09 16:29   ` Philippe Mathieu-Daudé
2020-07-09  0:36 ` [PATCH v5 09/11] hw/ssi: NPCM7xx Flash Interface Unit device model Havard Skinnemoen
2020-07-09 17:00   ` Philippe Mathieu-Daudé
2020-07-12  5:42     ` Havard Skinnemoen
2020-07-13 17:38       ` Philippe Mathieu-Daudé
2020-07-14  2:39         ` Havard Skinnemoen
2020-07-09  0:36 ` [PATCH v5 10/11] hw/arm: Wire up BMC boot flash for npcm750-evb and quanta-gsj Havard Skinnemoen
2020-07-13 14:57   ` Cédric Le Goater
2020-07-13 17:59     ` Philippe Mathieu-Daudé
2020-07-13 18:02       ` Philippe Mathieu-Daudé
2020-07-14  2:56     ` Havard Skinnemoen
2020-07-14  9:16       ` Markus Armbruster
2020-07-14 11:29         ` Philippe Mathieu-Daudé
2020-07-14 16:21           ` Markus Armbruster
2020-07-14 17:16             ` Philippe Mathieu-Daudé
2020-07-15  9:00               ` Markus Armbruster
2020-07-15 10:57                 ` Philippe Mathieu-Daudé
2020-07-15 20:54                   ` Havard Skinnemoen
2020-07-16 20:56                     ` Havard Skinnemoen
2020-07-17  7:48                       ` Philippe Mathieu-Daudé
2020-07-17  8:03                         ` Thomas Huth
2020-07-17  8:27                           ` Philippe Mathieu-Daudé
2020-07-17  9:00                             ` Philippe Mathieu-Daudé
2020-07-17 19:18                               ` Havard Skinnemoen
2020-07-17 20:21                                 ` Cédric Le Goater
2020-07-17 20:52                                 ` Philippe Mathieu-Daudé
2020-07-17 20:57                                   ` Havard Skinnemoen
2020-07-20  7:58                               ` Markus Armbruster
2020-07-15  7:42       ` Cédric Le Goater
2020-07-15 21:19         ` Havard Skinnemoen
2020-07-09  0:36 ` [PATCH v5 11/11] docs/system: Add Nuvoton machine documentation Havard Skinnemoen

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.