aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorTimur Tabi <timur@freescale.com>2011-06-23 02:16:48 -0400
committerKumar Gala <galak@kernel.crashing.org>2011-06-27 09:36:17 -0400
commitc8bfa77b56fecbc2734f67d5265dbd216413501d (patch)
treead403f0c594f854c2cf714e8f6424d98b24563e6 /arch
parent3846e332a9981b019849566bdbf78ba859856c67 (diff)
powerpc/86xx: improve calculation of DIU pixel clock on the MPC8610 HPCD
mpc8610hpcd_set_pixel_clock() calculates the correct value of the PXCLK bits in the CLKDVDR register for a given pixel clock rate. The code which performs this calculation is overly complicated and includes an error estimation routine that doesn't work most of the time anyway. Replace the code with the simpler routine that's currently used on the P1022DS. Signed-off-by: Timur Tabi <timur@freescale.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/platforms/86xx/mpc8610_hpcd.c107
1 files changed, 48 insertions, 59 deletions
diff --git a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
index a896511690c..74e018ef724 100644
--- a/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
+++ b/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
@@ -39,12 +39,19 @@
39#include <sysdev/fsl_pci.h> 39#include <sysdev/fsl_pci.h>
40#include <sysdev/fsl_soc.h> 40#include <sysdev/fsl_soc.h>
41#include <sysdev/simple_gpio.h> 41#include <sysdev/simple_gpio.h>
42#include <asm/fsl_guts.h>
42 43
43#include "mpc86xx.h" 44#include "mpc86xx.h"
44 45
45static struct device_node *pixis_node; 46static struct device_node *pixis_node;
46static unsigned char *pixis_bdcfg0, *pixis_arch; 47static unsigned char *pixis_bdcfg0, *pixis_arch;
47 48
49/* DIU Pixel Clock bits of the CLKDVDR Global Utilities register */
50#define CLKDVDR_PXCKEN 0x80000000
51#define CLKDVDR_PXCKINV 0x10000000
52#define CLKDVDR_PXCKDLY 0x06000000
53#define CLKDVDR_PXCLK_MASK 0x001F0000
54
48#ifdef CONFIG_SUSPEND 55#ifdef CONFIG_SUSPEND
49static irqreturn_t mpc8610_sw9_irq(int irq, void *data) 56static irqreturn_t mpc8610_sw9_irq(int irq, void *data)
50{ 57{
@@ -205,72 +212,54 @@ void mpc8610hpcd_set_monitor_port(int monitor_port)
205 bdcfg[monitor_port]); 212 bdcfg[monitor_port]);
206} 213}
207 214
215/**
216 * mpc8610hpcd_set_pixel_clock: program the DIU's clock
217 *
218 * @pixclock: the wavelength, in picoseconds, of the clock
219 */
208void mpc8610hpcd_set_pixel_clock(unsigned int pixclock) 220void mpc8610hpcd_set_pixel_clock(unsigned int pixclock)
209{ 221{
210 u32 __iomem *clkdvdr; 222 struct device_node *guts_np = NULL;
211 u32 temp; 223 struct ccsr_guts_86xx __iomem *guts;
212 /* variables for pixel clock calcs */ 224 unsigned long freq;
213 ulong bestval, bestfreq, speed_ccb, minpixclock, maxpixclock; 225 u64 temp;
214 ulong pixval; 226 u32 pxclk;
215 long err; 227
216 int i; 228 /* Map the global utilities registers. */
217 229 guts_np = of_find_compatible_node(NULL, NULL, "fsl,mpc8610-guts");
218 clkdvdr = ioremap(get_immrbase() + 0xe0800, sizeof(u32)); 230 if (!guts_np) {
219 if (!clkdvdr) { 231 pr_err("mpc8610hpcd: missing global utilties device node\n");
220 printk(KERN_ERR "Err: can't map clock divider register!\n");
221 return; 232 return;
222 } 233 }
223 234
224 /* Pixel Clock configuration */ 235 guts = of_iomap(guts_np, 0);
225 speed_ccb = fsl_get_sys_freq(); 236 of_node_put(guts_np);
226 237 if (!guts) {
227 /* Calculate the pixel clock with the smallest error */ 238 pr_err("mpc8610hpcd: could not map global utilties device\n");
228 /* calculate the following in steps to avoid overflow */ 239 return;
229 pr_debug("DIU pixclock in ps - %d\n", pixclock);
230 temp = 1000000000/pixclock;
231 temp *= 1000;
232 pixclock = temp;
233 pr_debug("DIU pixclock freq - %u\n", pixclock);
234
235 temp = pixclock * 5 / 100;
236 pr_debug("deviation = %d\n", temp);
237 minpixclock = pixclock - temp;
238 maxpixclock = pixclock + temp;
239 pr_debug("DIU minpixclock - %lu\n", minpixclock);
240 pr_debug("DIU maxpixclock - %lu\n", maxpixclock);
241 pixval = speed_ccb/pixclock;
242 pr_debug("DIU pixval = %lu\n", pixval);
243
244 err = 100000000;
245 bestval = pixval;
246 pr_debug("DIU bestval = %lu\n", bestval);
247
248 bestfreq = 0;
249 for (i = -1; i <= 1; i++) {
250 temp = speed_ccb / ((pixval+i) + 1);
251 pr_debug("DIU test pixval i= %d, pixval=%lu, temp freq. = %u\n",
252 i, pixval, temp);
253 if ((temp < minpixclock) || (temp > maxpixclock))
254 pr_debug("DIU exceeds monitor range (%lu to %lu)\n",
255 minpixclock, maxpixclock);
256 else if (abs(temp - pixclock) < err) {
257 pr_debug("Entered the else if block %d\n", i);
258 err = abs(temp - pixclock);
259 bestval = pixval+i;
260 bestfreq = temp;
261 }
262 } 240 }
263 241
264 pr_debug("DIU chose = %lx\n", bestval); 242 /* Convert pixclock from a wavelength to a frequency */
265 pr_debug("DIU error = %ld\n NomPixClk ", err); 243 temp = 1000000000000ULL;
266 pr_debug("DIU: Best Freq = %lx\n", bestfreq); 244 do_div(temp, pixclock);
267 /* Modify PXCLK in GUTS CLKDVDR */ 245 freq = temp;
268 pr_debug("DIU: Current value of CLKDVDR = 0x%08x\n", (*clkdvdr)); 246
269 temp = (*clkdvdr) & 0x2000FFFF; 247 /*
270 *clkdvdr = temp; /* turn off clock */ 248 * 'pxclk' is the ratio of the platform clock to the pixel clock.
271 *clkdvdr = temp | 0x80000000 | (((bestval) & 0x1F) << 16); 249 * On the MPC8610, the value programmed into CLKDVDR is the ratio
272 pr_debug("DIU: Modified value of CLKDVDR = 0x%08x\n", (*clkdvdr)); 250 * minus one. The valid range of values is 2-31.
273 iounmap(clkdvdr); 251 */
252 pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq) - 1;
253 pxclk = clamp_t(u32, pxclk, 2, 31);
254
255 /* Disable the pixel clock, and set it to non-inverted and no delay */
256 clrbits32(&guts->clkdvdr,
257 CLKDVDR_PXCKEN | CLKDVDR_PXCKDLY | CLKDVDR_PXCLK_MASK);
258
259 /* Enable the clock and set the pxclk */
260 setbits32(&guts->clkdvdr, CLKDVDR_PXCKEN | (pxclk << 16));
261
262 iounmap(guts);
274} 263}
275 264
276ssize_t mpc8610hpcd_show_monitor_port(int monitor_port, char *buf) 265ssize_t mpc8610hpcd_show_monitor_port(int monitor_port, char *buf)