summaryrefslogtreecommitdiff
path: root/drivers/clk/imx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2025-12-08 09:38:52 +0900
committerLinus Torvalds <torvalds@linux-foundation.org>2025-12-08 09:38:52 +0900
commitba65a4e7120a616d9c592750d9147f6dcafedffa (patch)
tree389e32f0bc828d4bb803e0136d5ba6e63986dac0 /drivers/clk/imx
parent67a454e6b1c604555c04501c77b7fedc5d98a779 (diff)
parent6f172175b6f3fe35b5d519fc314f7a0b603a9af9 (diff)
downloadlwn-ba65a4e7120a616d9c592750d9147f6dcafedffa.tar.gz
lwn-ba65a4e7120a616d9c592750d9147f6dcafedffa.zip
Merge tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux
Pull clk updates from Stephen Boyd: "This is entirely SoC clk drivers. The majority diff wise is for the new Rockchip and Qualcomm clk drivers which is mostly lines and lines of data structures to describe the clk hardware in these SoCs. Beyond those two, Renesas continues to incrementally add clks to their SoC drivers, causing them to show up higher in the diffstat this time because they added quite a few clks all over the place. Overall it is a semi-quiet release that has some new clk drivers and the usual fixes for clock data that was wrong or missing and non-critical cleanups that plug error paths or fix typos. New Drivers: - Qualcomm IPQ5424 Network Subsystem Clock Controller - Qualcomm SM8750 Video Clock Controller - Rockchip RV1126B and RK3506 clock drivers - i.MX8ULP SIM LPAV clock driver - Samsung ACPM (firmware interface) clock driver - Altera Agilex5 clock driver" * tag 'clk-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/clk/linux: (117 commits) clk: keystone: fix compile testing clk: keystone: syscon-clk: fix regmap leak on probe failure clk: qcom: Mark camcc_sm7150_hws static clk: samsung: exynos-clkout: Assign .num before accessing .hws clk: rockchip: Add clock and reset driver for RK3506 dt-bindings: clock: rockchip: Add RK3506 clock and reset unit clk: actions: Fix discarding const qualifier by 'container_of' macro clk: spacemit: Set clk_hw_onecell_data::num before using flex array clk: visconti: Add VIIF clocks dt-bindings: clock: tmpv770x: Add VIIF clocks dt-bindings: clock: tmpv770x: Remove definition of number of clocks clk: visconti: Do not define number of clocks in bindings clk: rockchip: Add clock controller for the RV1126B dt-bindings: clock, reset: Add support for rv1126b clk: rockchip: Implement rockchip_clk_register_armclk_multi_pll() clk: qcom: x1e80100-dispcc: Add USB4 router link resets dt-bindings: clock: qcom: x1e80100-dispcc: Add USB4 router link resets clk: qcom: videocc-sm8750: Add video clock controller driver for SM8750 dt-bindings: clock: qcom: Add SM8750 video clock controller clk: qcom: branch: Extend invert logic for branch2 mem clocks ...
Diffstat (limited to 'drivers/clk/imx')
-rw-r--r--drivers/clk/imx/Kconfig1
-rw-r--r--drivers/clk/imx/Makefile1
-rw-r--r--drivers/clk/imx/clk-composite-7ulp.c13
-rw-r--r--drivers/clk/imx/clk-imx8mp-audiomix.c39
-rw-r--r--drivers/clk/imx/clk-imx8ulp-sim-lpav.c156
5 files changed, 175 insertions, 35 deletions
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index 6ff6d934848a..b292e7ca5c24 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -105,6 +105,7 @@ config CLK_IMX8ULP
tristate "IMX8ULP CCM Clock Driver"
depends on ARCH_MXC || COMPILE_TEST
select MXC_CLK
+ select AUXILIARY_BUS
help
Build the driver for i.MX8ULP CCM Clock Driver
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 03f2b2a1ab63..208b46873a18 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -41,6 +41,7 @@ clk-imx-lpcg-scu-$(CONFIG_CLK_IMX8QXP) += clk-lpcg-scu.o clk-imx8qxp-lpcg.o
clk-imx-acm-$(CONFIG_CLK_IMX8QXP) = clk-imx8-acm.o
obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp.o
+obj-$(CONFIG_CLK_IMX8ULP) += clk-imx8ulp-sim-lpav.o
obj-$(CONFIG_CLK_IMX1) += clk-imx1.o
obj-$(CONFIG_CLK_IMX25) += clk-imx25.o
diff --git a/drivers/clk/imx/clk-composite-7ulp.c b/drivers/clk/imx/clk-composite-7ulp.c
index 8ed2e0ad2769..37d2fc197be6 100644
--- a/drivers/clk/imx/clk-composite-7ulp.c
+++ b/drivers/clk/imx/clk-composite-7ulp.c
@@ -7,6 +7,7 @@
#include <linux/bits.h>
#include <linux/clk-provider.h>
+#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/slab.h>
@@ -36,6 +37,9 @@ static int pcc_gate_enable(struct clk_hw *hw)
if (ret)
return ret;
+ /* Make sure the IP's clock is ready before release reset */
+ udelay(1);
+
spin_lock_irqsave(gate->lock, flags);
/*
* release the sw reset for peripherals associated with
@@ -47,6 +51,15 @@ static int pcc_gate_enable(struct clk_hw *hw)
spin_unlock_irqrestore(gate->lock, flags);
+ /*
+ * Read back the register to make sure the previous write has been
+ * done in the target HW register. For IP like GPU, after deassert
+ * the reset, need to wait for a while to make sure the sync reset
+ * is done
+ */
+ readl(gate->reg);
+ udelay(1);
+
return 0;
}
diff --git a/drivers/clk/imx/clk-imx8mp-audiomix.c b/drivers/clk/imx/clk-imx8mp-audiomix.c
index 775f62dddb11..131702f2c9ec 100644
--- a/drivers/clk/imx/clk-imx8mp-audiomix.c
+++ b/drivers/clk/imx/clk-imx8mp-audiomix.c
@@ -230,50 +230,19 @@ struct clk_imx8mp_audiomix_priv {
#if IS_ENABLED(CONFIG_RESET_CONTROLLER)
-static void clk_imx8mp_audiomix_reset_unregister_adev(void *_adev)
-{
- struct auxiliary_device *adev = _adev;
-
- auxiliary_device_delete(adev);
- auxiliary_device_uninit(adev);
-}
-
-static void clk_imx8mp_audiomix_reset_adev_release(struct device *dev)
-{
- struct auxiliary_device *adev = to_auxiliary_dev(dev);
-
- kfree(adev);
-}
-
static int clk_imx8mp_audiomix_reset_controller_register(struct device *dev,
struct clk_imx8mp_audiomix_priv *priv)
{
- struct auxiliary_device *adev __free(kfree) = NULL;
- int ret;
+ struct auxiliary_device *adev;
if (!of_property_present(dev->of_node, "#reset-cells"))
return 0;
- adev = kzalloc(sizeof(*adev), GFP_KERNEL);
+ adev = devm_auxiliary_device_create(dev, "reset", NULL);
if (!adev)
- return -ENOMEM;
-
- adev->name = "reset";
- adev->dev.parent = dev;
- adev->dev.release = clk_imx8mp_audiomix_reset_adev_release;
-
- ret = auxiliary_device_init(adev);
- if (ret)
- return ret;
+ return -ENODEV;
- ret = auxiliary_device_add(adev);
- if (ret) {
- auxiliary_device_uninit(adev);
- return ret;
- }
-
- return devm_add_action_or_reset(dev, clk_imx8mp_audiomix_reset_unregister_adev,
- no_free_ptr(adev));
+ return 0;
}
#else /* !CONFIG_RESET_CONTROLLER */
diff --git a/drivers/clk/imx/clk-imx8ulp-sim-lpav.c b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
new file mode 100644
index 000000000000..990c95b89b75
--- /dev/null
+++ b/drivers/clk/imx/clk-imx8ulp-sim-lpav.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <dt-bindings/clock/imx8ulp-clock.h>
+
+#include <linux/auxiliary_bus.h>
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define SYSCTRL0 0x8
+
+#define IMX8ULP_HIFI_CLK_GATE(gname, cname, pname, bidx) \
+ { \
+ .name = gname "_cg", \
+ .id = IMX8ULP_CLK_SIM_LPAV_HIFI_##cname, \
+ .parent = { .fw_name = pname }, \
+ .bit = bidx, \
+ }
+
+struct clk_imx8ulp_sim_lpav_data {
+ spinlock_t lock; /* shared by MUX, clock gate and reset */
+ unsigned long flags; /* for spinlock usage */
+ struct clk_hw_onecell_data clk_data; /* keep last */
+};
+
+struct clk_imx8ulp_sim_lpav_gate {
+ const char *name;
+ int id;
+ const struct clk_parent_data parent;
+ u8 bit;
+};
+
+static struct clk_imx8ulp_sim_lpav_gate gates[] = {
+ IMX8ULP_HIFI_CLK_GATE("hifi_core", CORE, "core", 17),
+ IMX8ULP_HIFI_CLK_GATE("hifi_pbclk", PBCLK, "bus", 18),
+ IMX8ULP_HIFI_CLK_GATE("hifi_plat", PLAT, "plat", 19)
+};
+
+static void clk_imx8ulp_sim_lpav_lock(void *arg) __acquires(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_lock_irqsave(&data->lock, data->flags);
+}
+
+static void clk_imx8ulp_sim_lpav_unlock(void *arg) __releases(&data->lock)
+{
+ struct clk_imx8ulp_sim_lpav_data *data = dev_get_drvdata(arg);
+
+ spin_unlock_irqrestore(&data->lock, data->flags);
+}
+
+static int clk_imx8ulp_sim_lpav_probe(struct platform_device *pdev)
+{
+ const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .lock = clk_imx8ulp_sim_lpav_lock,
+ .unlock = clk_imx8ulp_sim_lpav_unlock,
+ .lock_arg = &pdev->dev,
+ };
+ struct clk_imx8ulp_sim_lpav_data *data;
+ struct auxiliary_device *adev;
+ struct regmap *regmap;
+ void __iomem *base;
+ struct clk_hw *hw;
+ int i, ret;
+
+ data = devm_kzalloc(&pdev->dev,
+ struct_size(data, clk_data.hws, ARRAY_SIZE(gates)),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ dev_set_drvdata(&pdev->dev, data);
+
+ /*
+ * this lock is used directly by the clock gate and indirectly
+ * by the reset and mux controller via the regmap API
+ */
+ spin_lock_init(&data->lock);
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return dev_err_probe(&pdev->dev, PTR_ERR(base),
+ "failed to ioremap base\n");
+ /*
+ * although the clock gate doesn't use the regmap API to modify the
+ * registers, we still need the regmap because of the reset auxiliary
+ * driver and the MUX drivers, which use the parent device's regmap
+ */
+ regmap = devm_regmap_init_mmio(&pdev->dev, base, &regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(regmap),
+ "failed to initialize regmap\n");
+
+ data->clk_data.num = ARRAY_SIZE(gates);
+
+ for (i = 0; i < ARRAY_SIZE(gates); i++) {
+ hw = devm_clk_hw_register_gate_parent_data(&pdev->dev,
+ gates[i].name,
+ &gates[i].parent,
+ CLK_SET_RATE_PARENT,
+ base + SYSCTRL0,
+ gates[i].bit,
+ 0x0, &data->lock);
+ if (IS_ERR(hw))
+ return dev_err_probe(&pdev->dev, PTR_ERR(hw),
+ "failed to register %s gate\n",
+ gates[i].name);
+
+ data->clk_data.hws[i] = hw;
+ }
+
+ adev = devm_auxiliary_device_create(&pdev->dev, "reset", NULL);
+ if (!adev)
+ return dev_err_probe(&pdev->dev, -ENODEV,
+ "failed to register aux reset\n");
+
+ ret = devm_of_clk_add_hw_provider(&pdev->dev,
+ of_clk_hw_onecell_get,
+ &data->clk_data);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "failed to register clk hw provider\n");
+
+ /* used to probe MUX child device */
+ return devm_of_platform_populate(&pdev->dev);
+}
+
+static const struct of_device_id clk_imx8ulp_sim_lpav_of_match[] = {
+ { .compatible = "fsl,imx8ulp-sim-lpav" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, clk_imx8ulp_sim_lpav_of_match);
+
+static struct platform_driver clk_imx8ulp_sim_lpav_driver = {
+ .probe = clk_imx8ulp_sim_lpav_probe,
+ .driver = {
+ .name = "clk-imx8ulp-sim-lpav",
+ .of_match_table = clk_imx8ulp_sim_lpav_of_match,
+ },
+};
+module_platform_driver(clk_imx8ulp_sim_lpav_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("i.MX8ULP LPAV System Integration Module (SIM) clock driver");
+MODULE_AUTHOR("Laurentiu Mihalcea <laurentiu.mihalcea@nxp.com>");