aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms
diff options
context:
space:
mode:
authorGerhard Sittig <gsi@denx.de>2013-11-30 17:51:36 -0500
committerAnatolij Gustschin <agust@denx.de>2014-01-12 12:53:06 -0500
commitba2181271fc9e1ab0873299264fe7edd6e256c56 (patch)
treefb2cc03ba7224de398589c3919e5f4d4e69e5f0b /arch/powerpc/platforms
parent7b19f3bcefd0eba677e5cb3e5c88de38cd5c95c8 (diff)
powerpc/mpc512x: improve DIU related clock setup
adapt the DIU clock initialization to the COMMON_CLK approach: device tree based clock lookup, prepare and unprepare for clocks, work with frequencies not dividers, call the appropriate clk_*() routines and don't access CCM registers the "best clock" determination now completely relies on the platform's clock driver to pick a frequency close to what the caller requests, and merely checks whether the desired frequency was met (fits the tolerance of the monitor) this approach shall succeed upon first try in the usual case, will test a few less desirable yet acceptable frequencies in edge cases, and will fallback to "best effort" if none of the previously tried frequencies pass the test provide a fallback clock lookup approach in case the OF based clock lookup for the DIU fails, this allows for successful operation in the presence of an outdated device tree which lacks clock specs Cc: Anatolij Gustschin <agust@denx.de> Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Gerhard Sittig <gsi@denx.de> Signed-off-by: Anatolij Gustschin <agust@denx.de>
Diffstat (limited to 'arch/powerpc/platforms')
-rw-r--r--arch/powerpc/platforms/512x/mpc512x_shared.c169
1 files changed, 92 insertions, 77 deletions
diff --git a/arch/powerpc/platforms/512x/mpc512x_shared.c b/arch/powerpc/platforms/512x/mpc512x_shared.c
index 36b5652aada2..adb95f03d4d4 100644
--- a/arch/powerpc/platforms/512x/mpc512x_shared.c
+++ b/arch/powerpc/platforms/512x/mpc512x_shared.c
@@ -12,6 +12,7 @@
12 * (at your option) any later version. 12 * (at your option) any later version.
13 */ 13 */
14 14
15#include <linux/clk.h>
15#include <linux/kernel.h> 16#include <linux/kernel.h>
16#include <linux/io.h> 17#include <linux/io.h>
17#include <linux/irq.h> 18#include <linux/irq.h>
@@ -68,98 +69,112 @@ struct fsl_diu_shared_fb {
68 bool in_use; 69 bool in_use;
69}; 70};
70 71
71#define DIU_DIV_MASK 0x000000ff 72/* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */
72static void mpc512x_set_pixel_clock(unsigned int pixclock) 73static void mpc512x_set_pixel_clock(unsigned int pixclock)
73{ 74{
74 unsigned long bestval, bestfreq, speed, busfreq;
75 unsigned long minpixclock, maxpixclock, pixval;
76 struct mpc512x_ccm __iomem *ccm;
77 struct device_node *np; 75 struct device_node *np;
78 u32 temp; 76 struct clk *clk_diu;
79 long err; 77 unsigned long epsilon, minpixclock, maxpixclock;
80 int i; 78 unsigned long offset, want, got, delta;
81 79
82 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); 80 /* lookup and enable the DIU clock */
81 np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu");
83 if (!np) { 82 if (!np) {
84 pr_err("Can't find clock control module.\n"); 83 pr_err("Could not find DIU device tree node.\n");
85 return; 84 return;
86 } 85 }
87 86 clk_diu = of_clk_get(np, 0);
88 ccm = of_iomap(np, 0); 87 if (IS_ERR(clk_diu)) {
88 /* backwards compat with device trees that lack clock specs */
89 clk_diu = clk_get_sys(np->name, "ipg");
90 }
89 of_node_put(np); 91 of_node_put(np);
90 if (!ccm) { 92 if (IS_ERR(clk_diu)) {
91 pr_err("Can't map clock control module reg.\n"); 93 pr_err("Could not lookup DIU clock.\n");
92 return; 94 return;
93 } 95 }
94 96 if (clk_prepare_enable(clk_diu)) {
95 np = of_find_node_by_type(NULL, "cpu"); 97 pr_err("Could not enable DIU clock.\n");
96 if (np) {
97 const unsigned int *prop =
98 of_get_property(np, "bus-frequency", NULL);
99
100 of_node_put(np);
101 if (prop) {
102 busfreq = *prop;
103 } else {
104 pr_err("Can't get bus-frequency property\n");
105 return;
106 }
107 } else {
108 pr_err("Can't find 'cpu' node.\n");
109 return; 98 return;
110 } 99 }
111 100
112 /* Pixel Clock configuration */ 101 /*
113 pr_debug("DIU: Bus Frequency = %lu\n", busfreq); 102 * convert the picoseconds spec into the desired clock rate,
114 speed = busfreq * 4; /* DIU_DIV ratio is 4 * CSB_CLK / DIU_CLK */ 103 * determine the acceptable clock range for the monitor (+/- 5%),
115 104 * do the calculation in steps to avoid integer overflow
116 /* Calculate the pixel clock with the smallest error */ 105 */
117 /* calculate the following in steps to avoid overflow */ 106 pr_debug("DIU pixclock in ps - %u\n", pixclock);
118 pr_debug("DIU pixclock in ps - %d\n", pixclock); 107 pixclock = (1000000000 / pixclock) * 1000;
119 temp = (1000000000 / pixclock) * 1000; 108 pr_debug("DIU pixclock freq - %u\n", pixclock);
120 pixclock = temp; 109 epsilon = pixclock / 20; /* pixclock * 0.05 */
121 pr_debug("DIU pixclock freq - %u\n", pixclock); 110 pr_debug("DIU deviation - %lu\n", epsilon);
122 111 minpixclock = pixclock - epsilon;
123 temp = temp / 20; /* pixclock * 0.05 */ 112 maxpixclock = pixclock + epsilon;
124 pr_debug("deviation = %d\n", temp); 113 pr_debug("DIU minpixclock - %lu\n", minpixclock);
125 minpixclock = pixclock - temp; 114 pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
126 maxpixclock = pixclock + temp; 115
127 pr_debug("DIU minpixclock - %lu\n", minpixclock); 116 /*
128 pr_debug("DIU maxpixclock - %lu\n", maxpixclock); 117 * check whether the DIU supports the desired pixel clock
129 pixval = speed/pixclock; 118 *
130 pr_debug("DIU pixval = %lu\n", pixval); 119 * - simply request the desired clock and see what the
131 120 * platform's clock driver will make of it, assuming that it
132 err = LONG_MAX; 121 * will setup the best approximation of the requested value
133 bestval = pixval; 122 * - try other candidate frequencies in the order of decreasing
134 pr_debug("DIU bestval = %lu\n", bestval); 123 * preference (i.e. with increasing distance from the desired
135 124 * pixel clock, and checking the lower frequency before the
136 bestfreq = 0; 125 * higher frequency to not overload the hardware) until the
137 for (i = -1; i <= 1; i++) { 126 * first match is found -- any potential subsequent match
138 temp = speed / (pixval+i); 127 * would only be as good as the former match or typically
139 pr_debug("DIU test pixval i=%d, pixval=%lu, temp freq. = %u\n", 128 * would be less preferrable
140 i, pixval, temp); 129 *
141 if ((temp < minpixclock) || (temp > maxpixclock)) 130 * the offset increment of pixelclock divided by 64 is an
142 pr_debug("DIU exceeds monitor range (%lu to %lu)\n", 131 * arbitrary choice -- it's simple to calculate, in the typical
143 minpixclock, maxpixclock); 132 * case we expect the first check to succeed already, in the
144 else if (abs(temp - pixclock) < err) { 133 * worst case seven frequencies get tested (the exact center and
145 pr_debug("Entered the else if block %d\n", i); 134 * three more values each to the left and to the right) before
146 err = abs(temp - pixclock); 135 * the 5% tolerance window is exceeded, resulting in fast enough
147 bestval = pixval + i; 136 * execution yet high enough probability of finding a suitable
148 bestfreq = temp; 137 * value, while the error rate will be in the order of single
149 } 138 * percents
139 */
140 for (offset = 0; offset <= epsilon; offset += pixclock / 64) {
141 want = pixclock - offset;
142 pr_debug("DIU checking clock - %lu\n", want);
143 clk_set_rate(clk_diu, want);
144 got = clk_get_rate(clk_diu);
145 delta = abs(pixclock - got);
146 if (delta < epsilon)
147 break;
148 if (!offset)
149 continue;
150 want = pixclock + offset;
151 pr_debug("DIU checking clock - %lu\n", want);
152 clk_set_rate(clk_diu, want);
153 got = clk_get_rate(clk_diu);
154 delta = abs(pixclock - got);
155 if (delta < epsilon)
156 break;
150 } 157 }
158 if (offset <= epsilon) {
159 pr_debug("DIU clock accepted - %lu\n", want);
160 pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
161 pixclock, got, delta, epsilon);
162 return;
163 }
164 pr_warn("DIU pixclock auto search unsuccessful\n");
151 165
152 pr_debug("DIU chose = %lx\n", bestval); 166 /*
153 pr_debug("DIU error = %ld\n NomPixClk ", err); 167 * what is the most appropriate action to take when the search
154 pr_debug("DIU: Best Freq = %lx\n", bestfreq); 168 * for an available pixel clock which is acceptable to the
155 /* Modify DIU_DIV in CCM SCFR1 */ 169 * monitor has failed? disable the DIU (clock) or just provide
156 temp = in_be32(&ccm->scfr1); 170 * a "best effort"? we go with the latter
157 pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp); 171 */
158 temp &= ~DIU_DIV_MASK; 172 pr_warn("DIU pixclock best effort fallback (backend's choice)\n");
159 temp |= (bestval & DIU_DIV_MASK); 173 clk_set_rate(clk_diu, pixclock);
160 out_be32(&ccm->scfr1, temp); 174 got = clk_get_rate(clk_diu);
161 pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp); 175 delta = abs(pixclock - got);
162 iounmap(ccm); 176 pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n",
177 pixclock, got, delta, epsilon);
163} 178}
164 179
165static enum fsl_diu_monitor_port 180static enum fsl_diu_monitor_port