[RFC,4/8] clk: imx: add support for imx8mn gate clock

Message ID 20221019172019.2303223-5-dario.binacchi@amarulasolutions.com
State New
Headers show
Series
  • clk: imx8mn: setup clocks by device tree
Related show

Commit Message

Dario Binacchi Oct. 19, 2022, 5:20 p.m. UTC
This patch adds support for freescale imx8mn gate clock. Let's start
with this specific clock driver and hope that other variants can be
handled in the future as well.

Signed-off-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>
---

 drivers/clk/imx/Makefile   |   1 +
 drivers/clk/imx/clk-gate.c | 180 +++++++++++++++++++++++++++++++++++++
 2 files changed, 181 insertions(+)
 create mode 100644 drivers/clk/imx/clk-gate.c

Patch

diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index 88b9b9285d22..539add92be47 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -11,6 +11,7 @@  mxc-clk-objs += clk-divider-gate.o
 mxc-clk-objs += clk-fixup-div.o
 mxc-clk-objs += clk-fixup-mux.o
 mxc-clk-objs += clk-frac-pll.o
+mxc-clk-objs += clk-gate.o
 mxc-clk-objs += clk-gate2.o
 mxc-clk-objs += clk-gate-exclusive.o
 mxc-clk-objs += clk-pfd.o
diff --git a/drivers/clk/imx/clk-gate.c b/drivers/clk/imx/clk-gate.c
new file mode 100644
index 000000000000..70b0d95e8f3d
--- /dev/null
+++ b/drivers/clk/imx/clk-gate.c
@@ -0,0 +1,180 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Amarula Solutions
+ *
+ * Dario Binacchi <dario.binacchi@amarulasolutions.com>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include "clk.h"
+
+#define to_clk_imx_gate(_hw) container_of(_hw, struct clk_imx_gate, hw)
+
+struct clk_imx_gate {
+	struct clk_hw hw;
+	struct imx_clk_iomap iomap;
+	u32 enable_mask;
+};
+
+static int imx_clk_gate_enable(struct clk_hw *hw)
+{
+	struct clk_imx_gate *gate = to_clk_imx_gate(hw);
+	struct imx_clk_iomap *io = &gate->iomap;
+
+	return regmap_update_bits(io->regmap, io->offset, gate->enable_mask,
+				  gate->enable_mask);
+}
+
+static void imx_clk_gate_disable(struct clk_hw *hw)
+{
+	struct clk_imx_gate *gate = to_clk_imx_gate(hw);
+	struct imx_clk_iomap *io = &gate->iomap;
+
+	regmap_update_bits(io->regmap, io->offset, gate->enable_mask, 0);
+}
+
+static int imx_clk_gate_is_enabled(struct clk_hw *hw)
+{
+	struct clk_imx_gate *gate = to_clk_imx_gate(hw);
+	struct imx_clk_iomap *io = &gate->iomap;
+	unsigned int val;
+
+	if (regmap_read(io->regmap, io->offset, &val))
+		return -EIO;
+
+	return !!(val & gate->enable_mask);
+}
+
+const struct clk_ops imx_clk_gate_ops = {
+	.enable	= &imx_clk_gate_enable,
+	.disable = &imx_clk_gate_disable,
+	.is_enabled = &imx_clk_gate_is_enabled,
+};
+
+static void imx_clk_hw_unregister_gate(struct clk_hw *hw)
+{
+	struct clk_imx_gate *gate = to_clk_imx_gate(hw);
+
+	clk_hw_unregister(hw);
+	kfree(gate);
+}
+
+static struct clk_hw *imx_clk_hw_register_gate(struct device *dev,
+					       const char *name,
+					       const char *parent_name,
+					       unsigned long flags,
+					       struct imx_clk_iomap *iomap,
+					       u8 enable_bit)
+{
+	struct clk_init_data init = { NULL };
+	struct clk_imx_gate *gate;
+	struct clk_hw *hw;
+	int ret;
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.flags = flags;
+	init.ops = &imx_clk_gate_ops;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	memcpy(&gate->iomap, iomap, sizeof(*iomap));
+	gate->enable_mask = BIT(enable_bit);
+	gate->hw.init = &init;
+
+	hw = &gate->hw;
+	ret = clk_hw_register(dev, hw);
+	if (ret) {
+		kfree(gate);
+		return ERR_PTR(ret);
+	}
+
+	return hw;
+}
+
+static struct clk_hw *_of_imx_gate_clk_setup(struct device_node *node)
+{
+	struct clk_hw *hw;
+	struct imx_clk_iomap io;
+	struct device_node *parent_node;
+	const char *name, *parent_name;
+	u8 enable_bit = 0;
+	u32 val;
+	int ret;
+
+	parent_node = of_get_parent(node);
+	if (!parent_node) {
+		pr_err("%s: %pOFn must have 1 parent\n", __func__, node);
+		return ERR_PTR(-ENODEV);
+	}
+
+	io.regmap = syscon_node_to_regmap(parent_node);
+	of_node_put(parent_node);
+	if (IS_ERR(io.regmap)) {
+		pr_err("%s: missing regmap for %pOFn\n", __func__, node);
+		return ERR_CAST(io.regmap);
+	}
+
+	if (of_property_read_u32(node, "fsl,regmap-offset", &val)) {
+		pr_err("%s: missing regmap offset for %pOFn\n", __func__,
+		       node);
+		return ERR_PTR(-EIO);
+	}
+
+	io.offset = val;
+
+	if (!of_property_read_u32(node, "fsl,bit-shift", &val))
+		enable_bit = val;
+
+	if (of_clk_get_parent_count(node) != 1) {
+		pr_err("%s: %pOFn must have 1 parent clock\n", __func__, node);
+		return ERR_PTR(-EIO);
+	}
+
+	parent_name = of_clk_get_parent_name(node, 0);
+	name = imx_dt_clk_name(node);
+
+	hw = imx_clk_hw_register_gate(NULL, name, parent_name, 0, &io,
+				      enable_bit);
+	if (IS_ERR(hw)) {
+		/*
+		 * Clear OF_POPULATED flag so that clock registration can be
+		 * attempted again from probe function.
+		 */
+		of_node_clear_flag(node, OF_POPULATED);
+		return ERR_CAST(hw);
+	}
+
+	ret = of_clk_add_hw_provider(node, of_clk_hw_simple_get, hw);
+	if (ret) {
+		imx_clk_hw_unregister_gate(hw);
+		return ERR_PTR(ret);
+	}
+
+	pr_debug("%s: name: %s, parent: %s, offset: 0x%x, enable-bit: %d\n",
+		 __func__, name, parent_name, io.offset, enable_bit);
+
+	return hw;
+}
+
+/**
+ * of_imx_gate_clk_setup() - Setup function for imx gate clock
+ * @node:	device node for the clock
+ */
+void __init of_imx_gate_clk_setup(struct device_node *node)
+{
+	_of_imx_gate_clk_setup(node);
+}
+CLK_OF_DECLARE(fsl_imx8mn_gate_clk, "fsl,imx8mn-gate-clock",
+	       of_imx_gate_clk_setup);