Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/clk/at91/clk-utmi.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0-or-later
2
/*
3
* Copyright (C) 2013 Boris BREZILLON <[email protected]>
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
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
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
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
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
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
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
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
144
at91_clk_register_utmi_internal(struct regmap *regmap_pmc,
145
struct regmap *regmap_sfr,
146
const char *name, const char *parent_name,
147
struct clk_hw *parent_hw,
148
const struct clk_ops *ops, unsigned long flags)
149
{
150
struct clk_utmi *utmi;
151
struct clk_hw *hw;
152
struct clk_init_data init = {};
153
int ret;
154
155
if (!(parent_name || parent_hw))
156
return ERR_PTR(-EINVAL);
157
158
utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
159
if (!utmi)
160
return ERR_PTR(-ENOMEM);
161
162
init.name = name;
163
init.ops = ops;
164
if (parent_hw)
165
init.parent_hws = (const struct clk_hw **)&parent_hw;
166
else
167
init.parent_names = &parent_name;
168
init.num_parents = 1;
169
init.flags = flags;
170
171
utmi->hw.init = &init;
172
utmi->regmap_pmc = regmap_pmc;
173
utmi->regmap_sfr = regmap_sfr;
174
175
hw = &utmi->hw;
176
ret = clk_hw_register(NULL, &utmi->hw);
177
if (ret) {
178
kfree(utmi);
179
hw = ERR_PTR(ret);
180
}
181
182
return hw;
183
}
184
185
struct clk_hw * __init
186
at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr,
187
const char *name, const char *parent_name,
188
struct clk_hw *parent_hw)
189
{
190
return at91_clk_register_utmi_internal(regmap_pmc, regmap_sfr, name,
191
parent_name, parent_hw, &utmi_ops, CLK_SET_RATE_GATE);
192
}
193
194
static int clk_utmi_sama7g5_prepare(struct clk_hw *hw)
195
{
196
struct clk_utmi *utmi = to_clk_utmi(hw);
197
struct clk_hw *hw_parent;
198
unsigned long parent_rate;
199
unsigned int val;
200
201
hw_parent = clk_hw_get_parent(hw);
202
parent_rate = clk_hw_get_rate(hw_parent);
203
204
switch (parent_rate) {
205
case 16000000:
206
val = 0;
207
break;
208
case 20000000:
209
val = 2;
210
break;
211
case 24000000:
212
val = 3;
213
break;
214
case 32000000:
215
val = 5;
216
break;
217
default:
218
pr_err("UTMICK: unsupported main_xtal rate\n");
219
return -EINVAL;
220
}
221
222
regmap_write(utmi->regmap_pmc, AT91_PMC_XTALF, val);
223
224
return 0;
225
226
}
227
228
static int clk_utmi_sama7g5_is_prepared(struct clk_hw *hw)
229
{
230
struct clk_utmi *utmi = to_clk_utmi(hw);
231
struct clk_hw *hw_parent;
232
unsigned long parent_rate;
233
unsigned int val;
234
235
hw_parent = clk_hw_get_parent(hw);
236
parent_rate = clk_hw_get_rate(hw_parent);
237
238
regmap_read(utmi->regmap_pmc, AT91_PMC_XTALF, &val);
239
switch (val & 0x7) {
240
case 0:
241
if (parent_rate == 16000000)
242
return 1;
243
break;
244
case 2:
245
if (parent_rate == 20000000)
246
return 1;
247
break;
248
case 3:
249
if (parent_rate == 24000000)
250
return 1;
251
break;
252
case 5:
253
if (parent_rate == 32000000)
254
return 1;
255
break;
256
default:
257
break;
258
}
259
260
return 0;
261
}
262
263
static int clk_utmi_sama7g5_save_context(struct clk_hw *hw)
264
{
265
struct clk_utmi *utmi = to_clk_utmi(hw);
266
267
utmi->pms.status = clk_utmi_sama7g5_is_prepared(hw);
268
269
return 0;
270
}
271
272
static void clk_utmi_sama7g5_restore_context(struct clk_hw *hw)
273
{
274
struct clk_utmi *utmi = to_clk_utmi(hw);
275
276
if (utmi->pms.status)
277
clk_utmi_sama7g5_prepare(hw);
278
}
279
280
static const struct clk_ops sama7g5_utmi_ops = {
281
.prepare = clk_utmi_sama7g5_prepare,
282
.is_prepared = clk_utmi_sama7g5_is_prepared,
283
.recalc_rate = clk_utmi_recalc_rate,
284
.save_context = clk_utmi_sama7g5_save_context,
285
.restore_context = clk_utmi_sama7g5_restore_context,
286
};
287
288
struct clk_hw * __init
289
at91_clk_sama7g5_register_utmi(struct regmap *regmap_pmc, const char *name,
290
const char *parent_name, struct clk_hw *parent_hw)
291
{
292
return at91_clk_register_utmi_internal(regmap_pmc, NULL, name,
293
parent_name, parent_hw, &sama7g5_utmi_ops, 0);
294
}
295
296