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