From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133383; cv=none; d=zohomail.com; s=zohoarc; b=MINWdgR1qhG3F8NPnvbBxM8uXx0hshEdQL7NO9VdnmwT/bGQOv00x0bhpT4por55Pjy/7zFiyY4k+lGDTVlAfjPKvw1h/65omODQZp1hjPxDaC405wDqEDdK8XbEJl9E8Z5c5MlU08Qmsq+2hjFFjq0U3iQbY7KjFzlYp2Mmr/g= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133383; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=0mxuQctenD5q3cEFitgZ8dhJ0SSY9BkENI18PY6j88g=; b=Ax3FUeTIH7JC2eW5EaoWFgyEIxAww9GugrPo0w6qlXTM+V5pzoO9cifWxDmBTyCFQQDKOuC+UFTP1+4t9RX9tdJwmO28K/fHTo/D8YwdX5He4lcqu8HKkY7cpBo2Xo3oyUSk6NRt9uct7Wd1c6MxYFL1osq4NwKQRfRyMOO6Ku8= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1610133383352192.196913081993; Fri, 8 Jan 2021 11:16:23 -0800 (PST) Received: from localhost ([::1]:52408 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxFF-0004R6-V8 for importer@patchew.org; Fri, 08 Jan 2021 14:16:21 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40368) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3IK74XwgKCmUZXKDRWVKJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--wuhaotsh.bounces.google.com>) id 1kxx9l-00019g-4L for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:42 -0500 Received: from mail-pj1-x1049.google.com ([2607:f8b0:4864:20::1049]:43108) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3IK74XwgKCmUZXKDRWVKJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--wuhaotsh.bounces.google.com>) id 1kxx9X-0007K4-RN for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:39 -0500 Received: by mail-pj1-x1049.google.com with SMTP id c21so7520373pjr.8 for ; Fri, 08 Jan 2021 11:10:25 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=0mxuQctenD5q3cEFitgZ8dhJ0SSY9BkENI18PY6j88g=; b=K3+5waLLSbNbklG+Af8imabSddv//PqX33BAAjpv8jP4lbS2kaFAaDBAItA6zoENUb YtayqjC/JRITjOXoovzS/ko9l6HOTVP1B72VziVn4ImD5j7KkpgUaIQgYjEg8FFBPFoL Gaci1Q3ZFdhA071H2Nd5/ta1s6w+bHUq/R3bolE1m9vwatzSIEGSHSLPlv7B7FdezBNw 9xx4OlUiLFvS8nNCvTvicTwfrx6oMnQ7b+GSzwwwtNTL95jN+i5/CO+7RQHJlOYs+LxT l6qw9iBvKeMj8D8LD4eeDSZWlZrKDEkpGrRSP/l6kbEz1kx/w8CevGg9AEf/t0/NTpfB PtLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=0mxuQctenD5q3cEFitgZ8dhJ0SSY9BkENI18PY6j88g=; b=dJd35yREulpnjWXnxXoZ3XR/7NULHmhXLoFwR2+aWwngcAgoRrrT4nNyzNwhywnpfe ijCl57ewqJMmQCy2BTMf3wsBlEaKkOypCZz5saa/02HGa0EycipFDjT48qmREAUrqMfv /buyoiVhbUaBIsdQAOz360Is4gwJbnRnfR++Bsp7fsfz7m0E36+cH2PZSubZsSigXhZO ruRvICla0JfjuRp0Cge+yIEePWMQPC71bCa7qAEsQ3sbOAedtmSoNlvk5yie2qc0ZUa/ zJ7Jqh8AP3PRUq/gP63JJ9Ox2HImeyRZlu6v2QvtllgoBxMElKPmYIAbA+gnKmu0FCgt +9wQ== X-Gm-Message-State: AOAM530gbRxW9G3KgmHhRyeESX5e+uzScizdfvvLwgGiTNOGR9WeE7Jw uNp7MNUNzpmEtAcrK2yzp0vvLrC73qQKnQ== X-Google-Smtp-Source: ABdhPJxd5dnJ3XbU3ZEX7ZbaO6F4zubvXzTkpEE+wVnvO3YPUREzMkciETDzOL8TN6YndjeWM+oi1KK1N2g4SA== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a17:90a:d90e:: with SMTP id c14mr5116993pjv.85.1610133024194; Fri, 08 Jan 2021 11:10:24 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:40 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-2-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 1/6] hw/misc: Add clock converter in NPCM7XX CLK module To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1049; envelope-from=3IK74XwgKCmUZXKDRWVKJRRJOH.FRPTHPX-GHYHOQRQJQX.RUJ@flex--wuhaotsh.bounces.google.com; helo=mail-pj1-x1049.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 @@ =20 #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" =20 +/* + * The reference clock hz, and the SECCNT and CNTR25M registers in this mo= dule, + * is always 25 MHz. + */ +#define NPCM7XX_CLOCK_REF_HZ (25000000) + +/* 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_PWDEN BIT(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) =20 enum NPCM7xxCLKRegisters { NPCM7XX_CLK_CLKEN1, @@ -89,12 +103,609 @@ static const uint32_t cold_reset_values[NPCM7XX_CLK_N= R_REGS] =3D { [NPCM7XX_CLK_AHBCKFI] =3D 0x000000c8, }; =20 -/* 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) =20 +/* 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 =3D opaque; + uint32_t con =3D s->clk->regs[s->reg]; + uint64_t freq; + + /* The PLL is grounded if it is not locked yet. */ + if (con & PLLCON_LOKI) { + freq =3D clock_get_hz(s->clock_in); + freq *=3D PLLCON_FBDV(con); + freq /=3D PLLCON_INDV(con) * PLLCON_OTDV1(con) * PLLCON_OTDV2(con); + } else { + freq =3D 0; + } + + clock_update_hz(s->clock_out, freq); +} + +static void npcm7xx_clk_update_sel(void *opaque) +{ + NPCM7xxClockSELState *s =3D opaque; + uint32_t index =3D extract32(s->clk->regs[NPCM7XX_CLK_CLKSEL], s->offs= et, + s->len); + + if (index >=3D s->input_size) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: SEL index: %u out of range\n", + __func__, index); + index =3D 0; + } + clock_update_hz(s->clock_out, clock_get_hz(s->clock_in[index])); +} + +static void npcm7xx_clk_update_divider(void *opaque) +{ + NPCM7xxClockDividerState *s =3D opaque; + uint32_t freq; + + freq =3D 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 NPCM7XX_CLOCK_PLL2; + case NPCM7XX_CLK_PLLCONG: + return NPCM7XX_CLOCK_PLLG; + default: + g_assert_not_reached(); + } +} + +static void npcm7xx_clk_update_all_plls(NPCM7xxCLKState *clk) +{ + int i; + + for (i =3D 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + npcm7xx_clk_update_pll(&clk->plls[i]); + } +} + +static void npcm7xx_clk_update_all_sels(NPCM7xxCLKState *clk) +{ + int i; + + for (i =3D 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + npcm7xx_clk_update_sel(&clk->sels[i]); + } +} + +static void npcm7xx_clk_update_all_dividers(NPCM7xxCLKState *clk) +{ + int i; + + for (i =3D 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + npcm7xx_clk_update_divider(&clk->dividers[i]); + } +} + +static void npcm7xx_clk_update_all_clocks(NPCM7xxCLKState *clk) +{ + clock_update_hz(clk->clkref, NPCM7XX_CLOCK_REF_HZ); + npcm7xx_clk_update_all_plls(clk); + npcm7xx_clk_update_all_sels(clk); + npcm7xx_clk_update_all_dividers(clk); +} + +/* Types of clock sources. */ +typedef enum ClockSrcType { + CLKSRC_REF, + CLKSRC_PLL, + CLKSRC_SEL, + CLKSRC_DIV, +} ClockSrcType; + +typedef struct PLLInitInfo { + const char *name; + ClockSrcType src_type; + int src_index; + int reg; + const char *public_name; +} PLLInitInfo; + +typedef struct SELInitInfo { + const char *name; + uint8_t input_size; + ClockSrcType src_type[NPCM7XX_CLK_SEL_MAX_INPUT]; + int src_index[NPCM7XX_CLK_SEL_MAX_INPUT]; + int offset; + int len; + const char *public_name; +} SELInitInfo; + +typedef struct DividerInitInfo { + const char *name; + ClockSrcType src_type; + int src_index; + uint32_t (*divide)(NPCM7xxClockDividerState *s); + int reg; /* not used when type =3D=3D CONSTANT */ + int offset; /* not used when type =3D=3D CONSTANT */ + int len; /* not used when type =3D=3D CONSTANT */ + int divisor; /* used only when type =3D=3D CONSTANT */ + const char *public_name; +} DividerInitInfo; + +static const PLLInitInfo pll_init_info_list[] =3D { + [NPCM7XX_CLOCK_PLL0] =3D { + .name =3D "pll0", + .src_type =3D CLKSRC_REF, + .reg =3D NPCM7XX_CLK_PLLCON0, + }, + [NPCM7XX_CLOCK_PLL1] =3D { + .name =3D "pll1", + .src_type =3D CLKSRC_REF, + .reg =3D NPCM7XX_CLK_PLLCON1, + }, + [NPCM7XX_CLOCK_PLL2] =3D { + .name =3D "pll2", + .src_type =3D CLKSRC_REF, + .reg =3D NPCM7XX_CLK_PLLCON2, + }, + [NPCM7XX_CLOCK_PLLG] =3D { + .name =3D "pllg", + .src_type =3D CLKSRC_REF, + .reg =3D NPCM7XX_CLK_PLLCONG, + }, +}; + +static const SELInitInfo sel_init_info_list[] =3D { + [NPCM7XX_CLOCK_PIXCKSEL] =3D { + .name =3D "pixcksel", + .input_size =3D 2, + .src_type =3D {CLKSRC_PLL, CLKSRC_REF}, + .src_index =3D {NPCM7XX_CLOCK_PLLG, 0}, + .offset =3D 5, + .len =3D 1, + .public_name =3D "pixel-clock", + }, + [NPCM7XX_CLOCK_MCCKSEL] =3D { + .name =3D "mccksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_DIV, CLKSRC_REF, CLKSRC_REF, + /*MCBPCK, shouldn't be used in normal operation*/ + CLKSRC_REF}, + .src_index =3D {NPCM7XX_CLOCK_PLL1D2, 0, 0, 0}, + .offset =3D 12, + .len =3D 2, + .public_name =3D "mc-phy-clock", + }, + [NPCM7XX_CLOCK_CPUCKSEL] =3D { + .name =3D "cpucksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, + /*SYSBPCK, shouldn't be used in normal operation*/ + CLKSRC_REF}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, 0}, + .offset =3D 0, + .len =3D 2, + .public_name =3D "system-clock", + }, + [NPCM7XX_CLOCK_CLKOUTSEL] =3D { + .name =3D "clkoutsel", + .input_size =3D 5, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, + CLKSRC_PLL, CLKSRC_DIV}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLLG, NPCM7XX_CLOCK_PLL2D2}, + .offset =3D 18, + .len =3D 3, + .public_name =3D "tock", + }, + [NPCM7XX_CLOCK_UARTCKSEL] =3D { + .name =3D "uartcksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset =3D 8, + .len =3D 2, + }, + [NPCM7XX_CLOCK_TIMCKSEL] =3D { + .name =3D "timcksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset =3D 14, + .len =3D 2, + }, + [NPCM7XX_CLOCK_SDCKSEL] =3D { + .name =3D "sdcksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset =3D 6, + .len =3D 2, + }, + [NPCM7XX_CLOCK_GFXMSEL] =3D { + .name =3D "gfxmksel", + .input_size =3D 2, + .src_type =3D {CLKSRC_REF, CLKSRC_PLL}, + .src_index =3D {0, NPCM7XX_CLOCK_PLL2}, + .offset =3D 21, + .len =3D 1, + }, + [NPCM7XX_CLOCK_SUCKSEL] =3D { + .name =3D "sucksel", + .input_size =3D 4, + .src_type =3D {CLKSRC_PLL, CLKSRC_DIV, CLKSRC_REF, CLKSRC_DIV}, + .src_index =3D {NPCM7XX_CLOCK_PLL0, NPCM7XX_CLOCK_PLL1D2, 0, + NPCM7XX_CLOCK_PLL2D2}, + .offset =3D 10, + .len =3D 2, + }, +}; + +static const DividerInitInfo divider_init_info_list[] =3D { + [NPCM7XX_CLOCK_PLL1D2] =3D { + .name =3D "pll1d2", + .src_type =3D CLKSRC_PLL, + .src_index =3D NPCM7XX_CLOCK_PLL1, + .divide =3D divide_by_constant, + .divisor =3D 2, + }, + [NPCM7XX_CLOCK_PLL2D2] =3D { + .name =3D "pll2d2", + .src_type =3D CLKSRC_PLL, + .src_index =3D NPCM7XX_CLOCK_PLL2, + .divide =3D divide_by_constant, + .divisor =3D 2, + }, + [NPCM7XX_CLOCK_MC_DIVIDER] =3D { + .name =3D "mc-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_MCCKSEL, + .divide =3D divide_by_constant, + .divisor =3D 2, + .public_name =3D "mc-clock" + }, + [NPCM7XX_CLOCK_AXI_DIVIDER] =3D { + .name =3D "axi-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_CPUCKSEL, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 0, + .len =3D 1, + .public_name =3D "clk2" + }, + [NPCM7XX_CLOCK_AHB_DIVIDER] =3D { + .name =3D "ahb-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AXI_DIVIDER, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 26, + .len =3D 2, + .public_name =3D "clk4" + }, + [NPCM7XX_CLOCK_AHB3_DIVIDER] =3D { + .name =3D "ahb3-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 6, + .len =3D 5, + .public_name =3D "ahb3-spi3-clock" + }, + [NPCM7XX_CLOCK_SPI0_DIVIDER] =3D { + .name =3D "spi0-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV3, + .offset =3D 6, + .len =3D 5, + .public_name =3D "spi0-clock", + }, + [NPCM7XX_CLOCK_SPIX_DIVIDER] =3D { + .name =3D "spix-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV3, + .offset =3D 1, + .len =3D 5, + .public_name =3D "spix-clock", + }, + [NPCM7XX_CLOCK_APB1_DIVIDER] =3D { + .name =3D "apb1-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 24, + .len =3D 2, + .public_name =3D "apb1-clock", + }, + [NPCM7XX_CLOCK_APB2_DIVIDER] =3D { + .name =3D "apb2-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 26, + .len =3D 2, + .public_name =3D "apb2-clock", + }, + [NPCM7XX_CLOCK_APB3_DIVIDER] =3D { + .name =3D "apb3-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 28, + .len =3D 2, + .public_name =3D "apb3-clock", + }, + [NPCM7XX_CLOCK_APB4_DIVIDER] =3D { + .name =3D "apb4-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 30, + .len =3D 2, + .public_name =3D "apb4-clock", + }, + [NPCM7XX_CLOCK_APB5_DIVIDER] =3D { + .name =3D "apb5-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_AHB_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 22, + .len =3D 2, + .public_name =3D "apb5-clock", + }, + [NPCM7XX_CLOCK_CLKOUT_DIVIDER] =3D { + .name =3D "clkout-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_CLKOUTSEL, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 16, + .len =3D 5, + .public_name =3D "clkout", + }, + [NPCM7XX_CLOCK_UART_DIVIDER] =3D { + .name =3D "uart-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_UARTCKSEL, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 16, + .len =3D 5, + .public_name =3D "uart-clock", + }, + [NPCM7XX_CLOCK_TIMER_DIVIDER] =3D { + .name =3D "timer-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_TIMCKSEL, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 21, + .len =3D 5, + .public_name =3D "timer-clock", + }, + [NPCM7XX_CLOCK_ADC_DIVIDER] =3D { + .name =3D "adc-divider", + .src_type =3D CLKSRC_DIV, + .src_index =3D NPCM7XX_CLOCK_TIMER_DIVIDER, + .divide =3D shift_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 28, + .len =3D 3, + .public_name =3D "adc-clock", + }, + [NPCM7XX_CLOCK_MMC_DIVIDER] =3D { + .name =3D "mmc-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_SDCKSEL, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV1, + .offset =3D 11, + .len =3D 5, + .public_name =3D "mmc-clock", + }, + [NPCM7XX_CLOCK_SDHC_DIVIDER] =3D { + .name =3D "sdhc-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_SDCKSEL, + .divide =3D divide_by_reg_divisor_times_2, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 0, + .len =3D 4, + .public_name =3D "sdhc-clock", + }, + [NPCM7XX_CLOCK_GFXM_DIVIDER] =3D { + .name =3D "gfxm-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_GFXMSEL, + .divide =3D divide_by_constant, + .divisor =3D 3, + .public_name =3D "gfxm-clock", + }, + [NPCM7XX_CLOCK_UTMI_DIVIDER] =3D { + .name =3D "utmi-divider", + .src_type =3D CLKSRC_SEL, + .src_index =3D NPCM7XX_CLOCK_SUCKSEL, + .divide =3D divide_by_reg_divisor, + .reg =3D NPCM7XX_CLK_CLKDIV2, + .offset =3D 8, + .len =3D 5, + .public_name =3D "utmi-clock", + }, +}; + +static void npcm7xx_clk_pll_init(Object *obj) +{ + NPCM7xxClockPLLState *pll =3D NPCM7XX_CLOCK_PLL(obj); + + pll->clock_in =3D qdev_init_clock_in(DEVICE(pll), "clock-in", + npcm7xx_clk_update_pll, pll); + pll->clock_out =3D qdev_init_clock_out(DEVICE(pll), "clock-out"); +} + +static void npcm7xx_clk_sel_init(Object *obj) +{ + int i; + NPCM7xxClockSELState *sel =3D NPCM7XX_CLOCK_SEL(obj); + + for (i =3D 0; i < NPCM7XX_CLK_SEL_MAX_INPUT; ++i) { + sel->clock_in[i] =3D qdev_init_clock_in(DEVICE(sel), + g_strdup_printf("clock-in[%d]", i), + npcm7xx_clk_update_sel, sel); + } + sel->clock_out =3D qdev_init_clock_out(DEVICE(sel), "clock-out"); +} +static void npcm7xx_clk_divider_init(Object *obj) +{ + NPCM7xxClockDividerState *div =3D NPCM7XX_CLOCK_DIVIDER(obj); + + div->clock_in =3D qdev_init_clock_in(DEVICE(div), "clock-in", + npcm7xx_clk_update_divider, div); + div->clock_out =3D qdev_init_clock_out(DEVICE(div), "clock-out"); +} + +static void npcm7xx_init_clock_pll(NPCM7xxClockPLLState *pll, + NPCM7xxCLKState *clk, const PLLInitInfo *init_info) +{ + pll->name =3D init_info->name; + pll->clk =3D clk; + pll->reg =3D init_info->reg; + if (init_info->public_name !=3D NULL) { + qdev_alias_clock(DEVICE(pll), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static void npcm7xx_init_clock_sel(NPCM7xxClockSELState *sel, + NPCM7xxCLKState *clk, const SELInitInfo *init_info) +{ + int input_size =3D init_info->input_size; + + sel->name =3D init_info->name; + sel->clk =3D clk; + sel->input_size =3D init_info->input_size; + g_assert(input_size <=3D NPCM7XX_CLK_SEL_MAX_INPUT); + sel->offset =3D init_info->offset; + sel->len =3D init_info->len; + if (init_info->public_name !=3D NULL) { + qdev_alias_clock(DEVICE(sel), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static void npcm7xx_init_clock_divider(NPCM7xxClockDividerState *div, + NPCM7xxCLKState *clk, const DividerInitInfo *init_info) +{ + div->name =3D init_info->name; + div->clk =3D clk; + + div->divide =3D init_info->divide; + if (div->divide =3D=3D divide_by_constant) { + div->divisor =3D init_info->divisor; + } else { + div->reg =3D init_info->reg; + div->offset =3D init_info->offset; + div->len =3D init_info->len; + } + if (init_info->public_name !=3D NULL) { + qdev_alias_clock(DEVICE(div), "clock-out", DEVICE(clk), + init_info->public_name); + } +} + +static Clock *npcm7xx_get_clock(NPCM7xxCLKState *clk, ClockSrcType type, + int index) +{ + switch (type) { + case CLKSRC_REF: + return clk->clkref; + case CLKSRC_PLL: + return clk->plls[index].clock_out; + case CLKSRC_SEL: + return clk->sels[index].clock_out; + case CLKSRC_DIV: + return clk->dividers[index].clock_out; + default: + g_assert_not_reached(); + } +} + +static void npcm7xx_connect_clocks(NPCM7xxCLKState *clk) +{ + int i, j; + Clock *src; + + for (i =3D 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + src =3D npcm7xx_get_clock(clk, pll_init_info_list[i].src_type, + pll_init_info_list[i].src_index); + clock_set_source(clk->plls[i].clock_in, src); + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + for (j =3D 0; j < sel_init_info_list[i].input_size; ++j) { + src =3D npcm7xx_get_clock(clk, sel_init_info_list[i].src_type[= j], + sel_init_info_list[i].src_index[j]); + clock_set_source(clk->sels[i].clock_in[j], src); + } + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + src =3D npcm7xx_get_clock(clk, divider_init_info_list[i].src_type, + divider_init_info_list[i].src_index); + clock_set_source(clk->dividers[i].clock_in, src); + } +} + static uint64_t npcm7xx_clk_read(void *opaque, hwaddr offset, unsigned siz= e) { uint32_t reg =3D offset / sizeof(uint32_t); @@ -129,7 +740,7 @@ static uint64_t npcm7xx_clk_read(void *opaque, hwaddr o= ffset, unsigned size) * * The 4 LSBs are always zero: (1e9 / 640) << 4 =3D 25000000. */ - value =3D (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_TIMER_REF_= HZ; + value =3D (((now_ns - s->ref_ns) / 640) << 4) % NPCM7XX_CLOCK_REF_= HZ; break; =20 default: @@ -183,6 +794,20 @@ static void npcm7xx_clk_write(void *opaque, hwaddr off= set, value |=3D (value & PLLCON_LOKS); } } + /* Only update PLL when it is locked. */ + if (value & PLLCON_LOKI) { + npcm7xx_clk_update_pll(&s->plls[find_pll_by_reg(reg)]); + } + break; + + case NPCM7XX_CLK_CLKSEL: + npcm7xx_clk_update_all_sels(s); + break; + + case NPCM7XX_CLK_CLKDIV1: + case NPCM7XX_CLK_CLKDIV2: + case NPCM7XX_CLK_CLKDIV3: + npcm7xx_clk_update_all_dividers(s); break; =20 case NPCM7XX_CLK_CNTR25M: @@ -234,6 +859,7 @@ static void npcm7xx_clk_enter_reset(Object *obj, ResetT= ype type) case RESET_TYPE_COLD: memcpy(s->regs, cold_reset_values, sizeof(cold_reset_values)); s->ref_ns =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + npcm7xx_clk_update_all_clocks(s); return; } =20 @@ -245,6 +871,42 @@ static void npcm7xx_clk_enter_reset(Object *obj, Reset= Type type) __func__, type); } =20 +static void npcm7xx_clk_init_clock_hierarchy(NPCM7xxCLKState *s) +{ + int i; + + s->clkref =3D qdev_init_clock_in(DEVICE(s), "clkref", NULL, NULL); + + /* First pass: init all converter modules */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(pll_init_info_list) !=3D NPCM7XX_CLOCK_NR= _PLLS); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(sel_init_info_list) !=3D NPCM7XX_CLOCK_NR= _SELS); + QEMU_BUILD_BUG_ON(ARRAY_SIZE(divider_init_info_list) + !=3D NPCM7XX_CLOCK_NR_DIVIDERS); + for (i =3D 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + object_initialize_child(OBJECT(s), pll_init_info_list[i].name, + &s->plls[i], TYPE_NPCM7XX_CLOCK_PLL); + npcm7xx_init_clock_pll(&s->plls[i], s, + &pll_init_info_list[i]); + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + object_initialize_child(OBJECT(s), sel_init_info_list[i].name, + &s->sels[i], TYPE_NPCM7XX_CLOCK_SEL); + npcm7xx_init_clock_sel(&s->sels[i], s, + &sel_init_info_list[i]); + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + object_initialize_child(OBJECT(s), divider_init_info_list[i].name, + &s->dividers[i], TYPE_NPCM7XX_CLOCK_DIVIDER); + npcm7xx_init_clock_divider(&s->dividers[i], s, + ÷r_init_info_list[i]); + } + + /* Second pass: connect converter modules */ + npcm7xx_connect_clocks(s); + + clock_update_hz(s->clkref, NPCM7XX_CLOCK_REF_HZ); +} + static void npcm7xx_clk_init(Object *obj) { NPCM7xxCLKState *s =3D NPCM7XX_CLK(obj); @@ -252,21 +914,114 @@ static void npcm7xx_clk_init(Object *obj) memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, TYPE_NPCM7XX_CLK, 4 * KiB); sysbus_init_mmio(&s->parent, &s->iomem); +} + +static int npcm7xx_clk_post_load(void *opaque, int version_id) +{ + if (version_id >=3D 1) { + NPCM7xxCLKState *clk =3D opaque; + + npcm7xx_clk_update_all_clocks(clk); + } + + return 0; +} + +static void npcm7xx_clk_realize(DeviceState *dev, Error **errp) +{ + int i; + NPCM7xxCLKState *s =3D NPCM7XX_CLK(dev); + qdev_init_gpio_in_named(DEVICE(s), npcm7xx_clk_perform_watchdog_reset, NPCM7XX_WATCHDOG_RESET_GPIO_IN, NPCM7XX_NR_WATCHDOGS); + npcm7xx_clk_init_clock_hierarchy(s); + + /* Realize child devices */ + for (i =3D 0; i < NPCM7XX_CLOCK_NR_PLLS; ++i) { + if (!qdev_realize(DEVICE(&s->plls[i]), NULL, errp)) { + return; + } + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_SELS; ++i) { + if (!qdev_realize(DEVICE(&s->sels[i]), NULL, errp)) { + return; + } + } + for (i =3D 0; i < NPCM7XX_CLOCK_NR_DIVIDERS; ++i) { + if (!qdev_realize(DEVICE(&s->dividers[i]), NULL, errp)) { + return; + } + } } =20 -static const VMStateDescription vmstate_npcm7xx_clk =3D { - .name =3D "npcm7xx-clk", +static const VMStateDescription vmstate_npcm7xx_clk_pll =3D { + .name =3D "npcm7xx-clock-pll", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_CLOCK(clock_in, NPCM7xxClockPLLState), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_clk_sel =3D { + .name =3D "npcm7xx-clock-sel", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(clock_in, NPCM7xxClockSELState, + NPCM7XX_CLK_SEL_MAX_INPUT, 0, vmstate_clock, Clock), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_clk_divider =3D { + .name =3D "npcm7xx-clock-divider", .version_id =3D 0, .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_CLOCK(clock_in, NPCM7xxClockDividerState), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_clk =3D { + .name =3D "npcm7xx-clk", + .version_id =3D 1, + .minimum_version_id =3D 1, + .post_load =3D npcm7xx_clk_post_load, .fields =3D (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, NPCM7xxCLKState, NPCM7XX_CLK_NR_REGS), VMSTATE_INT64(ref_ns, NPCM7xxCLKState), + VMSTATE_CLOCK(clkref, NPCM7xxCLKState), VMSTATE_END_OF_LIST(), }, }; =20 +static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "NPCM7xx Clock PLL Module"; + dc->vmsd =3D &vmstate_npcm7xx_clk_pll; +} + +static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "NPCM7xx Clock SEL Module"; + dc->vmsd =3D &vmstate_npcm7xx_clk_sel; +} + +static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "NPCM7xx Clock Divider Module"; + dc->vmsd =3D &vmstate_npcm7xx_clk_divider; +} + static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) { ResettableClass *rc =3D RESETTABLE_CLASS(klass); @@ -276,9 +1031,34 @@ static void npcm7xx_clk_class_init(ObjectClass *klass= , void *data) =20 dc->desc =3D "NPCM7xx Clock Control Registers"; dc->vmsd =3D &vmstate_npcm7xx_clk; + dc->realize =3D npcm7xx_clk_realize; rc->phases.enter =3D npcm7xx_clk_enter_reset; } =20 +static const TypeInfo npcm7xx_clk_pll_info =3D { + .name =3D TYPE_NPCM7XX_CLOCK_PLL, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(NPCM7xxClockPLLState), + .instance_init =3D npcm7xx_clk_pll_init, + .class_init =3D npcm7xx_clk_pll_class_init, +}; + +static const TypeInfo npcm7xx_clk_sel_info =3D { + .name =3D TYPE_NPCM7XX_CLOCK_SEL, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(NPCM7xxClockSELState), + .instance_init =3D npcm7xx_clk_sel_init, + .class_init =3D npcm7xx_clk_sel_class_init, +}; + +static const TypeInfo npcm7xx_clk_divider_info =3D { + .name =3D TYPE_NPCM7XX_CLOCK_DIVIDER, + .parent =3D TYPE_DEVICE, + .instance_size =3D sizeof(NPCM7xxClockDividerState), + .instance_init =3D npcm7xx_clk_divider_init, + .class_init =3D npcm7xx_clk_divider_class_init, +}; + static const TypeInfo npcm7xx_clk_info =3D { .name =3D TYPE_NPCM7XX_CLK, .parent =3D TYPE_SYS_BUS_DEVICE, @@ -289,6 +1069,9 @@ static const TypeInfo npcm7xx_clk_info =3D { =20 static void npcm7xx_clk_register_type(void) { + type_register_static(&npcm7xx_clk_pll_info); + type_register_static(&npcm7xx_clk_sel_info); + type_register_static(&npcm7xx_clk_divider_info); type_register_static(&npcm7xx_clk_info); } type_init(npcm7xx_clk_register_type); diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h index 2338fbbdb5..f641f95f3e 100644 --- a/include/hw/misc/npcm7xx_clk.h +++ b/include/hw/misc/npcm7xx_clk.h @@ -17,6 +17,7 @@ #define NPCM7XX_CLK_H =20 #include "exec/memory.h" +#include "hw/clock.h" #include "hw/sysbus.h" =20 /* @@ -33,16 +34,151 @@ =20 #define NPCM7XX_WATCHDOG_RESET_GPIO_IN "npcm7xx-clk-watchdog-reset-gpio-in" =20 -typedef struct NPCM7xxCLKState { +/* Maximum amount of clock inputs in a SEL module. */ +#define NPCM7XX_CLK_SEL_MAX_INPUT 5 + +/* PLLs in CLK module. */ +typedef enum NPCM7xxClockPLL { + NPCM7XX_CLOCK_PLL0, + NPCM7XX_CLOCK_PLL1, + NPCM7XX_CLOCK_PLL2, + NPCM7XX_CLOCK_PLLG, + NPCM7XX_CLOCK_NR_PLLS, +} NPCM7xxClockPLL; + +/* SEL/MUX in CLK module. */ +typedef enum NPCM7xxClockSEL { + NPCM7XX_CLOCK_PIXCKSEL, + NPCM7XX_CLOCK_MCCKSEL, + NPCM7XX_CLOCK_CPUCKSEL, + NPCM7XX_CLOCK_CLKOUTSEL, + NPCM7XX_CLOCK_UARTCKSEL, + NPCM7XX_CLOCK_TIMCKSEL, + NPCM7XX_CLOCK_SDCKSEL, + NPCM7XX_CLOCK_GFXMSEL, + NPCM7XX_CLOCK_SUCKSEL, + NPCM7XX_CLOCK_NR_SELS, +} NPCM7xxClockSEL; + +/* Dividers in CLK module. */ +typedef enum NPCM7xxClockDivider { + NPCM7XX_CLOCK_PLL1D2, /* PLL1/2 */ + NPCM7XX_CLOCK_PLL2D2, /* PLL2/2 */ + NPCM7XX_CLOCK_MC_DIVIDER, + NPCM7XX_CLOCK_AXI_DIVIDER, + NPCM7XX_CLOCK_AHB_DIVIDER, + NPCM7XX_CLOCK_AHB3_DIVIDER, + NPCM7XX_CLOCK_SPI0_DIVIDER, + NPCM7XX_CLOCK_SPIX_DIVIDER, + NPCM7XX_CLOCK_APB1_DIVIDER, + NPCM7XX_CLOCK_APB2_DIVIDER, + NPCM7XX_CLOCK_APB3_DIVIDER, + NPCM7XX_CLOCK_APB4_DIVIDER, + NPCM7XX_CLOCK_APB5_DIVIDER, + NPCM7XX_CLOCK_CLKOUT_DIVIDER, + NPCM7XX_CLOCK_UART_DIVIDER, + NPCM7XX_CLOCK_TIMER_DIVIDER, + NPCM7XX_CLOCK_ADC_DIVIDER, + NPCM7XX_CLOCK_MMC_DIVIDER, + NPCM7XX_CLOCK_SDHC_DIVIDER, + NPCM7XX_CLOCK_GFXM_DIVIDER, /* divide by 3 */ + NPCM7XX_CLOCK_UTMI_DIVIDER, + NPCM7XX_CLOCK_NR_DIVIDERS, +} NPCM7xxClockConverter; + +typedef struct NPCM7xxCLKState NPCM7xxCLKState; + +/** + * struct NPCM7xxClockPLLState - A PLL module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @clock_in: The input clock of this module. + * @clock_out: The output clock of this module. + * @reg: The control registers for this PLL module. + */ +typedef struct NPCM7xxClockPLLState { + DeviceState parent; + + const char *name; + NPCM7xxCLKState *clk; + Clock *clock_in; + Clock *clock_out; + + int reg; +} NPCM7xxClockPLLState; + +/** + * struct NPCM7xxClockSELState - A SEL module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @input_size: The size of inputs of this module. + * @clock_in: The input clocks of this module. + * @clock_out: The output clocks of this module. + * @offset: The offset of this module in the control register. + * @len: The length of this module in the control register. + */ +typedef struct NPCM7xxClockSELState { + DeviceState parent; + + const char *name; + NPCM7xxCLKState *clk; + uint8_t input_size; + Clock *clock_in[NPCM7XX_CLK_SEL_MAX_INPUT]; + Clock *clock_out; + + int offset; + int len; +} NPCM7xxClockSELState; + +/** + * struct NPCM7xxClockDividerState - A Divider module in CLK module. + * @name: The name of the module. + * @clk: The CLK module that owns this module. + * @clock_in: The input clock of this module. + * @clock_out: The output clock of this module. + * @divide: The function the divider uses to divide the input. + * @reg: The index of the control register that contains the divisor. + * @offset: The offset of the divisor in the control register. + * @len: The length of the divisor in the control register. + * @divisor: The divisor for a constant divisor + */ +typedef struct NPCM7xxClockDividerState { + DeviceState parent; + + const char *name; + NPCM7xxCLKState *clk; + Clock *clock_in; + Clock *clock_out; + + uint32_t (*divide)(struct NPCM7xxClockDividerState *s); + union { + struct { + int reg; + int offset; + int len; + }; + int divisor; + }; +} NPCM7xxClockDividerState; + +struct NPCM7xxCLKState { SysBusDevice parent; =20 MemoryRegion iomem; =20 + /* Clock converters */ + NPCM7xxClockPLLState plls[NPCM7XX_CLOCK_NR_PLLS]; + NPCM7xxClockSELState sels[NPCM7XX_CLOCK_NR_SELS]; + NPCM7xxClockDividerState dividers[NPCM7XX_CLOCK_NR_DIVIDERS]; + uint32_t regs[NPCM7XX_CLK_NR_REGS]; =20 /* Time reference for SECCNT and CNTR25M, initialized by power on rese= t */ int64_t ref_ns; -} NPCM7xxCLKState; + + /* The incoming reference clock. */ + Clock *clkref; +}; =20 #define TYPE_NPCM7XX_CLK "npcm7xx-clk" #define NPCM7XX_CLK(obj) OBJECT_CHECK(NPCM7xxCLKState, (obj), TYPE_NPCM7XX= _CLK) --=20 2.29.2.729.g45daf8777d-goog From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133242; cv=none; d=zohomail.com; s=zohoarc; b=BzFxygBxKPb5JE3z2aT538Hn60mnnVjoF7noBu1OCS7Zx6YAG4/1C1IfYDESE7mIyWf3WMOldMLViyckhvAHzA+PhTaNeB6oi4ezckCP8OMV2Db/Ynlw98ZDCTVP3AtmYg8OsE6YmITCznawLSU+c+QwEF/t983WYyzCyAU4x6Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133242; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=Fyb4FJea0+LttCueKhL0/bu+E731whPsjDvUt294ZsI=; b=kNPPKo+cQJa+bi/+hoGNe+Zy+iJ//7QeanJ8tprWqFfkrwiidiE6PoRAGO3R7COdg3ApEcjpJ7mvxAe0XKO34lw+7yvW15Wn77okj+txkUuZmFOKQyFOc9dQzkHsRNKILFy829WfJRzd9cad+VsW30tN2V3k7rfLBuTg9aVgF6g= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1610133242436304.8417953385032; Fri, 8 Jan 2021 11:14:02 -0800 (PST) Received: from localhost ([::1]:49306 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxCz-0002gX-2u for importer@patchew.org; Fri, 08 Jan 2021 14:14:01 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40316) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3Ia74XwgKCmYaYLESXWLKSSKPI.GSQUIQY-HIZIPRSRKRY.SVK@flex--wuhaotsh.bounces.google.com>) id 1kxx9h-00018h-LI for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:38 -0500 Received: from mail-pl1-x64a.google.com ([2607:f8b0:4864:20::64a]:49575) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3Ia74XwgKCmYaYLESXWLKSSKPI.GSQUIQY-HIZIPRSRKRY.SVK@flex--wuhaotsh.bounces.google.com>) id 1kxx9a-0007KJ-Uh for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:37 -0500 Received: by mail-pl1-x64a.google.com with SMTP id bg11so6908620plb.16 for ; Fri, 08 Jan 2021 11:10:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=Fyb4FJea0+LttCueKhL0/bu+E731whPsjDvUt294ZsI=; b=XUeJlklbrhpads4eo5ZJOIw0RJpZd2uIRkJfiT1KlT6w4v0CKncgBi6O2/3uuxqARX 23H1j/O3mNS7aXJZLpvFe2G4MndiZlHrwCwS/k/feytAmLbkrFkU6EjAizvKtNPsHs+4 /KGlDatcU14ZEyltzyQNmbU6SdPaNgejgl/1P6iFikr7IgsG8WQ2bkSO4SDR6wFxOMBA 8bO7SCN4SOrU7bl0ds9pcT1+lcOcqt49YyLjYkr1lWUEkBe2uWlkjO15q4mV18dAc6uz wMgqf23/H/2I/z6zkbEZv1bXaOoQbNhY1AfnKwLRf1oanG4BCqJ94xcd/de1Nh25NCPx 6dcQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=Fyb4FJea0+LttCueKhL0/bu+E731whPsjDvUt294ZsI=; b=F4zMxghF2s5VyYSlEFlU/yhyHB6yvbd/sLTwPvT982K8jdQe38ILjPjsAY4pc19085 MYLd/jtT6I+3zL9sc/54e4gMPBh4ruNdQgZ9/YoKibBrhLZTurgIjsUwb80/yuAVVnxp GrOOD0+vjw0HFyks0tlfwOIFTqHf3HG7ePFzKko0ICpjuYZShadAyGltEESpn7g/p91B zFb8KxxL9RegKVy7um0E/b/cBcFixdPT9ZY/8OUqg27AUOHl4uZlOMvxi4Yje0jKX/80 JI2GS5FwhXFbc1sFcufQz1wxt6eY8p6D6YGK1ufmsztLoUtZRs4yXWjSLwlS+y2IQu0o mszg== X-Gm-Message-State: AOAM531EFNpynZY3DveRYd5uT4D3qz3TT8DE9MRocD/HW/U70vWptBY8 /INGma/tXhV6Z0RIUpVL7O5Ki9lWwDGWXw== X-Google-Smtp-Source: ABdhPJx69PeOr9IWJygMFXWSMmoWh8D5i6HFuP5pyUgkqI3UmKR//25O0nlB8IDZQ2B4zka0D3sdm1Qehe1E0w== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a62:1716:0:b029:19d:b78b:ef02 with SMTP id 22-20020a6217160000b029019db78bef02mr5030444pfx.11.1610133025893; Fri, 08 Jan 2021 11:10:25 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:41 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-3-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 2/6] hw/timer: Refactor NPCM7XX Timer to use CLK clock To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::64a; envelope-from=3Ia74XwgKCmYaYLESXWLKSSKPI.GSQUIQY-HIZIPRSRKRY.SVK@flex--wuhaotsh.bounces.google.com; helo=mail-pl1-x64a.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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; =20 + /* Connect the timer clock. */ + qdev_connect_clock_in(DEVICE(&s->tim[i]), "clock", qdev_get_clock_= out( + DEVICE(&s->clk), "timer-clock")); + sysbus_realize(sbd, &error_abort); sysbus_mmio_map(sbd, 0, npcm7xx_tim_addr[i]); =20 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" =20 #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 =3D count; + int64_t ticks =3D count; =20 - ns *=3D NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ; - ns *=3D npcm7xx_tcsr_prescaler(t->tcsr); + ticks *=3D npcm7xx_tcsr_prescaler(t->tcsr); =20 - return ns; + return clock_ticks_to_ns(t->ctrl->clock, ticks); } =20 /* 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 =3D ns / (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ); - count /=3D npcm7xx_tcsr_prescaler(t->tcsr); - - return count; + return ns / clock_ticks_to_ns(t->ctrl->clock, + npcm7xx_tcsr_prescaler(t->tcsr)); } =20 static uint32_t npcm7xx_watchdog_timer_prescaler(const NPCM7xxWatchdogTime= r *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 =3D npcm7xx_watchdog_timer_prescaler(t); - int64_t ns =3D (NANOSECONDS_PER_SECOND / NPCM7XX_TIMER_REF_HZ) * cycle= s; + int64_t ticks =3D cycles * npcm7xx_watchdog_timer_prescaler(t); + int64_t ns =3D clock_ticks_to_ns(t->ctrl->clock, ticks); =20 /* * The reset function always clears the current timer. The caller of t= he @@ -176,7 +171,6 @@ static void npcm7xx_watchdog_timer_reset_cycles(NPCM7xx= WatchdogTimer *t, */ npcm7xx_timer_clear(&t->base_timer); =20 - ns *=3D prescaler; t->base_timer.remaining_ns =3D ns; } =20 @@ -606,10 +600,11 @@ static void npcm7xx_timer_hold_reset(Object *obj) qemu_irq_lower(s->watchdog_timer.irq); } =20 -static void npcm7xx_timer_realize(DeviceState *dev, Error **errp) +static void npcm7xx_timer_init(Object *obj) { - NPCM7xxTimerCtrlState *s =3D NPCM7XX_TIMER(dev); - SysBusDevice *sbd =3D &s->parent; + NPCM7xxTimerCtrlState *s =3D NPCM7XX_TIMER(obj); + DeviceState *dev =3D DEVICE(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); int i; NPCM7xxWatchdogTimer *w; =20 @@ -627,11 +622,12 @@ static void npcm7xx_timer_realize(DeviceState *dev, E= rror **errp) npcm7xx_watchdog_timer_expired, w); sysbus_init_irq(sbd, &w->irq); =20 - memory_region_init_io(&s->iomem, OBJECT(s), &npcm7xx_timer_ops, s, + memory_region_init_io(&s->iomem, obj, &npcm7xx_timer_ops, s, TYPE_NPCM7XX_TIMER, 4 * KiB); sysbus_init_mmio(sbd, &s->iomem); qdev_init_gpio_out_named(dev, &w->reset_signal, NPCM7XX_WATCHDOG_RESET_GPIO_OUT, 1); + s->clock =3D qdev_init_clock_in(dev, "clock", NULL, NULL); } =20 static const VMStateDescription vmstate_npcm7xx_base_timer =3D { @@ -675,10 +671,11 @@ static const VMStateDescription vmstate_npcm7xx_watch= dog_timer =3D { =20 static const VMStateDescription vmstate_npcm7xx_timer_ctrl =3D { .name =3D "npcm7xx-timer-ctrl", - .version_id =3D 1, - .minimum_version_id =3D 1, + .version_id =3D 2, + .minimum_version_id =3D 2, .fields =3D (VMStateField[]) { VMSTATE_UINT32(tisr, NPCM7xxTimerCtrlState), + VMSTATE_CLOCK(clock, NPCM7xxTimerCtrlState), VMSTATE_STRUCT_ARRAY(timer, NPCM7xxTimerCtrlState, NPCM7XX_TIMERS_PER_CTRL, 0, vmstate_npcm7xx_t= imer, NPCM7xxTimer), @@ -697,7 +694,6 @@ static void npcm7xx_timer_class_init(ObjectClass *klass= , void *data) QEMU_BUILD_BUG_ON(NPCM7XX_TIMER_REGS_END > NPCM7XX_TIMER_NR_REGS); =20 dc->desc =3D "NPCM7xx Timer Controller"; - dc->realize =3D npcm7xx_timer_realize; dc->vmsd =3D &vmstate_npcm7xx_timer_ctrl; rc->phases.enter =3D npcm7xx_timer_enter_reset; rc->phases.hold =3D npcm7xx_timer_hold_reset; @@ -708,6 +704,7 @@ static const TypeInfo npcm7xx_timer_info =3D { .parent =3D TYPE_SYS_BUS_DEVICE, .instance_size =3D sizeof(NPCM7xxTimerCtrlState), .class_init =3D npcm7xx_timer_class_init, + .instance_init =3D npcm7xx_timer_init, }; =20 static void npcm7xx_timer_register_type(void) diff --git a/include/hw/misc/npcm7xx_clk.h b/include/hw/misc/npcm7xx_clk.h index f641f95f3e..d5c8d16ca4 100644 --- a/include/hw/misc/npcm7xx_clk.h +++ b/include/hw/misc/npcm7xx_clk.h @@ -20,12 +20,6 @@ #include "hw/clock.h" #include "hw/sysbus.h" =20 -/* - * The reference clock frequency for the timer modules, and the SECCNT and - * CNTR25M registers in this module, is always 25 MHz. - */ -#define NPCM7XX_TIMER_REF_HZ (25000000) - /* * Number of registers in our device state structure. Don't change this wi= thout * incrementing the version_id in the vmstate. diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_ti= mer.h index 6993fd723a..d45c051b56 100644 --- a/include/hw/timer/npcm7xx_timer.h +++ b/include/hw/timer/npcm7xx_timer.h @@ -101,6 +101,7 @@ struct NPCM7xxTimerCtrlState { =20 uint32_t tisr; =20 + Clock *clock; NPCM7xxTimer timer[NPCM7XX_TIMERS_PER_CTRL]; NPCM7xxWatchdogTimer watchdog_timer; }; --=20 2.29.2.729.g45daf8777d-goog From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133520; cv=none; d=zohomail.com; s=zohoarc; b=icYlFW55QWcO382i/0ZfkiZ1FOfORJEI3+yK4vVkFUf4/FhUo7Rsoq9q4IVsDp5n0QtXqt1J7VountPopIf322FpOWwJY+Fzau1gEOTP1IeQahY5cPDiLwejEB4BhWWemdEVAzRaJJ4aNj++bNBzgwfUR3lRDLTC1cZBW+RAAJk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133520; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=LFMApgmcvcDNCjLnfm4pNHIGZoEohJnPNwpMriHcQFE=; b=F8GPrBqL5d6fG6rnEzDOJlZFkVI1X6U/ZmPMFS01KIvV5MeO804Xmyt0yRJKJccDiaJ+GMHtygnBjaS5EVcYYI+l/tbwstKFMvkduAw51tyUtiNz/qu2X1wsf6ENJ7ViGW5f1/mBL6aYiLLCwBHhzvZEgcg6MoSm6f+OSslRHis= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1610133519982208.88293718543287; Fri, 8 Jan 2021 11:18:39 -0800 (PST) Received: from localhost ([::1]:55154 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxHS-0005px-KU for importer@patchew.org; Fri, 08 Jan 2021 14:18:38 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40340) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3JK74XwgKCmkdbOHVaZONVVNSL.JVTXLTb-KLcLSUVUNUb.VYN@flex--wuhaotsh.bounces.google.com>) id 1kxx9i-00018l-W9 for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:39 -0500 Received: from mail-pj1-x1049.google.com ([2607:f8b0:4864:20::1049]:55696) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3JK74XwgKCmkdbOHVaZONVVNSL.JVTXLTb-KLcLSUVUNUb.VYN@flex--wuhaotsh.bounces.google.com>) id 1kxx9d-0007LD-Ul for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:38 -0500 Received: by mail-pj1-x1049.google.com with SMTP id 25so7143935pjb.5 for ; Fri, 08 Jan 2021 11:10:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=LFMApgmcvcDNCjLnfm4pNHIGZoEohJnPNwpMriHcQFE=; b=AEKD5cxxfQ0J2L7YBJx0gbOMcsAYU2w3B4iC5LZgl3LV+i7OmDaUBdTrGZ7T9NXqQH CWLEfQ/MGC68khXQvO03jTSf5Z1MpreTHvi0p4LA8yQYV6LzHl7LaJggV3NOpha7+Bg7 BndSakaCk5sZ0FYLg4Cp99GDFTJLTJ0YMJrEr7BDcgPGkh+q0bcnVvP5Di6QQRkJAdeE gi7T4iWSrrfhzDjKhbVgw+YpJQf6xZGovlqpK1tZjDGj3I4wfdBvZ9nrO2/dlKxYxVVE dUspVay/HrqXiT2d33Nnj6qug0H7yOizYwzagJel/s2FJQRWslWJhiqnjO2iFrOtMuZc VAZA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=LFMApgmcvcDNCjLnfm4pNHIGZoEohJnPNwpMriHcQFE=; b=a+5auihmPinpKyGgp9on0TxDdu5x0U6Nq34waG785JfO1gZ/VejM2w86QPHRZ/IRgA JblINpbKdy8Jg6IpBfSZ9QrxO2uvnQq3uAT/FB7t/EwDHyLbjbGNLSuOJC7IM+kufl7F iIQxpxJZ8vzSwb3fadeTsgNs1PI9nYaOCImc062mO/aC8IqTxdBcw54chFnztNFtSNpO P0HQCeL3OY87bqCe8FB9HAlKU5P+8Lq9Mm56wF2Mnz7yJLaPEpQaZ37Nk3DXzJ8XY3bn 427/6ywhpjuLldXxdlguxt9p8IRwSIpXcoePIJqh8vPxF1jP+oynQZsy6WpeeFeTyATU G0ug== X-Gm-Message-State: AOAM5330wuIaBr5hWou/jnm/nw8uLfi3TurhJU6nvL6aYWz8W4jHUT85 HD+XJLP0Rf4EOAK0cDLuCaaxG3QPNAACUw== X-Google-Smtp-Source: ABdhPJxs/q21SPe8Gb2RU84T4IllVPIOMgUPzTODcAzg1jxmu5S5nFpk5dwVYaia2W2L0moQmVJ9NG6XOPRa/w== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a62:7f4c:0:b029:19e:23d1:cf0a with SMTP id a73-20020a627f4c0000b029019e23d1cf0amr5039230pfd.67.1610133028157; Fri, 08 Jan 2021 11:10:28 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:42 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-4-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 3/6] hw/adc: Add an ADC module for NPCM7XX To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::1049; envelope-from=3JK74XwgKCmkdbOHVaZONVVNSL.JVTXLTb-KLcLSUVUNUb.VYN@flex--wuhaotsh.bounces.google.com; helo=mail-pj1-x1049.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) =20 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 0000000000..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 WIT= HOUT + * 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_CONV BIT(14) +#define NPCM7XX_ADC_CON_DIV(rv) extract32(rv, 1, 8) + +#define NPCM7XX_ADC_MAX_RESULT 1023 +#define NPCM7XX_ADC_DEFAULT_IREF 2000000 +#define NPCM7XX_ADC_CONV_CYCLES 20 +#define NPCM7XX_ADC_RESET_CYCLES 10 +#define NPCM7XX_ADC_R0_INPUT 500000 +#define NPCM7XX_ADC_R1_INPUT 1500000 + +static void npcm7xx_adc_reset(NPCM7xxADCState *s) +{ + timer_del(&s->conv_timer); + s->con =3D 0x000c0001; + s->data =3D 0x00000000; +} + +static uint32_t npcm7xx_adc_convert(uint32_t input, uint32_t ref) +{ + uint32_t result; + + result =3D input * (NPCM7XX_ADC_MAX_RESULT + 1) / ref; + if (result > NPCM7XX_ADC_MAX_RESULT) { + result =3D 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 =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t ticks =3D cycles; + int64_t ns; + + ticks *=3D prescaler; + ns =3D clock_ticks_to_ns(clk, ticks); + ns +=3D now; + timer_mod(timer, ns); +} + +static void npcm7xx_adc_start_convert(NPCM7xxADCState *s) +{ + uint32_t prescaler =3D npcm7xx_adc_prescaler(s); + + npcm7xx_adc_start_timer(s->clock, &s->conv_timer, NPCM7XX_ADC_CONV_CYC= LES, + prescaler); +} + +static void npcm7xx_adc_convert_done(void *opaque) +{ + NPCM7xxADCState *s =3D opaque; + uint32_t input =3D NPCM7XX_ADC_CON_MUX(s->con); + uint32_t ref =3D (s->con & NPCM7XX_ADC_CON_REFSEL) + ? s->iref : s->vref; + + if (input >=3D NPCM7XX_ADC_NUM_INPUTS) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid input: %u\n", + __func__, input); + return; + } + s->data =3D npcm7xx_adc_convert(s->adci[input], ref); + if (s->con & NPCM7XX_ADC_CON_INT_EN) { + s->con |=3D NPCM7XX_ADC_CON_INT; + qemu_irq_raise(s->irq); + } + s->con &=3D ~NPCM7XX_ADC_CON_CONV; +} + +static void npcm7xx_adc_calibrate(NPCM7xxADCState *adc) +{ + adc->calibration_r_values[0] =3D npcm7xx_adc_convert(NPCM7XX_ADC_R0_IN= PUT, + adc->iref); + adc->calibration_r_values[1] =3D npcm7xx_adc_convert(NPCM7XX_ADC_R1_IN= PUT, + adc->iref); +} + +static void npcm7xx_adc_write_con(NPCM7xxADCState *s, uint32_t new_con) +{ + uint32_t old_con =3D s->con; + + /* Write ADC_INT to 1 to clear it */ + if (new_con & NPCM7XX_ADC_CON_INT) { + new_con &=3D ~NPCM7XX_ADC_CON_INT; + qemu_irq_lower(s->irq); + } else if (old_con & NPCM7XX_ADC_CON_INT) { + new_con |=3D NPCM7XX_ADC_CON_INT; + } + + s->con =3D new_con; + + if (s->con & NPCM7XX_ADC_CON_RST) { + npcm7xx_adc_reset(s); + return; + } + + if ((s->con & NPCM7XX_ADC_CON_EN)) { + if (s->con & NPCM7XX_ADC_CON_CONV) { + if (!(old_con & NPCM7XX_ADC_CON_CONV)) { + npcm7xx_adc_start_convert(s); + } + } else { + timer_del(&s->conv_timer); + } + } +} + +static uint64_t npcm7xx_adc_read(void *opaque, hwaddr offset, unsigned siz= e) +{ + uint64_t value =3D 0; + NPCM7xxADCState *s =3D opaque; + + switch (offset) { + case A_NPCM7XX_ADC_CON: + value =3D s->con; + break; + + case A_NPCM7XX_ADC_DATA: + value =3D s->data; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + trace_npcm7xx_adc_read(DEVICE(s)->canonical_path, offset, value); + return value; +} + +static void npcm7xx_adc_write(void *opaque, hwaddr offset, uint64_t v, + unsigned size) +{ + NPCM7xxADCState *s =3D opaque; + + trace_npcm7xx_adc_write(DEVICE(s)->canonical_path, offset, v); + switch (offset) { + case A_NPCM7XX_ADC_CON: + npcm7xx_adc_write_con(s, v); + break; + + case A_NPCM7XX_ADC_DATA: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", + __func__, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + break; + } + +} + +static const struct MemoryRegionOps npcm7xx_adc_ops =3D { + .read =3D npcm7xx_adc_read, + .write =3D npcm7xx_adc_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, +}; + +static void npcm7xx_adc_enter_reset(Object *obj, ResetType type) +{ + NPCM7xxADCState *s =3D NPCM7XX_ADC(obj); + + npcm7xx_adc_reset(s); +} + +static void npcm7xx_adc_hold_reset(Object *obj) +{ + NPCM7xxADCState *s =3D NPCM7XX_ADC(obj); + + qemu_irq_lower(s->irq); +} + +static void npcm7xx_adc_init(Object *obj) +{ + NPCM7xxADCState *s =3D NPCM7XX_ADC(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + int i; + + sysbus_init_irq(sbd, &s->irq); + + timer_init_ns(&s->conv_timer, QEMU_CLOCK_VIRTUAL, + npcm7xx_adc_convert_done, s); + memory_region_init_io(&s->iomem, obj, &npcm7xx_adc_ops, s, + TYPE_NPCM7XX_ADC, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); + s->clock =3D qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL); + + for (i =3D 0; i < NPCM7XX_ADC_NUM_INPUTS; ++i) { + object_property_add_uint32_ptr(obj, "adci[*]", + &s->adci[i], OBJ_PROP_FLAG_WRITE); + } + object_property_add_uint32_ptr(obj, "vref", + &s->vref, OBJ_PROP_FLAG_WRITE); + npcm7xx_adc_calibrate(s); +} + +static const VMStateDescription vmstate_npcm7xx_adc =3D { + .name =3D "npcm7xx-adc", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_TIMER(conv_timer, NPCM7xxADCState), + VMSTATE_UINT32(con, NPCM7xxADCState), + VMSTATE_UINT32(data, NPCM7xxADCState), + VMSTATE_CLOCK(clock, NPCM7xxADCState), + VMSTATE_UINT32_ARRAY(adci, NPCM7xxADCState, NPCM7XX_ADC_NUM_INPUTS= ), + VMSTATE_UINT32(vref, NPCM7xxADCState), + VMSTATE_UINT32(iref, NPCM7xxADCState), + VMSTATE_UINT16_ARRAY(calibration_r_values, NPCM7xxADCState, + NPCM7XX_ADC_NUM_CALIB), + VMSTATE_END_OF_LIST(), + }, +}; + +static Property npcm7xx_timer_properties[] =3D { + DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_= IREF), + DEFINE_PROP_END_OF_LIST(), +}; + +static void npcm7xx_adc_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "NPCM7xx ADC Module"; + dc->vmsd =3D &vmstate_npcm7xx_adc; + rc->phases.enter =3D npcm7xx_adc_enter_reset; + rc->phases.hold =3D npcm7xx_adc_hold_reset; + + device_class_set_props(dc, npcm7xx_timer_properties); +} + +static const TypeInfo npcm7xx_adc_info =3D { + .name =3D TYPE_NPCM7XX_ADC, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(NPCM7xxADCState), + .class_init =3D npcm7xx_adc_class_init, + .instance_init =3D npcm7xx_adc_init, +}; + +static void npcm7xx_adc_register_types(void) +{ + type_register_static(&npcm7xx_adc_info); +} + +type_init(npcm7xx_adc_register_types); diff --git a/hw/adc/trace-events b/hw/adc/trace-events new file mode 100644 index 0000000000..4c3279ece2 --- /dev/null +++ b/hw/adc/trace-events @@ -0,0 +1,5 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# npcm7xx_adc.c +npcm7xx_adc_read(const char *id, uint64_t offset, uint32_t value) " %s off= set: 0x%04" PRIx64 " value 0x%04" PRIx32 +npcm7xx_adc_write(const char *id, uint64_t offset, uint32_t value) "%s off= set: 0x%04" PRIx64 " value 0x%04" PRIx32 diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index fabfb1697b..b22a8c966d 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -51,6 +51,9 @@ #define NPCM7XX_EHCI_BA (0xf0806000) #define NPCM7XX_OHCI_BA (0xf0807000) =20 +/* ADC Module */ +#define NPCM7XX_ADC_BA (0xf000c000) + /* Internal AHB SRAM */ #define NPCM7XX_RAM3_BA (0xc0008000) #define NPCM7XX_RAM3_SZ (4 * KiB) @@ -61,6 +64,7 @@ #define NPCM7XX_ROM_BA (0xffff0000) #define NPCM7XX_ROM_SZ (64 * KiB) =20 + /* Clock configuration values to be fixed up when bypassing bootloader */ =20 /* Run PLL1 at 1600 MHz */ @@ -73,6 +77,7 @@ * interrupts. */ enum NPCM7xxInterrupt { + NPCM7XX_ADC_IRQ =3D 0, NPCM7XX_UART0_IRQ =3D 2, NPCM7XX_UART1_IRQ, NPCM7XX_UART2_IRQ, @@ -296,6 +301,14 @@ static void npcm7xx_init_fuses(NPCM7xxState *s) sizeof(value)); } =20 +static void npcm7xx_write_adc_calibration(NPCM7xxState *s) +{ + /* Both ADC and the fuse array must have realized. */ + QEMU_BUILD_BUG_ON(sizeof(s->adc.calibration_r_values) !=3D 4); + npcm7xx_otp_array_write(&s->fuse_array, s->adc.calibration_r_values, + NPCM7XX_FUSE_ADC_CALIB, sizeof(s->adc.calibration_r_values)); +} + static qemu_irq npcm7xx_irq(NPCM7xxState *s, int n) { return qdev_get_gpio_in(DEVICE(&s->a9mpcore), n); @@ -322,6 +335,7 @@ static void npcm7xx_init(Object *obj) TYPE_NPCM7XX_FUSE_ARRAY); object_initialize_child(obj, "mc", &s->mc, TYPE_NPCM7XX_MC); object_initialize_child(obj, "rng", &s->rng, TYPE_NPCM7XX_RNG); + object_initialize_child(obj, "adc", &s->adc, TYPE_NPCM7XX_ADC); =20 for (i =3D 0; i < ARRAY_SIZE(s->tim); i++) { object_initialize_child(obj, "tim[*]", &s->tim[i], TYPE_NPCM7XX_TI= MER); @@ -414,6 +428,15 @@ static void npcm7xx_realize(DeviceState *dev, Error **= errp) sysbus_realize(SYS_BUS_DEVICE(&s->mc), &error_abort); sysbus_mmio_map(SYS_BUS_DEVICE(&s->mc), 0, NPCM7XX_MC_BA); =20 + /* ADC Modules. Cannot fail. */ + qdev_connect_clock_in(DEVICE(&s->adc), "clock", qdev_get_clock_out( + DEVICE(&s->clk), "adc-clock")); + sysbus_realize(SYS_BUS_DEVICE(&s->adc), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->adc), 0, NPCM7XX_ADC_BA); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->adc), 0, + npcm7xx_irq(s, NPCM7XX_ADC_IRQ)); + npcm7xx_write_adc_calibration(s); + /* Timer Modules (TIM). Cannot fail. */ QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_tim_addr) !=3D ARRAY_SIZE(s->tim)= ); for (i =3D 0; i < ARRAY_SIZE(s->tim); i++) { @@ -528,7 +551,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **e= rrp) create_unimplemented_device("npcm7xx.vdmx", 0xe0800000, 4 * = KiB); create_unimplemented_device("npcm7xx.pcierc", 0xe1000000, 64 * = KiB); create_unimplemented_device("npcm7xx.kcs", 0xf0007000, 4 * = KiB); - create_unimplemented_device("npcm7xx.adc", 0xf000c000, 4 * = KiB); create_unimplemented_device("npcm7xx.gfxi", 0xf000e000, 4 * = KiB); create_unimplemented_device("npcm7xx.gpio[0]", 0xf0010000, 4 * = KiB); create_unimplemented_device("npcm7xx.gpio[1]", 0xf0011000, 4 * = KiB); diff --git a/include/hw/adc/npcm7xx_adc.h b/include/hw/adc/npcm7xx_adc.h new file mode 100644 index 0000000000..7d8442107a --- /dev/null +++ b/include/hw/adc/npcm7xx_adc.h @@ -0,0 +1,69 @@ +/* + * 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 WIT= HOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef NPCM7XX_ADC_H +#define NPCM7XX_ADC_H + +#include "hw/clock.h" +#include "hw/irq.h" +#include "hw/sysbus.h" +#include "qemu/timer.h" + +#define NPCM7XX_ADC_NUM_INPUTS 8 +/** + * This value should not be changed unless write_adc_calibration function = in + * hw/arm/npcm7xx.c is also changed. + */ +#define NPCM7XX_ADC_NUM_CALIB 2 + +/** + * struct NPCM7xxADCState - Analog to Digital Converter Module device stat= e. + * @parent: System bus device. + * @iomem: Memory region through which registers are accessed. + * @conv_timer: The timer counts down remaining cycles for the conversion. + * @irq: GIC interrupt line to fire on expiration (if enabled). + * @con: The Control Register. + * @data: The Data Buffer. + * @clock: The ADC Clock. + * @adci: The input voltage in units of uV. 1uv =3D 1e-6V. + * @vref: The external reference voltage. + * @iref: The internal reference voltage, initialized at launch time. + * @rv: The calibrated output values of 0.5V and 1.5V for the ADC. + */ +typedef struct { + SysBusDevice parent; + + MemoryRegion iomem; + + QEMUTimer conv_timer; + + qemu_irq irq; + uint32_t con; + uint32_t data; + Clock *clock; + + /* Voltages are in unit of uV. 1V =3D 1000000uV. */ + uint32_t adci[NPCM7XX_ADC_NUM_INPUTS]; + uint32_t vref; + uint32_t iref; + + uint16_t calibration_r_values[NPCM7XX_ADC_NUM_CALIB]; +} NPCM7xxADCState; + +#define TYPE_NPCM7XX_ADC "npcm7xx-adc" +#define NPCM7XX_ADC(obj) \ + OBJECT_CHECK(NPCM7xxADCState, (obj), TYPE_NPCM7XX_ADC) + +#endif /* NPCM7XX_ADC_H */ diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 5469247e38..51e1c7620d 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -17,6 +17,7 @@ #define NPCM7XX_H =20 #include "hw/boards.h" +#include "hw/adc/npcm7xx_adc.h" #include "hw/cpu/a9mpcore.h" #include "hw/gpio/npcm7xx_gpio.h" #include "hw/mem/npcm7xx_mc.h" @@ -76,6 +77,7 @@ typedef struct NPCM7xxState { NPCM7xxGCRState gcr; NPCM7xxCLKState clk; NPCM7xxTimerCtrlState tim[3]; + NPCM7xxADCState adc; NPCM7xxOTPState key_storage; NPCM7xxOTPState fuse_array; NPCM7xxMCState mc; diff --git a/meson.build b/meson.build index 563688d682..88966516a5 100644 --- a/meson.build +++ b/meson.build @@ -1687,6 +1687,7 @@ if have_system 'chardev', 'hw/9pfs', 'hw/acpi', + 'hw/adc', 'hw/alpha', 'hw/arm', 'hw/audio', diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 6a67c538be..955710d1c5 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -134,7 +134,8 @@ qtests_sparc64 =3D \ ['prom-env-test', 'boot-serial-test'] =20 qtests_npcm7xx =3D \ - ['npcm7xx_gpio-test', + ['npcm7xx_adc-test', + 'npcm7xx_gpio-test', 'npcm7xx_rng-test', 'npcm7xx_timer-test', 'npcm7xx_watchdog_timer-test'] diff --git a/tests/qtest/npcm7xx_adc-test.c b/tests/qtest/npcm7xx_adc-test.c new file mode 100644 index 0000000000..f029706945 --- /dev/null +++ b/tests/qtest/npcm7xx_adc-test.c @@ -0,0 +1,377 @@ +/* + * QTests for Nuvoton NPCM7xx ADCModules. + * + * 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 WIT= HOUT + * 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 "qemu/timer.h" +#include "libqos/libqtest.h" +#include "qapi/qmp/qdict.h" + +#define REF_HZ (25000000) + +#define CON_OFFSET 0x0 +#define DATA_OFFSET 0x4 + +#define NUM_INPUTS 8 +#define DEFAULT_IREF 2000000 +#define CONV_CYCLES 20 +#define RESET_CYCLES 10 +#define R0_INPUT 500000 +#define R1_INPUT 1500000 +#define MAX_RESULT 1023 + +#define DEFAULT_CLKDIV 5 + +#define FUSE_ARRAY_BA 0xf018a000 +#define FCTL_OFFSET 0x14 +#define FST_OFFSET 0x0 +#define FADDR_OFFSET 0x4 +#define FDATA_OFFSET 0x8 +#define ADC_CALIB_ADDR 24 +#define FUSE_READ 0x2 + +/* Register field definitions. */ +#define CON_MUX(rv) ((rv) << 24) +#define CON_INT_EN BIT(21) +#define CON_REFSEL BIT(19) +#define CON_INT BIT(18) +#define CON_EN BIT(17) +#define CON_RST BIT(16) +#define CON_CONV BIT(14) +#define CON_DIV(rv) extract32(rv, 1, 8) + +#define FST_RDST BIT(1) +#define FDATA_MASK 0xff + +#define MAX_ERROR 10000 +#define MIN_CALIB_INPUT 100000 +#define MAX_CALIB_INPUT 1800000 + +static const uint32_t input_list[] =3D { + 100000, + 500000, + 1000000, + 1500000, + 1800000, + 2000000, +}; + +static const uint32_t vref_list[] =3D { + 2000000, + 2200000, + 2500000, +}; + +static const uint32_t iref_list[] =3D { + 1800000, + 1900000, + 2000000, + 2100000, + 2200000, +}; + +static const uint32_t div_list[] =3D {0, 1, 3, 7, 15}; + +typedef struct ADC { + int irq; + uint64_t base_addr; +} ADC; + +ADC adc =3D { + .irq =3D 0, + .base_addr =3D 0xf000c000 +}; + +static uint32_t adc_read_con(QTestState *qts, const ADC *adc) +{ + return qtest_readl(qts, adc->base_addr + CON_OFFSET); +} + +static void adc_write_con(QTestState *qts, const ADC *adc, uint32_t value) +{ + qtest_writel(qts, adc->base_addr + CON_OFFSET, value); +} + +static uint32_t adc_read_data(QTestState *qts, const ADC *adc) +{ + return qtest_readl(qts, adc->base_addr + DATA_OFFSET); +} + +static uint32_t adc_calibrate(uint32_t measured, uint32_t *rv) +{ + return R0_INPUT + (R1_INPUT - R0_INPUT) * (int32_t)(measured - rv[0]) + / (int32_t)(rv[1] - rv[0]); +} + +static void adc_qom_set(QTestState *qts, const ADC *adc, + const char *name, uint32_t value) +{ + QDict *response; + const char *path =3D "/machine/soc/adc"; + + g_test_message("Setting properties %s of %s with value %u", + name, path, value); + response =3D 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 void adc_write_input(QTestState *qts, const ADC *adc, + uint32_t index, uint32_t value) +{ + char name[100]; + + sprintf(name, "adci[%u]", index); + adc_qom_set(qts, adc, name, value); +} + +static void adc_write_vref(QTestState *qts, const ADC *adc, uint32_t value) +{ + adc_qom_set(qts, adc, "vref", value); +} + +static uint32_t adc_calculate_output(uint32_t input, uint32_t ref) +{ + uint32_t output; + + g_assert_cmpuint(input, <=3D, ref); + output =3D (input * (MAX_RESULT + 1)) / ref; + if (output > MAX_RESULT) { + output =3D MAX_RESULT; + } + + return output; +} + +static uint32_t adc_prescaler(QTestState *qts, const ADC *adc) +{ + uint32_t div =3D extract32(adc_read_con(qts, adc), 1, 8); + + return 2 * (div + 1); +} + +static int64_t adc_calculate_steps(uint32_t cycles, uint32_t prescale, + uint32_t clkdiv) +{ + return (NANOSECONDS_PER_SECOND / (REF_HZ >> clkdiv)) * cycles * presca= le; +} + +static void adc_wait_conv_finished(QTestState *qts, const ADC *adc, + uint32_t clkdiv) +{ + uint32_t prescaler =3D adc_prescaler(qts, adc); + + /* + * ADC should takes roughly 20 cycles to convert one sample. So we ass= ert it + * should take 10~30 cycles here. + */ + qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES / 2, prescaler, + clkdiv)); + /* ADC is still converting. */ + g_assert_true(adc_read_con(qts, adc) & CON_CONV); + qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES, prescaler, clkd= iv)); + /* ADC has finished conversion. */ + g_assert_false(adc_read_con(qts, adc) & CON_CONV); +} + +/* Check ADC can be reset to default value. */ +static void test_init(gconstpointer adc_p) +{ + const ADC *adc =3D adc_p; + + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + adc_write_con(qts, adc, CON_REFSEL | CON_INT); + g_assert_cmphex(adc_read_con(qts, adc), =3D=3D, CON_REFSEL); + qtest_quit(qts); +} + +/* Check ADC can convert from an internal reference. */ +static void test_convert_internal(gconstpointer adc_p) +{ + const ADC *adc =3D adc_p; + uint32_t index, input, output, expected_output; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + + for (index =3D 0; index < NUM_INPUTS; ++index) { + for (size_t i =3D 0; i < ARRAY_SIZE(input_list); ++i) { + input =3D input_list[i]; + expected_output =3D adc_calculate_output(input, DEFAULT_IREF); + + adc_write_input(qts, adc, index, input); + adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT | + CON_EN | CON_CONV); + adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV); + g_assert_cmphex(adc_read_con(qts, adc), =3D=3D, CON_MUX(index)= | + CON_REFSEL | CON_EN); + g_assert_false(qtest_get_irq(qts, adc->irq)); + output =3D adc_read_data(qts, adc); + g_assert_cmpuint(output, =3D=3D, expected_output); + } + } + + qtest_quit(qts); +} + +/* Check ADC can convert from an external reference. */ +static void test_convert_external(gconstpointer adc_p) +{ + const ADC *adc =3D adc_p; + uint32_t index, input, vref, output, expected_output; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + + for (index =3D 0; index < NUM_INPUTS; ++index) { + for (size_t i =3D 0; i < ARRAY_SIZE(input_list); ++i) { + for (size_t j =3D 0; j < ARRAY_SIZE(vref_list); ++j) { + input =3D input_list[i]; + vref =3D vref_list[j]; + expected_output =3D adc_calculate_output(input, vref); + + adc_write_input(qts, adc, index, input); + adc_write_vref(qts, adc, vref); + adc_write_con(qts, adc, CON_MUX(index) | CON_INT | CON_EN | + CON_CONV); + adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV); + g_assert_cmphex(adc_read_con(qts, adc), =3D=3D, + CON_MUX(index) | CON_EN); + g_assert_false(qtest_get_irq(qts, adc->irq)); + output =3D adc_read_data(qts, adc); + g_assert_cmpuint(output, =3D=3D, expected_output); + } + } + } + + qtest_quit(qts); +} + +/* Check ADC interrupt files if and only if CON_INT_EN is set. */ +static void test_interrupt(gconstpointer adc_p) +{ + const ADC *adc =3D adc_p; + uint32_t index, input, output, expected_output; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + + index =3D 1; + input =3D input_list[1]; + expected_output =3D adc_calculate_output(input, DEFAULT_IREF); + + qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic"); + adc_write_input(qts, adc, index, input); + g_assert_false(qtest_get_irq(qts, adc->irq)); + adc_write_con(qts, adc, CON_MUX(index) | CON_INT_EN | CON_REFSEL | CON= _INT + | CON_EN | CON_CONV); + adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV); + g_assert_cmphex(adc_read_con(qts, adc), =3D=3D, CON_MUX(index) | CON_I= NT_EN + | CON_REFSEL | CON_INT | CON_EN); + g_assert_true(qtest_get_irq(qts, adc->irq)); + output =3D adc_read_data(qts, adc); + g_assert_cmpuint(output, =3D=3D, expected_output); + + qtest_quit(qts); +} + +/* Check ADC is reset after setting ADC_RST for 10 ADC cycles. */ +static void test_reset(gconstpointer adc_p) +{ + const ADC *adc =3D adc_p; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + + for (size_t i =3D 0; i < ARRAY_SIZE(div_list); ++i) { + uint32_t div =3D div_list[i]; + + adc_write_con(qts, adc, CON_INT | CON_EN | CON_RST | CON_DIV(div)); + qtest_clock_step(qts, adc_calculate_steps(RESET_CYCLES, + adc_prescaler(qts, adc), DEFAULT_CLKDIV)); + g_assert_false(adc_read_con(qts, adc) & CON_EN); + } + qtest_quit(qts); +} + +/* Check ADC Calibration works as desired. */ +static void test_calibrate(gconstpointer adc_p) +{ + int i, j; + const ADC *adc =3D adc_p; + + for (j =3D 0; j < ARRAY_SIZE(iref_list); ++j) { + uint32_t iref =3D iref_list[j]; + uint32_t expected_rv[] =3D { + adc_calculate_output(R0_INPUT, iref), + adc_calculate_output(R1_INPUT, iref), + }; + char buf[100]; + QTestState *qts; + + sprintf(buf, "-machine quanta-gsj -global npcm7xx-adc.iref=3D%u", = iref); + qts =3D qtest_init(buf); + + /* Check the converted value is correct using the calibration valu= e. */ + for (i =3D 0; i < ARRAY_SIZE(input_list); ++i) { + uint32_t input; + uint32_t output; + uint32_t expected_output; + uint32_t calibrated_voltage; + uint32_t index =3D 0; + + input =3D input_list[i]; + /* Calibration only works for input range 0.1V ~ 1.8V. */ + if (input < MIN_CALIB_INPUT || input > MAX_CALIB_INPUT) { + continue; + } + expected_output =3D adc_calculate_output(input, iref); + + adc_write_input(qts, adc, index, input); + adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT | + CON_EN | CON_CONV); + adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV); + g_assert_cmphex(adc_read_con(qts, adc), =3D=3D, + CON_REFSEL | CON_MUX(index) | CON_EN); + output =3D adc_read_data(qts, adc); + g_assert_cmpuint(output, =3D=3D, expected_output); + + calibrated_voltage =3D adc_calibrate(output, expected_rv); + g_assert_cmpuint(calibrated_voltage, >, input - MAX_ERROR); + g_assert_cmpuint(calibrated_voltage, <, input + MAX_ERROR); + } + + qtest_quit(qts); + } +} + +static void adc_add_test(const char *name, const ADC* wd, + GTestDataFunc fn) +{ + g_autofree char *full_name =3D g_strdup_printf("npcm7xx_adc/%s", name= ); + qtest_add_data_func(full_name, wd, fn); +} +#define add_test(name, td) adc_add_test(#name, td, test_##name) + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + add_test(init, &adc); + add_test(convert_internal, &adc); + add_test(convert_external, &adc); + add_test(interrupt, &adc); + add_test(reset, &adc); + add_test(calibrate, &adc); + + return g_test_run(); +} --=20 2.29.2.729.g45daf8777d-goog From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133995; cv=none; d=zohomail.com; s=zohoarc; b=RPIlvLJiEWAwuqx67YiMIqjH4HdqGrAkFrB9H4mBkzBUeG1OwyDrTY1lo5ckx5cOfbLYphhzrI2koTJMBy/HgZODZHybYhsI2PDK0lNM0+RIzbrlEVB7pQyKT0GG19Cf8tWY9b7zEnkTrnppQuSoqZ0DuLgC7mtiR60dZKYRa0c= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133995; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=r+c8gA3nmxYNL7aBu2zdA8HqAK+wSYkjLQrk13sw7cg=; b=PKAmNwG1n282NKm0t7yDW+Mr4Tp3+LwpXpTzbUNPBC+4KkSnPAe8ljleOLpj6zwRF0DpUsXe+bxA+PHNYA96jy7SgFAAFpL4BSchU+txKqpY/SrXv597XoNtbAv51bzo+zaPsu1ugwUVJmYbgv5PWu3+WjZdd8CiABJqSFFON/w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 161013399563658.209629190459054; Fri, 8 Jan 2021 11:26:35 -0800 (PST) Received: from localhost ([::1]:41478 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxP8-0004ni-E2 for importer@patchew.org; Fri, 08 Jan 2021 14:26:34 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40456) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3Ja74XwgKCmoecPIWbaPOWWOTM.KWUYMUc-LMdMTVWVOVc.WZO@flex--wuhaotsh.bounces.google.com>) id 1kxx9q-0001D9-PP for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:47 -0500 Received: from mail-pg1-x549.google.com ([2607:f8b0:4864:20::549]:46902) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3Ja74XwgKCmoecPIWbaPOWWOTM.KWUYMUc-LMdMTVWVOVc.WZO@flex--wuhaotsh.bounces.google.com>) id 1kxx9f-0007LQ-H6 for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:44 -0500 Received: by mail-pg1-x549.google.com with SMTP id w13so8078014pgr.13 for ; Fri, 08 Jan 2021 11:10:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=r+c8gA3nmxYNL7aBu2zdA8HqAK+wSYkjLQrk13sw7cg=; b=gKrLTVpjgNbbiR6i/na8jJLGb4k3XlrqMtU+USgazvWi46azrCyxsMifqwq7i4Jv1Y e3tmpRe99hBbj0bzZ4GI3qOYJS4xfZPmcIVullN+XhazpojIGXVKq1l8tSTSnaSQ9J5e IqimepUirEbWPqV4r8gcy6DVGw74B7ZZXnKlFM1hPSianFbONcSOTodF/PR84YGIP9cH 6ubv1fm1+MCUmidHKEVIemBDpfW+pIzMGbvj9nXlL4Qyo8qC7FxfbjR4BCkKybPDdBpU JvUcnYEhZZUYyzGU4vb7EkSCL/NmubHHqD3/lHNMSvslzab89aVj7yt95ldp3ebbsMRP DAyA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=r+c8gA3nmxYNL7aBu2zdA8HqAK+wSYkjLQrk13sw7cg=; b=ph2tPO2eOVaKTxQaDOpG4w9ECEuKHGIO/MqcFcj/XYIZIhxgm5at8cy4FaglV2CdWB dVfkoibXleXbq8nH696ZO73egcIGmsnyCE+JDy+RrYL2yWTDuFnCuXoKPl0vmHLw4H04 0f3LIAM8S5RRy0gzB0U9Jq1CzcVY5PR41ZBnUu2MNi417p8ar7dmNztN0jSuUCJ1qFcB ViImFDrgOf+34FbJRsx3O68CtYErfpN9uz4YDiqCPbH2fOFOfF75mBlEyrSNXTCycyym aL9k6+Mp1wXZ4S3c1aXoqko4Ueuw0NQQNpr/YxXD2nP7cYZ6A42mZj8gnpX++/ZnJxlR AGag== X-Gm-Message-State: AOAM531Y/3kaoCTU/nxpOWsgtfsV3FHaZCJzxdNFVE/nxNJDynvhQ1ao XD+i3nBow+9FeP6KDFoN+ljnRC6CoYkeEA== X-Google-Smtp-Source: ABdhPJyKzOOrjBP0P4HpWQn5EA0Rd6V1PnVNY1fumrQi7ubMwg4QObCGCU3dh8TOtV4vmUkKnpKcih621PV97A== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a17:902:7207:b029:da:fd0c:521a with SMTP id ba7-20020a1709027207b02900dafd0c521amr5193681plb.45.1610133029830; Fri, 08 Jan 2021 11:10:29 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:43 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-5-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 4/6] hw/misc: Add a PWM module for NPCM7XX To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::549; envelope-from=3Ja74XwgKCmoecPIWbaPOWWOTM.KWUYMUc-LMdMTVWVOVc.WZO@flex--wuhaotsh.bounces.google.com; helo=mail-pg1-x549.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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) =20 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 =3D 61, NPCM7XX_OHCI_IRQ =3D 62, + NPCM7XX_PWM0_IRQ =3D 93, /* PWM module 0 */ + NPCM7XX_PWM1_IRQ, /* PWM module 1 */ NPCM7XX_GPIO0_IRQ =3D 116, NPCM7XX_GPIO1_IRQ, NPCM7XX_GPIO2_IRQ, @@ -144,6 +146,12 @@ static const hwaddr npcm7xx_fiu3_flash_addr[] =3D { 0xb8000000, /* CS3 */ }; =20 +/* Register base address for each PWM Module */ +static const hwaddr npcm7xx_pwm_addr[] =3D { + 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, &s->fiu[i], TYPE_NPCM7XX_FIU); } + + for (i =3D 0; i < ARRAY_SIZE(s->pwm); i++) { + object_initialize_child(obj, "pwm[*]", &s->pwm[i], TYPE_NPCM7XX_PW= M); + } } =20 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(&s->ohci), 0, npcm7xx_irq(s, NPCM7XX_OHCI_IRQ)); =20 + /* PWM Modules. Cannot fail. */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_pwm_addr) !=3D ARRAY_SIZE(s->pwm)= ); + for (i =3D 0; i < ARRAY_SIZE(s->pwm); i++) { + SysBusDevice *sbd =3D SYS_BUS_DEVICE(&s->pwm[i]); + + qdev_connect_clock_in(DEVICE(&s->pwm[i]), "clock", qdev_get_clock_= out( + DEVICE(&s->clk), "apb3-clock")); + sysbus_realize(sbd, &error_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 se= lects * specified, but this is a programming error. @@ -580,8 +604,6 @@ static void npcm7xx_realize(DeviceState *dev, Error **e= rrp) create_unimplemented_device("npcm7xx.peci", 0xf0100000, 4 * = KiB); create_unimplemented_device("npcm7xx.siox[1]", 0xf0101000, 4 * = KiB); create_unimplemented_device("npcm7xx.siox[2]", 0xf0102000, 4 * = KiB); - create_unimplemented_device("npcm7xx.pwm[0]", 0xf0103000, 4 * = KiB); - create_unimplemented_device("npcm7xx.pwm[1]", 0xf0104000, 4 * = KiB); create_unimplemented_device("npcm7xx.mft[0]", 0xf0180000, 4 * = KiB); create_unimplemented_device("npcm7xx.mft[1]", 0xf0181000, 4 * = KiB); create_unimplemented_device("npcm7xx.mft[2]", 0xf0182000, 4 * = KiB); 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 b/hw/misc/npcm7xx_pwm.c new file mode 100644 index 0000000000..e99e3cc7ef --- /dev/null +++ b/hw/misc/npcm7xx_pwm.c @@ -0,0 +1,550 @@ +/* + * Nuvoton NPCM7xx PWM 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 WIT= HOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "qemu/osdep.h" +#include "hw/irq.h" +#include "hw/qdev-clock.h" +#include "hw/qdev-properties.h" +#include "hw/misc/npcm7xx_pwm.h" +#include "hw/registerfields.h" +#include "migration/vmstate.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "qemu/units.h" +#include "trace.h" + +REG32(NPCM7XX_PWM_PPR, 0x00); +REG32(NPCM7XX_PWM_CSR, 0x04); +REG32(NPCM7XX_PWM_PCR, 0x08); +REG32(NPCM7XX_PWM_CNR0, 0x0c); +REG32(NPCM7XX_PWM_CMR0, 0x10); +REG32(NPCM7XX_PWM_PDR0, 0x14); +REG32(NPCM7XX_PWM_CNR1, 0x18); +REG32(NPCM7XX_PWM_CMR1, 0x1c); +REG32(NPCM7XX_PWM_PDR1, 0x20); +REG32(NPCM7XX_PWM_CNR2, 0x24); +REG32(NPCM7XX_PWM_CMR2, 0x28); +REG32(NPCM7XX_PWM_PDR2, 0x2c); +REG32(NPCM7XX_PWM_CNR3, 0x30); +REG32(NPCM7XX_PWM_CMR3, 0x34); +REG32(NPCM7XX_PWM_PDR3, 0x38); +REG32(NPCM7XX_PWM_PIER, 0x3c); +REG32(NPCM7XX_PWM_PIIR, 0x40); +REG32(NPCM7XX_PWM_PWDR0, 0x44); +REG32(NPCM7XX_PWM_PWDR1, 0x48); +REG32(NPCM7XX_PWM_PWDR2, 0x4c); +REG32(NPCM7XX_PWM_PWDR3, 0x50); + +/* Register field definitions. */ +#define NPCM7XX_PPR(rv, index) extract32((rv), npcm7xx_ppr_base[index= ], 8) +#define NPCM7XX_CSR(rv, index) extract32((rv), npcm7xx_csr_base[index= ], 3) +#define NPCM7XX_CH(rv, index) extract32((rv), npcm7xx_ch_base[index]= , 4) +#define NPCM7XX_CH_EN BIT(0) +#define NPCM7XX_CH_INV BIT(2) +#define NPCM7XX_CH_MOD BIT(3) + +/* Offset of each PWM channel's prescaler in the PPR register. */ +static const int npcm7xx_ppr_base[] =3D { 0, 0, 8, 8 }; +/* Offset of each PWM channel's clock selector in the CSR register. */ +static const int npcm7xx_csr_base[] =3D { 0, 4, 8, 12 }; +/* Offset of each PWM channel's control variable in the PCR register. */ +static const int npcm7xx_ch_base[] =3D { 0, 8, 12, 16 }; + +static uint32_t npcm7xx_pwm_calculate_freq(NPCM7xxPWM *p) +{ + uint32_t ppr; + uint32_t csr; + uint32_t freq; + + if (!p->running) { + return 0; + } + + csr =3D NPCM7XX_CSR(p->module->csr, p->index); + ppr =3D NPCM7XX_PPR(p->module->ppr, p->index); + freq =3D clock_get_hz(p->module->clock); + freq /=3D ppr + 1; + /* csr can only be 0~4 */ + if (csr > 4) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid csr value %u\n", + __func__, csr); + csr =3D 4; + } + /* freq won't be changed if csr =3D=3D 4. */ + if (csr < 4) { + freq >>=3D csr + 1; + } + + return freq / (p->cnr + 1); +} + +static uint32_t npcm7xx_pwm_calculate_duty(NPCM7xxPWM *p) +{ + uint64_t duty; + + if (p->running) { + if (p->cnr =3D=3D 0) { + duty =3D 0; + } else if (p->cmr >=3D p->cnr) { + duty =3D NPCM7XX_PWM_MAX_DUTY; + } else { + duty =3D NPCM7XX_PWM_MAX_DUTY * (p->cmr + 1) / (p->cnr + 1); + } + } else { + duty =3D 0; + } + + if (p->inverted) { + duty =3D NPCM7XX_PWM_MAX_DUTY - duty; + } + + return duty; +} + +static void npcm7xx_pwm_update_freq(NPCM7xxPWM *p) +{ + uint32_t freq =3D npcm7xx_pwm_calculate_freq(p); + + if (freq !=3D p->freq) { + trace_npcm7xx_pwm_update_freq(DEVICE(p->module)->canonical_path, + p->index, p->freq, freq); + p->freq =3D freq; + } +} + +static void npcm7xx_pwm_update_duty(NPCM7xxPWM *p) +{ + uint32_t duty =3D npcm7xx_pwm_calculate_duty(p); + + if (duty !=3D p->duty) { + trace_npcm7xx_pwm_update_duty(DEVICE(p->module)->canonical_path, + p->index, p->duty, duty); + p->duty =3D duty; + } +} + +static void npcm7xx_pwm_update_output(NPCM7xxPWM *p) +{ + npcm7xx_pwm_update_freq(p); + npcm7xx_pwm_update_duty(p); +} + +static void npcm7xx_pwm_write_ppr(NPCM7xxPWMState *s, uint32_t new_ppr) +{ + int i; + uint32_t old_ppr =3D s->ppr; + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ppr_base) !=3D NPCM7XX_PWM_PER_MO= DULE); + s->ppr =3D new_ppr; + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { + if (NPCM7XX_PPR(old_ppr, i) !=3D NPCM7XX_PPR(new_ppr, i)) { + npcm7xx_pwm_update_freq(&s->pwm[i]); + } + } +} + +static void npcm7xx_pwm_write_csr(NPCM7xxPWMState *s, uint32_t new_csr) +{ + int i; + uint32_t old_csr =3D s->csr; + + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_csr_base) !=3D NPCM7XX_PWM_PER_MO= DULE); + s->csr =3D new_csr; + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { + if (NPCM7XX_CSR(old_csr, i) !=3D NPCM7XX_CSR(new_csr, i)) { + npcm7xx_pwm_update_freq(&s->pwm[i]); + } + } +} + +static void npcm7xx_pwm_write_pcr(NPCM7xxPWMState *s, uint32_t new_pcr) +{ + int i; + bool inverted; + uint32_t pcr; + NPCM7xxPWM *p; + + s->pcr =3D new_pcr; + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm7xx_ch_base) !=3D NPCM7XX_PWM_PER_MOD= ULE); + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { + p =3D &s->pwm[i]; + pcr =3D NPCM7XX_CH(new_pcr, i); + inverted =3D pcr & NPCM7XX_CH_INV; + + /* + * We only run a PWM channel with toggle mode. Single-shot mode do= es not + * generate frequency and duty-cycle values. + */ + if ((pcr & NPCM7XX_CH_EN) && (pcr & NPCM7XX_CH_MOD)) { + if (p->running) { + /* Re-run this PWM channel if inverted changed. */ + if (p->inverted ^ inverted) { + p->inverted =3D inverted; + npcm7xx_pwm_update_duty(p); + } + } else { + /* Run this PWM channel. */ + p->running =3D true; + p->inverted =3D inverted; + npcm7xx_pwm_update_output(p); + } + } else { + /* Clear this PWM channel. */ + p->running =3D false; + p->inverted =3D inverted; + npcm7xx_pwm_update_output(p); + } + } + +} + +static hwaddr npcm7xx_cnr_index(hwaddr offset) +{ + switch (offset) { + case A_NPCM7XX_PWM_CNR0: + return 0; + case A_NPCM7XX_PWM_CNR1: + return 1; + case A_NPCM7XX_PWM_CNR2: + return 2; + case A_NPCM7XX_PWM_CNR3: + return 3; + default: + g_assert_not_reached(); + } +} + +static hwaddr npcm7xx_cmr_index(hwaddr offset) +{ + switch (offset) { + case A_NPCM7XX_PWM_CMR0: + return 0; + case A_NPCM7XX_PWM_CMR1: + return 1; + case A_NPCM7XX_PWM_CMR2: + return 2; + case A_NPCM7XX_PWM_CMR3: + return 3; + default: + g_assert_not_reached(); + } +} + +static hwaddr npcm7xx_pdr_index(hwaddr offset) +{ + switch (offset) { + case A_NPCM7XX_PWM_PDR0: + return 0; + case A_NPCM7XX_PWM_PDR1: + return 1; + case A_NPCM7XX_PWM_PDR2: + return 2; + case A_NPCM7XX_PWM_PDR3: + return 3; + default: + g_assert_not_reached(); + } +} + +static hwaddr npcm7xx_pwdr_index(hwaddr offset) +{ + switch (offset) { + case A_NPCM7XX_PWM_PWDR0: + return 0; + case A_NPCM7XX_PWM_PWDR1: + return 1; + case A_NPCM7XX_PWM_PWDR2: + return 2; + case A_NPCM7XX_PWM_PWDR3: + return 3; + default: + g_assert_not_reached(); + } +} + +static uint64_t npcm7xx_pwm_read(void *opaque, hwaddr offset, unsigned siz= e) +{ + NPCM7xxPWMState *s =3D opaque; + uint64_t value =3D 0; + + switch (offset) { + case A_NPCM7XX_PWM_CNR0: + case A_NPCM7XX_PWM_CNR1: + case A_NPCM7XX_PWM_CNR2: + case A_NPCM7XX_PWM_CNR3: + value =3D s->pwm[npcm7xx_cnr_index(offset)].cnr; + break; + + case A_NPCM7XX_PWM_CMR0: + case A_NPCM7XX_PWM_CMR1: + case A_NPCM7XX_PWM_CMR2: + case A_NPCM7XX_PWM_CMR3: + value =3D s->pwm[npcm7xx_cmr_index(offset)].cmr; + break; + + case A_NPCM7XX_PWM_PDR0: + case A_NPCM7XX_PWM_PDR1: + case A_NPCM7XX_PWM_PDR2: + case A_NPCM7XX_PWM_PDR3: + value =3D s->pwm[npcm7xx_pdr_index(offset)].pdr; + break; + + case A_NPCM7XX_PWM_PWDR0: + case A_NPCM7XX_PWM_PWDR1: + case A_NPCM7XX_PWM_PWDR2: + case A_NPCM7XX_PWM_PWDR3: + value =3D s->pwm[npcm7xx_pwdr_index(offset)].pwdr; + break; + + case A_NPCM7XX_PWM_PPR: + value =3D s->ppr; + break; + + case A_NPCM7XX_PWM_CSR: + value =3D s->csr; + break; + + case A_NPCM7XX_PWM_PCR: + value =3D s->pcr; + break; + + case A_NPCM7XX_PWM_PIER: + value =3D s->pier; + break; + + case A_NPCM7XX_PWM_PIIR: + value =3D s->piir; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + break; + } + + trace_npcm7xx_pwm_read(DEVICE(s)->canonical_path, offset, value); + return value; +} + +static void npcm7xx_pwm_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + NPCM7xxPWMState *s =3D opaque; + NPCM7xxPWM *p; + uint32_t value =3D v; + + trace_npcm7xx_pwm_write(DEVICE(s)->canonical_path, offset, value); + switch (offset) { + case A_NPCM7XX_PWM_CNR0: + case A_NPCM7XX_PWM_CNR1: + case A_NPCM7XX_PWM_CNR2: + case A_NPCM7XX_PWM_CNR3: + p =3D &s->pwm[npcm7xx_cnr_index(offset)]; + p->cnr =3D value; + npcm7xx_pwm_update_output(p); + break; + + case A_NPCM7XX_PWM_CMR0: + case A_NPCM7XX_PWM_CMR1: + case A_NPCM7XX_PWM_CMR2: + case A_NPCM7XX_PWM_CMR3: + p =3D &s->pwm[npcm7xx_cmr_index(offset)]; + p->cmr =3D value; + npcm7xx_pwm_update_output(p); + break; + + case A_NPCM7XX_PWM_PDR0: + case A_NPCM7XX_PWM_PDR1: + case A_NPCM7XX_PWM_PDR2: + case A_NPCM7XX_PWM_PDR3: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: register @ 0x%04" HWADDR_PRIx " is read-only\n", + __func__, offset); + break; + + case A_NPCM7XX_PWM_PWDR0: + case A_NPCM7XX_PWM_PWDR1: + case A_NPCM7XX_PWM_PWDR2: + case A_NPCM7XX_PWM_PWDR3: + qemu_log_mask(LOG_UNIMP, + "%s: register @ 0x%04" HWADDR_PRIx " is not implement= ed\n", + __func__, offset); + break; + + case A_NPCM7XX_PWM_PPR: + npcm7xx_pwm_write_ppr(s, value); + break; + + case A_NPCM7XX_PWM_CSR: + npcm7xx_pwm_write_csr(s, value); + break; + + case A_NPCM7XX_PWM_PCR: + npcm7xx_pwm_write_pcr(s, value); + break; + + case A_NPCM7XX_PWM_PIER: + qemu_log_mask(LOG_UNIMP, + "%s: register @ 0x%04" HWADDR_PRIx " is not implement= ed\n", + __func__, offset); + break; + + case A_NPCM7XX_PWM_PIIR: + qemu_log_mask(LOG_UNIMP, + "%s: register @ 0x%04" HWADDR_PRIx " is not implement= ed\n", + __func__, offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid offset 0x%04" HWADDR_PRIx "\n", + __func__, offset); + break; + } +} + +static const struct MemoryRegionOps npcm7xx_pwm_ops =3D { + .read =3D npcm7xx_pwm_read, + .write =3D npcm7xx_pwm_write, + .endianness =3D DEVICE_LITTLE_ENDIAN, + .valid =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + .unaligned =3D false, + }, +}; + +static void npcm7xx_pwm_enter_reset(Object *obj, ResetType type) +{ + NPCM7xxPWMState *s =3D NPCM7XX_PWM(obj); + int i; + + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; i++) { + NPCM7xxPWM *p =3D &s->pwm[i]; + + p->cnr =3D 0x00000000; + p->cmr =3D 0x00000000; + p->pdr =3D 0x00000000; + p->pwdr =3D 0x00000000; + } + + s->ppr =3D 0x00000000; + s->csr =3D 0x00000000; + s->pcr =3D 0x00000000; + s->pier =3D 0x00000000; + s->piir =3D 0x00000000; +} + +static void npcm7xx_pwm_hold_reset(Object *obj) +{ + NPCM7xxPWMState *s =3D NPCM7XX_PWM(obj); + int i; + + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; i++) { + qemu_irq_lower(s->pwm[i].irq); + } +} + +static void npcm7xx_pwm_init(Object *obj) +{ + NPCM7xxPWMState *s =3D NPCM7XX_PWM(obj); + SysBusDevice *sbd =3D SYS_BUS_DEVICE(obj); + int i; + + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; i++) { + NPCM7xxPWM *p =3D &s->pwm[i]; + p->module =3D s; + p->index =3D i; + sysbus_init_irq(sbd, &p->irq); + } + + memory_region_init_io(&s->iomem, obj, &npcm7xx_pwm_ops, s, + TYPE_NPCM7XX_PWM, 4 * KiB); + sysbus_init_mmio(sbd, &s->iomem); + s->clock =3D qdev_init_clock_in(DEVICE(s), "clock", NULL, NULL); + + for (i =3D 0; i < NPCM7XX_PWM_PER_MODULE; ++i) { + object_property_add_uint32_ptr(obj, "freq[*]", + &s->pwm[i].freq, OBJ_PROP_FLAG_READ); + object_property_add_uint32_ptr(obj, "duty[*]", + &s->pwm[i].duty, OBJ_PROP_FLAG_READ); + } +} + +static const VMStateDescription vmstate_npcm7xx_pwm =3D { + .name =3D "npcm7xx-pwm", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_BOOL(running, NPCM7xxPWM), + VMSTATE_BOOL(inverted, NPCM7xxPWM), + VMSTATE_UINT8(index, NPCM7xxPWM), + VMSTATE_UINT32(cnr, NPCM7xxPWM), + VMSTATE_UINT32(cmr, NPCM7xxPWM), + VMSTATE_UINT32(pdr, NPCM7xxPWM), + VMSTATE_UINT32(pwdr, NPCM7xxPWM), + VMSTATE_UINT32(freq, NPCM7xxPWM), + VMSTATE_UINT32(duty, NPCM7xxPWM), + VMSTATE_END_OF_LIST(), + }, +}; + +static const VMStateDescription vmstate_npcm7xx_pwm_module =3D { + .name =3D "npcm7xx-pwm-module", + .version_id =3D 0, + .minimum_version_id =3D 0, + .fields =3D (VMStateField[]) { + VMSTATE_CLOCK(clock, NPCM7xxPWMState), + VMSTATE_STRUCT_ARRAY(pwm, NPCM7xxPWMState, + NPCM7XX_PWM_PER_MODULE, 0, vmstate_npcm7xx_pw= m, + NPCM7xxPWM), + VMSTATE_UINT32(ppr, NPCM7xxPWMState), + VMSTATE_UINT32(csr, NPCM7xxPWMState), + VMSTATE_UINT32(pcr, NPCM7xxPWMState), + VMSTATE_UINT32(pier, NPCM7xxPWMState), + VMSTATE_UINT32(piir, NPCM7xxPWMState), + VMSTATE_END_OF_LIST(), + }, +}; + +static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data) +{ + ResettableClass *rc =3D RESETTABLE_CLASS(klass); + DeviceClass *dc =3D DEVICE_CLASS(klass); + + dc->desc =3D "NPCM7xx PWM Controller"; + dc->vmsd =3D &vmstate_npcm7xx_pwm_module; + rc->phases.enter =3D npcm7xx_pwm_enter_reset; + rc->phases.hold =3D npcm7xx_pwm_hold_reset; +} + +static const TypeInfo npcm7xx_pwm_info =3D { + .name =3D TYPE_NPCM7XX_PWM, + .parent =3D TYPE_SYS_BUS_DEVICE, + .instance_size =3D sizeof(NPCM7xxPWMState), + .class_init =3D npcm7xx_pwm_class_init, + .instance_init =3D npcm7xx_pwm_init, +}; + +static void npcm7xx_pwm_register_type(void) +{ + type_register_static(&npcm7xx_pwm_info); +} +type_init(npcm7xx_pwm_register_type); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b5118acd3f..d626b9d7a7 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -120,6 +120,12 @@ npcm7xx_gcr_write(uint64_t offset, uint32_t value) "of= fset: 0x%04" PRIx64 " valu npcm7xx_rng_read(uint64_t offset, uint64_t value, unsigned size) "offset: = 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" npcm7xx_rng_write(uint64_t offset, uint64_t value, unsigned size) "offset:= 0x%04" PRIx64 " value: 0x%02" PRIx64 " size: %u" =20 +# npcm7xx_pwm.c +npcm7xx_pwm_read(const char *id, uint64_t offset, uint32_t value) "%s offs= et: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm7xx_pwm_write(const char *id, uint64_t offset, uint32_t value) "%s off= set: 0x%04" PRIx64 " value: 0x%08" PRIx32 +npcm7xx_pwm_update_freq(const char *id, uint8_t index, uint32_t old_value,= uint32_t new_value) "%s pwm[%u] Update Freq: old_freq: %u, new_freq: %u" +npcm7xx_pwm_update_duty(const char *id, uint8_t index, uint32_t old_value,= uint32_t new_value) "%s pwm[%u] Update Duty: old_duty: %u, new_duty: %u" + # stm32f4xx_syscfg.c stm32f4xx_syscfg_set_irq(int gpio, int line, int level) "Interupt: GPIO: %= d, Line: %d; Level: %d" stm32f4xx_pulse_exti(int irq) "Pulse EXTI: %d" diff --git a/include/hw/arm/npcm7xx.h b/include/hw/arm/npcm7xx.h index 51e1c7620d..f6227aa8aa 100644 --- a/include/hw/arm/npcm7xx.h +++ b/include/hw/arm/npcm7xx.h @@ -23,6 +23,7 @@ #include "hw/mem/npcm7xx_mc.h" #include "hw/misc/npcm7xx_clk.h" #include "hw/misc/npcm7xx_gcr.h" +#include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_rng.h" #include "hw/nvram/npcm7xx_otp.h" #include "hw/timer/npcm7xx_timer.h" @@ -78,6 +79,7 @@ typedef struct NPCM7xxState { NPCM7xxCLKState clk; NPCM7xxTimerCtrlState tim[3]; NPCM7xxADCState adc; + NPCM7xxPWMState pwm[2]; NPCM7xxOTPState key_storage; NPCM7xxOTPState fuse_array; NPCM7xxMCState mc; diff --git a/include/hw/misc/npcm7xx_pwm.h b/include/hw/misc/npcm7xx_pwm.h new file mode 100644 index 0000000000..5a689d3f66 --- /dev/null +++ b/include/hw/misc/npcm7xx_pwm.h @@ -0,0 +1,105 @@ +/* + * Nuvoton NPCM7xx PWM 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 WIT= HOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ +#ifndef NPCM7XX_PWM_H +#define NPCM7XX_PWM_H + +#include "hw/clock.h" +#include "hw/sysbus.h" +#include "hw/irq.h" + +/* Each PWM module holds 4 PWM channels. */ +#define NPCM7XX_PWM_PER_MODULE 4 + +/* + * Number of registers in one pwm module. Don't change this without increa= sing + * the version_id in vmstate. + */ +#define NPCM7XX_PWM_NR_REGS (0x54 / sizeof(uint32_t)) + +/* + * The maximum duty values. Each duty unit represents 1/NPCM7XX_PWM_MAX_DU= TY + * cycles. For example, if NPCM7XX_PWM_MAX_DUTY=3D1,000,000 and a PWM has = a duty + * value of 100,000 the duty cycle for that PWM is 10%. + */ +#define NPCM7XX_PWM_MAX_DUTY 1000000 + +typedef struct NPCM7xxPWMState NPCM7xxPWMState; + +/** + * struct NPCM7xxPWM - The state of a single PWM channel. + * @module: The PWM module that contains this channel. + * @irq: GIC interrupt line to fire on expiration if enabled. + * @running: Whether this PWM channel is generating output. + * @inverted: Whether this PWM channel is inverted. + * @index: The index of this PWM channel. + * @cnr: The counter register. + * @cmr: The comparator register. + * @pdr: The data register. + * @pwdr: The watchdog register. + * @freq: The frequency of this PWM channel. + * @duty: The duty cycle of this PWM channel. One unit represents + * 1/NPCM7XX_MAX_DUTY cycles. + */ +typedef struct NPCM7xxPWM { + NPCM7xxPWMState *module; + + qemu_irq irq; + + bool running; + bool inverted; + + uint8_t index; + uint32_t cnr; + uint32_t cmr; + uint32_t pdr; + uint32_t pwdr; + + uint32_t freq; + uint32_t duty; +} NPCM7xxPWM; + +/** + * struct NPCM7xxPWMState - Pulse Width Modulation device state. + * @parent: System bus device. + * @iomem: Memory region through which registers are accessed. + * @clock: The PWM clock. + * @pwm: The PWM channels owned by this module. + * @ppr: The prescaler register. + * @csr: The clock selector register. + * @pcr: The control register. + * @pier: The interrupt enable register. + * @piir: The interrupt indication register. + */ +struct NPCM7xxPWMState { + SysBusDevice parent; + + MemoryRegion iomem; + + Clock *clock; + NPCM7xxPWM pwm[NPCM7XX_PWM_PER_MODULE]; + + uint32_t ppr; + uint32_t csr; + uint32_t pcr; + uint32_t pier; + uint32_t piir; +}; + +#define TYPE_NPCM7XX_PWM "npcm7xx-pwm" +#define NPCM7XX_PWM(obj) \ + OBJECT_CHECK(NPCM7xxPWMState, (obj), TYPE_NPCM7XX_PWM) + +#endif /* NPCM7XX_PWM_H */ --=20 2.29.2.729.g45daf8777d-goog From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133837; cv=none; d=zohomail.com; s=zohoarc; b=F0pd59aIWUZO1/IpqeNlXDGQzjKgHDsgsBKlIYCuwAUvWHPKgQti4K/HEvn/bEQgq2A5rnDdaifxbQx3PolE2c9aHF8WtNAiLxcDwhaCNHmB4eRlE6wiYaX7mNEAkZIry4G3StHR3ZcwHRrlOmRdEDDiC5U2RqFuULUrKnUn0Ko= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133837; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=vapmRTP1KBSBnDnAlHWx2VYwkoCsg+HRcO+UGgH76zY=; b=TvkUn9tM3/nb7iDhMY9xITWtGalVuMQ85zMKur/sjRrdRR+jiAM+DPDkkK2gQW5B0u0WLXaMyVXkzlKocO1cEJ6/94ax982vgJDPo/F9BOX0qgcVy4Hxoibl9IycjfQhsQ+LSmjKGsS0c6yYi6lMbRQ5moOSH0pbW+NI0Wgu2vk= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1610133837406763.6175260384565; Fri, 8 Jan 2021 11:23:57 -0800 (PST) Received: from localhost ([::1]:34956 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxMZ-0001bc-7O for importer@patchew.org; Fri, 08 Jan 2021 14:23:55 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40458) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3J674XwgKCmwgeRKYdcRQYYQVO.MYWaOWe-NOfOVXYXQXe.YbQ@flex--wuhaotsh.bounces.google.com>) id 1kxx9q-0001DA-Pm for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:47 -0500 Received: from mail-pl1-x64a.google.com ([2607:f8b0:4864:20::64a]:44643) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3J674XwgKCmwgeRKYdcRQYYQVO.MYWaOWe-NOfOVXYXQXe.YbQ@flex--wuhaotsh.bounces.google.com>) id 1kxx9f-0007MJ-Hc for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:44 -0500 Received: by mail-pl1-x64a.google.com with SMTP id ba10so6906571plb.11 for ; Fri, 08 Jan 2021 11:10:32 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=vapmRTP1KBSBnDnAlHWx2VYwkoCsg+HRcO+UGgH76zY=; b=bDjqYKNr+ALw7XegDtghmoqnTIQn7lzYqtXMXnfw4a+nXH8uPfq5jkmYnLNZk2+3vr ctm3/P+dRv5mqx+GMSPd5Gs5XEBXJTEd+erEfimPc63Ema40dFq0iYIqkvPWCDuCRdHj HuT4QiZruSGQqsAQEvg6/5rozs6X9Ha0h6j1VpbXM4iLs9T85ISnn+C6so4hWTh7zQEH U+IN1Poq6d6m3zHHHoDbjvLQFauzONoWKlKaV5EkHxRfYQnP5vyHr3HNnSCPNkVyNVnV sT2uOR2z3280/fY8kx9/EkgYEzMMTPPWN1RlLnMovsBErM5eAUjvBF+RmuDaEr/SeMN6 KzRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=vapmRTP1KBSBnDnAlHWx2VYwkoCsg+HRcO+UGgH76zY=; b=WG/V5b2MMoQq6pXujIG2nsT4PWy8/vLScXP4aNg085DeDYbNzR5+uLgP9O/mWIFD68 GwYnXd/J/wVdgN3U0hfljr4L9dfQ6l5saVNiUzVLEQtoEc0XcTFnwdSLoWgf3S+Ec/KH zhVXiSx1LM/s/F5YaSjPTv2zBRc6xOQreAEjWY+ETG1+wMej8y6AderAqR65XpU4keGw saPA4u5UGYYPKwrdj35wqBoRlaky+mSOuWsTPuy4hdNn8KvjRqDcR1W8cFZtAh0pZgTt IYp6w3LB9GafHjYRFLUiBdmWGwo7FAhgxfdhUPkGmcl98q82EzAlhCB2Bw2WVphTH5pM BusA== X-Gm-Message-State: AOAM533bSNjlxqYSG6sl/y6Z0z8DSbUzvB8FskGKwuzZICUoZr26L4wv teg8zhExwvldZkq/DvHeQFpZ3mhMrL3ZEA== X-Google-Smtp-Source: ABdhPJykdSaCBhjsIFJ51bjQb1RuLlv2xWSYvWcMHBSefjRNyoUsiDttQU1HvcX8Q+sw1hf3fa1vcUECDsoJVA== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a62:7651:0:b029:1a5:929b:1681 with SMTP id r78-20020a6276510000b02901a5929b1681mr4995262pfc.27.1610133031547; Fri, 08 Jan 2021 11:10:31 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:44 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-6-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 5/6] hw/misc: Add QTest for NPCM7XX PWM Module To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::64a; envelope-from=3J674XwgKCmwgeRKYdcRQYYQVO.MYWaOWe-NOfOVXYXQXe.YbQ@flex--wuhaotsh.bounces.google.com; helo=mail-pl1-x64a.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 =3D \ qtests_npcm7xx =3D \ ['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 0000000000..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 WIT= HOUT + * 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 25000000 + +/* 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 PIER 0x3c +#define PIIR 0x40 + +/* 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_DUTY 1000000 + +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[] =3D { + { + .irq =3D 93, + .base_addr =3D 0xf0103000 + }, + { + .irq =3D 94, + .base_addr =3D 0xf0104000 + } +}; + +static const PWM pwm_list[] =3D { + { + .cnr_offset =3D 0x0c, + .cmr_offset =3D 0x10, + .pdr_offset =3D 0x14, + .pwdr_offset =3D 0x44, + }, + { + .cnr_offset =3D 0x18, + .cmr_offset =3D 0x1c, + .pdr_offset =3D 0x20, + .pwdr_offset =3D 0x48, + }, + { + .cnr_offset =3D 0x24, + .cmr_offset =3D 0x28, + .pdr_offset =3D 0x2c, + .pwdr_offset =3D 0x4c, + }, + { + .cnr_offset =3D 0x30, + .cmr_offset =3D 0x34, + .pdr_offset =3D 0x38, + .pwdr_offset =3D 0x50, + }, +}; + +static const int ppr_base[] =3D { 0, 0, 8, 8 }; +static const int csr_base[] =3D { 0, 4, 8, 12 }; +static const int pcr_base[] =3D { 0, 8, 12, 16 }; + +static const uint32_t ppr_list[] =3D { + 0, + 1, + 10, + 100, + 255, /* Max possible value. */ +}; + +static const uint32_t csr_list[] =3D { + 0, + 1, + 2, + 3, + 4, /* Max possible value. */ +}; + +static const uint32_t cnr_list[] =3D { + 0, + 1, + 50, + 100, + 150, + 200, + 1000, + 10000, + 65535, /* Max possible value. */ +}; + +static const uint32_t cmr_list[] =3D { + 0, + 1, + 10, + 50, + 100, + 150, + 200, + 1000, + 10000, + 65535, /* Max possible value. */ +}; + +/* Returns the index of the PWM module. */ +static int pwm_module_index(const PWMModule *module) +{ + ptrdiff_t diff =3D module - pwm_module_list; + + g_assert_true(diff >=3D 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 =3D pwm - pwm_list; + + g_assert_true(diff >=3D 0 && diff < ARRAY_SIZE(pwm_list)); + + return diff; +} + +static uint64_t pwm_qom_get(QTestState *qts, const char *path, const char = *name) +{ + QDict *response; + + g_test_message("Getting properties %s from %s", name, path); + response =3D qtest_qmp(qts, "{ 'execute': 'qom-get'," + " 'arguments': { 'path': %s, 'property': %s}}", + path, name); + /* The qom set message returns successfully. */ + g_assert_true(qdict_haskey(response, "return")); + return qnum_get_uint(qobject_to(QNum, qdict_get(response, "return"))); +} + +static uint64_t pwm_get_freq(QTestState *qts, int module_index, int pwm_in= dex) +{ + char path[100]; + char name[100]; + + sprintf(path, "/machine/soc/pwm[%d]", module_index); + sprintf(name, "freq[%d]", pwm_index); + + return pwm_qom_get(qts, path, name); +} + +static uint64_t pwm_get_duty(QTestState *qts, int module_index, int pwm_in= dex) +{ + char path[100]; + char name[100]; + + sprintf(path, "/machine/soc/pwm[%d]", module_index); + sprintf(name, "duty[%d]", pwm_index); + + return pwm_qom_get(qts, path, name); +} + +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) +{ + uint64_t freq =3D REF_HZ; + uint32_t clksel =3D qtest_readl(qts, CLK_BA + CLKSEL); + uint32_t pllcon; + uint32_t clkdiv1 =3D qtest_readl(qts, CLK_BA + CLKDIV1); + uint32_t clkdiv2 =3D qtest_readl(qts, CLK_BA + CLKDIV2); + + switch (CPUCKSEL(clksel)) { + case 0: + pllcon =3D qtest_readl(qts, CLK_BA + PLLCON0); + freq =3D get_pll(pllcon); + break; + case 1: + pllcon =3D qtest_readl(qts, CLK_BA + PLLCON1); + freq =3D get_pll(pllcon); + break; + case 2: + break; + case 3: + break; + default: + g_assert_not_reached(); + } + + freq >>=3D (CLK2CKDIV(clkdiv1) + CLK4CKDIV(clkdiv1) + APB3CKDIV(clkdiv= 2)); + + return freq; +} + +static uint32_t pwm_selector(uint32_t csr) +{ + switch (csr) { + case 0: + return 2; + case 1: + return 4; + case 2: + return 8; + case 3: + return 16; + case 4: + return 1; + default: + g_assert_not_reached(); + } +} + +static uint64_t pwm_compute_freq(QTestState *qts, uint32_t ppr, uint32_t c= sr, + uint32_t cnr) +{ + return read_pclk(qts) / ((ppr + 1) * pwm_selector(csr) * (cnr + 1)); +} + +static uint64_t pwm_compute_duty(uint32_t cnr, uint32_t cmr, bool inverted) +{ + uint64_t duty; + + if (cnr =3D=3D 0) { + /* PWM is stopped. */ + duty =3D 0; + } else if (cmr >=3D cnr) { + duty =3D MAX_DUTY; + } else { + duty =3D MAX_DUTY * (cmr + 1) / (cnr + 1); + } + + if (inverted) { + duty =3D MAX_DUTY - duty; + } + + return duty; +} + +static uint32_t pwm_read(QTestState *qts, const TestData *td, unsigned off= set) +{ + return qtest_readl(qts, td->module->base_addr + offset); +} + +static void pwm_write(QTestState *qts, const TestData *td, unsigned offset, + uint32_t value) +{ + qtest_writel(qts, td->module->base_addr + offset, value); +} + +static uint32_t pwm_read_ppr(QTestState *qts, const TestData *td) +{ + return extract32(pwm_read(qts, td, PPR), ppr_base[pwm_index(td->pwm)],= 8); +} + +static void pwm_write_ppr(QTestState *qts, const TestData *td, uint32_t va= lue) +{ + pwm_write(qts, td, PPR, value << ppr_base[pwm_index(td->pwm)]); +} + +static uint32_t pwm_read_csr(QTestState *qts, const TestData *td) +{ + return extract32(pwm_read(qts, td, CSR), csr_base[pwm_index(td->pwm)],= 3); +} + +static void pwm_write_csr(QTestState *qts, const TestData *td, uint32_t va= lue) +{ + pwm_write(qts, td, CSR, value << csr_base[pwm_index(td->pwm)]); +} + +static uint32_t pwm_read_pcr(QTestState *qts, const TestData *td) +{ + return extract32(pwm_read(qts, td, PCR), pcr_base[pwm_index(td->pwm)],= 4); +} + +static void pwm_write_pcr(QTestState *qts, const TestData *td, uint32_t va= lue) +{ + pwm_write(qts, td, PCR, value << pcr_base[pwm_index(td->pwm)]); +} + +static uint32_t pwm_read_cnr(QTestState *qts, const TestData *td) +{ + return pwm_read(qts, td, td->pwm->cnr_offset); +} + +static void pwm_write_cnr(QTestState *qts, const TestData *td, uint32_t va= lue) +{ + pwm_write(qts, td, td->pwm->cnr_offset, value); +} + +static uint32_t pwm_read_cmr(QTestState *qts, const TestData *td) +{ + return pwm_read(qts, td, td->pwm->cmr_offset); +} + +static void pwm_write_cmr(QTestState *qts, const TestData *td, uint32_t va= lue) +{ + pwm_write(qts, td, td->pwm->cmr_offset, value); +} + +/* Check pwm registers can be reset to default value */ +static void test_init(gconstpointer test_data) +{ + const TestData *td =3D test_data; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + int module =3D pwm_module_index(td->module); + int pwm =3D pwm_index(td->pwm); + + g_assert_cmpuint(pwm_get_freq(qts, module, pwm), =3D=3D, 0); + g_assert_cmpuint(pwm_get_duty(qts, module, pwm), =3D=3D, 0); + + qtest_quit(qts); +} + +/* One-shot mode should not change frequency and duty cycle. */ +static void test_oneshot(gconstpointer test_data) +{ + const TestData *td =3D test_data; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + int module =3D pwm_module_index(td->module); + int pwm =3D pwm_index(td->pwm); + uint32_t ppr, csr, pcr; + int i, j; + + pcr =3D CH_EN; + for (i =3D 0; i < ARRAY_SIZE(ppr_list); ++i) { + ppr =3D ppr_list[i]; + pwm_write_ppr(qts, td, ppr); + + for (j =3D 0; j < ARRAY_SIZE(csr_list); ++j) { + csr =3D csr_list[j]; + pwm_write_csr(qts, td, csr); + pwm_write_pcr(qts, td, pcr); + + g_assert_cmpuint(pwm_read_ppr(qts, td), =3D=3D, ppr); + g_assert_cmpuint(pwm_read_csr(qts, td), =3D=3D, csr); + g_assert_cmpuint(pwm_read_pcr(qts, td), =3D=3D, pcr); + g_assert_cmpuint(pwm_get_freq(qts, module, pwm), =3D=3D, 0); + g_assert_cmpuint(pwm_get_duty(qts, module, pwm), =3D=3D, 0); + } + } + + qtest_quit(qts); +} + +/* In toggle mode, the PWM generates correct outputs. */ +static void test_toggle(gconstpointer test_data) +{ + const TestData *td =3D test_data; + QTestState *qts =3D qtest_init("-machine quanta-gsj"); + int module =3D pwm_module_index(td->module); + int pwm =3D pwm_index(td->pwm); + uint32_t ppr, csr, pcr, cnr, cmr; + int i, j, k, l; + uint64_t expected_freq, expected_duty; + + pcr =3D CH_EN | CH_MOD; + for (i =3D 0; i < ARRAY_SIZE(ppr_list); ++i) { + ppr =3D ppr_list[i]; + pwm_write_ppr(qts, td, ppr); + + for (j =3D 0; j < ARRAY_SIZE(csr_list); ++j) { + csr =3D csr_list[j]; + pwm_write_csr(qts, td, csr); + + for (k =3D 0; k < ARRAY_SIZE(cnr_list); ++k) { + cnr =3D cnr_list[k]; + pwm_write_cnr(qts, td, cnr); + + for (l =3D 0; l < ARRAY_SIZE(cmr_list); ++l) { + cmr =3D cmr_list[l]; + pwm_write_cmr(qts, td, cmr); + expected_freq =3D pwm_compute_freq(qts, ppr, csr, cnr); + expected_duty =3D pwm_compute_duty(cnr, cmr, false); + + pwm_write_pcr(qts, td, pcr); + g_assert_cmpuint(pwm_read_ppr(qts, td), =3D=3D, ppr); + g_assert_cmpuint(pwm_read_csr(qts, td), =3D=3D, csr); + g_assert_cmpuint(pwm_read_pcr(qts, td), =3D=3D, pcr); + g_assert_cmpuint(pwm_read_cnr(qts, td), =3D=3D, cnr); + g_assert_cmpuint(pwm_read_cmr(qts, td), =3D=3D, cmr); + g_assert_cmpuint(pwm_get_duty(qts, module, pwm), + =3D=3D, expected_duty); + if (expected_duty !=3D 0 && expected_duty !=3D 100) { + /* Duty cycle with 0 or 100 doesn't need frequency= . */ + g_assert_cmpuint(pwm_get_freq(qts, module, pwm), + =3D=3D, expected_freq); + } + + /* Test inverted mode */ + expected_duty =3D pwm_compute_duty(cnr, cmr, true); + pwm_write_pcr(qts, td, pcr | CH_INV); + g_assert_cmpuint(pwm_read_pcr(qts, td), =3D=3D, pcr | = CH_INV); + g_assert_cmpuint(pwm_get_duty(qts, module, pwm), + =3D=3D, expected_duty); + if (expected_duty !=3D 0 && expected_duty !=3D 100) { + /* Duty cycle with 0 or 100 doesn't need frequency= . */ + g_assert_cmpuint(pwm_get_freq(qts, module, pwm), + =3D=3D, expected_freq); + } + + } + } + } + } + + qtest_quit(qts); +} + +static void pwm_add_test(const char *name, const TestData* td, + GTestDataFunc fn) +{ + g_autofree char *full_name =3D g_strdup_printf( + "npcm7xx_pwm/module[%d]/pwm[%d]/%s", pwm_module_index(td->modu= le), + pwm_index(td->pwm), name); + qtest_add_data_func(full_name, td, fn); +} +#define add_test(name, td) pwm_add_test(#name, td, test_##name) + +int main(int argc, char **argv) +{ + TestData test_data_list[ARRAY_SIZE(pwm_module_list) * ARRAY_SIZE(pwm_l= ist)]; + + g_test_init(&argc, &argv, NULL); + + for (int i =3D 0; i < ARRAY_SIZE(pwm_module_list); ++i) { + for (int j =3D 0; j < ARRAY_SIZE(pwm_list); ++j) { + TestData *td =3D &test_data_list[i * ARRAY_SIZE(pwm_list) + j]; + + td->module =3D &pwm_module_list[i]; + td->pwm =3D &pwm_list[j]; + + add_test(init, td); + add_test(oneshot, td); + add_test(toggle, td); + } + } + + return g_test_run(); +} --=20 2.29.2.729.g45daf8777d-goog From nobody Fri May 17 08:24:59 2024 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=nongnu.org ARC-Seal: i=1; a=rsa-sha256; t=1610133554; cv=none; d=zohomail.com; s=zohoarc; b=Z2gsZ5gczeAzJSOgnzp0HC9gEnoxwgGA6ao3ZTk/HZkCWlvDIIXlpWeMhs1Sk0KLJIhoKmjMaSsvkzO3Qr08+jYSaoN1qAqtOh1qzzQ2jZH1Dx2n8OXMM8p0f2oeg+u7wkJXUjKcOTjw/ts1CHpHHT51CoNwl7URPvprbEqzW9A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1610133554; h=Content-Type:Cc:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:References:Sender:Subject:To; bh=hNm0ut2O+1mIOJ9J+86PhKTX0lwSI4vyPoH9VqTzsXI=; b=nthtLiABftLkKAmytzwa0dQZEiFgmPMwt8feBsf7RYc8jxNXz1yrvtH2SxyDSYmxMnwAU9l4M7MiPgCrYQAzvhC/ZUuZu4QlMMGkmDZ6iKcfgDzNIYtx3zEGgJxMSCkj10RABBTlLuxVsOl+APOAjRMonGAxMcPe4tGSNsrou+c= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1610133554769381.4828724131879; Fri, 8 Jan 2021 11:19:14 -0800 (PST) Received: from localhost ([::1]:56358 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1kxxI1-0006Nw-Ir for importer@patchew.org; Fri, 08 Jan 2021 14:19:13 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]:40388) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from <3Ka74XwgKCm4igTMafeTSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--wuhaotsh.bounces.google.com>) id 1kxx9n-0001Bq-0s for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:44 -0500 Received: from mail-pf1-x449.google.com ([2607:f8b0:4864:20::449]:50949) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from <3Ka74XwgKCm4igTMafeTSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--wuhaotsh.bounces.google.com>) id 1kxx9f-0007MT-H4 for qemu-devel@nongnu.org; Fri, 08 Jan 2021 14:10:42 -0500 Received: by mail-pf1-x449.google.com with SMTP id l17so7231665pff.17 for ; Fri, 08 Jan 2021 11:10:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=sender:date:in-reply-to:message-id:mime-version:references:subject :from:to:cc; bh=hNm0ut2O+1mIOJ9J+86PhKTX0lwSI4vyPoH9VqTzsXI=; b=VfBXqzECNGvBewlj3It9ry2PgO5f0nywYkp74DWkdaQtCaptakXmNFarrXlSu3TaCL J3mCIqNUT4/1sU4Ppm3+kopykDmEtn2UkE6sDLujaDEJCtRI3sr6ouxNvExVHpCpB1EK 3WMNBoLVj2afTYTTjFzAIr/Rj4BTI7Z8KabH5i6syzCiFS50Q1hu4d11RRn898N9bxRJ XFY9I7hCV5AEPax3RC7yH5qW1pG4K81Lfs6DdQl9F4UoVu7ECwkwaXAX3X1OmVh/7ZTF tgQ8BnSByfYHVWTIURpw9LGzteZqul35vxkvdbuEZxOoMRxJNIi7XrkEcwm0wzev04Ur VjRQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=hNm0ut2O+1mIOJ9J+86PhKTX0lwSI4vyPoH9VqTzsXI=; b=f3K2X06G3fhLK1ve5VBrrrIpYvY3LldYF7MdxoLnHPesIuhgIfD+NiMk9TYLtIIWmW wJo/jv3nbz5cd7Up4tkIr/Mu6qhWFKq7OtZWPK+0kYiDAJa1hX3uprtuH02mXd/iusOb QvMJexNsavlQEdlDJCO7uEWQ5Cm3+lrx3W7Tp2S0XItL275Fye8/hxr16xvAvc8TLa2u Idht2tOmfOl6EIzDbGpbf/KoEXLO0OspPi+qeF72pYlH7o48xjheLVOfUeVQtu6gUq/e xBH5TsOcaflyDNjxLRNbqZz1LGE+FxwxfUqBFr8o0aV3zEokQjP065dc8pOrB6Nk2X+O VEPg== X-Gm-Message-State: AOAM530YDOmwliTq8YMvByKOzJ/C8mFkMavfk5KyIIwhUtyDxj9Q/FdQ FyXMYcNcUDH/R1hPtXX2iVB3zYnpYSVxWg== X-Google-Smtp-Source: ABdhPJy91Atode/M5C4++yLdoH978miwg6ED/2QXrMUSV43RQMLBYskQIFvTtom+zfkwWrIsMZF6Nqt/ZnWxHA== X-Received: from mimik.c.googlers.com ([fda3:e722:ac3:10:7f:e700:c0a8:4e]) (user=wuhaotsh job=sendgmr) by 2002:a17:90a:a485:: with SMTP id z5mr5279205pjp.160.1610133033191; Fri, 08 Jan 2021 11:10:33 -0800 (PST) Date: Fri, 8 Jan 2021 11:09:45 -0800 In-Reply-To: <20210108190945.949196-1-wuhaotsh@google.com> Message-Id: <20210108190945.949196-7-wuhaotsh@google.com> Mime-Version: 1.0 References: <20210108190945.949196-1-wuhaotsh@google.com> X-Mailer: git-send-email 2.29.2.729.g45daf8777d-goog Subject: [PATCH v5 6/6] hw/*: Use type casting for SysBusDevice in NPCM7XX To: peter.maydell@linaro.org Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::449; envelope-from=3Ka74XwgKCm4igTMafeTSaaSXQ.OaYcQYg-PQhQXZaZSZg.adS@flex--wuhaotsh.bounces.google.com; helo=mail-pf1-x449.google.com X-Spam_score_int: -99 X-Spam_score: -10.0 X-Spam_bar: ---------- X-Spam_report: (-10.0 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_MED=-0.386, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, USER_IN_DEF_DKIM_WL=-7.5 autolearn=unavailable autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: minyard@acm.org, venture@google.com, qemu-devel@nongnu.org, hskinnemoen@google.com, wuhaotsh@google.com, kfting@nuvoton.com, qemu-arm@nongnu.org, Avi.Fishman@nuvoton.com, f4bug@amsat.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" Reply-to: Hao Wu From: Hao Wu via X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 *mac= hine, uint32_t hw_straps) { NPCM7xxMachineClass *nmc =3D NPCM7XX_MACHINE_GET_CLASS(machine); - MachineClass *mc =3D &nmc->parent; + MachineClass *mc =3D MACHINE_CLASS(nmc); Object *obj; =20 if (strcmp(machine->cpu_type, mc->default_cpu_type) !=3D 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) =20 memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs", NPCM7XX_MC_REGS_SIZE); - sysbus_init_mmio(&s->parent, &s->mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); } =20 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) =20 memory_region_init_io(&s->iomem, obj, &npcm7xx_clk_ops, s, TYPE_NPCM7XX_CLK, 4 * KiB); - sysbus_init_mmio(&s->parent, &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } =20 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) =20 memory_region_init_io(&s->iomem, obj, &npcm7xx_gcr_ops, s, TYPE_NPCM7XX_GCR, 4 * KiB); - sysbus_init_mmio(&s->parent, &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } =20 static const VMStateDescription vmstate_npcm7xx_gcr =3D { 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) =20 memory_region_init_io(&s->iomem, obj, &npcm7xx_rng_ops, s, "regs", NPCM7XX_RNG_REGS_SIZE); - sysbus_init_mmio(&s->parent, &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } =20 static const VMStateDescription vmstate_npcm7xx_rng =3D { 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 =3D NPCM7XX_OTP_GET_CLASS(dev); NPCM7xxOTPState *s =3D NPCM7XX_OTP(dev); - SysBusDevice *sbd =3D &s->parent; + SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); =20 memset(s->array, 0, sizeof(s->array)); =20 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 =3D NPCM7XX_FIU(dev); - SysBusDevice *sbd =3D &s->parent; + SysBusDevice *sbd =3D SYS_BUS_DEVICE(dev); int i; =20 if (s->cs_count <=3D 0) { --=20 2.29.2.729.g45daf8777d-goog