Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Gerhard Sittig and committed by
Anatolij Gustschin
ba218127 7b19f3bc

+92 -77
+92 -77
arch/powerpc/platforms/512x/mpc512x_shared.c
··· 12 12 * (at your option) any later version. 13 13 */ 14 14 15 + #include <linux/clk.h> 15 16 #include <linux/kernel.h> 16 17 #include <linux/io.h> 17 18 #include <linux/irq.h> ··· 69 68 bool in_use; 70 69 }; 71 70 72 - #define DIU_DIV_MASK 0x000000ff 71 + /* receives a pixel clock spec in pico seconds, adjusts the DIU clock rate */ 73 72 static void mpc512x_set_pixel_clock(unsigned int pixclock) 74 73 { 75 - unsigned long bestval, bestfreq, speed, busfreq; 76 - unsigned long minpixclock, maxpixclock, pixval; 77 - struct mpc512x_ccm __iomem *ccm; 78 74 struct device_node *np; 79 - u32 temp; 80 - long err; 81 - int i; 75 + struct clk *clk_diu; 76 + unsigned long epsilon, minpixclock, maxpixclock; 77 + unsigned long offset, want, got, delta; 82 78 83 - np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-clock"); 79 + /* lookup and enable the DIU clock */ 80 + np = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-diu"); 84 81 if (!np) { 85 - pr_err("Can't find clock control module.\n"); 82 + pr_err("Could not find DIU device tree node.\n"); 86 83 return; 87 84 } 88 - 89 - ccm = of_iomap(np, 0); 85 + clk_diu = of_clk_get(np, 0); 86 + if (IS_ERR(clk_diu)) { 87 + /* backwards compat with device trees that lack clock specs */ 88 + clk_diu = clk_get_sys(np->name, "ipg"); 89 + } 90 90 of_node_put(np); 91 - if (!ccm) { 92 - pr_err("Can't map clock control module reg.\n"); 91 + if (IS_ERR(clk_diu)) { 92 + pr_err("Could not lookup DIU clock.\n"); 93 + return; 94 + } 95 + if (clk_prepare_enable(clk_diu)) { 96 + pr_err("Could not enable DIU clock.\n"); 93 97 return; 94 98 } 95 99 96 - np = of_find_node_by_type(NULL, "cpu"); 97 - if (np) { 98 - const unsigned int *prop = 99 - of_get_property(np, "bus-frequency", NULL); 100 + /* 101 + * convert the picoseconds spec into the desired clock rate, 102 + * determine the acceptable clock range for the monitor (+/- 5%), 103 + * do the calculation in steps to avoid integer overflow 104 + */ 105 + pr_debug("DIU pixclock in ps - %u\n", pixclock); 106 + pixclock = (1000000000 / pixclock) * 1000; 107 + pr_debug("DIU pixclock freq - %u\n", pixclock); 108 + epsilon = pixclock / 20; /* pixclock * 0.05 */ 109 + pr_debug("DIU deviation - %lu\n", epsilon); 110 + minpixclock = pixclock - epsilon; 111 + maxpixclock = pixclock + epsilon; 112 + pr_debug("DIU minpixclock - %lu\n", minpixclock); 113 + pr_debug("DIU maxpixclock - %lu\n", maxpixclock); 100 114 101 - of_node_put(np); 102 - if (prop) { 103 - busfreq = *prop; 104 - } else { 105 - pr_err("Can't get bus-frequency property\n"); 106 - return; 107 - } 108 - } else { 109 - pr_err("Can't find 'cpu' node.\n"); 115 + /* 116 + * check whether the DIU supports the desired pixel clock 117 + * 118 + * - simply request the desired clock and see what the 119 + * platform's clock driver will make of it, assuming that it 120 + * will setup the best approximation of the requested value 121 + * - try other candidate frequencies in the order of decreasing 122 + * preference (i.e. with increasing distance from the desired 123 + * pixel clock, and checking the lower frequency before the 124 + * higher frequency to not overload the hardware) until the 125 + * first match is found -- any potential subsequent match 126 + * would only be as good as the former match or typically 127 + * would be less preferrable 128 + * 129 + * the offset increment of pixelclock divided by 64 is an 130 + * arbitrary choice -- it's simple to calculate, in the typical 131 + * case we expect the first check to succeed already, in the 132 + * worst case seven frequencies get tested (the exact center and 133 + * three more values each to the left and to the right) before 134 + * the 5% tolerance window is exceeded, resulting in fast enough 135 + * execution yet high enough probability of finding a suitable 136 + * value, while the error rate will be in the order of single 137 + * percents 138 + */ 139 + for (offset = 0; offset <= epsilon; offset += pixclock / 64) { 140 + want = pixclock - offset; 141 + pr_debug("DIU checking clock - %lu\n", want); 142 + clk_set_rate(clk_diu, want); 143 + got = clk_get_rate(clk_diu); 144 + delta = abs(pixclock - got); 145 + if (delta < epsilon) 146 + break; 147 + if (!offset) 148 + continue; 149 + want = pixclock + offset; 150 + pr_debug("DIU checking clock - %lu\n", want); 151 + clk_set_rate(clk_diu, want); 152 + got = clk_get_rate(clk_diu); 153 + delta = abs(pixclock - got); 154 + if (delta < epsilon) 155 + break; 156 + } 157 + if (offset <= epsilon) { 158 + pr_debug("DIU clock accepted - %lu\n", want); 159 + pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 160 + pixclock, got, delta, epsilon); 110 161 return; 111 162 } 163 + pr_warn("DIU pixclock auto search unsuccessful\n"); 112 164 113 - /* Pixel Clock configuration */ 114 - pr_debug("DIU: Bus Frequency = %lu\n", busfreq); 115 - speed = busfreq * 4; /* DIU_DIV ratio is 4 * CSB_CLK / DIU_CLK */ 116 - 117 - /* Calculate the pixel clock with the smallest error */ 118 - /* calculate the following in steps to avoid overflow */ 119 - pr_debug("DIU pixclock in ps - %d\n", pixclock); 120 - temp = (1000000000 / pixclock) * 1000; 121 - pixclock = temp; 122 - pr_debug("DIU pixclock freq - %u\n", pixclock); 123 - 124 - temp = temp / 20; /* pixclock * 0.05 */ 125 - pr_debug("deviation = %d\n", temp); 126 - minpixclock = pixclock - temp; 127 - maxpixclock = pixclock + temp; 128 - pr_debug("DIU minpixclock - %lu\n", minpixclock); 129 - pr_debug("DIU maxpixclock - %lu\n", maxpixclock); 130 - pixval = speed/pixclock; 131 - pr_debug("DIU pixval = %lu\n", pixval); 132 - 133 - err = LONG_MAX; 134 - bestval = pixval; 135 - pr_debug("DIU bestval = %lu\n", bestval); 136 - 137 - bestfreq = 0; 138 - for (i = -1; i <= 1; i++) { 139 - temp = speed / (pixval+i); 140 - pr_debug("DIU test pixval i=%d, pixval=%lu, temp freq. = %u\n", 141 - i, pixval, temp); 142 - if ((temp < minpixclock) || (temp > maxpixclock)) 143 - pr_debug("DIU exceeds monitor range (%lu to %lu)\n", 144 - minpixclock, maxpixclock); 145 - else if (abs(temp - pixclock) < err) { 146 - pr_debug("Entered the else if block %d\n", i); 147 - err = abs(temp - pixclock); 148 - bestval = pixval + i; 149 - bestfreq = temp; 150 - } 151 - } 152 - 153 - pr_debug("DIU chose = %lx\n", bestval); 154 - pr_debug("DIU error = %ld\n NomPixClk ", err); 155 - pr_debug("DIU: Best Freq = %lx\n", bestfreq); 156 - /* Modify DIU_DIV in CCM SCFR1 */ 157 - temp = in_be32(&ccm->scfr1); 158 - pr_debug("DIU: Current value of SCFR1: 0x%08x\n", temp); 159 - temp &= ~DIU_DIV_MASK; 160 - temp |= (bestval & DIU_DIV_MASK); 161 - out_be32(&ccm->scfr1, temp); 162 - pr_debug("DIU: Modified value of SCFR1: 0x%08x\n", temp); 163 - iounmap(ccm); 165 + /* 166 + * what is the most appropriate action to take when the search 167 + * for an available pixel clock which is acceptable to the 168 + * monitor has failed? disable the DIU (clock) or just provide 169 + * a "best effort"? we go with the latter 170 + */ 171 + pr_warn("DIU pixclock best effort fallback (backend's choice)\n"); 172 + clk_set_rate(clk_diu, pixclock); 173 + got = clk_get_rate(clk_diu); 174 + delta = abs(pixclock - got); 175 + pr_debug("DIU pixclock want %u, got %lu, delta %lu, eps %lu\n", 176 + pixclock, got, delta, epsilon); 164 177 } 165 178 166 179 static enum fsl_diu_monitor_port