diff options
40 files changed, 1413 insertions, 64 deletions
diff --git a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml index fe2c5c1baf43..a8471367175b 100644 --- a/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml +++ b/Documentation/devicetree/bindings/clock/airoha,en7523-scu.yaml @@ -64,8 +64,6 @@ allOf: reg: minItems: 2 - '#reset-cells': false - - if: properties: compatible: @@ -85,6 +83,7 @@ examples: reg = <0x1fa20000 0x400>, <0x1fb00000 0x1000>; #clock-cells = <1>; + #reset-cells = <1>; }; - | diff --git a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml index caf442ead24b..31e106ef913d 100644 --- a/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml +++ b/Documentation/devicetree/bindings/clock/google,gs101-clock.yaml @@ -46,6 +46,9 @@ properties: "#clock-cells": const: 1 + power-domains: + maxItems: 1 + reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml index 72f59db73f76..5bf905f88a1a 100644 --- a/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml +++ b/Documentation/devicetree/bindings/clock/samsung,exynosautov920-clock.yaml @@ -38,6 +38,8 @@ properties: - samsung,exynosautov920-cmu-hsi0 - samsung,exynosautov920-cmu-hsi1 - samsung,exynosautov920-cmu-hsi2 + - samsung,exynosautov920-cmu-m2m + - samsung,exynosautov920-cmu-mfc - samsung,exynosautov920-cmu-misc - samsung,exynosautov920-cmu-peric0 - samsung,exynosautov920-cmu-peric1 @@ -226,6 +228,46 @@ allOf: - const: embd - const: ethernet + - if: + properties: + compatible: + contains: + const: samsung,exynosautov920-cmu-m2m + + then: + properties: + clocks: + items: + - description: External reference clock (38.4 MHz) + - description: CMU_M2M NOC clock (from CMU_TOP) + - description: CMU_M2M JPEG clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: noc + - const: jpeg + + - if: + properties: + compatible: + contains: + const: samsung,exynosautov920-cmu-mfc + + then: + properties: + clocks: + items: + - description: External reference clock (38.4 MHz) + - description: CMU_MFC MFC clock (from CMU_TOP) + - description: CMU_MFC WFD clock (from CMU_TOP) + + clock-names: + items: + - const: oscclk + - const: mfc + - const: wfd + required: - compatible - "#clock-cells" diff --git a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml index 9785aac3b5f3..d3bca6088d12 100644 --- a/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml +++ b/Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml @@ -24,6 +24,15 @@ properties: compatible: const: google,gs101-acpm-ipc + "#clock-cells": + const: 1 + description: + Clocks that are variable and index based. These clocks don't provide + an entire range of values between the limits but only discrete points + within the range. The firmware also manages the voltage scaling + appropriately with the clock scaling. The argument is the ID of the + clock contained by the firmware messages. + mboxes: maxItems: 1 @@ -45,6 +54,7 @@ properties: required: - compatible + - "#clock-cells" - mboxes - shmem @@ -56,6 +66,7 @@ examples: power-management { compatible = "google,gs101-acpm-ipc"; + #clock-cells = <1>; mboxes = <&ap2apm_mailbox>; shmem = <&apm_sram>; diff --git a/MAINTAINERS b/MAINTAINERS index 46126ce2f968..5d24c16d3ead 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10601,7 +10601,7 @@ F: Documentation/devicetree/bindings/soc/google/google,gs101-pmu-intr-gen.yaml F: arch/arm64/boot/dts/exynos/google/ F: drivers/clk/samsung/clk-gs101.c F: drivers/phy/samsung/phy-gs101-ufs.c -F: include/dt-bindings/clock/google,gs101.h +F: include/dt-bindings/clock/google,gs101* K: [gG]oogle.?[tT]ensor GPD FAN DRIVER @@ -22761,6 +22761,7 @@ L: linux-kernel@vger.kernel.org L: linux-samsung-soc@vger.kernel.org S: Supported F: Documentation/devicetree/bindings/firmware/google,gs101-acpm-ipc.yaml +F: drivers/clk/samsung/clk-acpm.c F: drivers/firmware/samsung/exynos-acpm* F: include/linux/firmware/samsung/exynos-acpm-protocol.h diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index b74a1767ca27..61ec08404442 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -125,8 +125,7 @@ obj-$(CONFIG_ARCH_HISI) += hisilicon/ obj-y += imgtec/ obj-y += imx/ obj-y += ingenic/ -obj-$(CONFIG_ARCH_K3) += keystone/ -obj-$(CONFIG_ARCH_KEYSTONE) += keystone/ +obj-y += keystone/ obj-y += mediatek/ obj-$(CONFIG_ARCH_MESON) += meson/ obj-y += microchip/ diff --git a/drivers/clk/actions/owl-common.h b/drivers/clk/actions/owl-common.h index 8fb65f3e82d7..5768a2e0f6a0 100644 --- a/drivers/clk/actions/owl-common.h +++ b/drivers/clk/actions/owl-common.h @@ -32,7 +32,7 @@ struct owl_clk_desc { }; static inline struct owl_clk_common * - hw_to_owl_clk_common(const struct clk_hw *hw) + hw_to_owl_clk_common(struct clk_hw *hw) { return container_of(hw, struct owl_clk_common, hw); } diff --git a/drivers/clk/actions/owl-composite.h b/drivers/clk/actions/owl-composite.h index bca38bf8f218..6d7c6f0c47c8 100644 --- a/drivers/clk/actions/owl-composite.h +++ b/drivers/clk/actions/owl-composite.h @@ -108,7 +108,7 @@ struct owl_composite { }, \ } -static inline struct owl_composite *hw_to_owl_comp(const struct clk_hw *hw) +static inline struct owl_composite *hw_to_owl_comp(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/actions/owl-divider.h b/drivers/clk/actions/owl-divider.h index 083be6d80954..d76f58782c52 100644 --- a/drivers/clk/actions/owl-divider.h +++ b/drivers/clk/actions/owl-divider.h @@ -49,7 +49,7 @@ struct owl_divider { }, \ } -static inline struct owl_divider *hw_to_owl_divider(const struct clk_hw *hw) +static inline struct owl_divider *hw_to_owl_divider(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/actions/owl-factor.h b/drivers/clk/actions/owl-factor.h index 04b89cbfdccb..24c704d40925 100644 --- a/drivers/clk/actions/owl-factor.h +++ b/drivers/clk/actions/owl-factor.h @@ -57,7 +57,7 @@ struct owl_factor { #define div_mask(d) ((1 << ((d)->width)) - 1) -static inline struct owl_factor *hw_to_owl_factor(const struct clk_hw *hw) +static inline struct owl_factor *hw_to_owl_factor(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/actions/owl-gate.h b/drivers/clk/actions/owl-gate.h index c2f161c93fda..ac458d4385ee 100644 --- a/drivers/clk/actions/owl-gate.h +++ b/drivers/clk/actions/owl-gate.h @@ -56,7 +56,7 @@ struct owl_gate { }, \ } \ -static inline struct owl_gate *hw_to_owl_gate(const struct clk_hw *hw) +static inline struct owl_gate *hw_to_owl_gate(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/actions/owl-mux.h b/drivers/clk/actions/owl-mux.h index 53b9ab665294..dc0ecc2d5e10 100644 --- a/drivers/clk/actions/owl-mux.h +++ b/drivers/clk/actions/owl-mux.h @@ -44,7 +44,7 @@ struct owl_mux { }, \ } -static inline struct owl_mux *hw_to_owl_mux(const struct clk_hw *hw) +static inline struct owl_mux *hw_to_owl_mux(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/actions/owl-pll.h b/drivers/clk/actions/owl-pll.h index 78e5fc360b03..58e19f1ade43 100644 --- a/drivers/clk/actions/owl-pll.h +++ b/drivers/clk/actions/owl-pll.h @@ -98,7 +98,7 @@ struct owl_pll { #define mul_mask(m) ((1 << ((m)->width)) - 1) -static inline struct owl_pll *hw_to_owl_pll(const struct clk_hw *hw) +static inline struct owl_pll *hw_to_owl_pll(struct clk_hw *hw) { struct owl_clk_common *common = hw_to_owl_clk_common(hw); diff --git a/drivers/clk/clk-en7523.c b/drivers/clk/clk-en7523.c index 15bbdeb60b8e..08cc8e5acf43 100644 --- a/drivers/clk/clk-en7523.c +++ b/drivers/clk/clk-en7523.c @@ -9,6 +9,7 @@ #include <linux/regmap.h> #include <linux/reset-controller.h> #include <dt-bindings/clock/en7523-clk.h> +#include <dt-bindings/reset/airoha,en7523-reset.h> #include <dt-bindings/reset/airoha,en7581-reset.h> #define RST_NR_PER_BANK 32 @@ -299,6 +300,53 @@ static const u16 en7581_rst_ofs[] = { REG_RST_CTRL1, }; +static const u16 en7523_rst_map[] = { + /* RST_CTRL2 */ + [EN7523_XPON_PHY_RST] = 0, + [EN7523_XSI_MAC_RST] = 7, + [EN7523_XSI_PHY_RST] = 8, + [EN7523_NPU_RST] = 9, + [EN7523_I2S_RST] = 10, + [EN7523_TRNG_RST] = 11, + [EN7523_TRNG_MSTART_RST] = 12, + [EN7523_DUAL_HSI0_RST] = 13, + [EN7523_DUAL_HSI1_RST] = 14, + [EN7523_HSI_RST] = 15, + [EN7523_DUAL_HSI0_MAC_RST] = 16, + [EN7523_DUAL_HSI1_MAC_RST] = 17, + [EN7523_HSI_MAC_RST] = 18, + [EN7523_WDMA_RST] = 19, + [EN7523_WOE0_RST] = 20, + [EN7523_WOE1_RST] = 21, + [EN7523_HSDMA_RST] = 22, + [EN7523_I2C2RBUS_RST] = 23, + [EN7523_TDMA_RST] = 24, + /* RST_CTRL1 */ + [EN7523_PCM1_ZSI_ISI_RST] = RST_NR_PER_BANK + 0, + [EN7523_FE_PDMA_RST] = RST_NR_PER_BANK + 1, + [EN7523_FE_QDMA_RST] = RST_NR_PER_BANK + 2, + [EN7523_PCM_SPIWP_RST] = RST_NR_PER_BANK + 4, + [EN7523_CRYPTO_RST] = RST_NR_PER_BANK + 6, + [EN7523_TIMER_RST] = RST_NR_PER_BANK + 8, + [EN7523_PCM1_RST] = RST_NR_PER_BANK + 11, + [EN7523_UART_RST] = RST_NR_PER_BANK + 12, + [EN7523_GPIO_RST] = RST_NR_PER_BANK + 13, + [EN7523_GDMA_RST] = RST_NR_PER_BANK + 14, + [EN7523_I2C_MASTER_RST] = RST_NR_PER_BANK + 16, + [EN7523_PCM2_ZSI_ISI_RST] = RST_NR_PER_BANK + 17, + [EN7523_SFC_RST] = RST_NR_PER_BANK + 18, + [EN7523_UART2_RST] = RST_NR_PER_BANK + 19, + [EN7523_GDMP_RST] = RST_NR_PER_BANK + 20, + [EN7523_FE_RST] = RST_NR_PER_BANK + 21, + [EN7523_USB_HOST_P0_RST] = RST_NR_PER_BANK + 22, + [EN7523_GSW_RST] = RST_NR_PER_BANK + 23, + [EN7523_SFC2_PCM_RST] = RST_NR_PER_BANK + 25, + [EN7523_PCIE0_RST] = RST_NR_PER_BANK + 26, + [EN7523_PCIE1_RST] = RST_NR_PER_BANK + 27, + [EN7523_PCIE_HB_RST] = RST_NR_PER_BANK + 29, + [EN7523_XPON_MAC_RST] = RST_NR_PER_BANK + 31, +}; + static const u16 en7581_rst_map[] = { /* RST_CTRL2 */ [EN7581_XPON_PHY_RST] = 0, @@ -357,6 +405,9 @@ static const u16 en7581_rst_map[] = { [EN7581_XPON_MAC_RST] = RST_NR_PER_BANK + 31, }; +static int en7581_reset_register(struct device *dev, void __iomem *base, + const u16 *rst_map, int nr_resets); + static u32 en7523_get_base_rate(const struct en_clk_desc *desc, u32 val) { if (!desc->base_bits) @@ -552,7 +603,8 @@ static int en7523_clk_hw_init(struct platform_device *pdev, en7523_register_clocks(&pdev->dev, clk_data, base, np_base); - return 0; + return en7581_reset_register(&pdev->dev, np_base, en7523_rst_map, + ARRAY_SIZE(en7523_rst_map)); } static void en7581_register_clocks(struct device *dev, struct clk_hw_onecell_data *clk_data, @@ -652,7 +704,8 @@ static const struct reset_control_ops en7581_reset_ops = { .status = en7523_reset_status, }; -static int en7581_reset_register(struct device *dev, void __iomem *base) +static int en7581_reset_register(struct device *dev, void __iomem *base, + const u16 *rst_map, int nr_resets) { struct en_rst_data *rst_data; @@ -661,10 +714,10 @@ static int en7581_reset_register(struct device *dev, void __iomem *base) return -ENOMEM; rst_data->bank_ofs = en7581_rst_ofs; - rst_data->idx_map = en7581_rst_map; + rst_data->idx_map = rst_map; rst_data->base = base; - rst_data->rcdev.nr_resets = ARRAY_SIZE(en7581_rst_map); + rst_data->rcdev.nr_resets = nr_resets; rst_data->rcdev.of_xlate = en7523_reset_xlate; rst_data->rcdev.ops = &en7581_reset_ops; rst_data->rcdev.of_node = dev->of_node; @@ -698,7 +751,8 @@ static int en7581_clk_hw_init(struct platform_device *pdev, val = readl(base + REG_NP_SCU_PCIC); writel(val | 3, base + REG_NP_SCU_PCIC); - return en7581_reset_register(&pdev->dev, base); + return en7581_reset_register(&pdev->dev, base, en7581_rst_map, + ARRAY_SIZE(en7581_rst_map)); } static int en7523_clk_probe(struct platform_device *pdev) diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index a4b42811de55..9d5071223f4c 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -496,8 +496,8 @@ static int ti_sci_scan_clocks_from_fw(struct sci_clk_provider *provider) static int _cmp_sci_clk_list(void *priv, const struct list_head *a, const struct list_head *b) { - struct sci_clk *ca = container_of(a, struct sci_clk, node); - struct sci_clk *cb = container_of(b, struct sci_clk, node); + const struct sci_clk *ca = container_of(a, struct sci_clk, node); + const struct sci_clk *cb = container_of(b, struct sci_clk, node); return _cmp_sci_clk(ca, &cb); } diff --git a/drivers/clk/keystone/syscon-clk.c b/drivers/clk/keystone/syscon-clk.c index c509929da854..ecf180a7949c 100644 --- a/drivers/clk/keystone/syscon-clk.c +++ b/drivers/clk/keystone/syscon-clk.c @@ -129,7 +129,7 @@ static int ti_syscon_gate_clk_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - regmap = regmap_init_mmio(dev, base, &ti_syscon_regmap_cfg); + regmap = devm_regmap_init_mmio(dev, base, &ti_syscon_regmap_cfg); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "failed to get regmap\n"); diff --git a/drivers/clk/samsung/Kconfig b/drivers/clk/samsung/Kconfig index 76a494e95027..70a8b82a0136 100644 --- a/drivers/clk/samsung/Kconfig +++ b/drivers/clk/samsung/Kconfig @@ -95,6 +95,16 @@ config EXYNOS_CLKOUT status of the certains clocks from SoC, but it could also be tied to other devices as an input clock. +config EXYNOS_ACPM_CLK + tristate "Clock driver controlled via ACPM interface" + depends on EXYNOS_ACPM_PROTOCOL || (COMPILE_TEST && !EXYNOS_ACPM_PROTOCOL) + help + This driver provides support for clocks that are controlled by + firmware that implements the ACPM interface. + + This driver uses the ACPM interface to interact with the firmware + providing all the clock controlls. + config TESLA_FSD_COMMON_CLK bool "Tesla FSD clock controller support" if COMPILE_TEST depends on COMMON_CLK_SAMSUNG diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile index ef464f434740..f3657f2e1b98 100644 --- a/drivers/clk/samsung/Makefile +++ b/drivers/clk/samsung/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos990.o obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov9.o obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov920.o obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-gs101.o +obj-$(CONFIG_EXYNOS_ACPM_CLK) += clk-acpm.o obj-$(CONFIG_S3C64XX_COMMON_CLK) += clk-s3c64xx.o obj-$(CONFIG_S5PV210_COMMON_CLK) += clk-s5pv210.o clk-s5pv210-audss.o obj-$(CONFIG_TESLA_FSD_COMMON_CLK) += clk-fsd.o diff --git a/drivers/clk/samsung/clk-acpm.c b/drivers/clk/samsung/clk-acpm.c new file mode 100644 index 000000000000..b90809ce3f88 --- /dev/null +++ b/drivers/clk/samsung/clk-acpm.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Samsung Exynos ACPM protocol based clock driver. + * + * Copyright 2025 Linaro Ltd. + */ + +#include <linux/array_size.h> +#include <linux/clk-provider.h> +#include <linux/container_of.h> +#include <linux/device/devres.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/firmware/samsung/exynos-acpm-protocol.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +struct acpm_clk { + u32 id; + struct clk_hw hw; + unsigned int mbox_chan_id; + const struct acpm_handle *handle; +}; + +struct acpm_clk_variant { + const char *name; +}; + +struct acpm_clk_driver_data { + const struct acpm_clk_variant *clks; + unsigned int nr_clks; + unsigned int mbox_chan_id; +}; + +#define to_acpm_clk(clk) container_of(clk, struct acpm_clk, hw) + +#define ACPM_CLK(cname) \ + { \ + .name = cname, \ + } + +static const struct acpm_clk_variant gs101_acpm_clks[] = { + ACPM_CLK("mif"), + ACPM_CLK("int"), + ACPM_CLK("cpucl0"), + ACPM_CLK("cpucl1"), + ACPM_CLK("cpucl2"), + ACPM_CLK("g3d"), + ACPM_CLK("g3dl2"), + ACPM_CLK("tpu"), + ACPM_CLK("intcam"), + ACPM_CLK("tnr"), + ACPM_CLK("cam"), + ACPM_CLK("mfc"), + ACPM_CLK("disp"), + ACPM_CLK("bo"), +}; + +static const struct acpm_clk_driver_data acpm_clk_gs101 = { + .clks = gs101_acpm_clks, + .nr_clks = ARRAY_SIZE(gs101_acpm_clks), + .mbox_chan_id = 0, +}; + +static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct acpm_clk *clk = to_acpm_clk(hw); + + return clk->handle->ops.dvfs_ops.get_rate(clk->handle, + clk->mbox_chan_id, clk->id); +} + +static int acpm_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + /* + * We can't figure out what rate it will be, so just return the + * rate back to the caller. acpm_clk_recalc_rate() will be called + * after the rate is set and we'll know what rate the clock is + * running at then. + */ + return 0; +} + +static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct acpm_clk *clk = to_acpm_clk(hw); + + return clk->handle->ops.dvfs_ops.set_rate(clk->handle, + clk->mbox_chan_id, clk->id, rate); +} + +static const struct clk_ops acpm_clk_ops = { + .recalc_rate = acpm_clk_recalc_rate, + .determine_rate = acpm_clk_determine_rate, + .set_rate = acpm_clk_set_rate, +}; + +static int acpm_clk_register(struct device *dev, struct acpm_clk *aclk, + const char *name) +{ + struct clk_init_data init = {}; + + init.name = name; + init.ops = &acpm_clk_ops; + aclk->hw.init = &init; + + return devm_clk_hw_register(dev, &aclk->hw); +} + +static int acpm_clk_probe(struct platform_device *pdev) +{ + const struct acpm_handle *acpm_handle; + struct clk_hw_onecell_data *clk_data; + struct clk_hw **hws; + struct device *dev = &pdev->dev; + struct acpm_clk *aclks; + unsigned int mbox_chan_id; + int i, err, count; + + acpm_handle = devm_acpm_get_by_node(dev, dev->parent->of_node); + if (IS_ERR(acpm_handle)) + return dev_err_probe(dev, PTR_ERR(acpm_handle), + "Failed to get acpm handle\n"); + + count = acpm_clk_gs101.nr_clks; + mbox_chan_id = acpm_clk_gs101.mbox_chan_id; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = count; + hws = clk_data->hws; + + aclks = devm_kcalloc(dev, count, sizeof(*aclks), GFP_KERNEL); + if (!aclks) + return -ENOMEM; + + for (i = 0; i < count; i++) { + struct acpm_clk *aclk = &aclks[i]; + + /* + * The code assumes the clock IDs start from zero, + * are sequential and do not have gaps. + */ + aclk->id = i; + aclk->handle = acpm_handle; + aclk->mbox_chan_id = mbox_chan_id; + + hws[i] = &aclk->hw; + + err = acpm_clk_register(dev, aclk, + acpm_clk_gs101.clks[i].name); + if (err) + return dev_err_probe(dev, err, + "Failed to register clock\n"); + } + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, + clk_data); +} + +static const struct platform_device_id acpm_clk_id[] = { + { "gs101-acpm-clk" }, + {} +}; +MODULE_DEVICE_TABLE(platform, acpm_clk_id); + +static struct platform_driver acpm_clk_driver = { + .driver = { + .name = "acpm-clocks", + }, + .probe = acpm_clk_probe, + .id_table = acpm_clk_id, +}; +module_platform_driver(acpm_clk_driver); + +MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>"); +MODULE_DESCRIPTION("Samsung Exynos ACPM clock driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/samsung/clk-exynos-clkout.c b/drivers/clk/samsung/clk-exynos-clkout.c index 5f1a4f5e2e59..5b21025338bd 100644 --- a/drivers/clk/samsung/clk-exynos-clkout.c +++ b/drivers/clk/samsung/clk-exynos-clkout.c @@ -175,6 +175,7 @@ static int exynos_clkout_probe(struct platform_device *pdev) clkout->mux.shift = EXYNOS_CLKOUT_MUX_SHIFT; clkout->mux.lock = &clkout->slock; + clkout->data.num = EXYNOS_CLKOUT_NR_CLKS; clkout->data.hws[0] = clk_hw_register_composite(NULL, "clkout", parent_names, parent_count, &clkout->mux.hw, &clk_mux_ops, NULL, NULL, &clkout->gate.hw, @@ -185,7 +186,6 @@ static int exynos_clkout_probe(struct platform_device *pdev) goto err_unmap; } - clkout->data.num = EXYNOS_CLKOUT_NR_CLKS; ret = of_clk_add_hw_provider(clkout->np, of_clk_hw_onecell_get, &clkout->data); if (ret) goto err_clk_unreg; diff --git a/drivers/clk/samsung/clk-exynosautov920.c b/drivers/clk/samsung/clk-exynosautov920.c index 572b6ace14ac..b90b73c3518f 100644 --- a/drivers/clk/samsung/clk-exynosautov920.c +++ b/drivers/clk/samsung/clk-exynosautov920.c @@ -27,6 +27,8 @@ #define CLKS_NR_HSI0 (CLK_DOUT_HSI0_PCIE_APB + 1) #define CLKS_NR_HSI1 (CLK_MOUT_HSI1_USBDRD + 1) #define CLKS_NR_HSI2 (CLK_DOUT_HSI2_ETHERNET_PTP + 1) +#define CLKS_NR_M2M (CLK_DOUT_M2M_NOCP + 1) +#define CLKS_NR_MFC (CLK_DOUT_MFC_NOCP + 1) /* ---- CMU_TOP ------------------------------------------------------------ */ @@ -1821,6 +1823,88 @@ static const struct samsung_cmu_info hsi2_cmu_info __initconst = { .clk_name = "noc", }; +/* ---- CMU_M2M --------------------------------------------------------- */ + +/* Register Offset definitions for CMU_M2M (0x1a800000) */ +#define PLL_CON0_MUX_CLKCMU_M2M_JPEG_USER 0x600 +#define PLL_CON0_MUX_CLKCMU_M2M_NOC_USER 0x610 +#define CLK_CON_DIV_DIV_CLK_M2M_NOCP 0x1800 + +static const unsigned long m2m_clk_regs[] __initconst = { + PLL_CON0_MUX_CLKCMU_M2M_JPEG_USER, + PLL_CON0_MUX_CLKCMU_M2M_NOC_USER, + CLK_CON_DIV_DIV_CLK_M2M_NOCP, +}; + +/* List of parent clocks for Muxes in CMU_M2M */ +PNAME(mout_clkcmu_m2m_noc_user_p) = { "oscclk", "dout_clkcmu_m2m_noc" }; +PNAME(mout_clkcmu_m2m_jpeg_user_p) = { "oscclk", "dout_clkcmu_m2m_jpeg" }; + +static const struct samsung_mux_clock m2m_mux_clks[] __initconst = { + MUX(CLK_MOUT_M2M_JPEG_USER, "mout_clkcmu_m2m_jpeg_user", + mout_clkcmu_m2m_jpeg_user_p, PLL_CON0_MUX_CLKCMU_M2M_JPEG_USER, 4, 1), + MUX(CLK_MOUT_M2M_NOC_USER, "mout_clkcmu_m2m_noc_user", + mout_clkcmu_m2m_noc_user_p, PLL_CON0_MUX_CLKCMU_M2M_NOC_USER, 4, 1), +}; + +static const struct samsung_div_clock m2m_div_clks[] __initconst = { + DIV(CLK_DOUT_M2M_NOCP, "dout_m2m_nocp", + "mout_clkcmu_m2m_noc_user", CLK_CON_DIV_DIV_CLK_M2M_NOCP, + 0, 3), +}; + +static const struct samsung_cmu_info m2m_cmu_info __initconst = { + .mux_clks = m2m_mux_clks, + .nr_mux_clks = ARRAY_SIZE(m2m_mux_clks), + .div_clks = m2m_div_clks, + .nr_div_clks = ARRAY_SIZE(m2m_div_clks), + .nr_clk_ids = CLKS_NR_M2M, + .clk_regs = m2m_clk_regs, + .nr_clk_regs = ARRAY_SIZE(m2m_clk_regs), + .clk_name = "noc", +}; + +/* ---- CMU_MFC --------------------------------------------------------- */ + +/* Register Offset definitions for CMU_MFC (0x19c00000) */ +#define PLL_CON0_MUX_CLKCMU_MFC_MFC_USER 0x600 +#define PLL_CON0_MUX_CLKCMU_MFC_WFD_USER 0x610 +#define CLK_CON_DIV_DIV_CLK_MFC_NOCP 0x1800 + +static const unsigned long mfc_clk_regs[] __initconst = { + PLL_CON0_MUX_CLKCMU_MFC_MFC_USER, + PLL_CON0_MUX_CLKCMU_MFC_WFD_USER, + CLK_CON_DIV_DIV_CLK_MFC_NOCP, +}; + +/* List of parent clocks for Muxes in CMU_MFC */ +PNAME(mout_clkcmu_mfc_mfc_user_p) = { "oscclk", "dout_clkcmu_mfc_mfc" }; +PNAME(mout_clkcmu_mfc_wfd_user_p) = { "oscclk", "dout_clkcmu_mfc_wfd" }; + +static const struct samsung_mux_clock mfc_mux_clks[] __initconst = { + MUX(CLK_MOUT_MFC_MFC_USER, "mout_clkcmu_mfc_mfc_user", + mout_clkcmu_mfc_mfc_user_p, PLL_CON0_MUX_CLKCMU_MFC_MFC_USER, 4, 1), + MUX(CLK_MOUT_MFC_WFD_USER, "mout_clkcmu_mfc_wfd_user", + mout_clkcmu_mfc_wfd_user_p, PLL_CON0_MUX_CLKCMU_MFC_WFD_USER, 4, 1), +}; + +static const struct samsung_div_clock mfc_div_clks[] __initconst = { + DIV(CLK_DOUT_MFC_NOCP, "dout_mfc_nocp", + "mout_clkcmu_mfc_mfc_user", CLK_CON_DIV_DIV_CLK_MFC_NOCP, + 0, 3), +}; + +static const struct samsung_cmu_info mfc_cmu_info __initconst = { + .mux_clks = mfc_mux_clks, + .nr_mux_clks = ARRAY_SIZE(mfc_mux_clks), + .div_clks = mfc_div_clks, + .nr_div_clks = ARRAY_SIZE(mfc_div_clks), + .nr_clk_ids = CLKS_NR_MFC, + .clk_regs = mfc_clk_regs, + .nr_clk_regs = ARRAY_SIZE(mfc_clk_regs), + .clk_name = "noc", +}; + static int __init exynosautov920_cmu_probe(struct platform_device *pdev) { const struct samsung_cmu_info *info; @@ -1851,6 +1935,12 @@ static const struct of_device_id exynosautov920_cmu_of_match[] = { }, { .compatible = "samsung,exynosautov920-cmu-hsi2", .data = &hsi2_cmu_info, + }, { + .compatible = "samsung,exynosautov920-cmu-m2m", + .data = &m2m_cmu_info, + }, { + .compatible = "samsung,exynosautov920-cmu-mfc", + .data = &mfc_cmu_info, }, { } }; diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c index 7bea7be1d7e4..0a8fc9649ae2 100644 --- a/drivers/clk/samsung/clk-pll.c +++ b/drivers/clk/samsung/clk-pll.c @@ -11,14 +11,12 @@ #include <linux/iopoll.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/timekeeping.h> #include <linux/clk-provider.h> #include <linux/io.h> #include "clk.h" #include "clk-pll.h" -#define PLL_TIMEOUT_US 20000U -#define PLL_TIMEOUT_LOOPS 1000000U +#define PLL_TIMEOUT_LOOPS 20000U struct samsung_clk_pll { struct clk_hw hw; @@ -71,20 +69,11 @@ static int samsung_pll_determine_rate(struct clk_hw *hw, return 0; } -static bool pll_early_timeout = true; - -static int __init samsung_pll_disable_early_timeout(void) -{ - pll_early_timeout = false; - return 0; -} -arch_initcall(samsung_pll_disable_early_timeout); - /* Wait until the PLL is locked */ static int samsung_pll_lock_wait(struct samsung_clk_pll *pll, unsigned int reg_mask) { - int i, ret; + int ret; u32 val; /* @@ -93,25 +82,15 @@ static int samsung_pll_lock_wait(struct samsung_clk_pll *pll, * initialized, another when the timekeeping is suspended. udelay() also * cannot be used when the clocksource is not running on arm64, since * the current timer is used as cycle counter. So a simple busy loop - * is used here in that special cases. The limit of iterations has been - * derived from experimental measurements of various PLLs on multiple - * Exynos SoC variants. Single register read time was usually in range - * 0.4...1.5 us, never less than 0.4 us. + * is used here. + * The limit of iterations has been derived from experimental + * measurements of various PLLs on multiple Exynos SoC variants. Single + * register read time was usually in range 0.4...1.5 us, never less than + * 0.4 us. */ - if (pll_early_timeout || timekeeping_suspended) { - i = PLL_TIMEOUT_LOOPS; - while (i-- > 0) { - if (readl_relaxed(pll->con_reg) & reg_mask) - return 0; - - cpu_relax(); - } - ret = -ETIMEDOUT; - } else { - ret = readl_relaxed_poll_timeout_atomic(pll->con_reg, val, - val & reg_mask, 0, PLL_TIMEOUT_US); - } - + ret = readl_relaxed_poll_timeout_atomic(pll->con_reg, val, + val & reg_mask, 0, + PLL_TIMEOUT_LOOPS); if (ret < 0) pr_err("Could not lock PLL %s\n", clk_hw_get_name(&pll->hw)); diff --git a/drivers/clk/socfpga/Kconfig b/drivers/clk/socfpga/Kconfig index 0cf16b894efb..d88277e2a898 100644 --- a/drivers/clk/socfpga/Kconfig +++ b/drivers/clk/socfpga/Kconfig @@ -13,7 +13,7 @@ config CLK_INTEL_SOCFPGA32 default ARM && ARCH_INTEL_SOCFPGA config CLK_INTEL_SOCFPGA64 - bool "Intel Stratix / Agilex / N5X clock controller support" if COMPILE_TEST && (!ARM64 || !ARCH_INTEL_SOCFPGA) + bool "Intel Stratix / Agilex / N5X / Agilex5 clock controller support" if COMPILE_TEST && (!ARM64 || !ARCH_INTEL_SOCFPGA) default ARM64 && ARCH_INTEL_SOCFPGA endif # CLK_INTEL_SOCFPGA diff --git a/drivers/clk/socfpga/Makefile b/drivers/clk/socfpga/Makefile index e8dfce339c91..a1ea2b988eaf 100644 --- a/drivers/clk/socfpga/Makefile +++ b/drivers/clk/socfpga/Makefile @@ -3,4 +3,4 @@ obj-$(CONFIG_CLK_INTEL_SOCFPGA32) += clk.o clk-gate.o clk-pll.o clk-periph.o \ clk-pll-a10.o clk-periph-a10.o clk-gate-a10.o obj-$(CONFIG_CLK_INTEL_SOCFPGA64) += clk-s10.o \ clk-pll-s10.o clk-periph-s10.o clk-gate-s10.o \ - clk-agilex.o + clk-agilex.o clk-agilex5.o diff --git a/drivers/clk/socfpga/clk-agilex5.c b/drivers/clk/socfpga/clk-agilex5.c new file mode 100644 index 000000000000..f7f0ad884f64 --- /dev/null +++ b/drivers/clk/socfpga/clk-agilex5.c @@ -0,0 +1,561 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022-2024, Intel Corporation + * Copyright (C) 2025, Altera Corporation + */ +#include <linux/slab.h> +#include <linux/clk-provider.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <dt-bindings/clock/intel,agilex5-clkmgr.h> +#include "stratix10-clk.h" +#include "clk.h" + +/* External parent clocks come from DT via fw_name */ +static const char * const boot_pll_parents[] = { + "osc1", + "cb-intosc-hs-div2-clk", +}; + +static const char * const main_pll_parents[] = { + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const periph_pll_parents[] = { + "osc1", + "cb-intosc-hs-div2-clk", +}; + +/* Core free muxes */ +static const char * const core0_free_mux[] = { + "main_pll_c1", + "peri_pll_c0", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const core1_free_mux[] = { + "main_pll_c1", + "peri_pll_c0", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const core2_free_mux[] = { + "main_pll_c0", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const core3_free_mux[] = { + "main_pll_c0", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const dsu_free_mux[] = { + "main_pll_c2", + "peri_pll_c0", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const noc_free_mux[] = { + "main_pll_c3", + "peri_pll_c1", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const emac_ptp_free_mux[] = { + "main_pll_c3", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const emaca_free_mux[] = { + "main_pll_c2", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const emacb_free_mux[] = { + "main_pll_c3", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const gpio_db_free_mux[] = { + "main_pll_c3", + "peri_pll_c1", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const psi_ref_free_mux[] = { + "main_pll_c1", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const usb31_free_mux[] = { + "main_pll_c3", + "peri_pll_c2", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const s2f_user0_free_mux[] = { + "main_pll_c1", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +static const char * const s2f_user1_free_mux[] = { + "main_pll_c1", + "peri_pll_c3", + "osc1", + "cb-intosc-hs-div2-clk", + "f2s-free-clk", +}; + +/* Secondary muxes between free_clk and boot_clk */ +static const char * const core0_mux[] = { + "core0_free_clk", + "boot_clk", +}; + +static const char * const core1_mux[] = { + "core1_free_clk", + "boot_clk", +}; + +static const char * const core2_mux[] = { + "core2_free_clk", + "boot_clk", +}; + +static const char * const core3_mux[] = { + "core3_free_clk", + "boot_clk", +}; + +static const char * const dsu_mux[] = { + "dsu_free_clk", + "boot_clk", +}; + +static const char * const noc_mux[] = { + "noc_free_clk", + "boot_clk", +}; + +static const char * const emac_mux[] = { + "emaca_free_clk", + "emacb_free_clk", + "boot_clk", +}; + +static const char * const s2f_user0_mux[] = { + "s2f_user0_free_clk", + "boot_clk", +}; + +static const char * const s2f_user1_mux[] = { + "s2f_user1_free_clk", + "boot_clk", +}; + +static const char * const psi_mux[] = { + "psi_ref_free_clk", + "boot_clk", +}; + +static const char * const gpio_db_mux[] = { + "gpio_db_free_clk", + "boot_clk", +}; + +static const char * const emac_ptp_mux[] = { + "emac_ptp_free_clk", + "boot_clk", +}; + +static const char * const usb31_mux[] = { + "usb31_free_clk", + "boot_clk", +}; + +static const struct agilex5_pll_clock agilex5_pll_clks[] = { + { + .id = AGILEX5_BOOT_CLK, + .name = "boot_clk", + .parent_names = boot_pll_parents, + .num_parents = ARRAY_SIZE(boot_pll_parents), + .flags = 0, + .offset = 0x0, + }, + { + .id = AGILEX5_MAIN_PLL_CLK, + .name = "main_pll", + .parent_names = main_pll_parents, + .num_parents = ARRAY_SIZE(main_pll_parents), + .flags = 0, + .offset = 0x48, + }, + { + .id = AGILEX5_PERIPH_PLL_CLK, + .name = "periph_pll", + .parent_names = periph_pll_parents, + .num_parents = ARRAY_SIZE(periph_pll_parents), + .flags = 0, + .offset = 0x9C, + }, +}; + +/* Main PLL C0, C1, C2, C3 and Peri PLL C0, C1, C2, C3. With ping-pong counter. */ +static const struct stratix10_perip_c_clock agilex5_main_perip_c_clks[] = { + { AGILEX5_MAIN_PLL_C0_CLK, "main_pll_c0", "main_pll", NULL, 1, 0, + 0x5C }, + { AGILEX5_MAIN_PLL_C1_CLK, "main_pll_c1", "main_pll", NULL, 1, 0, + 0x60 }, + { AGILEX5_MAIN_PLL_C2_CLK, "main_pll_c2", "main_pll", NULL, 1, 0, + 0x64 }, + { AGILEX5_MAIN_PLL_C3_CLK, "main_pll_c3", "main_pll", NULL, 1, 0, + 0x68 }, + { AGILEX5_PERIPH_PLL_C0_CLK, "peri_pll_c0", "periph_pll", NULL, 1, 0, + 0xB0 }, + { AGILEX5_PERIPH_PLL_C1_CLK, "peri_pll_c1", "periph_pll", NULL, 1, 0, + 0xB4 }, + { AGILEX5_PERIPH_PLL_C2_CLK, "peri_pll_c2", "periph_pll", NULL, 1, 0, + 0xB8 }, + { AGILEX5_PERIPH_PLL_C3_CLK, "peri_pll_c3", "periph_pll", NULL, 1, 0, + 0xBC }, +}; + +/* Non-SW clock-gated enabled clocks */ +static const struct agilex5_perip_cnt_clock agilex5_main_perip_cnt_clks[] = { + { AGILEX5_CORE0_FREE_CLK, "core0_free_clk", core0_free_mux, + ARRAY_SIZE(core0_free_mux), 0, 0x0100, 0, 0, 0}, + { AGILEX5_CORE1_FREE_CLK, "core1_free_clk", core1_free_mux, + ARRAY_SIZE(core1_free_mux), 0, 0x0104, 0, 0, 0}, + { AGILEX5_CORE2_FREE_CLK, "core2_free_clk", core2_free_mux, + ARRAY_SIZE(core2_free_mux), 0, 0x010C, 0, 0, 0}, + { AGILEX5_CORE3_FREE_CLK, "core3_free_clk", core3_free_mux, + ARRAY_SIZE(core3_free_mux), 0, 0x0110, 0, 0, 0}, + { AGILEX5_DSU_FREE_CLK, "dsu_free_clk", dsu_free_mux, + ARRAY_SIZE(dsu_free_mux), 0, 0xfc, 0, 0, 0}, + { AGILEX5_NOC_FREE_CLK, "noc_free_clk", noc_free_mux, + ARRAY_SIZE(noc_free_mux), 0, 0x40, 0, 0, 0 }, + { AGILEX5_EMAC_A_FREE_CLK, "emaca_free_clk", emaca_free_mux, + ARRAY_SIZE(emaca_free_mux), 0, 0xD4, 0, 0x88, 0 }, + { AGILEX5_EMAC_B_FREE_CLK, "emacb_free_clk", emacb_free_mux, + ARRAY_SIZE(emacb_free_mux), 0, 0xD8, 0, 0x88, 1 }, + { AGILEX5_EMAC_PTP_FREE_CLK, "emac_ptp_free_clk", emac_ptp_free_mux, + ARRAY_SIZE(emac_ptp_free_mux), 0, 0xDC, 0, 0x88, 2 }, + { AGILEX5_GPIO_DB_FREE_CLK, "gpio_db_free_clk", gpio_db_free_mux, + ARRAY_SIZE(gpio_db_free_mux), 0, 0xE0, 0, 0x88, 3 }, + { AGILEX5_S2F_USER0_FREE_CLK, "s2f_user0_free_clk", s2f_user0_free_mux, + ARRAY_SIZE(s2f_user0_free_mux), 0, 0xE8, 0, 0x30, 2 }, + { AGILEX5_S2F_USER1_FREE_CLK, "s2f_user1_free_clk", s2f_user1_free_mux, + ARRAY_SIZE(s2f_user1_free_mux), 0, 0xEC, 0, 0x88, 5 }, + { AGILEX5_PSI_REF_FREE_CLK, "psi_ref_free_clk", psi_ref_free_mux, + ARRAY_SIZE(psi_ref_free_mux), 0, 0xF0, 0, 0x88, 6 }, + { AGILEX5_USB31_FREE_CLK, "usb31_free_clk", usb31_free_mux, + ARRAY_SIZE(usb31_free_mux), 0, 0xF8, 0, 0x88, 7}, +}; + +static const char * const cs_pdbg_parents[] = { "cs_at_clk" }; +static const char * const usb31_bus_clk_early_parents[] = { "l4_main_clk" }; +static const char * const l4_mp_clk_parent[] = { "l4_mp_clk" }; +static const char * const l4_sp_clk_parent[] = { "l4_sp_clk" }; +static const char * const dfi_clk_parent[] = { "dfi_clk" }; + +/* SW Clock gate enabled clocks */ +static const struct agilex5_gate_clock agilex5_gate_clks[] = { + { AGILEX5_CORE0_CLK, "core0_clk", core0_mux, + ARRAY_SIZE(core0_mux), 0, 0x24, 8, 0, 0, 0, 0x30, 5, 0 }, + { AGILEX5_CORE1_CLK, "core1_clk", core1_mux, + ARRAY_SIZE(core1_mux), 0, 0x24, 9, 0, 0, 0, 0x30, 5, 0 }, + { AGILEX5_CORE2_CLK, "core2_clk", core2_mux, + ARRAY_SIZE(core2_mux), 0, 0x24, 10, 0, 0, 0, 0x30, 6, 0 }, + { AGILEX5_CORE3_CLK, "core3_clk", core3_mux, + ARRAY_SIZE(core3_mux), 0, 0x24, 11, 0, 0, 0, 0x30, 7, 0 }, + { AGILEX5_MPU_CLK, "dsu_clk", dsu_mux, ARRAY_SIZE(dsu_mux), 0, 0, 0, + 0, 0, 0, 0x34, 4, 0 }, + { AGILEX5_MPU_PERIPH_CLK, "mpu_periph_clk", dsu_mux, + ARRAY_SIZE(dsu_mux), 0, 0, 0, 0x44, 20, 2, 0x34, 4, 0 }, + { AGILEX5_MPU_CCU_CLK, "mpu_ccu_clk", dsu_mux, + ARRAY_SIZE(dsu_mux), 0, 0, 0, 0x44, 18, 2, 0x34, 4, 0 }, + { AGILEX5_L4_MAIN_CLK, "l4_main_clk", noc_mux, ARRAY_SIZE(noc_mux), + CLK_IS_CRITICAL, 0x24, 1, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_L4_MP_CLK, "l4_mp_clk", noc_mux, ARRAY_SIZE(noc_mux), 0, + 0x24, 2, 0x44, 4, 2, 0x30, 1, 0 }, + { AGILEX5_L4_SYS_FREE_CLK, "l4_sys_free_clk", noc_mux, + ARRAY_SIZE(noc_mux), 0, 0, 0, 0x44, 2, 2, 0x30, 1, 0 }, + { AGILEX5_L4_SP_CLK, "l4_sp_clk", noc_mux, ARRAY_SIZE(noc_mux), + CLK_IS_CRITICAL, 0x24, 3, 0x44, 6, 2, 0x30, 1, 0 }, + + /* Core sight clocks*/ + { AGILEX5_CS_AT_CLK, "cs_at_clk", noc_mux, ARRAY_SIZE(noc_mux), 0, + 0x24, 4, 0x44, 24, 2, 0x30, 1, 0 }, + { AGILEX5_CS_TRACE_CLK, "cs_trace_clk", noc_mux, + ARRAY_SIZE(noc_mux), 0, 0x24, 4, 0x44, 26, 2, 0x30, 1, 0 }, + { AGILEX5_CS_PDBG_CLK, "cs_pdbg_clk", cs_pdbg_parents, 1, 0, 0x24, 4, + 0x44, 28, 1, 0, 0, 0 }, + + /* Main Peripheral PLL1 Begin */ + { AGILEX5_EMAC0_CLK, "emac0_clk", emac_mux, ARRAY_SIZE(emac_mux), + 0, 0x7C, 0, 0, 0, 0, 0x94, 26, 0 }, + { AGILEX5_EMAC1_CLK, "emac1_clk", emac_mux, ARRAY_SIZE(emac_mux), + 0, 0x7C, 1, 0, 0, 0, 0x94, 27, 0 }, + { AGILEX5_EMAC2_CLK, "emac2_clk", emac_mux, ARRAY_SIZE(emac_mux), + 0, 0x7C, 2, 0, 0, 0, 0x94, 28, 0 }, + { AGILEX5_EMAC_PTP_CLK, "emac_ptp_clk", emac_ptp_mux, + ARRAY_SIZE(emac_ptp_mux), 0, 0x7C, 3, 0, 0, 0, 0x88, 2, 0 }, + { AGILEX5_GPIO_DB_CLK, "gpio_db_clk", gpio_db_mux, + ARRAY_SIZE(gpio_db_mux), 0, 0x7C, 4, 0x98, 0, 16, 0x88, 3, 1 }, + /* Main Peripheral PLL1 End */ + + /* Peripheral clocks */ + { AGILEX5_S2F_USER0_CLK, "s2f_user0_clk", s2f_user0_mux, + ARRAY_SIZE(s2f_user0_mux), 0, 0x24, 6, 0, 0, 0, 0x30, 2, 0 }, + { AGILEX5_S2F_USER1_CLK, "s2f_user1_clk", s2f_user1_mux, + ARRAY_SIZE(s2f_user1_mux), 0, 0x7C, 6, 0, 0, 0, 0x88, 5, 0 }, + { AGILEX5_PSI_REF_CLK, "psi_ref_clk", psi_mux, + ARRAY_SIZE(psi_mux), 0, 0x7C, 7, 0, 0, 0, 0x88, 6, 0 }, + { AGILEX5_USB31_SUSPEND_CLK, "usb31_suspend_clk", usb31_mux, + ARRAY_SIZE(usb31_mux), 0, 0x7C, 25, 0, 0, 0, 0x88, 7, 0 }, + { AGILEX5_USB31_BUS_CLK_EARLY, "usb31_bus_clk_early", usb31_bus_clk_early_parents, + 1, 0, 0x7C, 25, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_USB2OTG_HCLK, "usb2otg_hclk", l4_mp_clk_parent, 1, 0, 0x7C, + 8, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPIM_0_CLK, "spim_0_clk", l4_mp_clk_parent, 1, 0, 0x7C, 9, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPIM_1_CLK, "spim_1_clk", l4_mp_clk_parent, 1, 0, 0x7C, 11, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPIS_0_CLK, "spis_0_clk", l4_sp_clk_parent, 1, 0, 0x7C, 12, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPIS_1_CLK, "spis_1_clk", l4_sp_clk_parent, 1, 0, 0x7C, 13, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_DMA_CORE_CLK, "dma_core_clk", l4_mp_clk_parent, 1, 0, 0x7C, + 14, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_DMA_HS_CLK, "dma_hs_clk", l4_mp_clk_parent, 1, 0, 0x7C, 14, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I3C_0_CORE_CLK, "i3c_0_core_clk", l4_mp_clk_parent, 1, 0, + 0x7C, 18, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I3C_1_CORE_CLK, "i3c_1_core_clk", l4_mp_clk_parent, 1, 0, + 0x7C, 19, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I2C_0_PCLK, "i2c_0_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 15, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I2C_1_PCLK, "i2c_1_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 16, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I2C_EMAC0_PCLK, "i2c_emac0_pclk", l4_sp_clk_parent, 1, 0, + 0x7C, 17, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I2C_EMAC1_PCLK, "i2c_emac1_pclk", l4_sp_clk_parent, 1, 0, + 0x7C, 22, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_I2C_EMAC2_PCLK, "i2c_emac2_pclk", l4_sp_clk_parent, 1, 0, + 0x7C, 27, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_UART_0_PCLK, "uart_0_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 20, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_UART_1_PCLK, "uart_1_pclk", l4_sp_clk_parent, 1, 0, 0x7C, 21, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPTIMER_0_PCLK, "sptimer_0_pclk", l4_sp_clk_parent, 1, 0, + 0x7C, 23, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SPTIMER_1_PCLK, "sptimer_1_pclk", l4_sp_clk_parent, 1, 0, + 0x7C, 24, 0, 0, 0, 0, 0, 0 }, + + /*NAND, SD/MMC and SoftPHY overall clocking*/ + { AGILEX5_DFI_CLK, "dfi_clk", l4_mp_clk_parent, 1, 0, 0, 0, 0x44, 16, + 2, 0, 0, 0 }, + { AGILEX5_NAND_NF_CLK, "nand_nf_clk", dfi_clk_parent, 1, 0, 0x7C, 10, + 0, 0, 0, 0, 0, 0 }, + { AGILEX5_NAND_BCH_CLK, "nand_bch_clk", l4_mp_clk_parent, 1, 0, 0x7C, + 10, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SDMMC_SDPHY_REG_CLK, "sdmmc_sdphy_reg_clk", l4_mp_clk_parent, + 1, 0, 0x7C, 5, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SDMCLK, "sdmclk", dfi_clk_parent, 1, 0, 0x7C, 5, 0, 0, 0, + 0, 0, 0 }, + { AGILEX5_SOFTPHY_REG_PCLK, "softphy_reg_pclk", l4_mp_clk_parent, 1, 0, + 0x7C, 26, 0, 0, 0, 0, 0, 0 }, + { AGILEX5_SOFTPHY_PHY_CLK, "softphy_phy_clk", l4_mp_clk_parent, 1, 0, + 0x7C, 26, 0x44, 16, 2, 0, 0, 0 }, + { AGILEX5_SOFTPHY_CTRL_CLK, "softphy_ctrl_clk", dfi_clk_parent, 1, 0, + 0x7C, 26, 0, 0, 0, 0, 0, 0 }, +}; + +static int +agilex5_clk_register_c_perip(const struct stratix10_perip_c_clock *clks, + int nums, struct stratix10_clock_data *data) +{ + struct clk_hw *hw_clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + hw_clk = s10_register_periph(&clks[i], base); + if (IS_ERR(hw_clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + clks[i].name); + continue; + } + data->clk_data.hws[clks[i].id] = hw_clk; + } + return 0; +} + +static int +agilex5_clk_register_cnt_perip(const struct agilex5_perip_cnt_clock *clks, + int nums, struct stratix10_clock_data *data) +{ + struct clk_hw *hw_clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + hw_clk = agilex5_register_cnt_periph(&clks[i], base); + if (IS_ERR(hw_clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + clks[i].name); + continue; + } + data->clk_data.hws[clks[i].id] = hw_clk; + } + + return 0; +} + +static int agilex5_clk_register_gate(const struct agilex5_gate_clock *clks, + int nums, + struct stratix10_clock_data *data) +{ + struct clk_hw *hw_clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + hw_clk = agilex5_register_gate(&clks[i], base); + if (IS_ERR(hw_clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + clks[i].name); + continue; + } + data->clk_data.hws[clks[i].id] = hw_clk; + } + + return 0; +} + +static int agilex5_clk_register_pll(const struct agilex5_pll_clock *clks, + int nums, struct stratix10_clock_data *data) +{ + struct clk_hw *hw_clk; + void __iomem *base = data->base; + int i; + + for (i = 0; i < nums; i++) { + hw_clk = agilex5_register_pll(&clks[i], base); + if (IS_ERR(hw_clk)) { + pr_err("%s: failed to register clock %s\n", __func__, + clks[i].name); + continue; + } + data->clk_data.hws[clks[i].id] = hw_clk; + } + + return 0; +} + +static int agilex5_clkmgr_init(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct stratix10_clock_data *clk_data; + void __iomem *base; + int i, num_clks; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + num_clks = AGILEX5_NUM_CLKS; + + clk_data = devm_kzalloc(dev, struct_size(clk_data, clk_data.hws, + num_clks), GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->base = base; + clk_data->clk_data.num = num_clks; + + for (i = 0; i < num_clks; i++) + clk_data->clk_data.hws[i] = ERR_PTR(-ENOENT); + + agilex5_clk_register_pll(agilex5_pll_clks, ARRAY_SIZE(agilex5_pll_clks), + clk_data); + + /* mainPLL C0, C1, C2, C3 and periph PLL C0, C1, C2, C3*/ + agilex5_clk_register_c_perip(agilex5_main_perip_c_clks, + ARRAY_SIZE(agilex5_main_perip_c_clks), + clk_data); + + agilex5_clk_register_cnt_perip(agilex5_main_perip_cnt_clks, + ARRAY_SIZE(agilex5_main_perip_cnt_clks), + clk_data); + + agilex5_clk_register_gate(agilex5_gate_clks, + ARRAY_SIZE(agilex5_gate_clks), clk_data); + + of_clk_add_hw_provider(np, of_clk_hw_onecell_get, &clk_data->clk_data); + return 0; +} + +static int agilex5_clkmgr_probe(struct platform_device *pdev) +{ + int (*probe_func)(struct platform_device *init_func); + + probe_func = of_device_get_match_data(&pdev->dev); + if (!probe_func) + return -ENODEV; + return probe_func(pdev); +} + +static const struct of_device_id agilex5_clkmgr_match_table[] = { + { .compatible = "intel,agilex5-clkmgr", .data = agilex5_clkmgr_init }, + {} +}; + +static struct platform_driver agilex5_clkmgr_driver = { + .probe = agilex5_clkmgr_probe, + .driver = { + .name = "agilex5-clkmgr", + .suppress_bind_attrs = true, + .of_match_table = agilex5_clkmgr_match_table, + }, +}; + +static int __init agilex5_clk_init(void) +{ + return platform_driver_register(&agilex5_clkmgr_driver); +} +core_initcall(agilex5_clk_init); diff --git a/drivers/clk/socfpga/clk-gate-s10.c b/drivers/clk/socfpga/clk-gate-s10.c index 3930d922efb4..dce3ef137bf3 100644 --- a/drivers/clk/socfpga/clk-gate-s10.c +++ b/drivers/clk/socfpga/clk-gate-s10.c @@ -239,3 +239,56 @@ struct clk_hw *agilex_register_gate(const struct stratix10_gate_clock *clks, voi } return hw_clk; } + +struct clk_hw *agilex5_register_gate(const struct agilex5_gate_clock *clks, void __iomem *regbase) +{ + struct clk_hw *hw_clk; + struct socfpga_gate_clk *socfpga_clk; + struct clk_init_data init; + int ret; + + socfpga_clk = kzalloc(sizeof(*socfpga_clk), GFP_KERNEL); + if (!socfpga_clk) + return NULL; + + socfpga_clk->hw.reg = regbase + clks->gate_reg; + socfpga_clk->hw.bit_idx = clks->gate_idx; + + gateclk_ops.enable = clk_gate_ops.enable; + gateclk_ops.disable = clk_gate_ops.disable; + + socfpga_clk->fixed_div = clks->fixed_div; + + if (clks->div_reg) + socfpga_clk->div_reg = regbase + clks->div_reg; + else + socfpga_clk->div_reg = NULL; + + socfpga_clk->width = clks->div_width; + socfpga_clk->shift = clks->div_offset; + + if (clks->bypass_reg) + socfpga_clk->bypass_reg = regbase + clks->bypass_reg; + else + socfpga_clk->bypass_reg = NULL; + socfpga_clk->bypass_shift = clks->bypass_shift; + + if (streq(clks->name, "cs_pdbg_clk")) + init.ops = &dbgclk_ops; + else + init.ops = &agilex_gateclk_ops; + + init.name = clks->name; + init.flags = clks->flags; + init.num_parents = clks->num_parents; + init.parent_names = clks->parent_names; + socfpga_clk->hw.hw.init = &init; + hw_clk = &socfpga_clk->hw.hw; + + ret = clk_hw_register(NULL, &socfpga_clk->hw.hw); + if (ret) { + kfree(socfpga_clk); + return ERR_PTR(ret); + } + return hw_clk; +} diff --git a/drivers/clk/socfpga/clk-periph-s10.c b/drivers/clk/socfpga/clk-periph-s10.c index f5c1ca42b668..f12ca43ffe7c 100644 --- a/drivers/clk/socfpga/clk-periph-s10.c +++ b/drivers/clk/socfpga/clk-periph-s10.c @@ -214,3 +214,44 @@ struct clk_hw *s10_register_cnt_periph(const struct stratix10_perip_cnt_clock *c } return hw_clk; } + +struct clk_hw *agilex5_register_cnt_periph(const struct agilex5_perip_cnt_clock *clks, + void __iomem *regbase) +{ + struct clk_hw *hw_clk; + struct socfpga_periph_clk *periph_clk; + struct clk_init_data init; + const char *name = clks->name; + int ret; + + periph_clk = kzalloc(sizeof(*periph_clk), GFP_KERNEL); + if (WARN_ON(!periph_clk)) + return NULL; + + if (clks->offset) + periph_clk->hw.reg = regbase + clks->offset; + else + periph_clk->hw.reg = NULL; + + if (clks->bypass_reg) + periph_clk->bypass_reg = regbase + clks->bypass_reg; + else + periph_clk->bypass_reg = NULL; + periph_clk->bypass_shift = clks->bypass_shift; + periph_clk->fixed_div = clks->fixed_divider; + + init.name = name; + init.ops = &peri_cnt_clk_ops; + init.flags = clks->flags; + init.num_parents = clks->num_parents; + init.parent_names = clks->parent_names; + periph_clk->hw.hw.init = &init; + hw_clk = &periph_clk->hw.hw; + + ret = clk_hw_register(NULL, hw_clk); + if (ret) { + kfree(periph_clk); + return ERR_PTR(ret); + } + return hw_clk; +} diff --git a/drivers/clk/socfpga/clk-pll-s10.c b/drivers/clk/socfpga/clk-pll-s10.c index a88c212bda12..1be92827cd93 100644 --- a/drivers/clk/socfpga/clk-pll-s10.c +++ b/drivers/clk/socfpga/clk-pll-s10.c @@ -304,3 +304,39 @@ struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks, } return hw_clk; } + +struct clk_hw *agilex5_register_pll(const struct agilex5_pll_clock *clks, + void __iomem *reg) +{ + struct clk_hw *hw_clk; + struct socfpga_pll *pll_clk; + struct clk_init_data init; + const char *name = clks->name; + int ret; + + pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL); + if (WARN_ON(!pll_clk)) + return NULL; + + pll_clk->hw.reg = reg + clks->offset; + + if (streq(name, SOCFPGA_BOOT_CLK)) + init.ops = &clk_boot_ops; + else + init.ops = &agilex_clk_pll_ops; + + init.name = name; + init.flags = clks->flags; + init.num_parents = clks->num_parents; + init.parent_names = clks->parent_names; + pll_clk->hw.hw.init = &init; + pll_clk->hw.bit_idx = SOCFPGA_PLL_POWER; + hw_clk = &pll_clk->hw.hw; + + ret = clk_hw_register(NULL, hw_clk); + if (ret) { + kfree(pll_clk); + return ERR_PTR(ret); + } + return hw_clk; +} diff --git a/drivers/clk/socfpga/stratix10-clk.h b/drivers/clk/socfpga/stratix10-clk.h index 83fe4eb3133c..d1fe4578b3e0 100644 --- a/drivers/clk/socfpga/stratix10-clk.h +++ b/drivers/clk/socfpga/stratix10-clk.h @@ -73,12 +73,55 @@ struct stratix10_gate_clock { u8 fixed_div; }; +struct agilex5_pll_clock { + unsigned int id; + const char *name; + const char * const *parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; +}; + +struct agilex5_perip_cnt_clock { + unsigned int id; + const char *name; + const char * const *parent_names; + u8 num_parents; + unsigned long flags; + unsigned long offset; + u8 fixed_divider; + unsigned long bypass_reg; + unsigned long bypass_shift; +}; + +struct agilex5_gate_clock { + unsigned int id; + const char *name; + const char * const *parent_names; + u8 num_parents; + unsigned long flags; + unsigned long gate_reg; + u8 gate_idx; + unsigned long div_reg; + u8 div_offset; + u8 div_width; + unsigned long bypass_reg; + u8 bypass_shift; + u8 fixed_div; +}; + struct clk_hw *s10_register_pll(const struct stratix10_pll_clock *clks, void __iomem *reg); struct clk_hw *agilex_register_pll(const struct stratix10_pll_clock *clks, void __iomem *reg); struct clk_hw *n5x_register_pll(const struct stratix10_pll_clock *clks, void __iomem *reg); +struct clk_hw *agilex5_register_pll(const struct agilex5_pll_clock *clks, + void __iomem *reg); +struct clk_hw *agilex5_register_cnt_periph(const struct agilex5_perip_cnt_clock *clks, + void __iomem *regbase); +struct clk_hw *agilex5_register_gate(const struct agilex5_gate_clock *clks, + void __iomem *regbase); struct clk_hw *s10_register_periph(const struct stratix10_perip_c_clock *clks, void __iomem *reg); struct clk_hw *n5x_register_periph(const struct n5x_perip_c_clock *clks, diff --git a/drivers/clk/spacemit/ccu-k1.c b/drivers/clk/spacemit/ccu-k1.c index f5a9fe6ba185..4761bc1e3b6e 100644 --- a/drivers/clk/spacemit/ccu-k1.c +++ b/drivers/clk/spacemit/ccu-k1.c @@ -1018,6 +1018,8 @@ static int spacemit_ccu_register(struct device *dev, if (!clk_data) return -ENOMEM; + clk_data->num = data->num; + for (i = 0; i < data->num; i++) { struct clk_hw *hw = data->hws[i]; struct ccu_common *common; @@ -1044,8 +1046,6 @@ static int spacemit_ccu_register(struct device *dev, clk_data->hws[i] = hw; } - clk_data->num = data->num; - ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, clk_data); if (ret) dev_err(dev, "failed to add clock hardware provider (%d)\n", ret); diff --git a/drivers/clk/spacemit/ccu_mix.h b/drivers/clk/spacemit/ccu_mix.h index 54d40cd39b27..c406508e3504 100644 --- a/drivers/clk/spacemit/ccu_mix.h +++ b/drivers/clk/spacemit/ccu_mix.h @@ -220,4 +220,4 @@ extern const struct clk_ops spacemit_ccu_div_gate_ops; extern const struct clk_ops spacemit_ccu_mux_gate_ops; extern const struct clk_ops spacemit_ccu_mux_div_ops; extern const struct clk_ops spacemit_ccu_mux_div_gate_ops; -#endif /* _CCU_DIV_H_ */ +#endif /* _CCU_MIX_H_ */ diff --git a/drivers/clk/sprd/sc9860-clk.c b/drivers/clk/sprd/sc9860-clk.c index cc5ed2dd8267..d7fe924fbe97 100644 --- a/drivers/clk/sprd/sc9860-clk.c +++ b/drivers/clk/sprd/sc9860-clk.c @@ -2021,17 +2021,13 @@ MODULE_DEVICE_TABLE(of, sprd_sc9860_clk_ids); static int sc9860_clk_probe(struct platform_device *pdev) { - const struct of_device_id *match; const struct sprd_clk_desc *desc; int ret; - match = of_match_node(sprd_sc9860_clk_ids, pdev->dev.of_node); - if (!match) { - pr_err("%s: of_match_node() failed", __func__); + desc = device_get_match_data(&pdev->dev); + if (!desc) return -ENODEV; - } - desc = match->data; ret = sprd_clk_regmap_init(pdev, desc); if (ret) return ret; diff --git a/drivers/firmware/samsung/Makefile b/drivers/firmware/samsung/Makefile index 7b4c9f6f34f5..80d4f89b33a9 100644 --- a/drivers/firmware/samsung/Makefile +++ b/drivers/firmware/samsung/Makefile @@ -1,4 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -acpm-protocol-objs := exynos-acpm.o exynos-acpm-pmic.o +acpm-protocol-objs := exynos-acpm.o +acpm-protocol-objs += exynos-acpm-pmic.o +acpm-protocol-objs += exynos-acpm-dvfs.o obj-$(CONFIG_EXYNOS_ACPM_PROTOCOL) += acpm-protocol.o diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.c b/drivers/firmware/samsung/exynos-acpm-dvfs.c new file mode 100644 index 000000000000..1c5b2b143bcc --- /dev/null +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2020 Samsung Electronics Co., Ltd. + * Copyright 2020 Google LLC. + * Copyright 2025 Linaro Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/firmware/samsung/exynos-acpm-protocol.h> +#include <linux/ktime.h> +#include <linux/types.h> +#include <linux/units.h> + +#include "exynos-acpm.h" +#include "exynos-acpm-dvfs.h" + +#define ACPM_DVFS_ID GENMASK(11, 0) +#define ACPM_DVFS_REQ_TYPE GENMASK(15, 0) + +#define ACPM_DVFS_FREQ_REQ 0 +#define ACPM_DVFS_FREQ_GET 1 + +static void acpm_dvfs_set_xfer(struct acpm_xfer *xfer, u32 *cmd, size_t cmdlen, + unsigned int acpm_chan_id, bool response) +{ + xfer->acpm_chan_id = acpm_chan_id; + xfer->txd = cmd; + xfer->txlen = cmdlen; + + if (response) { + xfer->rxd = cmd; + xfer->rxlen = cmdlen; + } +} + +static void acpm_dvfs_init_set_rate_cmd(u32 cmd[4], unsigned int clk_id, + unsigned long rate) +{ + cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); + cmd[1] = rate / HZ_PER_KHZ; + cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_REQ); + cmd[3] = ktime_to_ms(ktime_get()); +} + +int acpm_dvfs_set_rate(const struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int clk_id, + unsigned long rate) +{ + struct acpm_xfer xfer = {0}; + u32 cmd[4]; + + acpm_dvfs_init_set_rate_cmd(cmd, clk_id, rate); + acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, false); + + return acpm_do_xfer(handle, &xfer); +} + +static void acpm_dvfs_init_get_rate_cmd(u32 cmd[4], unsigned int clk_id) +{ + cmd[0] = FIELD_PREP(ACPM_DVFS_ID, clk_id); + cmd[2] = FIELD_PREP(ACPM_DVFS_REQ_TYPE, ACPM_DVFS_FREQ_GET); + cmd[3] = ktime_to_ms(ktime_get()); +} + +unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int clk_id) +{ + struct acpm_xfer xfer; + unsigned int cmd[4] = {0}; + int ret; + + acpm_dvfs_init_get_rate_cmd(cmd, clk_id); + acpm_dvfs_set_xfer(&xfer, cmd, sizeof(cmd), acpm_chan_id, true); + + ret = acpm_do_xfer(handle, &xfer); + if (ret) + return 0; + + return xfer.rxd[1] * HZ_PER_KHZ; +} diff --git a/drivers/firmware/samsung/exynos-acpm-dvfs.h b/drivers/firmware/samsung/exynos-acpm-dvfs.h new file mode 100644 index 000000000000..9f2778e649c9 --- /dev/null +++ b/drivers/firmware/samsung/exynos-acpm-dvfs.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright 2020 Samsung Electronics Co., Ltd. + * Copyright 2020 Google LLC. + * Copyright 2025 Linaro Ltd. + */ +#ifndef __EXYNOS_ACPM_DVFS_H__ +#define __EXYNOS_ACPM_DVFS_H__ + +#include <linux/types.h> + +struct acpm_handle; + +int acpm_dvfs_set_rate(const struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int id, + unsigned long rate); +unsigned long acpm_dvfs_get_rate(const struct acpm_handle *handle, + unsigned int acpm_chan_id, + unsigned int clk_id); + +#endif /* __EXYNOS_ACPM_DVFS_H__ */ diff --git a/drivers/firmware/samsung/exynos-acpm.c b/drivers/firmware/samsung/exynos-acpm.c index 3a69fe3234c7..0cb269c70460 100644 --- a/drivers/firmware/samsung/exynos-acpm.c +++ b/drivers/firmware/samsung/exynos-acpm.c @@ -29,6 +29,7 @@ #include <linux/types.h> #include "exynos-acpm.h" +#include "exynos-acpm-dvfs.h" #include "exynos-acpm-pmic.h" #define ACPM_PROTOCOL_SEQNUM GENMASK(21, 16) @@ -176,9 +177,11 @@ struct acpm_info { /** * struct acpm_match_data - of_device_id data. * @initdata_base: offset in SRAM where the channels configuration resides. + * @acpm_clk_dev_name: base name for the ACPM clocks device that we're registering. */ struct acpm_match_data { loff_t initdata_base; + const char *acpm_clk_dev_name; }; #define client_to_acpm_chan(c) container_of(c, struct acpm_chan, cl) @@ -590,8 +593,12 @@ static int acpm_channels_init(struct acpm_info *acpm) */ static void acpm_setup_ops(struct acpm_info *acpm) { + struct acpm_dvfs_ops *dvfs_ops = &acpm->handle.ops.dvfs_ops; struct acpm_pmic_ops *pmic_ops = &acpm->handle.ops.pmic_ops; + dvfs_ops->set_rate = acpm_dvfs_set_rate; + dvfs_ops->get_rate = acpm_dvfs_get_rate; + pmic_ops->read_reg = acpm_pmic_read_reg; pmic_ops->bulk_read = acpm_pmic_bulk_read; pmic_ops->write_reg = acpm_pmic_write_reg; @@ -599,9 +606,15 @@ static void acpm_setup_ops(struct acpm_info *acpm) pmic_ops->update_reg = acpm_pmic_update_reg; } +static void acpm_clk_pdev_unregister(void *data) +{ + platform_device_unregister(data); +} + static int acpm_probe(struct platform_device *pdev) { const struct acpm_match_data *match_data; + struct platform_device *acpm_clk_pdev; struct device *dev = &pdev->dev; struct device_node *shmem; struct acpm_info *acpm; @@ -642,6 +655,18 @@ static int acpm_probe(struct platform_device *pdev) platform_set_drvdata(pdev, acpm); + acpm_clk_pdev = platform_device_register_data(dev, + match_data->acpm_clk_dev_name, + PLATFORM_DEVID_NONE, NULL, 0); + if (IS_ERR(acpm_clk_pdev)) + return dev_err_probe(dev, PTR_ERR(acpm_clk_pdev), + "Failed to register ACPM clocks device.\n"); + + ret = devm_add_action_or_reset(dev, acpm_clk_pdev_unregister, + acpm_clk_pdev); + if (ret) + return dev_err_probe(dev, ret, "Failed to add devm action.\n"); + return devm_of_platform_populate(dev); } @@ -741,6 +766,7 @@ EXPORT_SYMBOL_GPL(devm_acpm_get_by_node); static const struct acpm_match_data acpm_gs101 = { .initdata_base = ACPM_GS101_INITDATA_BASE, + .acpm_clk_dev_name = "gs101-acpm-clk", }; static const struct of_device_id acpm_match[] = { diff --git a/include/dt-bindings/clock/google,gs101-acpm.h b/include/dt-bindings/clock/google,gs101-acpm.h new file mode 100644 index 000000000000..e2ba89e09fa6 --- /dev/null +++ b/include/dt-bindings/clock/google,gs101-acpm.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright 2025 Linaro Ltd. + * + * Device Tree binding constants for Google gs101 ACPM clock controller. + */ + +#ifndef _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H +#define _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H + +#define GS101_CLK_ACPM_DVFS_MIF 0 +#define GS101_CLK_ACPM_DVFS_INT 1 +#define GS101_CLK_ACPM_DVFS_CPUCL0 2 +#define GS101_CLK_ACPM_DVFS_CPUCL1 3 +#define GS101_CLK_ACPM_DVFS_CPUCL2 4 +#define GS101_CLK_ACPM_DVFS_G3D 5 +#define GS101_CLK_ACPM_DVFS_G3DL2 6 +#define GS101_CLK_ACPM_DVFS_TPU 7 +#define GS101_CLK_ACPM_DVFS_INTCAM 8 +#define GS101_CLK_ACPM_DVFS_TNR 9 +#define GS101_CLK_ACPM_DVFS_CAM 10 +#define GS101_CLK_ACPM_DVFS_MFC 11 +#define GS101_CLK_ACPM_DVFS_DISP 12 +#define GS101_CLK_ACPM_DVFS_BO 13 + +#endif /* _DT_BINDINGS_CLOCK_GOOGLE_GS101_ACPM_H */ diff --git a/include/dt-bindings/clock/samsung,exynosautov920.h b/include/dt-bindings/clock/samsung,exynosautov920.h index 93e6233d1358..970d05167fc6 100644 --- a/include/dt-bindings/clock/samsung,exynosautov920.h +++ b/include/dt-bindings/clock/samsung,exynosautov920.h @@ -295,4 +295,14 @@ #define CLK_DOUT_HSI2_ETHERNET 6 #define CLK_DOUT_HSI2_ETHERNET_PTP 7 +/* CMU_M2M */ +#define CLK_MOUT_M2M_JPEG_USER 1 +#define CLK_MOUT_M2M_NOC_USER 2 +#define CLK_DOUT_M2M_NOCP 3 + +/* CMU_MFC */ +#define CLK_MOUT_MFC_MFC_USER 1 +#define CLK_MOUT_MFC_WFD_USER 2 +#define CLK_DOUT_MFC_NOCP 3 + #endif /* _DT_BINDINGS_CLOCK_EXYNOSAUTOV920_H */ diff --git a/include/dt-bindings/reset/airoha,en7523-reset.h b/include/dt-bindings/reset/airoha,en7523-reset.h new file mode 100644 index 000000000000..211e8a23a21c --- /dev/null +++ b/include/dt-bindings/reset/airoha,en7523-reset.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2024 iopsys Software Solutions AB. + * Copyright (C) 2025 Genexis AB. + * + * Author: Mikhail Kshevetskiy <mikhail.kshevetskiy@iopsys.eu> + * + * based on + * include/dt-bindings/reset/airoha,en7581-reset.h + * by Lorenzo Bianconi <lorenzo@kernel.org> + */ + +#ifndef __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ +#define __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ + +/* RST_CTRL2 */ +#define EN7523_XPON_PHY_RST 0 +#define EN7523_XSI_MAC_RST 1 +#define EN7523_XSI_PHY_RST 2 +#define EN7523_NPU_RST 3 +#define EN7523_I2S_RST 4 +#define EN7523_TRNG_RST 5 +#define EN7523_TRNG_MSTART_RST 6 +#define EN7523_DUAL_HSI0_RST 7 +#define EN7523_DUAL_HSI1_RST 8 +#define EN7523_HSI_RST 9 +#define EN7523_DUAL_HSI0_MAC_RST 10 +#define EN7523_DUAL_HSI1_MAC_RST 11 +#define EN7523_HSI_MAC_RST 12 +#define EN7523_WDMA_RST 13 +#define EN7523_WOE0_RST 14 +#define EN7523_WOE1_RST 15 +#define EN7523_HSDMA_RST 16 +#define EN7523_I2C2RBUS_RST 17 +#define EN7523_TDMA_RST 18 +/* RST_CTRL1 */ +#define EN7523_PCM1_ZSI_ISI_RST 19 +#define EN7523_FE_PDMA_RST 20 +#define EN7523_FE_QDMA_RST 21 +#define EN7523_PCM_SPIWP_RST 22 +#define EN7523_CRYPTO_RST 23 +#define EN7523_TIMER_RST 24 +#define EN7523_PCM1_RST 25 +#define EN7523_UART_RST 26 +#define EN7523_GPIO_RST 27 +#define EN7523_GDMA_RST 28 +#define EN7523_I2C_MASTER_RST 29 +#define EN7523_PCM2_ZSI_ISI_RST 30 +#define EN7523_SFC_RST 31 +#define EN7523_UART2_RST 32 +#define EN7523_GDMP_RST 33 +#define EN7523_FE_RST 34 +#define EN7523_USB_HOST_P0_RST 35 +#define EN7523_GSW_RST 36 +#define EN7523_SFC2_PCM_RST 37 +#define EN7523_PCIE0_RST 38 +#define EN7523_PCIE1_RST 39 +#define EN7523_PCIE_HB_RST 40 +#define EN7523_XPON_MAC_RST 41 + +#endif /* __DT_BINDINGS_RESET_CONTROLLER_AIROHA_EN7523_H_ */ diff --git a/include/linux/firmware/samsung/exynos-acpm-protocol.h b/include/linux/firmware/samsung/exynos-acpm-protocol.h index f628bf1862c2..2091da965a5a 100644 --- a/include/linux/firmware/samsung/exynos-acpm-protocol.h +++ b/include/linux/firmware/samsung/exynos-acpm-protocol.h @@ -13,6 +13,15 @@ struct acpm_handle; struct device_node; +struct acpm_dvfs_ops { + int (*set_rate)(const struct acpm_handle *handle, + unsigned int acpm_chan_id, unsigned int clk_id, + unsigned long rate); + unsigned long (*get_rate)(const struct acpm_handle *handle, + unsigned int acpm_chan_id, + unsigned int clk_id); +}; + struct acpm_pmic_ops { int (*read_reg)(const struct acpm_handle *handle, unsigned int acpm_chan_id, u8 type, u8 reg, u8 chan, @@ -32,6 +41,7 @@ struct acpm_pmic_ops { }; struct acpm_ops { + struct acpm_dvfs_ops dvfs_ops; struct acpm_pmic_ops pmic_ops; }; @@ -45,7 +55,16 @@ struct acpm_handle { struct device; +#if IS_ENABLED(CONFIG_EXYNOS_ACPM_PROTOCOL) const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, struct device_node *np); +#else + +static inline const struct acpm_handle *devm_acpm_get_by_node(struct device *dev, + struct device_node *np) +{ + return NULL; +} +#endif #endif /* __EXYNOS_ACPM_PROTOCOL_H */ |
