• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
4  */
5 
6 #include <linux/clk-provider.h>
7 #include <linux/clkdev.h>
8 #include <linux/clk/at91_pmc.h>
9 #include <linux/of.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/regmap.h>
12 #include <soc/at91/atmel-sfr.h>
13 
14 #include "pmc.h"
15 
16 /*
17  * The purpose of this clock is to generate a 480 MHz signal. A different
18  * rate can't be configured.
19  */
20 #define UTMI_RATE	480000000
21 
22 struct clk_utmi {
23 	struct clk_hw hw;
24 	struct regmap *regmap_pmc;
25 	struct regmap *regmap_sfr;
26 	struct at91_clk_pms pms;
27 };
28 
29 #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw)
30 
clk_utmi_ready(struct regmap * regmap)31 static inline bool clk_utmi_ready(struct regmap *regmap)
32 {
33 	unsigned int status;
34 
35 	regmap_read(regmap, AT91_PMC_SR, &status);
36 
37 	return status & AT91_PMC_LOCKU;
38 }
39 
clk_utmi_prepare(struct clk_hw * hw)40 static int clk_utmi_prepare(struct clk_hw *hw)
41 {
42 	struct clk_hw *hw_parent;
43 	struct clk_utmi *utmi = to_clk_utmi(hw);
44 	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
45 			    AT91_PMC_BIASEN;
46 	unsigned int utmi_ref_clk_freq;
47 	unsigned long parent_rate;
48 
49 	/*
50 	 * If mainck rate is different from 12 MHz, we have to configure the
51 	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
52 	 * the utmi clock.
53 	 */
54 	hw_parent = clk_hw_get_parent(hw);
55 	parent_rate = clk_hw_get_rate(hw_parent);
56 
57 	switch (parent_rate) {
58 	case 12000000:
59 		utmi_ref_clk_freq = 0;
60 		break;
61 	case 16000000:
62 		utmi_ref_clk_freq = 1;
63 		break;
64 	case 24000000:
65 		utmi_ref_clk_freq = 2;
66 		break;
67 	/*
68 	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
69 	 * maximum value is 24 MHz.
70 	 */
71 	case 48000000:
72 		utmi_ref_clk_freq = 3;
73 		break;
74 	default:
75 		pr_err("UTMICK: unsupported mainck rate\n");
76 		return -EINVAL;
77 	}
78 
79 	if (utmi->regmap_sfr) {
80 		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
81 				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
82 	} else if (utmi_ref_clk_freq) {
83 		pr_err("UTMICK: sfr node required\n");
84 		return -EINVAL;
85 	}
86 
87 	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr);
88 
89 	while (!clk_utmi_ready(utmi->regmap_pmc))
90 		cpu_relax();
91 
92 	return 0;
93 }
94 
clk_utmi_is_prepared(struct clk_hw * hw)95 static int clk_utmi_is_prepared(struct clk_hw *hw)
96 {
97 	struct clk_utmi *utmi = to_clk_utmi(hw);
98 
99 	return clk_utmi_ready(utmi->regmap_pmc);
100 }
101 
clk_utmi_unprepare(struct clk_hw * hw)102 static void clk_utmi_unprepare(struct clk_hw *hw)
103 {
104 	struct clk_utmi *utmi = to_clk_utmi(hw);
105 
106 	regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR,
107 			   AT91_PMC_UPLLEN, 0);
108 }
109 
clk_utmi_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)110 static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw,
111 					  unsigned long parent_rate)
112 {
113 	/* UTMI clk rate is fixed. */
114 	return UTMI_RATE;
115 }
116 
clk_utmi_save_context(struct clk_hw * hw)117 static int clk_utmi_save_context(struct clk_hw *hw)
118 {
119 	struct clk_utmi *utmi = to_clk_utmi(hw);
120 
121 	utmi->pms.status = clk_utmi_is_prepared(hw);
122 
123 	return 0;
124 }
125 
clk_utmi_restore_context(struct clk_hw * hw)126 static void clk_utmi_restore_context(struct clk_hw *hw)
127 {
128 	struct clk_utmi *utmi = to_clk_utmi(hw);
129 
130 	if (utmi->pms.status)
131 		clk_utmi_prepare(hw);
132 }
133 
134 static const struct clk_ops utmi_ops = {
135 	.prepare = clk_utmi_prepare,
136 	.unprepare = clk_utmi_unprepare,
137 	.is_prepared = clk_utmi_is_prepared,
138 	.recalc_rate = clk_utmi_recalc_rate,
139 	.save_context = clk_utmi_save_context,
140 	.restore_context = clk_utmi_restore_context,
141 };
142 
143 static struct clk_hw * __init
at91_clk_register_utmi_internal(struct regmap * regmap_pmc,struct regmap * regmap_sfr,const char * name,const char * parent_name,const struct clk_ops * ops,unsigned long flags)144 at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
145 				struct regmap *regmap_sfr,
146 				const char *name, const char *parent_name,
147 				const struct clk_ops *ops, unsigned long flags)
148 {
149 	struct clk_utmi *utmi;
150 	struct clk_hw *hw;
151 	struct clk_init_data init;
152 	int ret;
153 
154 	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
155 	if (!utmi)
156 		return ERR_PTR(-ENOMEM);
157 
158 	init.name = name;
159 	init.ops = ops;
160 	init.parent_names = parent_name ? &parent_name : NULL;
161 	init.num_parents = parent_name ? 1 : 0;
162 	init.flags = flags;
163 
164 	utmi->hw.init = &init;
165 	utmi->regmap_pmc = regmap_pmc;
166 	utmi->regmap_sfr = regmap_sfr;
167 
168 	hw = &utmi->hw;
169 	ret = clk_hw_register(NULL, &utmi->hw);
170 	if (ret) {
171 		kfree(utmi);
172 		hw = ERR_PTR(ret);
173 	}
174 
175 	return hw;
176 }
177 
178 struct clk_hw * __init
at91_clk_register_utmi(struct regmap * regmap_pmc,struct regmap * regmap_sfr,const char * name,const char * parent_name)179 at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
180 		       const char *name, const char *parent_name)
181 {
182 	return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
183 			parent_name, &utmi_ops, CLK_SET_RATE_GATE);
184 }
185 
clk_utmi_sama7g5_prepare(struct clk_hw * hw)186 static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
187 {
188 	struct clk_utmi *utmi = to_clk_utmi(hw);
189 	struct clk_hw *hw_parent;
190 	unsigned long parent_rate;
191 	unsigned int val;
192 
193 	hw_parent = clk_hw_get_parent(hw);
194 	parent_rate = clk_hw_get_rate(hw_parent);
195 
196 	switch (parent_rate) {
197 	case 16000000:
198 		val = 0;
199 		break;
200 	case 20000000:
201 		val = 2;
202 		break;
203 	case 24000000:
204 		val = 3;
205 		break;
206 	case 32000000:
207 		val = 5;
208 		break;
209 	default:
210 		pr_err("UTMICK: unsupported main_xtal rate\n");
211 		return -EINVAL;
212 	}
213 
214 	regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
215 
216 	return 0;
217 
218 }
219 
clk_utmi_sama7g5_is_prepared(struct clk_hw * hw)220 static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
221 {
222 	struct clk_utmi *utmi = to_clk_utmi(hw);
223 	struct clk_hw *hw_parent;
224 	unsigned long parent_rate;
225 	unsigned int val;
226 
227 	hw_parent = clk_hw_get_parent(hw);
228 	parent_rate = clk_hw_get_rate(hw_parent);
229 
230 	regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
231 	switch (val & 0x7) {
232 	case 0:
233 		if (parent_rate == 16000000)
234 			return 1;
235 		break;
236 	case 2:
237 		if (parent_rate == 20000000)
238 			return 1;
239 		break;
240 	case 3:
241 		if (parent_rate == 24000000)
242 			return 1;
243 		break;
244 	case 5:
245 		if (parent_rate == 32000000)
246 			return 1;
247 		break;
248 	default:
249 		break;
250 	}
251 
252 	return 0;
253 }
254 
clk_utmi_sama7g5_save_context(struct clk_hw * hw)255 static int clk_utmi_sama7g5_save_context(struct clk_hw *hw)
256 {
257 	struct clk_utmi *utmi = to_clk_utmi(hw);
258 
259 	utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw);
260 
261 	return 0;
262 }
263 
clk_utmi_sama7g5_restore_context(struct clk_hw * hw)264 static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw)
265 {
266 	struct clk_utmi *utmi = to_clk_utmi(hw);
267 
268 	if (utmi->pms.status)
269 		clk_utmi_sama7g5_prepare(hw);
270 }
271 
272 static const struct clk_ops sama7g5_utmi_ops = {
273 	.prepare = clk_utmi_sama7g5_prepare,
274 	.is_prepared = clk_utmi_sama7g5_is_prepared,
275 	.recalc_rate = clk_utmi_recalc_rate,
276 	.save_context = clk_utmi_sama7g5_save_context,
277 	.restore_context = clk_utmi_sama7g5_restore_context,
278 };
279 
280 struct clk_hw * __init
at91_clk_sama7g5_register_utmi(struct regmap * regmap_pmc,const char * name,const char * parent_name)281 at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
282 			       const char *parent_name)
283 {
284 	return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
285 			parent_name, &sama7g5_utmi_ops, 0);
286 }
287