[PATCH 3/4] hw/arm: Connect PWM fans in NPCM7XX boards

2021-03-05 Thread Hao Wu via
This patch adds fan_splitters (split IRQs) in NPCM7XX boards. Each fan
splitter corresponds to 1 PWM output and can connect to multiple fan
inputs (MFT devices).
In NPCM7XX boards(NPCM750 EVB and Quanta GSJ boards), we initializes
these splitters and connect them to their corresponding modules
according their specific device trees.

Reviewed-by: Doug Evans 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx_boards.c  | 99 
 include/hw/arm/npcm7xx.h | 11 -
 2 files changed, 109 insertions(+), 1 deletion(-)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index fbf6ce8e02..e22fe4bf8f 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -21,6 +21,7 @@
 #include "hw/core/cpu.h"
 #include "hw/i2c/smbus_eeprom.h"
 #include "hw/loader.h"
+#include "hw/qdev-core.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu-common.h"
@@ -116,6 +117,64 @@ static void at24c_eeprom_init(NPCM7xxState *soc, int bus, 
uint8_t addr,
 i2c_slave_realize_and_unref(i2c_dev, i2c_bus, _abort);
 }
 
+static void npcm7xx_init_pwm_splitter(NPCM7xxMachine *machine,
+  NPCM7xxState *soc, const int *fan_counts)
+{
+SplitIRQ *splitters = machine->fan_splitter;
+
+/*
+ * PWM 0~3 belong to module 0 output 0~3.
+ * PWM 4~7 belong to module 1 output 0~3.
+ */
+for (int i = 0; i < NPCM7XX_NR_PWM_MODULES; ++i) {
+for (int j = 0; j < NPCM7XX_PWM_PER_MODULE; ++j) {
+int splitter_no = i * NPCM7XX_PWM_PER_MODULE + j;
+DeviceState *splitter;
+
+if (fan_counts[splitter_no] < 1) {
+continue;
+}
+object_initialize_child(OBJECT(machine), "fan-splitter[*]",
+[splitter_no], TYPE_SPLIT_IRQ);
+splitter = DEVICE([splitter_no]);
+qdev_prop_set_uint16(splitter, "num-lines",
+ fan_counts[splitter_no]);
+qdev_realize(splitter, NULL, _abort);
+qdev_connect_gpio_out_named(DEVICE(>pwm[i]), "duty-gpio-out",
+j, qdev_get_gpio_in(splitter, 0));
+}
+}
+}
+
+static void npcm7xx_connect_pwm_fan(NPCM7xxState *soc, SplitIRQ *splitter,
+int fan_no, int output_no)
+{
+DeviceState *fan;
+int fan_input;
+qemu_irq fan_duty_gpio;
+
+g_assert(fan_no >= 0 && fan_no <= NPCM7XX_MFT_MAX_FAN_INPUT);
+/*
+ * Fan 0~1 belong to module 0 input 0~1.
+ * Fan 2~3 belong to module 1 input 0~1.
+ * ...
+ * Fan 14~15 belong to module 7 input 0~1.
+ * Fan 16~17 belong to module 0 input 2~3.
+ * Fan 18~19 belong to module 1 input 2~3.
+ */
+if (fan_no < 16) {
+fan = DEVICE(>mft[fan_no / 2]);
+fan_input = fan_no % 2;
+} else {
+fan = DEVICE(>mft[(fan_no - 16) / 2]);
+fan_input = fan_no % 2 + 2;
+}
+
+/* Connect the Fan to PWM module */
+fan_duty_gpio = qdev_get_gpio_in_named(fan, "duty", fan_input);
+qdev_connect_gpio_out(DEVICE(splitter), output_no, fan_duty_gpio);
+}
+
 static void npcm750_evb_i2c_init(NPCM7xxState *soc)
 {
 /* lm75 temperature sensor on SVB, tmp105 is compatible */
@@ -128,6 +187,30 @@ static void npcm750_evb_i2c_init(NPCM7xxState *soc)
 i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
 }
 
+static void npcm750_evb_fan_init(NPCM7xxMachine *machine, NPCM7xxState *soc)
+{
+SplitIRQ *splitter = machine->fan_splitter;
+static const int fan_counts[] = {2, 2, 2, 2, 2, 2, 2, 2};
+
+npcm7xx_init_pwm_splitter(machine, soc, fan_counts);
+npcm7xx_connect_pwm_fan(soc, [0], 0x00, 0);
+npcm7xx_connect_pwm_fan(soc, [0], 0x01, 1);
+npcm7xx_connect_pwm_fan(soc, [1], 0x02, 0);
+npcm7xx_connect_pwm_fan(soc, [1], 0x03, 1);
+npcm7xx_connect_pwm_fan(soc, [2], 0x04, 0);
+npcm7xx_connect_pwm_fan(soc, [2], 0x05, 1);
+npcm7xx_connect_pwm_fan(soc, [3], 0x06, 0);
+npcm7xx_connect_pwm_fan(soc, [3], 0x07, 1);
+npcm7xx_connect_pwm_fan(soc, [4], 0x08, 0);
+npcm7xx_connect_pwm_fan(soc, [4], 0x09, 1);
+npcm7xx_connect_pwm_fan(soc, [5], 0x0a, 0);
+npcm7xx_connect_pwm_fan(soc, [5], 0x0b, 1);
+npcm7xx_connect_pwm_fan(soc, [6], 0x0c, 0);
+npcm7xx_connect_pwm_fan(soc, [6], 0x0d, 1);
+npcm7xx_connect_pwm_fan(soc, [7], 0x0e, 0);
+npcm7xx_connect_pwm_fan(soc, [7], 0x0f, 1);
+}
+
 static void quanta_gsj_i2c_init(NPCM7xxState *soc)
 {
 /* GSJ machine have 4 max31725 temperature sensors, tmp105 is compatible. 
*/
@@ -142,6 +225,20 @@ static void quanta_gsj_i2c_init(NPCM7xxState *soc)
 /* TODO: Add additional i2c devices. */
 }
 
+static void quanta_gsj_fan_init(NPCM7xxMachine *machine, NPCM7xxState *soc)
+{
+SplitIRQ *splitter = machine->fan_splitter;
+static const int fan_counts[] = {2, 2, 2, 0, 0, 0, 0, 0};
+
+

[PATCH 4/4] tests/qtest: Test PWM fan RPM using MFT in PWM test

2021-03-05 Thread Hao Wu via
This patch adds testing of PWM fan RPMs in the existing npcm7xx pwm
test. It tests whether the MFT module can measure correct fan values
for a PWM fan in NPCM7XX boards.

Reviewed-by: Doug Evans 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 tests/qtest/npcm7xx_pwm-test.c | 205 -
 1 file changed, 199 insertions(+), 6 deletions(-)

diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
index 3d82654b81..72317f4c81 100644
--- a/tests/qtest/npcm7xx_pwm-test.c
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -45,6 +45,7 @@
 #define PLL_FBDV(rv)extract32((rv), 16, 12)
 #define PLL_OTDV1(rv)   extract32((rv), 8, 3)
 #define PLL_OTDV2(rv)   extract32((rv), 13, 3)
+#define APB4CKDIV(rv)   extract32((rv), 30, 2)
 #define APB3CKDIV(rv)   extract32((rv), 28, 2)
 #define CLK2CKDIV(rv)   extract32((rv), 0, 1)
 #define CLK4CKDIV(rv)   extract32((rv), 26, 2)
@@ -52,6 +53,49 @@
 
 #define MAX_DUTY100
 
+/* MFT (PWM fan) related */
+#define MFT_BA(n)   (0xf018 + ((n) * 0x1000))
+#define MFT_IRQ(n)  (96 + (n))
+#define MFT_CNT10x00
+#define MFT_CRA 0x02
+#define MFT_CRB 0x04
+#define MFT_CNT20x06
+#define MFT_PRSC0x08
+#define MFT_CKC 0x0a
+#define MFT_MCTRL   0x0c
+#define MFT_ICTRL   0x0e
+#define MFT_ICLR0x10
+#define MFT_IEN 0x12
+#define MFT_CPA 0x14
+#define MFT_CPB 0x16
+#define MFT_CPCFG   0x18
+#define MFT_INASEL  0x1a
+#define MFT_INBSEL  0x1c
+
+#define MFT_MCTRL_ALL   0x64
+#define MFT_ICLR_ALL0x3f
+#define MFT_IEN_ALL 0x3f
+#define MFT_CPCFG_EQ_MODE 0x44
+
+#define MFT_CKC_C2CSEL  BIT(3)
+#define MFT_CKC_C1CSEL  BIT(0)
+
+#define MFT_ICTRL_TFPND BIT(5)
+#define MFT_ICTRL_TEPND BIT(4)
+#define MFT_ICTRL_TDPND BIT(3)
+#define MFT_ICTRL_TCPND BIT(2)
+#define MFT_ICTRL_TBPND BIT(1)
+#define MFT_ICTRL_TAPND BIT(0)
+
+#define MFT_MAX_CNT 0x
+#define MFT_TIMEOUT 0x5000
+
+#define DEFAULT_RPM 19800
+#define DEFAULT_PRSC255
+#define MFT_PULSE_PER_REVOLUTION 2
+
+#define MAX_ERROR   1
+
 typedef struct PWMModule {
 int irq;
 uint64_t base_addr;
@@ -210,19 +254,36 @@ static uint64_t pwm_get_duty(QTestState *qts, int 
module_index, int pwm_index)
 return pwm_qom_get(qts, path, name);
 }
 
+static void mft_qom_set(QTestState *qts, int index, const char *name,
+uint32_t value)
+{
+QDict *response;
+char *path = g_strdup_printf("/machine/soc/mft[%d]", index);
+
+g_test_message("Setting properties %s of mft[%d] with value %u",
+   name, index, value);
+response = qtest_qmp(qts, "{ 'execute': 'qom-set',"
+" 'arguments': { 'path': %s, "
+" 'property': %s, 'value': %u}}",
+path, name, value);
+/* The qom set message returns successfully. */
+g_assert_true(qdict_haskey(response, "return"));
+}
+
 static uint32_t get_pll(uint32_t con)
 {
 return REF_HZ * PLL_FBDV(con) / (PLL_INDV(con) * PLL_OTDV1(con)
 * PLL_OTDV2(con));
 }
 
-static uint64_t read_pclk(QTestState *qts)
+static uint64_t read_pclk(QTestState *qts, bool mft)
 {
 uint64_t freq = REF_HZ;
 uint32_t clksel = qtest_readl(qts, CLK_BA + CLKSEL);
 uint32_t pllcon;
 uint32_t clkdiv1 = qtest_readl(qts, CLK_BA + CLKDIV1);
 uint32_t clkdiv2 = qtest_readl(qts, CLK_BA + CLKDIV2);
+uint32_t apbdiv = mft ? APB4CKDIV(clkdiv2) : APB3CKDIV(clkdiv2);
 
 switch (CPUCKSEL(clksel)) {
 case 0:
@@ -241,7 +302,7 @@ static uint64_t read_pclk(QTestState *qts)
 g_assert_not_reached();
 }
 
-freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv2));
+freq >>= (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + apbdiv);
 
 return freq;
 }
@@ -267,7 +328,7 @@ static uint32_t pwm_selector(uint32_t csr)
 static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t csr,
 uint32_t cnr)
 {
-return read_pclk(qts) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
+return read_pclk(qts, false) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1));
 }
 
 static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted)
@@ -301,6 +362,28 @@ static void pwm_write(QTestState *qts, const TestData *td, 
unsigned offset,
 qtest_writel(qts, td->module->base_addr + offset, value);
 }
 
+static uint8_t mft_readb(QTestState *qts, int index, unsigned offset)
+{
+return qtest_readb(qts, MFT_BA(index) + offset);
+}
+
+static uint16_t mft_readw(QTestState *qts, int index, unsigned offset)
+{
+return qtest_readw(qts, MFT_BA(index) + offset);
+}
+
+static void mft_writeb(QTestState *qts, int index, unsigned offset,
+uint8_t value)
+{
+qtest_writeb(qts, MFT_BA(index) + offset, value);
+}
+
+static void mft_writew(QTestState *qts, int index, unsigned offset,
+uint16_t value)
+{
+return qtest_writew(qts, MFT_BA(index) + offset, value);
+}

[PATCH 1/4] hw/misc: Add GPIOs for duty in NPCM7xx PWM

2021-03-05 Thread Hao Wu via
This patch adds GPIOs in NPCM7xx PWM module for its duty values.
The purpose of this is to connect it to the MFT module to provide
an input for measuring a PWM fan's RPM. Each PWM module has
NPCM7XX_PWM_PER_MODULE of GPIOs, each one corresponds to
one PWM instance and can connect to multiple fan instances in MFT.

Reviewed-by: Doug Evans 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/misc/npcm7xx_pwm.c | 4 
 include/hw/misc/npcm7xx_pwm.h | 4 +++-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c
index dabcb6c0f9..4c48b281ef 100644
--- a/hw/misc/npcm7xx_pwm.c
+++ b/hw/misc/npcm7xx_pwm.c
@@ -139,6 +139,7 @@ static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p)
 trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path,
   p->index, p->duty, duty);
 p->duty = duty;
+qemu_set_irq(p->module->duty_gpio_out[p->index], p->duty);
 }
 }
 
@@ -483,6 +484,7 @@ static void npcm7xx_pwm_init(Object *obj)
 SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 int i;
 
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(s->pwm) != NPCM7XX_PWM_PER_MODULE);
 for (i = 0; i < NPCM7XX_PWM_PER_MODULE; i++) {
 NPCM7xxPWM *p = >pwm[i];
 p->module = s;
@@ -501,6 +503,8 @@ static void npcm7xx_pwm_init(Object *obj)
 object_property_add_uint32_ptr(obj, "duty[*]",
 >pwm[i].duty, OBJ_PROP_FLAG_READ);
 }
+qdev_init_gpio_out_named(DEVICE(s), s->duty_gpio_out,
+ "duty-gpio-out", NPCM7XX_PWM_PER_MODULE);
 }
 
 static const VMStateDescription vmstate_npcm7xx_pwm = {
diff --git a/include/hw/misc/npcm7xx_pwm.h b/include/hw/misc/npcm7xx_pwm.h
index 5a689d3f66..7ad632a93a 100644
--- a/include/hw/misc/npcm7xx_pwm.h
+++ b/include/hw/misc/npcm7xx_pwm.h
@@ -77,6 +77,7 @@ typedef struct NPCM7xxPWM {
  * @iomem: Memory region through which registers are accessed.
  * @clock: The PWM clock.
  * @pwm: The PWM channels owned by this module.
+ * @duty_gpio_out: The duty cycle of each PWM channels as a output GPIO.
  * @ppr: The prescaler register.
  * @csr: The clock selector register.
  * @pcr: The control register.
@@ -89,7 +90,8 @@ struct NPCM7xxPWMState {
 MemoryRegion iomem;
 
 Clock   *clock;
-NPCM7xxPWM pwm[NPCM7XX_PWM_PER_MODULE];
+NPCM7xxPWM  pwm[NPCM7XX_PWM_PER_MODULE];
+qemu_irqduty_gpio_out[NPCM7XX_PWM_PER_MODULE];
 
 uint32_tppr;
 uint32_tcsr;
-- 
2.30.1.766.gb4fecdf3b7-goog




[PATCH 2/4] hw/misc: Add NPCM7XX MFT Module

2021-03-05 Thread Hao Wu via
This patch adds Multi Function Timer (MFT) module for NPCM7XX Soc.
This module is mainly used to configure PWM fans. It has just enough
functionality to make the PWM fan kernel module work.

The module takes two input, the max_rpm of a fan (modifiable via QMP)
and duty cycle (a GPIO from the PWM module.) The actual measured RPM
is equal to max_rpm * duty_cycle / NPCM7XX_PWM_MAX_DUTY. The RPM is
measured as a counter compared to a prescaled input clock. The kernel
driver reads this counter and report to user space.

Refs:
https://github.com/torvalds/linux/blob/master/drivers/hwmon/npcm750-pwm-fan.c

Reviewed-by: Doug Evans 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst   |   2 +-
 hw/arm/npcm7xx.c  |  45 ++-
 hw/misc/meson.build   |   1 +
 hw/misc/npcm7xx_mft.c | 541 ++
 hw/misc/trace-events  |   8 +
 include/hw/arm/npcm7xx.h  |   2 +
 include/hw/misc/npcm7xx_mft.h |  70 +
 7 files changed, 660 insertions(+), 9 deletions(-)
 create mode 100644 hw/misc/npcm7xx_mft.c
 create mode 100644 include/hw/misc/npcm7xx_mft.h

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 34fc799b2d..3fd3e88479 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -44,6 +44,7 @@ Supported devices
  * Analog to Digital Converter (ADC)
  * Pulse Width Modulation (PWM)
  * SMBus controller (SMBF)
+ * Tachometer
 
 Missing devices
 ---
@@ -62,7 +63,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
  * Serial I/O expansion
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index f8950f9470..da6f21d3c7 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -120,6 +120,14 @@ enum NPCM7xxInterrupt {
 NPCM7XX_SMBUS15_IRQ,
 NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
 NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
+NPCM7XX_MFT0_IRQ= 96,   /* MFT module 0 */
+NPCM7XX_MFT1_IRQ,   /* MFT module 1 */
+NPCM7XX_MFT2_IRQ,   /* MFT module 2 */
+NPCM7XX_MFT3_IRQ,   /* MFT module 3 */
+NPCM7XX_MFT4_IRQ,   /* MFT module 4 */
+NPCM7XX_MFT5_IRQ,   /* MFT module 5 */
+NPCM7XX_MFT6_IRQ,   /* MFT module 6 */
+NPCM7XX_MFT7_IRQ,   /* MFT module 7 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -168,6 +176,18 @@ static const hwaddr npcm7xx_pwm_addr[] = {
 0xf0104000,
 };
 
+/* Register base address for each MFT Module */
+static const hwaddr npcm7xx_mft_addr[] = {
+0xf018,
+0xf0181000,
+0xf0182000,
+0xf0183000,
+0xf0184000,
+0xf0185000,
+0xf0186000,
+0xf0187000,
+};
+
 /* Direct memory-mapped access to each SMBus Module. */
 static const hwaddr npcm7xx_smbus_addr[] = {
 0xf008,
@@ -406,6 +426,10 @@ static void npcm7xx_init(Object *obj)
 for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
 object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+object_initialize_child(obj, "mft[*]", >mft[i], TYPE_NPCM7XX_MFT);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -589,6 +613,19 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
 }
 
+/* MFT Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_mft_addr) != ARRAY_SIZE(s->mft));
+for (i = 0; i < ARRAY_SIZE(s->mft); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>mft[i]);
+
+qdev_connect_clock_in(DEVICE(>mft[i]), "clock-in",
+  qdev_get_clock_out(DEVICE(>clk),
+ "apb4-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_mft_addr[i]);
+sysbus_connect_irq(sbd, 0, npcm7xx_irq(s, NPCM7XX_MFT0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -632,14 +669,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   4 * KiB);
 create_unimplemented_device("npcm7xx.siox[1]",  0xf0101000,   4 * KiB);
 create_unimplemented_device("npcm7xx.siox[2]",  0xf0102000,   4 * KiB);
-create_unimplemented_device("npcm7xx.mft[0]",   0xf018,   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);
-

[PATCH 0/4] hw/misc: Add NPCM7XX Tachometer Device

2021-03-05 Thread Hao Wu via
This patch set implements the Tachometer (a.k.a Multi Functional Timer/MFT)
device in NPCM7XX SoC. This device is used by NPCM7XX boards to measure
the RPM of PWM fans.

To provide the RPM of a certain fan, since RPM = MAX_RPM * duty_percentage.
We convert the duty output in NPCM7XX PWM module into GPIOs and feed them
into the MFT module.

The connection of PWM modules and fan modules are derived from their specific
Linux device trees and coded in hw/arm/npcm7xx_boards.c.

We amend the QTest for the PWM module to include verifying the reading from
the Tachometer is correct.

Hao Wu (4):
  hw/misc: Add GPIOs for duty in NPCM7xx PWM
  hw/misc: Add NPCM7XX MFT Module
  hw/arm: Connect PWM fans in NPCM7XX boards
  tests/qtest: Test PWM fan RPM using MFT in PWM test

 docs/system/arm/nuvoton.rst|   2 +-
 hw/arm/npcm7xx.c   |  45 ++-
 hw/arm/npcm7xx_boards.c|  99 ++
 hw/misc/meson.build|   1 +
 hw/misc/npcm7xx_mft.c  | 541 +
 hw/misc/npcm7xx_pwm.c  |   4 +
 hw/misc/trace-events   |   8 +
 include/hw/arm/npcm7xx.h   |  13 +-
 include/hw/misc/npcm7xx_mft.h  |  70 +
 include/hw/misc/npcm7xx_pwm.h  |   4 +-
 tests/qtest/npcm7xx_pwm-test.c | 205 -
 11 files changed, 975 insertions(+), 17 deletions(-)
 create mode 100644 hw/misc/npcm7xx_mft.c
 create mode 100644 include/hw/misc/npcm7xx_mft.h

-- 
2.30.1.766.gb4fecdf3b7-goog




[PATCH v3 3/5] hw/arm: Add I2C sensors and EEPROM for GSJ machine

2021-02-10 Thread Hao Wu via
Add AT24 EEPROM and temperature sensors for GSJ machine.

Reviewed-by: Doug Evans
Reviewed-by: Tyrong Ting
Signed-off-by: Hao Wu 

diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig
index be017b997a..4e6f4ffe90 100644
--- a/hw/arm/Kconfig
+++ b/hw/arm/Kconfig
@@ -370,6 +370,7 @@ config NPCM7XX
 bool
 select A9MPCORE
 select ARM_GIC
+select AT24C  # EEPROM
 select PL310  # cache controller
 select SERIAL
 select SSI
diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 47a215bd01..fbf6ce8e02 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -19,6 +19,7 @@
 #include "exec/address-spaces.h"
 #include "hw/arm/npcm7xx.h"
 #include "hw/core/cpu.h"
+#include "hw/i2c/smbus_eeprom.h"
 #include "hw/loader.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
@@ -104,6 +105,17 @@ static I2CBus *npcm7xx_i2c_get_bus(NPCM7xxState *soc, 
uint32_t num)
 return I2C_BUS(qdev_get_child_bus(DEVICE(>smbus[num]), "i2c-bus"));
 }
 
+static void at24c_eeprom_init(NPCM7xxState *soc, int bus, uint8_t addr,
+  uint32_t rsize)
+{
+I2CBus *i2c_bus = npcm7xx_i2c_get_bus(soc, bus);
+I2CSlave *i2c_dev = i2c_slave_new("at24c-eeprom", addr);
+DeviceState *dev = DEVICE(i2c_dev);
+
+qdev_prop_set_uint32(dev, "rom-size", rsize);
+i2c_slave_realize_and_unref(i2c_dev, i2c_bus, _abort);
+}
+
 static void npcm750_evb_i2c_init(NPCM7xxState *soc)
 {
 /* lm75 temperature sensor on SVB, tmp105 is compatible */
@@ -116,6 +128,20 @@ static void npcm750_evb_i2c_init(NPCM7xxState *soc)
 i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
 }
 
+static void quanta_gsj_i2c_init(NPCM7xxState *soc)
+{
+/* GSJ machine have 4 max31725 temperature sensors, tmp105 is compatible. 
*/
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), "tmp105", 0x5c);
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 2), "tmp105", 0x5c);
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 3), "tmp105", 0x5c);
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 4), "tmp105", 0x5c);
+
+at24c_eeprom_init(soc, 9, 0x55, 8192);
+at24c_eeprom_init(soc, 10, 0x55, 8192);
+
+/* TODO: Add additional i2c devices. */
+}
+
 static void npcm750_evb_init(MachineState *machine)
 {
 NPCM7xxState *soc;
@@ -141,6 +167,7 @@ static void quanta_gsj_init(MachineState *machine)
 npcm7xx_load_bootrom(machine, soc);
 npcm7xx_connect_flash(>fiu[0], 0, "mx25l25635e",
   drive_get(IF_MTD, 0, 0));
+quanta_gsj_i2c_init(soc);
 npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.30.0.478.g8a0d178c01-goog




[PATCH v3 5/5] hw/i2c: Implement NPCM7XX SMBus Module FIFO Mode

2021-02-10 Thread Hao Wu via
This patch implements the FIFO mode of the SMBus module. In FIFO, the
user transmits or receives at most 16 bytes at a time. The FIFO mode
allows the module to transmit large amount of data faster than single
byte mode.

Since we only added the device in a patch that is only a few commits
away in the same patch set. We do not increase the VMstate version
number in this special case.

Reviewed-by: Doug Evans
Reviewed-by: Tyrong Ting
Signed-off-by: Hao Wu 
Reviewed-by: Corey Minyard 
Ack-by: Corey Minyard 

diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c
index a465740623..6b2f9e1aaa 100644
--- a/hw/i2c/npcm7xx_smbus.c
+++ b/hw/i2c/npcm7xx_smbus.c
@@ -129,14 +129,45 @@ enum NPCM7xxSMBusBank1Register {
 #define NPCM7XX_ADDR_EN BIT(7)
 #define NPCM7XX_ADDR_A(rv)  extract8((rv), 0, 6)
 
+/* FIFO Mode Register Fields */
+/* FIF_CTL fields */
+#define NPCM7XX_SMBFIF_CTL_FIFO_EN  BIT(4)
+#define NPCM7XX_SMBFIF_CTL_FAIR_RDY_IE  BIT(2)
+#define NPCM7XX_SMBFIF_CTL_FAIR_RDY BIT(1)
+#define NPCM7XX_SMBFIF_CTL_FAIR_BUSYBIT(0)
+/* FIF_CTS fields */
+#define NPCM7XX_SMBFIF_CTS_STR  BIT(7)
+#define NPCM7XX_SMBFIF_CTS_CLR_FIFO BIT(6)
+#define NPCM7XX_SMBFIF_CTS_RFTE_IE  BIT(3)
+#define NPCM7XX_SMBFIF_CTS_RXF_TXE  BIT(1)
+/* TXF_CTL fields */
+#define NPCM7XX_SMBTXF_CTL_THR_TXIE BIT(6)
+#define NPCM7XX_SMBTXF_CTL_TX_THR(rv)   extract8((rv), 0, 5)
+/* T_OUT fields */
+#define NPCM7XX_SMBT_OUT_ST BIT(7)
+#define NPCM7XX_SMBT_OUT_IE BIT(6)
+#define NPCM7XX_SMBT_OUT_CLKDIV(rv) extract8((rv), 0, 6)
+/* TXF_STS fields */
+#define NPCM7XX_SMBTXF_STS_TX_THST  BIT(6)
+#define NPCM7XX_SMBTXF_STS_TX_BYTES(rv) extract8((rv), 0, 5)
+/* RXF_STS fields */
+#define NPCM7XX_SMBRXF_STS_RX_THST  BIT(6)
+#define NPCM7XX_SMBRXF_STS_RX_BYTES(rv) extract8((rv), 0, 5)
+/* RXF_CTL fields */
+#define NPCM7XX_SMBRXF_CTL_THR_RXIE BIT(6)
+#define NPCM7XX_SMBRXF_CTL_LAST BIT(5)
+#define NPCM7XX_SMBRXF_CTL_RX_THR(rv)   extract8((rv), 0, 5)
+
 #define KEEP_OLD_BIT(o, n, b)   (((n) & (~(b))) | ((o) & (b)))
 #define WRITE_ONE_CLEAR(o, n, b)((n) & (b) ? (o) & (~(b)) : (o))
 
 #define NPCM7XX_SMBUS_ENABLED(s)((s)->ctl2 & NPCM7XX_SMBCTL2_ENABLE)
+#define NPCM7XX_SMBUS_FIFO_ENABLED(s) ((s)->fif_ctl & \
+   NPCM7XX_SMBFIF_CTL_FIFO_EN)
 
 /* VERSION fields values, read-only. */
 #define NPCM7XX_SMBUS_VERSION_NUMBER 1
-#define NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED 0
+#define NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED 1
 
 /* Reset values */
 #define NPCM7XX_SMB_ST_INIT_VAL 0x00
@@ -151,6 +182,14 @@ enum NPCM7xxSMBusBank1Register {
 #define NPCM7XX_SMB_ADDR_INIT_VAL   0x00
 #define NPCM7XX_SMB_SCLLT_INIT_VAL  0x00
 #define NPCM7XX_SMB_SCLHT_INIT_VAL  0x00
+#define NPCM7XX_SMB_FIF_CTL_INIT_VAL 0x00
+#define NPCM7XX_SMB_FIF_CTS_INIT_VAL 0x00
+#define NPCM7XX_SMB_FAIR_PER_INIT_VAL 0x00
+#define NPCM7XX_SMB_TXF_CTL_INIT_VAL 0x00
+#define NPCM7XX_SMB_T_OUT_INIT_VAL 0x3f
+#define NPCM7XX_SMB_TXF_STS_INIT_VAL 0x00
+#define NPCM7XX_SMB_RXF_STS_INIT_VAL 0x00
+#define NPCM7XX_SMB_RXF_CTL_INIT_VAL 0x01
 
 static uint8_t npcm7xx_smbus_get_version(void)
 {
@@ -171,7 +210,13 @@ static void npcm7xx_smbus_update_irq(NPCM7xxSMBusState *s)
(s->ctl1 & NPCM7XX_SMBCTL1_STASTRE &&
 s->st & NPCM7XX_SMBST_SDAST) ||
(s->ctl1 & NPCM7XX_SMBCTL1_EOBINTE &&
-s->cst3 & NPCM7XX_SMBCST3_EO_BUSY));
+s->cst3 & NPCM7XX_SMBCST3_EO_BUSY) ||
+   (s->rxf_ctl & NPCM7XX_SMBRXF_CTL_THR_RXIE &&
+s->rxf_sts & NPCM7XX_SMBRXF_STS_RX_THST) ||
+   (s->txf_ctl & NPCM7XX_SMBTXF_CTL_THR_TXIE &&
+s->txf_sts & NPCM7XX_SMBTXF_STS_TX_THST) ||
+   (s->fif_cts & NPCM7XX_SMBFIF_CTS_RFTE_IE &&
+s->fif_cts & NPCM7XX_SMBFIF_CTS_RXF_TXE));
 
 if (level) {
 s->cst2 |= NPCM7XX_SMBCST2_INTSTS;
@@ -189,6 +234,13 @@ static void npcm7xx_smbus_nack(NPCM7xxSMBusState *s)
 s->status = NPCM7XX_SMBUS_STATUS_NEGACK;
 }
 
+static void npcm7xx_smbus_clear_buffer(NPCM7xxSMBusState *s)
+{
+s->fif_cts &= ~NPCM7XX_SMBFIF_CTS_RXF_TXE;
+s->txf_sts = 0;
+s->rxf_sts = 0;
+}
+
 static void npcm7xx_smbus_send_byte(NPCM7xxSMBusState *s, uint8_t value)
 {
 int rv = i2c_send(s->bus, value);
@@ -197,6 +249,15 @@ static void npcm7xx_smbus_send_byte(NPCM7xxSMBusState *s, 
uint8_t value)
 npcm7xx_smbus_nack(s);
 } else {
 s->st |= NPCM7XX_SMBST_SDAST;
+if (NPCM7XX_SMBUS_FIFO_ENABLED(s)) {
+s->fif_cts |= NPCM7XX_SMBFIF_CTS_RXF_TXE;
+if (NPCM7XX_SMBTXF_STS_TX_BYTES(s->txf_sts) ==
+NPCM7XX_SMBTXF_CTL_TX_THR(s->txf_ctl)) {
+s->txf_sts = NPCM7XX_SMBTXF_STS_TX_THST;
+} else {
+

[PATCH v3 4/5] hw/i2c: Add a QTest for NPCM7XX SMBus Device

2021-02-10 Thread Hao Wu via
This patch adds a QTest for NPCM7XX SMBus's single byte mode. It sends a
byte to a device in the evaluation board, and verify the retrieved value
is equivalent to the sent value.

Reviewed-by: Doug Evans
Reviewed-by: Tyrong Ting
Signed-off-by: Hao Wu 
Reviewed-by: Peter Maydell 

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index c83bc211b6..ba6ecaed32 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -139,6 +139,7 @@ qtests_npcm7xx = \
'npcm7xx_gpio-test',
'npcm7xx_pwm-test',
'npcm7xx_rng-test',
+   'npcm7xx_smbus-test',
'npcm7xx_timer-test',
'npcm7xx_watchdog_timer-test']
 qtests_arm = \
diff --git a/tests/qtest/npcm7xx_smbus-test.c b/tests/qtest/npcm7xx_smbus-test.c
new file mode 100644
index 00..4594b107df
--- /dev/null
+++ b/tests/qtest/npcm7xx_smbus-test.c
@@ -0,0 +1,352 @@
+/*
+ * QTests for Nuvoton NPCM7xx SMBus Modules.
+ *
+ * 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 "qemu/bitops.h"
+#include "libqos/i2c.h"
+#include "libqos/libqtest.h"
+#include "hw/misc/tmp105_regs.h"
+
+#define NR_SMBUS_DEVICES16
+#define SMBUS_ADDR(x)   (0xf008 + 0x1000 * (x))
+#define SMBUS_IRQ(x)(64 + (x))
+
+#define EVB_DEVICE_ADDR 0x48
+#define INVALID_DEVICE_ADDR 0x01
+
+const int evb_bus_list[] = {0, 1, 2, 6};
+
+/* Offsets */
+enum CommonRegister {
+OFFSET_SDA = 0x0,
+OFFSET_ST  = 0x2,
+OFFSET_CST = 0x4,
+OFFSET_CTL1= 0x6,
+OFFSET_ADDR1   = 0x8,
+OFFSET_CTL2= 0xa,
+OFFSET_ADDR2   = 0xc,
+OFFSET_CTL3= 0xe,
+OFFSET_CST2= 0x18,
+OFFSET_CST3= 0x19,
+};
+
+enum NPCM7xxSMBusBank0Register {
+OFFSET_ADDR3   = 0x10,
+OFFSET_ADDR7   = 0x11,
+OFFSET_ADDR4   = 0x12,
+OFFSET_ADDR8   = 0x13,
+OFFSET_ADDR5   = 0x14,
+OFFSET_ADDR9   = 0x15,
+OFFSET_ADDR6   = 0x16,
+OFFSET_ADDR10  = 0x17,
+OFFSET_CTL4= 0x1a,
+OFFSET_CTL5= 0x1b,
+OFFSET_SCLLT   = 0x1c,
+OFFSET_FIF_CTL = 0x1d,
+OFFSET_SCLHT   = 0x1e,
+};
+
+enum NPCM7xxSMBusBank1Register {
+OFFSET_FIF_CTS  = 0x10,
+OFFSET_FAIR_PER = 0x11,
+OFFSET_TXF_CTL  = 0x12,
+OFFSET_T_OUT= 0x14,
+OFFSET_TXF_STS  = 0x1a,
+OFFSET_RXF_STS  = 0x1c,
+OFFSET_RXF_CTL  = 0x1e,
+};
+
+/* ST fields */
+#define ST_STP  BIT(7)
+#define ST_SDASTBIT(6)
+#define ST_BER  BIT(5)
+#define ST_NEGACK   BIT(4)
+#define ST_STASTR   BIT(3)
+#define ST_NMATCH   BIT(2)
+#define ST_MODE BIT(1)
+#define ST_XMIT BIT(0)
+
+/* CST fields */
+#define CST_ARPMATCHBIT(7)
+#define CST_MATCHAF BIT(6)
+#define CST_TGSCL   BIT(5)
+#define CST_TSDABIT(4)
+#define CST_GCMATCH BIT(3)
+#define CST_MATCH   BIT(2)
+#define CST_BB  BIT(1)
+#define CST_BUSYBIT(0)
+
+/* CST2 fields */
+#define CST2_INSTTS BIT(7)
+#define CST2_MATCH7FBIT(6)
+#define CST2_MATCH6FBIT(5)
+#define CST2_MATCH5FBIT(4)
+#define CST2_MATCH4FBIT(3)
+#define CST2_MATCH3FBIT(2)
+#define CST2_MATCH2FBIT(1)
+#define CST2_MATCH1FBIT(0)
+
+/* CST3 fields */
+#define CST3_EO_BUSYBIT(7)
+#define CST3_MATCH10F   BIT(2)
+#define CST3_MATCH9FBIT(1)
+#define CST3_MATCH8FBIT(0)
+
+/* CTL1 fields */
+#define CTL1_STASTREBIT(7)
+#define CTL1_NMINTE BIT(6)
+#define CTL1_GCMEN  BIT(5)
+#define CTL1_ACKBIT(4)
+#define CTL1_EOBINTEBIT(3)
+#define CTL1_INTEN  BIT(2)
+#define CTL1_STOP   BIT(1)
+#define CTL1_START  BIT(0)
+
+/* CTL2 fields */
+#define CTL2_SCLFRQ(rv) extract8((rv), 1, 6)
+#define CTL2_ENABLE BIT(0)
+
+/* CTL3 fields */
+#define CTL3_SCL_LVLBIT(7)
+#define CTL3_SDA_LVLBIT(6)
+#define CTL3_BNK_SELBIT(5)
+#define CTL3_400K_MODE  BIT(4)
+#define CTL3_IDL_START  BIT(3)
+#define CTL3_ARPMEN BIT(2)
+#define CTL3_SCLFRQ(rv) extract8((rv), 0, 2)
+
+/* ADDR fields */
+#define ADDR_EN BIT(7)
+#define ADDR_A(rv)  extract8((rv), 0, 6)
+
+
+static void check_running(QTestState *qts, uint64_t base_addr)
+{
+g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BUSY);
+g_assert_true(qtest_readb(qts, base_addr + OFFSET_CST) & CST_BB);
+}
+
+static void check_stopped(QTestState *qts, uint64_t base_addr)
+{
+   

[PATCH v3 1/5] hw/i2c: Implement NPCM7XX SMBus Module Single Mode

2021-02-10 Thread Hao Wu via
This commit implements the single-byte mode of the SMBus.

Each Nuvoton SoC has 16 System Management Bus (SMBus). These buses
compliant with SMBus and I2C protocol.

This patch implements the single-byte mode of the SMBus. In this mode,
the user sends or receives a byte each time. The SMBus device transmits
it to the underlying i2c device and sends an interrupt back to the QEMU
guest.

Reviewed-by: Doug Evans
Reviewed-by: Tyrong Ting
Signed-off-by: Hao Wu 
Reviewed-by: Corey Minyard 
Ack-by: Corey Minyard 

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index a1786342e2..34fc799b2d 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -43,6 +43,7 @@ Supported devices
  * GPIO controller
  * Analog to Digital Converter (ADC)
  * Pulse Width Modulation (PWM)
+ * SMBus controller (SMBF)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
 
  * Ethernet controllers (GMAC and EMC)
  * USB device (USBD)
- * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index d1fe9bd1df..f8950f9470 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,22 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_SMBUS0_IRQ  = 64,
+NPCM7XX_SMBUS1_IRQ,
+NPCM7XX_SMBUS2_IRQ,
+NPCM7XX_SMBUS3_IRQ,
+NPCM7XX_SMBUS4_IRQ,
+NPCM7XX_SMBUS5_IRQ,
+NPCM7XX_SMBUS6_IRQ,
+NPCM7XX_SMBUS7_IRQ,
+NPCM7XX_SMBUS8_IRQ,
+NPCM7XX_SMBUS9_IRQ,
+NPCM7XX_SMBUS10_IRQ,
+NPCM7XX_SMBUS11_IRQ,
+NPCM7XX_SMBUS12_IRQ,
+NPCM7XX_SMBUS13_IRQ,
+NPCM7XX_SMBUS14_IRQ,
+NPCM7XX_SMBUS15_IRQ,
 NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
 NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
@@ -152,6 +168,26 @@ static const hwaddr npcm7xx_pwm_addr[] = {
 0xf0104000,
 };
 
+/* Direct memory-mapped access to each SMBus Module. */
+static const hwaddr npcm7xx_smbus_addr[] = {
+0xf008,
+0xf0081000,
+0xf0082000,
+0xf0083000,
+0xf0084000,
+0xf0085000,
+0xf0086000,
+0xf0087000,
+0xf0088000,
+0xf0089000,
+0xf008a000,
+0xf008b000,
+0xf008c000,
+0xf008d000,
+0xf008e000,
+0xf008f000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +389,11 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, "gpio[*]", >gpio[i], 
TYPE_NPCM7XX_GPIO);
 }
 
+for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
+object_initialize_child(obj, "smbus[*]", >smbus[i],
+TYPE_NPCM7XX_SMBUS);
+}
+
 object_initialize_child(obj, "ehci", >ehci, TYPE_NPCM7XX_EHCI);
 object_initialize_child(obj, "ohci", >ohci, TYPE_SYSBUS_OHCI);
 
@@ -509,6 +550,17 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
npcm7xx_irq(s, NPCM7XX_GPIO0_IRQ + i));
 }
 
+/* SMBus modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_smbus_addr) != ARRAY_SIZE(s->smbus));
+for (i = 0; i < ARRAY_SIZE(s->smbus); i++) {
+Object *obj = OBJECT(>smbus[i]);
+
+sysbus_realize(SYS_BUS_DEVICE(obj), _abort);
+sysbus_mmio_map(SYS_BUS_DEVICE(obj), 0, npcm7xx_smbus_addr[i]);
+sysbus_connect_irq(SYS_BUS_DEVICE(obj), 0,
+   npcm7xx_irq(s, NPCM7XX_SMBUS0_IRQ + i));
+}
+
 /* USB Host */
 object_property_set_bool(OBJECT(>ehci), "companion-enable", true,
  _abort);
@@ -576,22 +628,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.pcierc",   0xe100,  64 * KiB);
 create_unimplemented_device("npcm7xx.kcs",  0xf0007000,   4 * KiB);
 create_unimplemented_device("npcm7xx.gfxi", 0xf000e000,   4 * KiB);
-create_unimplemented_device("npcm7xx.smbus[0]", 0xf008,   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);
-

[PATCH v3 2/5] hw/arm: Add I2C sensors for NPCM750 eval board

2021-02-10 Thread Hao Wu via
Add I2C temperature sensors for NPCM750 eval board.

Reviewed-by: Doug Evans
Reviewed-by: Tyrong Ting
Signed-off-by: Hao Wu 
Reviewed-by: Peter Maydell 

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 3fdd5cab01..47a215bd01 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -98,6 +98,24 @@ static NPCM7xxState *npcm7xx_create_soc(MachineState 
*machine,
 return NPCM7XX(obj);
 }
 
+static I2CBus *npcm7xx_i2c_get_bus(NPCM7xxState *soc, uint32_t num)
+{
+g_assert(num < ARRAY_SIZE(soc->smbus));
+return I2C_BUS(qdev_get_child_bus(DEVICE(>smbus[num]), "i2c-bus"));
+}
+
+static void npcm750_evb_i2c_init(NPCM7xxState *soc)
+{
+/* lm75 temperature sensor on SVB, tmp105 is compatible */
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 0), "tmp105", 0x48);
+/* lm75 temperature sensor on EB, tmp105 is compatible */
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 1), "tmp105", 0x48);
+/* tmp100 temperature sensor on EB, tmp105 is compatible */
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 2), "tmp105", 0x48);
+/* tmp100 temperature sensor on SVB, tmp105 is compatible */
+i2c_slave_create_simple(npcm7xx_i2c_get_bus(soc, 6), "tmp105", 0x48);
+}
+
 static void npcm750_evb_init(MachineState *machine)
 {
 NPCM7xxState *soc;
@@ -108,6 +126,7 @@ static void npcm750_evb_init(MachineState *machine)
 
 npcm7xx_load_bootrom(machine, soc);
 npcm7xx_connect_flash(>fiu[0], 0, "w25q256", drive_get(IF_MTD, 0, 0));
+npcm750_evb_i2c_init(soc);
 npcm7xx_load_kernel(machine, soc);
 }
 
-- 
2.30.0.478.g8a0d178c01-goog




[PATCH v3 0/5] hw/i2c: Add NPCM7XX SMBus Device

2021-02-10 Thread Hao Wu via
This patch set implements the System manager bus (SMBus) module in NPCM7XX
SoC. Basically, it emulates the data transactions of the module, not the
SDA/SCL levels. We have also added a QTest which contains read and write
operations for both single-byte and FIFO mode, and added basic I2C devices
for npcm750-evb and quanta-gsj boards.

Changes since v2:
- Remove first patch (GPIO) since it's already applied to target-arm.next
- Rename NPCM7XX_SMBUS_FIFO_EN to NPCM7XX_SMBUS_VERSION_FIFO_SUPPORTED
  to indicate it's a register field of the VERSION reg.
- Add select AT24C in hw/arm/Kconfig.
- Minor additional commit messages and comments to clarify things.

Changes since v1:
- Fix errors for i2c device addresses for temperature sensors in GSJ machine
- Use at24c device to emulate GSJ EEPROM. It supports more than 256 bytes.
- Fill in VMState in npcm7xx_smbus.c
- Change commit message in patch 3 and 4
- Fix order in npcm7xx.c IRQ list
- Add a few extra comments to make things clearer

Hao Wu (5):
  hw/i2c: Implement NPCM7XX SMBus Module Single Mode
  hw/arm: Add I2C sensors for NPCM750 eval board
  hw/arm: Add I2C sensors and EEPROM for GSJ machine
  hw/i2c: Add a QTest for NPCM7XX SMBus Device
  hw/i2c: Implement NPCM7XX SMBus Module FIFO Mode

 docs/system/arm/nuvoton.rst  |2 +-
 hw/arm/Kconfig   |1 +
 hw/arm/npcm7xx.c |   68 +-
 hw/arm/npcm7xx_boards.c  |   46 ++
 hw/i2c/meson.build   |1 +
 hw/i2c/npcm7xx_smbus.c   | 1099 ++
 hw/i2c/trace-events  |   12 +
 include/hw/arm/npcm7xx.h |2 +
 include/hw/i2c/npcm7xx_smbus.h   |  113 +++
 tests/qtest/meson.build  |1 +
 tests/qtest/npcm7xx_smbus-test.c |  495 ++
 11 files changed, 1823 insertions(+), 17 deletions(-)
 create mode 100644 hw/i2c/npcm7xx_smbus.c
 create mode 100644 include/hw/i2c/npcm7xx_smbus.h
 create mode 100644 tests/qtest/npcm7xx_smbus-test.c

-- 
2.30.0.478.g8a0d178c01-goog




Re: [PATCH] qtest/npcm7xx_pwm-test: Fix memleak in pwm_qom_get

2021-01-15 Thread Hao Wu via
On Fri, Jan 15, 2021 at 9:17 AM Havard Skinnemoen 
wrote:

> +Hao Wu
>
> On Fri, Jan 15, 2021 at 1:15 AM Philippe Mathieu-Daudé 
> wrote:
> >
> > On 1/15/21 8:56 AM, Gan Qixin wrote:
> > > The pwm_qom_get function didn't free "response", which caused an
> indirect
> > > memory leak. So use qobject_unref() to fix it.
> > >
> > > ASAN shows memory leak stack:
> > >
> > > Indirect leak of 7416 byte(s) in 18000 object(s) allocated from:
> > > #0 0x7f96e2f79d4e in __interceptor_calloc
> (/lib64/libasan.so.5+0x112d4e)
> > > #1 0x7f96e2d98a50 in g_malloc0 (/lib64/libglib-2.0.so.0+0x55a50)
> > > #2 0x556313112180 in qdict_new ../qobject/qdict.c:30
> > > #3 0x556313115bca in parse_object ../qobject/json-parser.c:318
> > > #4 0x556313117810 in parse_value ../qobject/json-parser.c:546
> > > #5 0x556313117bda in json_parser_parse ../qobject/json-parser.c:580
> > > #6 0x55631310fe67 in json_message_process_token
> ../qobject/json-streamer.c:92
> > > #7 0x5563131210b7 in json_lexer_feed_char
> ../qobject/json-lexer.c:313
> > > #8 0x556313121662 in json_lexer_feed ../qobject/json-lexer.c:350
> > > #9 0x5563131101e9 in json_message_parser_feed
> ../qobject/json-streamer.c:121
> > > #10 0x5563130cb81e in qmp_fd_receive ../tests/qtest/libqtest.c:614
> > > #11 0x5563130cba2b in qtest_qmp_receive_dict
> ../tests/qtest/libqtest.c:636
> > > #12 0x5563130cb939 in qtest_qmp_receive
> ../tests/qtest/libqtest.c:624
> > > #13 0x5563130cbe0d in qtest_vqmp ../tests/qtest/libqtest.c:715
> > > #14 0x5563130cc40f in qtest_qmp ../tests/qtest/libqtest.c:756
> > > #15 0x5563130c5623 in pwm_qom_get
> ../tests/qtest/npcm7xx_pwm-test.c:180
> > > #16 0x5563130c595e in pwm_get_duty
> ../tests/qtest/npcm7xx_pwm-test.c:210
> > > #17 0x5563130c7529 in test_toggle
> ../tests/qtest/npcm7xx_pwm-test.c:447
> > >
> > > Reported-by: Euler Robot 
> > > Signed-off-by: Gan Qixin 
> > > ---
> > > Cc: Havard Skinnemoen 
> > > Cc: Tyrone Ting 
> > > Cc: Thomas Huth 
> > > Cc: Laurent Vivier 
> > > ---
> > >  tests/qtest/npcm7xx_pwm-test.c | 5 -
> > >  1 file changed, 4 insertions(+), 1 deletion(-)
> >
> > Reviewed-by: Philippe Mathieu-Daudé 
>
> Reviewed-by: Havard Skinnemoen 
>
Reviewed-by: Hao Wu 

Thank you for finding this out!

>
> Thanks!
>


Re: [PATCHv4 1/2] hw: gpio: implement gpio-pwr driver for qemu reset/poweroff

2021-01-12 Thread Hao Wu via
On Tue, Jan 12, 2021 at 6:36 AM Maxim Uvarov 
wrote:

> Implement gpio-pwr driver to allow reboot and poweroff machine.
> This is simple driver with just 2 gpios lines. Current use case
> is to reboot and poweroff virt machine in secure mode. Secure
> pl066 gpio chip is needed for that.
>
> Signed-off-by: Maxim Uvarov 
>
Reviewed-by: Hao Wu 

> ---
>  hw/gpio/Kconfig |  3 ++
>  hw/gpio/gpio_pwr.c  | 70 +
>  hw/gpio/meson.build |  1 +
>  3 files changed, 74 insertions(+)
>  create mode 100644 hw/gpio/gpio_pwr.c
>
> diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig
> index b6fdaa2586..f0e7405f6e 100644
> --- a/hw/gpio/Kconfig
> +++ b/hw/gpio/Kconfig
> @@ -8,5 +8,8 @@ config PL061
>  config GPIO_KEY
>  bool
>
> +config GPIO_PWR
> +bool
> +
>  config SIFIVE_GPIO
>  bool
> diff --git a/hw/gpio/gpio_pwr.c b/hw/gpio/gpio_pwr.c
> new file mode 100644
> index 00..8ed8d5d24f
> --- /dev/null
> +++ b/hw/gpio/gpio_pwr.c
> @@ -0,0 +1,70 @@
> +/*
> + * GPIO qemu power controller
> + *
> + * Copyright (c) 2020 Linaro Limited
> + *
> + * Author: Maxim Uvarov 
> + *
> + * Virtual gpio driver which can be used on top of pl061
> + * to reboot and shutdown qemu virtual machine. One of use
> + * case is gpio driver for secure world application (ARM
> + * Trusted Firmware.).
> + *
> + * This work is licensed under the terms of the GNU GPL, version 2 or
> later.
> + * See the COPYING file in the top-level directory.
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + */
> +
> +/*
> + * QEMU interface:
> + * two named input GPIO lines:
> + *   'reset' : when asserted, trigger system reset
> + *   'shutdown' : when asserted, trigger system shutdown
> + */
> +
> +#include "qemu/osdep.h"
> +#include "hw/sysbus.h"
> +#include "sysemu/runstate.h"
> +
> +#define TYPE_GPIOPWR "gpio-pwr"
> +OBJECT_DECLARE_SIMPLE_TYPE(GPIO_PWR_State, GPIOPWR)
> +
> +struct GPIO_PWR_State {
> +SysBusDevice parent_obj;
> +};
> +
> +static void gpio_pwr_reset(void *opaque, int n, int level)
> +{
> +if (!level) {
> +qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
> +}
> +}
> +
> +static void gpio_pwr_shutdown(void *opaque, int n, int level)
> +{
> +if (!level) {
> +qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN);
> +}
> +}
> +
> +static void gpio_pwr_init(Object *obj)
> +{
> +DeviceState *dev = DEVICE(obj);
> +
> +qdev_init_gpio_in_named(dev, gpio_pwr_reset, "reset", 1);
> +qdev_init_gpio_in_named(dev, gpio_pwr_shutdown, "shutdown", 1);
> +}
> +
> +static const TypeInfo gpio_pwr_info = {
> +.name  = TYPE_GPIOPWR,
> +.parent= TYPE_SYS_BUS_DEVICE,
> +.instance_size = sizeof(GPIO_PWR_State),
> +.instance_init = gpio_pwr_init,
> +};
> +
> +static void gpio_pwr_register_types(void)
> +{
> +type_register_static(_pwr_info);
> +}
> +
> +type_init(gpio_pwr_register_types)
> diff --git a/hw/gpio/meson.build b/hw/gpio/meson.build
> index 5c0a7d7b95..79568f00ce 100644
> --- a/hw/gpio/meson.build
> +++ b/hw/gpio/meson.build
> @@ -1,5 +1,6 @@
>  softmmu_ss.add(when: 'CONFIG_E500', if_true: files('mpc8xxx.c'))
>  softmmu_ss.add(when: 'CONFIG_GPIO_KEY', if_true: files('gpio_key.c'))
> +softmmu_ss.add(when: 'CONFIG_GPIO_PWR', if_true: files('gpio_pwr.c'))
>  softmmu_ss.add(when: 'CONFIG_MAX7310', if_true: files('max7310.c'))
>  softmmu_ss.add(when: 'CONFIG_PL061', if_true: files('pl061.c'))
>  softmmu_ss.add(when: 'CONFIG_PUV3', if_true: files('puv3_gpio.c'))
> --
> 2.17.1
>
>
>


[PATCH v5 4/6] hw/misc: Add a PWM module for NPCM7XX

2021-01-08 Thread Hao Wu via
The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
identical PWM modules. Each module contains 4 PWM entries. Each PWM has
two outputs: frequency and duty_cycle. Both are computed using inputs
from software side.

This module does not model detail pulse signals since it is expensive.
It also does not model interrupts and watchdogs that are dependant on
the detail models. The interfaces for these are left in the module so
that anyone in need for these functionalities can implement on their
own.

The user can read the duty cycle and frequency using qom-get command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst   |   2 +-
 hw/arm/npcm7xx.c  |  26 +-
 hw/misc/meson.build   |   1 +
 hw/misc/npcm7xx_pwm.c | 550 ++
 hw/misc/trace-events  |   6 +
 include/hw/arm/npcm7xx.h  |   2 +
 include/hw/misc/npcm7xx_pwm.h | 105 +++
 7 files changed, 689 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/misc/npcm7xx_pwm.h

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 35829f8d0b..a1786342e2 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -42,6 +42,7 @@ Supported devices
  * USB host (USBH)
  * GPIO controller
  * Analog to Digital Converter (ADC)
+ * Pulse Width Modulation (PWM)
 
 Missing devices
 ---
@@ -61,7 +62,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Pulse Width Modulation (PWM)
  * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index b22a8c966d..72040d4079 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
+NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
 0xb800, /* CS3 */
 };
 
+/* Register base address for each PWM Module */
+static const hwaddr npcm7xx_pwm_addr[] = {
+0xf0103000,
+0xf0104000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
 TYPE_NPCM7XX_FIU);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
 
+/* PWM Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
+
+qdev_connect_clock_in(DEVICE(>pwm[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "apb3-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
+sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 * KiB);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ce15ffceb9..607cd38a21 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -64,6 +64,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: 
files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
diff --git a/hw/misc/npcm7xx_pwm.c 

[PATCH v5 5/6] hw/misc: Add QTest for NPCM7XX PWM Module

2021-01-08 Thread Hao Wu via
We add a qtest for the PWM in the previous patch. It proves it works as
expected.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
Reviewed-by: Peter Maydell 
---
 tests/qtest/meson.build|   1 +
 tests/qtest/npcm7xx_pwm-test.c | 490 +
 2 files changed, 491 insertions(+)
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 955710d1c5..0b5467f084 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -136,6 +136,7 @@ qtests_sparc64 = \
 qtests_npcm7xx = \
   ['npcm7xx_adc-test',
'npcm7xx_gpio-test',
+   'npcm7xx_pwm-test',
'npcm7xx_rng-test',
'npcm7xx_timer-test',
'npcm7xx_watchdog_timer-test']
diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
new file mode 100644
index 00..33fbdf5f54
--- /dev/null
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -0,0 +1,490 @@
+/*
+ * QTests for Nuvoton NPCM7xx PWM Modules.
+ *
+ * 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 "qemu/bitops.h"
+#include "libqos/libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+
+#define REF_HZ  2500
+
+/* Register field definitions. */
+#define CH_EN   BIT(0)
+#define CH_INV  BIT(2)
+#define CH_MOD  BIT(3)
+
+/* Registers shared between all PWMs in a module */
+#define PPR 0x00
+#define CSR 0x04
+#define PCR 0x08
+#define PIER0x3c
+#define PIIR0x40
+
+/* CLK module related */
+#define CLK_BA  0xf0801000
+#define CLKSEL  0x04
+#define CLKDIV1 0x08
+#define CLKDIV2 0x2c
+#define PLLCON0 0x0c
+#define PLLCON1 0x10
+#define PLL_INDV(rv)extract32((rv), 0, 6)
+#define PLL_FBDV(rv)extract32((rv), 16, 12)
+#define PLL_OTDV1(rv)   extract32((rv), 8, 3)
+#define PLL_OTDV2(rv)   extract32((rv), 13, 3)
+#define APB3CKDIV(rv)   extract32((rv), 28, 2)
+#define CLK2CKDIV(rv)   extract32((rv), 0, 1)
+#define CLK4CKDIV(rv)   extract32((rv), 26, 2)
+#define CPUCKSEL(rv)extract32((rv), 0, 2)
+
+#define MAX_DUTY100
+
+typedef struct PWMModule {
+int irq;
+uint64_t base_addr;
+} PWMModule;
+
+typedef struct PWM {
+uint32_t cnr_offset;
+uint32_t cmr_offset;
+uint32_t pdr_offset;
+uint32_t pwdr_offset;
+} PWM;
+
+typedef struct TestData {
+const PWMModule *module;
+const PWM *pwm;
+} TestData;
+
+static const PWMModule pwm_module_list[] = {
+{
+.irq= 93,
+.base_addr  = 0xf0103000
+},
+{
+.irq= 94,
+.base_addr  = 0xf0104000
+}
+};
+
+static const PWM pwm_list[] = {
+{
+.cnr_offset = 0x0c,
+.cmr_offset = 0x10,
+.pdr_offset = 0x14,
+.pwdr_offset= 0x44,
+},
+{
+.cnr_offset = 0x18,
+.cmr_offset = 0x1c,
+.pdr_offset = 0x20,
+.pwdr_offset= 0x48,
+},
+{
+.cnr_offset = 0x24,
+.cmr_offset = 0x28,
+.pdr_offset = 0x2c,
+.pwdr_offset= 0x4c,
+},
+{
+.cnr_offset = 0x30,
+.cmr_offset = 0x34,
+.pdr_offset = 0x38,
+.pwdr_offset= 0x50,
+},
+};
+
+static const int ppr_base[] = { 0, 0, 8, 8 };
+static const int csr_base[] = { 0, 4, 8, 12 };
+static const int pcr_base[] = { 0, 8, 12, 16 };
+
+static const uint32_t ppr_list[] = {
+0,
+1,
+10,
+100,
+255, /* Max possible value. */
+};
+
+static const uint32_t csr_list[] = {
+0,
+1,
+2,
+3,
+4, /* Max possible value. */
+};
+
+static const uint32_t cnr_list[] = {
+0,
+1,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+static const uint32_t cmr_list[] = {
+0,
+1,
+10,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+/* Returns the index of the PWM module. */
+static int pwm_module_index(const PWMModule *module)
+{
+ptrdiff_t diff = module - pwm_module_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
+
+return diff;
+}
+
+/* Returns the index of the PWM entry. */
+static int pwm_index(const PWM *pwm)
+{
+ptrdiff_t diff = pwm - pwm_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
+
+

[PATCH v5 6/6] hw/*: Use type casting for SysBusDevice in NPCM7XX

2021-01-08 Thread Hao Wu via
A device shouldn't access its parent object which is QOM internal.
Instead it should use type cast for this purporse. This patch fixes this
issue for all NPCM7XX Devices.

Signed-off-by: Hao Wu 
Reviewed-by: Peter Maydell 
---
 hw/arm/npcm7xx_boards.c | 2 +-
 hw/mem/npcm7xx_mc.c | 2 +-
 hw/misc/npcm7xx_clk.c   | 2 +-
 hw/misc/npcm7xx_gcr.c   | 2 +-
 hw/misc/npcm7xx_rng.c   | 2 +-
 hw/nvram/npcm7xx_otp.c  | 2 +-
 hw/ssi/npcm7xx_fiu.c| 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 306260fa67..3fdd5cab01 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -82,7 +82,7 @@ static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
 uint32_t hw_straps)
 {
 NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
-MachineClass *mc = >parent;
+MachineClass *mc = MACHINE_CLASS(nmc);
 Object *obj;
 
 if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
index 0435d06ab4..abc5af5620 100644
--- a/hw/mem/npcm7xx_mc.c
+++ b/hw/mem/npcm7xx_mc.c
@@ -62,7 +62,7 @@ static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
 
 memory_region_init_io(>mmio, OBJECT(s), _mc_ops, s, "regs",
   NPCM7XX_MC_REGS_SIZE);
-sysbus_init_mmio(>parent, >mmio);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >mmio);
 }
 
 static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 48bc9bdda5..0bcae9ce95 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -913,7 +913,7 @@ static void npcm7xx_clk_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _clk_ops, s,
   TYPE_NPCM7XX_CLK, 4 * KiB);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static int npcm7xx_clk_post_load(void *opaque, int version_id)
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
index 745f690809..eace9e1967 100644
--- a/hw/misc/npcm7xx_gcr.c
+++ b/hw/misc/npcm7xx_gcr.c
@@ -220,7 +220,7 @@ static void npcm7xx_gcr_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _gcr_ops, s,
   TYPE_NPCM7XX_GCR, 4 * KiB);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static const VMStateDescription vmstate_npcm7xx_gcr = {
diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c
index f650f3401f..b01df7cdb2 100644
--- a/hw/misc/npcm7xx_rng.c
+++ b/hw/misc/npcm7xx_rng.c
@@ -143,7 +143,7 @@ static void npcm7xx_rng_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _rng_ops, s, "regs",
   NPCM7XX_RNG_REGS_SIZE);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static const VMStateDescription vmstate_npcm7xx_rng = {
diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
index b16ca530ba..c61f2fc1aa 100644
--- a/hw/nvram/npcm7xx_otp.c
+++ b/hw/nvram/npcm7xx_otp.c
@@ -371,7 +371,7 @@ static void npcm7xx_otp_realize(DeviceState *dev, Error 
**errp)
 {
 NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
 NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
-SysBusDevice *sbd = >parent;
+SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 
 memset(s->array, 0, sizeof(s->array));
 
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
index 5040132b07..4eedb2927e 100644
--- a/hw/ssi/npcm7xx_fiu.c
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -498,7 +498,7 @@ static void npcm7xx_fiu_hold_reset(Object *obj)
 static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
 {
 NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
-SysBusDevice *sbd = >parent;
+SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 int i;
 
 if (s->cs_count <= 0) {
-- 
2.29.2.729.g45daf8777d-goog




[PATCH v5 3/6] hw/adc: Add an ADC module for NPCM7XX

2021-01-08 Thread Hao Wu via
The ADC is part of NPCM7XX Module. Its behavior is controled by the
ADC_CON register. It converts one of the eight analog inputs into a
digital input and stores it in the ADC_DATA register when enabled.

Users can alter input value by using qom-set QMP command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/adc/meson.build |   1 +
 hw/adc/npcm7xx_adc.c   | 301 ++
 hw/adc/trace-events|   5 +
 hw/arm/npcm7xx.c   |  24 ++-
 include/hw/adc/npcm7xx_adc.h   |  69 ++
 include/hw/arm/npcm7xx.h   |   2 +
 meson.build|   1 +
 tests/qtest/meson.build|   3 +-
 tests/qtest/npcm7xx_adc-test.c | 377 +
 10 files changed, 782 insertions(+), 3 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index b00d405d52..35829f8d0b 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -41,6 +41,7 @@ Supported devices
  * Random Number Generator (RNG)
  * USB host (USBH)
  * GPIO controller
+ * Analog to Digital Converter (ADC)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
  * USB device (USBD)
  * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
- * Analog to Digital Converter (ADC)
  * SD/MMC host
  * PECI interface
  * Pulse Width Modulation (PWM)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index 0d62ae96ae..6ddee23813 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
new file mode 100644
index 00..870a6d50c2
--- /dev/null
+++ b/hw/adc/npcm7xx_adc.c
@@ -0,0 +1,301 @@
+/*
+ * Nuvoton NPCM7xx ADC Module
+ *
+ * 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/adc/npcm7xx_adc.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/registerfields.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+REG32(NPCM7XX_ADC_CON, 0x0)
+REG32(NPCM7XX_ADC_DATA, 0x4)
+
+/* Register field definitions. */
+#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
+#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
+#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
+#define NPCM7XX_ADC_CON_INT BIT(18)
+#define NPCM7XX_ADC_CON_EN  BIT(17)
+#define NPCM7XX_ADC_CON_RST BIT(16)
+#define NPCM7XX_ADC_CON_CONVBIT(14)
+#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
+
+#define NPCM7XX_ADC_MAX_RESULT  1023
+#define NPCM7XX_ADC_DEFAULT_IREF200
+#define NPCM7XX_ADC_CONV_CYCLES 20
+#define NPCM7XX_ADC_RESET_CYCLES10
+#define NPCM7XX_ADC_R0_INPUT50
+#define NPCM7XX_ADC_R1_INPUT150
+
+static void npcm7xx_adc_reset(NPCM7xxADCState *s)
+{
+timer_del(>conv_timer);
+s->con = 0x000c0001;
+s->data = 0x;
+}
+
+static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
+{
+uint32_t result;
+
+result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
+if (result > NPCM7XX_ADC_MAX_RESULT) {
+result = NPCM7XX_ADC_MAX_RESULT;
+}
+
+return result;
+}
+
+static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
+{
+return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
+}
+
+static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
+uint32_t cycles, uint32_t prescaler)
+{
+int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t ticks = cycles;
+int64_t ns;
+
+ticks *= prescaler;
+ns = clock_ticks_to_ns(clk, ticks);
+ns += now;
+timer_mod(timer, ns);
+}
+
+static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >conv_timer, NPCM7XX_ADC_CONV_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_convert_done(void *opaque)
+{
+NPCM7xxADCState *s = opaque;
+uint32_t input = NPCM7XX_ADC_CON_MUX(s->con);
+uint32_t ref = (s->con & NPCM7XX_ADC_CON_REFSEL)
+

[PATCH v5 1/6] hw/misc: Add clock converter in NPCM7XX CLK module

2021-01-08 Thread Hao Wu via
This patch allows NPCM7XX CLK module to compute clocks that are used by
other NPCM7XX modules.

Add a new struct NPCM7xxClockConverterState which represents a
single converter.  Each clock converter in CLK module represents one
converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
takes one or more input clocks and converts them into one output clock.
They form a clock hierarchy in the CLK module and are responsible for
outputing clocks for various other modules in an NPCM7XX SoC.

Each converter has a function pointer called "convert" which represents
the unique logic for that converter.

The clock contains two initialization information: ConverterInitInfo and
ConverterConnectionInfo. They represent the vertices and edges in the
clock diagram respectively.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
Reviewed-by: Peter Maydell 
---
 hw/misc/npcm7xx_clk.c | 795 +-
 include/hw/misc/npcm7xx_clk.h | 140 +-
 2 files changed, 927 insertions(+), 8 deletions(-)

diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 6732437fe2..48bc9bdda5 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -18,6 +18,7 @@
 
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -27,9 +28,22 @@
 #include "trace.h"
 #include "sysemu/watchdog.h"
 
+/*
+ * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
+ * is always 25 MHz.
+ */
+#define NPCM7XX_CLOCK_REF_HZ(2500)
+
+/* Register Field Definitions */
+#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
+
 #define PLLCON_LOKI BIT(31)
 #define PLLCON_LOKS BIT(30)
 #define PLLCON_PWDENBIT(12)
+#define PLLCON_FBDV(con) extract32((con), 16, 12)
+#define PLLCON_OTDV2(con) extract32((con), 13, 3)
+#define PLLCON_OTDV1(con) extract32((con), 8, 3)
+#define PLLCON_INDV(con) extract32((con), 0, 6)
 
 enum NPCM7xxCLKRegisters {
 NPCM7XX_CLK_CLKEN1,
@@ -89,12 +103,609 @@ static const uint32_t 
cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
 [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
 };
 
-/* Register Field Definitions */
-#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
-
 /* The number of watchdogs that can trigger a reset. */
 #define NPCM7XX_NR_WATCHDOGS(3)
 
+/* Clock converter functions */
+
+#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
+#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
+(obj), TYPE_NPCM7XX_CLOCK_PLL)
+#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
+#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
+(obj), TYPE_NPCM7XX_CLOCK_SEL)
+#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
+#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
+(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
+
+static void npcm7xx_clk_update_pll(void *opaque)
+{
+NPCM7xxClockPLLState *s = opaque;
+uint32_t con = s->clk->regs[s->reg];
+uint64_t freq;
+
+/* The PLL is grounded if it is not locked yet. */
+if (con & PLLCON_LOKI) {
+freq = clock_get_hz(s->clock_in);
+freq *= PLLCON_FBDV(con);
+freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
+} else {
+freq = 0;
+}
+
+clock_update_hz(s->clock_out, freq);
+}
+
+static void npcm7xx_clk_update_sel(void *opaque)
+{
+NPCM7xxClockSELState *s = opaque;
+uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
+s->len);
+
+if (index >= s->input_size) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: SEL index: %u out of range\n",
+  __func__, index);
+index = 0;
+}
+clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
+}
+
+static void npcm7xx_clk_update_divider(void *opaque)
+{
+NPCM7xxClockDividerState *s = opaque;
+uint32_t freq;
+
+freq = s->divide(s);
+clock_update_hz(s->clock_out, freq);
+}
+
+static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) / s->divisor;
+}
+
+static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) /
+(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
+}
+
+static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
+{
+return divide_by_reg_divisor(s) / 2;
+}
+
+static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) >>
+extract32(s->clk->regs[s->reg], s->offset, s->len);
+}
+
+static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
+{
+switch (reg) {
+case NPCM7XX_CLK_PLLCON0:
+return NPCM7XX_CLOCK_PLL0;
+case NPCM7XX_CLK_PLLCON1:
+return NPCM7XX_CLOCK_PLL1;
+case 

[PATCH v5 2/6] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2021-01-08 Thread Hao Wu via
This patch makes NPCM7XX Timer to use a the timer clock generated by the
CLK module instead of the magic number TIMER_REF_HZ.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx.c |  5 
 hw/timer/npcm7xx_timer.c | 39 +++-
 include/hw/misc/npcm7xx_clk.h|  6 -
 include/hw/timer/npcm7xx_timer.h |  1 +
 4 files changed, 24 insertions(+), 27 deletions(-)

diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 47e2b6fc40..fabfb1697b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -22,6 +22,7 @@
 #include "hw/char/serial.h"
 #include "hw/loader.h"
 #include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/units.h"
@@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 int first_irq;
 int j;
 
+/* Connect the timer clock. */
+qdev_connect_clock_in(DEVICE(>tim[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "timer-clock"));
+
 sysbus_realize(sbd, _abort);
 sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
 
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index d24445bd6e..36e2c07db2 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -17,8 +17,8 @@
 #include "qemu/osdep.h"
 
 #include "hw/irq.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
-#include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
@@ -128,23 +128,18 @@ static uint32_t npcm7xx_tcsr_prescaler(uint32_t tcsr)
 /* 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;
+int64_t ticks = count;
 
-ns *= NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ;
-ns *= npcm7xx_tcsr_prescaler(t->tcsr);
+ticks *= npcm7xx_tcsr_prescaler(t->tcsr);
 
-return ns;
+return clock_ticks_to_ns(t->ctrl->clock, ticks);
 }
 
 /* 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_tcsr_prescaler(t->tcsr);
-
-return count;
+return ns / clock_ticks_to_ns(t->ctrl->clock,
+  npcm7xx_tcsr_prescaler(t->tcsr));
 }
 
 static uint32_t npcm7xx_watchdog_timer_prescaler(const NPCM7xxWatchdogTimer *t)
@@ -166,8 +161,8 @@ static uint32_t npcm7xx_watchdog_timer_prescaler(const 
NPCM7xxWatchdogTimer *t)
 static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
 int64_t cycles)
 {
-uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
-int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
+int64_t ticks = cycles * npcm7xx_watchdog_timer_prescaler(t);
+int64_t ns = clock_ticks_to_ns(t->ctrl->clock, ticks);
 
 /*
  * The reset function always clears the current timer. The caller of the
@@ -176,7 +171,6 @@ static void 
npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
  */
 npcm7xx_timer_clear(>base_timer);
 
-ns *= prescaler;
 t->base_timer.remaining_ns = ns;
 }
 
@@ -606,10 +600,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
 qemu_irq_lower(s->watchdog_timer.irq);
 }
 
-static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+static void npcm7xx_timer_init(Object *obj)
 {
-NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
-SysBusDevice *sbd = >parent;
+NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+DeviceState *dev = DEVICE(obj);
+SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 int i;
 NPCM7xxWatchdogTimer *w;
 
@@ -627,11 +622,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error 
**errp)
 npcm7xx_watchdog_timer_expired, w);
 sysbus_init_irq(sbd, >irq);
 
-memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
+memory_region_init_io(>iomem, obj, _timer_ops, s,
   TYPE_NPCM7XX_TIMER, 4 * KiB);
 sysbus_init_mmio(sbd, >iomem);
 qdev_init_gpio_out_named(dev, >reset_signal,
 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
+s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
@@ -675,10 +671,11 @@ static const VMStateDescription 
vmstate_npcm7xx_watchdog_timer = {
 
 static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
 .name = "npcm7xx-timer-ctrl",
-.version_id = 1,
-.minimum_version_id = 1,
+.version_id = 2,
+.minimum_version_id = 2,
 .fields = (VMStateField[]) {
 VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
 VMSTATE_STRUCT_ARRAY(timer, 

[PATCH v5 0/6] Additional NPCM7xx devices

2021-01-08 Thread Hao Wu via
This patch series include a few more NPCM7XX devices including

- Analog Digital Converter (ADC)
- Pulse Width Modulation (PWM)

We also modified the CLK module to generate clock values using qdev_clock.
These clocks are used to determine various clocks in NPCM7XX devices.

Thank you for your review.

Changes since v4:
- Use clock_ticks_to_ns to compute clock time in nanoseconds.
(Didn't apply to PWM patch since it requires a frequency as output.)
- Removed reset_timer and resets immediately in ADC patch.
- Removed "qemu/osdep.h" from headers and include them in .c files.
- Use REG32 for register fields.
- Fix a g_assert that a guest can trigger with incorrect input.

Changes since v3:
- Use type casting instead of accessing parent object in all devices.

Changes since v2:
- Split PWM test into a separate patch in the patch set
- Add trace events for PWM's update_freq/update_duty
- Add trace events for ioread/iowrite in ADC and PWM
- Use timer_get_ns in hw/timer/npcm7xx_timer.c
- Update commit message in ADC/PWM to mention qom-get/set method for usage
- Fix typos

Changes since v1:
- We removed the IPMI and KCS related code from this patch set.

Hao Wu (6):
  hw/misc: Add clock converter in NPCM7XX CLK module
  hw/timer: Refactor NPCM7XX Timer to use CLK clock
  hw/adc: Add an ADC module for NPCM7XX
  hw/misc: Add a PWM module for NPCM7XX
  hw/misc: Add QTest for NPCM7XX PWM Module
  hw/*: Use type casting for SysBusDevice in NPCM7XX

 docs/system/arm/nuvoton.rst  |   4 +-
 hw/adc/meson.build   |   1 +
 hw/adc/npcm7xx_adc.c | 301 
 hw/adc/trace-events  |   5 +
 hw/arm/npcm7xx.c |  55 ++-
 hw/arm/npcm7xx_boards.c  |   2 +-
 hw/mem/npcm7xx_mc.c  |   2 +-
 hw/misc/meson.build  |   1 +
 hw/misc/npcm7xx_clk.c| 797 ++-
 hw/misc/npcm7xx_gcr.c|   2 +-
 hw/misc/npcm7xx_pwm.c| 550 +
 hw/misc/npcm7xx_rng.c|   2 +-
 hw/misc/trace-events |   6 +
 hw/nvram/npcm7xx_otp.c   |   2 +-
 hw/ssi/npcm7xx_fiu.c |   2 +-
 hw/timer/npcm7xx_timer.c |  39 +-
 include/hw/adc/npcm7xx_adc.h |  69 +++
 include/hw/arm/npcm7xx.h |   4 +
 include/hw/misc/npcm7xx_clk.h| 146 +-
 include/hw/misc/npcm7xx_pwm.h| 105 
 include/hw/timer/npcm7xx_timer.h |   1 +
 meson.build  |   1 +
 tests/qtest/meson.build  |   4 +-
 tests/qtest/npcm7xx_adc-test.c   | 377 +++
 tests/qtest/npcm7xx_pwm-test.c   | 490 +++
 25 files changed, 2920 insertions(+), 48 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

-- 
2.29.2.729.g45daf8777d-goog




Re: [PATCH v3 3/6] target/arm: make ARMCPU.ctr 64-bit

2021-01-08 Thread Hao Wu via
On Fri, Jan 8, 2021 at 10:54 AM Leif Lindholm  wrote:

> When FEAT_MTE is implemented, the AArch64 view of CTR_EL0 adds the
> TminLine field in bits [37:32].
> Extend the ctr field to be able to hold this context.
>
> Signed-off-by: Leif Lindholm 
>
Reviewed-by: Hao Wu 

> ---
>  target/arm/cpu.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/target/arm/cpu.h b/target/arm/cpu.h
> index fadd1a47df..063228de2a 100644
> --- a/target/arm/cpu.h
> +++ b/target/arm/cpu.h
> @@ -931,7 +931,7 @@ struct ARMCPU {
>  uint64_t midr;
>  uint32_t revidr;
>  uint32_t reset_fpsid;
> -uint32_t ctr;
> +uint64_t ctr;
>  uint32_t reset_sctlr;
>  uint64_t pmceid0;
>  uint64_t pmceid1;
> --
> 2.20.1
>
>
>


Re: [PATCH v4 3/6] hw/adc: Add an ADC module for NPCM7XX

2021-01-07 Thread Hao Wu via
Thanks for your review. We'll apply your suggestions.

On Thu, Jan 7, 2021 at 1:07 PM Peter Maydell 
wrote:

> On Thu, 17 Dec 2020 at 00:45, Hao Wu  wrote:
> >
> > The ADC is part of NPCM7XX Module. Its behavior is controled by the
> > ADC_CON register. It converts one of the eight analog inputs into a
> > digital input and stores it in the ADC_DATA register when enabled.
> >
> > Users can alter input value by using qom-set QMP command.
> >
> > Reviewed-by: Havard Skinnemoen 
> > Reviewed-by: Tyrone Ting 
> > Signed-off-by: Hao Wu 
> > ---
> >  docs/system/arm/nuvoton.rst|   2 +-
> >  hw/adc/meson.build |   1 +
> >  hw/adc/npcm7xx_adc.c   | 321 ++
> >  hw/adc/trace-events|   5 +
> >  hw/arm/npcm7xx.c   |  24 +-
> >  include/hw/adc/npcm7xx_adc.h   |  72 ++
> >  include/hw/arm/npcm7xx.h   |   2 +
> >  meson.build|   1 +
> >  tests/qtest/meson.build|   3 +-
> >  tests/qtest/npcm7xx_adc-test.c | 400 +
> >  10 files changed, 828 insertions(+), 3 deletions(-)
> >  create mode 100644 hw/adc/npcm7xx_adc.c
> >  create mode 100644 hw/adc/trace-events
> >  create mode 100644 include/hw/adc/npcm7xx_adc.h
> >  create mode 100644 tests/qtest/npcm7xx_adc-test.c
> >
> > diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
> > index b00d405d52..35829f8d0b 100644
> > --- a/docs/system/arm/nuvoton.rst
> > +++ b/docs/system/arm/nuvoton.rst
> > @@ -41,6 +41,7 @@ Supported devices
> >   * Random Number Generator (RNG)
> >   * USB host (USBH)
> >   * GPIO controller
> > + * Analog to Digital Converter (ADC)
> >
> >  Missing devices
> >  ---
> > @@ -58,7 +59,6 @@ Missing devices
> >   * USB device (USBD)
> >   * SMBus controller (SMBF)
> >   * Peripheral SPI controller (PSPI)
> > - * Analog to Digital Converter (ADC)
> >   * SD/MMC host
> >   * PECI interface
> >   * Pulse Width Modulation (PWM)
> > diff --git a/hw/adc/meson.build b/hw/adc/meson.build
> > index 0d62ae96ae..6ddee23813 100644
> > --- a/hw/adc/meson.build
> > +++ b/hw/adc/meson.build
> > @@ -1 +1,2 @@
> >  softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true:
> files('stm32f2xx_adc.c'))
> > +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
> > diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
> > new file mode 100644
> > index 00..f213b6a6df
> > --- /dev/null
> > +++ b/hw/adc/npcm7xx_adc.c
> > @@ -0,0 +1,321 @@
> > +/*
> > + * Nuvoton NPCM7xx ADC Module
> > + *
> > + * 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 "hw/adc/npcm7xx_adc.h"
>
> First #include in every .c file must always be "qemu/osdep.h"
> (and .h files never include osdep.h).
>
We'll apply this globally in the patchset.

>
> > +#include "hw/qdev-clock.h"
> > +#include "hw/qdev-properties.h"
> > +#include "migration/vmstate.h"
> > +#include "qemu/log.h"
> > +#include "qemu/module.h"
> > +#include "qemu/timer.h"
> > +#include "qemu/units.h"
> > +#include "trace.h"
> > +
> > +/* 32-bit register indices. */
> > +enum NPCM7xxADCRegisters {
> > +NPCM7XX_ADC_CON,
> > +NPCM7XX_ADC_DATA,
> > +NPCM7XX_ADC_REGS_END,
> > +};
> > +
> > +/* Register field definitions. */
> > +#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
> > +#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
> > +#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
> > +#define NPCM7XX_ADC_CON_INT BIT(18)
> > +#define NPCM7XX_ADC_CON_EN  BIT(17)
> > +#define NPCM7XX_ADC_CON_RST BIT(16)
> > +#define NPCM7XX_ADC_CON_CONVBIT(14)
> > +#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
> > +
> > +#define NPCM7XX_ADC_MAX_RESULT  1023
> > +#define NPCM7XX_ADC_DEFAULT_IREF200
> > +#define NPCM7XX_ADC_CONV_CYCLES 20
> > +#define NPCM7XX_ADC_RESET_CYCLES10
> > +#define NPCM7XX_ADC_R0_INPUT50
> > +#define NPCM7XX_ADC_R1_INPUT150
> > +
> > +static void npcm7xx_adc_reset(NPCM7xxADCState *s)
> > +{
> > +timer_del(>conv_timer);
> > +timer_del(>reset_timer);
> > +s->con = 0x000c0001;
> > +s->data = 0x;
> > +}
> > +
> > +static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
> > +{
> > +uint32_t result;
> > +
> > +result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
> > +if (result > NPCM7XX_ADC_MAX_RESULT) {
> > +result = NPCM7XX_ADC_MAX_RESULT;
> > +}
> > +
> > +return result;
> > +}
> 

Re: [PATCH v4 2/6] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2021-01-07 Thread Hao Wu via
On Thu, Jan 7, 2021 at 12:51 PM Peter Maydell 
wrote:

> On Thu, 17 Dec 2020 at 00:45, Hao Wu  wrote:
> >
> > This patch makes NPCM7XX Timer to use a the timer clock generated by the
> > CLK module instead of the magic number TIMER_REF_HZ.
> >
> > Reviewed-by: Havard Skinnemoen 
> > Reviewed-by: Tyrone Ting 
> > Signed-off-by: Hao Wu 
> > ---
> >  hw/arm/npcm7xx.c |  5 +
> >  hw/timer/npcm7xx_timer.c | 25 ++---
> >  include/hw/misc/npcm7xx_clk.h|  6 --
> >  include/hw/timer/npcm7xx_timer.h |  1 +
> >  4 files changed, 20 insertions(+), 17 deletions(-)
>
> > @@ -130,7 +130,7 @@ 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 *= clock_get_ns(t->ctrl->clock);
> >  ns *= npcm7xx_tcsr_prescaler(t->tcsr);
>
> I'm afraid that since you wrote and sent this we've updated the
> clock API (in commits 554d523785ef868 and de6a65f11d7e5a2a93f),
> so clock_get_ns() doesn't exist any more. Instead there is
> a new function clock_ticks_to_ns().
>
> The idea of the new function is that clocks don't necessarily
> have a period which is a whole number of nanoseconds, so
> doing arithmetic on the return value from clock_get_ns()
> introduces rounding errors, especially in the case of
> "multiply clock_get_ns() by a tick count to get a duration".
> (The worst case for this is "clock frequency >1GHz", at which
> point the rounding means that clock_get_ns() returns 0...)
>
> There is as yet no function for "convert duration to
> tick count", so code like:
>count = ns / clock_get_ns(t->ctrl->clock);
>
> should probably for the moment call clock_ticks_to_ns()
> passing a tick count of 1. But I plan to write a patchset
> soon which will avoid the need to do that.
>
> Strictly speaking, even "call clock_ticks_to_ns() and
> then multiply by the prescaler value" can introduce
> rounding error; to deal with that I think you'd need to
> either have an internal Clock object whose period you
> adjusted as the prescalar value was updated by the guest,
> or else do arithmetic with the return value of clock_get()
> (which is in units of 2^-32 ns); I'm not sure either is
> worth it.
>
In this particular case, rounding error is less of a concern since the
clock should be ~25MHz (in the old implementation it was a fixed value.)

Since the prescaler is always an integer, a possible alternative might be
ns = clock_ticks_to_ns(t->ctrl->clock, count *
npcm7xx_tcsr_prescaler(t->tcsr))

and for code to convert ns to count can go
count = ns / clock_ticks_to_ns(t->ctrl->clock,
npcm7xx_tcsr_prescaler(t->tcsr))
or use the new API you proposed.

We'll also need to apply similar changes to other places in the patchset
(ADC and/or PWM) that need to compute clock value.

>
> thanks
> -- PMM
>


Re: [PATCH v4 0/6] Additional NPCM7xx devices

2021-01-05 Thread Hao Wu via
Ping?

On Wed, Dec 16, 2020 at 4:45 PM Hao Wu  wrote:

> This patch series include a few more NPCM7XX devices including
>
> - Analog Digital Converter (ADC)
> - Pulse Width Modulation (PWM)
>
> We also modified the CLK module to generate clock values using qdev_clock.
> These clocks are used to determine various clocks in NPCM7XX devices.
>
> Thank you for your review.
>
> Changes since v3:
> - Use type casting instead of accessing parent object in all devices.
>
> Changes since v2:
> - Split PWM test into a separate patch in the patch set
> - Add trace events for PWM's update_freq/update_duty
> - Add trace events for ioread/iowrite in ADC and PWM
> - Use timer_get_ns in hw/timer/npcm7xx_timer.c
> - Update commit message in ADC/PWM to mention qom-get/set method for usage
> - Fix typos
>
> Changes since v1:
> - We removed the IPMI and KCS related code from this patch set.
>
> Hao Wu (6):
>   hw/misc: Add clock converter in NPCM7XX CLK module
>   hw/timer: Refactor NPCM7XX Timer to use CLK clock
>   hw/adc: Add an ADC module for NPCM7XX
>   hw/misc: Add a PWM module for NPCM7XX
>   hw/misc: Add QTest for NPCM7XX PWM Module
>   hw/*: Use type casting for SysBusDevice in NPCM7XX
>
>  docs/system/arm/nuvoton.rst  |   4 +-
>  hw/adc/meson.build   |   1 +
>  hw/adc/npcm7xx_adc.c | 321 +
>  hw/adc/trace-events  |   5 +
>  hw/arm/npcm7xx.c |  55 ++-
>  hw/arm/npcm7xx_boards.c  |   2 +-
>  hw/mem/npcm7xx_mc.c  |   2 +-
>  hw/misc/meson.build  |   1 +
>  hw/misc/npcm7xx_clk.c| 797 ++-
>  hw/misc/npcm7xx_gcr.c|   2 +-
>  hw/misc/npcm7xx_pwm.c| 559 ++
>  hw/misc/npcm7xx_rng.c|   2 +-
>  hw/misc/trace-events |   6 +
>  hw/nvram/npcm7xx_otp.c   |   2 +-
>  hw/ssi/npcm7xx_fiu.c |   2 +-
>  hw/timer/npcm7xx_timer.c |  25 +-
>  include/hw/adc/npcm7xx_adc.h |  72 +++
>  include/hw/arm/npcm7xx.h |   4 +
>  include/hw/misc/npcm7xx_clk.h| 146 +-
>  include/hw/misc/npcm7xx_pwm.h| 106 
>  include/hw/timer/npcm7xx_timer.h |   1 +
>  meson.build  |   1 +
>  tests/qtest/meson.build  |   4 +-
>  tests/qtest/npcm7xx_adc-test.c   | 400 
>  tests/qtest/npcm7xx_pwm-test.c   | 490 +++
>  25 files changed, 2972 insertions(+), 38 deletions(-)
>  create mode 100644 hw/adc/npcm7xx_adc.c
>  create mode 100644 hw/adc/trace-events
>  create mode 100644 hw/misc/npcm7xx_pwm.c
>  create mode 100644 include/hw/adc/npcm7xx_adc.h
>  create mode 100644 include/hw/misc/npcm7xx_pwm.h
>  create mode 100644 tests/qtest/npcm7xx_adc-test.c
>  create mode 100644 tests/qtest/npcm7xx_pwm-test.c
>
> --
> 2.29.2.684.gfbc64c5ab5-goog
>
>


[PATCH v4 2/6] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2020-12-16 Thread Hao Wu via
This patch makes NPCM7XX Timer to use a the timer clock generated by the
CLK module instead of the magic number TIMER_REF_HZ.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx.c |  5 +
 hw/timer/npcm7xx_timer.c | 25 ++---
 include/hw/misc/npcm7xx_clk.h|  6 --
 include/hw/timer/npcm7xx_timer.h |  1 +
 4 files changed, 20 insertions(+), 17 deletions(-)

diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 47e2b6fc40..fabfb1697b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -22,6 +22,7 @@
 #include "hw/char/serial.h"
 #include "hw/loader.h"
 #include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/units.h"
@@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 int first_irq;
 int j;
 
+/* Connect the timer clock. */
+qdev_connect_clock_in(DEVICE(>tim[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "timer-clock"));
+
 sysbus_realize(sbd, _abort);
 sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
 
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index d24445bd6e..6e990d611a 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -17,8 +17,8 @@
 #include "qemu/osdep.h"
 
 #include "hw/irq.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
-#include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
@@ -130,7 +130,7 @@ 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 *= clock_get_ns(t->ctrl->clock);
 ns *= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return ns;
@@ -141,7 +141,7 @@ 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 = ns / clock_get_ns(t->ctrl->clock);
 count /= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return count;
@@ -167,7 +167,7 @@ static void 
npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
 int64_t cycles)
 {
 uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
-int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
+int64_t ns = clock_get_ns(t->ctrl->clock) * cycles;
 
 /*
  * The reset function always clears the current timer. The caller of the
@@ -606,10 +606,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
 qemu_irq_lower(s->watchdog_timer.irq);
 }
 
-static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+static void npcm7xx_timer_init(Object *obj)
 {
-NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
-SysBusDevice *sbd = >parent;
+NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
+DeviceState *dev = DEVICE(obj);
+SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 int i;
 NPCM7xxWatchdogTimer *w;
 
@@ -627,11 +628,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error 
**errp)
 npcm7xx_watchdog_timer_expired, w);
 sysbus_init_irq(sbd, >irq);
 
-memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
+memory_region_init_io(>iomem, obj, _timer_ops, s,
   TYPE_NPCM7XX_TIMER, 4 * KiB);
 sysbus_init_mmio(sbd, >iomem);
 qdev_init_gpio_out_named(dev, >reset_signal,
 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
+s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
@@ -675,10 +677,11 @@ static const VMStateDescription 
vmstate_npcm7xx_watchdog_timer = {
 
 static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
 .name = "npcm7xx-timer-ctrl",
-.version_id = 1,
-.minimum_version_id = 1,
+.version_id = 2,
+.minimum_version_id = 2,
 .fields = (VMStateField[]) {
 VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
 VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
  NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
  NPCM7xxTimer),
@@ -697,7 +700,6 @@ static void npcm7xx_timer_class_init(ObjectClass *klass, 
void *data)
 QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
 
 dc->desc = "NPCM7xx Timer Controller";
-dc->realize = npcm7xx_timer_realize;
 dc->vmsd = _npcm7xx_timer_ctrl;
 rc->phases.enter = npcm7xx_timer_enter_reset;
 rc->phases.hold = npcm7xx_timer_hold_reset;
@@ -708,6 +710,7 @@ static const TypeInfo npcm7xx_timer_info = {
 .parent = TYPE_SYS_BUS_DEVICE,
 .instance_size  = sizeof(NPCM7xxTimerCtrlState),
 .class_init = npcm7xx_timer_class_init,
+.instance_init  = npcm7xx_timer_init,
 

[PATCH v4 5/6] hw/misc: Add QTest for NPCM7XX PWM Module

2020-12-16 Thread Hao Wu via
We add a qtest for the PWM in the previous patch. It proves it works as
expected.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 tests/qtest/meson.build|   1 +
 tests/qtest/npcm7xx_pwm-test.c | 490 +
 2 files changed, 491 insertions(+)
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 955710d1c5..0b5467f084 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -136,6 +136,7 @@ qtests_sparc64 = \
 qtests_npcm7xx = \
   ['npcm7xx_adc-test',
'npcm7xx_gpio-test',
+   'npcm7xx_pwm-test',
'npcm7xx_rng-test',
'npcm7xx_timer-test',
'npcm7xx_watchdog_timer-test']
diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
new file mode 100644
index 00..33fbdf5f54
--- /dev/null
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -0,0 +1,490 @@
+/*
+ * QTests for Nuvoton NPCM7xx PWM Modules.
+ *
+ * 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 "qemu/bitops.h"
+#include "libqos/libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+
+#define REF_HZ  2500
+
+/* Register field definitions. */
+#define CH_EN   BIT(0)
+#define CH_INV  BIT(2)
+#define CH_MOD  BIT(3)
+
+/* Registers shared between all PWMs in a module */
+#define PPR 0x00
+#define CSR 0x04
+#define PCR 0x08
+#define PIER0x3c
+#define PIIR0x40
+
+/* CLK module related */
+#define CLK_BA  0xf0801000
+#define CLKSEL  0x04
+#define CLKDIV1 0x08
+#define CLKDIV2 0x2c
+#define PLLCON0 0x0c
+#define PLLCON1 0x10
+#define PLL_INDV(rv)extract32((rv), 0, 6)
+#define PLL_FBDV(rv)extract32((rv), 16, 12)
+#define PLL_OTDV1(rv)   extract32((rv), 8, 3)
+#define PLL_OTDV2(rv)   extract32((rv), 13, 3)
+#define APB3CKDIV(rv)   extract32((rv), 28, 2)
+#define CLK2CKDIV(rv)   extract32((rv), 0, 1)
+#define CLK4CKDIV(rv)   extract32((rv), 26, 2)
+#define CPUCKSEL(rv)extract32((rv), 0, 2)
+
+#define MAX_DUTY100
+
+typedef struct PWMModule {
+int irq;
+uint64_t base_addr;
+} PWMModule;
+
+typedef struct PWM {
+uint32_t cnr_offset;
+uint32_t cmr_offset;
+uint32_t pdr_offset;
+uint32_t pwdr_offset;
+} PWM;
+
+typedef struct TestData {
+const PWMModule *module;
+const PWM *pwm;
+} TestData;
+
+static const PWMModule pwm_module_list[] = {
+{
+.irq= 93,
+.base_addr  = 0xf0103000
+},
+{
+.irq= 94,
+.base_addr  = 0xf0104000
+}
+};
+
+static const PWM pwm_list[] = {
+{
+.cnr_offset = 0x0c,
+.cmr_offset = 0x10,
+.pdr_offset = 0x14,
+.pwdr_offset= 0x44,
+},
+{
+.cnr_offset = 0x18,
+.cmr_offset = 0x1c,
+.pdr_offset = 0x20,
+.pwdr_offset= 0x48,
+},
+{
+.cnr_offset = 0x24,
+.cmr_offset = 0x28,
+.pdr_offset = 0x2c,
+.pwdr_offset= 0x4c,
+},
+{
+.cnr_offset = 0x30,
+.cmr_offset = 0x34,
+.pdr_offset = 0x38,
+.pwdr_offset= 0x50,
+},
+};
+
+static const int ppr_base[] = { 0, 0, 8, 8 };
+static const int csr_base[] = { 0, 4, 8, 12 };
+static const int pcr_base[] = { 0, 8, 12, 16 };
+
+static const uint32_t ppr_list[] = {
+0,
+1,
+10,
+100,
+255, /* Max possible value. */
+};
+
+static const uint32_t csr_list[] = {
+0,
+1,
+2,
+3,
+4, /* Max possible value. */
+};
+
+static const uint32_t cnr_list[] = {
+0,
+1,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+static const uint32_t cmr_list[] = {
+0,
+1,
+10,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+/* Returns the index of the PWM module. */
+static int pwm_module_index(const PWMModule *module)
+{
+ptrdiff_t diff = module - pwm_module_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
+
+return diff;
+}
+
+/* Returns the index of the PWM entry. */
+static int pwm_index(const PWM *pwm)
+{
+ptrdiff_t diff = pwm - pwm_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
+
+return diff;
+}
+
+static 

[PATCH v4 1/6] hw/misc: Add clock converter in NPCM7XX CLK module

2020-12-16 Thread Hao Wu via
This patch allows NPCM7XX CLK module to compute clocks that are used by
other NPCM7XX modules.

Add a new struct NPCM7xxClockConverterState which represents a
single converter.  Each clock converter in CLK module represents one
converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
takes one or more input clocks and converts them into one output clock.
They form a clock hierarchy in the CLK module and are responsible for
outputing clocks for various other modules in an NPCM7XX SoC.

Each converter has a function pointer called "convert" which represents
the unique logic for that converter.

The clock contains two initialization information: ConverterInitInfo and
ConverterConnectionInfo. They represent the vertices and edges in the
clock diagram respectively.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/misc/npcm7xx_clk.c | 795 +-
 include/hw/misc/npcm7xx_clk.h | 140 +-
 2 files changed, 927 insertions(+), 8 deletions(-)

diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 6732437fe2..48bc9bdda5 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -18,6 +18,7 @@
 
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -27,9 +28,22 @@
 #include "trace.h"
 #include "sysemu/watchdog.h"
 
+/*
+ * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
+ * is always 25 MHz.
+ */
+#define NPCM7XX_CLOCK_REF_HZ(2500)
+
+/* Register Field Definitions */
+#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
+
 #define PLLCON_LOKI BIT(31)
 #define PLLCON_LOKS BIT(30)
 #define PLLCON_PWDENBIT(12)
+#define PLLCON_FBDV(con) extract32((con), 16, 12)
+#define PLLCON_OTDV2(con) extract32((con), 13, 3)
+#define PLLCON_OTDV1(con) extract32((con), 8, 3)
+#define PLLCON_INDV(con) extract32((con), 0, 6)
 
 enum NPCM7xxCLKRegisters {
 NPCM7XX_CLK_CLKEN1,
@@ -89,12 +103,609 @@ static const uint32_t 
cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
 [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
 };
 
-/* Register Field Definitions */
-#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
-
 /* The number of watchdogs that can trigger a reset. */
 #define NPCM7XX_NR_WATCHDOGS(3)
 
+/* Clock converter functions */
+
+#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
+#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
+(obj), TYPE_NPCM7XX_CLOCK_PLL)
+#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
+#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
+(obj), TYPE_NPCM7XX_CLOCK_SEL)
+#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
+#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
+(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
+
+static void npcm7xx_clk_update_pll(void *opaque)
+{
+NPCM7xxClockPLLState *s = opaque;
+uint32_t con = s->clk->regs[s->reg];
+uint64_t freq;
+
+/* The PLL is grounded if it is not locked yet. */
+if (con & PLLCON_LOKI) {
+freq = clock_get_hz(s->clock_in);
+freq *= PLLCON_FBDV(con);
+freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
+} else {
+freq = 0;
+}
+
+clock_update_hz(s->clock_out, freq);
+}
+
+static void npcm7xx_clk_update_sel(void *opaque)
+{
+NPCM7xxClockSELState *s = opaque;
+uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
+s->len);
+
+if (index >= s->input_size) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: SEL index: %u out of range\n",
+  __func__, index);
+index = 0;
+}
+clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
+}
+
+static void npcm7xx_clk_update_divider(void *opaque)
+{
+NPCM7xxClockDividerState *s = opaque;
+uint32_t freq;
+
+freq = s->divide(s);
+clock_update_hz(s->clock_out, freq);
+}
+
+static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) / s->divisor;
+}
+
+static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) /
+(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
+}
+
+static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
+{
+return divide_by_reg_divisor(s) / 2;
+}
+
+static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) >>
+extract32(s->clk->regs[s->reg], s->offset, s->len);
+}
+
+static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
+{
+switch (reg) {
+case NPCM7XX_CLK_PLLCON0:
+return NPCM7XX_CLOCK_PLL0;
+case NPCM7XX_CLK_PLLCON1:
+return NPCM7XX_CLOCK_PLL1;
+case NPCM7XX_CLK_PLLCON2:
+return 

[PATCH v4 6/6] hw/*: Use type casting for SysBusDevice in NPCM7XX

2020-12-16 Thread Hao Wu via
A device shouldn't access its parent object which is QOM internal.
Instead it should use type cast for this purporse. This patch fixes this
issue for all NPCM7XX Devices.

Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx_boards.c | 2 +-
 hw/mem/npcm7xx_mc.c | 2 +-
 hw/misc/npcm7xx_clk.c   | 2 +-
 hw/misc/npcm7xx_gcr.c   | 2 +-
 hw/misc/npcm7xx_rng.c   | 2 +-
 hw/nvram/npcm7xx_otp.c  | 2 +-
 hw/ssi/npcm7xx_fiu.c| 2 +-
 7 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c
index 306260fa67..3fdd5cab01 100644
--- a/hw/arm/npcm7xx_boards.c
+++ b/hw/arm/npcm7xx_boards.c
@@ -82,7 +82,7 @@ static NPCM7xxState *npcm7xx_create_soc(MachineState *machine,
 uint32_t hw_straps)
 {
 NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_GET_CLASS(machine);
-MachineClass *mc = >parent;
+MachineClass *mc = MACHINE_CLASS(nmc);
 Object *obj;
 
 if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) {
diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c
index 0435d06ab4..abc5af5620 100644
--- a/hw/mem/npcm7xx_mc.c
+++ b/hw/mem/npcm7xx_mc.c
@@ -62,7 +62,7 @@ static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
 
 memory_region_init_io(>mmio, OBJECT(s), _mc_ops, s, "regs",
   NPCM7XX_MC_REGS_SIZE);
-sysbus_init_mmio(>parent, >mmio);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >mmio);
 }
 
 static void npcm7xx_mc_class_init(ObjectClass *klass, void *data)
diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 48bc9bdda5..0bcae9ce95 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -913,7 +913,7 @@ static void npcm7xx_clk_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _clk_ops, s,
   TYPE_NPCM7XX_CLK, 4 * KiB);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static int npcm7xx_clk_post_load(void *opaque, int version_id)
diff --git a/hw/misc/npcm7xx_gcr.c b/hw/misc/npcm7xx_gcr.c
index 745f690809..eace9e1967 100644
--- a/hw/misc/npcm7xx_gcr.c
+++ b/hw/misc/npcm7xx_gcr.c
@@ -220,7 +220,7 @@ static void npcm7xx_gcr_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _gcr_ops, s,
   TYPE_NPCM7XX_GCR, 4 * KiB);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static const VMStateDescription vmstate_npcm7xx_gcr = {
diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c
index f650f3401f..b01df7cdb2 100644
--- a/hw/misc/npcm7xx_rng.c
+++ b/hw/misc/npcm7xx_rng.c
@@ -143,7 +143,7 @@ static void npcm7xx_rng_init(Object *obj)
 
 memory_region_init_io(>iomem, obj, _rng_ops, s, "regs",
   NPCM7XX_RNG_REGS_SIZE);
-sysbus_init_mmio(>parent, >iomem);
+sysbus_init_mmio(SYS_BUS_DEVICE(s), >iomem);
 }
 
 static const VMStateDescription vmstate_npcm7xx_rng = {
diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c
index b16ca530ba..c61f2fc1aa 100644
--- a/hw/nvram/npcm7xx_otp.c
+++ b/hw/nvram/npcm7xx_otp.c
@@ -371,7 +371,7 @@ static void npcm7xx_otp_realize(DeviceState *dev, Error 
**errp)
 {
 NPCM7xxOTPClass *oc = NPCM7XX_OTP_GET_CLASS(dev);
 NPCM7xxOTPState *s = NPCM7XX_OTP(dev);
-SysBusDevice *sbd = >parent;
+SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 
 memset(s->array, 0, sizeof(s->array));
 
diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c
index 5040132b07..4eedb2927e 100644
--- a/hw/ssi/npcm7xx_fiu.c
+++ b/hw/ssi/npcm7xx_fiu.c
@@ -498,7 +498,7 @@ static void npcm7xx_fiu_hold_reset(Object *obj)
 static void npcm7xx_fiu_realize(DeviceState *dev, Error **errp)
 {
 NPCM7xxFIUState *s = NPCM7XX_FIU(dev);
-SysBusDevice *sbd = >parent;
+SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 int i;
 
 if (s->cs_count <= 0) {
-- 
2.29.2.684.gfbc64c5ab5-goog




[PATCH v4 4/6] hw/misc: Add a PWM module for NPCM7XX

2020-12-16 Thread Hao Wu via
The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
identical PWM modules. Each module contains 4 PWM entries. Each PWM has
two outputs: frequency and duty_cycle. Both are computed using inputs
from software side.

This module does not model detail pulse signals since it is expensive.
It also does not model interrupts and watchdogs that are dependant on
the detail models. The interfaces for these are left in the module so
that anyone in need for these functionalities can implement on their
own.

The user can read the duty cycle and frequency using qom-get command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst   |   2 +-
 hw/arm/npcm7xx.c  |  26 +-
 hw/misc/meson.build   |   1 +
 hw/misc/npcm7xx_pwm.c | 559 ++
 hw/misc/trace-events  |   6 +
 include/hw/arm/npcm7xx.h  |   2 +
 include/hw/misc/npcm7xx_pwm.h | 106 +++
 7 files changed, 699 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/misc/npcm7xx_pwm.h

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 35829f8d0b..a1786342e2 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -42,6 +42,7 @@ Supported devices
  * USB host (USBH)
  * GPIO controller
  * Analog to Digital Converter (ADC)
+ * Pulse Width Modulation (PWM)
 
 Missing devices
 ---
@@ -61,7 +62,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Pulse Width Modulation (PWM)
  * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index b22a8c966d..72040d4079 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
+NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
 0xb800, /* CS3 */
 };
 
+/* Register base address for each PWM Module */
+static const hwaddr npcm7xx_pwm_addr[] = {
+0xf0103000,
+0xf0104000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
 TYPE_NPCM7XX_FIU);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
 
+/* PWM Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
+
+qdev_connect_clock_in(DEVICE(>pwm[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "apb3-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
+sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 * KiB);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ce15ffceb9..607cd38a21 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -64,6 +64,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: 
files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
diff --git a/hw/misc/npcm7xx_pwm.c 

[PATCH v4 3/6] hw/adc: Add an ADC module for NPCM7XX

2020-12-16 Thread Hao Wu via
The ADC is part of NPCM7XX Module. Its behavior is controled by the
ADC_CON register. It converts one of the eight analog inputs into a
digital input and stores it in the ADC_DATA register when enabled.

Users can alter input value by using qom-set QMP command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/adc/meson.build |   1 +
 hw/adc/npcm7xx_adc.c   | 321 ++
 hw/adc/trace-events|   5 +
 hw/arm/npcm7xx.c   |  24 +-
 include/hw/adc/npcm7xx_adc.h   |  72 ++
 include/hw/arm/npcm7xx.h   |   2 +
 meson.build|   1 +
 tests/qtest/meson.build|   3 +-
 tests/qtest/npcm7xx_adc-test.c | 400 +
 10 files changed, 828 insertions(+), 3 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index b00d405d52..35829f8d0b 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -41,6 +41,7 @@ Supported devices
  * Random Number Generator (RNG)
  * USB host (USBH)
  * GPIO controller
+ * Analog to Digital Converter (ADC)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
  * USB device (USBD)
  * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
- * Analog to Digital Converter (ADC)
  * SD/MMC host
  * PECI interface
  * Pulse Width Modulation (PWM)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index 0d62ae96ae..6ddee23813 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
new file mode 100644
index 00..f213b6a6df
--- /dev/null
+++ b/hw/adc/npcm7xx_adc.c
@@ -0,0 +1,321 @@
+/*
+ * Nuvoton NPCM7xx ADC Module
+ *
+ * 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 "hw/adc/npcm7xx_adc.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+/* 32-bit register indices. */
+enum NPCM7xxADCRegisters {
+NPCM7XX_ADC_CON,
+NPCM7XX_ADC_DATA,
+NPCM7XX_ADC_REGS_END,
+};
+
+/* Register field definitions. */
+#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
+#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
+#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
+#define NPCM7XX_ADC_CON_INT BIT(18)
+#define NPCM7XX_ADC_CON_EN  BIT(17)
+#define NPCM7XX_ADC_CON_RST BIT(16)
+#define NPCM7XX_ADC_CON_CONVBIT(14)
+#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
+
+#define NPCM7XX_ADC_MAX_RESULT  1023
+#define NPCM7XX_ADC_DEFAULT_IREF200
+#define NPCM7XX_ADC_CONV_CYCLES 20
+#define NPCM7XX_ADC_RESET_CYCLES10
+#define NPCM7XX_ADC_R0_INPUT50
+#define NPCM7XX_ADC_R1_INPUT150
+
+static void npcm7xx_adc_reset(NPCM7xxADCState *s)
+{
+timer_del(>conv_timer);
+timer_del(>reset_timer);
+s->con = 0x000c0001;
+s->data = 0x;
+}
+
+static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
+{
+uint32_t result;
+
+result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
+if (result > NPCM7XX_ADC_MAX_RESULT) {
+result = NPCM7XX_ADC_MAX_RESULT;
+}
+
+return result;
+}
+
+static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
+{
+return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
+}
+
+static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
+uint32_t cycles, uint32_t prescaler)
+{
+int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t freq = clock_get_hz(clk);
+int64_t ns;
+
+ns = (NANOSECONDS_PER_SECOND * cycles * prescaler / freq);
+ns += now;
+timer_mod(timer, ns);
+}
+
+static void npcm7xx_adc_start_reset(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >reset_timer, 
NPCM7XX_ADC_RESET_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+

[PATCH v4 0/6] Additional NPCM7xx devices

2020-12-16 Thread Hao Wu via
This patch series include a few more NPCM7XX devices including

- Analog Digital Converter (ADC)
- Pulse Width Modulation (PWM)

We also modified the CLK module to generate clock values using qdev_clock.
These clocks are used to determine various clocks in NPCM7XX devices.

Thank you for your review.

Changes since v3:
- Use type casting instead of accessing parent object in all devices.

Changes since v2:
- Split PWM test into a separate patch in the patch set
- Add trace events for PWM's update_freq/update_duty
- Add trace events for ioread/iowrite in ADC and PWM
- Use timer_get_ns in hw/timer/npcm7xx_timer.c
- Update commit message in ADC/PWM to mention qom-get/set method for usage
- Fix typos

Changes since v1:
- We removed the IPMI and KCS related code from this patch set.

Hao Wu (6):
  hw/misc: Add clock converter in NPCM7XX CLK module
  hw/timer: Refactor NPCM7XX Timer to use CLK clock
  hw/adc: Add an ADC module for NPCM7XX
  hw/misc: Add a PWM module for NPCM7XX
  hw/misc: Add QTest for NPCM7XX PWM Module
  hw/*: Use type casting for SysBusDevice in NPCM7XX

 docs/system/arm/nuvoton.rst  |   4 +-
 hw/adc/meson.build   |   1 +
 hw/adc/npcm7xx_adc.c | 321 +
 hw/adc/trace-events  |   5 +
 hw/arm/npcm7xx.c |  55 ++-
 hw/arm/npcm7xx_boards.c  |   2 +-
 hw/mem/npcm7xx_mc.c  |   2 +-
 hw/misc/meson.build  |   1 +
 hw/misc/npcm7xx_clk.c| 797 ++-
 hw/misc/npcm7xx_gcr.c|   2 +-
 hw/misc/npcm7xx_pwm.c| 559 ++
 hw/misc/npcm7xx_rng.c|   2 +-
 hw/misc/trace-events |   6 +
 hw/nvram/npcm7xx_otp.c   |   2 +-
 hw/ssi/npcm7xx_fiu.c |   2 +-
 hw/timer/npcm7xx_timer.c |  25 +-
 include/hw/adc/npcm7xx_adc.h |  72 +++
 include/hw/arm/npcm7xx.h |   4 +
 include/hw/misc/npcm7xx_clk.h| 146 +-
 include/hw/misc/npcm7xx_pwm.h| 106 
 include/hw/timer/npcm7xx_timer.h |   1 +
 meson.build  |   1 +
 tests/qtest/meson.build  |   4 +-
 tests/qtest/npcm7xx_adc-test.c   | 400 
 tests/qtest/npcm7xx_pwm-test.c   | 490 +++
 25 files changed, 2972 insertions(+), 38 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

-- 
2.29.2.684.gfbc64c5ab5-goog




Re: [PATCH v3 4/5] hw/misc: Add a PWM module for NPCM7XX

2020-12-16 Thread Hao Wu via
Thanks for the review. We can add a patch in this patchset to fix this
issue.

On Wed, Dec 16, 2020 at 11:02 AM Peter Maydell 
wrote:

> On Tue, 15 Dec 2020 at 00:13, Hao Wu  wrote:
> >
> > The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
> > identical PWM modules. Each module contains 4 PWM entries. Each PWM has
> > two outputs: frequency and duty_cycle. Both are computed using inputs
> > from software side.
> >
> > This module does not model detail pulse signals since it is expensive.
> > It also does not model interrupts and watchdogs that are dependant on
> > the detail models. The interfaces for these are left in the module so
> > that anyone in need for these functionalities can implement on their
> > own.
> >
> > The user can read the duty cycle and frequency using qom-get command.
> >
> > Reviewed-by: Havard Skinnemoen 
> > Reviewed-by: Tyrone Ting 
> > Signed-off-by: Hao Wu 
>
>
> > +static void npcm7xx_pwm_init(Object *obj)
> > +{
> > +NPCM7xxPWMState *s = NPCM7XX_PWM(obj);
> > +SysBusDevice *sbd = >parent;
>
> This isn't right. A device shouldn't be poking around
> in the 'parent' or 'parentobj' member of its struct --
> that is a QOM internal. If you want "this device, cast
> to a SysBusDevice", the way to write that is:
>SysBusDevice *sbd = SYS_BUS_DEVICE(s);
>
> (or you could pass 'obj'; same thing).
>
> Looking at the code currently in the tree it also is making this
> same mistake:
>
> $ git grep -- '->parent' hw/*/npcm*
> hw/arm/npcm7xx_boards.c:MachineClass *mc = >parent;
> hw/mem/npcm7xx_mc.c:sysbus_init_mmio(>parent, >mmio);
> hw/misc/npcm7xx_clk.c:sysbus_init_mmio(>parent, >iomem);
> hw/misc/npcm7xx_gcr.c:sysbus_init_mmio(>parent, >iomem);
> hw/misc/npcm7xx_rng.c:sysbus_init_mmio(>parent, >iomem);
> hw/nvram/npcm7xx_otp.c:SysBusDevice *sbd = >parent;
> hw/ssi/npcm7xx_fiu.c:SysBusDevice *sbd = >parent;
> hw/timer/npcm7xx_timer.c:SysBusDevice *sbd = >parent;
>
> These all should be using QOM cast macros. Would somebody
> who's working on these devices like to send a patch ?
>
> thanks
> -- PMM
>


Re: [PATCH v3 0/5] Additional NPCM7xx devices

2020-12-15 Thread Hao Wu via
Thanks for the tip! I'll use that in the future.

Best,

Hao

On Tue, Dec 15, 2020 at 7:17 AM Philippe Mathieu-Daudé 
wrote:

> On 12/15/20 1:13 AM, Hao Wu via wrote:
> > This patch series include a few more NPCM7XX devices including
> >
> > - Analog Digital Converter (ADC)
> > - Pulse Width Modulation (PWM)
> >
> > We also modified the CLK module to generate clock values using
> qdev_clock.
> > These clocks are used to determine various clocks in NPCM7XX devices.
> >
> > Thank you for your review.
>
> The list is still rewriting your author ident.
>
> Maybe try 'git-format-patch --from' next time to force it
> on each patch?
>
> Regards,
>
> Phil.
>


Re: [PATCH v3 2/5] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> This patch makes NPCM7XX Timer to use a the timer clock generated by the
> CLK module instead of the magic number TIMER_REF_HZ.
>
> Reviewed-by: Havard Skinnemoen 
> Reviewed-by: Tyrone Ting 
> Signed-off-by: Hao Wu 
> ---
>  hw/arm/npcm7xx.c |  5 +
>  hw/timer/npcm7xx_timer.c | 23 +--
>  include/hw/misc/npcm7xx_clk.h|  6 --
>  include/hw/timer/npcm7xx_timer.h |  1 +
>  4 files changed, 19 insertions(+), 16 deletions(-)
>
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index 47e2b6fc40..fabfb1697b 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -22,6 +22,7 @@
>  #include "hw/char/serial.h"
>  #include "hw/loader.h"
>  #include "hw/misc/unimp.h"
> +#include "hw/qdev-clock.h"
>  #include "hw/qdev-properties.h"
>  #include "qapi/error.h"
>  #include "qemu/units.h"
> @@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error
> **errp)
>  int first_irq;
>  int j;
>
> +/* Connect the timer clock. */
> +qdev_connect_clock_in(DEVICE(>tim[i]), "clock",
> qdev_get_clock_out(
> +DEVICE(>clk), "timer-clock"));
> +
>  sysbus_realize(sbd, _abort);
>  sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
>
> diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
> index d24445bd6e..8147b53000 100644
> --- a/hw/timer/npcm7xx_timer.c
> +++ b/hw/timer/npcm7xx_timer.c
> @@ -17,8 +17,8 @@
>  #include "qemu/osdep.h"
>
>  #include "hw/irq.h"
> +#include "hw/qdev-clock.h"
>  #include "hw/qdev-properties.h"
> -#include "hw/misc/npcm7xx_clk.h"
>  #include "hw/timer/npcm7xx_timer.h"
>  #include "migration/vmstate.h"
>  #include "qemu/bitops.h"
> @@ -130,7 +130,7 @@ 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 *= clock_get_ns(t->ctrl->clock);
>  ns *= npcm7xx_tcsr_prescaler(t->tcsr);
>
>  return ns;
> @@ -141,7 +141,7 @@ 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 = ns / clock_get_ns(t->ctrl->clock);
>  count /= npcm7xx_tcsr_prescaler(t->tcsr);
>
>  return count;
> @@ -167,7 +167,7 @@ static void
> npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
>  int64_t cycles)
>  {
>  uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
> -int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
> +int64_t ns = clock_get_ns(t->ctrl->clock) * cycles;
>
>  /*
>   * The reset function always clears the current timer. The caller of
> the
> @@ -606,10 +606,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
>  qemu_irq_lower(s->watchdog_timer.irq);
>  }
>
> -static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
> +static void npcm7xx_timer_init(Object *obj)
>  {
> -NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
> +NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
>  SysBusDevice *sbd = >parent;
> +DeviceState *dev = DEVICE(obj);
>  int i;
>  NPCM7xxWatchdogTimer *w;
>
> @@ -627,11 +628,12 @@ static void npcm7xx_timer_realize(DeviceState *dev,
> Error **errp)
>  npcm7xx_watchdog_timer_expired, w);
>  sysbus_init_irq(sbd, >irq);
>
> -memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
> +memory_region_init_io(>iomem, obj, _timer_ops, s,
>TYPE_NPCM7XX_TIMER, 4 * KiB);
>  sysbus_init_mmio(sbd, >iomem);
>  qdev_init_gpio_out_named(dev, >reset_signal,
>  NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
> +s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
>  }
>
>  static const VMStateDescription vmstate_npcm7xx_base_timer = {
> @@ -675,10 +677,11 @@ static const VMStateDescription
> vmstate_npcm7xx_watchdog_timer = {
>
>  static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
>  .name = "npcm7xx-timer-ctrl",
> -.version_id = 1,
> -.minimum_version_id = 1,
> +.version_id = 2,
> +.minimum_version_id = 2,
>  .fields = (VMStateField[]) {
>  VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
> +VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
>  VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
>   NPCM7XX_TIMERS_PER_CTRL, 0,
> vmstate_npcm7xx_timer,
>   NPCM7xxTimer),
> @@ -697,7 +700,6 @@ static void npcm7xx_timer_class_init(ObjectClass
> *klass, void *data)
>  QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
>
>  dc->desc = "NPCM7xx Timer Controller";
> -dc->realize = npcm7xx_timer_realize;
>  dc->vmsd = _npcm7xx_timer_ctrl;
>  rc->phases.enter = npcm7xx_timer_enter_reset;
>  rc->phases.hold = npcm7xx_timer_hold_reset;
> @@ -708,6 +710,7 @@ static 

Re: [PATCH v3 3/5] hw/adc: Add an ADC module for NPCM7XX

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> The ADC is part of NPCM7XX Module. Its behavior is controled by the
> ADC_CON register. It converts one of the eight analog inputs into a
> digital input and stores it in the ADC_DATA register when enabled.
>
> Users can alter input value by using qom-set QMP command.
>
> Reviewed-by: Havard Skinnemoen 
> Reviewed-by: Tyrone Ting 
> Signed-off-by: Hao Wu 
> ---
>  docs/system/arm/nuvoton.rst|   2 +-
>  hw/adc/meson.build |   1 +
>  hw/adc/npcm7xx_adc.c   | 321 ++
>  hw/adc/trace-events|   5 +
>  hw/arm/npcm7xx.c   |  24 +-
>  include/hw/adc/npcm7xx_adc.h   |  72 ++
>  include/hw/arm/npcm7xx.h   |   2 +
>  meson.build|   1 +
>  tests/qtest/meson.build|   3 +-
>  tests/qtest/npcm7xx_adc-test.c | 400 +
>  10 files changed, 828 insertions(+), 3 deletions(-)
>  create mode 100644 hw/adc/npcm7xx_adc.c
>  create mode 100644 hw/adc/trace-events
>  create mode 100644 include/hw/adc/npcm7xx_adc.h
>  create mode 100644 tests/qtest/npcm7xx_adc-test.c
>
> diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
> index b00d405d52..35829f8d0b 100644
> --- a/docs/system/arm/nuvoton.rst
> +++ b/docs/system/arm/nuvoton.rst
> @@ -41,6 +41,7 @@ Supported devices
>   * Random Number Generator (RNG)
>   * USB host (USBH)
>   * GPIO controller
> + * Analog to Digital Converter (ADC)
>
>  Missing devices
>  ---
> @@ -58,7 +59,6 @@ Missing devices
>   * USB device (USBD)
>   * SMBus controller (SMBF)
>   * Peripheral SPI controller (PSPI)
> - * Analog to Digital Converter (ADC)
>   * SD/MMC host
>   * PECI interface
>   * Pulse Width Modulation (PWM)
> diff --git a/hw/adc/meson.build b/hw/adc/meson.build
> index 0d62ae96ae..6ddee23813 100644
> --- a/hw/adc/meson.build
> +++ b/hw/adc/meson.build
> @@ -1 +1,2 @@
>  softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true:
> files('stm32f2xx_adc.c'))
> +softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
> diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
> new file mode 100644
> index 00..c2c4819d3f
> --- /dev/null
> +++ b/hw/adc/npcm7xx_adc.c
> @@ -0,0 +1,321 @@
> +/*
> + * Nuvoton NPCM7xx ADC Module
> + *
> + * 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 "hw/adc/npcm7xx_adc.h"
> +#include "hw/qdev-clock.h"
> +#include "hw/qdev-properties.h"
> +#include "migration/vmstate.h"
> +#include "qemu/log.h"
> +#include "qemu/module.h"
> +#include "qemu/timer.h"
> +#include "qemu/units.h"
> +#include "trace.h"
> +
> +/* 32-bit register indices. */
> +enum NPCM7xxADCRegisters {
> +NPCM7XX_ADC_CON,
> +NPCM7XX_ADC_DATA,
> +NPCM7XX_ADC_REGS_END,
> +};
> +
> +/* Register field definitions. */
> +#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
> +#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
> +#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
> +#define NPCM7XX_ADC_CON_INT BIT(18)
> +#define NPCM7XX_ADC_CON_EN  BIT(17)
> +#define NPCM7XX_ADC_CON_RST BIT(16)
> +#define NPCM7XX_ADC_CON_CONVBIT(14)
> +#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
> +
> +#define NPCM7XX_ADC_MAX_RESULT  1023
> +#define NPCM7XX_ADC_DEFAULT_IREF200
> +#define NPCM7XX_ADC_CONV_CYCLES 20
> +#define NPCM7XX_ADC_RESET_CYCLES10
> +#define NPCM7XX_ADC_R0_INPUT50
> +#define NPCM7XX_ADC_R1_INPUT150
> +
> +static void npcm7xx_adc_reset(NPCM7xxADCState *s)
> +{
> +timer_del(>conv_timer);
> +timer_del(>reset_timer);
> +s->con = 0x000c0001;
> +s->data = 0x;
> +}
> +
> +static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
> +{
> +uint32_t result;
> +
> +result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
> +if (result > NPCM7XX_ADC_MAX_RESULT) {
> +result = NPCM7XX_ADC_MAX_RESULT;
> +}
> +
> +return result;
> +}
> +
> +static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
> +{
> +return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
> +}
> +
> +static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
> +uint32_t cycles, uint32_t prescaler)
> +{
> +int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
> +int64_t freq = clock_get_hz(clk);
> +int64_t ns;
> +
> +ns = (NANOSECONDS_PER_SECOND * cycles * prescaler / freq);
> +ns += now;
> +timer_mod(timer, ns);
> +}
> +
> +static void 

Re: [PATCH v3 5/5] hw/misc: Add QTest for NPCM7XX PWM Module

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> We add a qtest for the PWM in the previous patch. It proves it works as
> expected.
>
> Reviewed-by: Havard Skinnemoen 
> Reviewed-by: Tyrone Ting 
> Signed-off-by: Hao Wu 
> ---
>  tests/qtest/meson.build|   1 +
>  tests/qtest/npcm7xx_pwm-test.c | 490 +
>  2 files changed, 491 insertions(+)
>  create mode 100644 tests/qtest/npcm7xx_pwm-test.c
>
> diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
> index 955710d1c5..0b5467f084 100644
> --- a/tests/qtest/meson.build
> +++ b/tests/qtest/meson.build
> @@ -136,6 +136,7 @@ qtests_sparc64 = \
>  qtests_npcm7xx = \
>['npcm7xx_adc-test',
> 'npcm7xx_gpio-test',
> +   'npcm7xx_pwm-test',
> 'npcm7xx_rng-test',
> 'npcm7xx_timer-test',
> 'npcm7xx_watchdog_timer-test']
> diff --git a/tests/qtest/npcm7xx_pwm-test.c
> b/tests/qtest/npcm7xx_pwm-test.c
> new file mode 100644
> index 00..33fbdf5f54
> --- /dev/null
> +++ b/tests/qtest/npcm7xx_pwm-test.c
> @@ -0,0 +1,490 @@
> +/*
> + * QTests for Nuvoton NPCM7xx PWM Modules.
> + *
> + * 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 "qemu/bitops.h"
> +#include "libqos/libqtest.h"
> +#include "qapi/qmp/qdict.h"
> +#include "qapi/qmp/qnum.h"
> +
> +#define REF_HZ  2500
> +
> +/* Register field definitions. */
> +#define CH_EN   BIT(0)
> +#define CH_INV  BIT(2)
> +#define CH_MOD  BIT(3)
> +
> +/* Registers shared between all PWMs in a module */
> +#define PPR 0x00
> +#define CSR 0x04
> +#define PCR 0x08
> +#define PIER0x3c
> +#define PIIR0x40
> +
> +/* CLK module related */
> +#define CLK_BA  0xf0801000
> +#define CLKSEL  0x04
> +#define CLKDIV1 0x08
> +#define CLKDIV2 0x2c
> +#define PLLCON0 0x0c
> +#define PLLCON1 0x10
> +#define PLL_INDV(rv)extract32((rv), 0, 6)
> +#define PLL_FBDV(rv)extract32((rv), 16, 12)
> +#define PLL_OTDV1(rv)   extract32((rv), 8, 3)
> +#define PLL_OTDV2(rv)   extract32((rv), 13, 3)
> +#define APB3CKDIV(rv)   extract32((rv), 28, 2)
> +#define CLK2CKDIV(rv)   extract32((rv), 0, 1)
> +#define CLK4CKDIV(rv)   extract32((rv), 26, 2)
> +#define CPUCKSEL(rv)extract32((rv), 0, 2)
> +
> +#define MAX_DUTY100
> +
> +typedef struct PWMModule {
> +int irq;
> +uint64_t base_addr;
> +} PWMModule;
> +
> +typedef struct PWM {
> +uint32_t cnr_offset;
> +uint32_t cmr_offset;
> +uint32_t pdr_offset;
> +uint32_t pwdr_offset;
> +} PWM;
> +
> +typedef struct TestData {
> +const PWMModule *module;
> +const PWM *pwm;
> +} TestData;
> +
> +static const PWMModule pwm_module_list[] = {
> +{
> +.irq= 93,
> +.base_addr  = 0xf0103000
> +},
> +{
> +.irq= 94,
> +.base_addr  = 0xf0104000
> +}
> +};
> +
> +static const PWM pwm_list[] = {
> +{
> +.cnr_offset = 0x0c,
> +.cmr_offset = 0x10,
> +.pdr_offset = 0x14,
> +.pwdr_offset= 0x44,
> +},
> +{
> +.cnr_offset = 0x18,
> +.cmr_offset = 0x1c,
> +.pdr_offset = 0x20,
> +.pwdr_offset= 0x48,
> +},
> +{
> +.cnr_offset = 0x24,
> +.cmr_offset = 0x28,
> +.pdr_offset = 0x2c,
> +.pwdr_offset= 0x4c,
> +},
> +{
> +.cnr_offset = 0x30,
> +.cmr_offset = 0x34,
> +.pdr_offset = 0x38,
> +.pwdr_offset= 0x50,
> +},
> +};
> +
> +static const int ppr_base[] = { 0, 0, 8, 8 };
> +static const int csr_base[] = { 0, 4, 8, 12 };
> +static const int pcr_base[] = { 0, 8, 12, 16 };
> +
> +static const uint32_t ppr_list[] = {
> +0,
> +1,
> +10,
> +100,
> +255, /* Max possible value. */
> +};
> +
> +static const uint32_t csr_list[] = {
> +0,
> +1,
> +2,
> +3,
> +4, /* Max possible value. */
> +};
> +
> +static const uint32_t cnr_list[] = {
> +0,
> +1,
> +50,
> +100,
> +150,
> +200,
> +1000,
> +1,
> +65535, /* Max possible value. */
> +};
> +
> +static const uint32_t cmr_list[] = {
> +0,
> +1,
> +10,
> +50,
> +100,
> +150,
> +200,
> +1000,
> +1,
> +65535, /* Max possible value. */
> +};
> +
> +/* Returns the index of the PWM module. */
> 

Re: [PATCH v3 0/5] Additional NPCM7xx devices

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> This patch series include a few more NPCM7XX devices including
>
> - Analog Digital Converter (ADC)
> - Pulse Width Modulation (PWM)
>
> We also modified the CLK module to generate clock values using qdev_clock.
> These clocks are used to determine various clocks in NPCM7XX devices.
>
> Thank you for your review.
>
> Changes since v2:
> - Split PWM test into a separate patch in the patch set
> - Add trace events for PWM's update_freq/update_duty
> - Add trace events for ioread/iowrite in ADC and PWM
> - Use timer_get_ns in hw/timer/npcm7xx_timer.c
> - Update commit message in ADC/PWM to mention qom-get/set method for usage
> - Fix typos
>
> Changes since v1:
> - We removed the IPMI and KCS related code from this patch set.
>
> Hao Wu (5):
>   hw/misc: Add clock converter in NPCM7XX CLK module
>   hw/timer: Refactor NPCM7XX Timer to use CLK clock
>   hw/adc: Add an ADC module for NPCM7XX
>   hw/misc: Add a PWM module for NPCM7XX
>   hw/misc: Add QTest for NPCM7XX PWM Module
>
>  docs/system/arm/nuvoton.rst  |   4 +-
>  hw/adc/meson.build   |   1 +
>  hw/adc/npcm7xx_adc.c | 321 +
>  hw/adc/trace-events  |   5 +
>  hw/arm/npcm7xx.c |  55 ++-
>  hw/misc/meson.build  |   1 +
>  hw/misc/npcm7xx_clk.c| 795 ++-
>  hw/misc/npcm7xx_pwm.c| 559 ++
>  hw/misc/trace-events |   6 +
>  hw/timer/npcm7xx_timer.c |  23 +-
>  include/hw/adc/npcm7xx_adc.h |  72 +++
>  include/hw/arm/npcm7xx.h |   4 +
>  include/hw/misc/npcm7xx_clk.h| 146 +-
>  include/hw/misc/npcm7xx_pwm.h| 106 +
>  include/hw/timer/npcm7xx_timer.h |   1 +
>  meson.build  |   1 +
>  tests/qtest/meson.build  |   4 +-
>  tests/qtest/npcm7xx_adc-test.c   | 400 
>  tests/qtest/npcm7xx_pwm-test.c   | 490 +++
>  19 files changed, 2964 insertions(+), 30 deletions(-)
>  create mode 100644 hw/adc/npcm7xx_adc.c
>  create mode 100644 hw/adc/trace-events
>  create mode 100644 hw/misc/npcm7xx_pwm.c
>  create mode 100644 include/hw/adc/npcm7xx_adc.h
>  create mode 100644 include/hw/misc/npcm7xx_pwm.h
>  create mode 100644 tests/qtest/npcm7xx_adc-test.c
>  create mode 100644 tests/qtest/npcm7xx_pwm-test.c
>
> --
> 2.29.2.684.gfbc64c5ab5-goog
>
>


Re: [PATCH v3 4/5] hw/misc: Add a PWM module for NPCM7XX

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
> identical PWM modules. Each module contains 4 PWM entries. Each PWM has
> two outputs: frequency and duty_cycle. Both are computed using inputs
> from software side.
>
> This module does not model detail pulse signals since it is expensive.
> It also does not model interrupts and watchdogs that are dependant on
> the detail models. The interfaces for these are left in the module so
> that anyone in need for these functionalities can implement on their
> own.
>
> The user can read the duty cycle and frequency using qom-get command.
>
> Reviewed-by: Havard Skinnemoen 
> Reviewed-by: Tyrone Ting 
> Signed-off-by: Hao Wu 
> ---
>  docs/system/arm/nuvoton.rst   |   2 +-
>  hw/arm/npcm7xx.c  |  26 +-
>  hw/misc/meson.build   |   1 +
>  hw/misc/npcm7xx_pwm.c | 559 ++
>  hw/misc/trace-events  |   6 +
>  include/hw/arm/npcm7xx.h  |   2 +
>  include/hw/misc/npcm7xx_pwm.h | 106 +++
>  7 files changed, 699 insertions(+), 3 deletions(-)
>  create mode 100644 hw/misc/npcm7xx_pwm.c
>  create mode 100644 include/hw/misc/npcm7xx_pwm.h
>
> diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
> index 35829f8d0b..a1786342e2 100644
> --- a/docs/system/arm/nuvoton.rst
> +++ b/docs/system/arm/nuvoton.rst
> @@ -42,6 +42,7 @@ Supported devices
>   * USB host (USBH)
>   * GPIO controller
>   * Analog to Digital Converter (ADC)
> + * Pulse Width Modulation (PWM)
>
>  Missing devices
>  ---
> @@ -61,7 +62,6 @@ Missing devices
>   * Peripheral SPI controller (PSPI)
>   * SD/MMC host
>   * PECI interface
> - * Pulse Width Modulation (PWM)
>   * Tachometer
>   * PCI and PCIe root complex and bridges
>   * VDM and MCTP support
> diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
> index b22a8c966d..72040d4079 100644
> --- a/hw/arm/npcm7xx.c
> +++ b/hw/arm/npcm7xx.c
> @@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
>  NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
>  NPCM7XX_EHCI_IRQ= 61,
>  NPCM7XX_OHCI_IRQ= 62,
> +NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
> +NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
>  NPCM7XX_GPIO0_IRQ   = 116,
>  NPCM7XX_GPIO1_IRQ,
>  NPCM7XX_GPIO2_IRQ,
> @@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
>  0xb800, /* CS3 */
>  };
>
> +/* Register base address for each PWM Module */
> +static const hwaddr npcm7xx_pwm_addr[] = {
> +0xf0103000,
> +0xf0104000,
> +};
> +
>  static const struct {
>  hwaddr regs_addr;
>  uint32_t unconnected_pins;
> @@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
>  object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
>  TYPE_NPCM7XX_FIU);
>  }
> +
> +for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
> +object_initialize_child(obj, "pwm[*]", >pwm[i],
> TYPE_NPCM7XX_PWM);
> +}
>  }
>
>  static void npcm7xx_realize(DeviceState *dev, Error **errp)
> @@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error
> **errp)
>  sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
> npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
>
> +/* PWM Modules. Cannot fail. */
> +QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
> +for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
> +SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
> +
> +qdev_connect_clock_in(DEVICE(>pwm[i]), "clock",
> qdev_get_clock_out(
> +DEVICE(>clk), "apb3-clock"));
> +sysbus_realize(sbd, _abort);
> +sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
> +sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
> +}
> +
>  /*
>   * Flash Interface Unit (FIU). Can fail if incorrect number of chip
> selects
>   * specified, but this is a programming error.
> @@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error
> **errp)
>  create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 *
> KiB);
>  create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 *
> KiB);
>  create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 *
> KiB);
> diff --git a/hw/misc/meson.build b/hw/misc/meson.build
> index ce15ffceb9..607cd38a21 100644
> --- a/hw/misc/meson.build
> +++ b/hw/misc/meson.build
> @@ -64,6 +64,7 @@ 

[PATCH v3 3/5] hw/adc: Add an ADC module for NPCM7XX

2020-12-14 Thread Hao Wu via
The ADC is part of NPCM7XX Module. Its behavior is controled by the
ADC_CON register. It converts one of the eight analog inputs into a
digital input and stores it in the ADC_DATA register when enabled.

Users can alter input value by using qom-set QMP command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/adc/meson.build |   1 +
 hw/adc/npcm7xx_adc.c   | 321 ++
 hw/adc/trace-events|   5 +
 hw/arm/npcm7xx.c   |  24 +-
 include/hw/adc/npcm7xx_adc.h   |  72 ++
 include/hw/arm/npcm7xx.h   |   2 +
 meson.build|   1 +
 tests/qtest/meson.build|   3 +-
 tests/qtest/npcm7xx_adc-test.c | 400 +
 10 files changed, 828 insertions(+), 3 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index b00d405d52..35829f8d0b 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -41,6 +41,7 @@ Supported devices
  * Random Number Generator (RNG)
  * USB host (USBH)
  * GPIO controller
+ * Analog to Digital Converter (ADC)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
  * USB device (USBD)
  * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
- * Analog to Digital Converter (ADC)
  * SD/MMC host
  * PECI interface
  * Pulse Width Modulation (PWM)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index 0d62ae96ae..6ddee23813 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
new file mode 100644
index 00..c2c4819d3f
--- /dev/null
+++ b/hw/adc/npcm7xx_adc.c
@@ -0,0 +1,321 @@
+/*
+ * Nuvoton NPCM7xx ADC Module
+ *
+ * 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 "hw/adc/npcm7xx_adc.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+#include "trace.h"
+
+/* 32-bit register indices. */
+enum NPCM7xxADCRegisters {
+NPCM7XX_ADC_CON,
+NPCM7XX_ADC_DATA,
+NPCM7XX_ADC_REGS_END,
+};
+
+/* Register field definitions. */
+#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
+#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
+#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
+#define NPCM7XX_ADC_CON_INT BIT(18)
+#define NPCM7XX_ADC_CON_EN  BIT(17)
+#define NPCM7XX_ADC_CON_RST BIT(16)
+#define NPCM7XX_ADC_CON_CONVBIT(14)
+#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
+
+#define NPCM7XX_ADC_MAX_RESULT  1023
+#define NPCM7XX_ADC_DEFAULT_IREF200
+#define NPCM7XX_ADC_CONV_CYCLES 20
+#define NPCM7XX_ADC_RESET_CYCLES10
+#define NPCM7XX_ADC_R0_INPUT50
+#define NPCM7XX_ADC_R1_INPUT150
+
+static void npcm7xx_adc_reset(NPCM7xxADCState *s)
+{
+timer_del(>conv_timer);
+timer_del(>reset_timer);
+s->con = 0x000c0001;
+s->data = 0x;
+}
+
+static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
+{
+uint32_t result;
+
+result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
+if (result > NPCM7XX_ADC_MAX_RESULT) {
+result = NPCM7XX_ADC_MAX_RESULT;
+}
+
+return result;
+}
+
+static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
+{
+return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
+}
+
+static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
+uint32_t cycles, uint32_t prescaler)
+{
+int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t freq = clock_get_hz(clk);
+int64_t ns;
+
+ns = (NANOSECONDS_PER_SECOND * cycles * prescaler / freq);
+ns += now;
+timer_mod(timer, ns);
+}
+
+static void npcm7xx_adc_start_reset(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >reset_timer, 
NPCM7XX_ADC_RESET_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+

Re: [PATCH v3 1/5] hw/misc: Add clock converter in NPCM7XX CLK module

2020-12-14 Thread Hao Wu via
On Mon, Dec 14, 2020 at 4:13 PM Hao Wu  wrote:

> This patch allows NPCM7XX CLK module to compute clocks that are used by
> other NPCM7XX modules.
>
> Add a new struct NPCM7xxClockConverterState which represents a
> single converter.  Each clock converter in CLK module represents one
> converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
> takes one or more input clocks and converts them into one output clock.
> They form a clock hierarchy in the CLK module and are responsible for
> outputing clocks for various other modules in an NPCM7XX SoC.
>
> Each converter has a function pointer called "convert" which represents
> the unique logic for that converter.
>
> The clock contains two initialization information: ConverterInitInfo and
> ConverterConnectionInfo. They represent the vertices and edges in the
> clock diagram respectively.
>
> Reviewed-by: Havard Skinnemoen 
> Reviewed-by: Tyrone Ting 
> Signed-off-by: Hao Wu 
> ---
>  hw/misc/npcm7xx_clk.c | 795 +-
>  include/hw/misc/npcm7xx_clk.h | 140 +-
>  2 files changed, 927 insertions(+), 8 deletions(-)
>
> diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
> index 6732437fe2..48bc9bdda5 100644
> --- a/hw/misc/npcm7xx_clk.c
> +++ b/hw/misc/npcm7xx_clk.c
> @@ -18,6 +18,7 @@
>
>  #include "hw/misc/npcm7xx_clk.h"
>  #include "hw/timer/npcm7xx_timer.h"
> +#include "hw/qdev-clock.h"
>  #include "migration/vmstate.h"
>  #include "qemu/error-report.h"
>  #include "qemu/log.h"
> @@ -27,9 +28,22 @@
>  #include "trace.h"
>  #include "sysemu/watchdog.h"
>
> +/*
> + * The reference clock hz, and the SECCNT and CNTR25M registers in this
> module,
> + * is always 25 MHz.
> + */
> +#define NPCM7XX_CLOCK_REF_HZ(2500)
> +
> +/* Register Field Definitions */
> +#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
> +
>  #define PLLCON_LOKI BIT(31)
>  #define PLLCON_LOKS BIT(30)
>  #define PLLCON_PWDENBIT(12)
> +#define PLLCON_FBDV(con) extract32((con), 16, 12)
> +#define PLLCON_OTDV2(con) extract32((con), 13, 3)
> +#define PLLCON_OTDV1(con) extract32((con), 8, 3)
> +#define PLLCON_INDV(con) extract32((con), 0, 6)
>
>  enum NPCM7xxCLKRegisters {
>  NPCM7XX_CLK_CLKEN1,
> @@ -89,12 +103,609 @@ static const uint32_t
> cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
>  [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
>  };
>
> -/* Register Field Definitions */
> -#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
> -
>  /* The number of watchdogs that can trigger a reset. */
>  #define NPCM7XX_NR_WATCHDOGS(3)
>
> +/* Clock converter functions */
> +
> +#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
> +#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
> +(obj), TYPE_NPCM7XX_CLOCK_PLL)
> +#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
> +#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
> +(obj), TYPE_NPCM7XX_CLOCK_SEL)
> +#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
> +#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState,
> \
> +(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
> +
> +static void npcm7xx_clk_update_pll(void *opaque)
> +{
> +NPCM7xxClockPLLState *s = opaque;
> +uint32_t con = s->clk->regs[s->reg];
> +uint64_t freq;
> +
> +/* The PLL is grounded if it is not locked yet. */
> +if (con & PLLCON_LOKI) {
> +freq = clock_get_hz(s->clock_in);
> +freq *= PLLCON_FBDV(con);
> +freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
> +} else {
> +freq = 0;
> +}
> +
> +clock_update_hz(s->clock_out, freq);
> +}
> +
> +static void npcm7xx_clk_update_sel(void *opaque)
> +{
> +NPCM7xxClockSELState *s = opaque;
> +uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL],
> s->offset,
> +s->len);
> +
> +if (index >= s->input_size) {
> +qemu_log_mask(LOG_GUEST_ERROR,
> +  "%s: SEL index: %u out of range\n",
> +  __func__, index);
> +index = 0;
> +}
> +clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
> +}
> +
> +static void npcm7xx_clk_update_divider(void *opaque)
> +{
> +NPCM7xxClockDividerState *s = opaque;
> +uint32_t freq;
> +
> +freq = s->divide(s);
> +clock_update_hz(s->clock_out, freq);
> +}
> +
> +static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
> +{
> +return clock_get_hz(s->clock_in) / s->divisor;
> +}
> +
> +static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
> +{
> +return clock_get_hz(s->clock_in) /
> +(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
> +}
> +
> +static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
> +{
> +return divide_by_reg_divisor(s) / 2;
> +}
> +
> +static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
> +{
> +return clock_get_hz(s->clock_in) >>
> +

[PATCH v3 4/5] hw/misc: Add a PWM module for NPCM7XX

2020-12-14 Thread Hao Wu via
The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
identical PWM modules. Each module contains 4 PWM entries. Each PWM has
two outputs: frequency and duty_cycle. Both are computed using inputs
from software side.

This module does not model detail pulse signals since it is expensive.
It also does not model interrupts and watchdogs that are dependant on
the detail models. The interfaces for these are left in the module so
that anyone in need for these functionalities can implement on their
own.

The user can read the duty cycle and frequency using qom-get command.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst   |   2 +-
 hw/arm/npcm7xx.c  |  26 +-
 hw/misc/meson.build   |   1 +
 hw/misc/npcm7xx_pwm.c | 559 ++
 hw/misc/trace-events  |   6 +
 include/hw/arm/npcm7xx.h  |   2 +
 include/hw/misc/npcm7xx_pwm.h | 106 +++
 7 files changed, 699 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/misc/npcm7xx_pwm.h

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 35829f8d0b..a1786342e2 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -42,6 +42,7 @@ Supported devices
  * USB host (USBH)
  * GPIO controller
  * Analog to Digital Converter (ADC)
+ * Pulse Width Modulation (PWM)
 
 Missing devices
 ---
@@ -61,7 +62,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Pulse Width Modulation (PWM)
  * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index b22a8c966d..72040d4079 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
+NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
 0xb800, /* CS3 */
 };
 
+/* Register base address for each PWM Module */
+static const hwaddr npcm7xx_pwm_addr[] = {
+0xf0103000,
+0xf0104000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
 TYPE_NPCM7XX_FIU);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
 
+/* PWM Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
+
+qdev_connect_clock_in(DEVICE(>pwm[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "apb3-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
+sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 * KiB);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ce15ffceb9..607cd38a21 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -64,6 +64,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: 
files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', if_true: files(
diff --git a/hw/misc/npcm7xx_pwm.c 

[PATCH v3 2/5] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2020-12-14 Thread Hao Wu via
This patch makes NPCM7XX Timer to use a the timer clock generated by the
CLK module instead of the magic number TIMER_REF_HZ.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx.c |  5 +
 hw/timer/npcm7xx_timer.c | 23 +--
 include/hw/misc/npcm7xx_clk.h|  6 --
 include/hw/timer/npcm7xx_timer.h |  1 +
 4 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 47e2b6fc40..fabfb1697b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -22,6 +22,7 @@
 #include "hw/char/serial.h"
 #include "hw/loader.h"
 #include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/units.h"
@@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 int first_irq;
 int j;
 
+/* Connect the timer clock. */
+qdev_connect_clock_in(DEVICE(>tim[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "timer-clock"));
+
 sysbus_realize(sbd, _abort);
 sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
 
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index d24445bd6e..8147b53000 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -17,8 +17,8 @@
 #include "qemu/osdep.h"
 
 #include "hw/irq.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
-#include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
@@ -130,7 +130,7 @@ 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 *= clock_get_ns(t->ctrl->clock);
 ns *= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return ns;
@@ -141,7 +141,7 @@ 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 = ns / clock_get_ns(t->ctrl->clock);
 count /= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return count;
@@ -167,7 +167,7 @@ static void 
npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
 int64_t cycles)
 {
 uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
-int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
+int64_t ns = clock_get_ns(t->ctrl->clock) * cycles;
 
 /*
  * The reset function always clears the current timer. The caller of the
@@ -606,10 +606,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
 qemu_irq_lower(s->watchdog_timer.irq);
 }
 
-static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+static void npcm7xx_timer_init(Object *obj)
 {
-NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
+NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
 SysBusDevice *sbd = >parent;
+DeviceState *dev = DEVICE(obj);
 int i;
 NPCM7xxWatchdogTimer *w;
 
@@ -627,11 +628,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error 
**errp)
 npcm7xx_watchdog_timer_expired, w);
 sysbus_init_irq(sbd, >irq);
 
-memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
+memory_region_init_io(>iomem, obj, _timer_ops, s,
   TYPE_NPCM7XX_TIMER, 4 * KiB);
 sysbus_init_mmio(sbd, >iomem);
 qdev_init_gpio_out_named(dev, >reset_signal,
 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
+s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
@@ -675,10 +677,11 @@ static const VMStateDescription 
vmstate_npcm7xx_watchdog_timer = {
 
 static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
 .name = "npcm7xx-timer-ctrl",
-.version_id = 1,
-.minimum_version_id = 1,
+.version_id = 2,
+.minimum_version_id = 2,
 .fields = (VMStateField[]) {
 VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
 VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
  NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
  NPCM7xxTimer),
@@ -697,7 +700,6 @@ static void npcm7xx_timer_class_init(ObjectClass *klass, 
void *data)
 QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
 
 dc->desc = "NPCM7xx Timer Controller";
-dc->realize = npcm7xx_timer_realize;
 dc->vmsd = _npcm7xx_timer_ctrl;
 rc->phases.enter = npcm7xx_timer_enter_reset;
 rc->phases.hold = npcm7xx_timer_hold_reset;
@@ -708,6 +710,7 @@ static const TypeInfo npcm7xx_timer_info = {
 .parent = TYPE_SYS_BUS_DEVICE,
 .instance_size  = sizeof(NPCM7xxTimerCtrlState),
 .class_init = npcm7xx_timer_class_init,
+.instance_init  = npcm7xx_timer_init,
 };
 
 static void 

[PATCH v3 5/5] hw/misc: Add QTest for NPCM7XX PWM Module

2020-12-14 Thread Hao Wu via
We add a qtest for the PWM in the previous patch. It proves it works as
expected.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 tests/qtest/meson.build|   1 +
 tests/qtest/npcm7xx_pwm-test.c | 490 +
 2 files changed, 491 insertions(+)
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build
index 955710d1c5..0b5467f084 100644
--- a/tests/qtest/meson.build
+++ b/tests/qtest/meson.build
@@ -136,6 +136,7 @@ qtests_sparc64 = \
 qtests_npcm7xx = \
   ['npcm7xx_adc-test',
'npcm7xx_gpio-test',
+   'npcm7xx_pwm-test',
'npcm7xx_rng-test',
'npcm7xx_timer-test',
'npcm7xx_watchdog_timer-test']
diff --git a/tests/qtest/npcm7xx_pwm-test.c b/tests/qtest/npcm7xx_pwm-test.c
new file mode 100644
index 00..33fbdf5f54
--- /dev/null
+++ b/tests/qtest/npcm7xx_pwm-test.c
@@ -0,0 +1,490 @@
+/*
+ * QTests for Nuvoton NPCM7xx PWM Modules.
+ *
+ * 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 "qemu/bitops.h"
+#include "libqos/libqtest.h"
+#include "qapi/qmp/qdict.h"
+#include "qapi/qmp/qnum.h"
+
+#define REF_HZ  2500
+
+/* Register field definitions. */
+#define CH_EN   BIT(0)
+#define CH_INV  BIT(2)
+#define CH_MOD  BIT(3)
+
+/* Registers shared between all PWMs in a module */
+#define PPR 0x00
+#define CSR 0x04
+#define PCR 0x08
+#define PIER0x3c
+#define PIIR0x40
+
+/* CLK module related */
+#define CLK_BA  0xf0801000
+#define CLKSEL  0x04
+#define CLKDIV1 0x08
+#define CLKDIV2 0x2c
+#define PLLCON0 0x0c
+#define PLLCON1 0x10
+#define PLL_INDV(rv)extract32((rv), 0, 6)
+#define PLL_FBDV(rv)extract32((rv), 16, 12)
+#define PLL_OTDV1(rv)   extract32((rv), 8, 3)
+#define PLL_OTDV2(rv)   extract32((rv), 13, 3)
+#define APB3CKDIV(rv)   extract32((rv), 28, 2)
+#define CLK2CKDIV(rv)   extract32((rv), 0, 1)
+#define CLK4CKDIV(rv)   extract32((rv), 26, 2)
+#define CPUCKSEL(rv)extract32((rv), 0, 2)
+
+#define MAX_DUTY100
+
+typedef struct PWMModule {
+int irq;
+uint64_t base_addr;
+} PWMModule;
+
+typedef struct PWM {
+uint32_t cnr_offset;
+uint32_t cmr_offset;
+uint32_t pdr_offset;
+uint32_t pwdr_offset;
+} PWM;
+
+typedef struct TestData {
+const PWMModule *module;
+const PWM *pwm;
+} TestData;
+
+static const PWMModule pwm_module_list[] = {
+{
+.irq= 93,
+.base_addr  = 0xf0103000
+},
+{
+.irq= 94,
+.base_addr  = 0xf0104000
+}
+};
+
+static const PWM pwm_list[] = {
+{
+.cnr_offset = 0x0c,
+.cmr_offset = 0x10,
+.pdr_offset = 0x14,
+.pwdr_offset= 0x44,
+},
+{
+.cnr_offset = 0x18,
+.cmr_offset = 0x1c,
+.pdr_offset = 0x20,
+.pwdr_offset= 0x48,
+},
+{
+.cnr_offset = 0x24,
+.cmr_offset = 0x28,
+.pdr_offset = 0x2c,
+.pwdr_offset= 0x4c,
+},
+{
+.cnr_offset = 0x30,
+.cmr_offset = 0x34,
+.pdr_offset = 0x38,
+.pwdr_offset= 0x50,
+},
+};
+
+static const int ppr_base[] = { 0, 0, 8, 8 };
+static const int csr_base[] = { 0, 4, 8, 12 };
+static const int pcr_base[] = { 0, 8, 12, 16 };
+
+static const uint32_t ppr_list[] = {
+0,
+1,
+10,
+100,
+255, /* Max possible value. */
+};
+
+static const uint32_t csr_list[] = {
+0,
+1,
+2,
+3,
+4, /* Max possible value. */
+};
+
+static const uint32_t cnr_list[] = {
+0,
+1,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+static const uint32_t cmr_list[] = {
+0,
+1,
+10,
+50,
+100,
+150,
+200,
+1000,
+1,
+65535, /* Max possible value. */
+};
+
+/* Returns the index of the PWM module. */
+static int pwm_module_index(const PWMModule *module)
+{
+ptrdiff_t diff = module - pwm_module_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_module_list));
+
+return diff;
+}
+
+/* Returns the index of the PWM entry. */
+static int pwm_index(const PWM *pwm)
+{
+ptrdiff_t diff = pwm - pwm_list;
+
+g_assert_true(diff >= 0 && diff < ARRAY_SIZE(pwm_list));
+
+return diff;
+}
+
+static 

[PATCH v3 1/5] hw/misc: Add clock converter in NPCM7XX CLK module

2020-12-14 Thread Hao Wu via
This patch allows NPCM7XX CLK module to compute clocks that are used by
other NPCM7XX modules.

Add a new struct NPCM7xxClockConverterState which represents a
single converter.  Each clock converter in CLK module represents one
converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
takes one or more input clocks and converts them into one output clock.
They form a clock hierarchy in the CLK module and are responsible for
outputing clocks for various other modules in an NPCM7XX SoC.

Each converter has a function pointer called "convert" which represents
the unique logic for that converter.

The clock contains two initialization information: ConverterInitInfo and
ConverterConnectionInfo. They represent the vertices and edges in the
clock diagram respectively.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/misc/npcm7xx_clk.c | 795 +-
 include/hw/misc/npcm7xx_clk.h | 140 +-
 2 files changed, 927 insertions(+), 8 deletions(-)

diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 6732437fe2..48bc9bdda5 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -18,6 +18,7 @@
 
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -27,9 +28,22 @@
 #include "trace.h"
 #include "sysemu/watchdog.h"
 
+/*
+ * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
+ * is always 25 MHz.
+ */
+#define NPCM7XX_CLOCK_REF_HZ(2500)
+
+/* Register Field Definitions */
+#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
+
 #define PLLCON_LOKI BIT(31)
 #define PLLCON_LOKS BIT(30)
 #define PLLCON_PWDENBIT(12)
+#define PLLCON_FBDV(con) extract32((con), 16, 12)
+#define PLLCON_OTDV2(con) extract32((con), 13, 3)
+#define PLLCON_OTDV1(con) extract32((con), 8, 3)
+#define PLLCON_INDV(con) extract32((con), 0, 6)
 
 enum NPCM7xxCLKRegisters {
 NPCM7XX_CLK_CLKEN1,
@@ -89,12 +103,609 @@ static const uint32_t 
cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
 [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
 };
 
-/* Register Field Definitions */
-#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
-
 /* The number of watchdogs that can trigger a reset. */
 #define NPCM7XX_NR_WATCHDOGS(3)
 
+/* Clock converter functions */
+
+#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
+#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
+(obj), TYPE_NPCM7XX_CLOCK_PLL)
+#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
+#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
+(obj), TYPE_NPCM7XX_CLOCK_SEL)
+#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
+#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
+(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
+
+static void npcm7xx_clk_update_pll(void *opaque)
+{
+NPCM7xxClockPLLState *s = opaque;
+uint32_t con = s->clk->regs[s->reg];
+uint64_t freq;
+
+/* The PLL is grounded if it is not locked yet. */
+if (con & PLLCON_LOKI) {
+freq = clock_get_hz(s->clock_in);
+freq *= PLLCON_FBDV(con);
+freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
+} else {
+freq = 0;
+}
+
+clock_update_hz(s->clock_out, freq);
+}
+
+static void npcm7xx_clk_update_sel(void *opaque)
+{
+NPCM7xxClockSELState *s = opaque;
+uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
+s->len);
+
+if (index >= s->input_size) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: SEL index: %u out of range\n",
+  __func__, index);
+index = 0;
+}
+clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
+}
+
+static void npcm7xx_clk_update_divider(void *opaque)
+{
+NPCM7xxClockDividerState *s = opaque;
+uint32_t freq;
+
+freq = s->divide(s);
+clock_update_hz(s->clock_out, freq);
+}
+
+static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) / s->divisor;
+}
+
+static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) /
+(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
+}
+
+static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
+{
+return divide_by_reg_divisor(s) / 2;
+}
+
+static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) >>
+extract32(s->clk->regs[s->reg], s->offset, s->len);
+}
+
+static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
+{
+switch (reg) {
+case NPCM7XX_CLK_PLLCON0:
+return NPCM7XX_CLOCK_PLL0;
+case NPCM7XX_CLK_PLLCON1:
+return NPCM7XX_CLOCK_PLL1;
+case NPCM7XX_CLK_PLLCON2:
+return 

[PATCH v3 0/5] Additional NPCM7xx devices

2020-12-14 Thread Hao Wu via
This patch series include a few more NPCM7XX devices including

- Analog Digital Converter (ADC)
- Pulse Width Modulation (PWM)

We also modified the CLK module to generate clock values using qdev_clock.
These clocks are used to determine various clocks in NPCM7XX devices.

Thank you for your review.

Changes since v2:
- Split PWM test into a separate patch in the patch set
- Add trace events for PWM's update_freq/update_duty
- Add trace events for ioread/iowrite in ADC and PWM
- Use timer_get_ns in hw/timer/npcm7xx_timer.c
- Update commit message in ADC/PWM to mention qom-get/set method for usage
- Fix typos

Changes since v1:
- We removed the IPMI and KCS related code from this patch set.

Hao Wu (5):
  hw/misc: Add clock converter in NPCM7XX CLK module
  hw/timer: Refactor NPCM7XX Timer to use CLK clock
  hw/adc: Add an ADC module for NPCM7XX
  hw/misc: Add a PWM module for NPCM7XX
  hw/misc: Add QTest for NPCM7XX PWM Module

 docs/system/arm/nuvoton.rst  |   4 +-
 hw/adc/meson.build   |   1 +
 hw/adc/npcm7xx_adc.c | 321 +
 hw/adc/trace-events  |   5 +
 hw/arm/npcm7xx.c |  55 ++-
 hw/misc/meson.build  |   1 +
 hw/misc/npcm7xx_clk.c| 795 ++-
 hw/misc/npcm7xx_pwm.c| 559 ++
 hw/misc/trace-events |   6 +
 hw/timer/npcm7xx_timer.c |  23 +-
 include/hw/adc/npcm7xx_adc.h |  72 +++
 include/hw/arm/npcm7xx.h |   4 +
 include/hw/misc/npcm7xx_clk.h| 146 +-
 include/hw/misc/npcm7xx_pwm.h| 106 +
 include/hw/timer/npcm7xx_timer.h |   1 +
 meson.build  |   1 +
 tests/qtest/meson.build  |   4 +-
 tests/qtest/npcm7xx_adc-test.c   | 400 
 tests/qtest/npcm7xx_pwm-test.c   | 490 +++
 19 files changed, 2964 insertions(+), 30 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/adc/trace-events
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

-- 
2.29.2.684.gfbc64c5ab5-goog




Re: [PATCH v2 3/4] hw/adc: Add an ADC module for NPCM7XX

2020-12-14 Thread Hao Wu via
Thanks for your comment! We'll incorporate them into our next patch version.

We plan to let the user use QOM get/set QMP commands to control ADC/PWM
values, similar to hw/misc/tmp105.c. The user can set a voltage value as
input using QOM-set, and the QEMU guest can read the converted value
through this module. Similar for PWM, the user can read the duty-cycle and
frequency using QOM-get. The user can also run a third-party simulator and
alter these values during execution. Our test code also shows how to deal
with these values.

If you have a better suggestion, please let us know.

On Sun, Dec 13, 2020 at 3:47 AM Philippe Mathieu-Daudé 
wrote:

> On 12/11/20 11:22 PM, Hao Wu via wrote:
> > The ADC is part of NPCM7XX Module. Its behavior is controled by the
> > ADC_CON register. It converts one of the eight analog inputs into a
> > digital input and stores it in the ADC_DATA register when enabled.
> >
> > Reviewed-by: Havard Skinnemoen 
> > Reviewed-by: Tyrone Ting 
> > Signed-off-by: Hao Wu 
> > ---
> >  docs/system/arm/nuvoton.rst|   2 +-
> >  hw/adc/meson.build |   1 +
> >  hw/adc/npcm7xx_adc.c   | 318 ++
> >  hw/arm/npcm7xx.c   |  24 +-
> >  include/hw/adc/npcm7xx_adc.h   |  72 ++
> >  include/hw/arm/npcm7xx.h   |   2 +
> >  tests/qtest/meson.build|   3 +-
> >  tests/qtest/npcm7xx_adc-test.c | 400 +
> >  8 files changed, 819 insertions(+), 3 deletions(-)
> >  create mode 100644 hw/adc/npcm7xx_adc.c
> >  create mode 100644 include/hw/adc/npcm7xx_adc.h
> >  create mode 100644 tests/qtest/npcm7xx_adc-test.c
> ...
>
> > +static void npcm7xx_adc_init(Object *obj)
> > +{
> > +NPCM7xxADCState *s = NPCM7XX_ADC(obj);
> > +SysBusDevice *sbd = >parent;
> > +int i;
> > +
> > +sysbus_init_irq(sbd, >irq);
> > +
> > +timer_init_ns(>conv_timer, QEMU_CLOCK_VIRTUAL,
> > +npcm7xx_adc_convert_done, s);
> > +timer_init_ns(>reset_timer, QEMU_CLOCK_VIRTUAL,
> > +npcm7xx_adc_reset_done, s);
> > +memory_region_init_io(>iomem, obj, _adc_ops, s,
> > +  TYPE_NPCM7XX_ADC, 4 * KiB);
> > +sysbus_init_mmio(sbd, >iomem);
> > +s->clock = qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL);
> > +
> > +for (i = 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) {
> > +object_property_add_uint32_ptr(obj, "adci[*]",
> > +>adci[i], OBJ_PROP_FLAG_WRITE);
>
> How do you use this, any example?
>
> FWIW I'm experimenting with other ADC to use the "audio/audio.h"
> API (which is not voice/audio specific, but generic DSP), then
> I can pass any QEMU source and consume it using AUD_read() to fill
> the ADC buffer (device sram or in main ram).
>
> But I'm doing that alone during my free time, so don't expect it
> any time soon :(
>
> > +}
> > +object_property_add_uint32_ptr(obj, "vref",
> > +>vref, OBJ_PROP_FLAG_WRITE);
> > +npcm7xx_adc_calibrate(s);
> > +}
>


Re: [PATCH 0/7] Additional NPCM7xx devices and IPMI BMC emulation support

2020-12-11 Thread Hao Wu via
Thanks for the comments!

I've removed IPMI part from the patch sets. I'll send a separate patch sets
once the refactor is done. I'll also include Havard's documentation in it.

I haven't thought of a better name. We can update the name accordingly.

On Fri, Dec 11, 2020 at 4:26 PM Havard Skinnemoen 
wrote:

> On Fri, Dec 11, 2020 at 4:16 PM Corey Minyard  wrote:
>
>> On Fri, Dec 11, 2020 at 12:56:07PM -0800, Hao Wu wrote:
>> > Tl,dr: We'll remove the IPMI changes from the current patch set and
>> > refactor
>> >   them in a separate patch set.
>> >
>> > Thank you for your review! On high level, we are trying to emulate the
>> BMC
>> > side of the IPMI protocol. So we cannot directly use the existing IPMI
>> code.
>> > However, they do have a lot in duplication as you pointed out. So we'll
>> > refactor
>> > the existing IPMI code and update in a way that we only add the required
>> > functionality.
>>
>> Ah, I didn't figure that out from what you posted.  So the idea is you
>> can create the BMC side of the system in one qemu session with your
>> changes and then you connect it to a host system running qemu with the
>> host side of the interface.
>>
>> The wire protocol is basically symmetric, but the command handling will
>> need to be separate.  So you probably want to split out the base
>> protocol from ipmi_bmc_extern into its own file and use that from your
>> own file, to avoid the duplication.
>>
>> You need to do proper ATTN handling on the BMC side.  And you will also
>> need ties into GPIOs and whatnot for doing the reset, NMI, etc.
>>
>> "ipmi_host" is probably not the best name.  At least to me that implied
>> the host side of the interface.  I'm not coming up with something I
>> really like, though.  Maybe "bmc_host"?  That's more descriptive, though
>> I'm sure a better name exists.  Then you could have "bmc_host_extern"
>> for the protocol.  If you come up with a better naming scheme, the
>> existing files can be renamed, too.
>>
>
> The naming is my fault.
>
> My thinking was that ipmi-host-extern is to the BMC what ipmi-bmc-extern
> is to the host. That is, the former represents the host as seen by the BMC,
> and the latter represents the BMC as seen by the host.
>
> I sent some docs to the list earlier this year, but sadly, I never got
> around to follow up. You can see the generated docs here:
>
> https://hskinnemoen.github.io/qemu/specs/ipmi.html
>
> Hao, perhaps you should include my documentation patches in your next IPMI
> series? If we come up with a better naming scheme for both sides, I can
> update the docs accordingly.
>
> Havard
>
>
>> Thanks,
>>
>> -corey
>>
>> >
>> > As for the KCS module, the BMC side of the protocol is the opposite
>> > direction
>> > of the existing ipmi_kcs.c code which is on the host/CPU side. For
>> example,
>> > in READ_STATE the CPU would read data while the BMC would write data.
>> > So we can't directly use the same implementation. (They're different
>> files
>> > in
>> > Linux either.) However, we can refactor it to re-use some of the common
>> > definitions.
>> >
>> > We would like to remove the IPMI and KCS stuff from the current patch
>> set.
>> > We'll send the refactored code in a separate patch set after addressing
>> > your concerns.
>> >
>> > Thanks again for the review!
>> >
>> > On Thu, Dec 10, 2020 at 7:04 PM Corey Minyard  wrote:
>> >
>> > > On Thu, Dec 10, 2020 at 05:51:49PM -0800, Hao Wu wrote:
>> > > > This patch series include a few more NPCM7XX devices including
>> > > >
>> > > > - Analog Digital Converter (ADC)
>> > > > - Pulse Width Modulation (PWM)
>> > > > - Keyboard Style Controller (KSC)
>> > > >
>> > > > To utilize these modules we also add two extra functionalities:
>> > > >
>> > > > 1. We modified the CLK module to generate clock values using
>> qdev_clock.
>> > > >These clocks are used to determine various clocks in NPCM7XX
>> devices.
>> > > > 2. We added support for emulating IPMI responder devices in BMC
>> machines,
>> > > >similar to the existing IPMI device support for CPU emulation.
>> This
>> > > allows
>> > > >a qemu instance running BMC firmware to serve as an external BMC
>> for
>> > > a qemu
>> > > >instance running server software. It utilizes the KCS module we
>> > > implemented.
>> > >
>> > > Looking at the IPMI changes, why didn't you just re-use the existing
>> > > IPMI infrastructure?  ipmi_host.[ch] is basically a subset of
>> ipmi.[ch],
>> > > and the ipmi_host_extern looks like a copy of of ipmi_bmc_extern with
>> > > some names changed.  That kind of code duplication is not acceptable.
>> > > Plus you copied my code and removed my copyrights, which is really
>> > > not acceptable and illegal.
>> > >
>> > > I'm not exactly sure why you needed you own KCS interface, either.  It
>> > > looks like the interface is somewhat different in some ways, but
>> > > integrating it into the current KCS code is probably a better choice
>> if
>> > > that can be done.
>> > >
>> > > -corey
>> 

[PATCH v2 3/4] hw/adc: Add an ADC module for NPCM7XX

2020-12-11 Thread Hao Wu via
The ADC is part of NPCM7XX Module. Its behavior is controled by the
ADC_CON register. It converts one of the eight analog inputs into a
digital input and stores it in the ADC_DATA register when enabled.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/adc/meson.build |   1 +
 hw/adc/npcm7xx_adc.c   | 318 ++
 hw/arm/npcm7xx.c   |  24 +-
 include/hw/adc/npcm7xx_adc.h   |  72 ++
 include/hw/arm/npcm7xx.h   |   2 +
 tests/qtest/meson.build|   3 +-
 tests/qtest/npcm7xx_adc-test.c | 400 +
 8 files changed, 819 insertions(+), 3 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index b00d405d52..35829f8d0b 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -41,6 +41,7 @@ Supported devices
  * Random Number Generator (RNG)
  * USB host (USBH)
  * GPIO controller
+ * Analog to Digital Converter (ADC)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
  * USB device (USBD)
  * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
- * Analog to Digital Converter (ADC)
  * SD/MMC host
  * PECI interface
  * Pulse Width Modulation (PWM)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index 0d62ae96ae..6ddee23813 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
new file mode 100644
index 00..4492303977
--- /dev/null
+++ b/hw/adc/npcm7xx_adc.c
@@ -0,0 +1,318 @@
+/*
+ * Nuvoton NPCM7xx ADC Module
+ *
+ * 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 "hw/adc/npcm7xx_adc.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+
+/* 32-bit register indices. */
+enum NPCM7xxADCRegisters {
+NPCM7XX_ADC_CON,
+NPCM7XX_ADC_DATA,
+NPCM7XX_ADC_REGS_END,
+};
+
+/* Register field definitions. */
+#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
+#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
+#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
+#define NPCM7XX_ADC_CON_INT BIT(18)
+#define NPCM7XX_ADC_CON_EN  BIT(17)
+#define NPCM7XX_ADC_CON_RST BIT(16)
+#define NPCM7XX_ADC_CON_CONVBIT(14)
+#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
+
+#define NPCM7XX_ADC_MAX_RESULT  1023
+#define NPCM7XX_ADC_DEFAULT_IREF200
+#define NPCM7XX_ADC_CONV_CYCLES 20
+#define NPCM7XX_ADC_RESET_CYCLES10
+#define NPCM7XX_ADC_R0_INPUT50
+#define NPCM7XX_ADC_R1_INPUT150
+
+static void npcm7xx_adc_reset(NPCM7xxADCState *s)
+{
+timer_del(>conv_timer);
+timer_del(>reset_timer);
+s->con = 0x000c0001;
+s->data = 0x;
+}
+
+static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
+{
+uint32_t result;
+
+result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
+if (result > NPCM7XX_ADC_MAX_RESULT) {
+result = NPCM7XX_ADC_MAX_RESULT;
+}
+
+return result;
+}
+
+static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
+{
+return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
+}
+
+static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
+uint32_t cycles, uint32_t prescaler)
+{
+int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t freq = clock_get_hz(clk);
+int64_t ns;
+
+ns = (NANOSECONDS_PER_SECOND * cycles * prescaler / freq);
+ns += now;
+timer_mod(timer, ns);
+}
+
+static void npcm7xx_adc_start_reset(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >reset_timer, 
NPCM7XX_ADC_RESET_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >conv_timer, NPCM7XX_ADC_CONV_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_reset_done(void *opaque)
+{
+NPCM7xxADCState *s = opaque;
+
+npcm7xx_adc_reset(s);

[PATCH v2 1/4] hw/misc: Add clock converter in NPCM7XX CLK module

2020-12-11 Thread Hao Wu via
This patch allows NPCM7XX CLK module to compute clocks that are used by
other NPCM7XX modules.

Add a new struct NPCM7xxClockConverterState which represents a
single converter.  Each clock converter in CLK module represents one
converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
takes one or more input clocks and converts them into one output clock.
They form a clock hierarchy in the CLK module and are responsible for
outputing clocks for various other modules in an NPCM7XX SoC.

Each converter has a function pointer called "convert" which represents
the unique logic for that converter.

The clock contains two initialization information: ConverterInitInfo and
ConverterConnectionInfo. They represent the vertices and edges in the
clock diagram respectively.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/misc/npcm7xx_clk.c | 795 +-
 include/hw/misc/npcm7xx_clk.h | 140 +-
 2 files changed, 927 insertions(+), 8 deletions(-)

diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 6732437fe2..48bc9bdda5 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -18,6 +18,7 @@
 
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -27,9 +28,22 @@
 #include "trace.h"
 #include "sysemu/watchdog.h"
 
+/*
+ * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
+ * is always 25 MHz.
+ */
+#define NPCM7XX_CLOCK_REF_HZ(2500)
+
+/* Register Field Definitions */
+#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
+
 #define PLLCON_LOKI BIT(31)
 #define PLLCON_LOKS BIT(30)
 #define PLLCON_PWDENBIT(12)
+#define PLLCON_FBDV(con) extract32((con), 16, 12)
+#define PLLCON_OTDV2(con) extract32((con), 13, 3)
+#define PLLCON_OTDV1(con) extract32((con), 8, 3)
+#define PLLCON_INDV(con) extract32((con), 0, 6)
 
 enum NPCM7xxCLKRegisters {
 NPCM7XX_CLK_CLKEN1,
@@ -89,12 +103,609 @@ static const uint32_t 
cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
 [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
 };
 
-/* Register Field Definitions */
-#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
-
 /* The number of watchdogs that can trigger a reset. */
 #define NPCM7XX_NR_WATCHDOGS(3)
 
+/* Clock converter functions */
+
+#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
+#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
+(obj), TYPE_NPCM7XX_CLOCK_PLL)
+#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
+#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
+(obj), TYPE_NPCM7XX_CLOCK_SEL)
+#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
+#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
+(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
+
+static void npcm7xx_clk_update_pll(void *opaque)
+{
+NPCM7xxClockPLLState *s = opaque;
+uint32_t con = s->clk->regs[s->reg];
+uint64_t freq;
+
+/* The PLL is grounded if it is not locked yet. */
+if (con & PLLCON_LOKI) {
+freq = clock_get_hz(s->clock_in);
+freq *= PLLCON_FBDV(con);
+freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
+} else {
+freq = 0;
+}
+
+clock_update_hz(s->clock_out, freq);
+}
+
+static void npcm7xx_clk_update_sel(void *opaque)
+{
+NPCM7xxClockSELState *s = opaque;
+uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
+s->len);
+
+if (index >= s->input_size) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: SEL index: %u out of range\n",
+  __func__, index);
+index = 0;
+}
+clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
+}
+
+static void npcm7xx_clk_update_divider(void *opaque)
+{
+NPCM7xxClockDividerState *s = opaque;
+uint32_t freq;
+
+freq = s->divide(s);
+clock_update_hz(s->clock_out, freq);
+}
+
+static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) / s->divisor;
+}
+
+static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) /
+(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
+}
+
+static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
+{
+return divide_by_reg_divisor(s) / 2;
+}
+
+static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) >>
+extract32(s->clk->regs[s->reg], s->offset, s->len);
+}
+
+static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
+{
+switch (reg) {
+case NPCM7XX_CLK_PLLCON0:
+return NPCM7XX_CLOCK_PLL0;
+case NPCM7XX_CLK_PLLCON1:
+return NPCM7XX_CLOCK_PLL1;
+case NPCM7XX_CLK_PLLCON2:
+return 

[PATCH v2 2/4] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2020-12-11 Thread Hao Wu via
This patch makes NPCM7XX Timer to use a the timer clock generated by the
CLK module instead of the magic nubmer TIMER_REF_HZ.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx.c |  5 +
 hw/timer/npcm7xx_timer.c | 25 +++--
 include/hw/misc/npcm7xx_clk.h|  6 --
 include/hw/timer/npcm7xx_timer.h |  1 +
 4 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 47e2b6fc40..fabfb1697b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -22,6 +22,7 @@
 #include "hw/char/serial.h"
 #include "hw/loader.h"
 #include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/units.h"
@@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 int first_irq;
 int j;
 
+/* Connect the timer clock. */
+qdev_connect_clock_in(DEVICE(>tim[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "timer-clock"));
+
 sysbus_realize(sbd, _abort);
 sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
 
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index d24445bd6e..9469c959e2 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -17,8 +17,8 @@
 #include "qemu/osdep.h"
 
 #include "hw/irq.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
-#include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
@@ -130,7 +130,7 @@ 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 *= NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock);
 ns *= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return ns;
@@ -141,7 +141,7 @@ 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 = ns / (NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock));
 count /= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return count;
@@ -166,8 +166,10 @@ static uint32_t npcm7xx_watchdog_timer_prescaler(const 
NPCM7xxWatchdogTimer *t)
 static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
 int64_t cycles)
 {
+g_assert(clock_get_hz(t->ctrl->clock) == 2500);
 uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
-int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
+int64_t ns = (NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock))
+* cycles;
 
 /*
  * The reset function always clears the current timer. The caller of the
@@ -606,10 +608,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
 qemu_irq_lower(s->watchdog_timer.irq);
 }
 
-static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+static void npcm7xx_timer_init(Object *obj)
 {
-NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
+NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
 SysBusDevice *sbd = >parent;
+DeviceState *dev = DEVICE(obj);
 int i;
 NPCM7xxWatchdogTimer *w;
 
@@ -627,11 +630,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error 
**errp)
 npcm7xx_watchdog_timer_expired, w);
 sysbus_init_irq(sbd, >irq);
 
-memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
+memory_region_init_io(>iomem, obj, _timer_ops, s,
   TYPE_NPCM7XX_TIMER, 4 * KiB);
 sysbus_init_mmio(sbd, >iomem);
 qdev_init_gpio_out_named(dev, >reset_signal,
 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
+s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
@@ -675,10 +679,11 @@ static const VMStateDescription 
vmstate_npcm7xx_watchdog_timer = {
 
 static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
 .name = "npcm7xx-timer-ctrl",
-.version_id = 1,
-.minimum_version_id = 1,
+.version_id = 2,
+.minimum_version_id = 2,
 .fields = (VMStateField[]) {
 VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
 VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
  NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
  NPCM7xxTimer),
@@ -697,7 +702,6 @@ static void npcm7xx_timer_class_init(ObjectClass *klass, 
void *data)
 QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
 
 dc->desc = "NPCM7xx Timer Controller";
-dc->realize = npcm7xx_timer_realize;
 dc->vmsd = _npcm7xx_timer_ctrl;
 rc->phases.enter = npcm7xx_timer_enter_reset;
 rc->phases.hold = npcm7xx_timer_hold_reset;
@@ -708,6 +712,7 @@ static const TypeInfo npcm7xx_timer_info = {
 .parent 

[PATCH v2 4/4] hw/misc: Add a PWM module for NPCM7XX

2020-12-11 Thread Hao Wu via
The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
identical PWM modules. Each module contains 4 PWM entries. Each PWM has
two outputs: frequency and duty_cycle. Both are computed using inputs
from software side.

This module does not model detail pulse signals since it is expensive.
It also does not model interrupts and watchdogs that are dependant on
the detail models. The interfaces for these are left in the module so
that anyone in need for these functionalities can implement on their
own.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/arm/npcm7xx.c   |  26 +-
 hw/misc/meson.build|   1 +
 hw/misc/npcm7xx_pwm.c  | 535 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/misc/npcm7xx_pwm.h  | 106 +++
 tests/qtest/meson.build|   1 +
 tests/qtest/npcm7xx_pwm-test.c | 490 ++
 8 files changed, 1160 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 35829f8d0b..a1786342e2 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -42,6 +42,7 @@ Supported devices
  * USB host (USBH)
  * GPIO controller
  * Analog to Digital Converter (ADC)
+ * Pulse Width Modulation (PWM)
 
 Missing devices
 ---
@@ -61,7 +62,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Pulse Width Modulation (PWM)
  * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index b22a8c966d..72040d4079 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
+NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
 0xb800, /* CS3 */
 };
 
+/* Register base address for each PWM Module */
+static const hwaddr npcm7xx_pwm_addr[] = {
+0xf0103000,
+0xf0104000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
 TYPE_NPCM7XX_FIU);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
 
+/* PWM Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
+
+qdev_connect_clock_in(DEVICE(>pwm[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "apb3-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
+sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 * KiB);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ce15ffceb9..607cd38a21 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -64,6 +64,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: 
files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', 

[PATCH v2 0/4] Additional NPCM7xx devices

2020-12-11 Thread Hao Wu via
This patch series include a few more NPCM7XX devices including

- Analog Digital Converter (ADC)
- Pulse Width Modulation (PWM)

We also modified the CLK module to generate clock values using qdev_clock.
These clocks are used to determine various clocks in NPCM7XX devices.

Thank you for your review!

Changes since v1:
We removed the IPMI and KCS related code from this patch set.

Hao Wu (4):
  hw/misc: Add clock converter in NPCM7XX CLK module
  hw/timer: Refactor NPCM7XX Timer to use CLK clock
  hw/adc: Add an ADC module for NPCM7XX
  hw/misc: Add a PWM module for NPCM7XX

 docs/system/arm/nuvoton.rst  |   4 +-
 hw/adc/meson.build   |   1 +
 hw/adc/npcm7xx_adc.c | 318 +
 hw/arm/npcm7xx.c |  55 ++-
 hw/misc/meson.build  |   1 +
 hw/misc/npcm7xx_clk.c| 795 ++-
 hw/misc/npcm7xx_pwm.c| 535 +
 hw/timer/npcm7xx_timer.c |  25 +-
 include/hw/adc/npcm7xx_adc.h |  72 +++
 include/hw/arm/npcm7xx.h |   4 +
 include/hw/misc/npcm7xx_clk.h| 146 +-
 include/hw/misc/npcm7xx_pwm.h| 106 +
 include/hw/timer/npcm7xx_timer.h |   1 +
 tests/qtest/meson.build  |   4 +-
 tests/qtest/npcm7xx_adc-test.c   | 400 
 tests/qtest/npcm7xx_pwm-test.c   | 490 +++
 16 files changed, 2927 insertions(+), 30 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

-- 
2.29.2.684.gfbc64c5ab5-goog




Re: [PATCH 0/7] Additional NPCM7xx devices and IPMI BMC emulation support

2020-12-11 Thread Hao Wu via
Tl,dr: We'll remove the IPMI changes from the current patch set and
refactor
  them in a separate patch set.

Thank you for your review! On high level, we are trying to emulate the BMC
side of the IPMI protocol. So we cannot directly use the existing IPMI code.
However, they do have a lot in duplication as you pointed out. So we'll
refactor
the existing IPMI code and update in a way that we only add the required
functionality.

As for the KCS module, the BMC side of the protocol is the opposite
direction
of the existing ipmi_kcs.c code which is on the host/CPU side. For example,
in READ_STATE the CPU would read data while the BMC would write data.
So we can't directly use the same implementation. (They're different files
in
Linux either.) However, we can refactor it to re-use some of the common
definitions.

We would like to remove the IPMI and KCS stuff from the current patch set.
We'll send the refactored code in a separate patch set after addressing
your concerns.

Thanks again for the review!

On Thu, Dec 10, 2020 at 7:04 PM Corey Minyard  wrote:

> On Thu, Dec 10, 2020 at 05:51:49PM -0800, Hao Wu wrote:
> > This patch series include a few more NPCM7XX devices including
> >
> > - Analog Digital Converter (ADC)
> > - Pulse Width Modulation (PWM)
> > - Keyboard Style Controller (KSC)
> >
> > To utilize these modules we also add two extra functionalities:
> >
> > 1. We modified the CLK module to generate clock values using qdev_clock.
> >These clocks are used to determine various clocks in NPCM7XX devices.
> > 2. We added support for emulating IPMI responder devices in BMC machines,
> >similar to the existing IPMI device support for CPU emulation. This
> allows
> >a qemu instance running BMC firmware to serve as an external BMC for
> a qemu
> >instance running server software. It utilizes the KCS module we
> implemented.
>
> Looking at the IPMI changes, why didn't you just re-use the existing
> IPMI infrastructure?  ipmi_host.[ch] is basically a subset of ipmi.[ch],
> and the ipmi_host_extern looks like a copy of of ipmi_bmc_extern with
> some names changed.  That kind of code duplication is not acceptable.
> Plus you copied my code and removed my copyrights, which is really
> not acceptable and illegal.
>
> I'm not exactly sure why you needed you own KCS interface, either.  It
> looks like the interface is somewhat different in some ways, but
> integrating it into the current KCS code is probably a better choice if
> that can be done.
>
> -corey
>
> >
> > Hao Wu (7):
> >   hw/misc: Add clock converter in NPCM7XX CLK module
> >   hw/timer: Refactor NPCM7XX Timer to use CLK clock
> >   hw/adc: Add an ADC module for NPCM7XX
> >   hw/misc: Add a PWM module for NPCM7XX
> >   hw/ipmi: Add an IPMI host interface
> >   hw/ipmi: Add a KCS Module for NPCM7XX
> >   hw/ipmi: Add an IPMI external host device
> >
> >  default-configs/devices/arm-softmmu.mak |   2 +
> >  docs/system/arm/nuvoton.rst |   6 +-
> >  hw/adc/meson.build  |   1 +
> >  hw/adc/npcm7xx_adc.c| 318 ++
> >  hw/arm/npcm7xx.c|  65 +-
> >  hw/ipmi/Kconfig |   5 +
> >  hw/ipmi/ipmi_host.c |  40 ++
> >  hw/ipmi/ipmi_host_extern.c  | 435 +
> >  hw/ipmi/meson.build |   3 +
> >  hw/ipmi/npcm7xx_kcs.c   | 570 +
> >  hw/misc/meson.build |   1 +
> >  hw/misc/npcm7xx_clk.c   | 795 +++-
> >  hw/misc/npcm7xx_pwm.c   | 535 
> >  hw/timer/npcm7xx_timer.c|  25 +-
> >  include/hw/adc/npcm7xx_adc.h|  72 +++
> >  include/hw/arm/npcm7xx.h|   6 +
> >  include/hw/ipmi/ipmi_host.h |  56 ++
> >  include/hw/ipmi/ipmi_responder.h|  66 ++
> >  include/hw/ipmi/npcm7xx_kcs.h   | 105 
> >  include/hw/misc/npcm7xx_clk.h   | 146 -
> >  include/hw/misc/npcm7xx_pwm.h   | 106 
> >  include/hw/timer/npcm7xx_timer.h|   1 +
> >  tests/qtest/meson.build |   4 +-
> >  tests/qtest/npcm7xx_adc-test.c  | 400 
> >  tests/qtest/npcm7xx_pwm-test.c  | 490 +++
> >  25 files changed, 4221 insertions(+), 32 deletions(-)
> >  create mode 100644 hw/adc/npcm7xx_adc.c
> >  create mode 100644 hw/ipmi/ipmi_host.c
> >  create mode 100644 hw/ipmi/ipmi_host_extern.c
> >  create mode 100644 hw/ipmi/npcm7xx_kcs.c
> >  create mode 100644 hw/misc/npcm7xx_pwm.c
> >  create mode 100644 include/hw/adc/npcm7xx_adc.h
> >  create mode 100644 include/hw/ipmi/ipmi_host.h
> >  create mode 100644 include/hw/ipmi/ipmi_responder.h
> >  create mode 100644 include/hw/ipmi/npcm7xx_kcs.h
> >  create mode 100644 include/hw/misc/npcm7xx_pwm.h
> >  create mode 100644 tests/qtest/npcm7xx_adc-test.c
> >  create mode 100644 tests/qtest/npcm7xx_pwm-test.c
> >
> 

[PATCH 7/7] hw/ipmi: Add an IPMI external host device

2020-12-10 Thread Hao Wu via
The IPMI external host device works for Baseband Management Controller
(BMC) emulations. It works as a representation of a host class that
connects to a given BMC.  It can connect to a real host hardware or a
emulated or simulated host device. In particular it can connect to a
host QEMU instance with device ipmi-bmc-extern.

For more details of IPMI host device in BMC emulation, see
docs/specs/ipmi.rst.

Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/ipmi/ipmi_host_extern.c | 435 +
 hw/ipmi/meson.build|   1 +
 2 files changed, 436 insertions(+)
 create mode 100644 hw/ipmi/ipmi_host_extern.c

diff --git a/hw/ipmi/ipmi_host_extern.c b/hw/ipmi/ipmi_host_extern.c
new file mode 100644
index 00..6b5d45d403
--- /dev/null
+++ b/hw/ipmi/ipmi_host_extern.c
@@ -0,0 +1,435 @@
+/*
+ * IPMI Host external connection
+ *
+ * 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.
+ */
+
+/*
+ * This is designed to connect to a host QEMU VM that runs ipmi_bmc_extern.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/error-report.h"
+#include "qemu/module.h"
+#include "qapi/error.h"
+#include "qemu/timer.h"
+#include "chardev/char-fe.h"
+#include "hw/ipmi/ipmi.h"
+#include "hw/ipmi/ipmi_host.h"
+#include "hw/ipmi/ipmi_responder.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+
+#define VM_MSG_CHAR0xA0 /* Marks end of message */
+#define VM_CMD_CHAR0xA1 /* Marks end of a command */
+#define VM_ESCAPE_CHAR 0xAA /* Set bit 4 from the next byte to 0 */
+
+#define VM_PROTOCOL_VERSION1
+#define VM_CMD_VERSION 0xff /* A version number byte follows */
+#define VM_CMD_RESET   0x04
+#define VM_CMD_CAPABILITIES0x08
+
+#define TYPE_IPMI_HOST_EXTERN "ipmi-host-extern"
+#define IPMI_HOST_EXTERN(obj) OBJECT_CHECK(IPMIHostExtern, (obj), \
+TYPE_IPMI_HOST_EXTERN)
+
+typedef struct IPMIHostExtern {
+IPMIHost parent;
+CharBackend chr;
+struct QEMUTimer *extern_timer;
+
+bool connected;
+uint8_t capability;
+
+unsigned char inbuf[MAX_IPMI_MSG_SIZE + 2];
+unsigned int inpos;
+bool in_escape;
+bool in_too_many;
+bool sending_cmd;
+
+unsigned char outbuf[(MAX_IPMI_MSG_SIZE + 2) * 2 + 1];
+unsigned int outpos;
+unsigned int outlen;
+} IPMIHostExtern;
+
+static unsigned char
+ipmb_checksum(const unsigned char *data, int size, unsigned char start)
+{
+unsigned char csum = start;
+
+for (; size > 0; size--, data++) {
+csum += *data;
+}
+return csum;
+}
+
+static void continue_send(IPMIHostExtern *ihe)
+{
+int ret;
+
+if (ihe->outlen == 0) {
+return;
+}
+ret = qemu_chr_fe_write(>chr, ihe->outbuf + ihe->outpos,
+ihe->outlen - ihe->outpos);
+if (ret > 0) {
+ihe->outpos += ret;
+}
+if (ihe->outpos < ihe->outlen) {
+/* Not fully transmitted, try again in a 10ms */
+timer_mod_ns(ihe->extern_timer,
+ qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 1000);
+} else {
+/* Sent */
+ihe->outlen = 0;
+ihe->outpos = 0;
+}
+}
+
+static void extern_timeout(void *opaque)
+{
+IPMIHostExtern *ihe = opaque;
+
+if (ihe->connected) {
+continue_send(ihe);
+}
+}
+
+static void addchar(IPMIHostExtern *ihe, unsigned char ch)
+{
+switch (ch) {
+case VM_MSG_CHAR:
+case VM_CMD_CHAR:
+case VM_ESCAPE_CHAR:
+/* Escape the special characters. */
+ihe->outbuf[ihe->outlen++] = VM_ESCAPE_CHAR;
+ch |= 0x10;
+/* fall through */
+default:
+ihe->outbuf[ihe->outlen++] = ch;
+}
+}
+
+static void send_version(IPMIHostExtern *ihe)
+{
+addchar(ihe, VM_CMD_VERSION);
+addchar(ihe, VM_PROTOCOL_VERSION);
+ihe->outbuf[ihe->outlen++] = VM_CMD_CHAR;
+continue_send(ihe);
+}
+
+/*
+ * Handle a command (typically IPMI response) from IPMI responder
+ * and send it out to the external host.
+ */
+static void ipmi_host_extern_handle_command(IPMIHost *h, uint8_t *cmd,
+unsigned cmd_len, unsigned max_cmd_len, uint8_t msg_id)
+{
+IPMIHostExtern *ihe = IPMI_HOST_EXTERN(h);
+uint8_t err = 0, csum;
+int i;
+
+if (!ihe->connected) {
+/* We are not connected to external host. Just do nothing. */
+return;
+}
+addchar(ihe, msg_id);
+/* If it's too short or it was truncated, return an error. */
+if 

[PATCH 6/7] hw/ipmi: Add a KCS Module for NPCM7XX

2020-12-10 Thread Hao Wu via
Add a KCS module for NPCM7xx SoC. This module implements the IPMI
responder interface and is responsible to communicate with an external
host via the KCS channels in an NPCM7xx SoC.

Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst   |   2 +-
 hw/arm/npcm7xx.c  |  10 +-
 hw/ipmi/meson.build   |   1 +
 hw/ipmi/npcm7xx_kcs.c | 570 ++
 include/hw/arm/npcm7xx.h  |   2 +
 include/hw/ipmi/npcm7xx_kcs.h | 105 +++
 6 files changed, 688 insertions(+), 2 deletions(-)
 create mode 100644 hw/ipmi/npcm7xx_kcs.c
 create mode 100644 include/hw/ipmi/npcm7xx_kcs.h

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index a1786342e2..21a02deba5 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -43,6 +43,7 @@ Supported devices
  * GPIO controller
  * Analog to Digital Converter (ADC)
  * Pulse Width Modulation (PWM)
+ * Keyboard Controller Style (KCS) channels
 
 Missing devices
 ---
@@ -50,7 +51,6 @@ Missing devices
  * 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)
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 72040d4079..4b7116d496 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -46,6 +46,7 @@
 #define NPCM7XX_CLK_BA  (0xf0801000)
 #define NPCM7XX_MC_BA   (0xf0824000)
 #define NPCM7XX_RNG_BA  (0xf000b000)
+#define NPCM7XX_KCS_BA  (0xf0007000)
 
 /* USB Host modules */
 #define NPCM7XX_EHCI_BA (0xf0806000)
@@ -82,6 +83,7 @@ enum NPCM7xxInterrupt {
 NPCM7XX_UART1_IRQ,
 NPCM7XX_UART2_IRQ,
 NPCM7XX_UART3_IRQ,
+NPCM7XX_KCS_HIB_IRQ = 9,
 NPCM7XX_TIMER0_IRQ  = 32,   /* Timer Module 0 */
 NPCM7XX_TIMER1_IRQ,
 NPCM7XX_TIMER2_IRQ,
@@ -353,6 +355,7 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, "gpio[*]", >gpio[i], 
TYPE_NPCM7XX_GPIO);
 }
 
+object_initialize_child(obj, "kcs", >kcs, TYPE_NPCM7XX_KCS);
 object_initialize_child(obj, "ehci", >ehci, TYPE_NPCM7XX_EHCI);
 object_initialize_child(obj, "ohci", >ohci, TYPE_SYSBUS_OHCI);
 
@@ -509,6 +512,12 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
npcm7xx_irq(s, NPCM7XX_GPIO0_IRQ + i));
 }
 
+/* KCS modules. Cannot fail. */
+sysbus_realize(SYS_BUS_DEVICE(>kcs), _abort);
+sysbus_mmio_map(SYS_BUS_DEVICE(>kcs), 0, NPCM7XX_KCS_BA);
+sysbus_connect_irq(SYS_BUS_DEVICE(>kcs), 0,
+   npcm7xx_irq(s, NPCM7XX_KCS_HIB_IRQ));
+
 /* USB Host */
 object_property_set_bool(OBJECT(>ehci), "companion-enable", true,
  _abort);
@@ -574,7 +583,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.shm",  0xc0001000,   4 * KiB);
 create_unimplemented_device("npcm7xx.vdmx", 0xe080,   4 * KiB);
 create_unimplemented_device("npcm7xx.pcierc",   0xe100,  64 * KiB);
-create_unimplemented_device("npcm7xx.kcs",  0xf0007000,   4 * KiB);
 create_unimplemented_device("npcm7xx.gfxi", 0xf000e000,   4 * KiB);
 create_unimplemented_device("npcm7xx.gpio[0]",  0xf001,   4 * KiB);
 create_unimplemented_device("npcm7xx.gpio[1]",  0xf0011000,   4 * KiB);
diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build
index 9ec4dcb957..1261489fbd 100644
--- a/hw/ipmi/meson.build
+++ b/hw/ipmi/meson.build
@@ -8,5 +8,6 @@ ipmi_ss.add(when: 'CONFIG_ISA_IPMI_BT', if_true: 
files('isa_ipmi_bt.c'))
 ipmi_ss.add(when: 'CONFIG_PCI_IPMI_BT', if_true: files('pci_ipmi_bt.c'))
 ipmi_ss.add(when: 'CONFIG_IPMI_SSIF', if_true: files('smbus_ipmi.c'))
 ipmi_ss.add(when: 'CONFIG_IPMI_HOST', if_true: files('ipmi_host.c'))
+ipmi_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_kcs.c'))
 
 softmmu_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss)
diff --git a/hw/ipmi/npcm7xx_kcs.c b/hw/ipmi/npcm7xx_kcs.c
new file mode 100644
index 00..f568f0cf20
--- /dev/null
+++ b/hw/ipmi/npcm7xx_kcs.c
@@ -0,0 +1,570 @@
+/*
+ * Nuvoton NPCM7xx KCS Module
+ *
+ * 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/ipmi/ipmi_host.h"
+#include "hw/ipmi/npcm7xx_kcs.h"
+#include "migration/vmstate.h"

[PATCH 5/7] hw/ipmi: Add an IPMI host interface

2020-12-10 Thread Hao Wu via
The IPMI host interface is used to emulate IPMI related devices on a
System-on-Chip (SoC) used for Baseband Management Controller (BMC).
This interface consists of two components: IPMI host and IPMI responder.
An IPMI responder is a device to intercept reads and writes to the
system interface registers in the host. An IPMI host emulates the host
behavior on a BMC emulation.

For more information see docs/spec/ipmi.rst.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 default-configs/devices/arm-softmmu.mak |  2 +
 hw/ipmi/Kconfig |  5 ++
 hw/ipmi/ipmi_host.c | 40 +++
 hw/ipmi/meson.build |  1 +
 include/hw/ipmi/ipmi_host.h | 56 +
 include/hw/ipmi/ipmi_responder.h| 66 +
 6 files changed, 170 insertions(+)
 create mode 100644 hw/ipmi/ipmi_host.c
 create mode 100644 include/hw/ipmi/ipmi_host.h
 create mode 100644 include/hw/ipmi/ipmi_responder.h

diff --git a/default-configs/devices/arm-softmmu.mak 
b/default-configs/devices/arm-softmmu.mak
index 08a32123b4..864bac4501 100644
--- a/default-configs/devices/arm-softmmu.mak
+++ b/default-configs/devices/arm-softmmu.mak
@@ -27,6 +27,8 @@ CONFIG_GUMSTIX=y
 CONFIG_SPITZ=y
 CONFIG_TOSA=y
 CONFIG_Z2=y
+CONFIG_IPMI=y
+CONFIG_IPMI_HOST=y
 CONFIG_NPCM7XX=y
 CONFIG_COLLIE=y
 CONFIG_ASPEED_SOC=y
diff --git a/hw/ipmi/Kconfig b/hw/ipmi/Kconfig
index 9befd4f422..9e487eb42f 100644
--- a/hw/ipmi/Kconfig
+++ b/hw/ipmi/Kconfig
@@ -6,6 +6,11 @@ config IPMI_LOCAL
 default y
 depends on IPMI
 
+config IPMI_HOST
+bool
+default y
+depends on IPMI
+
 config IPMI_EXTERN
 bool
 default y
diff --git a/hw/ipmi/ipmi_host.c b/hw/ipmi/ipmi_host.c
new file mode 100644
index 00..7a6d4eb323
--- /dev/null
+++ b/hw/ipmi/ipmi_host.c
@@ -0,0 +1,40 @@
+/*
+ * IPMI Host emulation
+ *
+ * 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 "hw/ipmi/ipmi_host.h"
+#include "hw/ipmi/ipmi_responder.h"
+
+static TypeInfo ipmi_responder_type_info = {
+.name = TYPE_IPMI_RESPONDER,
+.parent = TYPE_INTERFACE,
+.class_size = sizeof(IPMIResponderClass),
+};
+
+static TypeInfo ipmi_host_type_info = {
+.name = TYPE_IPMI_HOST,
+.parent = TYPE_DEVICE,
+.instance_size = sizeof(IPMIHost),
+.abstract = true,
+.class_size = sizeof(IPMIHostClass),
+};
+
+static void ipmi_register_types(void)
+{
+type_register_static(_responder_type_info);
+type_register_static(_host_type_info);
+}
+
+type_init(ipmi_register_types)
diff --git a/hw/ipmi/meson.build b/hw/ipmi/meson.build
index 9622ea2a2c..9ec4dcb957 100644
--- a/hw/ipmi/meson.build
+++ b/hw/ipmi/meson.build
@@ -7,5 +7,6 @@ ipmi_ss.add(when: 'CONFIG_PCI_IPMI_KCS', if_true: 
files('pci_ipmi_kcs.c'))
 ipmi_ss.add(when: 'CONFIG_ISA_IPMI_BT', if_true: files('isa_ipmi_bt.c'))
 ipmi_ss.add(when: 'CONFIG_PCI_IPMI_BT', if_true: files('pci_ipmi_bt.c'))
 ipmi_ss.add(when: 'CONFIG_IPMI_SSIF', if_true: files('smbus_ipmi.c'))
+ipmi_ss.add(when: 'CONFIG_IPMI_HOST', if_true: files('ipmi_host.c'))
 
 softmmu_ss.add_all(when: 'CONFIG_IPMI', if_true: ipmi_ss)
diff --git a/include/hw/ipmi/ipmi_host.h b/include/hw/ipmi/ipmi_host.h
new file mode 100644
index 00..a703cc3854
--- /dev/null
+++ b/include/hw/ipmi/ipmi_host.h
@@ -0,0 +1,56 @@
+/*
+ * IPMI host 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 HW_IPMI_HOST_H
+#define HW_IPMI_HOST_H
+
+#include "hw/ipmi/ipmi_responder.h"
+
+#define TYPE_IPMI_HOST "ipmi-host"
+#define IPMI_HOST(obj) \
+ OBJECT_CHECK(IPMIHost, (obj), TYPE_IPMI_HOST)
+#define IPMI_HOST_CLASS(obj_class) \
+ OBJECT_CLASS_CHECK(IPMIHostClass, (obj_class), TYPE_IPMI_HOST)
+#define IPMI_HOST_GET_CLASS(obj) \
+ OBJECT_GET_CLASS(IPMIHostClass, (obj), TYPE_IPMI_HOST)
+
+/**
+ * struct IPMIHost defines an IPMI host interface. It can be a simulator or a
+ * connection to an emulated or real host.
+ * 

[PATCH 3/7] hw/adc: Add an ADC module for NPCM7XX

2020-12-10 Thread Hao Wu via
The ADC is part of NPCM7XX Module. Its behavior is controled by the
ADC_CON register. It converts one of the eight analog inputs into a
digital input and stores it in the ADC_DATA register when enabled.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/adc/meson.build |   1 +
 hw/adc/npcm7xx_adc.c   | 318 ++
 hw/arm/npcm7xx.c   |  24 +-
 include/hw/adc/npcm7xx_adc.h   |  72 ++
 include/hw/arm/npcm7xx.h   |   2 +
 tests/qtest/meson.build|   3 +-
 tests/qtest/npcm7xx_adc-test.c | 400 +
 8 files changed, 819 insertions(+), 3 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index b00d405d52..35829f8d0b 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -41,6 +41,7 @@ Supported devices
  * Random Number Generator (RNG)
  * USB host (USBH)
  * GPIO controller
+ * Analog to Digital Converter (ADC)
 
 Missing devices
 ---
@@ -58,7 +59,6 @@ Missing devices
  * USB device (USBD)
  * SMBus controller (SMBF)
  * Peripheral SPI controller (PSPI)
- * Analog to Digital Converter (ADC)
  * SD/MMC host
  * PECI interface
  * Pulse Width Modulation (PWM)
diff --git a/hw/adc/meson.build b/hw/adc/meson.build
index 0d62ae96ae..6ddee23813 100644
--- a/hw/adc/meson.build
+++ b/hw/adc/meson.build
@@ -1 +1,2 @@
 softmmu_ss.add(when: 'CONFIG_STM32F2XX_ADC', if_true: files('stm32f2xx_adc.c'))
+softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_adc.c'))
diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c
new file mode 100644
index 00..4492303977
--- /dev/null
+++ b/hw/adc/npcm7xx_adc.c
@@ -0,0 +1,318 @@
+/*
+ * Nuvoton NPCM7xx ADC Module
+ *
+ * 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 "hw/adc/npcm7xx_adc.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "migration/vmstate.h"
+#include "qemu/log.h"
+#include "qemu/module.h"
+#include "qemu/timer.h"
+#include "qemu/units.h"
+
+/* 32-bit register indices. */
+enum NPCM7xxADCRegisters {
+NPCM7XX_ADC_CON,
+NPCM7XX_ADC_DATA,
+NPCM7XX_ADC_REGS_END,
+};
+
+/* Register field definitions. */
+#define NPCM7XX_ADC_CON_MUX(rv) extract32(rv, 24, 4)
+#define NPCM7XX_ADC_CON_INT_EN  BIT(21)
+#define NPCM7XX_ADC_CON_REFSEL  BIT(19)
+#define NPCM7XX_ADC_CON_INT BIT(18)
+#define NPCM7XX_ADC_CON_EN  BIT(17)
+#define NPCM7XX_ADC_CON_RST BIT(16)
+#define NPCM7XX_ADC_CON_CONVBIT(14)
+#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8)
+
+#define NPCM7XX_ADC_MAX_RESULT  1023
+#define NPCM7XX_ADC_DEFAULT_IREF200
+#define NPCM7XX_ADC_CONV_CYCLES 20
+#define NPCM7XX_ADC_RESET_CYCLES10
+#define NPCM7XX_ADC_R0_INPUT50
+#define NPCM7XX_ADC_R1_INPUT150
+
+static void npcm7xx_adc_reset(NPCM7xxADCState *s)
+{
+timer_del(>conv_timer);
+timer_del(>reset_timer);
+s->con = 0x000c0001;
+s->data = 0x;
+}
+
+static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref)
+{
+uint32_t result;
+
+result = input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref;
+if (result > NPCM7XX_ADC_MAX_RESULT) {
+result = NPCM7XX_ADC_MAX_RESULT;
+}
+
+return result;
+}
+
+static uint32_t npcm7xx_adc_prescaler(NPCM7xxADCState *s)
+{
+return 2 * (NPCM7XX_ADC_CON_DIV(s->con) + 1);
+}
+
+static void npcm7xx_adc_start_timer(Clock *clk, QEMUTimer *timer,
+uint32_t cycles, uint32_t prescaler)
+{
+int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+int64_t freq = clock_get_hz(clk);
+int64_t ns;
+
+ns = (NANOSECONDS_PER_SECOND * cycles * prescaler / freq);
+ns += now;
+timer_mod(timer, ns);
+}
+
+static void npcm7xx_adc_start_reset(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >reset_timer, 
NPCM7XX_ADC_RESET_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_start_convert(NPCM7xxADCState *s)
+{
+uint32_t prescaler = npcm7xx_adc_prescaler(s);
+
+npcm7xx_adc_start_timer(s->clock, >conv_timer, NPCM7XX_ADC_CONV_CYCLES,
+prescaler);
+}
+
+static void npcm7xx_adc_reset_done(void *opaque)
+{
+NPCM7xxADCState *s = opaque;
+
+npcm7xx_adc_reset(s);

[PATCH 2/7] hw/timer: Refactor NPCM7XX Timer to use CLK clock

2020-12-10 Thread Hao Wu via
This patch makes NPCM7XX Timer to use a the timer clock generated by the
CLK module instead of the magic nubmer TIMER_REF_HZ.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/arm/npcm7xx.c |  5 +
 hw/timer/npcm7xx_timer.c | 25 +++--
 include/hw/misc/npcm7xx_clk.h|  6 --
 include/hw/timer/npcm7xx_timer.h |  1 +
 4 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index 47e2b6fc40..fabfb1697b 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -22,6 +22,7 @@
 #include "hw/char/serial.h"
 #include "hw/loader.h"
 #include "hw/misc/unimp.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
 #include "qapi/error.h"
 #include "qemu/units.h"
@@ -420,6 +421,10 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 int first_irq;
 int j;
 
+/* Connect the timer clock. */
+qdev_connect_clock_in(DEVICE(>tim[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "timer-clock"));
+
 sysbus_realize(sbd, _abort);
 sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]);
 
diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c
index d24445bd6e..9469c959e2 100644
--- a/hw/timer/npcm7xx_timer.c
+++ b/hw/timer/npcm7xx_timer.c
@@ -17,8 +17,8 @@
 #include "qemu/osdep.h"
 
 #include "hw/irq.h"
+#include "hw/qdev-clock.h"
 #include "hw/qdev-properties.h"
-#include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
 #include "migration/vmstate.h"
 #include "qemu/bitops.h"
@@ -130,7 +130,7 @@ 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 *= NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock);
 ns *= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return ns;
@@ -141,7 +141,7 @@ 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 = ns / (NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock));
 count /= npcm7xx_tcsr_prescaler(t->tcsr);
 
 return count;
@@ -166,8 +166,10 @@ static uint32_t npcm7xx_watchdog_timer_prescaler(const 
NPCM7xxWatchdogTimer *t)
 static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xxWatchdogTimer *t,
 int64_t cycles)
 {
+g_assert(clock_get_hz(t->ctrl->clock) == 2500);
 uint32_t prescaler = npcm7xx_watchdog_timer_prescaler(t);
-int64_t ns = (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycles;
+int64_t ns = (NANOSECONDS_PER_SECOND / clock_get_hz(t->ctrl->clock))
+* cycles;
 
 /*
  * The reset function always clears the current timer. The caller of the
@@ -606,10 +608,11 @@ static void npcm7xx_timer_hold_reset(Object *obj)
 qemu_irq_lower(s->watchdog_timer.irq);
 }
 
-static void npcm7xx_timer_realize(DeviceState *dev, Error **errp)
+static void npcm7xx_timer_init(Object *obj)
 {
-NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(dev);
+NPCM7xxTimerCtrlState *s = NPCM7XX_TIMER(obj);
 SysBusDevice *sbd = >parent;
+DeviceState *dev = DEVICE(obj);
 int i;
 NPCM7xxWatchdogTimer *w;
 
@@ -627,11 +630,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, Error 
**errp)
 npcm7xx_watchdog_timer_expired, w);
 sysbus_init_irq(sbd, >irq);
 
-memory_region_init_io(>iomem, OBJECT(s), _timer_ops, s,
+memory_region_init_io(>iomem, obj, _timer_ops, s,
   TYPE_NPCM7XX_TIMER, 4 * KiB);
 sysbus_init_mmio(sbd, >iomem);
 qdev_init_gpio_out_named(dev, >reset_signal,
 NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1);
+s->clock = qdev_init_clock_in(dev, "clock", NULL, NULL);
 }
 
 static const VMStateDescription vmstate_npcm7xx_base_timer = {
@@ -675,10 +679,11 @@ static const VMStateDescription 
vmstate_npcm7xx_watchdog_timer = {
 
 static const VMStateDescription vmstate_npcm7xx_timer_ctrl = {
 .name = "npcm7xx-timer-ctrl",
-.version_id = 1,
-.minimum_version_id = 1,
+.version_id = 2,
+.minimum_version_id = 2,
 .fields = (VMStateField[]) {
 VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState),
+VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState),
 VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState,
  NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_timer,
  NPCM7xxTimer),
@@ -697,7 +702,6 @@ static void npcm7xx_timer_class_init(ObjectClass *klass, 
void *data)
 QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS);
 
 dc->desc = "NPCM7xx Timer Controller";
-dc->realize = npcm7xx_timer_realize;
 dc->vmsd = _npcm7xx_timer_ctrl;
 rc->phases.enter = npcm7xx_timer_enter_reset;
 rc->phases.hold = npcm7xx_timer_hold_reset;
@@ -708,6 +712,7 @@ static const TypeInfo npcm7xx_timer_info = {
 .parent 

[PATCH 1/7] hw/misc: Add clock converter in NPCM7XX CLK module

2020-12-10 Thread Hao Wu via
This patch allows NPCM7XX CLK module to compute clocks that are used by
other NPCM7XX modules.

Add a new struct NPCM7xxClockConverterState which represents a
single converter.  Each clock converter in CLK module represents one
converter in NPCM7XX CLK Module(PLL, SEL or Divider). Each converter
takes one or more input clocks and converts them into one output clock.
They form a clock hierarchy in the CLK module and are responsible for
outputing clocks for various other modules in an NPCM7XX SoC.

Each converter has a function pointer called "convert" which represents
the unique logic for that converter.

The clock contains two initialization information: ConverterInitInfo and
ConverterConnectionInfo. They represent the vertices and edges in the
clock diagram respectively.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 hw/misc/npcm7xx_clk.c | 795 +-
 include/hw/misc/npcm7xx_clk.h | 140 +-
 2 files changed, 927 insertions(+), 8 deletions(-)

diff --git a/hw/misc/npcm7xx_clk.c b/hw/misc/npcm7xx_clk.c
index 6732437fe2..48bc9bdda5 100644
--- a/hw/misc/npcm7xx_clk.c
+++ b/hw/misc/npcm7xx_clk.c
@@ -18,6 +18,7 @@
 
 #include "hw/misc/npcm7xx_clk.h"
 #include "hw/timer/npcm7xx_timer.h"
+#include "hw/qdev-clock.h"
 #include "migration/vmstate.h"
 #include "qemu/error-report.h"
 #include "qemu/log.h"
@@ -27,9 +28,22 @@
 #include "trace.h"
 #include "sysemu/watchdog.h"
 
+/*
+ * The reference clock hz, and the SECCNT and CNTR25M registers in this module,
+ * is always 25 MHz.
+ */
+#define NPCM7XX_CLOCK_REF_HZ(2500)
+
+/* Register Field Definitions */
+#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
+
 #define PLLCON_LOKI BIT(31)
 #define PLLCON_LOKS BIT(30)
 #define PLLCON_PWDENBIT(12)
+#define PLLCON_FBDV(con) extract32((con), 16, 12)
+#define PLLCON_OTDV2(con) extract32((con), 13, 3)
+#define PLLCON_OTDV1(con) extract32((con), 8, 3)
+#define PLLCON_INDV(con) extract32((con), 0, 6)
 
 enum NPCM7xxCLKRegisters {
 NPCM7XX_CLK_CLKEN1,
@@ -89,12 +103,609 @@ static const uint32_t 
cold_reset_values[NPCM7XX_CLK_NR_REGS] = {
 [NPCM7XX_CLK_AHBCKFI]   = 0x00c8,
 };
 
-/* Register Field Definitions */
-#define NPCM7XX_CLK_WDRCR_CA9C  BIT(0) /* Cortex A9 Cores */
-
 /* The number of watchdogs that can trigger a reset. */
 #define NPCM7XX_NR_WATCHDOGS(3)
 
+/* Clock converter functions */
+
+#define TYPE_NPCM7XX_CLOCK_PLL "npcm7xx-clock-pll"
+#define NPCM7XX_CLOCK_PLL(obj) OBJECT_CHECK(NPCM7xxClockPLLState, \
+(obj), TYPE_NPCM7XX_CLOCK_PLL)
+#define TYPE_NPCM7XX_CLOCK_SEL "npcm7xx-clock-sel"
+#define NPCM7XX_CLOCK_SEL(obj) OBJECT_CHECK(NPCM7xxClockSELState, \
+(obj), TYPE_NPCM7XX_CLOCK_SEL)
+#define TYPE_NPCM7XX_CLOCK_DIVIDER "npcm7xx-clock-divider"
+#define NPCM7XX_CLOCK_DIVIDER(obj) OBJECT_CHECK(NPCM7xxClockDividerState, \
+(obj), TYPE_NPCM7XX_CLOCK_DIVIDER)
+
+static void npcm7xx_clk_update_pll(void *opaque)
+{
+NPCM7xxClockPLLState *s = opaque;
+uint32_t con = s->clk->regs[s->reg];
+uint64_t freq;
+
+/* The PLL is grounded if it is not locked yet. */
+if (con & PLLCON_LOKI) {
+freq = clock_get_hz(s->clock_in);
+freq *= PLLCON_FBDV(con);
+freq /= PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con);
+} else {
+freq = 0;
+}
+
+clock_update_hz(s->clock_out, freq);
+}
+
+static void npcm7xx_clk_update_sel(void *opaque)
+{
+NPCM7xxClockSELState *s = opaque;
+uint32_t index = extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offset,
+s->len);
+
+if (index >= s->input_size) {
+qemu_log_mask(LOG_GUEST_ERROR,
+  "%s: SEL index: %u out of range\n",
+  __func__, index);
+index = 0;
+}
+clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index]));
+}
+
+static void npcm7xx_clk_update_divider(void *opaque)
+{
+NPCM7xxClockDividerState *s = opaque;
+uint32_t freq;
+
+freq = s->divide(s);
+clock_update_hz(s->clock_out, freq);
+}
+
+static uint32_t divide_by_constant(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) / s->divisor;
+}
+
+static uint32_t divide_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) /
+(extract32(s->clk->regs[s->reg], s->offset, s->len) + 1);
+}
+
+static uint32_t divide_by_reg_divisor_times_2(NPCM7xxClockDividerState *s)
+{
+return divide_by_reg_divisor(s) / 2;
+}
+
+static uint32_t shift_by_reg_divisor(NPCM7xxClockDividerState *s)
+{
+return clock_get_hz(s->clock_in) >>
+extract32(s->clk->regs[s->reg], s->offset, s->len);
+}
+
+static NPCM7xxClockPLL find_pll_by_reg(enum NPCM7xxCLKRegisters reg)
+{
+switch (reg) {
+case NPCM7XX_CLK_PLLCON0:
+return NPCM7XX_CLOCK_PLL0;
+case NPCM7XX_CLK_PLLCON1:
+return NPCM7XX_CLOCK_PLL1;
+case NPCM7XX_CLK_PLLCON2:
+return 

[PATCH 4/7] hw/misc: Add a PWM module for NPCM7XX

2020-12-10 Thread Hao Wu via
The PWM module is part of NPCM7XX module. Each NPCM7XX module has two
identical PWM modules. Each module contains 4 PWM entries. Each PWM has
two outputs: frequency and duty_cycle. Both are computed using inputs
from software side.

This module does not model detail pulse signals since it is expensive.
It also does not model interrupts and watchdogs that are dependant on
the detail models. The interfaces for these are left in the module so
that anyone in need for these functionalities can implement on their
own.

Reviewed-by: Havard Skinnemoen 
Reviewed-by: Tyrone Ting 
Signed-off-by: Hao Wu 
---
 docs/system/arm/nuvoton.rst|   2 +-
 hw/arm/npcm7xx.c   |  26 +-
 hw/misc/meson.build|   1 +
 hw/misc/npcm7xx_pwm.c  | 535 +
 include/hw/arm/npcm7xx.h   |   2 +
 include/hw/misc/npcm7xx_pwm.h  | 106 +++
 tests/qtest/meson.build|   1 +
 tests/qtest/npcm7xx_pwm-test.c | 490 ++
 8 files changed, 1160 insertions(+), 3 deletions(-)
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

diff --git a/docs/system/arm/nuvoton.rst b/docs/system/arm/nuvoton.rst
index 35829f8d0b..a1786342e2 100644
--- a/docs/system/arm/nuvoton.rst
+++ b/docs/system/arm/nuvoton.rst
@@ -42,6 +42,7 @@ Supported devices
  * USB host (USBH)
  * GPIO controller
  * Analog to Digital Converter (ADC)
+ * Pulse Width Modulation (PWM)
 
 Missing devices
 ---
@@ -61,7 +62,6 @@ Missing devices
  * Peripheral SPI controller (PSPI)
  * SD/MMC host
  * PECI interface
- * Pulse Width Modulation (PWM)
  * Tachometer
  * PCI and PCIe root complex and bridges
  * VDM and MCTP support
diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c
index b22a8c966d..72040d4079 100644
--- a/hw/arm/npcm7xx.c
+++ b/hw/arm/npcm7xx.c
@@ -102,6 +102,8 @@ enum NPCM7xxInterrupt {
 NPCM7XX_WDG2_IRQ,   /* Timer Module 2 Watchdog */
 NPCM7XX_EHCI_IRQ= 61,
 NPCM7XX_OHCI_IRQ= 62,
+NPCM7XX_PWM0_IRQ= 93,   /* PWM module 0 */
+NPCM7XX_PWM1_IRQ,   /* PWM module 1 */
 NPCM7XX_GPIO0_IRQ   = 116,
 NPCM7XX_GPIO1_IRQ,
 NPCM7XX_GPIO2_IRQ,
@@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] = {
 0xb800, /* CS3 */
 };
 
+/* Register base address for each PWM Module */
+static const hwaddr npcm7xx_pwm_addr[] = {
+0xf0103000,
+0xf0104000,
+};
+
 static const struct {
 hwaddr regs_addr;
 uint32_t unconnected_pins;
@@ -353,6 +361,10 @@ static void npcm7xx_init(Object *obj)
 object_initialize_child(obj, npcm7xx_fiu[i].name, >fiu[i],
 TYPE_NPCM7XX_FIU);
 }
+
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+object_initialize_child(obj, "pwm[*]", >pwm[i], TYPE_NPCM7XX_PWM);
+}
 }
 
 static void npcm7xx_realize(DeviceState *dev, Error **errp)
@@ -513,6 +525,18 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 sysbus_connect_irq(SYS_BUS_DEVICE(>ohci), 0,
npcm7xx_irq(s, NPCM7XX_OHCI_IRQ));
 
+/* PWM Modules. Cannot fail. */
+QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) != ARRAY_SIZE(s->pwm));
+for (i = 0; i < ARRAY_SIZE(s->pwm); i++) {
+SysBusDevice *sbd = SYS_BUS_DEVICE(>pwm[i]);
+
+qdev_connect_clock_in(DEVICE(>pwm[i]), "clock", qdev_get_clock_out(
+DEVICE(>clk), "apb3-clock"));
+sysbus_realize(sbd, _abort);
+sysbus_mmio_map(sbd, 0, npcm7xx_pwm_addr[i]);
+sysbus_connect_irq(sbd, i, npcm7xx_irq(s, NPCM7XX_PWM0_IRQ + i));
+}
+
 /*
  * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects
  * specified, but this is a programming error.
@@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **errp)
 create_unimplemented_device("npcm7xx.peci", 0xf010,   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]",   0xf018,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[1]",   0xf0181000,   4 * KiB);
 create_unimplemented_device("npcm7xx.mft[2]",   0xf0182000,   4 * KiB);
diff --git a/hw/misc/meson.build b/hw/misc/meson.build
index ce15ffceb9..607cd38a21 100644
--- a/hw/misc/meson.build
+++ b/hw/misc/meson.build
@@ -64,6 +64,7 @@ softmmu_ss.add(when: 'CONFIG_MAINSTONE', if_true: 
files('mst_fpga.c'))
 softmmu_ss.add(when: 'CONFIG_NPCM7XX', if_true: files(
   'npcm7xx_clk.c',
   'npcm7xx_gcr.c',
+  'npcm7xx_pwm.c',
   'npcm7xx_rng.c',
 ))
 softmmu_ss.add(when: 'CONFIG_OMAP', 

[PATCH 0/7] Additional NPCM7xx devices and IPMI BMC emulation support

2020-12-10 Thread Hao Wu via
This patch series include a few more NPCM7XX devices including

- Analog Digital Converter (ADC)
- Pulse Width Modulation (PWM)
- Keyboard Style Controller (KSC)

To utilize these modules we also add two extra functionalities:

1. We modified the CLK module to generate clock values using qdev_clock.
   These clocks are used to determine various clocks in NPCM7XX devices.
2. We added support for emulating IPMI responder devices in BMC machines,
   similar to the existing IPMI device support for CPU emulation. This allows
   a qemu instance running BMC firmware to serve as an external BMC for a qemu
   instance running server software. It utilizes the KCS module we implemented.

Hao Wu (7):
  hw/misc: Add clock converter in NPCM7XX CLK module
  hw/timer: Refactor NPCM7XX Timer to use CLK clock
  hw/adc: Add an ADC module for NPCM7XX
  hw/misc: Add a PWM module for NPCM7XX
  hw/ipmi: Add an IPMI host interface
  hw/ipmi: Add a KCS Module for NPCM7XX
  hw/ipmi: Add an IPMI external host device

 default-configs/devices/arm-softmmu.mak |   2 +
 docs/system/arm/nuvoton.rst |   6 +-
 hw/adc/meson.build  |   1 +
 hw/adc/npcm7xx_adc.c| 318 ++
 hw/arm/npcm7xx.c|  65 +-
 hw/ipmi/Kconfig |   5 +
 hw/ipmi/ipmi_host.c |  40 ++
 hw/ipmi/ipmi_host_extern.c  | 435 +
 hw/ipmi/meson.build |   3 +
 hw/ipmi/npcm7xx_kcs.c   | 570 +
 hw/misc/meson.build |   1 +
 hw/misc/npcm7xx_clk.c   | 795 +++-
 hw/misc/npcm7xx_pwm.c   | 535 
 hw/timer/npcm7xx_timer.c|  25 +-
 include/hw/adc/npcm7xx_adc.h|  72 +++
 include/hw/arm/npcm7xx.h|   6 +
 include/hw/ipmi/ipmi_host.h |  56 ++
 include/hw/ipmi/ipmi_responder.h|  66 ++
 include/hw/ipmi/npcm7xx_kcs.h   | 105 
 include/hw/misc/npcm7xx_clk.h   | 146 -
 include/hw/misc/npcm7xx_pwm.h   | 106 
 include/hw/timer/npcm7xx_timer.h|   1 +
 tests/qtest/meson.build |   4 +-
 tests/qtest/npcm7xx_adc-test.c  | 400 
 tests/qtest/npcm7xx_pwm-test.c  | 490 +++
 25 files changed, 4221 insertions(+), 32 deletions(-)
 create mode 100644 hw/adc/npcm7xx_adc.c
 create mode 100644 hw/ipmi/ipmi_host.c
 create mode 100644 hw/ipmi/ipmi_host_extern.c
 create mode 100644 hw/ipmi/npcm7xx_kcs.c
 create mode 100644 hw/misc/npcm7xx_pwm.c
 create mode 100644 include/hw/adc/npcm7xx_adc.h
 create mode 100644 include/hw/ipmi/ipmi_host.h
 create mode 100644 include/hw/ipmi/ipmi_responder.h
 create mode 100644 include/hw/ipmi/npcm7xx_kcs.h
 create mode 100644 include/hw/misc/npcm7xx_pwm.h
 create mode 100644 tests/qtest/npcm7xx_adc-test.c
 create mode 100644 tests/qtest/npcm7xx_pwm-test.c

-- 
2.29.2.684.gfbc64c5ab5-goog