2
2
* Copyright (c) 2015 Endless Mobile, Inc.
3
3
* Author: Carlo Caione <[email protected] >
4
4
*
5
+ * Copyright (c) 2018 Baylibre, SAS.
6
+ * Author: Jerome Brunet <[email protected] >
7
+ *
5
8
* This program is free software; you can redistribute it and/or modify it
6
9
* under the terms and conditions of the GNU General Public License,
7
10
* version 2, as published by the Free Software Foundation.
27
30
* | |
28
31
* FREF VCO
29
32
*
30
- * out = ( in * M / N) >> OD
33
+ * out = in * (m + frac / frac_max) / (n << sum(ods))
31
34
*/
32
35
33
36
#include <linux/clk-provider.h>
@@ -48,73 +51,110 @@ meson_clk_pll_data(struct clk_regmap *clk)
48
51
return (struct meson_clk_pll_data * )clk -> data ;
49
52
}
50
53
54
+ static unsigned long __pll_params_to_rate (unsigned long parent_rate ,
55
+ const struct pll_rate_table * pllt ,
56
+ u16 frac ,
57
+ struct meson_clk_pll_data * pll )
58
+ {
59
+ u64 rate = (u64 )parent_rate * pllt -> m ;
60
+ unsigned int od = pllt -> od + pllt -> od2 + pllt -> od3 ;
61
+
62
+ if (frac && MESON_PARM_APPLICABLE (& pll -> frac )) {
63
+ u64 frac_rate = (u64 )parent_rate * frac ;
64
+
65
+ rate += DIV_ROUND_UP_ULL (frac_rate ,
66
+ (1 << pll -> frac .width ));
67
+ }
68
+
69
+ return DIV_ROUND_UP_ULL (rate , pllt -> n << od );
70
+ }
71
+
51
72
static unsigned long meson_clk_pll_recalc_rate (struct clk_hw * hw ,
52
73
unsigned long parent_rate )
53
74
{
54
75
struct clk_regmap * clk = to_clk_regmap (hw );
55
76
struct meson_clk_pll_data * pll = meson_clk_pll_data (clk );
56
- u64 rate ;
57
- u16 n , m , frac = 0 , od , od2 = 0 , od3 = 0 ;
58
-
59
- n = meson_parm_read (clk -> map , & pll -> n );
60
- m = meson_parm_read (clk -> map , & pll -> m );
61
- od = meson_parm_read (clk -> map , & pll -> od );
62
-
63
- if (MESON_PARM_APPLICABLE (& pll -> od2 ))
64
- od2 = meson_parm_read (clk -> map , & pll -> od2 );
77
+ struct pll_rate_table pllt ;
78
+ u16 frac ;
65
79
66
- if (MESON_PARM_APPLICABLE (& pll -> od3 ))
67
- od3 = meson_parm_read (clk -> map , & pll -> od3 );
80
+ pllt .n = meson_parm_read (clk -> map , & pll -> n );
81
+ pllt .m = meson_parm_read (clk -> map , & pll -> m );
82
+ pllt .od = meson_parm_read (clk -> map , & pll -> od );
68
83
69
- rate = (u64 )m * parent_rate ;
84
+ pllt .od2 = MESON_PARM_APPLICABLE (& pll -> od2 ) ?
85
+ meson_parm_read (clk -> map , & pll -> od2 ) :
86
+ 0 ;
70
87
71
- if (MESON_PARM_APPLICABLE (& pll -> frac )) {
72
- frac = meson_parm_read (clk -> map , & pll -> frac );
88
+ pllt .od3 = MESON_PARM_APPLICABLE (& pll -> od3 ) ?
89
+ meson_parm_read (clk -> map , & pll -> od3 ) :
90
+ 0 ;
73
91
74
- rate += mul_u64_u32_shr (parent_rate , frac , pll -> frac .width );
75
- }
92
+ frac = MESON_PARM_APPLICABLE (& pll -> frac ) ?
93
+ meson_parm_read (clk -> map , & pll -> frac ) :
94
+ 0 ;
76
95
77
- return div_u64 ( rate , n ) >> od >> od2 >> od3 ;
96
+ return __pll_params_to_rate ( parent_rate , & pllt , frac , pll ) ;
78
97
}
79
98
80
- static long meson_clk_pll_round_rate (struct clk_hw * hw , unsigned long rate ,
81
- unsigned long * parent_rate )
99
+ static u16 __pll_params_with_frac (unsigned long rate ,
100
+ unsigned long parent_rate ,
101
+ const struct pll_rate_table * pllt ,
102
+ struct meson_clk_pll_data * pll )
82
103
{
83
- struct clk_regmap * clk = to_clk_regmap (hw );
84
- struct meson_clk_pll_data * pll = meson_clk_pll_data (clk );
85
- const struct pll_rate_table * pllt ;
86
-
87
- /*
88
- * if the table is missing, just return the current rate
89
- * since we don't have the other available frequencies
90
- */
91
- if (!pll -> table )
92
- return meson_clk_pll_recalc_rate (hw , * parent_rate );
104
+ u16 frac_max = (1 << pll -> frac .width );
105
+ u64 val = (u64 )rate * pllt -> n ;
93
106
94
- for (pllt = pll -> table ; pllt -> rate ; pllt ++ ) {
95
- if (rate <= pllt -> rate )
96
- return pllt -> rate ;
97
- }
107
+ val <<= pllt -> od + pllt -> od2 + pllt -> od3 ;
108
+ val = div_u64 (val * frac_max , parent_rate );
109
+ val -= pllt -> m * frac_max ;
98
110
99
- /* else return the smallest value */
100
- return pll -> table [0 ].rate ;
111
+ return min ((u16 )val , (u16 )(frac_max - 1 ));
101
112
}
102
113
103
114
static const struct pll_rate_table *
104
- meson_clk_get_pll_settings (const struct pll_rate_table * table ,
105
- unsigned long rate )
115
+ meson_clk_get_pll_settings (unsigned long rate ,
116
+ struct meson_clk_pll_data * pll )
106
117
{
107
- const struct pll_rate_table * pllt ;
118
+ const struct pll_rate_table * table = pll -> table ;
119
+ unsigned int i = 0 ;
108
120
109
121
if (!table )
110
122
return NULL ;
111
123
112
- for (pllt = table ; pllt -> rate ; pllt ++ ) {
113
- if (rate == pllt -> rate )
114
- return pllt ;
115
- }
124
+ /* Find the first table element exceeding rate */
125
+ while (table [i ].rate && table [i ].rate <= rate )
126
+ i ++ ;
127
+
128
+ /* Select the setting of the rounded down rate */
129
+ if (i != 0 )
130
+ i -- ;
131
+
132
+ return (struct pll_rate_table * )& table [i ];
133
+ }
134
+
135
+ static long meson_clk_pll_round_rate (struct clk_hw * hw , unsigned long rate ,
136
+ unsigned long * parent_rate )
137
+ {
138
+ struct clk_regmap * clk = to_clk_regmap (hw );
139
+ struct meson_clk_pll_data * pll = meson_clk_pll_data (clk );
140
+ const struct pll_rate_table * pllt =
141
+ meson_clk_get_pll_settings (rate , pll );
142
+ u16 frac ;
143
+
144
+ if (!pllt )
145
+ return meson_clk_pll_recalc_rate (hw , * parent_rate );
146
+
147
+ if (!MESON_PARM_APPLICABLE (& pll -> frac )
148
+ || rate == pllt -> rate )
149
+ return pllt -> rate ;
116
150
117
- return NULL ;
151
+ /*
152
+ * The rate provided by the setting is not an exact match, let's
153
+ * try to improve the result using the fractional parameter
154
+ */
155
+ frac = __pll_params_with_frac (rate , * parent_rate , pllt , pll );
156
+
157
+ return __pll_params_to_rate (* parent_rate , pllt , frac , pll );
118
158
}
119
159
120
160
static int meson_clk_pll_wait_lock (struct clk_hw * hw )
@@ -154,13 +194,14 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
154
194
struct meson_clk_pll_data * pll = meson_clk_pll_data (clk );
155
195
const struct pll_rate_table * pllt ;
156
196
unsigned long old_rate ;
197
+ u16 frac = 0 ;
157
198
158
199
if (parent_rate == 0 || rate == 0 )
159
200
return - EINVAL ;
160
201
161
202
old_rate = rate ;
162
203
163
- pllt = meson_clk_get_pll_settings (pll -> table , rate );
204
+ pllt = meson_clk_get_pll_settings (rate , pll );
164
205
if (!pllt )
165
206
return - EINVAL ;
166
207
@@ -177,8 +218,10 @@ static int meson_clk_pll_set_rate(struct clk_hw *hw, unsigned long rate,
177
218
if (MESON_PARM_APPLICABLE (& pll -> od3 ))
178
219
meson_parm_write (clk -> map , & pll -> od3 , pllt -> od3 );
179
220
180
- if (MESON_PARM_APPLICABLE (& pll -> frac ))
181
- meson_parm_write (clk -> map , & pll -> frac , pllt -> frac );
221
+ if (MESON_PARM_APPLICABLE (& pll -> frac )) {
222
+ frac = __pll_params_with_frac (rate , parent_rate , pllt , pll );
223
+ meson_parm_write (clk -> map , & pll -> frac , frac );
224
+ }
182
225
183
226
/* make sure the reset is cleared at this point */
184
227
meson_parm_write (clk -> map , & pll -> rst , 0 );
0 commit comments