xref: /openbmc/linux/drivers/clk/mediatek/clk-gate.c (revision 9a87ffc99ec8eb8d35eed7c4f816d75f5cc9662e)
11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29741b1a6SJames Liao /*
39741b1a6SJames Liao  * Copyright (c) 2014 MediaTek Inc.
49741b1a6SJames Liao  * Author: James Liao <jamesjj.liao@mediatek.com>
59741b1a6SJames Liao  */
69741b1a6SJames Liao 
7ee488dc9SChen-Yu Tsai #include <linux/clk-provider.h>
819b8d438SChen-Yu Tsai #include <linux/mfd/syscon.h>
932b028fbSMiles Chen #include <linux/module.h>
10625afe4fSChen-Yu Tsai #include <linux/printk.h>
11ee488dc9SChen-Yu Tsai #include <linux/regmap.h>
12625afe4fSChen-Yu Tsai #include <linux/slab.h>
13625afe4fSChen-Yu Tsai #include <linux/types.h>
149741b1a6SJames Liao 
159741b1a6SJames Liao #include "clk-gate.h"
169741b1a6SJames Liao 
17ee488dc9SChen-Yu Tsai struct mtk_clk_gate {
18ee488dc9SChen-Yu Tsai 	struct clk_hw	hw;
19ee488dc9SChen-Yu Tsai 	struct regmap	*regmap;
20ee488dc9SChen-Yu Tsai 	int		set_ofs;
21ee488dc9SChen-Yu Tsai 	int		clr_ofs;
22ee488dc9SChen-Yu Tsai 	int		sta_ofs;
23ee488dc9SChen-Yu Tsai 	u8		bit;
24ee488dc9SChen-Yu Tsai };
25ee488dc9SChen-Yu Tsai 
to_mtk_clk_gate(struct clk_hw * hw)26ee488dc9SChen-Yu Tsai static inline struct mtk_clk_gate *to_mtk_clk_gate(struct clk_hw *hw)
27ee488dc9SChen-Yu Tsai {
28ee488dc9SChen-Yu Tsai 	return container_of(hw, struct mtk_clk_gate, hw);
29ee488dc9SChen-Yu Tsai }
30ee488dc9SChen-Yu Tsai 
mtk_get_clockgating(struct clk_hw * hw)3142334638SAngeloGioacchino Del Regno static u32 mtk_get_clockgating(struct clk_hw *hw)
329741b1a6SJames Liao {
335fd9c05cSGeliang Tang 	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
349741b1a6SJames Liao 	u32 val;
359741b1a6SJames Liao 
369741b1a6SJames Liao 	regmap_read(cg->regmap, cg->sta_ofs, &val);
379741b1a6SJames Liao 
3842334638SAngeloGioacchino Del Regno 	return val & BIT(cg->bit);
3942334638SAngeloGioacchino Del Regno }
409741b1a6SJames Liao 
mtk_cg_bit_is_cleared(struct clk_hw * hw)4142334638SAngeloGioacchino Del Regno static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
4242334638SAngeloGioacchino Del Regno {
4342334638SAngeloGioacchino Del Regno 	return mtk_get_clockgating(hw) == 0;
449741b1a6SJames Liao }
459741b1a6SJames Liao 
mtk_cg_bit_is_set(struct clk_hw * hw)469741b1a6SJames Liao static int mtk_cg_bit_is_set(struct clk_hw *hw)
479741b1a6SJames Liao {
4842334638SAngeloGioacchino Del Regno 	return mtk_get_clockgating(hw) != 0;
499741b1a6SJames Liao }
509741b1a6SJames Liao 
mtk_cg_set_bit(struct clk_hw * hw)519741b1a6SJames Liao static void mtk_cg_set_bit(struct clk_hw *hw)
529741b1a6SJames Liao {
535fd9c05cSGeliang Tang 	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
549741b1a6SJames Liao 
559741b1a6SJames Liao 	regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
569741b1a6SJames Liao }
579741b1a6SJames Liao 
mtk_cg_clr_bit(struct clk_hw * hw)589741b1a6SJames Liao static void mtk_cg_clr_bit(struct clk_hw *hw)
599741b1a6SJames Liao {
605fd9c05cSGeliang Tang 	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
619741b1a6SJames Liao 
629741b1a6SJames Liao 	regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
639741b1a6SJames Liao }
649741b1a6SJames Liao 
mtk_cg_set_bit_no_setclr(struct clk_hw * hw)65e9862118SShunli Wang static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
66e9862118SShunli Wang {
67e9862118SShunli Wang 	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
68e9862118SShunli Wang 
69d95abcabSAngeloGioacchino Del Regno 	regmap_set_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
70e9862118SShunli Wang }
71e9862118SShunli Wang 
mtk_cg_clr_bit_no_setclr(struct clk_hw * hw)72e9862118SShunli Wang static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
73e9862118SShunli Wang {
74e9862118SShunli Wang 	struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
75e9862118SShunli Wang 
76d95abcabSAngeloGioacchino Del Regno 	regmap_clear_bits(cg->regmap, cg->sta_ofs, BIT(cg->bit));
77e9862118SShunli Wang }
78e9862118SShunli Wang 
mtk_cg_enable(struct clk_hw * hw)799741b1a6SJames Liao static int mtk_cg_enable(struct clk_hw *hw)
809741b1a6SJames Liao {
819741b1a6SJames Liao 	mtk_cg_clr_bit(hw);
829741b1a6SJames Liao 
839741b1a6SJames Liao 	return 0;
849741b1a6SJames Liao }
859741b1a6SJames Liao 
mtk_cg_disable(struct clk_hw * hw)869741b1a6SJames Liao static void mtk_cg_disable(struct clk_hw *hw)
879741b1a6SJames Liao {
889741b1a6SJames Liao 	mtk_cg_set_bit(hw);
899741b1a6SJames Liao }
909741b1a6SJames Liao 
mtk_cg_enable_inv(struct clk_hw * hw)919741b1a6SJames Liao static int mtk_cg_enable_inv(struct clk_hw *hw)
929741b1a6SJames Liao {
939741b1a6SJames Liao 	mtk_cg_set_bit(hw);
949741b1a6SJames Liao 
959741b1a6SJames Liao 	return 0;
969741b1a6SJames Liao }
979741b1a6SJames Liao 
mtk_cg_disable_inv(struct clk_hw * hw)989741b1a6SJames Liao static void mtk_cg_disable_inv(struct clk_hw *hw)
999741b1a6SJames Liao {
1009741b1a6SJames Liao 	mtk_cg_clr_bit(hw);
1019741b1a6SJames Liao }
1029741b1a6SJames Liao 
mtk_cg_enable_no_setclr(struct clk_hw * hw)103e9862118SShunli Wang static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
104e9862118SShunli Wang {
105e9862118SShunli Wang 	mtk_cg_clr_bit_no_setclr(hw);
106e9862118SShunli Wang 
107e9862118SShunli Wang 	return 0;
108e9862118SShunli Wang }
109e9862118SShunli Wang 
mtk_cg_disable_no_setclr(struct clk_hw * hw)110e9862118SShunli Wang static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
111e9862118SShunli Wang {
112e9862118SShunli Wang 	mtk_cg_set_bit_no_setclr(hw);
113e9862118SShunli Wang }
114e9862118SShunli Wang 
mtk_cg_enable_inv_no_setclr(struct clk_hw * hw)115e9862118SShunli Wang static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
116e9862118SShunli Wang {
117e9862118SShunli Wang 	mtk_cg_set_bit_no_setclr(hw);
118e9862118SShunli Wang 
119e9862118SShunli Wang 	return 0;
120e9862118SShunli Wang }
121e9862118SShunli Wang 
mtk_cg_disable_inv_no_setclr(struct clk_hw * hw)122e9862118SShunli Wang static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
123e9862118SShunli Wang {
124e9862118SShunli Wang 	mtk_cg_clr_bit_no_setclr(hw);
125e9862118SShunli Wang }
126e9862118SShunli Wang 
1279741b1a6SJames Liao const struct clk_ops mtk_clk_gate_ops_setclr = {
1289741b1a6SJames Liao 	.is_enabled	= mtk_cg_bit_is_cleared,
1299741b1a6SJames Liao 	.enable		= mtk_cg_enable,
1309741b1a6SJames Liao 	.disable	= mtk_cg_disable,
1319741b1a6SJames Liao };
13232b028fbSMiles Chen EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr);
1339741b1a6SJames Liao 
1349741b1a6SJames Liao const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
1359741b1a6SJames Liao 	.is_enabled	= mtk_cg_bit_is_set,
1369741b1a6SJames Liao 	.enable		= mtk_cg_enable_inv,
1379741b1a6SJames Liao 	.disable	= mtk_cg_disable_inv,
1389741b1a6SJames Liao };
13932b028fbSMiles Chen EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_setclr_inv);
1409741b1a6SJames Liao 
141e9862118SShunli Wang const struct clk_ops mtk_clk_gate_ops_no_setclr = {
142e9862118SShunli Wang 	.is_enabled	= mtk_cg_bit_is_cleared,
143e9862118SShunli Wang 	.enable		= mtk_cg_enable_no_setclr,
144e9862118SShunli Wang 	.disable	= mtk_cg_disable_no_setclr,
145e9862118SShunli Wang };
14632b028fbSMiles Chen EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr);
147e9862118SShunli Wang 
148e9862118SShunli Wang const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
149e9862118SShunli Wang 	.is_enabled	= mtk_cg_bit_is_set,
150e9862118SShunli Wang 	.enable		= mtk_cg_enable_inv_no_setclr,
151e9862118SShunli Wang 	.disable	= mtk_cg_disable_inv_no_setclr,
152e9862118SShunli Wang };
15332b028fbSMiles Chen EXPORT_SYMBOL_GPL(mtk_clk_gate_ops_no_setclr_inv);
154e9862118SShunli Wang 
mtk_clk_register_gate(struct device * dev,const char * name,const char * parent_name,struct regmap * regmap,int set_ofs,int clr_ofs,int sta_ofs,u8 bit,const struct clk_ops * ops,unsigned long flags)155*20498d52SAngeloGioacchino Del Regno static struct clk_hw *mtk_clk_register_gate(struct device *dev, const char *name,
1569741b1a6SJames Liao 					 const char *parent_name,
157ee488dc9SChen-Yu Tsai 					 struct regmap *regmap, int set_ofs,
158ee488dc9SChen-Yu Tsai 					 int clr_ofs, int sta_ofs, u8 bit,
1595a1cc4c2SJasper Mattsson 					 const struct clk_ops *ops,
160*20498d52SAngeloGioacchino Del Regno 					 unsigned long flags)
1619741b1a6SJames Liao {
1629741b1a6SJames Liao 	struct mtk_clk_gate *cg;
1636f691a58SChen-Yu Tsai 	int ret;
16495f58981SRicky Liang 	struct clk_init_data init = {};
1659741b1a6SJames Liao 
1669741b1a6SJames Liao 	cg = kzalloc(sizeof(*cg), GFP_KERNEL);
1679741b1a6SJames Liao 	if (!cg)
1689741b1a6SJames Liao 		return ERR_PTR(-ENOMEM);
1699741b1a6SJames Liao 
1709741b1a6SJames Liao 	init.name = name;
171b3cf181cSWeiyi Lu 	init.flags = flags | CLK_SET_RATE_PARENT;
1729741b1a6SJames Liao 	init.parent_names = parent_name ? &parent_name : NULL;
1739741b1a6SJames Liao 	init.num_parents = parent_name ? 1 : 0;
1749741b1a6SJames Liao 	init.ops = ops;
1759741b1a6SJames Liao 
1769741b1a6SJames Liao 	cg->regmap = regmap;
1779741b1a6SJames Liao 	cg->set_ofs = set_ofs;
1789741b1a6SJames Liao 	cg->clr_ofs = clr_ofs;
1799741b1a6SJames Liao 	cg->sta_ofs = sta_ofs;
1809741b1a6SJames Liao 	cg->bit = bit;
1819741b1a6SJames Liao 
1829741b1a6SJames Liao 	cg->hw.init = &init;
1839741b1a6SJames Liao 
1846f691a58SChen-Yu Tsai 	ret = clk_hw_register(dev, &cg->hw);
1856f691a58SChen-Yu Tsai 	if (ret) {
1869741b1a6SJames Liao 		kfree(cg);
1876f691a58SChen-Yu Tsai 		return ERR_PTR(ret);
1889741b1a6SJames Liao 	}
18932b028fbSMiles Chen 
1906f691a58SChen-Yu Tsai 	return &cg->hw;
1916f691a58SChen-Yu Tsai }
1926f691a58SChen-Yu Tsai 
mtk_clk_unregister_gate(struct clk_hw * hw)1936f691a58SChen-Yu Tsai static void mtk_clk_unregister_gate(struct clk_hw *hw)
19444dd1414SChen-Yu Tsai {
19544dd1414SChen-Yu Tsai 	struct mtk_clk_gate *cg;
19644dd1414SChen-Yu Tsai 	if (!hw)
19744dd1414SChen-Yu Tsai 		return;
19844dd1414SChen-Yu Tsai 
19944dd1414SChen-Yu Tsai 	cg = to_mtk_clk_gate(hw);
20044dd1414SChen-Yu Tsai 
2016f691a58SChen-Yu Tsai 	clk_hw_unregister(hw);
20244dd1414SChen-Yu Tsai 	kfree(cg);
20344dd1414SChen-Yu Tsai }
20444dd1414SChen-Yu Tsai 
mtk_clk_register_gates(struct device * dev,struct device_node * node,const struct mtk_gate * clks,int num,struct clk_hw_onecell_data * clk_data)205*20498d52SAngeloGioacchino Del Regno int mtk_clk_register_gates(struct device *dev, struct device_node *node,
20619b8d438SChen-Yu Tsai 			   const struct mtk_gate *clks, int num,
207*20498d52SAngeloGioacchino Del Regno 			   struct clk_hw_onecell_data *clk_data)
20819b8d438SChen-Yu Tsai {
20919b8d438SChen-Yu Tsai 	int i;
2106f691a58SChen-Yu Tsai 	struct clk_hw *hw;
21119b8d438SChen-Yu Tsai 	struct regmap *regmap;
21219b8d438SChen-Yu Tsai 
21319b8d438SChen-Yu Tsai 	if (!clk_data)
21419b8d438SChen-Yu Tsai 		return -ENOMEM;
21519b8d438SChen-Yu Tsai 
21619b8d438SChen-Yu Tsai 	regmap = device_node_to_regmap(node);
21719b8d438SChen-Yu Tsai 	if (IS_ERR(regmap)) {
21819b8d438SChen-Yu Tsai 		pr_err("Cannot find regmap for %pOF: %pe\n", node, regmap);
21919b8d438SChen-Yu Tsai 		return PTR_ERR(regmap);
22019b8d438SChen-Yu Tsai 	}
22119b8d438SChen-Yu Tsai 
22219b8d438SChen-Yu Tsai 	for (i = 0; i < num; i++) {
22319b8d438SChen-Yu Tsai 		const struct mtk_gate *gate = &clks[i];
22419b8d438SChen-Yu Tsai 
225609cc5e1SChen-Yu Tsai 		if (!IS_ERR_OR_NULL(clk_data->hws[gate->id])) {
226d54bb86bSChen-Yu Tsai 			pr_warn("%pOF: Trying to register duplicate clock ID: %d\n",
227d54bb86bSChen-Yu Tsai 				node, gate->id);
22819b8d438SChen-Yu Tsai 			continue;
229d54bb86bSChen-Yu Tsai 		}
23019b8d438SChen-Yu Tsai 
231*20498d52SAngeloGioacchino Del Regno 		hw = mtk_clk_register_gate(dev, gate->name, gate->parent_name,
23219b8d438SChen-Yu Tsai 					    regmap,
23319b8d438SChen-Yu Tsai 					    gate->regs->set_ofs,
23419b8d438SChen-Yu Tsai 					    gate->regs->clr_ofs,
23519b8d438SChen-Yu Tsai 					    gate->regs->sta_ofs,
23619b8d438SChen-Yu Tsai 					    gate->shift, gate->ops,
237*20498d52SAngeloGioacchino Del Regno 					    gate->flags);
23819b8d438SChen-Yu Tsai 
2396f691a58SChen-Yu Tsai 		if (IS_ERR(hw)) {
2406f691a58SChen-Yu Tsai 			pr_err("Failed to register clk %s: %pe\n", gate->name,
2416f691a58SChen-Yu Tsai 			       hw);
242e938a134SChen-Yu Tsai 			goto err;
24319b8d438SChen-Yu Tsai 		}
24419b8d438SChen-Yu Tsai 
2456f691a58SChen-Yu Tsai 		clk_data->hws[gate->id] = hw;
24619b8d438SChen-Yu Tsai 	}
24719b8d438SChen-Yu Tsai 
24819b8d438SChen-Yu Tsai 	return 0;
249e938a134SChen-Yu Tsai 
250e938a134SChen-Yu Tsai err:
251e938a134SChen-Yu Tsai 	while (--i >= 0) {
252e938a134SChen-Yu Tsai 		const struct mtk_gate *gate = &clks[i];
253e938a134SChen-Yu Tsai 
254609cc5e1SChen-Yu Tsai 		if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
255e938a134SChen-Yu Tsai 			continue;
256e938a134SChen-Yu Tsai 
2576f691a58SChen-Yu Tsai 		mtk_clk_unregister_gate(clk_data->hws[gate->id]);
258609cc5e1SChen-Yu Tsai 		clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
259e938a134SChen-Yu Tsai 	}
260e938a134SChen-Yu Tsai 
2616f691a58SChen-Yu Tsai 	return PTR_ERR(hw);
26219b8d438SChen-Yu Tsai }
26319b8d438SChen-Yu Tsai EXPORT_SYMBOL_GPL(mtk_clk_register_gates);
26419b8d438SChen-Yu Tsai 
mtk_clk_unregister_gates(const struct mtk_gate * clks,int num,struct clk_hw_onecell_data * clk_data)26544dd1414SChen-Yu Tsai void mtk_clk_unregister_gates(const struct mtk_gate *clks, int num,
266609cc5e1SChen-Yu Tsai 			      struct clk_hw_onecell_data *clk_data)
26744dd1414SChen-Yu Tsai {
26844dd1414SChen-Yu Tsai 	int i;
26944dd1414SChen-Yu Tsai 
27044dd1414SChen-Yu Tsai 	if (!clk_data)
27144dd1414SChen-Yu Tsai 		return;
27244dd1414SChen-Yu Tsai 
27344dd1414SChen-Yu Tsai 	for (i = num; i > 0; i--) {
27444dd1414SChen-Yu Tsai 		const struct mtk_gate *gate = &clks[i - 1];
27544dd1414SChen-Yu Tsai 
276609cc5e1SChen-Yu Tsai 		if (IS_ERR_OR_NULL(clk_data->hws[gate->id]))
27744dd1414SChen-Yu Tsai 			continue;
27844dd1414SChen-Yu Tsai 
2796f691a58SChen-Yu Tsai 		mtk_clk_unregister_gate(clk_data->hws[gate->id]);
280609cc5e1SChen-Yu Tsai 		clk_data->hws[gate->id] = ERR_PTR(-ENOENT);
28144dd1414SChen-Yu Tsai 	}
28244dd1414SChen-Yu Tsai }
28344dd1414SChen-Yu Tsai EXPORT_SYMBOL_GPL(mtk_clk_unregister_gates);
28444dd1414SChen-Yu Tsai 
28532b028fbSMiles Chen MODULE_LICENSE("GPL");
286