aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc/platforms/86xx/mpc8610_hpcd.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/platforms/86xx/mpc8610_hpcd.c')
-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)