]>
Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
f090fb37 BB |
2 | /* |
3 | * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com> | |
f090fb37 BB |
4 | */ |
5 | ||
6 | #include <linux/clk-provider.h> | |
7 | #include <linux/clkdev.h> | |
8 | #include <linux/clk/at91_pmc.h> | |
f090fb37 | 9 | #include <linux/of.h> |
1bdf0232 BB |
10 | #include <linux/mfd/syscon.h> |
11 | #include <linux/regmap.h> | |
92041a9f | 12 | #include <soc/at91/atmel-sfr.h> |
f090fb37 BB |
13 | |
14 | #include "pmc.h" | |
15 | ||
92041a9f LD |
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 | |
f090fb37 BB |
21 | |
22 | struct clk_utmi { | |
23 | struct clk_hw hw; | |
92041a9f LD |
24 | struct regmap *regmap_pmc; |
25 | struct regmap *regmap_sfr; | |
f090fb37 BB |
26 | }; |
27 | ||
28 | #define to_clk_utmi(hw) container_of(hw, struct clk_utmi, hw) | |
29 | ||
1bdf0232 BB |
30 | static inline bool clk_utmi_ready(struct regmap *regmap) |
31 | { | |
32 | unsigned int status; | |
33 | ||
34 | regmap_read(regmap, AT91_PMC_SR, &status); | |
35 | ||
36 | return status & AT91_PMC_LOCKU; | |
37 | } | |
38 | ||
f090fb37 BB |
39 | static int clk_utmi_prepare(struct clk_hw *hw) |
40 | { | |
92041a9f | 41 | struct clk_hw *hw_parent; |
f090fb37 | 42 | struct clk_utmi *utmi = to_clk_utmi(hw); |
1bdf0232 BB |
43 | unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT | |
44 | AT91_PMC_BIASEN; | |
92041a9f LD |
45 | unsigned int utmi_ref_clk_freq; |
46 | unsigned long parent_rate; | |
47 | ||
48 | /* | |
49 | * If mainck rate is different from 12 MHz, we have to configure the | |
50 | * FREQ field of the SFR_UTMICKTRIM register to generate properly | |
51 | * the utmi clock. | |
52 | */ | |
53 | hw_parent = clk_hw_get_parent(hw); | |
54 | parent_rate = clk_hw_get_rate(hw_parent); | |
55 | ||
56 | switch (parent_rate) { | |
57 | case 12000000: | |
58 | utmi_ref_clk_freq = 0; | |
59 | break; | |
60 | case 16000000: | |
61 | utmi_ref_clk_freq = 1; | |
62 | break; | |
63 | case 24000000: | |
64 | utmi_ref_clk_freq = 2; | |
65 | break; | |
66 | /* | |
67 | * Not supported on SAMA5D2 but it's not an issue since MAINCK | |
68 | * maximum value is 24 MHz. | |
69 | */ | |
70 | case 48000000: | |
71 | utmi_ref_clk_freq = 3; | |
72 | break; | |
73 | default: | |
74 | pr_err("UTMICK: unsupported mainck rate\n"); | |
75 | return -EINVAL; | |
76 | } | |
f090fb37 | 77 | |
92041a9f LD |
78 | if (utmi->regmap_sfr) { |
79 | regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM, | |
80 | AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq); | |
81 | } else if (utmi_ref_clk_freq) { | |
82 | pr_err("UTMICK: sfr node required\n"); | |
83 | return -EINVAL; | |
84 | } | |
f090fb37 | 85 | |
92041a9f LD |
86 | regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, uckr, uckr); |
87 | ||
88 | while (!clk_utmi_ready(utmi->regmap_pmc)) | |
99a81706 | 89 | cpu_relax(); |
f090fb37 BB |
90 | |
91 | return 0; | |
92 | } | |
93 | ||
94 | static int clk_utmi_is_prepared(struct clk_hw *hw) | |
95 | { | |
96 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
f090fb37 | 97 | |
92041a9f | 98 | return clk_utmi_ready(utmi->regmap_pmc); |
f090fb37 BB |
99 | } |
100 | ||
101 | static void clk_utmi_unprepare(struct clk_hw *hw) | |
102 | { | |
103 | struct clk_utmi *utmi = to_clk_utmi(hw); | |
f090fb37 | 104 | |
92041a9f LD |
105 | regmap_update_bits(utmi->regmap_pmc, AT91_CKGR_UCKR, |
106 | AT91_PMC_UPLLEN, 0); | |
f090fb37 BB |
107 | } |
108 | ||
109 | static unsigned long clk_utmi_recalc_rate(struct clk_hw *hw, | |
110 | unsigned long parent_rate) | |
111 | { | |
92041a9f LD |
112 | /* UTMI clk rate is fixed. */ |
113 | return UTMI_RATE; | |
f090fb37 BB |
114 | } |
115 | ||
116 | static const struct clk_ops utmi_ops = { | |
117 | .prepare = clk_utmi_prepare, | |
118 | .unprepare = clk_utmi_unprepare, | |
119 | .is_prepared = clk_utmi_is_prepared, | |
120 | .recalc_rate = clk_utmi_recalc_rate, | |
121 | }; | |
122 | ||
b2e39dc0 | 123 | struct clk_hw * __init |
92041a9f | 124 | at91_clk_register_utmi(struct regmap *regmap_pmc, struct regmap *regmap_sfr, |
f090fb37 BB |
125 | const char *name, const char *parent_name) |
126 | { | |
f090fb37 | 127 | struct clk_utmi *utmi; |
f5644f10 | 128 | struct clk_hw *hw; |
f090fb37 | 129 | struct clk_init_data init; |
f5644f10 | 130 | int ret; |
f090fb37 BB |
131 | |
132 | utmi = kzalloc(sizeof(*utmi), GFP_KERNEL); | |
133 | if (!utmi) | |
134 | return ERR_PTR(-ENOMEM); | |
135 | ||
136 | init.name = name; | |
137 | init.ops = &utmi_ops; | |
138 | init.parent_names = parent_name ? &parent_name : NULL; | |
139 | init.num_parents = parent_name ? 1 : 0; | |
140 | init.flags = CLK_SET_RATE_GATE; | |
141 | ||
142 | utmi->hw.init = &init; | |
92041a9f LD |
143 | utmi->regmap_pmc = regmap_pmc; |
144 | utmi->regmap_sfr = regmap_sfr; | |
f090fb37 | 145 | |
f5644f10 SB |
146 | hw = &utmi->hw; |
147 | ret = clk_hw_register(NULL, &utmi->hw); | |
148 | if (ret) { | |
f090fb37 | 149 | kfree(utmi); |
f5644f10 SB |
150 | hw = ERR_PTR(ret); |
151 | } | |
f090fb37 | 152 | |
f5644f10 | 153 | return hw; |
f090fb37 | 154 | } |