• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2016 Maxime Ripard
4  * Maxime Ripard <maxime.ripard@free-electrons.com>
5  */
6 
7 #include <linux/clk-provider.h>
8 #include <linux/io.h>
9 #include <linux/delay.h>
10 
11 #include "ccu_gate.h"
12 #include "ccu_mult.h"
13 
14 struct _ccu_mult {
15 	unsigned long	mult, min, max;
16 };
17 
ccu_mult_find_best(unsigned long parent,u64 rate,struct _ccu_mult * mult)18 static void ccu_mult_find_best(unsigned long parent, u64 rate,
19 			       struct _ccu_mult *mult)
20 {
21 	u64 _mult = rate;
22 
23 	do_div(_mult, parent);
24 	if (_mult < mult->min)
25 		_mult = mult->min;
26 
27 	if (_mult > mult->max)
28 		_mult = mult->max;
29 
30 	mult->mult = _mult;
31 }
32 
ccu_mult_round_rate(struct ccu_mux_internal * mux,struct clk_hw * parent,unsigned long * parent_rate,unsigned long rate,void * data)33 static unsigned long ccu_mult_round_rate(struct ccu_mux_internal *mux,
34 					 struct clk_hw *parent,
35 					 unsigned long *parent_rate,
36 					 unsigned long rate,
37 					 void *data)
38 {
39 	struct ccu_mult *cm = data;
40 	struct _ccu_mult _cm;
41 
42 	_cm.min = cm->mult.min;
43 
44 	if (cm->mult.max)
45 		_cm.max = cm->mult.max;
46 	else
47 		_cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
48 
49 	ccu_mult_find_best(*parent_rate, rate, &_cm);
50 
51 	return *parent_rate * _cm.mult;
52 }
53 
ccu_mult_disable(struct clk_hw * hw)54 static void ccu_mult_disable(struct clk_hw *hw)
55 {
56 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
57 
58 	return ccu_gate_helper_disable(&cm->common, cm->enable);
59 }
60 
ccu_mult_enable(struct clk_hw * hw)61 static int ccu_mult_enable(struct clk_hw *hw)
62 {
63 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
64 
65 	return ccu_gate_helper_enable(&cm->common, cm->enable);
66 }
67 
ccu_mult_is_enabled(struct clk_hw * hw)68 static int ccu_mult_is_enabled(struct clk_hw *hw)
69 {
70 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
71 
72 	return ccu_gate_helper_is_enabled(&cm->common, cm->enable);
73 }
74 
ccu_mult_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)75 static unsigned long ccu_mult_recalc_rate(struct clk_hw *hw,
76 					unsigned long parent_rate)
77 {
78 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
79 	unsigned long val;
80 	u32 reg;
81 
82 	if (ccu_frac_helper_is_enabled(&cm->common, &cm->frac))
83 		return ccu_frac_helper_read_rate(&cm->common, &cm->frac);
84 
85 	reg = readl(cm->common.base + cm->common.reg);
86 	val = reg >> cm->mult.shift;
87 	val &= (1 << cm->mult.width) - 1;
88 
89 	parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
90 						  parent_rate);
91 
92 	return parent_rate * (val + cm->mult.offset);
93 }
94 
ccu_mult_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)95 static int ccu_mult_determine_rate(struct clk_hw *hw,
96 				struct clk_rate_request *req)
97 {
98 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
99 
100 	return ccu_mux_helper_determine_rate(&cm->common, &cm->mux,
101 					     req, ccu_mult_round_rate, cm);
102 }
103 
ccu_mult_set_rate(struct clk_hw * hw,unsigned long _rate,unsigned long parent_rate)104 static int ccu_mult_set_rate(struct clk_hw *hw, unsigned long _rate,
105 			   unsigned long parent_rate)
106 {
107 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
108 	struct _ccu_mult _cm;
109 	unsigned long flags;
110 	u32 reg;
111 	u64 rate = _rate;
112 
113 	if (ccu_frac_helper_has_rate(&cm->common, &cm->frac, rate)) {
114 		ccu_frac_helper_enable(&cm->common, &cm->frac);
115 
116 		return ccu_frac_helper_set_rate(&cm->common, &cm->frac,
117 						rate, cm->lock);
118 	} else {
119 		ccu_frac_helper_disable(&cm->common, &cm->frac);
120 	}
121 
122 	parent_rate = ccu_mux_helper_apply_prediv(&cm->common, &cm->mux, -1,
123 						  parent_rate);
124 
125 	_cm.min = cm->mult.min;
126 
127 	if (cm->mult.max)
128 		_cm.max = cm->mult.max;
129 	else
130 		_cm.max = (1 << cm->mult.width) + cm->mult.offset - 1;
131 
132 	ccu_mult_find_best(parent_rate, rate, &_cm);
133 
134 	spin_lock_irqsave(cm->common.lock, flags);
135 
136 	reg = readl(cm->common.base + cm->common.reg);
137 	reg &= ~GENMASK(cm->mult.width + cm->mult.shift - 1, cm->mult.shift);
138 	reg |= ((_cm.mult - cm->mult.offset) << cm->mult.shift);
139 
140 	writel(reg, cm->common.base + cm->common.reg);
141 
142 	spin_unlock_irqrestore(cm->common.lock, flags);
143 
144 	ccu_helper_wait_for_lock(&cm->common, cm->lock);
145 	msleep(2);
146 
147 	return 0;
148 }
149 
ccu_mult_get_parent(struct clk_hw * hw)150 static u8 ccu_mult_get_parent(struct clk_hw *hw)
151 {
152 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
153 
154 	return ccu_mux_helper_get_parent(&cm->common, &cm->mux);
155 }
156 
ccu_mult_set_parent(struct clk_hw * hw,u8 index)157 static int ccu_mult_set_parent(struct clk_hw *hw, u8 index)
158 {
159 	struct ccu_mult *cm = hw_to_ccu_mult(hw);
160 
161 	return ccu_mux_helper_set_parent(&cm->common, &cm->mux, index);
162 }
163 
164 const struct clk_ops ccu_mult_ops = {
165 	.disable	= ccu_mult_disable,
166 	.enable		= ccu_mult_enable,
167 	.is_enabled	= ccu_mult_is_enabled,
168 
169 	.get_parent	= ccu_mult_get_parent,
170 	.set_parent	= ccu_mult_set_parent,
171 
172 	.determine_rate	= ccu_mult_determine_rate,
173 	.recalc_rate	= ccu_mult_recalc_rate,
174 	.set_rate	= ccu_mult_set_rate,
175 };
176 EXPORT_SYMBOL_GPL(ccu_mult_ops);
177