blob: 75d584566431f8ff12ccd3a7a9a74bce58b9183a [file] [log] [blame]
xjb04a4022021-11-25 15:01:52 +08001/*
2 * Copyright (c) 2014 MediaTek Inc.
3 * Author: James Liao <jamesjj.liao@mediatek.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include <linux/of.h>
15#include <linux/of_address.h>
16#include <linux/io.h>
17#include <linux/slab.h>
18#include <linux/delay.h>
19#include <linux/clkdev.h>
20#include "clk-mtk.h"
21#include "clk-gate.h"
22#define INV_OFS -1
23static int is_subsys_pwr_on(struct mtk_clk_gate *cg)
24{
25 struct pwr_status *pwr = cg->pwr_stat;
26 u32 val = 0, val2 = 0;
27 if (pwr != NULL && cg->pwr_regmap != NULL) {
28 if (pwr->pwr_ofs != INV_OFS && pwr->pwr2_ofs != INV_OFS) {
29 regmap_read(cg->pwr_regmap, pwr->pwr_ofs, &val);
30 regmap_read(cg->pwr_regmap, pwr->pwr2_ofs, &val2);
31 if ((val & pwr->mask) != pwr->val &&
32 (val2 & pwr->mask) != pwr->val)
33 return false;
34 } else if (pwr->other_ofs != INV_OFS) {
35 regmap_read(cg->pwr_regmap, pwr->other_ofs, &val);
36 if ((val & pwr->mask) != pwr->val)
37 return false;
38 }
39 }
40 return true;
41}
42static int mtk_cg_bit_is_cleared(struct clk_hw *hw)
43{
44 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
45 u32 val;
46 regmap_read(cg->regmap, cg->sta_ofs, &val);
47 val &= BIT(cg->bit);
48 return val == 0;
49}
50static int mtk_cg_bit_is_set(struct clk_hw *hw)
51{
52 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
53 u32 val;
54 regmap_read(cg->regmap, cg->sta_ofs, &val);
55 val &= BIT(cg->bit);
56 return val != 0;
57}
58static int mtk_cg_is_enabled(struct clk_hw *hw)
59{
60 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
61 struct clk_hw *p_hw = clk_hw_get_parent(hw);
62 struct clk_hw *mux_hw = clk_hw_get_parent(p_hw);
63 const char *c_n = clk_hw_get_name(hw);
64 const char *mux_n = clk_hw_get_name(mux_hw);
65 pr_notice("%s: c(%s), p(%s) is %s\n", __func__, c_n, mux_n,
66 clk_hw_is_enabled(mux_hw) ? "enabled" : "disabled");
67 if (!clk_hw_is_enabled(mux_hw))
68 return 0;
69 if (!is_subsys_pwr_on(cg))
70 return 0;
71 return mtk_cg_bit_is_cleared(hw);
72}
73static int mtk_en_is_enabled(struct clk_hw *hw)
74{
75 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
76 struct clk_hw *p_hw = clk_hw_get_parent(hw);
77 struct clk_hw *mux_hw = clk_hw_get_parent(p_hw);
78 const char *c_n = clk_hw_get_name(hw);
79 const char *mux_n = clk_hw_get_name(mux_hw);
80 pr_notice("%s: c(%s), p(%s) is %s\n", __func__, c_n, mux_n,
81 clk_hw_is_enabled(mux_hw) ? "enabled" : "disabled");
82 if (!clk_hw_is_enabled(mux_hw))
83 return 0;
84 if (!is_subsys_pwr_on(cg))
85 return 0;
86 return mtk_cg_bit_is_set(hw);
87}
88static void mtk_cg_set_bit(struct clk_hw *hw)
89{
90 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
91#ifdef CONFIG_MACH_MT6853
92 int val = 0;
93 int i = 0;
94#endif
95 regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
96#ifdef CONFIG_MACH_MT6853
97 regmap_read(cg->regmap, cg->sta_ofs, &val);
98 while ((val & BIT(cg->bit)) != BIT(cg->bit)) {
99 regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
100 regmap_read(cg->regmap, cg->sta_ofs, &val);
101 if (i > 5)
102 break;
103 i++;
104 }
105#endif
106}
107static void mtk_cg_clr_bit(struct clk_hw *hw)
108{
109 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
110#ifdef CONFIG_MACH_MT6853
111 int val = 0;
112 int i = 0;
113#endif
114 regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
115#ifdef CONFIG_MACH_MT6853
116 regmap_read(cg->regmap, cg->sta_ofs, &val);
117 while ((val & BIT(cg->bit)) != 0) {
118 regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
119 regmap_read(cg->regmap, cg->sta_ofs, &val);
120 if (i > 5)
121 break;
122 i++;
123 }
124#endif
125}
126static void mtk_cg_set_bit_unused(struct clk_hw *hw)
127{
128 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
129 const char *c_n = clk_hw_get_name(hw);
130 pr_notice("disable_unused - %s\n", c_n);
131 regmap_write(cg->regmap, cg->set_ofs, BIT(cg->bit));
132}
133static void mtk_cg_clr_bit_unused(struct clk_hw *hw)
134{
135 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
136 const char *c_n = clk_hw_get_name(hw);
137 pr_notice("disable_unused - %s\n", c_n);
138 regmap_write(cg->regmap, cg->clr_ofs, BIT(cg->bit));
139}
140static void mtk_cg_set_bit_no_setclr(struct clk_hw *hw)
141{
142 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
143 u32 cgbit = BIT(cg->bit);
144 regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, cgbit);
145}
146static void mtk_cg_clr_bit_no_setclr(struct clk_hw *hw)
147{
148 struct mtk_clk_gate *cg = to_mtk_clk_gate(hw);
149 u32 cgbit = BIT(cg->bit);
150 regmap_update_bits(cg->regmap, cg->sta_ofs, cgbit, 0);
151}
152static int mtk_cg_enable(struct clk_hw *hw)
153{
154 mtk_cg_clr_bit(hw);
155 return 0;
156}
157static void mtk_cg_disable(struct clk_hw *hw)
158{
159 mtk_cg_set_bit(hw);
160}
161static void mtk_cg_disable_unused(struct clk_hw *hw)
162{
163 mtk_cg_set_bit_unused(hw);
164}
165static int mtk_cg_enable_inv(struct clk_hw *hw)
166{
167 mtk_cg_set_bit(hw);
168 return 0;
169}
170static void mtk_cg_disable_inv(struct clk_hw *hw)
171{
172 mtk_cg_clr_bit(hw);
173}
174static void mtk_cg_disable_inv_unused(struct clk_hw *hw)
175{
176 mtk_cg_clr_bit_unused(hw);
177}
178static int mtk_cg_enable_no_setclr(struct clk_hw *hw)
179{
180 mtk_cg_clr_bit_no_setclr(hw);
181 return 0;
182}
183static void mtk_cg_disable_no_setclr(struct clk_hw *hw)
184{
185 mtk_cg_set_bit_no_setclr(hw);
186}
187static int mtk_cg_enable_inv_no_setclr(struct clk_hw *hw)
188{
189 mtk_cg_set_bit_no_setclr(hw);
190 return 0;
191}
192static void mtk_cg_disable_inv_no_setclr(struct clk_hw *hw)
193{
194 mtk_cg_clr_bit_no_setclr(hw);
195}
196const struct clk_ops mtk_clk_gate_ops_setclr = {
197 .is_enabled = mtk_cg_is_enabled,
198 .enable = mtk_cg_enable,
199 .disable = mtk_cg_disable,
200 .disable_unused = mtk_cg_disable_unused,
201};
202const struct clk_ops mtk_clk_gate_ops_setclr_inv = {
203 .is_enabled = mtk_en_is_enabled,
204 .enable = mtk_cg_enable_inv,
205 .disable = mtk_cg_disable_inv,
206 .disable_unused = mtk_cg_disable_inv_unused,
207};
208const struct clk_ops mtk_clk_gate_ops_no_setclr = {
209 .is_enabled = mtk_cg_is_enabled,
210 .enable = mtk_cg_enable_no_setclr,
211 .disable = mtk_cg_disable_no_setclr,
212};
213const struct clk_ops mtk_clk_gate_ops_no_setclr_inv = {
214 .is_enabled = mtk_en_is_enabled,
215 .enable = mtk_cg_enable_inv_no_setclr,
216 .disable = mtk_cg_disable_inv_no_setclr,
217};
218struct clk *mtk_clk_register_gate(
219 const char *name,
220 const char *parent_name,
221 struct regmap *regmap,
222 int set_ofs,
223 int clr_ofs,
224 int sta_ofs,
225 u8 bit,
226 const struct clk_ops *ops,
227 unsigned long flags,
228 struct pwr_status *pwr_stat,
229 struct regmap *pwr_regmap)
230{
231 struct mtk_clk_gate *cg;
232 struct clk *clk;
233 struct clk_init_data init = {};
234 cg = kzalloc(sizeof(*cg), GFP_KERNEL);
235 if (!cg)
236 return ERR_PTR(-ENOMEM);
237 init.name = name;
238 init.flags = flags | CLK_SET_RATE_PARENT;
239 init.parent_names = parent_name ? &parent_name : NULL;
240 init.num_parents = parent_name ? 1 : 0;
241 init.ops = ops;
242 cg->regmap = regmap;
243 cg->set_ofs = set_ofs;
244 cg->clr_ofs = clr_ofs;
245 cg->sta_ofs = sta_ofs;
246 cg->bit = bit;
247 cg->pwr_stat = pwr_stat;
248 cg->pwr_regmap = pwr_regmap;
249 cg->hw.init = &init;
250 clk = clk_register(NULL, &cg->hw);
251 if (IS_ERR(clk))
252 kfree(cg);
253 return clk;
254}