Nuvoton's PSPI is a general purpose SPI module which enables
connections to SPI-based peripheral devices.
Signed-off-by: Hao Wu
Reviewed-by: Chris Rauer
Reviewed-by: Philippe Mathieu-Daude
---
MAINTAINERS| 6 +-
hw/ssi/meson.build | 2 +-
hw/ssi/npcm_pspi.c | 221 +
hw/ssi/trace-events| 5 +
include/hw/ssi/npcm_pspi.h | 53 +
5 files changed, 283 insertions(+), 4 deletions(-)
create mode 100644 hw/ssi/npcm_pspi.c
create mode 100644 include/hw/ssi/npcm_pspi.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 347936e41c..1e2a711373 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -803,9 +803,9 @@ M: Tyrone Ting
M: Hao Wu
L: qemu-...@nongnu.org
S: Supported
-F: hw/*/npcm7xx*
-F: include/hw/*/npcm7xx*
-F: tests/qtest/npcm7xx*
+F: hw/*/npcm*
+F: include/hw/*/npcm*
+F: tests/qtest/npcm*
F: pc-bios/npcm7xx_bootrom.bin
F: roms/vbootrom
F: docs/system/arm/nuvoton.rst
diff --git a/hw/ssi/meson.build b/hw/ssi/meson.build
index 702aa5e4df..904a47161a 100644
--- a/hw/ssi/meson.build
+++ b/hw/ssi/meson.build
@@ -1,6 +1,6 @@
softmmu_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files('aspeed_smc.c'))
softmmu_ss.add(when: 'CONFIG_MSF2', if_true: files('mss-spi.c'))
-softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_fiu.c',
'npcm_pspi.c'))
softmmu_ss.add(when: 'CONFIG_PL022', if_true: files('pl022.c'))
softmmu_ss.add(when: 'CONFIG_SIFIVE_SPI', if_true: files('sifive_spi.c'))
softmmu_ss.add(when: 'CONFIG_SSI', if_true: files('ssi.c'))
diff --git a/hw/ssi/npcm_pspi.c b/hw/ssi/npcm_pspi.c
new file mode 100644
index 00..1fd865d04e
--- /dev/null
+++ b/hw/ssi/npcm_pspi.c
@@ -0,0 +1,221 @@
+/*
+ * Nuvoton NPCM Peripheral SPI Module (PSPI)
+ *
+ * Copyright 2023 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/registerfields.h"
+#include "hw/ssi/npcm_pspi.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"
+
+REG16(PSPI_DATA, 0x0)
+REG16(PSPI_CTL1, 0x2)
+FIELD(PSPI_CTL1, SPIEN, 0, 1)
+FIELD(PSPI_CTL1, MOD, 2, 1)
+FIELD(PSPI_CTL1, EIR, 5, 1)
+FIELD(PSPI_CTL1, EIW, 6, 1)
+FIELD(PSPI_CTL1, SCM, 7, 1)
+FIELD(PSPI_CTL1, SCIDL, 8, 1)
+FIELD(PSPI_CTL1, SCDV, 9, 7)
+REG16(PSPI_STAT, 0x4)
+FIELD(PSPI_STAT, BSY, 0, 1)
+FIELD(PSPI_STAT, RBF, 1, 1)
+
+static void npcm_pspi_update_irq(NPCMPSPIState *s)
+{
+int level = 0;
+
+/* Only fire IRQ when the module is enabled. */
+if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, SPIEN)) {
+/* Update interrupt as BSY is cleared. */
+if ((!FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, BSY)) &&
+FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIW)) {
+level = 1;
+}
+
+/* Update interrupt as RBF is set. */
+if (FIELD_EX16(s->regs[R_PSPI_STAT], PSPI_STAT, RBF) &&
+FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, EIR)) {
+level = 1;
+}
+}
+qemu_set_irq(s->irq, level);
+}
+
+static uint16_t npcm_pspi_read_data(NPCMPSPIState *s)
+{
+uint16_t value = s->regs[R_PSPI_DATA];
+
+/* Clear stat bits as the value are read out. */
+s->regs[R_PSPI_STAT] = 0;
+
+return value;
+}
+
+static void npcm_pspi_write_data(NPCMPSPIState *s, uint16_t data)
+{
+uint16_t value = 0;
+
+if (FIELD_EX16(s->regs[R_PSPI_CTL1], PSPI_CTL1, MOD)) {
+value = ssi_transfer(s->spi, extract16(data, 8, 8)) << 8;
+}
+value |= ssi_transfer(s->spi, extract16(data, 0, 8));
+s->regs[R_PSPI_DATA] = value;
+
+/* Mark data as available */
+s->regs[R_PSPI_STAT] = R_PSPI_STAT_BSY_MASK | R_PSPI_STAT_RBF_MASK;
+}
+
+/* Control register read handler. */
+static uint64_t npcm_pspi_ctrl_read(void *opaque, hwaddr addr,
+unsigned int size)
+{
+NPCMPSPIState *s = opaque;
+uint16_t value;
+
+switch (addr) {
+case A_PSPI_DATA:
+value = npcm_pspi_read_data(s);
+break;
+
+case A_PSPI_CTL1:
+value = s->regs[R_PSPI_CTL1];
+break;
+
+case A_PSPI_STAT:
+value = s->regs[R_PSPI_STAT];
+break;
+
+default:
+qemu_log_mask(LOG_GUEST_ERROR,
+ "%s: write to invalid offset 0x%