From 948a378f8aef5bd7240a38e3a864c94276d6aa56 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
Date: Fri, 10 Jun 2022 18:20:29 +0100
Subject: [PATCH] clk: gate: add support for regmap based gates

While we have nice wrappers for simple bit-flip MMIO based clock gates,
a single bit to toggle in a regmap still requires to write a lot of clock
framework boilerplate.

Support generic wrappers for regmap based clock gates, by adding them to
the existing clock-gates.c file. Since a read-modify-write operation in a
regmap can be much more complex than a readl/writel pair, we cannot use
the .enable/.disable ops members, but do the actual flipping already in
.prepare/.unprepare, where we can sleep. Also we cannot provide an
.is_enabled function, since this must not sleep as well.
On the upside all the locking for the r/m/w operation is provided by
regmap already, so we can skip that.
The rest of the CCF boilerplate code can be shared.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 drivers/clk/clk-gate.c       | 60 ++++++++++++++++++++++++++++++++++--
 include/linux/clk-provider.h | 36 +++++++++++++++++++---
 2 files changed, 89 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 64283807600b..f83aef5e6e79 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -9,6 +9,7 @@
 #include <linux/clk-provider.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/err.h>
@@ -124,11 +125,42 @@ const struct clk_ops clk_gate_ops = {
 };
 EXPORT_SYMBOL_GPL(clk_gate_ops);
 
+static int clk_gate_regmap_setclrbit(struct clk_hw *hw, bool enable)
+{
+	struct clk_gate *gate = to_clk_gate(hw);
+	bool set = gate->flags & CLK_GATE_SET_TO_DISABLE;
+
+	set ^= enable;
+
+	if (set)
+		return regmap_set_bits(gate->regmap, gate->regmap_offs,
+				       BIT(gate->bit_idx));
+	else
+		return regmap_clear_bits(gate->regmap, gate->regmap_offs,
+					 BIT(gate->bit_idx));
+}
+
+static int clk_gate_regmap_prepare(struct clk_hw *hw)
+{
+	return clk_gate_regmap_setclrbit(hw, true);
+}
+
+static void clk_gate_regmap_unprepare(struct clk_hw *hw)
+{
+	clk_gate_regmap_setclrbit(hw, false);
+}
+
+const struct clk_ops clk_gate_regmap_ops = {
+	.prepare = clk_gate_regmap_prepare,
+	.unprepare = clk_gate_regmap_unprepare,
+};
+
 struct clk_hw *__clk_hw_register_gate(struct device *dev,
 		struct device_node *np, const char *name,
 		const char *parent_name, const struct clk_hw *parent_hw,
 		const struct clk_parent_data *parent_data,
 		unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
 		void __iomem *reg, u8 bit_idx,
 		u8 clk_gate_flags, spinlock_t *lock)
 {
@@ -150,7 +182,10 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
 		return ERR_PTR(-ENOMEM);
 
 	init.name = name;
-	init.ops = &clk_gate_ops;
+	if (regmap)
+		init.ops = &clk_gate_regmap_ops;
+	else
+		init.ops = &clk_gate_ops;
 	init.flags = flags;
 	init.parent_names = parent_name ? &parent_name : NULL;
 	init.parent_hws = parent_hw ? &parent_hw : NULL;
@@ -162,6 +197,8 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
 
 	/* struct clk_gate assignments */
 	gate->reg = reg;
+	gate->regmap = regmap;
+	gate->regmap_offs = regmap_offs;
 	gate->bit_idx = bit_idx;
 	gate->flags = clk_gate_flags;
 	gate->lock = lock;
@@ -197,6 +234,22 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 }
 EXPORT_SYMBOL_GPL(clk_register_gate);
 
+struct clk *clk_register_regmap_gate(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
+		u8 bit_idx, u8 clk_gate_flags)
+{
+	struct clk_hw *hw;
+
+	hw = clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap,
+					 regmap_offs, bit_idx, clk_gate_flags);
+
+	if (IS_ERR(hw))
+		return ERR_CAST(hw);
+	return hw->clk;
+}
+EXPORT_SYMBOL_GPL(clk_register_regmap_gate);
+
 void clk_unregister_gate(struct clk *clk)
 {
 	struct clk_gate *gate;
@@ -234,6 +287,7 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
 		const char *parent_name, const struct clk_hw *parent_hw,
 		const struct clk_parent_data *parent_data,
 		unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
 		void __iomem *reg, u8 bit_idx,
 		u8 clk_gate_flags, spinlock_t *lock)
 {
@@ -244,8 +298,8 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
 		return ERR_PTR(-ENOMEM);
 
 	hw = __clk_hw_register_gate(dev, np, name, parent_name, parent_hw,
-				    parent_data, flags, reg, bit_idx,
-				    clk_gate_flags, lock);
+				    parent_data, flags, regmap, regmap_offs,
+				    reg, bit_idx, clk_gate_flags, lock);
 
 	if (!IS_ERR(hw)) {
 		*ptr = hw;
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 267cd06b54a0..5de2c071fcf8 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -8,6 +8,7 @@
 
 #include <linux/of.h>
 #include <linux/of_clk.h>
+#include <linux/regmap.h>
 
 /*
  * flags used across common struct clk.  these flags should only affect the
@@ -510,6 +511,8 @@ void of_fixed_clk_setup(struct device_node *np);
 struct clk_gate {
 	struct clk_hw hw;
 	void __iomem	*reg;
+	struct regmap	*regmap;
+	unsigned int	regmap_offs;
 	u8		bit_idx;
 	u8		flags;
 	spinlock_t	*lock;
@@ -527,6 +530,7 @@ struct clk_hw *__clk_hw_register_gate(struct device *dev,
 		const char *parent_name, const struct clk_hw *parent_hw,
 		const struct clk_parent_data *parent_data,
 		unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
 		void __iomem *reg, u8 bit_idx,
 		u8 clk_gate_flags, spinlock_t *lock);
 struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
@@ -534,12 +538,17 @@ struct clk_hw *__devm_clk_hw_register_gate(struct device *dev,
 		const char *parent_name, const struct clk_hw *parent_hw,
 		const struct clk_parent_data *parent_data,
 		unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
 		void __iomem *reg, u8 bit_idx,
 		u8 clk_gate_flags, spinlock_t *lock);
 struct clk *clk_register_gate(struct device *dev, const char *name,
 		const char *parent_name, unsigned long flags,
 		void __iomem *reg, u8 bit_idx,
 		u8 clk_gate_flags, spinlock_t *lock);
+struct clk *clk_register_regmap_gate(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		struct regmap *regmap, unsigned int regmap_offs,
+		u8 bit_idx, u8 clk_gate_flags);
 /**
  * clk_hw_register_gate - register a gate clock with the clock framework
  * @dev: device that is registering this clock
@@ -554,8 +563,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 #define clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,     \
 			     clk_gate_flags, lock)			      \
 	__clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL,      \
-			       NULL, (flags), (reg), (bit_idx),		      \
+			       NULL, (flags), NULL, 0, (reg), (bit_idx),      \
 			       (clk_gate_flags), (lock))
+
+#define clk_hw_register_regmap_gate(dev, name, parent_name, flags, regmap,    \
+				    regmap_offs, bit_idx, clk_gate_flags)     \
+	__clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL,      \
+			       NULL, (flags), regmap, regmap_offs, NULL,      \
+			       (bit_idx), (clk_gate_flags), NULL)
 /**
  * clk_hw_register_gate_parent_hw - register a gate clock with the clock
  * framework
@@ -571,8 +586,15 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 #define clk_hw_register_gate_parent_hw(dev, name, parent_hw, flags, reg,      \
 				       bit_idx, clk_gate_flags, lock)	      \
 	__clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw),        \
-			       NULL, (flags), (reg), (bit_idx),		      \
+			       NULL, (flags), NULL, 0, (reg), (bit_idx),      \
 			       (clk_gate_flags), (lock))
+
+#define clk_hw_register_regmap_gate_parent_hw(dev, name, parent_hw, flags,    \
+					      regmap, regmap_offs, bit_idx,   \
+					      clk_gate_flags)		      \
+	__clk_hw_register_gate((dev), NULL, (name), NULL, (parent_hw),        \
+			       NULL, (flags), regmap, regmap_offs, NULL,      \
+			       (bit_idx), (clk_gate_flags), NULL)
 /**
  * clk_hw_register_gate_parent_data - register a gate clock with the clock
  * framework
@@ -588,7 +610,7 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 #define clk_hw_register_gate_parent_data(dev, name, parent_data, flags, reg,  \
 				       bit_idx, clk_gate_flags, lock)	      \
 	__clk_hw_register_gate((dev), NULL, (name), NULL, NULL, (parent_data), \
-			       (flags), (reg), (bit_idx),		      \
+			       (flags), NULL, 0, (reg), (bit_idx),	      \
 			       (clk_gate_flags), (lock))
 /**
  * devm_clk_hw_register_gate - register a gate clock with the clock framework
@@ -604,8 +626,14 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
 #define devm_clk_hw_register_gate(dev, name, parent_name, flags, reg, bit_idx,\
 				  clk_gate_flags, lock)			      \
 	__devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
-			       NULL, (flags), (reg), (bit_idx),		      \
+			       NULL, (flags), NULL, 0, (reg), (bit_idx),      \
 			       (clk_gate_flags), (lock))
+#define devm_clk_hw_register_regmap_gate(dev, name, parent_name, flags,	      \
+					 regmap, regmap_offs, bit_idx,	      \
+					 clk_gate_flags)		      \
+	__devm_clk_hw_register_gate((dev), NULL, (name), (parent_name), NULL, \
+			       NULL, (flags), (regmap), (regmap_offs), NULL,  \
+			       (bit_idx), (clk_gate_flags), NULL)
 void clk_unregister_gate(struct clk *clk);
 void clk_hw_unregister_gate(struct clk_hw *hw);
 int clk_gate_is_enabled(struct clk_hw *hw);
-- 
2.34.1

