From af38f516e996ea8e914eb2516ac965d886fade51 Mon Sep 17 00:00:00 2001 From: Octav Zlatior Date: Wed, 27 May 2015 18:06:34 +0200 Subject: clk: sunxi: Fixes sun6i PLL1 factor calculation The fix takes two aspects into account: * proper factor calculation using prime number factorization * tweaking the factors for best stability Signed-off-by: Octav Zlatior --- drivers/clk/sunxi/clk-sunxi.c | 164 +++++++++++++++++++++++++++++++++++------- 1 file changed, 138 insertions(+), 26 deletions(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 9c0dc7438774..1f02b028db48 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -297,50 +297,162 @@ static void sun4i_get_pll1_factors(u32 *freq, u32 parent_rate, * parent_rate should always be 24MHz */ static void sun6i_a31_get_pll1_factors(u32 *freq, u32 parent_rate, - u8 *n, u8 *k, u8 *m, u8 *p) + u8 *n, u8 *k, u8 *m, u8 *p) { - const u32 parent_mhz = 24; + const u8 pll_base_count = 11; /* number of primes till 32 */ + const u8 pll_primes[11] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31}; + const u32 parent_mhz = 24; /* always 24 */ + const u8 f0[11] = {3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* 2^3 * 3^1 */ const u8 N_max = 32; /* 5 bits */ const u8 K_max = 4; /* 2 bits */ const u8 M_max = 4; /* 2 bits */ - u8 N, K, M; /* multiplicators (e.g. N = n+1) */ - u32 freq_mhz, steps_mhz, NxK; + u8 N = 1, K = 1, M = 1; /* multiplicators (e.g. N = n+1) */ + u32 freq_mhz, tmp; + u8 success; + u8 ft[11]; /* target frequency will be factorized here */ + int i; freq_mhz = *freq / 1000000; - /* We will try to get the maximum resolution */ - if (freq_mhz < parent_mhz*N_max*K_max/4) - M = 4; - else if (freq_mhz < parent_mhz*N_max*K_max/3) - M = 3; - else if (freq_mhz < parent_mhz*N_max*K_max/2) - M = 2; - else - M = 1; - - steps_mhz = parent_mhz / M; + /* minimum possible frequency is 6 */ + if (freq_mhz<6) + freq_mhz = 6; + + success = 0; + /* try to factorize; if it fails, try with next lower number */ + while (!success) { + memset(&ft, 0x00, pll_base_count); + tmp = freq_mhz; + for (i=0; i=1 && ft[0]<1) { + freq_mhz--; + continue; + } + if (ft[1]==0 && ft[0]<3) { + freq_mhz--; + continue; + } + /* of course, it might be that the number is not obtainable with the + * current registers, but that should be pretty rare and we cannot + * really tell in this phase; there is another check below */ + success = 1; + } - NxK = freq_mhz / steps_mhz; + /* get the N, K and M values based on the factors; for each prime number, + * try to get the required exponent and to adjust M, K and N to reach it */ + for (i=pll_base_count-1; i>=0; i--) { + int target = ft[i]-f0[i]; + /* required exponent from N, K and M; if it's 0, there is nothing to do + * for this prime number, we move on */ + if (target==0) + continue; + /* we try to set the divider and multiplier as to achieve the desired + * exponent; we always check if the value does not go over the maximum + * values for multipliers */ + /* if target is lower than zero, we must use the divider M */ + if (target<0) + while (target<0 && M*pll_primes[i]<=M_max) { + target++; + M *= pll_primes[i]; + } + /* otherwise, we use multipliers K and N (in this order) */ + else { + while (target>0 && K*pll_primes[i]<=K_max) { + target--; + K *= pll_primes[i]; + } + while (target>0 && N*pll_primes[i]<=N_max) { + target--; + N *= pll_primes[i]; + } + } + } - /* We try to keep K as low as possible */ - if (freq_mhz <= steps_mhz*N_max*1) - K = 1; - else if (freq_mhz <= steps_mhz*N_max*2) - K = 2; - else if (freq_mhz <= steps_mhz*N_max*3) - K = 3; - else - K = 4; + /* have we succeeded? if not, we call the function again with the next + * lower frequency */ + if (parent_mhz*N*K/M != freq_mhz) { + *freq -= 1000000; + sun6i_a31_get_pll1_factors(freq, parent_rate, n, k, m, p); + return; + } - N = NxK / K; + /* some tweaks to improve pll stability */ + /* The pll can be quite tricky with tranzitions; these tweaks are based on + * empirical observations on the behaviour of the pll */ + /* M cannot be too low */ + switch (M) { + case 1: { + u8 factor = 1; + if (N*4 <= N_max) + factor = 4; + else if (N*3 <= N_max) + factor = 2; + else if (N*2 <= N_max) + factor = 2; + M *= factor; + N *= factor; + break; + } + case 2: + if (N*2 <= N_max) { + M *= 2; + N *= 2; + } + break; + } + /* K cannot be too low either */ + switch (K) { + case 1: { + u8 factor = 1; + if (N%4 == 0) + factor = 4; + else if (N%3 == 0) + factor = 3; + else if (N%2 == 0) + factor = 2; + K *= factor; + N /= factor; + break; + } + case 2: + if (N%2 == 0) { + K *= 2; + N /= 2; + } + break; + } + /* 3 is the best value ever! */ + if (K==M && K!=3) + K = M = 3; + /* 4 is better than 2, 2 is better than 1 */ + else if (M<3 && K<3) { + M *= 2; + K *= 2; + } *freq = (parent_rate * N * K) / M; + /* finally, setting the register values based on N, K and M */ if (n != NULL) { *n = N-1; *k = K-1; *m = M-1; } + } /** -- cgit v1.2.3