[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CAGb2v65aMMVu8W1PW+6NZM+YM72YFV9_FWVWtj6QHJ1CgetEsA@mail.gmail.com>
Date: Sun, 25 Jan 2026 12:32:00 +0800
From: Chen-Yu Tsai <wens@...nel.org>
To: Junhui Liu <junhui.liu@...moral.tech>
Cc: Michael Turquette <mturquette@...libre.com>, Stephen Boyd <sboyd@...nel.org>,
Jernej Skrabec <jernej.skrabec@...il.com>, Samuel Holland <samuel@...lland.org>,
Alexandre Belloni <alexandre.belloni@...tlin.com>, Rob Herring <robh@...nel.org>,
Krzysztof Kozlowski <krzk+dt@...nel.org>, Conor Dooley <conor+dt@...nel.org>,
Maxime Ripard <mripard@...nel.org>, linux-clk@...r.kernel.org,
linux-arm-kernel@...ts.infradead.org, linux-sunxi@...ts.linux.dev,
linux-kernel@...r.kernel.org, linux-rtc@...r.kernel.org,
devicetree@...r.kernel.org
Subject: Re: [PATCH 4/7] clk: sunxi-ng: Extract common RTC CCU clock logic
On Wed, Jan 21, 2026 at 7:04 PM Junhui Liu <junhui.liu@...moral.tech> wrote:
>
> Extract the IOSC and 32k clock logic from ccu-sun6i-rtc into a shared
> module to simplify adding RTC CCU support for new SoCs. This is needed
> because newer Allwinner SoCs introduce additional DCXO/HOSC logic that
> prevents direct reuse of the existing driver.
>
> Signed-off-by: Junhui Liu <junhui.liu@...moral.tech>
> ---
> drivers/clk/sunxi-ng/Makefile | 3 +
> drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 152 +----------------------------------
> drivers/clk/sunxi-ng/ccu_rtc.c | 136 +++++++++++++++++++++++++++++++
> drivers/clk/sunxi-ng/ccu_rtc.h | 37 +++++++++
> 4 files changed, 177 insertions(+), 151 deletions(-)
>
> diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile
> index a1c4087d7241..c3f810a025a8 100644
> --- a/drivers/clk/sunxi-ng/Makefile
> +++ b/drivers/clk/sunxi-ng/Makefile
> @@ -23,6 +23,9 @@ sunxi-ccu-y += ccu_nkmp.o
> sunxi-ccu-y += ccu_nm.o
> sunxi-ccu-y += ccu_mp.o
>
> +# RTC clocks
> +sunxi-ccu-y += ccu_rtc.o
> +
> # SoC support
> obj-$(CONFIG_SUNIV_F1C100S_CCU) += suniv-f1c100s-ccu.o
> obj-$(CONFIG_SUN20I_D1_CCU) += sun20i-d1-ccu.o
> diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> index 6f888169412c..562ba752bcec 100644
> --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c
> @@ -14,37 +14,12 @@
>
> #include "ccu_common.h"
>
> -#include "ccu_div.h"
> #include "ccu_gate.h"
> #include "ccu_mux.h"
> +#include "ccu_rtc.h"
>
> #include "ccu-sun6i-rtc.h"
>
> -#define IOSC_ACCURACY 300000000 /* 30% */
> -#define IOSC_RATE 16000000
> -
> -#define LOSC_RATE 32768
> -#define LOSC_RATE_SHIFT 15
> -
> -#define LOSC_CTRL_REG 0x0
> -#define LOSC_CTRL_KEY 0x16aa0000
> -
> -#define IOSC_32K_CLK_DIV_REG 0x8
> -#define IOSC_32K_CLK_DIV GENMASK(4, 0)
> -#define IOSC_32K_PRE_DIV 32
> -
> -#define IOSC_CLK_CALI_REG 0xc
> -#define IOSC_CLK_CALI_DIV_ONES 22
> -#define IOSC_CLK_CALI_EN BIT(1)
> -#define IOSC_CLK_CALI_SRC_SEL BIT(0)
> -
> -#define LOSC_OUT_GATING_REG 0x60
> -
> -#define DCXO_CTRL_REG 0x160
> -#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
> -
> -#define SUN6I_RTC_AUX_ID(_name) "rtc_sun6i." #_name
> -
> struct sun6i_rtc_match_data {
> bool have_ext_osc32k : 1;
> bool have_iosc_calibration : 1;
> @@ -53,137 +28,12 @@ struct sun6i_rtc_match_data {
> u8 osc32k_fanout_nparents;
> };
>
> -static int ccu_iosc_enable(struct clk_hw *hw)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> - return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static void ccu_iosc_disable(struct clk_hw *hw)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> - return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static int ccu_iosc_is_enabled(struct clk_hw *hw)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> - return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
> -}
> -
> -static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> -
> - if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> - u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> - /*
> - * Recover the IOSC frequency by shifting the ones place of
> - * (fixed-point divider * 32768) into bit zero.
> - */
> - if (reg & IOSC_CLK_CALI_EN)
> - return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
> - }
> -
> - return IOSC_RATE;
> -}
> -
> -static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
> - unsigned long parent_accuracy)
> -{
> - return IOSC_ACCURACY;
> -}
> -
> -static const struct clk_ops ccu_iosc_ops = {
> - .enable = ccu_iosc_enable,
> - .disable = ccu_iosc_disable,
> - .is_enabled = ccu_iosc_is_enabled,
> - .recalc_rate = ccu_iosc_recalc_rate,
> - .recalc_accuracy = ccu_iosc_recalc_accuracy,
> -};
> -
> static struct ccu_common iosc_clk = {
> .reg = DCXO_CTRL_REG,
> .hw.init = CLK_HW_INIT_NO_PARENT("iosc", &ccu_iosc_ops,
> CLK_GET_RATE_NOCACHE),
> };
>
> -static int ccu_iosc_32k_prepare(struct clk_hw *hw)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> - u32 val;
> -
> - if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> - return 0;
> -
> - val = readl(cm->base + IOSC_CLK_CALI_REG);
> - writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
> - cm->base + IOSC_CLK_CALI_REG);
> -
> - return 0;
> -}
> -
> -static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> - u32 val;
> -
> - if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> - return;
> -
> - val = readl(cm->base + IOSC_CLK_CALI_REG);
> - writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
> - cm->base + IOSC_CLK_CALI_REG);
> -}
> -
> -static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
> - unsigned long parent_rate)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> - u32 val;
> -
> - if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> - val = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> - /* Assume the calibrated 32k clock is accurate. */
> - if (val & IOSC_CLK_CALI_SRC_SEL)
> - return LOSC_RATE;
> - }
> -
> - val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
> -
> - return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
> -}
> -
> -static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
> - unsigned long parent_accuracy)
> -{
> - struct ccu_common *cm = hw_to_ccu_common(hw);
> - u32 val;
> -
> - if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> - val = readl(cm->base + IOSC_CLK_CALI_REG);
> -
> - /* Assume the calibrated 32k clock is accurate. */
> - if (val & IOSC_CLK_CALI_SRC_SEL)
> - return 0;
> - }
> -
> - return parent_accuracy;
> -}
> -
> -static const struct clk_ops ccu_iosc_32k_ops = {
> - .prepare = ccu_iosc_32k_prepare,
> - .unprepare = ccu_iosc_32k_unprepare,
> - .recalc_rate = ccu_iosc_32k_recalc_rate,
> - .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
> -};
> -
> static struct ccu_common iosc_32k_clk = {
> .hw.init = CLK_HW_INIT_HW("iosc-32k", &iosc_clk.hw,
> &ccu_iosc_32k_ops,
> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.c b/drivers/clk/sunxi-ng/ccu_rtc.c
> new file mode 100644
> index 000000000000..cfc10218517c
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.c
> @@ -0,0 +1,136 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) 2021 Samuel Holland <samuel@...lland.org>
> + */
> +
> +#include <linux/clk-provider.h>
> +#include <linux/io.h>
> +
> +#include "ccu_common.h"
> +
> +#include "ccu_gate.h"
> +#include "ccu_rtc.h"
> +
> +static int ccu_iosc_enable(struct clk_hw *hw)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> + return ccu_gate_helper_enable(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static void ccu_iosc_disable(struct clk_hw *hw)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> + return ccu_gate_helper_disable(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static int ccu_iosc_is_enabled(struct clk_hw *hw)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> + return ccu_gate_helper_is_enabled(cm, DCXO_CTRL_CLK16M_RC_EN);
> +}
> +
> +static unsigned long ccu_iosc_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> +
> + if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> + u32 reg = readl(cm->base + IOSC_CLK_CALI_REG);
> + /*
> + * Recover the IOSC frequency by shifting the ones place of
> + * (fixed-point divider * 32768) into bit zero.
> + */
> + if (reg & IOSC_CLK_CALI_EN)
> + return reg >> (IOSC_CLK_CALI_DIV_ONES - LOSC_RATE_SHIFT);
> + }
> +
> + return IOSC_RATE;
> +}
> +
> +static unsigned long ccu_iosc_recalc_accuracy(struct clk_hw *hw,
> + unsigned long parent_accuracy)
> +{
> + return IOSC_ACCURACY;
> +}
> +
> +const struct clk_ops ccu_iosc_ops = {
> + .enable = ccu_iosc_enable,
> + .disable = ccu_iosc_disable,
> + .is_enabled = ccu_iosc_is_enabled,
> + .recalc_rate = ccu_iosc_recalc_rate,
> + .recalc_accuracy = ccu_iosc_recalc_accuracy,
> +};
You need to export the symbol.
> +
> +static int ccu_iosc_32k_prepare(struct clk_hw *hw)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> + u32 val;
> +
> + if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> + return 0;
> +
> + val = readl(cm->base + IOSC_CLK_CALI_REG);
> + writel(val | IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL,
> + cm->base + IOSC_CLK_CALI_REG);
> +
> + return 0;
> +}
> +
> +static void ccu_iosc_32k_unprepare(struct clk_hw *hw)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> + u32 val;
> +
> + if (!(cm->features & CCU_FEATURE_IOSC_CALIBRATION))
> + return;
> +
> + val = readl(cm->base + IOSC_CLK_CALI_REG);
> + writel(val & ~(IOSC_CLK_CALI_EN | IOSC_CLK_CALI_SRC_SEL),
> + cm->base + IOSC_CLK_CALI_REG);
> +}
> +
> +static unsigned long ccu_iosc_32k_recalc_rate(struct clk_hw *hw,
> + unsigned long parent_rate)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> + u32 val;
> +
> + if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> + val = readl(cm->base + IOSC_CLK_CALI_REG);
> +
> + /* Assume the calibrated 32k clock is accurate. */
> + if (val & IOSC_CLK_CALI_SRC_SEL)
> + return LOSC_RATE;
> + }
> +
> + val = readl(cm->base + IOSC_32K_CLK_DIV_REG) & IOSC_32K_CLK_DIV;
> +
> + return parent_rate / IOSC_32K_PRE_DIV / (val + 1);
> +}
> +
> +static unsigned long ccu_iosc_32k_recalc_accuracy(struct clk_hw *hw,
> + unsigned long parent_accuracy)
> +{
> + struct ccu_common *cm = hw_to_ccu_common(hw);
> + u32 val;
> +
> + if (cm->features & CCU_FEATURE_IOSC_CALIBRATION) {
> + val = readl(cm->base + IOSC_CLK_CALI_REG);
> +
> + /* Assume the calibrated 32k clock is accurate. */
> + if (val & IOSC_CLK_CALI_SRC_SEL)
> + return 0;
> + }
> +
> + return parent_accuracy;
> +}
> +
> +const struct clk_ops ccu_iosc_32k_ops = {
> + .prepare = ccu_iosc_32k_prepare,
> + .unprepare = ccu_iosc_32k_unprepare,
> + .recalc_rate = ccu_iosc_32k_recalc_rate,
> + .recalc_accuracy = ccu_iosc_32k_recalc_accuracy,
> +};
Same here.
> diff --git a/drivers/clk/sunxi-ng/ccu_rtc.h b/drivers/clk/sunxi-ng/ccu_rtc.h
> new file mode 100644
> index 000000000000..1c44c2206a25
> --- /dev/null
> +++ b/drivers/clk/sunxi-ng/ccu_rtc.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) 2021 Samuel Holland <samuel@...lland.org>
> + */
> +
> +#ifndef _CCU_RTC_H_
> +#define _CCU_RTC_H_
> +
> +#define IOSC_ACCURACY 300000000 /* 30% */
> +#define IOSC_RATE 16000000
> +
> +#define LOSC_RATE 32768
> +#define LOSC_RATE_SHIFT 15
> +
> +#define LOSC_CTRL_REG 0x0
> +#define LOSC_CTRL_KEY 0x16aa0000
> +
> +#define IOSC_32K_CLK_DIV_REG 0x8
> +#define IOSC_32K_CLK_DIV GENMASK(4, 0)
> +#define IOSC_32K_PRE_DIV 32
> +
> +#define IOSC_CLK_CALI_REG 0xc
> +#define IOSC_CLK_CALI_DIV_ONES 22
> +#define IOSC_CLK_CALI_EN BIT(1)
> +#define IOSC_CLK_CALI_SRC_SEL BIT(0)
> +
> +#define LOSC_OUT_GATING_REG 0x60
> +
> +#define DCXO_CTRL_REG 0x160
> +#define DCXO_CTRL_CLK16M_RC_EN BIT(0)
Please keep all internals in the .c file.
ChenYu
> +
> +#define SUN6I_RTC_AUX_ID(_name) "rtc_sun6i." #_name
> +
> +extern const struct clk_ops ccu_iosc_ops;
> +extern const struct clk_ops ccu_iosc_32k_ops;
> +
> +#endif /* _CCU_RTC_H_ */
>
> --
> 2.52.0
>
>
Powered by blists - more mailing lists