diff options
Diffstat (limited to 'drivers/clk/sunxi-ng')
-rw-r--r-- | drivers/clk/sunxi-ng/Kconfig | 5 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun55i-a523-mcu.c | 469 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun55i-a523.c | 21 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun55i-a523.h | 14 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu-sun6i-rtc.c | 11 | ||||
-rw-r--r-- | drivers/clk/sunxi-ng/ccu_div.h | 18 |
7 files changed, 523 insertions, 17 deletions
diff --git a/drivers/clk/sunxi-ng/Kconfig b/drivers/clk/sunxi-ng/Kconfig index 8896fd052ef1..6af2d020e03e 100644 --- a/drivers/clk/sunxi-ng/Kconfig +++ b/drivers/clk/sunxi-ng/Kconfig @@ -57,6 +57,11 @@ config SUN55I_A523_CCU default ARCH_SUNXI depends on ARM64 || COMPILE_TEST +config SUN55I_A523_MCU_CCU + tristate "Support for the Allwinner A523/T527 MCU CCU" + default ARCH_SUNXI + depends on ARM64 || COMPILE_TEST + config SUN55I_A523_R_CCU tristate "Support for the Allwinner A523/T527 PRCM CCU" default ARCH_SUNXI diff --git a/drivers/clk/sunxi-ng/Makefile b/drivers/clk/sunxi-ng/Makefile index 82e471036de6..a1c4087d7241 100644 --- a/drivers/clk/sunxi-ng/Makefile +++ b/drivers/clk/sunxi-ng/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_SUN50I_H6_CCU) += sun50i-h6-ccu.o obj-$(CONFIG_SUN50I_H6_R_CCU) += sun50i-h6-r-ccu.o 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_SUN4I_A10_CCU) += sun4i-a10-ccu.o obj-$(CONFIG_SUN5I_CCU) += sun5i-ccu.o @@ -61,6 +62,7 @@ sun50i-h6-ccu-y += ccu-sun50i-h6.o sun50i-h6-r-ccu-y += ccu-sun50i-h6-r.o 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 sun4i-a10-ccu-y += ccu-sun4i-a10.o sun5i-ccu-y += ccu-sun5i.o diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523-mcu.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523-mcu.c new file mode 100644 index 000000000000..197844f0fe4e --- /dev/null +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523-mcu.c @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Chen-Yu Tsai <wens@csie.org> + * + * 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/sun55i-a523-mcu-ccu.h> +#include <dt-bindings/reset/sun55i-a523-mcu-ccu.h> + +#include "ccu_common.h" +#include "ccu_reset.h" + +#include "ccu_div.h" +#include "ccu_gate.h" +#include "ccu_mp.h" +#include "ccu_mult.h" +#include "ccu_nm.h" + +static const struct clk_parent_data osc24M[] = { + { .fw_name = "hosc" } +}; + +static const struct clk_parent_data ahb[] = { + { .fw_name = "r-ahb" } +}; + +static const struct clk_parent_data apb[] = { + { .fw_name = "r-apb0" } +}; + +#define SUN55I_A523_PLL_AUDIO1_REG 0x00c +static struct ccu_sdm_setting pll_audio1_sdm_table[] = { + { .rate = 2167603200, .pattern = 0xa000a234, .m = 1, .n = 90 }, /* div2->22.5792 */ + { .rate = 2359296000, .pattern = 0xa0009ba6, .m = 1, .n = 98 }, /* div2->24.576 */ + { .rate = 1806336000, .pattern = 0xa000872b, .m = 1, .n = 75 }, /* div5->22.576 */ +}; + +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), + .sdm = _SUNXI_CCU_SDM(pll_audio1_sdm_table, BIT(24), + 0x010, BIT(31)), + .min_rate = 180000000U, + .max_rate = 3500000000U, + .common = { + .reg = 0x00c, + .features = CCU_FEATURE_SIGMA_DELTA_MOD, + .hw.init = CLK_HW_INIT_PARENTS_DATA("pll-audio1", + osc24M, &ccu_nm_ops, + CLK_SET_RATE_GATE), + }, +}; + +/* + * /2 and /5 dividers are actually programmable, but we just use the + * values from the BSP, since the audio PLL only needs to provide a + * couple clock rates. This also matches the names given in the manual. + */ +static const struct clk_hw *pll_audio1_div_parents[] = { &pll_audio1_clk.common.hw }; +static CLK_FIXED_FACTOR_HWS(pll_audio1_div2_clk, "pll-audio1-div2", + pll_audio1_div_parents, 2, 1, + CLK_SET_RATE_PARENT); +static CLK_FIXED_FACTOR_HWS(pll_audio1_div5_clk, "pll-audio1-div5", + pll_audio1_div_parents, 5, 1, + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_M_WITH_GATE(audio_out_clk, "audio-out", + "pll-audio1-div2", 0x01c, + 0, 5, BIT(31), CLK_SET_RATE_PARENT); + +static const struct clk_parent_data dsp_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + /* + * The order of the following two parent is from the BSP code. It is + * the opposite in the manual. Testing with the DSP is required to + * figure out the real order. + */ + { .hw = &pll_audio1_div5_clk.hw }, + { .hw = &pll_audio1_div2_clk.hw }, + { .fw_name = "dsp" }, +}; +static SUNXI_CCU_M_DATA_WITH_MUX_GATE(dsp_clk, "mcu-dsp", dsp_parents, 0x0020, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + 0); + +static const struct clk_parent_data i2s_parents[] = { + { .fw_name = "pll-audio0-4x" }, + { .hw = &pll_audio1_div2_clk.hw }, + { .hw = &pll_audio1_div5_clk.hw }, +}; + +static SUNXI_CCU_DUALDIV_MUX_GATE(i2s0_clk, "i2s0", i2s_parents, 0x02c, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); +static SUNXI_CCU_DUALDIV_MUX_GATE(i2s1_clk, "i2s1", i2s_parents, 0x030, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); +static SUNXI_CCU_DUALDIV_MUX_GATE(i2s2_clk, "i2s2", i2s_parents, 0x034, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); +static SUNXI_CCU_DUALDIV_MUX_GATE(i2s3_clk, "i2s3", i2s_parents, 0x038, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static const struct clk_parent_data i2s3_asrc_parents[] = { + { .fw_name = "pll-periph0-300m" }, + { .hw = &pll_audio1_div2_clk.hw }, + { .hw = &pll_audio1_div5_clk.hw }, +}; +static SUNXI_CCU_DUALDIV_MUX_GATE(i2s3_asrc_clk, "i2s3-asrc", + i2s3_asrc_parents, 0x03c, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_DATA(bus_i2s0_clk, "bus-i2s0", apb, 0x040, BIT(0), 0); +static SUNXI_CCU_GATE_DATA(bus_i2s1_clk, "bus-i2s1", apb, 0x040, BIT(1), 0); +static SUNXI_CCU_GATE_DATA(bus_i2s2_clk, "bus-i2s2", apb, 0x040, BIT(2), 0); +static SUNXI_CCU_GATE_DATA(bus_i2s3_clk, "bus-i2s3", apb, 0x040, BIT(3), 0); + +static const struct clk_parent_data audio_parents[] = { + { .fw_name = "pll-audio0-4x" }, + { .hw = &pll_audio1_div2_clk.hw }, + { .hw = &pll_audio1_div5_clk.hw }, +}; +static SUNXI_CCU_DUALDIV_MUX_GATE(spdif_tx_clk, "spdif-tx", + audio_parents, 0x044, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); +static SUNXI_CCU_DUALDIV_MUX_GATE(spdif_rx_clk, "spdif-rx", + i2s3_asrc_parents, 0x048, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_DATA(bus_spdif_clk, "bus-spdif", apb, 0x04c, BIT(0), 0); + +static SUNXI_CCU_DUALDIV_MUX_GATE(dmic_clk, "dmic", audio_parents, 0x050, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_DATA(bus_dmic_clk, "bus-dmic", apb, 0x054, BIT(0), 0); + +static SUNXI_CCU_DUALDIV_MUX_GATE(audio_dac_clk, "audio-dac", + audio_parents, 0x058, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); +static SUNXI_CCU_DUALDIV_MUX_GATE(audio_adc_clk, "audio-adc", + audio_parents, 0x05c, + 0, 5, /* M */ + 5, 5, /* P */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + +static SUNXI_CCU_GATE_DATA(bus_audio_codec_clk, "bus-audio-codec", + apb, 0x060, BIT(0), 0); + +static SUNXI_CCU_GATE_DATA(bus_dsp_msgbox_clk, "bus-dsp-msgbox", + ahb, 0x068, BIT(0), 0); +static SUNXI_CCU_GATE_DATA(bus_dsp_cfg_clk, "bus-dsp-cfg", + apb, 0x06c, BIT(0), 0); + +static SUNXI_CCU_GATE_DATA(bus_npu_hclk, "bus-npu-hclk", ahb, 0x070, BIT(1), 0); +static SUNXI_CCU_GATE_DATA(bus_npu_aclk, "bus-npu-aclk", ahb, 0x070, BIT(2), 0); + +static const struct clk_parent_data timer_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, + { .fw_name = "r-ahb" } +}; +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer0_clk, "mcu-timer0", timer_parents, + 0x074, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer1_clk, "mcu-timer1", timer_parents, + 0x078, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer2_clk, "mcu-timer2", timer_parents, + 0x07c, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer3_clk, "mcu-timer3", timer_parents, + 0x080, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer4_clk, "mcu-timer4", timer_parents, + 0x084, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_P_DATA_WITH_MUX_GATE(mcu_timer5_clk, "mcu-timer5", timer_parents, + 0x088, + 1, 3, /* P */ + 4, 2, /* mux */ + BIT(0), /* gate */ + 0); +static SUNXI_CCU_GATE_DATA(bus_mcu_timer_clk, "bus-mcu-timer", ahb, 0x08c, BIT(0), 0); +static SUNXI_CCU_GATE_DATA(bus_mcu_dma_clk, "bus-mcu-dma", ahb, 0x104, BIT(0), 0); +/* tzma* only found in BSP code. */ +static SUNXI_CCU_GATE_DATA(tzma0_clk, "tzma0", ahb, 0x108, BIT(0), 0); +static SUNXI_CCU_GATE_DATA(tzma1_clk, "tzma1", ahb, 0x10c, BIT(0), 0); +/* parent is a guess as this block is not shown in the system bus tree diagram */ +static SUNXI_CCU_GATE_DATA(bus_pubsram_clk, "bus-pubsram", ahb, 0x114, BIT(0), 0); + +/* + * user manual has "mbus" clock as parent of both clocks below, + * but this makes more sense, since BSP MCU DMA controller has + * reference to both of them, likely needing both enabled. + */ +static SUNXI_CCU_GATE_FW(mbus_mcu_clk, "mbus-mcu", "mbus", 0x11c, BIT(1), 0); +static SUNXI_CCU_GATE_HW(mbus_mcu_dma_clk, "mbus-mcu-dma", + &mbus_mcu_clk.common.hw, 0x11c, BIT(0), 0); + +static const struct clk_parent_data riscv_pwm_parents[] = { + { .fw_name = "hosc" }, + { .fw_name = "losc" }, + { .fw_name = "iosc" }, +}; + +static SUNXI_CCU_MUX_DATA_WITH_GATE(riscv_clk, "riscv", + riscv_pwm_parents, 0x120, + 27, 3, BIT(31), 0); +/* Parents are guesses as these two blocks are not shown in the system bus tree diagram */ +static SUNXI_CCU_GATE_DATA(bus_riscv_cfg_clk, "bus-riscv-cfg", ahb, + 0x124, BIT(0), 0); +static SUNXI_CCU_GATE_DATA(bus_riscv_msgbox_clk, "bus-riscv-msgbox", ahb, + 0x128, BIT(0), 0); + +static SUNXI_CCU_MUX_DATA_WITH_GATE(mcu_pwm0_clk, "mcu-pwm0", + riscv_pwm_parents, 0x130, + 24, 3, BIT(31), 0); +static SUNXI_CCU_GATE_DATA(bus_mcu_pwm0_clk, "bus-mcu-pwm0", apb, + 0x134, BIT(0), 0); + +/* + * 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 *sun55i_a523_mcu_ccu_clks[] = { + &pll_audio1_clk.common, + &audio_out_clk.common, + &dsp_clk.common, + &i2s0_clk.common, + &i2s1_clk.common, + &i2s2_clk.common, + &i2s3_clk.common, + &i2s3_asrc_clk.common, + &bus_i2s0_clk.common, + &bus_i2s1_clk.common, + &bus_i2s2_clk.common, + &bus_i2s3_clk.common, + &spdif_tx_clk.common, + &spdif_rx_clk.common, + &bus_spdif_clk.common, + &dmic_clk.common, + &bus_dmic_clk.common, + &audio_dac_clk.common, + &audio_adc_clk.common, + &bus_audio_codec_clk.common, + &bus_dsp_msgbox_clk.common, + &bus_dsp_cfg_clk.common, + &bus_npu_aclk.common, + &bus_npu_hclk.common, + &mcu_timer0_clk.common, + &mcu_timer1_clk.common, + &mcu_timer2_clk.common, + &mcu_timer3_clk.common, + &mcu_timer4_clk.common, + &mcu_timer5_clk.common, + &bus_mcu_timer_clk.common, + &bus_mcu_dma_clk.common, + &tzma0_clk.common, + &tzma1_clk.common, + &bus_pubsram_clk.common, + &mbus_mcu_dma_clk.common, + &mbus_mcu_clk.common, + &riscv_clk.common, + &bus_riscv_cfg_clk.common, + &bus_riscv_msgbox_clk.common, + &mcu_pwm0_clk.common, + &bus_mcu_pwm0_clk.common, +}; + +static struct clk_hw_onecell_data sun55i_a523_mcu_hw_clks = { + .hws = { + [CLK_MCU_PLL_AUDIO1] = &pll_audio1_clk.common.hw, + [CLK_MCU_PLL_AUDIO1_DIV2] = &pll_audio1_div2_clk.hw, + [CLK_MCU_PLL_AUDIO1_DIV5] = &pll_audio1_div5_clk.hw, + [CLK_MCU_AUDIO_OUT] = &audio_out_clk.common.hw, + [CLK_MCU_DSP] = &dsp_clk.common.hw, + [CLK_MCU_I2S0] = &i2s0_clk.common.hw, + [CLK_MCU_I2S1] = &i2s1_clk.common.hw, + [CLK_MCU_I2S2] = &i2s2_clk.common.hw, + [CLK_MCU_I2S3] = &i2s3_clk.common.hw, + [CLK_MCU_I2S3_ASRC] = &i2s3_asrc_clk.common.hw, + [CLK_BUS_MCU_I2S0] = &bus_i2s0_clk.common.hw, + [CLK_BUS_MCU_I2S1] = &bus_i2s1_clk.common.hw, + [CLK_BUS_MCU_I2S2] = &bus_i2s2_clk.common.hw, + [CLK_BUS_MCU_I2S3] = &bus_i2s3_clk.common.hw, + [CLK_MCU_SPDIF_TX] = &spdif_tx_clk.common.hw, + [CLK_MCU_SPDIF_RX] = &spdif_rx_clk.common.hw, + [CLK_BUS_MCU_SPDIF] = &bus_spdif_clk.common.hw, + [CLK_MCU_DMIC] = &dmic_clk.common.hw, + [CLK_BUS_MCU_DMIC] = &bus_dmic_clk.common.hw, + [CLK_MCU_AUDIO_CODEC_DAC] = &audio_dac_clk.common.hw, + [CLK_MCU_AUDIO_CODEC_ADC] = &audio_adc_clk.common.hw, + [CLK_BUS_MCU_AUDIO_CODEC] = &bus_audio_codec_clk.common.hw, + [CLK_BUS_MCU_DSP_MSGBOX] = &bus_dsp_msgbox_clk.common.hw, + [CLK_BUS_MCU_DSP_CFG] = &bus_dsp_cfg_clk.common.hw, + [CLK_BUS_MCU_NPU_HCLK] = &bus_npu_hclk.common.hw, + [CLK_BUS_MCU_NPU_ACLK] = &bus_npu_aclk.common.hw, + [CLK_MCU_TIMER0] = &mcu_timer0_clk.common.hw, + [CLK_MCU_TIMER1] = &mcu_timer1_clk.common.hw, + [CLK_MCU_TIMER2] = &mcu_timer2_clk.common.hw, + [CLK_MCU_TIMER3] = &mcu_timer3_clk.common.hw, + [CLK_MCU_TIMER4] = &mcu_timer4_clk.common.hw, + [CLK_MCU_TIMER5] = &mcu_timer5_clk.common.hw, + [CLK_BUS_MCU_TIMER] = &bus_mcu_timer_clk.common.hw, + [CLK_BUS_MCU_DMA] = &bus_mcu_dma_clk.common.hw, + [CLK_MCU_TZMA0] = &tzma0_clk.common.hw, + [CLK_MCU_TZMA1] = &tzma1_clk.common.hw, + [CLK_BUS_MCU_PUBSRAM] = &bus_pubsram_clk.common.hw, + [CLK_MCU_MBUS_DMA] = &mbus_mcu_dma_clk.common.hw, + [CLK_MCU_MBUS] = &mbus_mcu_clk.common.hw, + [CLK_MCU_RISCV] = &riscv_clk.common.hw, + [CLK_BUS_MCU_RISCV_CFG] = &bus_riscv_cfg_clk.common.hw, + [CLK_BUS_MCU_RISCV_MSGBOX] = &bus_riscv_msgbox_clk.common.hw, + [CLK_MCU_PWM0] = &mcu_pwm0_clk.common.hw, + [CLK_BUS_MCU_PWM0] = &bus_mcu_pwm0_clk.common.hw, + }, + .num = CLK_BUS_MCU_PWM0 + 1, +}; + +static struct ccu_reset_map sun55i_a523_mcu_ccu_resets[] = { + [RST_BUS_MCU_I2S0] = { 0x0040, BIT(16) }, + [RST_BUS_MCU_I2S1] = { 0x0040, BIT(17) }, + [RST_BUS_MCU_I2S2] = { 0x0040, BIT(18) }, + [RST_BUS_MCU_I2S3] = { 0x0040, BIT(19) }, + [RST_BUS_MCU_SPDIF] = { 0x004c, BIT(16) }, + [RST_BUS_MCU_DMIC] = { 0x0054, BIT(16) }, + [RST_BUS_MCU_AUDIO_CODEC] = { 0x0060, BIT(16) }, + [RST_BUS_MCU_DSP_MSGBOX] = { 0x0068, BIT(16) }, + [RST_BUS_MCU_DSP_CFG] = { 0x006c, BIT(16) }, + [RST_BUS_MCU_NPU] = { 0x0070, BIT(16) }, + [RST_BUS_MCU_TIMER] = { 0x008c, BIT(16) }, + /* dsp and dsp_debug resets only found in BSP code. */ + [RST_BUS_MCU_DSP_DEBUG] = { 0x0100, BIT(16) }, + [RST_BUS_MCU_DSP] = { 0x0100, BIT(17) }, + [RST_BUS_MCU_DMA] = { 0x0104, BIT(16) }, + [RST_BUS_MCU_PUBSRAM] = { 0x0114, BIT(16) }, + [RST_BUS_MCU_RISCV_CFG] = { 0x0124, BIT(16) }, + [RST_BUS_MCU_RISCV_DEBUG] = { 0x0124, BIT(17) }, + [RST_BUS_MCU_RISCV_CORE] = { 0x0124, BIT(18) }, + [RST_BUS_MCU_RISCV_MSGBOX] = { 0x0128, BIT(16) }, + [RST_BUS_MCU_PWM0] = { 0x0134, BIT(16) }, +}; + +static const struct sunxi_ccu_desc sun55i_a523_mcu_ccu_desc = { + .ccu_clks = sun55i_a523_mcu_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun55i_a523_mcu_ccu_clks), + + .hw_clks = &sun55i_a523_mcu_hw_clks, + + .resets = sun55i_a523_mcu_ccu_resets, + .num_resets = ARRAY_SIZE(sun55i_a523_mcu_ccu_resets), +}; + +static int sun55i_a523_mcu_ccu_probe(struct platform_device *pdev) +{ + void __iomem *reg; + u32 val; + int ret; + + reg = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + val = readl(reg + SUN55I_A523_PLL_AUDIO1_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. + */ + val |= BIT(31) | BIT(30) | BIT(29); + + /* Enforce p1 = 5, p0 = 2 (the default) for PLL_AUDIO1 */ + val &= ~(GENMASK(22, 20) | GENMASK(18, 16)); + val |= (4 << 20) | (1 << 16); + + writel(val, reg + SUN55I_A523_PLL_AUDIO1_REG); + + ret = devm_sunxi_ccu_probe(&pdev->dev, reg, &sun55i_a523_mcu_ccu_desc); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id sun55i_a523_mcu_ccu_ids[] = { + { .compatible = "allwinner,sun55i-a523-mcu-ccu" }, + { } +}; + +static struct platform_driver sun55i_a523_mcu_ccu_driver = { + .probe = sun55i_a523_mcu_ccu_probe, + .driver = { + .name = "sun55i-a523-mcu-ccu", + .suppress_bind_attrs = true, + .of_match_table = sun55i_a523_mcu_ccu_ids, + }, +}; +module_platform_driver(sun55i_a523_mcu_ccu_driver); + +MODULE_IMPORT_NS("SUNXI_CCU"); +MODULE_DESCRIPTION("Support for the Allwinner A523 MCU CCU"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c index 1a9a1cb869e2..acb532f8361b 100644 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.c +++ b/drivers/clk/sunxi-ng/ccu-sun55i-a523.c @@ -11,6 +11,9 @@ #include <linux/module.h> #include <linux/platform_device.h> +#include <dt-bindings/clock/sun55i-a523-ccu.h> +#include <dt-bindings/reset/sun55i-a523-ccu.h> + #include "../clk.h" #include "ccu_common.h" @@ -25,8 +28,6 @@ #include "ccu_nkmp.h" #include "ccu_nm.h" -#include "ccu-sun55i-a523.h" - /* * The 24 MHz oscillator, the root of most of the clock tree. * .fw_name is the string used in the DT "clock-names" property, used to @@ -486,6 +487,18 @@ static SUNXI_CCU_M_HW_WITH_MUX_GATE(ve_clk, "ve", ve_parents, 0x690, static SUNXI_CCU_GATE_HWS(bus_ve_clk, "bus-ve", ahb_hws, 0x69c, BIT(0), 0); +static const struct clk_hw *npu_parents[] = { + &pll_periph0_480M_clk.common.hw, + &pll_periph0_600M_clk.hw, + &pll_periph0_800M_clk.common.hw, + &pll_npu_2x_clk.hw, +}; +static SUNXI_CCU_M_HW_WITH_MUX_GATE(npu_clk, "npu", npu_parents, 0x6e0, + 0, 5, /* M */ + 24, 3, /* mux */ + BIT(31), /* gate */ + CLK_SET_RATE_PARENT); + static SUNXI_CCU_GATE_HWS(bus_dma_clk, "bus-dma", ahb_hws, 0x70c, BIT(0), 0); static SUNXI_CCU_GATE_HWS(bus_msgbox_clk, "bus-msgbox", ahb_hws, 0x71c, @@ -1217,6 +1230,7 @@ static struct ccu_common *sun55i_a523_ccu_clks[] = { &bus_ce_sys_clk.common, &ve_clk.common, &bus_ve_clk.common, + &npu_clk.common, &bus_dma_clk.common, &bus_msgbox_clk.common, &bus_spinlock_clk.common, @@ -1343,7 +1357,6 @@ static struct ccu_common *sun55i_a523_ccu_clks[] = { }; static struct clk_hw_onecell_data sun55i_a523_hw_clks = { - .num = CLK_NUMBER, .hws = { [CLK_PLL_DDR0] = &pll_ddr_clk.common.hw, [CLK_PLL_PERIPH0_4X] = &pll_periph0_4x_clk.common.hw, @@ -1524,7 +1537,9 @@ static struct clk_hw_onecell_data sun55i_a523_hw_clks = { [CLK_FANOUT0] = &fanout0_clk.common.hw, [CLK_FANOUT1] = &fanout1_clk.common.hw, [CLK_FANOUT2] = &fanout2_clk.common.hw, + [CLK_NPU] = &npu_clk.common.hw, }, + .num = CLK_NPU + 1, }; static struct ccu_reset_map sun55i_a523_ccu_resets[] = { diff --git a/drivers/clk/sunxi-ng/ccu-sun55i-a523.h b/drivers/clk/sunxi-ng/ccu-sun55i-a523.h deleted file mode 100644 index fc8dd42f1b47..000000000000 --- a/drivers/clk/sunxi-ng/ccu-sun55i-a523.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright 2024 Arm Ltd. - */ - -#ifndef _CCU_SUN55I_A523_H -#define _CCU_SUN55I_A523_H - -#include <dt-bindings/clock/sun55i-a523-ccu.h> -#include <dt-bindings/reset/sun55i-a523-ccu.h> - -#define CLK_NUMBER (CLK_FANOUT2 + 1) - -#endif /* _CCU_SUN55I_A523_H */ diff --git a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c index 0536e880b80f..f6bfeba009e8 100644 --- a/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c +++ b/drivers/clk/sunxi-ng/ccu-sun6i-rtc.c @@ -325,6 +325,13 @@ static const struct sun6i_rtc_match_data sun50i_r329_rtc_ccu_data = { .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents), }; +static const struct sun6i_rtc_match_data sun55i_a523_rtc_ccu_data = { + .have_ext_osc32k = true, + .have_iosc_calibration = true, + .osc32k_fanout_parents = sun50i_r329_osc32k_fanout_parents, + .osc32k_fanout_nparents = ARRAY_SIZE(sun50i_r329_osc32k_fanout_parents), +}; + static const struct of_device_id sun6i_rtc_ccu_match[] = { { .compatible = "allwinner,sun50i-h616-rtc", @@ -334,6 +341,10 @@ static const struct of_device_id sun6i_rtc_ccu_match[] = { .compatible = "allwinner,sun50i-r329-rtc", .data = &sun50i_r329_rtc_ccu_data, }, + { + .compatible = "allwinner,sun55i-a523-rtc", + .data = &sun55i_a523_rtc_ccu_data, + }, {}, }; MODULE_DEVICE_TABLE(of, sun6i_rtc_ccu_match); diff --git a/drivers/clk/sunxi-ng/ccu_div.h b/drivers/clk/sunxi-ng/ccu_div.h index 90d49ee8e0cc..be00b3277e97 100644 --- a/drivers/clk/sunxi-ng/ccu_div.h +++ b/drivers/clk/sunxi-ng/ccu_div.h @@ -274,6 +274,24 @@ struct ccu_div { SUNXI_CCU_M_HWS_WITH_GATE(_struct, _name, _parent, _reg, \ _mshift, _mwidth, 0, _flags) +#define SUNXI_CCU_P_DATA_WITH_MUX_GATE(_struct, _name, _parents, _reg, \ + _mshift, _mwidth, \ + _muxshift, _muxwidth, \ + _gate, _flags) \ + struct ccu_div _struct = { \ + .enable = _gate, \ + .div = _SUNXI_CCU_DIV_FLAGS(_mshift, _mwidth, \ + CLK_DIVIDER_POWER_OF_TWO), \ + .mux = _SUNXI_CCU_MUX(_muxshift, _muxwidth), \ + .common = { \ + .reg = _reg, \ + .hw.init = CLK_HW_INIT_PARENTS_DATA(_name, \ + _parents, \ + &ccu_div_ops, \ + _flags), \ + }, \ + } + static inline struct ccu_div *hw_to_ccu_div(struct clk_hw *hw) { struct ccu_common *common = hw_to_ccu_common(hw); |