xref: /openbmc/linux/drivers/clk/qcom/clk-rcg2.c (revision 278002edb19bce2c628fafb0af936e77000f3a5b)
17ef6f118SAmit Nischal // SPDX-License-Identifier: GPL-2.0
2bcd61c0fSStephen Boyd /*
3bdc3bbddSAmit Nischal  * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved.
4bcd61c0fSStephen Boyd  */
5bcd61c0fSStephen Boyd 
6bcd61c0fSStephen Boyd #include <linux/kernel.h>
7bcd61c0fSStephen Boyd #include <linux/bitops.h>
8bcd61c0fSStephen Boyd #include <linux/err.h>
9bcd61c0fSStephen Boyd #include <linux/bug.h>
10bcd61c0fSStephen Boyd #include <linux/export.h>
11bcd61c0fSStephen Boyd #include <linux/clk-provider.h>
12bcd61c0fSStephen Boyd #include <linux/delay.h>
13cddf1f82STaniya Das #include <linux/rational.h>
14bcd61c0fSStephen Boyd #include <linux/regmap.h>
1599cbd064SStephen Boyd #include <linux/math64.h>
16d0696770SNikita Travkin #include <linux/minmax.h>
17cc4f6944STaniya Das #include <linux/slab.h>
18bcd61c0fSStephen Boyd 
19bcd61c0fSStephen Boyd #include <asm/div64.h>
20bcd61c0fSStephen Boyd 
21bcd61c0fSStephen Boyd #include "clk-rcg.h"
2250c6a503SStephen Boyd #include "common.h"
23bcd61c0fSStephen Boyd 
24bcd61c0fSStephen Boyd #define CMD_REG			0x0
25bcd61c0fSStephen Boyd #define CMD_UPDATE		BIT(0)
26bcd61c0fSStephen Boyd #define CMD_ROOT_EN		BIT(1)
27bcd61c0fSStephen Boyd #define CMD_DIRTY_CFG		BIT(4)
28bcd61c0fSStephen Boyd #define CMD_DIRTY_N		BIT(5)
29bcd61c0fSStephen Boyd #define CMD_DIRTY_M		BIT(6)
30bcd61c0fSStephen Boyd #define CMD_DIRTY_D		BIT(7)
31bcd61c0fSStephen Boyd #define CMD_ROOT_OFF		BIT(31)
32bcd61c0fSStephen Boyd 
33bcd61c0fSStephen Boyd #define CFG_REG			0x4
34bcd61c0fSStephen Boyd #define CFG_SRC_DIV_SHIFT	0
35bcd61c0fSStephen Boyd #define CFG_SRC_SEL_SHIFT	8
36bcd61c0fSStephen Boyd #define CFG_SRC_SEL_MASK	(0x7 << CFG_SRC_SEL_SHIFT)
37bcd61c0fSStephen Boyd #define CFG_MODE_SHIFT		12
38bcd61c0fSStephen Boyd #define CFG_MODE_MASK		(0x3 << CFG_MODE_SHIFT)
39bcd61c0fSStephen Boyd #define CFG_MODE_DUAL_EDGE	(0x2 << CFG_MODE_SHIFT)
40bdc3bbddSAmit Nischal #define CFG_HW_CLK_CTRL_MASK	BIT(20)
41bcd61c0fSStephen Boyd 
42bcd61c0fSStephen Boyd #define M_REG			0x8
43bcd61c0fSStephen Boyd #define N_REG			0xc
44bcd61c0fSStephen Boyd #define D_REG			0x10
45bcd61c0fSStephen Boyd 
4696dc791dSTaniya Das #define RCG_CFG_OFFSET(rcg)	((rcg)->cmd_rcgr + (rcg)->cfg_off + CFG_REG)
4796dc791dSTaniya Das #define RCG_M_OFFSET(rcg)	((rcg)->cmd_rcgr + (rcg)->cfg_off + M_REG)
4896dc791dSTaniya Das #define RCG_N_OFFSET(rcg)	((rcg)->cmd_rcgr + (rcg)->cfg_off + N_REG)
4996dc791dSTaniya Das #define RCG_D_OFFSET(rcg)	((rcg)->cmd_rcgr + (rcg)->cfg_off + D_REG)
5096dc791dSTaniya Das 
51cc4f6944STaniya Das /* Dynamic Frequency Scaling */
52cc4f6944STaniya Das #define MAX_PERF_LEVEL		8
53cc4f6944STaniya Das #define SE_CMD_DFSR_OFFSET	0x14
54cc4f6944STaniya Das #define SE_CMD_DFS_EN		BIT(0)
55cc4f6944STaniya Das #define SE_PERF_DFSR(level)	(0x1c + 0x4 * (level))
56cc4f6944STaniya Das #define SE_PERF_M_DFSR(level)	(0x5c + 0x4 * (level))
57cc4f6944STaniya Das #define SE_PERF_N_DFSR(level)	(0x9c + 0x4 * (level))
58cc4f6944STaniya Das 
59081ba802SRajendra Nayak enum freq_policy {
60081ba802SRajendra Nayak 	FLOOR,
61081ba802SRajendra Nayak 	CEIL,
62081ba802SRajendra Nayak };
63081ba802SRajendra Nayak 
clk_rcg2_is_enabled(struct clk_hw * hw)64bcd61c0fSStephen Boyd static int clk_rcg2_is_enabled(struct clk_hw *hw)
65bcd61c0fSStephen Boyd {
66bcd61c0fSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
67bcd61c0fSStephen Boyd 	u32 cmd;
68bcd61c0fSStephen Boyd 	int ret;
69bcd61c0fSStephen Boyd 
70bcd61c0fSStephen Boyd 	ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
71bcd61c0fSStephen Boyd 	if (ret)
72bcd61c0fSStephen Boyd 		return ret;
73bcd61c0fSStephen Boyd 
74aa014149SStephen Boyd 	return (cmd & CMD_ROOT_OFF) == 0;
75bcd61c0fSStephen Boyd }
76bcd61c0fSStephen Boyd 
__clk_rcg2_get_parent(struct clk_hw * hw,u32 cfg)77703db1f5SBjorn Andersson static u8 __clk_rcg2_get_parent(struct clk_hw *hw, u32 cfg)
78bcd61c0fSStephen Boyd {
79bcd61c0fSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
80497295afSStephen Boyd 	int num_parents = clk_hw_get_num_parents(hw);
81703db1f5SBjorn Andersson 	int i;
82bcd61c0fSStephen Boyd 
83bcd61c0fSStephen Boyd 	cfg &= CFG_SRC_SEL_MASK;
84bcd61c0fSStephen Boyd 	cfg >>= CFG_SRC_SEL_SHIFT;
85bcd61c0fSStephen Boyd 
86bcd61c0fSStephen Boyd 	for (i = 0; i < num_parents; i++)
87293d2e97SGeorgi Djakov 		if (cfg == rcg->parent_map[i].cfg)
88bcd61c0fSStephen Boyd 			return i;
89bcd61c0fSStephen Boyd 
907f218978SGeorgi Djakov 	pr_debug("%s: Clock %s has invalid parent, using default.\n",
91ac269395SStephen Boyd 		 __func__, clk_hw_get_name(hw));
927f218978SGeorgi Djakov 	return 0;
93bcd61c0fSStephen Boyd }
94bcd61c0fSStephen Boyd 
clk_rcg2_get_parent(struct clk_hw * hw)95703db1f5SBjorn Andersson static u8 clk_rcg2_get_parent(struct clk_hw *hw)
96703db1f5SBjorn Andersson {
97703db1f5SBjorn Andersson 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
98703db1f5SBjorn Andersson 	u32 cfg;
99703db1f5SBjorn Andersson 	int ret;
100703db1f5SBjorn Andersson 
101703db1f5SBjorn Andersson 	ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
102703db1f5SBjorn Andersson 	if (ret) {
103703db1f5SBjorn Andersson 		pr_debug("%s: Unable to read CFG register for %s\n",
104703db1f5SBjorn Andersson 			 __func__, clk_hw_get_name(hw));
105703db1f5SBjorn Andersson 		return 0;
106703db1f5SBjorn Andersson 	}
107703db1f5SBjorn Andersson 
108703db1f5SBjorn Andersson 	return __clk_rcg2_get_parent(hw, cfg);
109703db1f5SBjorn Andersson }
110703db1f5SBjorn Andersson 
update_config(struct clk_rcg2 * rcg)111bcd61c0fSStephen Boyd static int update_config(struct clk_rcg2 *rcg)
112bcd61c0fSStephen Boyd {
113bcd61c0fSStephen Boyd 	int count, ret;
114bcd61c0fSStephen Boyd 	u32 cmd;
115bcd61c0fSStephen Boyd 	struct clk_hw *hw = &rcg->clkr.hw;
116ac269395SStephen Boyd 	const char *name = clk_hw_get_name(hw);
117bcd61c0fSStephen Boyd 
118bcd61c0fSStephen Boyd 	ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
119bcd61c0fSStephen Boyd 				 CMD_UPDATE, CMD_UPDATE);
120bcd61c0fSStephen Boyd 	if (ret)
121bcd61c0fSStephen Boyd 		return ret;
122bcd61c0fSStephen Boyd 
123bcd61c0fSStephen Boyd 	/* Wait for update to take effect */
124bcd61c0fSStephen Boyd 	for (count = 500; count > 0; count--) {
125bcd61c0fSStephen Boyd 		ret = regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG, &cmd);
126bcd61c0fSStephen Boyd 		if (ret)
127bcd61c0fSStephen Boyd 			return ret;
128bcd61c0fSStephen Boyd 		if (!(cmd & CMD_UPDATE))
129bcd61c0fSStephen Boyd 			return 0;
130bcd61c0fSStephen Boyd 		udelay(1);
131bcd61c0fSStephen Boyd 	}
132bcd61c0fSStephen Boyd 
133bcd61c0fSStephen Boyd 	WARN(1, "%s: rcg didn't update its configuration.", name);
13421ea4b62STaniya Das 	return -EBUSY;
135bcd61c0fSStephen Boyd }
136bcd61c0fSStephen Boyd 
clk_rcg2_set_parent(struct clk_hw * hw,u8 index)137bcd61c0fSStephen Boyd static int clk_rcg2_set_parent(struct clk_hw *hw, u8 index)
138bcd61c0fSStephen Boyd {
139bcd61c0fSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
140bcd61c0fSStephen Boyd 	int ret;
141293d2e97SGeorgi Djakov 	u32 cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
142bcd61c0fSStephen Boyd 
14396dc791dSTaniya Das 	ret = regmap_update_bits(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg),
144293d2e97SGeorgi Djakov 				 CFG_SRC_SEL_MASK, cfg);
145bcd61c0fSStephen Boyd 	if (ret)
146bcd61c0fSStephen Boyd 		return ret;
147bcd61c0fSStephen Boyd 
148bcd61c0fSStephen Boyd 	return update_config(rcg);
149bcd61c0fSStephen Boyd }
150bcd61c0fSStephen Boyd 
151bcd61c0fSStephen Boyd /*
152bcd61c0fSStephen Boyd  * Calculate m/n:d rate
153bcd61c0fSStephen Boyd  *
154bcd61c0fSStephen Boyd  *          parent_rate     m
155bcd61c0fSStephen Boyd  *   rate = ----------- x  ---
156bcd61c0fSStephen Boyd  *            hid_div       n
157bcd61c0fSStephen Boyd  */
158bcd61c0fSStephen Boyd static unsigned long
calc_rate(unsigned long rate,u32 m,u32 n,u32 mode,u32 hid_div)159bcd61c0fSStephen Boyd calc_rate(unsigned long rate, u32 m, u32 n, u32 mode, u32 hid_div)
160bcd61c0fSStephen Boyd {
16150b5ee6fSDevi Priya 	if (hid_div)
16250b5ee6fSDevi Priya 		rate = mult_frac(rate, 2, hid_div + 1);
163bcd61c0fSStephen Boyd 
16450b5ee6fSDevi Priya 	if (mode)
16550b5ee6fSDevi Priya 		rate = mult_frac(rate, m, n);
166bcd61c0fSStephen Boyd 
167bcd61c0fSStephen Boyd 	return rate;
168bcd61c0fSStephen Boyd }
169bcd61c0fSStephen Boyd 
170bcd61c0fSStephen Boyd static unsigned long
__clk_rcg2_recalc_rate(struct clk_hw * hw,unsigned long parent_rate,u32 cfg)171703db1f5SBjorn Andersson __clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate, u32 cfg)
172bcd61c0fSStephen Boyd {
173bcd61c0fSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
174703db1f5SBjorn Andersson 	u32 hid_div, m = 0, n = 0, mode = 0, mask;
175bcd61c0fSStephen Boyd 
176bcd61c0fSStephen Boyd 	if (rcg->mnd_width) {
177bcd61c0fSStephen Boyd 		mask = BIT(rcg->mnd_width) - 1;
17896dc791dSTaniya Das 		regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m);
179bcd61c0fSStephen Boyd 		m &= mask;
18096dc791dSTaniya Das 		regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &n);
181bcd61c0fSStephen Boyd 		n =  ~n;
182bcd61c0fSStephen Boyd 		n &= mask;
183bcd61c0fSStephen Boyd 		n += m;
184bcd61c0fSStephen Boyd 		mode = cfg & CFG_MODE_MASK;
185bcd61c0fSStephen Boyd 		mode >>= CFG_MODE_SHIFT;
186bcd61c0fSStephen Boyd 	}
187bcd61c0fSStephen Boyd 
188bcd61c0fSStephen Boyd 	mask = BIT(rcg->hid_width) - 1;
189bcd61c0fSStephen Boyd 	hid_div = cfg >> CFG_SRC_DIV_SHIFT;
190bcd61c0fSStephen Boyd 	hid_div &= mask;
191bcd61c0fSStephen Boyd 
192bcd61c0fSStephen Boyd 	return calc_rate(parent_rate, m, n, mode, hid_div);
193bcd61c0fSStephen Boyd }
194bcd61c0fSStephen Boyd 
195703db1f5SBjorn Andersson static unsigned long
clk_rcg2_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)196703db1f5SBjorn Andersson clk_rcg2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
197703db1f5SBjorn Andersson {
198703db1f5SBjorn Andersson 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
199703db1f5SBjorn Andersson 	u32 cfg;
200703db1f5SBjorn Andersson 
201703db1f5SBjorn Andersson 	regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
202703db1f5SBjorn Andersson 
203703db1f5SBjorn Andersson 	return __clk_rcg2_recalc_rate(hw, parent_rate, cfg);
204703db1f5SBjorn Andersson }
205703db1f5SBjorn Andersson 
_freq_tbl_determine_rate(struct clk_hw * hw,const struct freq_tbl * f,struct clk_rate_request * req,enum freq_policy policy)206081ba802SRajendra Nayak static int _freq_tbl_determine_rate(struct clk_hw *hw, const struct freq_tbl *f,
207081ba802SRajendra Nayak 				    struct clk_rate_request *req,
208081ba802SRajendra Nayak 				    enum freq_policy policy)
209bcd61c0fSStephen Boyd {
2100817b62cSBoris Brezillon 	unsigned long clk_flags, rate = req->rate;
211ac269395SStephen Boyd 	struct clk_hw *p;
2122f272e7bSGeorgi Djakov 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
2132f272e7bSGeorgi Djakov 	int index;
214bcd61c0fSStephen Boyd 
215081ba802SRajendra Nayak 	switch (policy) {
216081ba802SRajendra Nayak 	case FLOOR:
217081ba802SRajendra Nayak 		f = qcom_find_freq_floor(f, rate);
218081ba802SRajendra Nayak 		break;
219081ba802SRajendra Nayak 	case CEIL:
22050c6a503SStephen Boyd 		f = qcom_find_freq(f, rate);
221081ba802SRajendra Nayak 		break;
222081ba802SRajendra Nayak 	default:
223081ba802SRajendra Nayak 		return -EINVAL;
22457b2364dSYueHaibing 	}
225081ba802SRajendra Nayak 
226bcd61c0fSStephen Boyd 	if (!f)
227bcd61c0fSStephen Boyd 		return -EINVAL;
228bcd61c0fSStephen Boyd 
2292f272e7bSGeorgi Djakov 	index = qcom_find_src_index(hw, rcg->parent_map, f->src);
2302f272e7bSGeorgi Djakov 	if (index < 0)
2312f272e7bSGeorgi Djakov 		return index;
2322f272e7bSGeorgi Djakov 
23398d8a60eSStephen Boyd 	clk_flags = clk_hw_get_flags(hw);
234ac269395SStephen Boyd 	p = clk_hw_get_parent_by_index(hw, index);
235908b0501SDouglas Anderson 	if (!p)
236908b0501SDouglas Anderson 		return -EINVAL;
237908b0501SDouglas Anderson 
238bcd61c0fSStephen Boyd 	if (clk_flags & CLK_SET_RATE_PARENT) {
239c7d2a0ebSEvan Green 		rate = f->freq;
240bcd61c0fSStephen Boyd 		if (f->pre_div) {
241efd164b5SJeffrey Hugo 			if (!rate)
242efd164b5SJeffrey Hugo 				rate = req->rate;
243bcd61c0fSStephen Boyd 			rate /= 2;
244bcd61c0fSStephen Boyd 			rate *= f->pre_div + 1;
245bcd61c0fSStephen Boyd 		}
246bcd61c0fSStephen Boyd 
247bcd61c0fSStephen Boyd 		if (f->n) {
248bcd61c0fSStephen Boyd 			u64 tmp = rate;
249bcd61c0fSStephen Boyd 			tmp = tmp * f->n;
250bcd61c0fSStephen Boyd 			do_div(tmp, f->m);
251bcd61c0fSStephen Boyd 			rate = tmp;
252bcd61c0fSStephen Boyd 		}
253bcd61c0fSStephen Boyd 	} else {
254ac269395SStephen Boyd 		rate =  clk_hw_get_rate(p);
255bcd61c0fSStephen Boyd 	}
256ac269395SStephen Boyd 	req->best_parent_hw = p;
2570817b62cSBoris Brezillon 	req->best_parent_rate = rate;
2580817b62cSBoris Brezillon 	req->rate = f->freq;
259bcd61c0fSStephen Boyd 
2600817b62cSBoris Brezillon 	return 0;
261bcd61c0fSStephen Boyd }
262bcd61c0fSStephen Boyd 
clk_rcg2_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)2630817b62cSBoris Brezillon static int clk_rcg2_determine_rate(struct clk_hw *hw,
2640817b62cSBoris Brezillon 				   struct clk_rate_request *req)
265bcd61c0fSStephen Boyd {
266bcd61c0fSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
267bcd61c0fSStephen Boyd 
268081ba802SRajendra Nayak 	return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, CEIL);
269081ba802SRajendra Nayak }
270081ba802SRajendra Nayak 
clk_rcg2_determine_floor_rate(struct clk_hw * hw,struct clk_rate_request * req)271081ba802SRajendra Nayak static int clk_rcg2_determine_floor_rate(struct clk_hw *hw,
272081ba802SRajendra Nayak 					 struct clk_rate_request *req)
273081ba802SRajendra Nayak {
274081ba802SRajendra Nayak 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
275081ba802SRajendra Nayak 
276081ba802SRajendra Nayak 	return _freq_tbl_determine_rate(hw, rcg->freq_tbl, req, FLOOR);
277bcd61c0fSStephen Boyd }
278bcd61c0fSStephen Boyd 
__clk_rcg2_configure(struct clk_rcg2 * rcg,const struct freq_tbl * f,u32 * _cfg)279703db1f5SBjorn Andersson static int __clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f,
280703db1f5SBjorn Andersson 				u32 *_cfg)
281bcd61c0fSStephen Boyd {
28258922910STaniya Das 	u32 cfg, mask, d_val, not2d_val, n_minus_m;
283293d2e97SGeorgi Djakov 	struct clk_hw *hw = &rcg->clkr.hw;
284293d2e97SGeorgi Djakov 	int ret, index = qcom_find_src_index(hw, rcg->parent_map, f->src);
285293d2e97SGeorgi Djakov 
286293d2e97SGeorgi Djakov 	if (index < 0)
287293d2e97SGeorgi Djakov 		return index;
288bcd61c0fSStephen Boyd 
289bcd61c0fSStephen Boyd 	if (rcg->mnd_width && f->n) {
290bcd61c0fSStephen Boyd 		mask = BIT(rcg->mnd_width) - 1;
29199cbd064SStephen Boyd 		ret = regmap_update_bits(rcg->clkr.regmap,
29296dc791dSTaniya Das 				RCG_M_OFFSET(rcg), mask, f->m);
293bcd61c0fSStephen Boyd 		if (ret)
294bcd61c0fSStephen Boyd 			return ret;
295bcd61c0fSStephen Boyd 
29699cbd064SStephen Boyd 		ret = regmap_update_bits(rcg->clkr.regmap,
29796dc791dSTaniya Das 				RCG_N_OFFSET(rcg), mask, ~(f->n - f->m));
298bcd61c0fSStephen Boyd 		if (ret)
299bcd61c0fSStephen Boyd 			return ret;
300bcd61c0fSStephen Boyd 
30158922910STaniya Das 		/* Calculate 2d value */
30258922910STaniya Das 		d_val = f->n;
30358922910STaniya Das 
30458922910STaniya Das 		n_minus_m = f->n - f->m;
30558922910STaniya Das 		n_minus_m *= 2;
30658922910STaniya Das 
30758922910STaniya Das 		d_val = clamp_t(u32, d_val, f->m, n_minus_m);
30858922910STaniya Das 		not2d_val = ~d_val & mask;
30958922910STaniya Das 
31099cbd064SStephen Boyd 		ret = regmap_update_bits(rcg->clkr.regmap,
31158922910STaniya Das 				RCG_D_OFFSET(rcg), mask, not2d_val);
312bcd61c0fSStephen Boyd 		if (ret)
313bcd61c0fSStephen Boyd 			return ret;
314bcd61c0fSStephen Boyd 	}
315bcd61c0fSStephen Boyd 
316bcd61c0fSStephen Boyd 	mask = BIT(rcg->hid_width) - 1;
317bdc3bbddSAmit Nischal 	mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK;
318bcd61c0fSStephen Boyd 	cfg = f->pre_div << CFG_SRC_DIV_SHIFT;
319293d2e97SGeorgi Djakov 	cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
3200b21503dSArchit Taneja 	if (rcg->mnd_width && f->n && (f->m != f->n))
321bcd61c0fSStephen Boyd 		cfg |= CFG_MODE_DUAL_EDGE;
322a0e0ec74SKonrad Dybcio 	if (rcg->hw_clk_ctrl)
323a0e0ec74SKonrad Dybcio 		cfg |= CFG_HW_CLK_CTRL_MASK;
324703db1f5SBjorn Andersson 
325703db1f5SBjorn Andersson 	*_cfg &= ~mask;
326703db1f5SBjorn Andersson 	*_cfg |= cfg;
327703db1f5SBjorn Andersson 
328703db1f5SBjorn Andersson 	return 0;
3297ef6f118SAmit Nischal }
3307ef6f118SAmit Nischal 
clk_rcg2_configure(struct clk_rcg2 * rcg,const struct freq_tbl * f)3317ef6f118SAmit Nischal static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f)
3327ef6f118SAmit Nischal {
333703db1f5SBjorn Andersson 	u32 cfg;
3347ef6f118SAmit Nischal 	int ret;
3357ef6f118SAmit Nischal 
336703db1f5SBjorn Andersson 	ret = regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
337703db1f5SBjorn Andersson 	if (ret)
338703db1f5SBjorn Andersson 		return ret;
339703db1f5SBjorn Andersson 
340703db1f5SBjorn Andersson 	ret = __clk_rcg2_configure(rcg, f, &cfg);
341703db1f5SBjorn Andersson 	if (ret)
342703db1f5SBjorn Andersson 		return ret;
343703db1f5SBjorn Andersson 
344703db1f5SBjorn Andersson 	ret = regmap_write(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), cfg);
345bcd61c0fSStephen Boyd 	if (ret)
346bcd61c0fSStephen Boyd 		return ret;
347bcd61c0fSStephen Boyd 
348bcd61c0fSStephen Boyd 	return update_config(rcg);
349bcd61c0fSStephen Boyd }
350bcd61c0fSStephen Boyd 
__clk_rcg2_set_rate(struct clk_hw * hw,unsigned long rate,enum freq_policy policy)351081ba802SRajendra Nayak static int __clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
352081ba802SRajendra Nayak 			       enum freq_policy policy)
35399cbd064SStephen Boyd {
35499cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
35599cbd064SStephen Boyd 	const struct freq_tbl *f;
35699cbd064SStephen Boyd 
357081ba802SRajendra Nayak 	switch (policy) {
358081ba802SRajendra Nayak 	case FLOOR:
359081ba802SRajendra Nayak 		f = qcom_find_freq_floor(rcg->freq_tbl, rate);
360081ba802SRajendra Nayak 		break;
361081ba802SRajendra Nayak 	case CEIL:
36250c6a503SStephen Boyd 		f = qcom_find_freq(rcg->freq_tbl, rate);
363081ba802SRajendra Nayak 		break;
364081ba802SRajendra Nayak 	default:
365081ba802SRajendra Nayak 		return -EINVAL;
36657b2364dSYueHaibing 	}
367081ba802SRajendra Nayak 
36899cbd064SStephen Boyd 	if (!f)
36999cbd064SStephen Boyd 		return -EINVAL;
37099cbd064SStephen Boyd 
37199cbd064SStephen Boyd 	return clk_rcg2_configure(rcg, f);
37299cbd064SStephen Boyd }
37399cbd064SStephen Boyd 
clk_rcg2_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)374bcd61c0fSStephen Boyd static int clk_rcg2_set_rate(struct clk_hw *hw, unsigned long rate,
375bcd61c0fSStephen Boyd 			    unsigned long parent_rate)
376bcd61c0fSStephen Boyd {
377081ba802SRajendra Nayak 	return __clk_rcg2_set_rate(hw, rate, CEIL);
378081ba802SRajendra Nayak }
379081ba802SRajendra Nayak 
clk_rcg2_set_floor_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)380081ba802SRajendra Nayak static int clk_rcg2_set_floor_rate(struct clk_hw *hw, unsigned long rate,
381081ba802SRajendra Nayak 				   unsigned long parent_rate)
382081ba802SRajendra Nayak {
383081ba802SRajendra Nayak 	return __clk_rcg2_set_rate(hw, rate, FLOOR);
384bcd61c0fSStephen Boyd }
385bcd61c0fSStephen Boyd 
clk_rcg2_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)386bcd61c0fSStephen Boyd static int clk_rcg2_set_rate_and_parent(struct clk_hw *hw,
387bcd61c0fSStephen Boyd 		unsigned long rate, unsigned long parent_rate, u8 index)
388bcd61c0fSStephen Boyd {
389081ba802SRajendra Nayak 	return __clk_rcg2_set_rate(hw, rate, CEIL);
390081ba802SRajendra Nayak }
391081ba802SRajendra Nayak 
clk_rcg2_set_floor_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)392081ba802SRajendra Nayak static int clk_rcg2_set_floor_rate_and_parent(struct clk_hw *hw,
393081ba802SRajendra Nayak 		unsigned long rate, unsigned long parent_rate, u8 index)
394081ba802SRajendra Nayak {
395081ba802SRajendra Nayak 	return __clk_rcg2_set_rate(hw, rate, FLOOR);
396bcd61c0fSStephen Boyd }
397bcd61c0fSStephen Boyd 
clk_rcg2_get_duty_cycle(struct clk_hw * hw,struct clk_duty * duty)3987f891fafSTaniya Das static int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
3997f891fafSTaniya Das {
4007f891fafSTaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
4017f891fafSTaniya Das 	u32 notn_m, n, m, d, not2d, mask;
4027f891fafSTaniya Das 
4037f891fafSTaniya Das 	if (!rcg->mnd_width) {
4047f891fafSTaniya Das 		/* 50 % duty-cycle for Non-MND RCGs */
4057f891fafSTaniya Das 		duty->num = 1;
4067f891fafSTaniya Das 		duty->den = 2;
4077f891fafSTaniya Das 		return 0;
4087f891fafSTaniya Das 	}
4097f891fafSTaniya Das 
4107f891fafSTaniya Das 	regmap_read(rcg->clkr.regmap, RCG_D_OFFSET(rcg), &not2d);
4117f891fafSTaniya Das 	regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m);
4127f891fafSTaniya Das 	regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &notn_m);
4137f891fafSTaniya Das 
4147f891fafSTaniya Das 	if (!not2d && !m && !notn_m) {
4157f891fafSTaniya Das 		/* 50 % duty-cycle always */
4167f891fafSTaniya Das 		duty->num = 1;
4177f891fafSTaniya Das 		duty->den = 2;
4187f891fafSTaniya Das 		return 0;
4197f891fafSTaniya Das 	}
4207f891fafSTaniya Das 
4217f891fafSTaniya Das 	mask = BIT(rcg->mnd_width) - 1;
4227f891fafSTaniya Das 
4237f891fafSTaniya Das 	d = ~(not2d) & mask;
4247f891fafSTaniya Das 	d = DIV_ROUND_CLOSEST(d, 2);
4257f891fafSTaniya Das 
4267f891fafSTaniya Das 	n = (~(notn_m) + m) & mask;
4277f891fafSTaniya Das 
4287f891fafSTaniya Das 	duty->num = d;
4297f891fafSTaniya Das 	duty->den = n;
4307f891fafSTaniya Das 
4317f891fafSTaniya Das 	return 0;
4327f891fafSTaniya Das }
4337f891fafSTaniya Das 
clk_rcg2_set_duty_cycle(struct clk_hw * hw,struct clk_duty * duty)4347f891fafSTaniya Das static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty)
4357f891fafSTaniya Das {
4367f891fafSTaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
437bdafb609SNikita Travkin 	u32 notn_m, n, m, d, not2d, mask, duty_per, cfg;
4387f891fafSTaniya Das 	int ret;
4397f891fafSTaniya Das 
4407f891fafSTaniya Das 	/* Duty-cycle cannot be modified for non-MND RCGs */
4417f891fafSTaniya Das 	if (!rcg->mnd_width)
4427f891fafSTaniya Das 		return -EINVAL;
4437f891fafSTaniya Das 
4447f891fafSTaniya Das 	mask = BIT(rcg->mnd_width) - 1;
4457f891fafSTaniya Das 
4467f891fafSTaniya Das 	regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), &notn_m);
4477f891fafSTaniya Das 	regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m);
448bdafb609SNikita Travkin 	regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg);
449bdafb609SNikita Travkin 
450bdafb609SNikita Travkin 	/* Duty-cycle cannot be modified if MND divider is in bypass mode. */
451bdafb609SNikita Travkin 	if (!(cfg & CFG_MODE_MASK))
452bdafb609SNikita Travkin 		return -EINVAL;
4537f891fafSTaniya Das 
4547f891fafSTaniya Das 	n = (~(notn_m) + m) & mask;
4557f891fafSTaniya Das 
4567f891fafSTaniya Das 	duty_per = (duty->num * 100) / duty->den;
4577f891fafSTaniya Das 
4587f891fafSTaniya Das 	/* Calculate 2d value */
4597f891fafSTaniya Das 	d = DIV_ROUND_CLOSEST(n * duty_per * 2, 100);
4607f891fafSTaniya Das 
461d0696770SNikita Travkin 	/*
462d0696770SNikita Travkin 	 * Check bit widths of 2d. If D is too big reduce duty cycle.
463d0696770SNikita Travkin 	 * Also make sure it is never zero.
464d0696770SNikita Travkin 	 */
465d0696770SNikita Travkin 	d = clamp_val(d, 1, mask);
4667f891fafSTaniya Das 
4677f891fafSTaniya Das 	if ((d / 2) > (n - m))
4687f891fafSTaniya Das 		d = (n - m) * 2;
4697f891fafSTaniya Das 	else if ((d / 2) < (m / 2))
4707f891fafSTaniya Das 		d = m;
4717f891fafSTaniya Das 
4727f891fafSTaniya Das 	not2d = ~d & mask;
4737f891fafSTaniya Das 
4747f891fafSTaniya Das 	ret = regmap_update_bits(rcg->clkr.regmap, RCG_D_OFFSET(rcg), mask,
4757f891fafSTaniya Das 				 not2d);
4767f891fafSTaniya Das 	if (ret)
4777f891fafSTaniya Das 		return ret;
4787f891fafSTaniya Das 
4797f891fafSTaniya Das 	return update_config(rcg);
4807f891fafSTaniya Das }
4817f891fafSTaniya Das 
482bcd61c0fSStephen Boyd const struct clk_ops clk_rcg2_ops = {
483bcd61c0fSStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
484bcd61c0fSStephen Boyd 	.get_parent = clk_rcg2_get_parent,
485bcd61c0fSStephen Boyd 	.set_parent = clk_rcg2_set_parent,
486bcd61c0fSStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
487bcd61c0fSStephen Boyd 	.determine_rate = clk_rcg2_determine_rate,
488bcd61c0fSStephen Boyd 	.set_rate = clk_rcg2_set_rate,
489bcd61c0fSStephen Boyd 	.set_rate_and_parent = clk_rcg2_set_rate_and_parent,
4907f891fafSTaniya Das 	.get_duty_cycle = clk_rcg2_get_duty_cycle,
4917f891fafSTaniya Das 	.set_duty_cycle = clk_rcg2_set_duty_cycle,
492bcd61c0fSStephen Boyd };
493bcd61c0fSStephen Boyd EXPORT_SYMBOL_GPL(clk_rcg2_ops);
49499cbd064SStephen Boyd 
495081ba802SRajendra Nayak const struct clk_ops clk_rcg2_floor_ops = {
496081ba802SRajendra Nayak 	.is_enabled = clk_rcg2_is_enabled,
497081ba802SRajendra Nayak 	.get_parent = clk_rcg2_get_parent,
498081ba802SRajendra Nayak 	.set_parent = clk_rcg2_set_parent,
499081ba802SRajendra Nayak 	.recalc_rate = clk_rcg2_recalc_rate,
500081ba802SRajendra Nayak 	.determine_rate = clk_rcg2_determine_floor_rate,
501081ba802SRajendra Nayak 	.set_rate = clk_rcg2_set_floor_rate,
502081ba802SRajendra Nayak 	.set_rate_and_parent = clk_rcg2_set_floor_rate_and_parent,
5037f891fafSTaniya Das 	.get_duty_cycle = clk_rcg2_get_duty_cycle,
5047f891fafSTaniya Das 	.set_duty_cycle = clk_rcg2_set_duty_cycle,
505081ba802SRajendra Nayak };
506081ba802SRajendra Nayak EXPORT_SYMBOL_GPL(clk_rcg2_floor_ops);
507081ba802SRajendra Nayak 
508c5d2c96bSChristian Marangi const struct clk_ops clk_rcg2_mux_closest_ops = {
509c5d2c96bSChristian Marangi 	.determine_rate = __clk_mux_determine_rate_closest,
510c5d2c96bSChristian Marangi 	.get_parent = clk_rcg2_get_parent,
511c5d2c96bSChristian Marangi 	.set_parent = clk_rcg2_set_parent,
512c5d2c96bSChristian Marangi };
513c5d2c96bSChristian Marangi EXPORT_SYMBOL_GPL(clk_rcg2_mux_closest_ops);
514c5d2c96bSChristian Marangi 
51599cbd064SStephen Boyd struct frac_entry {
51699cbd064SStephen Boyd 	int num;
51799cbd064SStephen Boyd 	int den;
51899cbd064SStephen Boyd };
51999cbd064SStephen Boyd 
52099cbd064SStephen Boyd static const struct frac_entry frac_table_675m[] = {	/* link rate of 270M */
52199cbd064SStephen Boyd 	{ 52, 295 },	/* 119 M */
52299cbd064SStephen Boyd 	{ 11, 57 },	/* 130.25 M */
52399cbd064SStephen Boyd 	{ 63, 307 },	/* 138.50 M */
52499cbd064SStephen Boyd 	{ 11, 50 },	/* 148.50 M */
52599cbd064SStephen Boyd 	{ 47, 206 },	/* 154 M */
52699cbd064SStephen Boyd 	{ 31, 100 },	/* 205.25 M */
52799cbd064SStephen Boyd 	{ 107, 269 },	/* 268.50 M */
52899cbd064SStephen Boyd 	{ },
52999cbd064SStephen Boyd };
53099cbd064SStephen Boyd 
53199cbd064SStephen Boyd static struct frac_entry frac_table_810m[] = { /* Link rate of 162M */
53299cbd064SStephen Boyd 	{ 31, 211 },	/* 119 M */
53399cbd064SStephen Boyd 	{ 32, 199 },	/* 130.25 M */
53499cbd064SStephen Boyd 	{ 63, 307 },	/* 138.50 M */
53599cbd064SStephen Boyd 	{ 11, 60 },	/* 148.50 M */
53699cbd064SStephen Boyd 	{ 50, 263 },	/* 154 M */
53799cbd064SStephen Boyd 	{ 31, 120 },	/* 205.25 M */
53899cbd064SStephen Boyd 	{ 119, 359 },	/* 268.50 M */
53999cbd064SStephen Boyd 	{ },
54099cbd064SStephen Boyd };
54199cbd064SStephen Boyd 
clk_edp_pixel_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)54299cbd064SStephen Boyd static int clk_edp_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
54399cbd064SStephen Boyd 			      unsigned long parent_rate)
54499cbd064SStephen Boyd {
54599cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
54699cbd064SStephen Boyd 	struct freq_tbl f = *rcg->freq_tbl;
54799cbd064SStephen Boyd 	const struct frac_entry *frac;
54899cbd064SStephen Boyd 	int delta = 100000;
54999cbd064SStephen Boyd 	s64 src_rate = parent_rate;
55099cbd064SStephen Boyd 	s64 request;
55199cbd064SStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
55299cbd064SStephen Boyd 	u32 hid_div;
55399cbd064SStephen Boyd 
55499cbd064SStephen Boyd 	if (src_rate == 810000000)
55599cbd064SStephen Boyd 		frac = frac_table_810m;
55699cbd064SStephen Boyd 	else
55799cbd064SStephen Boyd 		frac = frac_table_675m;
55899cbd064SStephen Boyd 
55999cbd064SStephen Boyd 	for (; frac->num; frac++) {
56099cbd064SStephen Boyd 		request = rate;
56199cbd064SStephen Boyd 		request *= frac->den;
56299cbd064SStephen Boyd 		request = div_s64(request, frac->num);
56399cbd064SStephen Boyd 		if ((src_rate < (request - delta)) ||
56499cbd064SStephen Boyd 		    (src_rate > (request + delta)))
56599cbd064SStephen Boyd 			continue;
56699cbd064SStephen Boyd 
56799cbd064SStephen Boyd 		regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
56899cbd064SStephen Boyd 				&hid_div);
56999cbd064SStephen Boyd 		f.pre_div = hid_div;
57099cbd064SStephen Boyd 		f.pre_div >>= CFG_SRC_DIV_SHIFT;
57199cbd064SStephen Boyd 		f.pre_div &= mask;
57299cbd064SStephen Boyd 		f.m = frac->num;
57399cbd064SStephen Boyd 		f.n = frac->den;
57499cbd064SStephen Boyd 
57599cbd064SStephen Boyd 		return clk_rcg2_configure(rcg, &f);
57699cbd064SStephen Boyd 	}
57799cbd064SStephen Boyd 
57899cbd064SStephen Boyd 	return -EINVAL;
57999cbd064SStephen Boyd }
58099cbd064SStephen Boyd 
clk_edp_pixel_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)58199cbd064SStephen Boyd static int clk_edp_pixel_set_rate_and_parent(struct clk_hw *hw,
58299cbd064SStephen Boyd 		unsigned long rate, unsigned long parent_rate, u8 index)
58399cbd064SStephen Boyd {
58499cbd064SStephen Boyd 	/* Parent index is set statically in frequency table */
58599cbd064SStephen Boyd 	return clk_edp_pixel_set_rate(hw, rate, parent_rate);
58699cbd064SStephen Boyd }
58799cbd064SStephen Boyd 
clk_edp_pixel_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)5880817b62cSBoris Brezillon static int clk_edp_pixel_determine_rate(struct clk_hw *hw,
5890817b62cSBoris Brezillon 					struct clk_rate_request *req)
59099cbd064SStephen Boyd {
59199cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
59299cbd064SStephen Boyd 	const struct freq_tbl *f = rcg->freq_tbl;
59399cbd064SStephen Boyd 	const struct frac_entry *frac;
59499cbd064SStephen Boyd 	int delta = 100000;
59599cbd064SStephen Boyd 	s64 request;
59699cbd064SStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
59799cbd064SStephen Boyd 	u32 hid_div;
5982f272e7bSGeorgi Djakov 	int index = qcom_find_src_index(hw, rcg->parent_map, f->src);
59999cbd064SStephen Boyd 
60099cbd064SStephen Boyd 	/* Force the correct parent */
601ac269395SStephen Boyd 	req->best_parent_hw = clk_hw_get_parent_by_index(hw, index);
602ac269395SStephen Boyd 	req->best_parent_rate = clk_hw_get_rate(req->best_parent_hw);
60399cbd064SStephen Boyd 
6040817b62cSBoris Brezillon 	if (req->best_parent_rate == 810000000)
60599cbd064SStephen Boyd 		frac = frac_table_810m;
60699cbd064SStephen Boyd 	else
60799cbd064SStephen Boyd 		frac = frac_table_675m;
60899cbd064SStephen Boyd 
60999cbd064SStephen Boyd 	for (; frac->num; frac++) {
6100817b62cSBoris Brezillon 		request = req->rate;
61199cbd064SStephen Boyd 		request *= frac->den;
61299cbd064SStephen Boyd 		request = div_s64(request, frac->num);
6130817b62cSBoris Brezillon 		if ((req->best_parent_rate < (request - delta)) ||
6140817b62cSBoris Brezillon 		    (req->best_parent_rate > (request + delta)))
61599cbd064SStephen Boyd 			continue;
61699cbd064SStephen Boyd 
61799cbd064SStephen Boyd 		regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
61899cbd064SStephen Boyd 				&hid_div);
61999cbd064SStephen Boyd 		hid_div >>= CFG_SRC_DIV_SHIFT;
62099cbd064SStephen Boyd 		hid_div &= mask;
62199cbd064SStephen Boyd 
6220817b62cSBoris Brezillon 		req->rate = calc_rate(req->best_parent_rate,
6230817b62cSBoris Brezillon 				      frac->num, frac->den,
6240817b62cSBoris Brezillon 				      !!frac->den, hid_div);
6250817b62cSBoris Brezillon 		return 0;
62699cbd064SStephen Boyd 	}
62799cbd064SStephen Boyd 
62899cbd064SStephen Boyd 	return -EINVAL;
62999cbd064SStephen Boyd }
63099cbd064SStephen Boyd 
63199cbd064SStephen Boyd const struct clk_ops clk_edp_pixel_ops = {
63299cbd064SStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
63399cbd064SStephen Boyd 	.get_parent = clk_rcg2_get_parent,
63499cbd064SStephen Boyd 	.set_parent = clk_rcg2_set_parent,
63599cbd064SStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
63699cbd064SStephen Boyd 	.set_rate = clk_edp_pixel_set_rate,
63799cbd064SStephen Boyd 	.set_rate_and_parent = clk_edp_pixel_set_rate_and_parent,
63899cbd064SStephen Boyd 	.determine_rate = clk_edp_pixel_determine_rate,
63999cbd064SStephen Boyd };
64099cbd064SStephen Boyd EXPORT_SYMBOL_GPL(clk_edp_pixel_ops);
64199cbd064SStephen Boyd 
clk_byte_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)6420817b62cSBoris Brezillon static int clk_byte_determine_rate(struct clk_hw *hw,
6430817b62cSBoris Brezillon 				   struct clk_rate_request *req)
64499cbd064SStephen Boyd {
64599cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
64699cbd064SStephen Boyd 	const struct freq_tbl *f = rcg->freq_tbl;
6472f272e7bSGeorgi Djakov 	int index = qcom_find_src_index(hw, rcg->parent_map, f->src);
64899cbd064SStephen Boyd 	unsigned long parent_rate, div;
64999cbd064SStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
650ac269395SStephen Boyd 	struct clk_hw *p;
65199cbd064SStephen Boyd 
6520817b62cSBoris Brezillon 	if (req->rate == 0)
65399cbd064SStephen Boyd 		return -EINVAL;
65499cbd064SStephen Boyd 
655ac269395SStephen Boyd 	req->best_parent_hw = p = clk_hw_get_parent_by_index(hw, index);
656ac269395SStephen Boyd 	req->best_parent_rate = parent_rate = clk_hw_round_rate(p, req->rate);
65799cbd064SStephen Boyd 
6580817b62cSBoris Brezillon 	div = DIV_ROUND_UP((2 * parent_rate), req->rate) - 1;
65999cbd064SStephen Boyd 	div = min_t(u32, div, mask);
66099cbd064SStephen Boyd 
6610817b62cSBoris Brezillon 	req->rate = calc_rate(parent_rate, 0, 0, 0, div);
6620817b62cSBoris Brezillon 
6630817b62cSBoris Brezillon 	return 0;
66499cbd064SStephen Boyd }
66599cbd064SStephen Boyd 
clk_byte_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)66699cbd064SStephen Boyd static int clk_byte_set_rate(struct clk_hw *hw, unsigned long rate,
66799cbd064SStephen Boyd 			 unsigned long parent_rate)
66899cbd064SStephen Boyd {
66999cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
67099cbd064SStephen Boyd 	struct freq_tbl f = *rcg->freq_tbl;
67199cbd064SStephen Boyd 	unsigned long div;
67299cbd064SStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
67399cbd064SStephen Boyd 
67499cbd064SStephen Boyd 	div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
67599cbd064SStephen Boyd 	div = min_t(u32, div, mask);
67699cbd064SStephen Boyd 
67799cbd064SStephen Boyd 	f.pre_div = div;
67899cbd064SStephen Boyd 
67999cbd064SStephen Boyd 	return clk_rcg2_configure(rcg, &f);
68099cbd064SStephen Boyd }
68199cbd064SStephen Boyd 
clk_byte_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)68299cbd064SStephen Boyd static int clk_byte_set_rate_and_parent(struct clk_hw *hw,
68399cbd064SStephen Boyd 		unsigned long rate, unsigned long parent_rate, u8 index)
68499cbd064SStephen Boyd {
68599cbd064SStephen Boyd 	/* Parent index is set statically in frequency table */
68699cbd064SStephen Boyd 	return clk_byte_set_rate(hw, rate, parent_rate);
68799cbd064SStephen Boyd }
68899cbd064SStephen Boyd 
68999cbd064SStephen Boyd const struct clk_ops clk_byte_ops = {
69099cbd064SStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
69199cbd064SStephen Boyd 	.get_parent = clk_rcg2_get_parent,
69299cbd064SStephen Boyd 	.set_parent = clk_rcg2_set_parent,
69399cbd064SStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
69499cbd064SStephen Boyd 	.set_rate = clk_byte_set_rate,
69599cbd064SStephen Boyd 	.set_rate_and_parent = clk_byte_set_rate_and_parent,
69699cbd064SStephen Boyd 	.determine_rate = clk_byte_determine_rate,
69799cbd064SStephen Boyd };
69899cbd064SStephen Boyd EXPORT_SYMBOL_GPL(clk_byte_ops);
69999cbd064SStephen Boyd 
clk_byte2_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)7008ee9c7deSStephen Boyd static int clk_byte2_determine_rate(struct clk_hw *hw,
7018ee9c7deSStephen Boyd 				    struct clk_rate_request *req)
7028ee9c7deSStephen Boyd {
7038ee9c7deSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
7048ee9c7deSStephen Boyd 	unsigned long parent_rate, div;
7058ee9c7deSStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
7068ee9c7deSStephen Boyd 	struct clk_hw *p;
7078ee9c7deSStephen Boyd 	unsigned long rate = req->rate;
7088ee9c7deSStephen Boyd 
7098ee9c7deSStephen Boyd 	if (rate == 0)
7108ee9c7deSStephen Boyd 		return -EINVAL;
7118ee9c7deSStephen Boyd 
7128ee9c7deSStephen Boyd 	p = req->best_parent_hw;
7138ee9c7deSStephen Boyd 	req->best_parent_rate = parent_rate = clk_hw_round_rate(p, rate);
7148ee9c7deSStephen Boyd 
7158ee9c7deSStephen Boyd 	div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
7168ee9c7deSStephen Boyd 	div = min_t(u32, div, mask);
7178ee9c7deSStephen Boyd 
7188ee9c7deSStephen Boyd 	req->rate = calc_rate(parent_rate, 0, 0, 0, div);
7198ee9c7deSStephen Boyd 
7208ee9c7deSStephen Boyd 	return 0;
7218ee9c7deSStephen Boyd }
7228ee9c7deSStephen Boyd 
clk_byte2_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)7238ee9c7deSStephen Boyd static int clk_byte2_set_rate(struct clk_hw *hw, unsigned long rate,
7248ee9c7deSStephen Boyd 			 unsigned long parent_rate)
7258ee9c7deSStephen Boyd {
7268ee9c7deSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
7278ee9c7deSStephen Boyd 	struct freq_tbl f = { 0 };
7288ee9c7deSStephen Boyd 	unsigned long div;
7298ee9c7deSStephen Boyd 	int i, num_parents = clk_hw_get_num_parents(hw);
7308ee9c7deSStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
7318ee9c7deSStephen Boyd 	u32 cfg;
7328ee9c7deSStephen Boyd 
7338ee9c7deSStephen Boyd 	div = DIV_ROUND_UP((2 * parent_rate), rate) - 1;
7348ee9c7deSStephen Boyd 	div = min_t(u32, div, mask);
7358ee9c7deSStephen Boyd 
7368ee9c7deSStephen Boyd 	f.pre_div = div;
7378ee9c7deSStephen Boyd 
7388ee9c7deSStephen Boyd 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
7398ee9c7deSStephen Boyd 	cfg &= CFG_SRC_SEL_MASK;
7408ee9c7deSStephen Boyd 	cfg >>= CFG_SRC_SEL_SHIFT;
7418ee9c7deSStephen Boyd 
7428ee9c7deSStephen Boyd 	for (i = 0; i < num_parents; i++) {
7438ee9c7deSStephen Boyd 		if (cfg == rcg->parent_map[i].cfg) {
7448ee9c7deSStephen Boyd 			f.src = rcg->parent_map[i].src;
7458ee9c7deSStephen Boyd 			return clk_rcg2_configure(rcg, &f);
7468ee9c7deSStephen Boyd 		}
7478ee9c7deSStephen Boyd 	}
7488ee9c7deSStephen Boyd 
7498ee9c7deSStephen Boyd 	return -EINVAL;
7508ee9c7deSStephen Boyd }
7518ee9c7deSStephen Boyd 
clk_byte2_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)7528ee9c7deSStephen Boyd static int clk_byte2_set_rate_and_parent(struct clk_hw *hw,
7538ee9c7deSStephen Boyd 		unsigned long rate, unsigned long parent_rate, u8 index)
7548ee9c7deSStephen Boyd {
7558ee9c7deSStephen Boyd 	/* Read the hardware to determine parent during set_rate */
7568ee9c7deSStephen Boyd 	return clk_byte2_set_rate(hw, rate, parent_rate);
7578ee9c7deSStephen Boyd }
7588ee9c7deSStephen Boyd 
7598ee9c7deSStephen Boyd const struct clk_ops clk_byte2_ops = {
7608ee9c7deSStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
7618ee9c7deSStephen Boyd 	.get_parent = clk_rcg2_get_parent,
7628ee9c7deSStephen Boyd 	.set_parent = clk_rcg2_set_parent,
7638ee9c7deSStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
7648ee9c7deSStephen Boyd 	.set_rate = clk_byte2_set_rate,
7658ee9c7deSStephen Boyd 	.set_rate_and_parent = clk_byte2_set_rate_and_parent,
7668ee9c7deSStephen Boyd 	.determine_rate = clk_byte2_determine_rate,
7678ee9c7deSStephen Boyd };
7688ee9c7deSStephen Boyd EXPORT_SYMBOL_GPL(clk_byte2_ops);
7698ee9c7deSStephen Boyd 
77099cbd064SStephen Boyd static const struct frac_entry frac_table_pixel[] = {
77199cbd064SStephen Boyd 	{ 3, 8 },
77299cbd064SStephen Boyd 	{ 2, 9 },
77399cbd064SStephen Boyd 	{ 4, 9 },
77499cbd064SStephen Boyd 	{ 1, 1 },
775b527358cSTaniya Das 	{ 2, 3 },
77699cbd064SStephen Boyd 	{ }
77799cbd064SStephen Boyd };
77899cbd064SStephen Boyd 
clk_pixel_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)7790817b62cSBoris Brezillon static int clk_pixel_determine_rate(struct clk_hw *hw,
7800817b62cSBoris Brezillon 				    struct clk_rate_request *req)
78199cbd064SStephen Boyd {
78299cbd064SStephen Boyd 	unsigned long request, src_rate;
78399cbd064SStephen Boyd 	int delta = 100000;
78499cbd064SStephen Boyd 	const struct frac_entry *frac = frac_table_pixel;
78599cbd064SStephen Boyd 
78699cbd064SStephen Boyd 	for (; frac->num; frac++) {
7870817b62cSBoris Brezillon 		request = (req->rate * frac->den) / frac->num;
78899cbd064SStephen Boyd 
789ac269395SStephen Boyd 		src_rate = clk_hw_round_rate(req->best_parent_hw, request);
79099cbd064SStephen Boyd 		if ((src_rate < (request - delta)) ||
79199cbd064SStephen Boyd 			(src_rate > (request + delta)))
79299cbd064SStephen Boyd 			continue;
79399cbd064SStephen Boyd 
7940817b62cSBoris Brezillon 		req->best_parent_rate = src_rate;
7950817b62cSBoris Brezillon 		req->rate = (src_rate * frac->num) / frac->den;
7960817b62cSBoris Brezillon 		return 0;
79799cbd064SStephen Boyd 	}
79899cbd064SStephen Boyd 
79999cbd064SStephen Boyd 	return -EINVAL;
80099cbd064SStephen Boyd }
80199cbd064SStephen Boyd 
clk_pixel_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)80299cbd064SStephen Boyd static int clk_pixel_set_rate(struct clk_hw *hw, unsigned long rate,
80399cbd064SStephen Boyd 		unsigned long parent_rate)
80499cbd064SStephen Boyd {
80599cbd064SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
8068ee9c7deSStephen Boyd 	struct freq_tbl f = { 0 };
80799cbd064SStephen Boyd 	const struct frac_entry *frac = frac_table_pixel;
8086d451367SHai Li 	unsigned long request;
80999cbd064SStephen Boyd 	int delta = 100000;
81099cbd064SStephen Boyd 	u32 mask = BIT(rcg->hid_width) - 1;
8118ee9c7deSStephen Boyd 	u32 hid_div, cfg;
8128ee9c7deSStephen Boyd 	int i, num_parents = clk_hw_get_num_parents(hw);
8138ee9c7deSStephen Boyd 
8148ee9c7deSStephen Boyd 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
8158ee9c7deSStephen Boyd 	cfg &= CFG_SRC_SEL_MASK;
8168ee9c7deSStephen Boyd 	cfg >>= CFG_SRC_SEL_SHIFT;
8178ee9c7deSStephen Boyd 
8188ee9c7deSStephen Boyd 	for (i = 0; i < num_parents; i++)
8198ee9c7deSStephen Boyd 		if (cfg == rcg->parent_map[i].cfg) {
8208ee9c7deSStephen Boyd 			f.src = rcg->parent_map[i].src;
8218ee9c7deSStephen Boyd 			break;
8228ee9c7deSStephen Boyd 		}
82399cbd064SStephen Boyd 
82499cbd064SStephen Boyd 	for (; frac->num; frac++) {
82599cbd064SStephen Boyd 		request = (rate * frac->den) / frac->num;
82699cbd064SStephen Boyd 
8276d451367SHai Li 		if ((parent_rate < (request - delta)) ||
8286d451367SHai Li 			(parent_rate > (request + delta)))
82999cbd064SStephen Boyd 			continue;
83099cbd064SStephen Boyd 
83199cbd064SStephen Boyd 		regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
83299cbd064SStephen Boyd 				&hid_div);
83399cbd064SStephen Boyd 		f.pre_div = hid_div;
83499cbd064SStephen Boyd 		f.pre_div >>= CFG_SRC_DIV_SHIFT;
83599cbd064SStephen Boyd 		f.pre_div &= mask;
83699cbd064SStephen Boyd 		f.m = frac->num;
83799cbd064SStephen Boyd 		f.n = frac->den;
83899cbd064SStephen Boyd 
83999cbd064SStephen Boyd 		return clk_rcg2_configure(rcg, &f);
84099cbd064SStephen Boyd 	}
84199cbd064SStephen Boyd 	return -EINVAL;
84299cbd064SStephen Boyd }
84399cbd064SStephen Boyd 
clk_pixel_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)84499cbd064SStephen Boyd static int clk_pixel_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
84599cbd064SStephen Boyd 		unsigned long parent_rate, u8 index)
84699cbd064SStephen Boyd {
84799cbd064SStephen Boyd 	return clk_pixel_set_rate(hw, rate, parent_rate);
84899cbd064SStephen Boyd }
84999cbd064SStephen Boyd 
85099cbd064SStephen Boyd const struct clk_ops clk_pixel_ops = {
85199cbd064SStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
85299cbd064SStephen Boyd 	.get_parent = clk_rcg2_get_parent,
85399cbd064SStephen Boyd 	.set_parent = clk_rcg2_set_parent,
85499cbd064SStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
85599cbd064SStephen Boyd 	.set_rate = clk_pixel_set_rate,
85699cbd064SStephen Boyd 	.set_rate_and_parent = clk_pixel_set_rate_and_parent,
85799cbd064SStephen Boyd 	.determine_rate = clk_pixel_determine_rate,
85899cbd064SStephen Boyd };
85999cbd064SStephen Boyd EXPORT_SYMBOL_GPL(clk_pixel_ops);
86055213e1aSStephen Boyd 
clk_gfx3d_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)86155213e1aSStephen Boyd static int clk_gfx3d_determine_rate(struct clk_hw *hw,
86255213e1aSStephen Boyd 				    struct clk_rate_request *req)
86355213e1aSStephen Boyd {
864d968fda3SDmitry Baryshkov 	struct clk_rate_request parent_req = { .min_rate = 0, .max_rate = ULONG_MAX };
8657cbb78a9SAngeloGioacchino Del Regno 	struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw);
8667cbb78a9SAngeloGioacchino Del Regno 	struct clk_hw *xo, *p0, *p1, *p2;
8670ae67123SMarijn Suijten 	unsigned long p0_rate;
8680ae67123SMarijn Suijten 	u8 mux_div = cgfx->div;
86955213e1aSStephen Boyd 	int ret;
87055213e1aSStephen Boyd 
8717cbb78a9SAngeloGioacchino Del Regno 	p0 = cgfx->hws[0];
8727cbb78a9SAngeloGioacchino Del Regno 	p1 = cgfx->hws[1];
8737cbb78a9SAngeloGioacchino Del Regno 	p2 = cgfx->hws[2];
8747cbb78a9SAngeloGioacchino Del Regno 	/*
8757cbb78a9SAngeloGioacchino Del Regno 	 * This function does ping-pong the RCG between PLLs: if we don't
8767cbb78a9SAngeloGioacchino Del Regno 	 * have at least one fixed PLL and two variable ones,
8777cbb78a9SAngeloGioacchino Del Regno 	 * then it's not going to work correctly.
8787cbb78a9SAngeloGioacchino Del Regno 	 */
8797cbb78a9SAngeloGioacchino Del Regno 	if (WARN_ON(!p0 || !p1 || !p2))
8807cbb78a9SAngeloGioacchino Del Regno 		return -EINVAL;
8817cbb78a9SAngeloGioacchino Del Regno 
88255213e1aSStephen Boyd 	xo = clk_hw_get_parent_by_index(hw, 0);
88355213e1aSStephen Boyd 	if (req->rate == clk_hw_get_rate(xo)) {
88455213e1aSStephen Boyd 		req->best_parent_hw = xo;
88555213e1aSStephen Boyd 		return 0;
88655213e1aSStephen Boyd 	}
88755213e1aSStephen Boyd 
8880ae67123SMarijn Suijten 	if (mux_div == 0)
8890ae67123SMarijn Suijten 		mux_div = 1;
8900ae67123SMarijn Suijten 
8910ae67123SMarijn Suijten 	parent_req.rate = req->rate * mux_div;
89255213e1aSStephen Boyd 
8937cbb78a9SAngeloGioacchino Del Regno 	/* This has to be a fixed rate PLL */
8947cbb78a9SAngeloGioacchino Del Regno 	p0_rate = clk_hw_get_rate(p0);
89555213e1aSStephen Boyd 
8960ae67123SMarijn Suijten 	if (parent_req.rate == p0_rate) {
8977cbb78a9SAngeloGioacchino Del Regno 		req->rate = req->best_parent_rate = p0_rate;
8987cbb78a9SAngeloGioacchino Del Regno 		req->best_parent_hw = p0;
89955213e1aSStephen Boyd 		return 0;
90055213e1aSStephen Boyd 	}
90155213e1aSStephen Boyd 
9027cbb78a9SAngeloGioacchino Del Regno 	if (req->best_parent_hw == p0) {
90355213e1aSStephen Boyd 		/* Are we going back to a previously used rate? */
9040ae67123SMarijn Suijten 		if (clk_hw_get_rate(p2) == parent_req.rate)
9057cbb78a9SAngeloGioacchino Del Regno 			req->best_parent_hw = p2;
90655213e1aSStephen Boyd 		else
9077cbb78a9SAngeloGioacchino Del Regno 			req->best_parent_hw = p1;
9087cbb78a9SAngeloGioacchino Del Regno 	} else if (req->best_parent_hw == p2) {
9097cbb78a9SAngeloGioacchino Del Regno 		req->best_parent_hw = p1;
91055213e1aSStephen Boyd 	} else {
9117cbb78a9SAngeloGioacchino Del Regno 		req->best_parent_hw = p2;
91255213e1aSStephen Boyd 	}
91355213e1aSStephen Boyd 
914af1e62f2SMaxime Ripard 	clk_hw_get_rate_range(req->best_parent_hw,
915af1e62f2SMaxime Ripard 			      &parent_req.min_rate, &parent_req.max_rate);
916af1e62f2SMaxime Ripard 
917af1e62f2SMaxime Ripard 	if (req->min_rate > parent_req.min_rate)
918af1e62f2SMaxime Ripard 		parent_req.min_rate = req->min_rate;
919af1e62f2SMaxime Ripard 
920af1e62f2SMaxime Ripard 	if (req->max_rate < parent_req.max_rate)
921af1e62f2SMaxime Ripard 		parent_req.max_rate = req->max_rate;
922af1e62f2SMaxime Ripard 
92355213e1aSStephen Boyd 	ret = __clk_determine_rate(req->best_parent_hw, &parent_req);
92455213e1aSStephen Boyd 	if (ret)
92555213e1aSStephen Boyd 		return ret;
92655213e1aSStephen Boyd 
92755213e1aSStephen Boyd 	req->rate = req->best_parent_rate = parent_req.rate;
9280ae67123SMarijn Suijten 	req->rate /= mux_div;
92955213e1aSStephen Boyd 
93055213e1aSStephen Boyd 	return 0;
93155213e1aSStephen Boyd }
93255213e1aSStephen Boyd 
clk_gfx3d_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)93355213e1aSStephen Boyd static int clk_gfx3d_set_rate_and_parent(struct clk_hw *hw, unsigned long rate,
93455213e1aSStephen Boyd 		unsigned long parent_rate, u8 index)
93555213e1aSStephen Boyd {
9367cbb78a9SAngeloGioacchino Del Regno 	struct clk_rcg2_gfx3d *cgfx = to_clk_rcg2_gfx3d(hw);
9377cbb78a9SAngeloGioacchino Del Regno 	struct clk_rcg2 *rcg = &cgfx->rcg;
93855213e1aSStephen Boyd 	u32 cfg;
93955213e1aSStephen Boyd 	int ret;
94055213e1aSStephen Boyd 
94155213e1aSStephen Boyd 	cfg = rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
9427cbb78a9SAngeloGioacchino Del Regno 	/* On some targets, the GFX3D RCG may need to divide PLL frequency */
9437cbb78a9SAngeloGioacchino Del Regno 	if (cgfx->div > 1)
9447cbb78a9SAngeloGioacchino Del Regno 		cfg |= ((2 * cgfx->div) - 1) << CFG_SRC_DIV_SHIFT;
9457cbb78a9SAngeloGioacchino Del Regno 
94655213e1aSStephen Boyd 	ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, cfg);
94755213e1aSStephen Boyd 	if (ret)
94855213e1aSStephen Boyd 		return ret;
94955213e1aSStephen Boyd 
95055213e1aSStephen Boyd 	return update_config(rcg);
95155213e1aSStephen Boyd }
95255213e1aSStephen Boyd 
clk_gfx3d_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)95355213e1aSStephen Boyd static int clk_gfx3d_set_rate(struct clk_hw *hw, unsigned long rate,
95455213e1aSStephen Boyd 			      unsigned long parent_rate)
95555213e1aSStephen Boyd {
95655213e1aSStephen Boyd 	/*
95755213e1aSStephen Boyd 	 * We should never get here; clk_gfx3d_determine_rate() should always
95855213e1aSStephen Boyd 	 * make us use a different parent than what we're currently using, so
95955213e1aSStephen Boyd 	 * clk_gfx3d_set_rate_and_parent() should always be called.
96055213e1aSStephen Boyd 	 */
96155213e1aSStephen Boyd 	return 0;
96255213e1aSStephen Boyd }
96355213e1aSStephen Boyd 
96455213e1aSStephen Boyd const struct clk_ops clk_gfx3d_ops = {
96555213e1aSStephen Boyd 	.is_enabled = clk_rcg2_is_enabled,
96655213e1aSStephen Boyd 	.get_parent = clk_rcg2_get_parent,
96755213e1aSStephen Boyd 	.set_parent = clk_rcg2_set_parent,
96855213e1aSStephen Boyd 	.recalc_rate = clk_rcg2_recalc_rate,
96955213e1aSStephen Boyd 	.set_rate = clk_gfx3d_set_rate,
97055213e1aSStephen Boyd 	.set_rate_and_parent = clk_gfx3d_set_rate_and_parent,
97155213e1aSStephen Boyd 	.determine_rate = clk_gfx3d_determine_rate,
97255213e1aSStephen Boyd };
97355213e1aSStephen Boyd EXPORT_SYMBOL_GPL(clk_gfx3d_ops);
9747ef6f118SAmit Nischal 
clk_rcg2_set_force_enable(struct clk_hw * hw)9757ef6f118SAmit Nischal static int clk_rcg2_set_force_enable(struct clk_hw *hw)
9767ef6f118SAmit Nischal {
9777ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
9787ef6f118SAmit Nischal 	const char *name = clk_hw_get_name(hw);
9797ef6f118SAmit Nischal 	int ret, count;
9807ef6f118SAmit Nischal 
9817ef6f118SAmit Nischal 	ret = regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
9827ef6f118SAmit Nischal 				 CMD_ROOT_EN, CMD_ROOT_EN);
9837ef6f118SAmit Nischal 	if (ret)
9847ef6f118SAmit Nischal 		return ret;
9857ef6f118SAmit Nischal 
9867ef6f118SAmit Nischal 	/* wait for RCG to turn ON */
9877ef6f118SAmit Nischal 	for (count = 500; count > 0; count--) {
9887ef6f118SAmit Nischal 		if (clk_rcg2_is_enabled(hw))
9897ef6f118SAmit Nischal 			return 0;
9907ef6f118SAmit Nischal 
9917ef6f118SAmit Nischal 		udelay(1);
9927ef6f118SAmit Nischal 	}
9937ef6f118SAmit Nischal 
9947ef6f118SAmit Nischal 	pr_err("%s: RCG did not turn on\n", name);
9957ef6f118SAmit Nischal 	return -ETIMEDOUT;
9967ef6f118SAmit Nischal }
9977ef6f118SAmit Nischal 
clk_rcg2_clear_force_enable(struct clk_hw * hw)9987ef6f118SAmit Nischal static int clk_rcg2_clear_force_enable(struct clk_hw *hw)
9997ef6f118SAmit Nischal {
10007ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
10017ef6f118SAmit Nischal 
10027ef6f118SAmit Nischal 	return regmap_update_bits(rcg->clkr.regmap, rcg->cmd_rcgr + CMD_REG,
10037ef6f118SAmit Nischal 					CMD_ROOT_EN, 0);
10047ef6f118SAmit Nischal }
10057ef6f118SAmit Nischal 
10067ef6f118SAmit Nischal static int
clk_rcg2_shared_force_enable_clear(struct clk_hw * hw,const struct freq_tbl * f)10077ef6f118SAmit Nischal clk_rcg2_shared_force_enable_clear(struct clk_hw *hw, const struct freq_tbl *f)
10087ef6f118SAmit Nischal {
10097ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
10107ef6f118SAmit Nischal 	int ret;
10117ef6f118SAmit Nischal 
10127ef6f118SAmit Nischal 	ret = clk_rcg2_set_force_enable(hw);
10137ef6f118SAmit Nischal 	if (ret)
10147ef6f118SAmit Nischal 		return ret;
10157ef6f118SAmit Nischal 
10167ef6f118SAmit Nischal 	ret = clk_rcg2_configure(rcg, f);
10177ef6f118SAmit Nischal 	if (ret)
10187ef6f118SAmit Nischal 		return ret;
10197ef6f118SAmit Nischal 
10207ef6f118SAmit Nischal 	return clk_rcg2_clear_force_enable(hw);
10217ef6f118SAmit Nischal }
10227ef6f118SAmit Nischal 
__clk_rcg2_shared_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,enum freq_policy policy)1023*80864fe5SDmitry Baryshkov static int __clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
1024*80864fe5SDmitry Baryshkov 				      unsigned long parent_rate,
1025*80864fe5SDmitry Baryshkov 				      enum freq_policy policy)
10267ef6f118SAmit Nischal {
10277ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
10287ef6f118SAmit Nischal 	const struct freq_tbl *f;
10297ef6f118SAmit Nischal 
1030*80864fe5SDmitry Baryshkov 	switch (policy) {
1031*80864fe5SDmitry Baryshkov 	case FLOOR:
1032*80864fe5SDmitry Baryshkov 		f = qcom_find_freq_floor(rcg->freq_tbl, rate);
1033*80864fe5SDmitry Baryshkov 		break;
1034*80864fe5SDmitry Baryshkov 	case CEIL:
10357ef6f118SAmit Nischal 		f = qcom_find_freq(rcg->freq_tbl, rate);
1036*80864fe5SDmitry Baryshkov 		break;
1037*80864fe5SDmitry Baryshkov 	default:
10387ef6f118SAmit Nischal 		return -EINVAL;
1039*80864fe5SDmitry Baryshkov 	}
10407ef6f118SAmit Nischal 
10417ef6f118SAmit Nischal 	/*
1042703db1f5SBjorn Andersson 	 * In case clock is disabled, update the M, N and D registers, cache
1043703db1f5SBjorn Andersson 	 * the CFG value in parked_cfg and don't hit the update bit of CMD
1044703db1f5SBjorn Andersson 	 * register.
10457ef6f118SAmit Nischal 	 */
1046703db1f5SBjorn Andersson 	if (!clk_hw_is_enabled(hw))
1047703db1f5SBjorn Andersson 		return __clk_rcg2_configure(rcg, f, &rcg->parked_cfg);
10487ef6f118SAmit Nischal 
10497ef6f118SAmit Nischal 	return clk_rcg2_shared_force_enable_clear(hw, f);
10507ef6f118SAmit Nischal }
10517ef6f118SAmit Nischal 
clk_rcg2_shared_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)1052*80864fe5SDmitry Baryshkov static int clk_rcg2_shared_set_rate(struct clk_hw *hw, unsigned long rate,
1053*80864fe5SDmitry Baryshkov 				    unsigned long parent_rate)
1054*80864fe5SDmitry Baryshkov {
1055*80864fe5SDmitry Baryshkov 	return __clk_rcg2_shared_set_rate(hw, rate, parent_rate, CEIL);
1056*80864fe5SDmitry Baryshkov }
1057*80864fe5SDmitry Baryshkov 
clk_rcg2_shared_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)10587ef6f118SAmit Nischal static int clk_rcg2_shared_set_rate_and_parent(struct clk_hw *hw,
10597ef6f118SAmit Nischal 		unsigned long rate, unsigned long parent_rate, u8 index)
10607ef6f118SAmit Nischal {
1061*80864fe5SDmitry Baryshkov 	return __clk_rcg2_shared_set_rate(hw, rate, parent_rate, CEIL);
1062*80864fe5SDmitry Baryshkov }
1063*80864fe5SDmitry Baryshkov 
clk_rcg2_shared_set_floor_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)1064*80864fe5SDmitry Baryshkov static int clk_rcg2_shared_set_floor_rate(struct clk_hw *hw, unsigned long rate,
1065*80864fe5SDmitry Baryshkov 					  unsigned long parent_rate)
1066*80864fe5SDmitry Baryshkov {
1067*80864fe5SDmitry Baryshkov 	return __clk_rcg2_shared_set_rate(hw, rate, parent_rate, FLOOR);
1068*80864fe5SDmitry Baryshkov }
1069*80864fe5SDmitry Baryshkov 
clk_rcg2_shared_set_floor_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)1070*80864fe5SDmitry Baryshkov static int clk_rcg2_shared_set_floor_rate_and_parent(struct clk_hw *hw,
1071*80864fe5SDmitry Baryshkov 		unsigned long rate, unsigned long parent_rate, u8 index)
1072*80864fe5SDmitry Baryshkov {
1073*80864fe5SDmitry Baryshkov 	return __clk_rcg2_shared_set_rate(hw, rate, parent_rate, FLOOR);
10747ef6f118SAmit Nischal }
10757ef6f118SAmit Nischal 
clk_rcg2_shared_enable(struct clk_hw * hw)10767ef6f118SAmit Nischal static int clk_rcg2_shared_enable(struct clk_hw *hw)
10777ef6f118SAmit Nischal {
10787ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
10797ef6f118SAmit Nischal 	int ret;
10807ef6f118SAmit Nischal 
10817ef6f118SAmit Nischal 	/*
10827ef6f118SAmit Nischal 	 * Set the update bit because required configuration has already
10837ef6f118SAmit Nischal 	 * been written in clk_rcg2_shared_set_rate()
10847ef6f118SAmit Nischal 	 */
10857ef6f118SAmit Nischal 	ret = clk_rcg2_set_force_enable(hw);
10867ef6f118SAmit Nischal 	if (ret)
10877ef6f118SAmit Nischal 		return ret;
10887ef6f118SAmit Nischal 
1089703db1f5SBjorn Andersson 	/* Write back the stored configuration corresponding to current rate */
1090703db1f5SBjorn Andersson 	ret = regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, rcg->parked_cfg);
1091703db1f5SBjorn Andersson 	if (ret)
1092703db1f5SBjorn Andersson 		return ret;
1093703db1f5SBjorn Andersson 
10947ef6f118SAmit Nischal 	ret = update_config(rcg);
10957ef6f118SAmit Nischal 	if (ret)
10967ef6f118SAmit Nischal 		return ret;
10977ef6f118SAmit Nischal 
10987ef6f118SAmit Nischal 	return clk_rcg2_clear_force_enable(hw);
10997ef6f118SAmit Nischal }
11007ef6f118SAmit Nischal 
clk_rcg2_shared_disable(struct clk_hw * hw)11017ef6f118SAmit Nischal static void clk_rcg2_shared_disable(struct clk_hw *hw)
11027ef6f118SAmit Nischal {
11037ef6f118SAmit Nischal 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
11047ef6f118SAmit Nischal 
11057ef6f118SAmit Nischal 	/*
11067ef6f118SAmit Nischal 	 * Store current configuration as switching to safe source would clear
11077ef6f118SAmit Nischal 	 * the SRC and DIV of CFG register
11087ef6f118SAmit Nischal 	 */
1109703db1f5SBjorn Andersson 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &rcg->parked_cfg);
11107ef6f118SAmit Nischal 
11117ef6f118SAmit Nischal 	/*
11127ef6f118SAmit Nischal 	 * Park the RCG at a safe configuration - sourced off of safe source.
11137ef6f118SAmit Nischal 	 * Force enable and disable the RCG while configuring it to safeguard
11147ef6f118SAmit Nischal 	 * against any update signal coming from the downstream clock.
11157ef6f118SAmit Nischal 	 * The current parent is still prepared and enabled at this point, and
11167ef6f118SAmit Nischal 	 * the safe source is always on while application processor subsystem
11177ef6f118SAmit Nischal 	 * is online. Therefore, the RCG can safely switch its parent.
11187ef6f118SAmit Nischal 	 */
11197ef6f118SAmit Nischal 	clk_rcg2_set_force_enable(hw);
11207ef6f118SAmit Nischal 
11217ef6f118SAmit Nischal 	regmap_write(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG,
11227ef6f118SAmit Nischal 		     rcg->safe_src_index << CFG_SRC_SEL_SHIFT);
11237ef6f118SAmit Nischal 
11247ef6f118SAmit Nischal 	update_config(rcg);
11257ef6f118SAmit Nischal 
11267ef6f118SAmit Nischal 	clk_rcg2_clear_force_enable(hw);
1127703db1f5SBjorn Andersson }
11287ef6f118SAmit Nischal 
clk_rcg2_shared_get_parent(struct clk_hw * hw)1129703db1f5SBjorn Andersson static u8 clk_rcg2_shared_get_parent(struct clk_hw *hw)
1130703db1f5SBjorn Andersson {
1131703db1f5SBjorn Andersson 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1132703db1f5SBjorn Andersson 
1133703db1f5SBjorn Andersson 	/* If the shared rcg is parked use the cached cfg instead */
1134703db1f5SBjorn Andersson 	if (!clk_hw_is_enabled(hw))
1135703db1f5SBjorn Andersson 		return __clk_rcg2_get_parent(hw, rcg->parked_cfg);
1136703db1f5SBjorn Andersson 
1137703db1f5SBjorn Andersson 	return clk_rcg2_get_parent(hw);
1138703db1f5SBjorn Andersson }
1139703db1f5SBjorn Andersson 
clk_rcg2_shared_set_parent(struct clk_hw * hw,u8 index)1140703db1f5SBjorn Andersson static int clk_rcg2_shared_set_parent(struct clk_hw *hw, u8 index)
1141703db1f5SBjorn Andersson {
1142703db1f5SBjorn Andersson 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1143703db1f5SBjorn Andersson 
1144703db1f5SBjorn Andersson 	/* If the shared rcg is parked only update the cached cfg */
1145703db1f5SBjorn Andersson 	if (!clk_hw_is_enabled(hw)) {
1146703db1f5SBjorn Andersson 		rcg->parked_cfg &= ~CFG_SRC_SEL_MASK;
1147703db1f5SBjorn Andersson 		rcg->parked_cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT;
1148703db1f5SBjorn Andersson 
1149703db1f5SBjorn Andersson 		return 0;
1150703db1f5SBjorn Andersson 	}
1151703db1f5SBjorn Andersson 
1152703db1f5SBjorn Andersson 	return clk_rcg2_set_parent(hw, index);
1153703db1f5SBjorn Andersson }
1154703db1f5SBjorn Andersson 
1155703db1f5SBjorn Andersson static unsigned long
clk_rcg2_shared_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)1156703db1f5SBjorn Andersson clk_rcg2_shared_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1157703db1f5SBjorn Andersson {
1158703db1f5SBjorn Andersson 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1159703db1f5SBjorn Andersson 
1160703db1f5SBjorn Andersson 	/* If the shared rcg is parked use the cached cfg instead */
1161703db1f5SBjorn Andersson 	if (!clk_hw_is_enabled(hw))
1162703db1f5SBjorn Andersson 		return __clk_rcg2_recalc_rate(hw, parent_rate, rcg->parked_cfg);
1163703db1f5SBjorn Andersson 
1164703db1f5SBjorn Andersson 	return clk_rcg2_recalc_rate(hw, parent_rate);
11657ef6f118SAmit Nischal }
11667ef6f118SAmit Nischal 
clk_rcg2_shared_init(struct clk_hw * hw)116756a118f5SStephen Boyd static int clk_rcg2_shared_init(struct clk_hw *hw)
116856a118f5SStephen Boyd {
116956a118f5SStephen Boyd 	/*
117056a118f5SStephen Boyd 	 * This does a few things:
117156a118f5SStephen Boyd 	 *
117256a118f5SStephen Boyd 	 *  1. Sets rcg->parked_cfg to reflect the value at probe so that the
117356a118f5SStephen Boyd 	 *     proper parent is reported from clk_rcg2_shared_get_parent().
117456a118f5SStephen Boyd 	 *
117556a118f5SStephen Boyd 	 *  2. Clears the force enable bit of the RCG because we rely on child
117656a118f5SStephen Boyd 	 *     clks (branches) to turn the RCG on/off with a hardware feedback
117756a118f5SStephen Boyd 	 *     mechanism and only set the force enable bit in the RCG when we
117856a118f5SStephen Boyd 	 *     want to make sure the clk stays on for parent switches or
117956a118f5SStephen Boyd 	 *     parking.
118056a118f5SStephen Boyd 	 *
118156a118f5SStephen Boyd 	 *  3. Parks shared RCGs on the safe source at registration because we
118256a118f5SStephen Boyd 	 *     can't be certain that the parent clk will stay on during boot,
118356a118f5SStephen Boyd 	 *     especially if the parent is shared. If this RCG is enabled at
118456a118f5SStephen Boyd 	 *     boot, and the parent is turned off, the RCG will get stuck on. A
118556a118f5SStephen Boyd 	 *     GDSC can wedge if is turned on and the RCG is stuck on because
118656a118f5SStephen Boyd 	 *     the GDSC's controller will hang waiting for the clk status to
118756a118f5SStephen Boyd 	 *     toggle on when it never does.
118856a118f5SStephen Boyd 	 *
118956a118f5SStephen Boyd 	 * The safest option here is to "park" the RCG at init so that the clk
119056a118f5SStephen Boyd 	 * can never get stuck on or off. This ensures the GDSC can't get
119156a118f5SStephen Boyd 	 * wedged.
119256a118f5SStephen Boyd 	 */
119356a118f5SStephen Boyd 	clk_rcg2_shared_disable(hw);
119456a118f5SStephen Boyd 
119556a118f5SStephen Boyd 	return 0;
119656a118f5SStephen Boyd }
119756a118f5SStephen Boyd 
11987ef6f118SAmit Nischal const struct clk_ops clk_rcg2_shared_ops = {
119956a118f5SStephen Boyd 	.init = clk_rcg2_shared_init,
12007ef6f118SAmit Nischal 	.enable = clk_rcg2_shared_enable,
12017ef6f118SAmit Nischal 	.disable = clk_rcg2_shared_disable,
1202703db1f5SBjorn Andersson 	.get_parent = clk_rcg2_shared_get_parent,
1203703db1f5SBjorn Andersson 	.set_parent = clk_rcg2_shared_set_parent,
1204703db1f5SBjorn Andersson 	.recalc_rate = clk_rcg2_shared_recalc_rate,
12057ef6f118SAmit Nischal 	.determine_rate = clk_rcg2_determine_rate,
12067ef6f118SAmit Nischal 	.set_rate = clk_rcg2_shared_set_rate,
12077ef6f118SAmit Nischal 	.set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent,
12087ef6f118SAmit Nischal };
12097ef6f118SAmit Nischal EXPORT_SYMBOL_GPL(clk_rcg2_shared_ops);
1210cc4f6944STaniya Das 
1211*80864fe5SDmitry Baryshkov const struct clk_ops clk_rcg2_shared_floor_ops = {
1212*80864fe5SDmitry Baryshkov 	.enable = clk_rcg2_shared_enable,
1213*80864fe5SDmitry Baryshkov 	.disable = clk_rcg2_shared_disable,
1214*80864fe5SDmitry Baryshkov 	.get_parent = clk_rcg2_shared_get_parent,
1215*80864fe5SDmitry Baryshkov 	.set_parent = clk_rcg2_shared_set_parent,
1216*80864fe5SDmitry Baryshkov 	.recalc_rate = clk_rcg2_shared_recalc_rate,
1217*80864fe5SDmitry Baryshkov 	.determine_rate = clk_rcg2_determine_floor_rate,
1218*80864fe5SDmitry Baryshkov 	.set_rate = clk_rcg2_shared_set_floor_rate,
1219*80864fe5SDmitry Baryshkov 	.set_rate_and_parent = clk_rcg2_shared_set_floor_rate_and_parent,
1220*80864fe5SDmitry Baryshkov };
1221*80864fe5SDmitry Baryshkov EXPORT_SYMBOL_GPL(clk_rcg2_shared_floor_ops);
1222*80864fe5SDmitry Baryshkov 
clk_rcg2_shared_no_init_park(struct clk_hw * hw)12237c391eafSStephen Boyd static int clk_rcg2_shared_no_init_park(struct clk_hw *hw)
12247c391eafSStephen Boyd {
12257c391eafSStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
12267c391eafSStephen Boyd 
12277c391eafSStephen Boyd 	/*
12287c391eafSStephen Boyd 	 * Read the config register so that the parent is properly mapped at
12297c391eafSStephen Boyd 	 * registration time.
12307c391eafSStephen Boyd 	 */
12317c391eafSStephen Boyd 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &rcg->parked_cfg);
12327c391eafSStephen Boyd 
12337c391eafSStephen Boyd 	return 0;
12347c391eafSStephen Boyd }
12357c391eafSStephen Boyd 
12367c391eafSStephen Boyd /*
12377c391eafSStephen Boyd  * Like clk_rcg2_shared_ops but skip the init so that the clk frequency is left
12387c391eafSStephen Boyd  * unchanged at registration time.
12397c391eafSStephen Boyd  */
12407c391eafSStephen Boyd const struct clk_ops clk_rcg2_shared_no_init_park_ops = {
12417c391eafSStephen Boyd 	.init = clk_rcg2_shared_no_init_park,
12427c391eafSStephen Boyd 	.enable = clk_rcg2_shared_enable,
12437c391eafSStephen Boyd 	.disable = clk_rcg2_shared_disable,
12447c391eafSStephen Boyd 	.get_parent = clk_rcg2_shared_get_parent,
12457c391eafSStephen Boyd 	.set_parent = clk_rcg2_shared_set_parent,
12467c391eafSStephen Boyd 	.recalc_rate = clk_rcg2_shared_recalc_rate,
12477c391eafSStephen Boyd 	.determine_rate = clk_rcg2_determine_rate,
12487c391eafSStephen Boyd 	.set_rate = clk_rcg2_shared_set_rate,
12497c391eafSStephen Boyd 	.set_rate_and_parent = clk_rcg2_shared_set_rate_and_parent,
12507c391eafSStephen Boyd };
12517c391eafSStephen Boyd EXPORT_SYMBOL_GPL(clk_rcg2_shared_no_init_park_ops);
12527c391eafSStephen Boyd 
1253cc4f6944STaniya Das /* Common APIs to be used for DFS based RCGR */
clk_rcg2_dfs_populate_freq(struct clk_hw * hw,unsigned int l,struct freq_tbl * f)1254cc4f6944STaniya Das static void clk_rcg2_dfs_populate_freq(struct clk_hw *hw, unsigned int l,
1255cc4f6944STaniya Das 				       struct freq_tbl *f)
1256cc4f6944STaniya Das {
1257cc4f6944STaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1258cc4f6944STaniya Das 	struct clk_hw *p;
1259cc4f6944STaniya Das 	unsigned long prate = 0;
126021e157c6SStephen Boyd 	u32 val, mask, cfg, mode, src;
1261cc4f6944STaniya Das 	int i, num_parents;
1262cc4f6944STaniya Das 
1263cc4f6944STaniya Das 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(l), &cfg);
1264cc4f6944STaniya Das 
1265cc4f6944STaniya Das 	mask = BIT(rcg->hid_width) - 1;
1266cc4f6944STaniya Das 	f->pre_div = 1;
1267cc4f6944STaniya Das 	if (cfg & mask)
1268cc4f6944STaniya Das 		f->pre_div = cfg & mask;
1269cc4f6944STaniya Das 
127021e157c6SStephen Boyd 	src = cfg & CFG_SRC_SEL_MASK;
127121e157c6SStephen Boyd 	src >>= CFG_SRC_SEL_SHIFT;
1272cc4f6944STaniya Das 
1273cc4f6944STaniya Das 	num_parents = clk_hw_get_num_parents(hw);
1274cc4f6944STaniya Das 	for (i = 0; i < num_parents; i++) {
127521e157c6SStephen Boyd 		if (src == rcg->parent_map[i].cfg) {
1276cc4f6944STaniya Das 			f->src = rcg->parent_map[i].src;
1277cc4f6944STaniya Das 			p = clk_hw_get_parent_by_index(&rcg->clkr.hw, i);
1278cc4f6944STaniya Das 			prate = clk_hw_get_rate(p);
1279cc4f6944STaniya Das 		}
1280cc4f6944STaniya Das 	}
1281cc4f6944STaniya Das 
1282cc4f6944STaniya Das 	mode = cfg & CFG_MODE_MASK;
1283cc4f6944STaniya Das 	mode >>= CFG_MODE_SHIFT;
1284cc4f6944STaniya Das 	if (mode) {
1285cc4f6944STaniya Das 		mask = BIT(rcg->mnd_width) - 1;
1286cc4f6944STaniya Das 		regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_M_DFSR(l),
1287cc4f6944STaniya Das 			    &val);
1288cc4f6944STaniya Das 		val &= mask;
1289cc4f6944STaniya Das 		f->m = val;
1290cc4f6944STaniya Das 
1291cc4f6944STaniya Das 		regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_N_DFSR(l),
1292cc4f6944STaniya Das 			    &val);
1293cc4f6944STaniya Das 		val = ~val;
1294cc4f6944STaniya Das 		val &= mask;
1295cc4f6944STaniya Das 		val += f->m;
1296cc4f6944STaniya Das 		f->n = val;
1297cc4f6944STaniya Das 	}
1298cc4f6944STaniya Das 
1299cc4f6944STaniya Das 	f->freq = calc_rate(prate, f->m, f->n, mode, f->pre_div);
1300cc4f6944STaniya Das }
1301cc4f6944STaniya Das 
clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 * rcg)1302cc4f6944STaniya Das static int clk_rcg2_dfs_populate_freq_table(struct clk_rcg2 *rcg)
1303cc4f6944STaniya Das {
1304cc4f6944STaniya Das 	struct freq_tbl *freq_tbl;
1305cc4f6944STaniya Das 	int i;
1306cc4f6944STaniya Das 
13072bdb2a1cSDouglas Anderson 	/* Allocate space for 1 extra since table is NULL terminated */
13082bdb2a1cSDouglas Anderson 	freq_tbl = kcalloc(MAX_PERF_LEVEL + 1, sizeof(*freq_tbl), GFP_KERNEL);
1309cc4f6944STaniya Das 	if (!freq_tbl)
1310cc4f6944STaniya Das 		return -ENOMEM;
1311cc4f6944STaniya Das 	rcg->freq_tbl = freq_tbl;
1312cc4f6944STaniya Das 
1313cc4f6944STaniya Das 	for (i = 0; i < MAX_PERF_LEVEL; i++)
1314cc4f6944STaniya Das 		clk_rcg2_dfs_populate_freq(&rcg->clkr.hw, i, freq_tbl + i);
1315cc4f6944STaniya Das 
1316cc4f6944STaniya Das 	return 0;
1317cc4f6944STaniya Das }
1318cc4f6944STaniya Das 
clk_rcg2_dfs_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)1319cc4f6944STaniya Das static int clk_rcg2_dfs_determine_rate(struct clk_hw *hw,
1320cc4f6944STaniya Das 				   struct clk_rate_request *req)
1321cc4f6944STaniya Das {
1322cc4f6944STaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1323cc4f6944STaniya Das 	int ret;
1324cc4f6944STaniya Das 
1325cc4f6944STaniya Das 	if (!rcg->freq_tbl) {
1326cc4f6944STaniya Das 		ret = clk_rcg2_dfs_populate_freq_table(rcg);
1327cc4f6944STaniya Das 		if (ret) {
1328cc4f6944STaniya Das 			pr_err("Failed to update DFS tables for %s\n",
1329cc4f6944STaniya Das 					clk_hw_get_name(hw));
1330cc4f6944STaniya Das 			return ret;
1331cc4f6944STaniya Das 		}
1332cc4f6944STaniya Das 	}
1333cc4f6944STaniya Das 
1334cc4f6944STaniya Das 	return clk_rcg2_determine_rate(hw, req);
1335cc4f6944STaniya Das }
1336cc4f6944STaniya Das 
1337cc4f6944STaniya Das static unsigned long
clk_rcg2_dfs_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)1338cc4f6944STaniya Das clk_rcg2_dfs_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
1339cc4f6944STaniya Das {
1340cc4f6944STaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1341cc4f6944STaniya Das 	u32 level, mask, cfg, m = 0, n = 0, mode, pre_div;
1342cc4f6944STaniya Das 
1343cc4f6944STaniya Das 	regmap_read(rcg->clkr.regmap,
1344cc4f6944STaniya Das 		    rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &level);
1345cc4f6944STaniya Das 	level &= GENMASK(4, 1);
1346cc4f6944STaniya Das 	level >>= 1;
1347cc4f6944STaniya Das 
1348cc4f6944STaniya Das 	if (rcg->freq_tbl)
1349cc4f6944STaniya Das 		return rcg->freq_tbl[level].freq;
1350cc4f6944STaniya Das 
1351cc4f6944STaniya Das 	/*
1352cc4f6944STaniya Das 	 * Assume that parent_rate is actually the parent because
1353cc4f6944STaniya Das 	 * we can't do any better at figuring it out when the table
1354cc4f6944STaniya Das 	 * hasn't been populated yet. We only populate the table
1355cc4f6944STaniya Das 	 * in determine_rate because we can't guarantee the parents
1356cc4f6944STaniya Das 	 * will be registered with the framework until then.
1357cc4f6944STaniya Das 	 */
1358cc4f6944STaniya Das 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + SE_PERF_DFSR(level),
1359cc4f6944STaniya Das 		    &cfg);
1360cc4f6944STaniya Das 
1361cc4f6944STaniya Das 	mask = BIT(rcg->hid_width) - 1;
1362cc4f6944STaniya Das 	pre_div = 1;
1363cc4f6944STaniya Das 	if (cfg & mask)
1364cc4f6944STaniya Das 		pre_div = cfg & mask;
1365cc4f6944STaniya Das 
1366cc4f6944STaniya Das 	mode = cfg & CFG_MODE_MASK;
1367cc4f6944STaniya Das 	mode >>= CFG_MODE_SHIFT;
1368cc4f6944STaniya Das 	if (mode) {
1369cc4f6944STaniya Das 		mask = BIT(rcg->mnd_width) - 1;
1370cc4f6944STaniya Das 		regmap_read(rcg->clkr.regmap,
1371cc4f6944STaniya Das 			    rcg->cmd_rcgr + SE_PERF_M_DFSR(level), &m);
1372cc4f6944STaniya Das 		m &= mask;
1373cc4f6944STaniya Das 
1374cc4f6944STaniya Das 		regmap_read(rcg->clkr.regmap,
1375cc4f6944STaniya Das 			    rcg->cmd_rcgr + SE_PERF_N_DFSR(level), &n);
1376cc4f6944STaniya Das 		n = ~n;
1377cc4f6944STaniya Das 		n &= mask;
1378cc4f6944STaniya Das 		n += m;
1379cc4f6944STaniya Das 	}
1380cc4f6944STaniya Das 
1381cc4f6944STaniya Das 	return calc_rate(parent_rate, m, n, mode, pre_div);
1382cc4f6944STaniya Das }
1383cc4f6944STaniya Das 
1384cc4f6944STaniya Das static const struct clk_ops clk_rcg2_dfs_ops = {
1385cc4f6944STaniya Das 	.is_enabled = clk_rcg2_is_enabled,
1386cc4f6944STaniya Das 	.get_parent = clk_rcg2_get_parent,
1387cc4f6944STaniya Das 	.determine_rate = clk_rcg2_dfs_determine_rate,
1388cc4f6944STaniya Das 	.recalc_rate = clk_rcg2_dfs_recalc_rate,
1389cc4f6944STaniya Das };
1390cc4f6944STaniya Das 
clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data * data,struct regmap * regmap)1391cc4f6944STaniya Das static int clk_rcg2_enable_dfs(const struct clk_rcg_dfs_data *data,
1392cc4f6944STaniya Das 			       struct regmap *regmap)
1393cc4f6944STaniya Das {
1394cc4f6944STaniya Das 	struct clk_rcg2 *rcg = data->rcg;
1395cc4f6944STaniya Das 	struct clk_init_data *init = data->init;
1396cc4f6944STaniya Das 	u32 val;
1397cc4f6944STaniya Das 	int ret;
1398cc4f6944STaniya Das 
1399cc4f6944STaniya Das 	ret = regmap_read(regmap, rcg->cmd_rcgr + SE_CMD_DFSR_OFFSET, &val);
1400cc4f6944STaniya Das 	if (ret)
1401cc4f6944STaniya Das 		return -EINVAL;
1402cc4f6944STaniya Das 
1403cc4f6944STaniya Das 	if (!(val & SE_CMD_DFS_EN))
1404cc4f6944STaniya Das 		return 0;
1405cc4f6944STaniya Das 
1406cc4f6944STaniya Das 	/*
1407cc4f6944STaniya Das 	 * Rate changes with consumer writing a register in
1408cc4f6944STaniya Das 	 * their own I/O region
1409cc4f6944STaniya Das 	 */
1410cc4f6944STaniya Das 	init->flags |= CLK_GET_RATE_NOCACHE;
1411cc4f6944STaniya Das 	init->ops = &clk_rcg2_dfs_ops;
1412cc4f6944STaniya Das 
1413cc4f6944STaniya Das 	rcg->freq_tbl = NULL;
1414cc4f6944STaniya Das 
1415cc4f6944STaniya Das 	return 0;
1416cc4f6944STaniya Das }
1417cc4f6944STaniya Das 
qcom_cc_register_rcg_dfs(struct regmap * regmap,const struct clk_rcg_dfs_data * rcgs,size_t len)1418cc4f6944STaniya Das int qcom_cc_register_rcg_dfs(struct regmap *regmap,
1419cc4f6944STaniya Das 			     const struct clk_rcg_dfs_data *rcgs, size_t len)
1420cc4f6944STaniya Das {
1421cc4f6944STaniya Das 	int i, ret;
1422cc4f6944STaniya Das 
1423cc4f6944STaniya Das 	for (i = 0; i < len; i++) {
1424cc4f6944STaniya Das 		ret = clk_rcg2_enable_dfs(&rcgs[i], regmap);
1425c4214413SStephen Boyd 		if (ret)
1426cc4f6944STaniya Das 			return ret;
1427cc4f6944STaniya Das 	}
1428cc4f6944STaniya Das 
1429cc4f6944STaniya Das 	return 0;
1430cc4f6944STaniya Das }
1431cc4f6944STaniya Das EXPORT_SYMBOL_GPL(qcom_cc_register_rcg_dfs);
1432cddf1f82STaniya Das 
clk_rcg2_dp_set_rate(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate)1433cddf1f82STaniya Das static int clk_rcg2_dp_set_rate(struct clk_hw *hw, unsigned long rate,
1434cddf1f82STaniya Das 			unsigned long parent_rate)
1435cddf1f82STaniya Das {
1436cddf1f82STaniya Das 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1437cddf1f82STaniya Das 	struct freq_tbl f = { 0 };
1438cddf1f82STaniya Das 	u32 mask = BIT(rcg->hid_width) - 1;
1439cddf1f82STaniya Das 	u32 hid_div, cfg;
1440cddf1f82STaniya Das 	int i, num_parents = clk_hw_get_num_parents(hw);
1441cddf1f82STaniya Das 	unsigned long num, den;
1442cddf1f82STaniya Das 
1443cddf1f82STaniya Das 	rational_best_approximation(parent_rate, rate,
1444cddf1f82STaniya Das 			GENMASK(rcg->mnd_width - 1, 0),
1445cddf1f82STaniya Das 			GENMASK(rcg->mnd_width - 1, 0), &den, &num);
1446cddf1f82STaniya Das 
1447cddf1f82STaniya Das 	if (!num || !den)
1448cddf1f82STaniya Das 		return -EINVAL;
1449cddf1f82STaniya Das 
1450cddf1f82STaniya Das 	regmap_read(rcg->clkr.regmap, rcg->cmd_rcgr + CFG_REG, &cfg);
1451cddf1f82STaniya Das 	hid_div = cfg;
1452cddf1f82STaniya Das 	cfg &= CFG_SRC_SEL_MASK;
1453cddf1f82STaniya Das 	cfg >>= CFG_SRC_SEL_SHIFT;
1454cddf1f82STaniya Das 
1455cddf1f82STaniya Das 	for (i = 0; i < num_parents; i++) {
1456cddf1f82STaniya Das 		if (cfg == rcg->parent_map[i].cfg) {
1457cddf1f82STaniya Das 			f.src = rcg->parent_map[i].src;
1458cddf1f82STaniya Das 			break;
1459cddf1f82STaniya Das 		}
1460cddf1f82STaniya Das 	}
1461cddf1f82STaniya Das 
1462cddf1f82STaniya Das 	f.pre_div = hid_div;
1463cddf1f82STaniya Das 	f.pre_div >>= CFG_SRC_DIV_SHIFT;
1464cddf1f82STaniya Das 	f.pre_div &= mask;
1465cddf1f82STaniya Das 
1466cddf1f82STaniya Das 	if (num != den) {
1467cddf1f82STaniya Das 		f.m = num;
1468cddf1f82STaniya Das 		f.n = den;
1469cddf1f82STaniya Das 	} else {
1470cddf1f82STaniya Das 		f.m = 0;
1471cddf1f82STaniya Das 		f.n = 0;
1472cddf1f82STaniya Das 	}
1473cddf1f82STaniya Das 
1474cddf1f82STaniya Das 	return clk_rcg2_configure(rcg, &f);
1475cddf1f82STaniya Das }
1476cddf1f82STaniya Das 
clk_rcg2_dp_set_rate_and_parent(struct clk_hw * hw,unsigned long rate,unsigned long parent_rate,u8 index)1477cddf1f82STaniya Das static int clk_rcg2_dp_set_rate_and_parent(struct clk_hw *hw,
1478cddf1f82STaniya Das 		unsigned long rate, unsigned long parent_rate, u8 index)
1479cddf1f82STaniya Das {
1480cddf1f82STaniya Das 	return clk_rcg2_dp_set_rate(hw, rate, parent_rate);
1481cddf1f82STaniya Das }
1482cddf1f82STaniya Das 
clk_rcg2_dp_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)1483cddf1f82STaniya Das static int clk_rcg2_dp_determine_rate(struct clk_hw *hw,
1484cddf1f82STaniya Das 				struct clk_rate_request *req)
1485cddf1f82STaniya Das {
1486355a7d75SStephen Boyd 	struct clk_rcg2 *rcg = to_clk_rcg2(hw);
1487355a7d75SStephen Boyd 	unsigned long num, den;
1488355a7d75SStephen Boyd 	u64 tmp;
1489cddf1f82STaniya Das 
1490355a7d75SStephen Boyd 	/* Parent rate is a fixed phy link rate */
1491355a7d75SStephen Boyd 	rational_best_approximation(req->best_parent_rate, req->rate,
1492355a7d75SStephen Boyd 			GENMASK(rcg->mnd_width - 1, 0),
1493355a7d75SStephen Boyd 			GENMASK(rcg->mnd_width - 1, 0), &den, &num);
1494cddf1f82STaniya Das 
1495355a7d75SStephen Boyd 	if (!num || !den)
1496355a7d75SStephen Boyd 		return -EINVAL;
1497355a7d75SStephen Boyd 
1498355a7d75SStephen Boyd 	tmp = req->best_parent_rate * num;
1499355a7d75SStephen Boyd 	do_div(tmp, den);
1500355a7d75SStephen Boyd 	req->rate = tmp;
1501cddf1f82STaniya Das 
1502cddf1f82STaniya Das 	return 0;
1503cddf1f82STaniya Das }
1504cddf1f82STaniya Das 
1505cddf1f82STaniya Das const struct clk_ops clk_dp_ops = {
1506cddf1f82STaniya Das 	.is_enabled = clk_rcg2_is_enabled,
1507cddf1f82STaniya Das 	.get_parent = clk_rcg2_get_parent,
1508cddf1f82STaniya Das 	.set_parent = clk_rcg2_set_parent,
1509cddf1f82STaniya Das 	.recalc_rate = clk_rcg2_recalc_rate,
1510cddf1f82STaniya Das 	.set_rate = clk_rcg2_dp_set_rate,
1511cddf1f82STaniya Das 	.set_rate_and_parent = clk_rcg2_dp_set_rate_and_parent,
1512cddf1f82STaniya Das 	.determine_rate = clk_rcg2_dp_determine_rate,
1513cddf1f82STaniya Das };
1514cddf1f82STaniya Das EXPORT_SYMBOL_GPL(clk_dp_ops);
1515