Re: [PATCH v2 4/8] clk: sunxi-ng: a733: Add PLL clocks support
Chen-Yu Tsai <[email protected]>
| Newsgroups | org.kernel.vger.linux-clk,dev.linux.lists.linux-sunxi,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <CAGb2v65KKifCUYUxnZ5Vm=XNqNdiNC1i+U9mgX2djtPkwvtN2g@mail.gmail.com> |
(trimmed recipient list a bit) On Sat, Jul 11, 2026 at 4:12 PM Junhui Liu <[email protected]> wrote: > > Add PLL clock support for the main CCU of the Allwinner A733 SoC. The > structure is mostly similar to the sun55i, with the addition of a > PLL_REF clock that normalizes the hardware-detected DCXO/hosc frequency > (19.2MHz, 24MHz, or 26MHz) into a consistent 24MHz reference for all > subsequent PLLs. > > The behaviors of PLL_AUDIO0 and PLL_AUDIO1 are ported from the vendor > driver. Specifically, PLL_AUDIO0 is configured with SDM parameters to > provide a 22.5792MHz * 4 output, while PLL_AUDIO1 is integrated into > the main CCU without using SDM. > > Tested-by: Jerome Brunet <[email protected]> > Signed-off-by: Junhui Liu <[email protected]> > --- > drivers/clk/sunxi-ng/Kconfig | 5 + > drivers/clk/sunxi-ng/Makefile | 2 + > drivers/clk/sunxi-ng/ccu-sun60i-a733.c | 539 +++++++++++++++++++++++++++++++++ > 3 files changed, 546 insertions(+) > > diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig > index 202e793dc754..cffa83056934 100644 > --- a/drivers/clk/sunxi-ng/Kconfig > +++ b/drivers/clk/sunxi-ng/Kconfig > @@ -67,6 +67,11 @@ config SUN55I_A523_R_CCU > default ARCH_SUNXI > depends on ARM64 || COMPILE_TEST > > +config SUN60I_A733_CCU > + tristate "Support for the Allwinner A733 CCU" > + default ARCH_SUNXI > + depends on ARM64 || COMPILE_TEST > + > config SUN60I_A733_R_CCU > tristate "Support for the Allwinner A733 PRCM CCU" > default ARCH_SUNXI > diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile > index d3702bdb7a23..3a39eb9287da 100644 > --- a/drivers/clk/sunxi-ng/Makefile > +++ b/drivers/clk/sunxi-ng/Makefile > @@ -36,6 +36,7 @@ obj-$(CONFIG_SUN50I_H616_CCU) += sun50i-h616-ccu.o > obj-$(CONFIG_SUN55I_A523_CCU) += sun55i-a523-ccu.o > obj-$(CONFIG_SUN55I_A523_MCU_CCU) += sun55i-a523-mcu-ccu.o > obj-$(CONFIG_SUN55I_A523_R_CCU) += sun55i-a523-r-ccu.o > +obj-$(CONFIG_SUN60I_A733_CCU) += sun60i-a733-ccu.o > obj-$(CONFIG_SUN60I_A733_R_CCU) += sun60i-a733-r-ccu.o > obj-$(CONFIG_SUN4I_A10_CCU) += sun4i-a10-ccu.o > obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o > @@ -65,6 +66,7 @@ sun50i-h616-ccu-y += ccu-sun50i-h616.o > sun55i-a523-ccu-y += ccu-sun55i-a523.o > sun55i-a523-mcu-ccu-y += ccu-sun55i-a523-mcu.o > sun55i-a523-r-ccu-y += ccu-sun55i-a523-r.o > +sun60i-a733-ccu-y += ccu-sun60i-a733.o > sun60i-a733-r-ccu-y += ccu-sun60i-a733-r.o > sun4i-a10-ccu-y += ccu-sun4i-a10.o > sun5i-ccu-y += ccu-sun5i.o > diff --git a/drivers/clk/sunxi-ng/ccu-sun60i-a733.c b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c > new file mode 100644 > index 000000000000..f80db6ab1a98 > --- /dev/null > +++ b/drivers/clk/sunxi-ng/ccu-sun60i-a733.c > @@ -0,0 +1,539 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Copyright (C) 2023 [email protected] > + * Copyright (C) 2026 Junhui Liu <[email protected]> > + * Based on the A523 CCU driver: > + * Copyright (C) 2023-2024 Arm Ltd. > + */ > + > +#include <linux/clk-provider.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/platform_device.h> > + > +#include <dt-bindings/clock/sun60i-a733-ccu.h> > +#include <dt-bindings/reset/sun60i-a733-ccu.h> > + > +#include "../clk.h" What do you need this for? > + > +#include "ccu_common.h" > + > +#include "ccu_div.h" > +#include "ccu_mult.h" > +#include "ccu_nkmp.h" > +#include "ccu_nm.h" > + > +/* > + * The DCXO oscillator, the root of most of the clock tree, which may be > + * 19.2MHz, 24MHz, or 26MHz. > + */ > +static const struct clk_parent_data hosc[] = { > + { .fw_name = "hosc" } > +}; > + > +/************************************************************************** > + * PLLs * > + **************************************************************************/ > + > +/* > + * Undocumented, taken from the vendor kernel. > + * PLL_REF normalizes the DCXO frequency to a 24MHz reference for downstream > + * PLLs. > + */ > +#define SUN60I_A733_PLL_REF_REG 0x000 > +static struct ccu_nkmp pll_ref_clk = { > + .enable = BIT(27), > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT(8, 8), > + .m = _SUNXI_CCU_DIV(16, 7), /* output divider */ > + .p = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_REF_REG, > + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-ref", hosc, > + &ccu_nkmp_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +/* > + * Most clock-defining macros expect an *array* of parent clocks, even if > + * they do not contain a muxer to select between different parents. > + * The macros ending in just _HW take a simple clock pointer, but then create > + * a single-entry array out of that. The macros using _HWS take such an > + * array (even when it is a single entry one), this avoids having those > + * helper arrays created inside *every* clock definition. > + * This means for every clock that is referenced more than once it is > + * useful to create such a dummy array and use _HWS. > + */ > +static const struct clk_hw *pll_ref_hws[] = { > + &pll_ref_clk.common.hw > +}; > + > +#define SUN60I_A733_PLL_DDR_REG 0x020 > +static struct ccu_nkmp pll_ddr_clk = { > + .enable = BIT(27), > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(20, 3), /* output divider */ > + .p = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_DDR_REG, > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-ddr", pll_ref_hws, > + &ccu_nkmp_ops, > + CLK_SET_RATE_GATE | > + CLK_IS_CRITICAL), > + }, > +}; > + > +/* > + * There is no actual clock output with that frequency (2.4 GHz), instead it > + * has multiple outputs with adjustable dividers from that base frequency. > + * Model them separately as divider clocks based on that parent here. > + */ > +#define SUN60I_A733_PLL_PERIPH0_REG 0x0a0 > +static struct ccu_nm pll_periph0_4x_clk = { > + .enable = BIT(25) | BIT(26) | BIT(27), The gate helper sort of expects this to be just one bit. And the three bits here are for the three individual outputs (2x, 800M, 480M). I think you should move the bits to the individual outputs, and use BIT(31) here instead (or just leave out the enable bit for this one). > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_PERIPH0_REG, > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-periph0-4x", > + pll_ref_hws, &ccu_nm_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +static const struct clk_hw *pll_periph0_4x_hws[] = { > + &pll_periph0_4x_clk.common.hw > +}; > + > +static SUNXI_CCU_M_HWS(pll_periph0_2x_clk, "pll-periph0-2x", pll_periph0_4x_hws, > + SUN60I_A733_PLL_PERIPH0_REG, 20, 3, 0); > +static const struct clk_hw *pll_periph0_2x_hws[] = { > + &pll_periph0_2x_clk.common.hw > +}; > +static SUNXI_CCU_M_HWS(pll_periph0_800M_clk, "pll-periph0-800M", pll_periph0_4x_hws, > + SUN60I_A733_PLL_PERIPH0_REG, 16, 3, 0); > +static SUNXI_CCU_M_HWS(pll_periph0_480M_clk, "pll-periph0-480M", pll_periph0_4x_hws, > + SUN60I_A733_PLL_PERIPH0_REG, 2, 3, 0); > +static const struct clk_hw *pll_periph0_480M_hws[] = { > + &pll_periph0_480M_clk.common.hw > +}; > +static CLK_FIXED_FACTOR_HWS(pll_periph0_600M_clk, "pll-periph0-600M", > + pll_periph0_2x_hws, 2, 1, 0); > +static CLK_FIXED_FACTOR_HWS(pll_periph0_400M_clk, "pll-periph0-400M", > + pll_periph0_2x_hws, 3, 1, 0); > +static CLK_FIXED_FACTOR_HWS(pll_periph0_300M_clk, "pll-periph0-300M", > + pll_periph0_2x_hws, 4, 1, 0); Technically this is divided from the 600M one. > +static CLK_FIXED_FACTOR_HWS(pll_periph0_200M_clk, "pll-periph0-200M", > + pll_periph0_2x_hws, 6, 1, 0); This one from the 400M one. > +static CLK_FIXED_FACTOR_HWS(pll_periph0_150M_clk, "pll-periph0-150M", > + pll_periph0_2x_hws, 8, 1, 0); This one from the 300M one. The same comments apply to PLL periph1 as well. [...] > +#define SUN60I_A733_PLL_VIDEO0_REG 0x120 > +static struct ccu_nm pll_video0_12x_clk = { > + .enable = BIT(26) | BIT(27), Same thing as peri0. The gates are for the individual outputs. They should be modeled as such. Also applies to the other video PLLs. [...] > +/* > + * PLL_AUDIO0 has a m1 divider in addition to the usual N, M factors. > + * Since we only need some fixed frequency from this PLL (22.5792MHz x 4), > + * ignore the divider and force it to 1 (encoded as 0), in the probe function > + * below. > + * The M factor must be an even number to produce a 50% duty cycle output. > + */ > +#define SUN60I_A733_PLL_AUDIO0_REG 0x260 > +static struct ccu_sdm_setting pll_audio0_sdm_table[] = { > + { .rate = 90316800, .pattern = 0xa002872b, .m = 20, .n = 75 }, /* 22.5792 * 4 */ > +}; > + > +static struct ccu_nm pll_audio0_4x_clk = { > + .enable = BIT(27), > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(16, 7), > + .sdm = _SUNXI_CCU_SDM_DUAL_PAT(pll_audio0_sdm_table, 0, > + 0x268, BIT(31), > + 0x26c, BIT(27) | BIT(31)), Ideally we should call this the "new fractional" mode. But that's probably a different subject. > + .common = { > + .reg = SUN60I_A733_PLL_AUDIO0_REG, > + .features = CCU_FEATURE_SIGMA_DELTA_MOD, > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-audio0-4x", pll_ref_hws, > + &ccu_nm_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +#define SUN60I_A733_PLL_AUDIO1_REG 0x280 > +static struct ccu_nm pll_audio1_clk = { > + .enable = BIT(27), > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_AUDIO1_REG, You should still fill in the CCU_FEATURE_SIGMA_DELTA_MOD flag and .sdm field. Even though you aren't using it here, it could have been left on somehow, and if the kernel doesn't know about it, it can't turn it off, and then the clock coming out of it won't be correct. > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-audio1", pll_ref_hws, > + &ccu_nm_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +static const struct clk_hw *pll_audio1_hws[] = { > + &pll_audio1_clk.common.hw > +}; > +static SUNXI_CCU_M_HWS(pll_audio1_div2_clk, "pll-audio1-div2", pll_audio1_hws, > + SUN60I_A733_PLL_AUDIO1_REG, 20, 3, 0); > +static SUNXI_CCU_M_HWS(pll_audio1_div5_clk, "pll-audio1-div5", pll_audio1_hws, > + SUN60I_A733_PLL_AUDIO1_REG, 16, 3, 0); Perhaps these should be made fixed? Otherwise the names don't apply correctly. > + > +#define SUN60I_A733_PLL_NPU_REG 0x2a0 > +static struct ccu_nkmp pll_npu_clk = { > + .enable = BIT(27), > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(20, 3), /* output divider */ > + .p = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_NPU_REG, > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-npu", pll_ref_hws, > + &ccu_nkmp_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +#define SUN60I_A733_PLL_DE_REG 0x2e0 > +static struct ccu_nm pll_de_12x_clk = { > + .enable = BIT(26) | BIT(27), Same as before. These are gates for the individual outputs. Please move them there. > + .lock = BIT(28), > + .n = _SUNXI_CCU_MULT_MIN(8, 8, 11), > + .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ > + .common = { > + .reg = SUN60I_A733_PLL_DE_REG, > + .hw.init = CLK_HW_INIT_PARENTS_HW("pll-de-12x", pll_ref_hws, > + &ccu_nm_ops, > + CLK_SET_RATE_GATE), > + }, > +}; > + > +static const struct clk_hw *pll_de_hws[] = { > + &pll_de_12x_clk.common.hw > +}; > +static SUNXI_CCU_M_HWS(pll_de_4x_clk, "pll-de-4x", pll_de_hws, > + SUN60I_A733_PLL_DE_REG, 20, 3, 0); > +static SUNXI_CCU_M_HWS(pll_de_3x_clk, "pll-de-3x", pll_de_hws, > + SUN60I_A733_PLL_DE_REG, 16, 3, 0); Should we make the values fixed or the divider read-only? > + > +/* > + * Contains all clocks that are controlled by a hardware register. They > + * have a (sunxi) .common member, which needs to be initialised by the common > + * sunxi CCU code, to be filled with the MMIO base address and the shared lock. > + */ > +static struct ccu_common *sun60i_a733_ccu_clks[] = { > + &pll_ref_clk.common, > + &pll_ddr_clk.common, > + &pll_periph0_4x_clk.common, > + &pll_periph0_2x_clk.common, > + &pll_periph0_800M_clk.common, > + &pll_periph0_480M_clk.common, > + &pll_periph1_4x_clk.common, > + &pll_periph1_2x_clk.common, > + &pll_periph1_800M_clk.common, > + &pll_periph1_480M_clk.common, > + &pll_gpu0_clk.common, > + &pll_video0_12x_clk.common, > + &pll_video0_4x_clk.common, > + &pll_video0_3x_clk.common, > + &pll_video1_12x_clk.common, > + &pll_video1_4x_clk.common, > + &pll_video1_3x_clk.common, > + &pll_video2_12x_clk.common, > + &pll_video2_4x_clk.common, > + &pll_video2_3x_clk.common, > + &pll_ve0_clk.common, > + &pll_ve1_clk.common, > + &pll_audio0_4x_clk.common, > + &pll_audio1_clk.common, > + &pll_audio1_div2_clk.common, > + &pll_audio1_div5_clk.common, > + &pll_npu_clk.common, > + &pll_de_12x_clk.common, > + &pll_de_4x_clk.common, > + &pll_de_3x_clk.common, > +}; > + > +static struct clk_hw_onecell_data sun60i_a733_hw_clks = { > + .hws = { > + [CLK_PLL_REF] = &pll_ref_clk.common.hw, > + [CLK_PLL_DDR] = &pll_ddr_clk.common.hw, > + [CLK_PLL_PERIPH0_4X] = &pll_periph0_4x_clk.common.hw, > + [CLK_PLL_PERIPH0_2X] = &pll_periph0_2x_clk.common.hw, > + [CLK_PLL_PERIPH0_800M] = &pll_periph0_800M_clk.common.hw, > + [CLK_PLL_PERIPH0_480M] = &pll_periph0_480M_clk.common.hw, > + [CLK_PLL_PERIPH0_600M] = &pll_periph0_600M_clk.hw, > + [CLK_PLL_PERIPH0_400M] = &pll_periph0_400M_clk.hw, > + [CLK_PLL_PERIPH0_300M] = &pll_periph0_300M_clk.hw, > + [CLK_PLL_PERIPH0_200M] = &pll_periph0_200M_clk.hw, > + [CLK_PLL_PERIPH0_160M] = &pll_periph0_160M_clk.hw, > + [CLK_PLL_PERIPH0_150M] = &pll_periph0_150M_clk.hw, > + [CLK_PLL_PERIPH1_4X] = &pll_periph1_4x_clk.common.hw, > + [CLK_PLL_PERIPH1_2X] = &pll_periph1_2x_clk.common.hw, > + [CLK_PLL_PERIPH1_800M] = &pll_periph1_800M_clk.common.hw, > + [CLK_PLL_PERIPH1_480M] = &pll_periph1_480M_clk.common.hw, > + [CLK_PLL_PERIPH1_600M] = &pll_periph1_600M_clk.hw, > + [CLK_PLL_PERIPH1_400M] = &pll_periph1_400M_clk.hw, > + [CLK_PLL_PERIPH1_300M] = &pll_periph1_300M_clk.hw, > + [CLK_PLL_PERIPH1_200M] = &pll_periph1_200M_clk.hw, > + [CLK_PLL_PERIPH1_160M] = &pll_periph1_160M_clk.hw, > + [CLK_PLL_PERIPH1_150M] = &pll_periph1_150M_clk.hw, > + [CLK_PLL_GPU0] = &pll_gpu0_clk.common.hw, > + [CLK_PLL_VIDEO0_12X] = &pll_video0_12x_clk.common.hw, > + [CLK_PLL_VIDEO0_4X] = &pll_video0_4x_clk.common.hw, > + [CLK_PLL_VIDEO0_3X] = &pll_video0_3x_clk.common.hw, > + [CLK_PLL_VIDEO1_12X] = &pll_video1_12x_clk.common.hw, > + [CLK_PLL_VIDEO1_4X] = &pll_video1_4x_clk.common.hw, > + [CLK_PLL_VIDEO1_3X] = &pll_video1_3x_clk.common.hw, > + [CLK_PLL_VIDEO2_12X] = &pll_video2_12x_clk.common.hw, > + [CLK_PLL_VIDEO2_4X] = &pll_video2_4x_clk.common.hw, > + [CLK_PLL_VIDEO2_3X] = &pll_video2_3x_clk.common.hw, > + [CLK_PLL_VE0] = &pll_ve0_clk.common.hw, > + [CLK_PLL_VE1] = &pll_ve1_clk.common.hw, > + [CLK_PLL_AUDIO0_4X] = &pll_audio0_4x_clk.common.hw, > + [CLK_PLL_AUDIO1] = &pll_audio1_clk.common.hw, > + [CLK_PLL_AUDIO1_DIV2] = &pll_audio1_div2_clk.common.hw, > + [CLK_PLL_AUDIO1_DIV5] = &pll_audio1_div5_clk.common.hw, > + [CLK_PLL_NPU] = &pll_npu_clk.common.hw, > + [CLK_PLL_DE_12X] = &pll_de_12x_clk.common.hw, > + [CLK_PLL_DE_4X] = &pll_de_4x_clk.common.hw, > + [CLK_PLL_DE_3X] = &pll_de_3x_clk.common.hw, > + }, > + .num = CLK_FANOUT3 + 1, > +}; > + > +static const struct sunxi_ccu_desc sun60i_a733_ccu_desc = { > + .ccu_clks = sun60i_a733_ccu_clks, > + .num_ccu_clks = ARRAY_SIZE(sun60i_a733_ccu_clks), > + > + .hw_clks = &sun60i_a733_hw_clks, > +}; > + > +static const u32 pll_regs[] = { > + SUN60I_A733_PLL_REF_REG, > + SUN60I_A733_PLL_DDR_REG, > + SUN60I_A733_PLL_PERIPH0_REG, > + SUN60I_A733_PLL_PERIPH1_REG, > + SUN60I_A733_PLL_GPU_REG, > + SUN60I_A733_PLL_VIDEO0_REG, > + SUN60I_A733_PLL_VIDEO1_REG, > + SUN60I_A733_PLL_VIDEO2_REG, > + SUN60I_A733_PLL_VE0_REG, > + SUN60I_A733_PLL_VE1_REG, > + SUN60I_A733_PLL_AUDIO0_REG, > + SUN60I_A733_PLL_AUDIO1_REG, > + SUN60I_A733_PLL_NPU_REG, > + SUN60I_A733_PLL_DE_REG, > +}; > + > +static int sun60i_a733_ccu_probe(struct platform_device *pdev) > +{ > + void __iomem *reg; > + u32 val; > + int i, ret; > + > + reg = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(reg)) > + return PTR_ERR(reg); > + > + /* > + * The PLL clock code does not model all bits, for instance it does > + * not support a separate enable and gate bit. We present the > + * gate bit(27) as the enable bit, but then have to set the > + * PLL Enable, LDO Enable, and Lock Enable bits on all PLLs here. > + */ > + for (i = 0; i < ARRAY_SIZE(pll_regs); i++) { > + val = readl(reg + pll_regs[i]); > + val |= BIT(31) | BIT(30) | BIT(29); > + writel(val, reg + pll_regs[i]); > + } > + > + /* Enforce m1 = 0 for PLL_AUDIO0 */ > + val = readl(reg + SUN60I_A733_PLL_AUDIO0_REG); > + val &= ~BIT(1); > + writel(val, reg + SUN60I_A733_PLL_AUDIO0_REG); We should also force the divider for all the divN or Nx outputs. Thanks ChenYu > + > + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun60i_a733_ccu_desc); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static const struct of_device_id sun60i_a733_ccu_ids[] = { > + { .compatible = "allwinner,sun60i-a733-ccu" }, > + { /* sentinel */ } > +}; > + > +static struct platform_driver sun60i_a733_ccu_driver = { > + .probe = sun60i_a733_ccu_probe, > + .driver = { > + .name = "sun60i-a733-ccu", > + .suppress_bind_attrs = true, > + .of_match_table = sun60i_a733_ccu_ids, > + }, > +}; > +module_platform_driver(sun60i_a733_ccu_driver); > + > +MODULE_IMPORT_NS("SUNXI_CCU"); > +MODULE_DESCRIPTION("Support for the Allwinner A733 CCU"); > +MODULE_LICENSE("GPL"); > > -- > 2.54.0 > >