Add support for the MT8196 vlpckgen clock controller, which provides
muxes and dividers for clock selection in other IP blocks.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Laura Nao <laura.nao@collabora.com>
---
drivers/clk/mediatek/Makefile | 2 +-
drivers/clk/mediatek/clk-mt8196-vlpckgen.c | 769 +++++++++++++++++++++
2 files changed, 770 insertions(+), 1 deletion(-)
create mode 100644 drivers/clk/mediatek/clk-mt8196-vlpckgen.c
diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile
index 0688d7bf4979..24683dd51783 100644
--- a/drivers/clk/mediatek/Makefile
+++ b/drivers/clk/mediatek/Makefile
@@ -161,7 +161,7 @@ obj-$(CONFIG_COMMON_CLK_MT8195_VENCSYS) += clk-mt8195-venc.o
obj-$(CONFIG_COMMON_CLK_MT8195_VPPSYS) += clk-mt8195-vpp0.o clk-mt8195-vpp1.o
obj-$(CONFIG_COMMON_CLK_MT8195_WPESYS) += clk-mt8195-wpe.o
obj-$(CONFIG_COMMON_CLK_MT8196) += clk-mt8196-apmixedsys.o clk-mt8196-topckgen.o \
- clk-mt8196-topckgen2.o
+ clk-mt8196-topckgen2.o clk-mt8196-vlpckgen.o
obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-mt8365.o
obj-$(CONFIG_COMMON_CLK_MT8365_APU) += clk-mt8365-apu.o
obj-$(CONFIG_COMMON_CLK_MT8365_CAM) += clk-mt8365-cam.o
diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c
new file mode 100644
index 000000000000..23a673dd4c5c
--- /dev/null
+++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c
@@ -0,0 +1,769 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * Guangjie Song <guangjie.song@mediatek.com>
+ * Copyright (c) 2025 Collabora Ltd.
+ * Laura Nao <laura.nao@collabora.com>
+ */
+#include <dt-bindings/clock/mediatek,mt8196-clock.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#include "clk-mtk.h"
+#include "clk-mux.h"
+#include "clk-pll.h"
+
+/* MUX SEL REG */
+#define VLP_CLK_CFG_UPDATE 0x0004
+#define VLP_CLK_CFG_UPDATE1 0x0008
+#define VLP_CLK_CFG_0 0x0010
+#define VLP_CLK_CFG_0_SET 0x0014
+#define VLP_CLK_CFG_0_CLR 0x0018
+#define VLP_CLK_CFG_1 0x0020
+#define VLP_CLK_CFG_1_SET 0x0024
+#define VLP_CLK_CFG_1_CLR 0x0028
+#define VLP_CLK_CFG_2 0x0030
+#define VLP_CLK_CFG_2_SET 0x0034
+#define VLP_CLK_CFG_2_CLR 0x0038
+#define VLP_CLK_CFG_3 0x0040
+#define VLP_CLK_CFG_3_SET 0x0044
+#define VLP_CLK_CFG_3_CLR 0x0048
+#define VLP_CLK_CFG_4 0x0050
+#define VLP_CLK_CFG_4_SET 0x0054
+#define VLP_CLK_CFG_4_CLR 0x0058
+#define VLP_CLK_CFG_5 0x0060
+#define VLP_CLK_CFG_5_SET 0x0064
+#define VLP_CLK_CFG_5_CLR 0x0068
+#define VLP_CLK_CFG_6 0x0070
+#define VLP_CLK_CFG_6_SET 0x0074
+#define VLP_CLK_CFG_6_CLR 0x0078
+#define VLP_CLK_CFG_7 0x0080
+#define VLP_CLK_CFG_7_SET 0x0084
+#define VLP_CLK_CFG_7_CLR 0x0088
+#define VLP_CLK_CFG_8 0x0090
+#define VLP_CLK_CFG_8_SET 0x0094
+#define VLP_CLK_CFG_8_CLR 0x0098
+#define VLP_CLK_CFG_9 0x00a0
+#define VLP_CLK_CFG_9_SET 0x00a4
+#define VLP_CLK_CFG_9_CLR 0x00a8
+#define VLP_CLK_CFG_10 0x00b0
+#define VLP_CLK_CFG_10_SET 0x00b4
+#define VLP_CLK_CFG_10_CLR 0x00b8
+#define VLP_OCIC_FENC_STATUS_MON_0 0x039c
+#define VLP_OCIC_FENC_STATUS_MON_1 0x03a0
+
+/* MUX SHIFT */
+#define TOP_MUX_SCP_SHIFT 0
+#define TOP_MUX_SCP_SPI_SHIFT 1
+#define TOP_MUX_SCP_IIC_SHIFT 2
+#define TOP_MUX_SCP_IIC_HS_SHIFT 3
+#define TOP_MUX_PWRAP_ULPOSC_SHIFT 4
+#define TOP_MUX_SPMI_M_TIA_32K_SHIFT 5
+#define TOP_MUX_APXGPT_26M_B_SHIFT 6
+#define TOP_MUX_DPSW_SHIFT 7
+#define TOP_MUX_DPSW_CENTRAL_SHIFT 8
+#define TOP_MUX_SPMI_M_MST_SHIFT 9
+#define TOP_MUX_DVFSRC_SHIFT 10
+#define TOP_MUX_PWM_VLP_SHIFT 11
+#define TOP_MUX_AXI_VLP_SHIFT 12
+#define TOP_MUX_SYSTIMER_26M_SHIFT 13
+#define TOP_MUX_SSPM_SHIFT 14
+#define TOP_MUX_SRCK_SHIFT 15
+#define TOP_MUX_CAMTG0_SHIFT 16
+#define TOP_MUX_CAMTG1_SHIFT 17
+#define TOP_MUX_CAMTG2_SHIFT 18
+#define TOP_MUX_CAMTG3_SHIFT 19
+#define TOP_MUX_CAMTG4_SHIFT 20
+#define TOP_MUX_CAMTG5_SHIFT 21
+#define TOP_MUX_CAMTG6_SHIFT 22
+#define TOP_MUX_CAMTG7_SHIFT 23
+#define TOP_MUX_SSPM_26M_SHIFT 25
+#define TOP_MUX_ULPOSC_SSPM_SHIFT 26
+#define TOP_MUX_VLP_PBUS_26M_SHIFT 27
+#define TOP_MUX_DEBUG_ERR_FLAG_VLP_26M_SHIFT 28
+#define TOP_MUX_DPMSRDMA_SHIFT 29
+#define TOP_MUX_VLP_PBUS_156M_SHIFT 30
+#define TOP_MUX_SPM_SHIFT 0
+#define TOP_MUX_MMINFRA_VLP_SHIFT 1
+#define TOP_MUX_USB_TOP_SHIFT 2
+#define TOP_MUX_SSUSB_XHCI_SHIFT 3
+#define TOP_MUX_NOC_VLP_SHIFT 4
+#define TOP_MUX_AUDIO_H_SHIFT 5
+#define TOP_MUX_AUD_ENGEN1_SHIFT 6
+#define TOP_MUX_AUD_ENGEN2_SHIFT 7
+#define TOP_MUX_AUD_INTBUS_SHIFT 8
+#define TOP_MUX_SPU_VLP_26M_SHIFT 9
+#define TOP_MUX_SPU0_VLP_SHIFT 10
+#define TOP_MUX_SPU1_VLP_SHIFT 11
+
+/* CKSTA REG */
+#define VLP_CKSTA_REG0 0x0250
+#define VLP_CKSTA_REG1 0x0254
+
+/* HW Voter REG */
+#define HWV_CG_9_SET 0x0048
+#define HWV_CG_9_CLR 0x004c
+#define HWV_CG_9_DONE 0x2c24
+#define HWV_CG_10_SET 0x0050
+#define HWV_CG_10_CLR 0x0054
+#define HWV_CG_10_DONE 0x2c28
+
+/* PLL REG */
+#define VLP_AP_PLL_CON3 0x264
+#define VLP_APLL1_TUNER_CON0 0x2a4
+#define VLP_APLL2_TUNER_CON0 0x2a8
+#define VLP_APLL1_CON0 0x274
+#define VLP_APLL1_CON1 0x278
+#define VLP_APLL1_CON2 0x27c
+#define VLP_APLL1_CON3 0x280
+#define VLP_APLL2_CON0 0x28c
+#define VLP_APLL2_CON1 0x290
+#define VLP_APLL2_CON2 0x294
+#define VLP_APLL2_CON3 0x298
+
+#define VLP_PLLEN_ALL 0x080
+#define VLP_PLLEN_ALL_SET 0x084
+#define VLP_PLLEN_ALL_CLR 0x088
+
+#define MT8196_PLL_FMAX (3800UL * MHZ)
+#define MT8196_PLL_FMIN (1500UL * MHZ)
+#define MT8196_INTEGER_BITS 8
+
+#define PLL_FENC(_id, _name, _reg, _fenc_sta_ofs, _fenc_sta_bit,\
+ _flags, _pd_reg, _pd_shift, \
+ _pcw_reg, _pcw_shift, _pcwbits, \
+ _pll_en_bit) { \
+ .id = _id, \
+ .name = _name, \
+ .reg = _reg, \
+ .fenc_sta_ofs = _fenc_sta_ofs, \
+ .fenc_sta_bit = _fenc_sta_bit, \
+ .flags = _flags, \
+ .fmax = MT8196_PLL_FMAX, \
+ .fmin = MT8196_PLL_FMIN, \
+ .pd_reg = _pd_reg, \
+ .pd_shift = _pd_shift, \
+ .pcw_reg = _pcw_reg, \
+ .pcw_shift = _pcw_shift, \
+ .pcwbits = _pcwbits, \
+ .pcwibits = MT8196_INTEGER_BITS, \
+ .en_reg = VLP_PLLEN_ALL, \
+ .en_set_reg = VLP_PLLEN_ALL_SET, \
+ .en_clr_reg = VLP_PLLEN_ALL_CLR, \
+ .pll_en_bit = _pll_en_bit, \
+ .ops = &mtk_pll_fenc_clr_set_ops, \
+}
+
+static DEFINE_SPINLOCK(mt8196_clk_vlp_lock);
+
+static const char * const vlp_scp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d6",
+ "mainpll_d4",
+ "mainpll_d3",
+ "vlp_apll1"
+};
+
+static const char * const vlp_scp_spi_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d2",
+ "mainpll_d5_d2"
+};
+
+static const char * const vlp_scp_iic_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_scp_iic_hs_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d4",
+ "mainpll_d7_d2",
+ "mainpll_d7"
+};
+
+static const char * const vlp_pwrap_ulposc_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_spmi_32k_parents[] = {
+ "clk26m",
+ "clk32k",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_apxgpt_26m_b_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_dpsw_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d7",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_dpsw_central_parents[] = {
+ "clk26m",
+ "osc_d10",
+ "osc_d7",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_spmi_m_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "osc_d14",
+ "osc_d10"
+};
+
+static const char * const vlp_dvfsrc_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_pwm_vlp_parents[] = {
+ "clk26m",
+ "clk32k",
+ "osc_d20",
+ "osc_d8",
+ "mainpll_d4_d8"
+};
+
+static const char * const vlp_axi_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d7_d4",
+ "osc_d4",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_systimer_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_sspm_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d5_d2",
+ "osc_d2",
+ "mainpll_d6"
+};
+
+static const char * const vlp_srck_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_camtg0_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "ulposc3",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg1_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "ulposc3",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg2_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg3_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg4_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg5_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg6_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_camtg7_parents[] = {
+ "clk26m",
+ "univpll_192m_d32",
+ "univpll_192m_d16",
+ "clk13m",
+ "osc_d40",
+ "osc_d32",
+ "univpll_192m_d10",
+ "univpll_192m_d8",
+ "univpll_d6_d16",
+ "osc_d20",
+ "ck2_tvdpll1_d16",
+ "univpll_d6_d8"
+};
+
+static const char * const vlp_sspm_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_ulposc_sspm_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d4_d2"
+};
+
+static const char * const vlp_vlp_pbus_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_debug_err_flag_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_dpmsrdma_parents[] = {
+ "clk26m",
+ "mainpll_d7_d2"
+};
+
+static const char * const vlp_vlp_pbus_156m_parents[] = {
+ "clk26m",
+ "osc_d2",
+ "mainpll_d7_d2",
+ "mainpll_d7"
+};
+
+static const char * const vlp_spm_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4"
+};
+
+static const char * const vlp_mminfra_parents[] = {
+ "clk26m",
+ "osc_d4",
+ "mainpll_d3"
+};
+
+static const char * const vlp_usb_parents[] = {
+ "clk26m",
+ "mainpll_d9"
+};
+
+static const char * const vlp_usb_xhci_parents[] = {
+ "clk26m",
+ "mainpll_d9"
+};
+
+static const char * const vlp_noc_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d9"
+};
+
+static const char * const vlp_audio_h_parents[] = {
+ "clk26m",
+ "vlp_apll1",
+ "vlp_apll2"
+};
+
+static const char * const vlp_aud_engen1_parents[] = {
+ "clk26m",
+ "apll1_d8",
+ "apll1_d4"
+};
+
+static const char * const vlp_aud_engen2_parents[] = {
+ "clk26m",
+ "apll2_d8",
+ "apll2_d4"
+};
+
+static const char * const vlp_aud_intbus_parents[] = {
+ "clk26m",
+ "mainpll_d7_d4",
+ "mainpll_d4_d4"
+};
+
+static const char * const vlp_spvlp_26m_parents[] = {
+ "clk26m",
+ "osc_d20"
+};
+
+static const char * const vlp_spu0_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const char * const vlp_spu1_vlp_parents[] = {
+ "clk26m",
+ "osc_d20",
+ "mainpll_d4_d4",
+ "mainpll_d4_d2",
+ "mainpll_d7",
+ "mainpll_d6",
+ "mainpll_d5"
+};
+
+static const struct mtk_mux vlp_muxes[] = {
+ /* VLP_CLK_CFG_0 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_SCP, "vlp_scp", vlp_scp_parents,
+ VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET, VLP_CLK_CFG_0_CLR,
+ 0, 3, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 31),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_SPI, "vlp_scp_spi",
+ vlp_scp_spi_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 8, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_SPI_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_IIC, "vlp_scp_iic",
+ vlp_scp_iic_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_IIC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SCP_IIC_HS, "vlp_scp_iic_hs",
+ vlp_scp_iic_hs_parents, VLP_CLK_CFG_0, VLP_CLK_CFG_0_SET,
+ VLP_CLK_CFG_0_CLR, 24, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SCP_IIC_HS_SHIFT),
+ /* VLP_CLK_CFG_1 */
+ MUX_CLR_SET_UPD(CLK_VLP_PWRAP_ULPOSC, "vlp_pwrap_ulposc",
+ vlp_pwrap_ulposc_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 0, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_PWRAP_ULPOSC_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPMI_M_TIA_32K, "vlp_spmi_32k",
+ vlp_spmi_32k_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 8, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SPMI_M_TIA_32K_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_APXGPT_26M_B, "vlp_apxgpt_26m_b",
+ vlp_apxgpt_26m_b_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 16, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_APXGPT_26M_B_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DPSW, "vlp_dpsw",
+ vlp_dpsw_parents, VLP_CLK_CFG_1, VLP_CLK_CFG_1_SET,
+ VLP_CLK_CFG_1_CLR, 24, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPSW_SHIFT),
+ /* VLP_CLK_CFG_2 */
+ MUX_CLR_SET_UPD(CLK_VLP_DPSW_CENTRAL, "vlp_dpsw_central",
+ vlp_dpsw_central_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 0, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPSW_CENTRAL_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPMI_M_MST, "vlp_spmi_m",
+ vlp_spmi_m_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 8, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SPMI_M_MST_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DVFSRC, "vlp_dvfsrc",
+ vlp_dvfsrc_parents, VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET,
+ VLP_CLK_CFG_2_CLR, 16, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DVFSRC_SHIFT),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_PWM_VLP, "vlp_pwm_vlp", vlp_pwm_vlp_parents,
+ VLP_CLK_CFG_2, VLP_CLK_CFG_2_SET, VLP_CLK_CFG_2_CLR,
+ 24, 3, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_PWM_VLP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 20),
+ /* VLP_CLK_CFG_3 */
+ MUX_CLR_SET_UPD(CLK_VLP_AXI_VLP, "vlp_axi_vlp",
+ vlp_axi_vlp_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 0, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_AXI_VLP_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SYSTIMER_26M, "vlp_systimer_26m",
+ vlp_systimer_26m_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SYSTIMER_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SSPM, "vlp_sspm",
+ vlp_sspm_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 16, 3,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SSPM_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SRCK, "vlp_srck",
+ vlp_srck_parents, VLP_CLK_CFG_3, VLP_CLK_CFG_3_SET,
+ VLP_CLK_CFG_3_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SRCK_SHIFT),
+ /* VLP_CLK_CFG_4 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG0, "vlp_camtg0", vlp_camtg0_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 0, 4, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG0_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 15),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG1, "vlp_camtg1", vlp_camtg1_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 8, 4, 15, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG1_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 14),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG2, "vlp_camtg2", vlp_camtg2_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 16, 4, 23, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG2_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 13),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG3, "vlp_camtg3", vlp_camtg3_parents,
+ VLP_CLK_CFG_4, VLP_CLK_CFG_4_SET, VLP_CLK_CFG_4_CLR,
+ HWV_CG_9_DONE, HWV_CG_9_SET, HWV_CG_9_CLR,
+ 24, 4, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG3_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 12),
+ /* VLP_CLK_CFG_5 */
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG4, "vlp_camtg4", vlp_camtg4_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 0, 4, 7, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG4_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 11),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG5, "vlp_camtg5", vlp_camtg5_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 8, 4, 15, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG5_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 10),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG6, "vlp_camtg6", vlp_camtg6_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 16, 4, 23, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG6_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 9),
+ MUX_GATE_HWV_FENC_CLR_SET_UPD(CLK_VLP_CAMTG7, "vlp_camtg7", vlp_camtg7_parents,
+ VLP_CLK_CFG_5, VLP_CLK_CFG_5_SET, VLP_CLK_CFG_5_CLR,
+ HWV_CG_10_DONE, HWV_CG_10_SET, HWV_CG_10_CLR,
+ 24, 4, 31, VLP_CLK_CFG_UPDATE, TOP_MUX_CAMTG7_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_0, 8),
+ /* VLP_CLK_CFG_6 */
+ MUX_CLR_SET_UPD(CLK_VLP_SSPM_26M, "vlp_sspm_26m",
+ vlp_sspm_26m_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_SSPM_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_ULPOSC_SSPM, "vlp_ulposc_sspm",
+ vlp_ulposc_sspm_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_ULPOSC_SSPM_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_VLP_PBUS_26M, "vlp_vlp_pbus_26m",
+ vlp_vlp_pbus_26m_parents, VLP_CLK_CFG_6, VLP_CLK_CFG_6_SET,
+ VLP_CLK_CFG_6_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_VLP_PBUS_26M_SHIFT),
+ /* VLP_CLK_CFG_7 */
+ MUX_CLR_SET_UPD(CLK_VLP_DEBUG_ERR_FLAG, "vlp_debug_err_flag",
+ vlp_debug_err_flag_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 0, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DEBUG_ERR_FLAG_VLP_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_DPMSRDMA, "vlp_dpmsrdma",
+ vlp_dpmsrdma_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 8, 1,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_DPMSRDMA_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_VLP_PBUS_156M, "vlp_vlp_pbus_156m",
+ vlp_vlp_pbus_156m_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 16, 2,
+ VLP_CLK_CFG_UPDATE, TOP_MUX_VLP_PBUS_156M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPM, "vlp_spm",
+ vlp_spm_parents, VLP_CLK_CFG_7, VLP_CLK_CFG_7_SET,
+ VLP_CLK_CFG_7_CLR, 24, 1,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPM_SHIFT),
+ /* VLP_CLK_CFG_8 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_MMINFRA, "vlp_mminfra", vlp_mminfra_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 0, 2, 7, VLP_CLK_CFG_UPDATE1, TOP_MUX_MMINFRA_VLP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 31),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_USB_TOP, "vlp_usb", vlp_usb_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 8, 1, 15, VLP_CLK_CFG_UPDATE1, TOP_MUX_USB_TOP_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 30),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_USB_XHCI, "vlp_usb_xhci", vlp_usb_xhci_parents,
+ VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET, VLP_CLK_CFG_8_CLR,
+ 16, 1, 23, VLP_CLK_CFG_UPDATE1, TOP_MUX_SSUSB_XHCI_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 29),
+ MUX_CLR_SET_UPD(CLK_VLP_NOC_VLP, "vlp_noc_vlp",
+ vlp_noc_vlp_parents, VLP_CLK_CFG_8, VLP_CLK_CFG_8_SET,
+ VLP_CLK_CFG_8_CLR, 24, 2,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_NOC_VLP_SHIFT),
+ /* VLP_CLK_CFG_9 */
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_AUDIO_H, "vlp_audio_h", vlp_audio_h_parents,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 0, 2, 7, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUDIO_H_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 27),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_AUD_ENGEN1, "vlp_aud_engen1", vlp_aud_engen1_parents,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 8, 2, 15, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_ENGEN1_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 26),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_AUD_ENGEN2, "vlp_aud_engen2", vlp_aud_engen2_parents,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 16, 2, 23, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_ENGEN2_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 25),
+ MUX_GATE_FENC_CLR_SET_UPD(CLK_VLP_AUD_INTBUS, "vlp_aud_intbus", vlp_aud_intbus_parents,
+ VLP_CLK_CFG_9, VLP_CLK_CFG_9_SET, VLP_CLK_CFG_9_CLR,
+ 24, 2, 31, VLP_CLK_CFG_UPDATE1, TOP_MUX_AUD_INTBUS_SHIFT,
+ VLP_OCIC_FENC_STATUS_MON_1, 24),
+ /* VLP_CLK_CFG_10 */
+ MUX_CLR_SET_UPD(CLK_VLP_SPVLP_26M, "vlp_spvlp_26m",
+ vlp_spvlp_26m_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 0, 1,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU_VLP_26M_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPU0_VLP, "vlp_spu0_vlp",
+ vlp_spu0_vlp_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 8, 3,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU0_VLP_SHIFT),
+ MUX_CLR_SET_UPD(CLK_VLP_SPU1_VLP, "vlp_spu1_vlp",
+ vlp_spu1_vlp_parents, VLP_CLK_CFG_10, VLP_CLK_CFG_10_SET,
+ VLP_CLK_CFG_10_CLR, 16, 3,
+ VLP_CLK_CFG_UPDATE1, TOP_MUX_SPU1_VLP_SHIFT),
+};
+
+static const struct mtk_pll_data vlp_plls[] = {
+ PLL_FENC(CLK_VLP_APLL1, "vlp_apll1", VLP_APLL1_CON0, 0x0358, 1, 0,
+ VLP_APLL1_CON1, 24, VLP_APLL1_CON2, 0, 32, 0),
+ PLL_FENC(CLK_VLP_APLL2, "vlp_apll2", VLP_APLL2_CON0, 0x0358, 0, 0,
+ VLP_APLL2_CON1, 24, VLP_APLL2_CON2, 0, 32, 1),
+};
+
+static int clk_mt8196_vlp_probe(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data;
+ int r;
+ struct device_node *node = pdev->dev.of_node;
+
+ clk_data = mtk_alloc_clk_data(ARRAY_SIZE(vlp_muxes) +
+ ARRAY_SIZE(vlp_plls));
+ if (!clk_data)
+ return -ENOMEM;
+
+ r = mtk_clk_register_muxes(&pdev->dev, vlp_muxes, ARRAY_SIZE(vlp_muxes),
+ node, &mt8196_clk_vlp_lock, clk_data);
+ if (r)
+ goto free_clk_data;
+
+ r = mtk_clk_register_plls(node, vlp_plls, ARRAY_SIZE(vlp_plls),
+ clk_data);
+ if (r)
+ goto unregister_muxes;
+
+ r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
+ if (r)
+ goto unregister_plls;
+
+ platform_set_drvdata(pdev, clk_data);
+
+ return r;
+
+unregister_plls:
+ mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data);
+unregister_muxes:
+ mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data);
+free_clk_data:
+ mtk_free_clk_data(clk_data);
+
+ return r;
+}
+
+static void clk_mt8196_vlp_remove(struct platform_device *pdev)
+{
+ struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev);
+ struct device_node *node = pdev->dev.of_node;
+
+ of_clk_del_provider(node);
+ mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data);
+ mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data);
+ mtk_free_clk_data(clk_data);
+}
+
+static const struct of_device_id of_match_clk_mt8196_vlp_ck[] = {
+ { .compatible = "mediatek,mt8196-vlpckgen" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vlp_ck);
+
+static struct platform_driver clk_mt8196_vlp_drv = {
+ .probe = clk_mt8196_vlp_probe,
+ .remove = clk_mt8196_vlp_remove,
+ .driver = {
+ .name = "clk-mt8196-vlpck",
+ .of_match_table = of_match_clk_mt8196_vlp_ck,
+ },
+};
+
+MODULE_DESCRIPTION("MediaTek MT8196 VLP clock generator driver");
+module_platform_driver(clk_mt8196_vlp_drv);
+MODULE_LICENSE("GPL");
--
2.39.5
Hi, On Tue, Jun 24, 2025 at 10:33 PM Laura Nao <laura.nao@collabora.com> wrote: > > Add support for the MT8196 vlpckgen clock controller, which provides > muxes and dividers for clock selection in other IP blocks. > > Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > Signed-off-by: Laura Nao <laura.nao@collabora.com> > --- > drivers/clk/mediatek/Makefile | 2 +- > drivers/clk/mediatek/clk-mt8196-vlpckgen.c | 769 +++++++++++++++++++++ > 2 files changed, 770 insertions(+), 1 deletion(-) > create mode 100644 drivers/clk/mediatek/clk-mt8196-vlpckgen.c > > diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile > index 0688d7bf4979..24683dd51783 100644 > --- a/drivers/clk/mediatek/Makefile > +++ b/drivers/clk/mediatek/Makefile > @@ -161,7 +161,7 @@ obj-$(CONFIG_COMMON_CLK_MT8195_VENCSYS) += clk-mt8195-venc.o > obj-$(CONFIG_COMMON_CLK_MT8195_VPPSYS) += clk-mt8195-vpp0.o clk-mt8195-vpp1.o > obj-$(CONFIG_COMMON_CLK_MT8195_WPESYS) += clk-mt8195-wpe.o > obj-$(CONFIG_COMMON_CLK_MT8196) += clk-mt8196-apmixedsys.o clk-mt8196-topckgen.o \ > - clk-mt8196-topckgen2.o > + clk-mt8196-topckgen2.o clk-mt8196-vlpckgen.o > obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-mt8365.o > obj-$(CONFIG_COMMON_CLK_MT8365_APU) += clk-mt8365-apu.o > obj-$(CONFIG_COMMON_CLK_MT8365_CAM) += clk-mt8365-cam.o > diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c > new file mode 100644 > index 000000000000..23a673dd4c5c > --- /dev/null > +++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c > @@ -0,0 +1,769 @@ [...] > +static const char * const vlp_camtg0_parents[] = { > + "clk26m", > + "univpll_192m_d32", > + "univpll_192m_d16", > + "clk13m", > + "osc_d40", > + "osc_d32", > + "univpll_192m_d10", > + "univpll_192m_d8", > + "univpll_d6_d16", > + "ulposc3", > + "osc_d20", > + "ck2_tvdpll1_d16", > + "univpll_d6_d8" > +}; It seems all the vlp_camtg* parents are the same. Please merge them and just have one list. > +static const char * const vlp_sspm_26m_parents[] = { > + "clk26m", > + "osc_d20" > +}; > + > +static const char * const vlp_ulposc_sspm_parents[] = { > + "clk26m", > + "osc_d2", > + "mainpll_d4_d2" > +}; > + > +static const char * const vlp_vlp_pbus_26m_parents[] = { > + "clk26m", > + "osc_d20" > +}; > + > +static const char * const vlp_debug_err_flag_parents[] = { > + "clk26m", > + "osc_d20" > +}; > + > +static const char * const vlp_dpmsrdma_parents[] = { > + "clk26m", > + "mainpll_d7_d2" > +}; > + > +static const char * const vlp_vlp_pbus_156m_parents[] = { > + "clk26m", > + "osc_d2", > + "mainpll_d7_d2", > + "mainpll_d7" > +}; > + > +static const char * const vlp_spm_parents[] = { > + "clk26m", > + "mainpll_d7_d4" > +}; > + > +static const char * const vlp_mminfra_parents[] = { > + "clk26m", > + "osc_d4", > + "mainpll_d3" > +}; > + > +static const char * const vlp_usb_parents[] = { > + "clk26m", > + "mainpll_d9" > +}; The previous and the next one are the same. > +static const char * const vlp_usb_xhci_parents[] = { > + "clk26m", > + "mainpll_d9" > +}; > + > +static const char * const vlp_noc_vlp_parents[] = { > + "clk26m", > + "osc_d20", > + "mainpll_d9" > +}; > + > +static const char * const vlp_audio_h_parents[] = { > + "clk26m", > + "vlp_apll1", > + "vlp_apll2" > +}; > + > +static const char * const vlp_aud_engen1_parents[] = { > + "clk26m", > + "apll1_d8", > + "apll1_d4" > +}; The previous and the next one are the same. > +static const char * const vlp_aud_engen2_parents[] = { > + "clk26m", > + "apll2_d8", > + "apll2_d4" > +}; > + > +static const char * const vlp_aud_intbus_parents[] = { > + "clk26m", > + "mainpll_d7_d4", > + "mainpll_d4_d4" > +}; > + > +static const char * const vlp_spvlp_26m [...] > +static int clk_mt8196_vlp_probe(struct platform_device *pdev) > +{ > + struct clk_hw_onecell_data *clk_data; > + int r; > + struct device_node *node = pdev->dev.of_node; > + > + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(vlp_muxes) + > + ARRAY_SIZE(vlp_plls)); > + if (!clk_data) > + return -ENOMEM; > + > + r = mtk_clk_register_muxes(&pdev->dev, vlp_muxes, ARRAY_SIZE(vlp_muxes), > + node, &mt8196_clk_vlp_lock, clk_data); > + if (r) > + goto free_clk_data; > + > + r = mtk_clk_register_plls(node, vlp_plls, ARRAY_SIZE(vlp_plls), > + clk_data); > + if (r) > + goto unregister_muxes; > + > + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); > + if (r) > + goto unregister_plls; > + > + platform_set_drvdata(pdev, clk_data); > + > + return r; > + > +unregister_plls: > + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); > +unregister_muxes: > + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); > +free_clk_data: > + mtk_free_clk_data(clk_data); The AFE driver sets some tuner parameters in the VLPCKGEN block at probe time. Maybe we could do that here instead? /* vlp_cksys_clk: 0x1c016000 */ #define VLP_APLL1_TUNER_CON0 0x02a4 #define VLP_APLL2_TUNER_CON0 0x02a8 /* vlp apll1 tuner default value*/ #define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d /* vlp apll2 tuner default value + 1*/ #define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265 regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, VLP_APLL1_TUNER_CON0_VALUE); regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, VLP_APLL2_TUNER_CON0_VALUE); ChenYu > + > + return r; > +} > + > +static void clk_mt8196_vlp_remove(struct platform_device *pdev) > +{ > + struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); > + struct device_node *node = pdev->dev.of_node; > + > + of_clk_del_provider(node); > + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); > + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); > + mtk_free_clk_data(clk_data); > +} > + > +static const struct of_device_id of_match_clk_mt8196_vlp_ck[] = { > + { .compatible = "mediatek,mt8196-vlpckgen" }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vlp_ck); > + > +static struct platform_driver clk_mt8196_vlp_drv = { > + .probe = clk_mt8196_vlp_probe, > + .remove = clk_mt8196_vlp_remove, > + .driver = { > + .name = "clk-mt8196-vlpck", > + .of_match_table = of_match_clk_mt8196_vlp_ck, > + }, > +}; > + > +MODULE_DESCRIPTION("MediaTek MT8196 VLP clock generator driver"); > +module_platform_driver(clk_mt8196_vlp_drv); > +MODULE_LICENSE("GPL"); > -- > 2.39.5 >
On Tue, Jul 15, 2025 at 3:28 PM Chen-Yu Tsai <wenst@chromium.org> wrote: > > Hi, > > > On Tue, Jun 24, 2025 at 10:33 PM Laura Nao <laura.nao@collabora.com> wrote: > > > > Add support for the MT8196 vlpckgen clock controller, which provides > > muxes and dividers for clock selection in other IP blocks. > > > > Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > > Signed-off-by: Laura Nao <laura.nao@collabora.com> > > --- > > drivers/clk/mediatek/Makefile | 2 +- > > drivers/clk/mediatek/clk-mt8196-vlpckgen.c | 769 +++++++++++++++++++++ > > 2 files changed, 770 insertions(+), 1 deletion(-) > > create mode 100644 drivers/clk/mediatek/clk-mt8196-vlpckgen.c > > > > diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile > > index 0688d7bf4979..24683dd51783 100644 > > --- a/drivers/clk/mediatek/Makefile > > +++ b/drivers/clk/mediatek/Makefile > > @@ -161,7 +161,7 @@ obj-$(CONFIG_COMMON_CLK_MT8195_VENCSYS) += clk-mt8195-venc.o > > obj-$(CONFIG_COMMON_CLK_MT8195_VPPSYS) += clk-mt8195-vpp0.o clk-mt8195-vpp1.o > > obj-$(CONFIG_COMMON_CLK_MT8195_WPESYS) += clk-mt8195-wpe.o > > obj-$(CONFIG_COMMON_CLK_MT8196) += clk-mt8196-apmixedsys.o clk-mt8196-topckgen.o \ > > - clk-mt8196-topckgen2.o > > + clk-mt8196-topckgen2.o clk-mt8196-vlpckgen.o > > obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-mt8365.o > > obj-$(CONFIG_COMMON_CLK_MT8365_APU) += clk-mt8365-apu.o > > obj-$(CONFIG_COMMON_CLK_MT8365_CAM) += clk-mt8365-cam.o > > diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c > > new file mode 100644 > > index 000000000000..23a673dd4c5c > > --- /dev/null > > +++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c > > @@ -0,0 +1,769 @@ > > [...] > > > +static const char * const vlp_camtg0_parents[] = { > > + "clk26m", > > + "univpll_192m_d32", > > + "univpll_192m_d16", > > + "clk13m", > > + "osc_d40", > > + "osc_d32", > > + "univpll_192m_d10", > > + "univpll_192m_d8", > > + "univpll_d6_d16", > > + "ulposc3", > > + "osc_d20", > > + "ck2_tvdpll1_d16", > > + "univpll_d6_d8" > > +}; > > It seems all the vlp_camtg* parents are the same. Please merge them > and just have one list. > > > +static const char * const vlp_sspm_26m_parents[] = { > > + "clk26m", > > + "osc_d20" > > +}; > > + > > +static const char * const vlp_ulposc_sspm_parents[] = { > > + "clk26m", > > + "osc_d2", > > + "mainpll_d4_d2" > > +}; > > + > > +static const char * const vlp_vlp_pbus_26m_parents[] = { > > + "clk26m", > > + "osc_d20" > > +}; > > + > > +static const char * const vlp_debug_err_flag_parents[] = { > > + "clk26m", > > + "osc_d20" > > +}; > > + > > +static const char * const vlp_dpmsrdma_parents[] = { > > + "clk26m", > > + "mainpll_d7_d2" > > +}; > > + > > +static const char * const vlp_vlp_pbus_156m_parents[] = { > > + "clk26m", > > + "osc_d2", > > + "mainpll_d7_d2", > > + "mainpll_d7" > > +}; > > + > > +static const char * const vlp_spm_parents[] = { > > + "clk26m", > > + "mainpll_d7_d4" > > +}; > > + > > +static const char * const vlp_mminfra_parents[] = { > > + "clk26m", > > + "osc_d4", > > + "mainpll_d3" > > +}; > > + > > +static const char * const vlp_usb_parents[] = { > > + "clk26m", > > + "mainpll_d9" > > +}; > > The previous and the next one are the same. > > > +static const char * const vlp_usb_xhci_parents[] = { > > + "clk26m", > > + "mainpll_d9" > > +}; > > + > > +static const char * const vlp_noc_vlp_parents[] = { > > + "clk26m", > > + "osc_d20", > > + "mainpll_d9" > > +}; > > + > > +static const char * const vlp_audio_h_parents[] = { > > + "clk26m", > > + "vlp_apll1", > > + "vlp_apll2" > > +}; > > + > > +static const char * const vlp_aud_engen1_parents[] = { > > + "clk26m", > > + "apll1_d8", > > + "apll1_d4" > > +}; > > The previous and the next one are the same. > > > +static const char * const vlp_aud_engen2_parents[] = { > > + "clk26m", > > + "apll2_d8", > > + "apll2_d4" > > +}; > > + > > +static const char * const vlp_aud_intbus_parents[] = { > > + "clk26m", > > + "mainpll_d7_d4", > > + "mainpll_d4_d4" > > +}; Also, all these audio related clocks (audio_h, aud_engen1, aud_engen2 aud_intbus) have a "vlp_clk26m" clock as their parent. It should be: - clk26m (clk26m from the top ckgen domain) - vlp_clk26m (clk26m from the VLP domain) - (from PLLs) - (from PLLs) Moreover, an offline discussion with the audio owner suggests that of the two 26 MHz clock parents, we really just want the one from the VLP domain, as that one is usable even under suspend. This could be done by providing an index table. ChenYu > > + > > +static const char * const vlp_spvlp_26m > > [...] > > > +static int clk_mt8196_vlp_probe(struct platform_device *pdev) > > +{ > > + struct clk_hw_onecell_data *clk_data; > > + int r; > > + struct device_node *node = pdev->dev.of_node; > > + > > + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(vlp_muxes) + > > + ARRAY_SIZE(vlp_plls)); > > + if (!clk_data) > > + return -ENOMEM; > > + > > + r = mtk_clk_register_muxes(&pdev->dev, vlp_muxes, ARRAY_SIZE(vlp_muxes), > > + node, &mt8196_clk_vlp_lock, clk_data); > > + if (r) > > + goto free_clk_data; > > + > > + r = mtk_clk_register_plls(node, vlp_plls, ARRAY_SIZE(vlp_plls), > > + clk_data); > > + if (r) > > + goto unregister_muxes; > > + > > + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); > > + if (r) > > + goto unregister_plls; > > + > > + platform_set_drvdata(pdev, clk_data); > > + > > + return r; > > + > > +unregister_plls: > > + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); > > +unregister_muxes: > > + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); > > +free_clk_data: > > + mtk_free_clk_data(clk_data); > > The AFE driver sets some tuner parameters in the VLPCKGEN block at probe > time. Maybe we could do that here instead? > > /* vlp_cksys_clk: 0x1c016000 */ > #define VLP_APLL1_TUNER_CON0 0x02a4 > #define VLP_APLL2_TUNER_CON0 0x02a8 > > /* vlp apll1 tuner default value*/ > #define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d > /* vlp apll2 tuner default value + 1*/ > #define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265 > > regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, > VLP_APLL1_TUNER_CON0_VALUE); > regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, > VLP_APLL2_TUNER_CON0_VALUE); > > ChenYu > > > + > > + return r; > > +} > > + > > +static void clk_mt8196_vlp_remove(struct platform_device *pdev) > > +{ > > + struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); > > + struct device_node *node = pdev->dev.of_node; > > + > > + of_clk_del_provider(node); > > + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); > > + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); > > + mtk_free_clk_data(clk_data); > > +} > > + > > +static const struct of_device_id of_match_clk_mt8196_vlp_ck[] = { > > + { .compatible = "mediatek,mt8196-vlpckgen" }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vlp_ck); > > + > > +static struct platform_driver clk_mt8196_vlp_drv = { > > + .probe = clk_mt8196_vlp_probe, > > + .remove = clk_mt8196_vlp_remove, > > + .driver = { > > + .name = "clk-mt8196-vlpck", > > + .of_match_table = of_match_clk_mt8196_vlp_ck, > > + }, > > +}; > > + > > +MODULE_DESCRIPTION("MediaTek MT8196 VLP clock generator driver"); > > +module_platform_driver(clk_mt8196_vlp_drv); > > +MODULE_LICENSE("GPL"); > > -- > > 2.39.5 > >
On 7/18/25 10:31, Chen-Yu Tsai wrote: > On Tue, Jul 15, 2025 at 3:28 PM Chen-Yu Tsai <wenst@chromium.org> wrote: >> >> Hi, >> >> >> On Tue, Jun 24, 2025 at 10:33 PM Laura Nao <laura.nao@collabora.com> wrote: >>> >>> Add support for the MT8196 vlpckgen clock controller, which provides >>> muxes and dividers for clock selection in other IP blocks. >>> >>> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> >>> Signed-off-by: Laura Nao <laura.nao@collabora.com> >>> --- >>> drivers/clk/mediatek/Makefile | 2 +- >>> drivers/clk/mediatek/clk-mt8196-vlpckgen.c | 769 +++++++++++++++++++++ >>> 2 files changed, 770 insertions(+), 1 deletion(-) >>> create mode 100644 drivers/clk/mediatek/clk-mt8196-vlpckgen.c >>> >>> diff --git a/drivers/clk/mediatek/Makefile b/drivers/clk/mediatek/Makefile >>> index 0688d7bf4979..24683dd51783 100644 >>> --- a/drivers/clk/mediatek/Makefile >>> +++ b/drivers/clk/mediatek/Makefile >>> @@ -161,7 +161,7 @@ obj-$(CONFIG_COMMON_CLK_MT8195_VENCSYS) += clk-mt8195-venc.o >>> obj-$(CONFIG_COMMON_CLK_MT8195_VPPSYS) += clk-mt8195-vpp0.o clk-mt8195-vpp1.o >>> obj-$(CONFIG_COMMON_CLK_MT8195_WPESYS) += clk-mt8195-wpe.o >>> obj-$(CONFIG_COMMON_CLK_MT8196) += clk-mt8196-apmixedsys.o clk-mt8196-topckgen.o \ >>> - clk-mt8196-topckgen2.o >>> + clk-mt8196-topckgen2.o clk-mt8196-vlpckgen.o >>> obj-$(CONFIG_COMMON_CLK_MT8365) += clk-mt8365-apmixedsys.o clk-mt8365.o >>> obj-$(CONFIG_COMMON_CLK_MT8365_APU) += clk-mt8365-apu.o >>> obj-$(CONFIG_COMMON_CLK_MT8365_CAM) += clk-mt8365-cam.o >>> diff --git a/drivers/clk/mediatek/clk-mt8196-vlpckgen.c b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c >>> new file mode 100644 >>> index 000000000000..23a673dd4c5c >>> --- /dev/null >>> +++ b/drivers/clk/mediatek/clk-mt8196-vlpckgen.c >>> @@ -0,0 +1,769 @@ >> >> [...] >> >>> +static const char * const vlp_camtg0_parents[] = { >>> + "clk26m", >>> + "univpll_192m_d32", >>> + "univpll_192m_d16", >>> + "clk13m", >>> + "osc_d40", >>> + "osc_d32", >>> + "univpll_192m_d10", >>> + "univpll_192m_d8", >>> + "univpll_d6_d16", >>> + "ulposc3", >>> + "osc_d20", >>> + "ck2_tvdpll1_d16", >>> + "univpll_d6_d8" >>> +}; >> >> It seems all the vlp_camtg* parents are the same. Please merge them >> and just have one list. >> >>> +static const char * const vlp_sspm_26m_parents[] = { >>> + "clk26m", >>> + "osc_d20" >>> +}; >>> + >>> +static const char * const vlp_ulposc_sspm_parents[] = { >>> + "clk26m", >>> + "osc_d2", >>> + "mainpll_d4_d2" >>> +}; >>> + >>> +static const char * const vlp_vlp_pbus_26m_parents[] = { >>> + "clk26m", >>> + "osc_d20" >>> +}; >>> + >>> +static const char * const vlp_debug_err_flag_parents[] = { >>> + "clk26m", >>> + "osc_d20" >>> +}; >>> + >>> +static const char * const vlp_dpmsrdma_parents[] = { >>> + "clk26m", >>> + "mainpll_d7_d2" >>> +}; >>> + >>> +static const char * const vlp_vlp_pbus_156m_parents[] = { >>> + "clk26m", >>> + "osc_d2", >>> + "mainpll_d7_d2", >>> + "mainpll_d7" >>> +}; >>> + >>> +static const char * const vlp_spm_parents[] = { >>> + "clk26m", >>> + "mainpll_d7_d4" >>> +}; >>> + >>> +static const char * const vlp_mminfra_parents[] = { >>> + "clk26m", >>> + "osc_d4", >>> + "mainpll_d3" >>> +}; >>> + >>> +static const char * const vlp_usb_parents[] = { >>> + "clk26m", >>> + "mainpll_d9" >>> +}; >> >> The previous and the next one are the same. >> >>> +static const char * const vlp_usb_xhci_parents[] = { >>> + "clk26m", >>> + "mainpll_d9" >>> +}; >>> + >>> +static const char * const vlp_noc_vlp_parents[] = { >>> + "clk26m", >>> + "osc_d20", >>> + "mainpll_d9" >>> +}; >>> + >>> +static const char * const vlp_audio_h_parents[] = { >>> + "clk26m", >>> + "vlp_apll1", >>> + "vlp_apll2" >>> +}; >>> + >>> +static const char * const vlp_aud_engen1_parents[] = { >>> + "clk26m", >>> + "apll1_d8", >>> + "apll1_d4" >>> +}; >> >> The previous and the next one are the same. >> >>> +static const char * const vlp_aud_engen2_parents[] = { >>> + "clk26m", >>> + "apll2_d8", >>> + "apll2_d4" >>> +}; >>> + >>> +static const char * const vlp_aud_intbus_parents[] = { >>> + "clk26m", >>> + "mainpll_d7_d4", >>> + "mainpll_d4_d4" >>> +}; > > Also, all these audio related clocks (audio_h, aud_engen1, aud_engen2 > aud_intbus) have a "vlp_clk26m" clock as their parent. It should be: > > - clk26m (clk26m from the top ckgen domain) > - vlp_clk26m (clk26m from the VLP domain) > - (from PLLs) > - (from PLLs) > > Moreover, an offline discussion with the audio owner suggests that > of the two 26 MHz clock parents, we really just want the one from > the VLP domain, as that one is usable even under suspend. This > could be done by providing an index table. > Hi ChenYu, Thanks for the feedback - I’ll make these changes along with the previous suggestions and submit a v3. Best, Laura > ChenYu > >>> + >>> +static const char * const vlp_spvlp_26m >> >> [...] >> >>> +static int clk_mt8196_vlp_probe(struct platform_device *pdev) >>> +{ >>> + struct clk_hw_onecell_data *clk_data; >>> + int r; >>> + struct device_node *node = pdev->dev.of_node; >>> + >>> + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(vlp_muxes) + >>> + ARRAY_SIZE(vlp_plls)); >>> + if (!clk_data) >>> + return -ENOMEM; >>> + >>> + r = mtk_clk_register_muxes(&pdev->dev, vlp_muxes, ARRAY_SIZE(vlp_muxes), >>> + node, &mt8196_clk_vlp_lock, clk_data); >>> + if (r) >>> + goto free_clk_data; >>> + >>> + r = mtk_clk_register_plls(node, vlp_plls, ARRAY_SIZE(vlp_plls), >>> + clk_data); >>> + if (r) >>> + goto unregister_muxes; >>> + >>> + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); >>> + if (r) >>> + goto unregister_plls; >>> + >>> + platform_set_drvdata(pdev, clk_data); >>> + >>> + return r; >>> + >>> +unregister_plls: >>> + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); >>> +unregister_muxes: >>> + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); >>> +free_clk_data: >>> + mtk_free_clk_data(clk_data); >> >> The AFE driver sets some tuner parameters in the VLPCKGEN block at probe >> time. Maybe we could do that here instead? >> >> /* vlp_cksys_clk: 0x1c016000 */ >> #define VLP_APLL1_TUNER_CON0 0x02a4 >> #define VLP_APLL2_TUNER_CON0 0x02a8 >> >> /* vlp apll1 tuner default value*/ >> #define VLP_APLL1_TUNER_CON0_VALUE 0x6f28bd4d >> /* vlp apll2 tuner default value + 1*/ >> #define VLP_APLL2_TUNER_CON0_VALUE 0x78fd5265 >> >> regmap_write(afe_priv->vlp_ck, VLP_APLL1_TUNER_CON0, >> VLP_APLL1_TUNER_CON0_VALUE); >> regmap_write(afe_priv->vlp_ck, VLP_APLL2_TUNER_CON0, >> VLP_APLL2_TUNER_CON0_VALUE); >> >> ChenYu >> >>> + >>> + return r; >>> +} >>> + >>> +static void clk_mt8196_vlp_remove(struct platform_device *pdev) >>> +{ >>> + struct clk_hw_onecell_data *clk_data = platform_get_drvdata(pdev); >>> + struct device_node *node = pdev->dev.of_node; >>> + >>> + of_clk_del_provider(node); >>> + mtk_clk_unregister_plls(vlp_plls, ARRAY_SIZE(vlp_plls), clk_data); >>> + mtk_clk_unregister_muxes(vlp_muxes, ARRAY_SIZE(vlp_muxes), clk_data); >>> + mtk_free_clk_data(clk_data); >>> +} >>> + >>> +static const struct of_device_id of_match_clk_mt8196_vlp_ck[] = { >>> + { .compatible = "mediatek,mt8196-vlpckgen" }, >>> + { /* sentinel */ } >>> +}; >>> +MODULE_DEVICE_TABLE(of, of_match_clk_mt8196_vlp_ck); >>> + >>> +static struct platform_driver clk_mt8196_vlp_drv = { >>> + .probe = clk_mt8196_vlp_probe, >>> + .remove = clk_mt8196_vlp_remove, >>> + .driver = { >>> + .name = "clk-mt8196-vlpck", >>> + .of_match_table = of_match_clk_mt8196_vlp_ck, >>> + }, >>> +}; >>> + >>> +MODULE_DESCRIPTION("MediaTek MT8196 VLP clock generator driver"); >>> +module_platform_driver(clk_mt8196_vlp_drv); >>> +MODULE_LICENSE("GPL"); >>> -- >>> 2.39.5 >>>
© 2016 - 2025 Red Hat, Inc.