]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - board/freescale/p1022ds/diu.c
Add GPL-2.0+ SPDX-License-Identifier to source files
[karo-tx-uboot.git] / board / freescale / p1022ds / diu.c
index 12b40a01ed00f071f12c905bfc6f17973bade791..6fd4d953b6e60f6456c971ae349227d9170b0df8 100644 (file)
@@ -1,17 +1,15 @@
 /*
- * Copyright 2010 Freescale Semiconductor, Inc.
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
  * Authors: Timur Tabi <timur@freescale.com>
  *
  * FSL DIU Framebuffer driver
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <command.h>
+#include <linux/ctype.h>
 #include <asm/io.h>
 #include <stdio_dev.h>
 #include <video_fb.h>
@@ -32,6 +30,7 @@
 
 #define PMUXCR_ELBCDIU_MASK    0xc0000000
 #define PMUXCR_ELBCDIU_NOR16   0x80000000
+#define PMUXCR_ELBCDIU_DIU     0x40000000
 
 /*
  * DIU Area Descriptor
@@ -61,6 +60,8 @@ static u8 px_brdcfg0;
 static u32 pmuxcr;
 static void *lbc_lcs0_ba;
 static void *lbc_lcs1_ba;
+static u32 old_br0, old_or0, old_br1, old_or1;
+static u32 new_br0, new_or0, new_br1, new_or1;
 
 void diu_set_pixel_clock(unsigned int pixclock)
 {
@@ -72,7 +73,7 @@ void diu_set_pixel_clock(unsigned int pixclock)
        temp = 1000000000 / pixclock;
        temp *= 1000;
        pixval = speed_ccb / temp;
-       debug("DIU pixval = %lu\n", pixval);
+       debug("DIU pixval = %u\n", pixval);
 
        /* Modify PXCLK in GUTS CLKDVDR */
        temp = in_be32(&gur->clkdvdr) & 0x2000FFFF;
@@ -80,16 +81,69 @@ void diu_set_pixel_clock(unsigned int pixclock)
        out_be32(&gur->clkdvdr, temp | 0x80000000 | ((pixval & 0x1F) << 16));
 }
 
-int platform_diu_init(unsigned int *xres, unsigned int *yres)
+int platform_diu_init(unsigned int xres, unsigned int yres, const char *port)
 {
        ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
-       char *monitor_port;
+       const char *name;
        u32 pixel_format;
        u8 temp;
+       phys_addr_t phys0, phys1; /* BR0/BR1 physical addresses */
 
-       /* Save the LBC LCS0 and LCS1 addresses for the DIU mux functions */
-       lbc_lcs0_ba = (void *)(get_lbc_br(0) & get_lbc_or(0) & 0xFFFF8000);
-       lbc_lcs1_ba = (void *)(get_lbc_br(1) & get_lbc_or(1) & 0xFFFF8000);
+       /*
+        * Indirect mode requires both BR0 and BR1 to be set to "GPCM",
+        * otherwise writes to these addresses won't actually appear on the
+        * local bus, and so the PIXIS won't see them.
+        *
+        * In FCM mode, writes go to the NAND controller, which does not pass
+        * them to the localbus directly.  So we force BR0 and BR1 into GPCM
+        * mode, since we don't care about what's behind the localbus any
+        * more.  However, we save those registers first, so that we can
+        * restore them when necessary.
+        */
+       new_br0 = old_br0 = get_lbc_br(0);
+       new_br1 = old_br1 = get_lbc_br(1);
+       new_or0 = old_or0 = get_lbc_or(0);
+       new_or1 = old_or1 = get_lbc_or(1);
+
+       /*
+        * Use the existing BRx/ORx values if it's already GPCM. Otherwise,
+        * force the values to simple 32KB GPCM windows with the most
+        * conservative timing.
+        */
+       if ((old_br0 & BR_MSEL) != BR_MS_GPCM) {
+               new_br0 = (get_lbc_br(0) & BR_BA) | BR_V;
+               new_or0 = OR_AM_32KB | 0xFF7;
+               set_lbc_br(0, new_br0);
+               set_lbc_or(0, new_or0);
+       }
+       if ((old_br1 & BR_MSEL) != BR_MS_GPCM) {
+               new_br1 = (get_lbc_br(1) & BR_BA) | BR_V;
+               new_or1 = OR_AM_32KB | 0xFF7;
+               set_lbc_br(1, new_br1);
+               set_lbc_or(1, new_or1);
+       }
+
+       /*
+        * Determine the physical addresses for Chip Selects 0 and 1.  The
+        * BR0/BR1 registers contain the truncated physical addresses for the
+        * chip selects, mapped via the localbus LAW.  Since the BRx registers
+        * only contain the lower 32 bits of the address, we have to determine
+        * the upper 4 bits some other way.  The proper way is to scan the LAW
+        * table looking for a matching localbus address. Instead, we cheat.
+        * We know that the upper bits are 0 for 32-bit addressing, or 0xF for
+        * 36-bit addressing.
+        */
+#ifdef CONFIG_PHYS_64BIT
+       phys0 = 0xf00000000ULL | (old_br0 & old_or0 & BR_BA);
+       phys1 = 0xf00000000ULL | (old_br1 & old_or1 & BR_BA);
+#else
+       phys0 = old_br0 & old_or0 & BR_BA;
+       phys1 = old_br1 & old_or1 & BR_BA;
+#endif
+
+        /* Save the LBC LCS0 and LCS1 addresses for the DIU mux functions */
+       lbc_lcs0_ba = map_physmem(phys0, 1, 0);
+       lbc_lcs1_ba = map_physmem(phys1, 1, 0);
 
        pixel_format = cpu_to_le32(AD_BYTE_F | (3 << AD_ALPHA_C_SHIFT) |
                (0 << AD_BLUE_C_SHIFT) | (1 << AD_GREEN_C_SHIFT) |
@@ -99,21 +153,23 @@ int platform_diu_init(unsigned int *xres, unsigned int *yres)
 
        temp = in_8(&pixis->brdcfg1);
 
-       monitor_port = getenv("monitor");
-       if (!strncmp(monitor_port, "1", 1)) { /* 1 - Single link LVDS */
-               *xres = 1024;
-               *yres = 768;
-               /* Enable the DFP port, disable the DVI and the backlight */
-               temp &= ~(PX_BRDCFG1_DVIEN | PX_BRDCFG1_BACKLIGHT);
-               temp |= PX_BRDCFG1_DFPEN;
+       if (strncmp(port, "lvds", 4) == 0) {
+               /* Single link LVDS */
+               temp &= ~PX_BRDCFG1_DVIEN;
+               /*
+                * LVDS also needs backlight enabled, otherwise the display
+                * will be blank.
+                */
+               temp |= (PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT);
+               name = "Single-Link LVDS";
        } else {        /* DVI */
-               *xres = 1280;
-               *yres = 1024;
                /* Enable the DVI port, disable the DFP and the backlight */
                temp &= ~(PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT);
                temp |= PX_BRDCFG1_DVIEN;
+               name = "DVI";
        }
 
+       printf("DIU:   Switching to %s monitor @ %ux%u\n", name, xres, yres);
        out_8(&pixis->brdcfg1, temp);
 
        /*
@@ -130,17 +186,15 @@ int platform_diu_init(unsigned int *xres, unsigned int *yres)
        out_8(lbc_lcs0_ba, offsetof(ngpixis_t, brdcfg0));
        px_brdcfg0 = in_8(lbc_lcs1_ba);
        out_8(lbc_lcs1_ba, px_brdcfg0 | PX_BRDCFG0_ELBC_DIU);
+       in_8(lbc_lcs1_ba);
 
-       /* Setting PMUXCR to switch to DVI from ELBC */
-       clrsetbits_be32(&gur->pmuxcr,
-               PMUXCR_ELBCDIU_MASK, PMUXCR_ELBCDIU_NOR16);
+       /* Set PMUXCR to switch the muxed pins from the LBC to the DIU */
+       clrsetbits_be32(&gur->pmuxcr, PMUXCR_ELBCDIU_MASK, PMUXCR_ELBCDIU_DIU);
        pmuxcr = in_be32(&gur->pmuxcr);
 
-       return fsl_diu_init(*xres, pixel_format, 0);
+       return fsl_diu_init(xres, yres, pixel_format, 0);
 }
 
-#ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS
-
 /*
  * set_mux_to_lbc - disable the DIU so that we can read/write to elbc
  *
@@ -161,18 +215,16 @@ static int set_mux_to_lbc(void)
        ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR;
 
        /* Switch the muxes only if they're currently set to DIU mode */
-       if ((in_be32(&gur->pmuxcr) & PMUXCR_ELBCDIU_MASK) ==
+       if ((in_be32(&gur->pmuxcr) & PMUXCR_ELBCDIU_MASK) !=
            PMUXCR_ELBCDIU_NOR16) {
                /*
                 * In DIU mode, the PIXIS can only be accessed indirectly
                 * since we can't read/write the LBC directly.
                 */
-
                /* Set the board mux to LBC.  This will disable the display. */
                out_8(lbc_lcs0_ba, offsetof(ngpixis_t, brdcfg0));
-               px_brdcfg0 = in_8(lbc_lcs1_ba);
-               out_8(lbc_lcs1_ba, (px_brdcfg0 & ~(PX_BRDCFG0_ELBC_SPI_MASK
-                       | PX_BRDCFG0_ELBC_DIU)) | PX_BRDCFG0_ELBC_SPI_ELBC);
+               out_8(lbc_lcs1_ba, px_brdcfg0);
+               in_8(lbc_lcs1_ba);
 
                /* Disable indirect PIXIS mode */
                out_8(lbc_lcs0_ba, offsetof(ngpixis_t, csr));
@@ -183,6 +235,12 @@ static int set_mux_to_lbc(void)
                         PMUXCR_ELBCDIU_NOR16);
                in_be32(&gur->pmuxcr);
 
+               /* Restore the BR0 and BR1 settings */
+               set_lbc_br(0, old_br0);
+               set_lbc_or(0, old_or0);
+               set_lbc_br(1, old_br1);
+               set_lbc_or(1, old_or1);
+
                return 1;
        }
 
@@ -198,12 +256,18 @@ static void set_mux_to_diu(void)
 {
        ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR;
 
+       /* Set BR0 and BR1 to GPCM mode */
+       set_lbc_br(0, new_br0);
+       set_lbc_or(0, new_or0);
+       set_lbc_br(1, new_br1);
+       set_lbc_or(1, new_or1);
+
        /* Enable indirect PIXIS mode */
        setbits_8(&pixis->csr, PX_CTL_ALTACC);
 
        /* Set the board mux to DIU.  This will enable the display. */
        out_8(lbc_lcs0_ba, offsetof(ngpixis_t, brdcfg0));
-       out_8(lbc_lcs1_ba, px_brdcfg0);
+       out_8(lbc_lcs1_ba, px_brdcfg0 | PX_BRDCFG0_ELBC_DIU);
        in_8(lbc_lcs1_ba);
 
        /* Set the chip mux to DIU mode. */
@@ -211,13 +275,84 @@ static void set_mux_to_diu(void)
        in_be32(&gur->pmuxcr);
 }
 
+/*
+ * pixis_read - board-specific function to read from the PIXIS
+ *
+ * This function overrides the generic pixis_read() function, so that it can
+ * use PIXIS indirect mode if necessary.
+ */
+u8 pixis_read(unsigned int reg)
+{
+       ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR;
+
+       /* Use indirect mode if the mux is currently set to DIU mode */
+       if ((in_be32(&gur->pmuxcr) & PMUXCR_ELBCDIU_MASK) !=
+           PMUXCR_ELBCDIU_NOR16) {
+               out_8(lbc_lcs0_ba, reg);
+               return in_8(lbc_lcs1_ba);
+       } else {
+               void *p = (void *)PIXIS_BASE;
+
+               return in_8(p + reg);
+       }
+}
+
+/*
+ * pixis_write - board-specific function to write to the PIXIS
+ *
+ * This function overrides the generic pixis_write() function, so that it can
+ * use PIXIS indirect mode if necessary.
+ */
+void pixis_write(unsigned int reg, u8 value)
+{
+       ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC85xx_GUTS_ADDR;
+
+       /* Use indirect mode if the mux is currently set to DIU mode */
+       if ((in_be32(&gur->pmuxcr) & PMUXCR_ELBCDIU_MASK) !=
+           PMUXCR_ELBCDIU_NOR16) {
+               out_8(lbc_lcs0_ba, reg);
+               out_8(lbc_lcs1_ba, value);
+               /* Do a read-back to ensure the write completed */
+               in_8(lbc_lcs1_ba);
+       } else {
+               void *p = (void *)PIXIS_BASE;
+
+               out_8(p + reg, value);
+       }
+}
+
+void pixis_bank_reset(void)
+{
+       /*
+        * For some reason, a PIXIS bank reset does not work if the PIXIS is
+        * in indirect mode, so switch to direct mode first.
+        */
+       set_mux_to_lbc();
+
+       out_8(&pixis->vctl, 0);
+       out_8(&pixis->vctl, 1);
+
+       while (1);
+}
+
+#ifdef CONFIG_CFI_FLASH_USE_WEAK_ACCESSORS
+
 void flash_write8(u8 value, void *addr)
 {
        int sw = set_mux_to_lbc();
 
        __raw_writeb(value, addr);
-       if (sw)
+       if (sw) {
+               /*
+                * To ensure the post-write is completed to eLBC, software must
+                * perform a dummy read from one valid address from eLBC space
+                * before changing the eLBC_DIU from NOR mode to DIU mode.
+                * set_mux_to_diu() includes a sync that will ensure the
+                * __raw_readb() completes before it switches the mux.
+                */
+               __raw_readb(addr);
                set_mux_to_diu();
+       }
 }
 
 void flash_write16(u16 value, void *addr)
@@ -225,8 +360,17 @@ void flash_write16(u16 value, void *addr)
        int sw = set_mux_to_lbc();
 
        __raw_writew(value, addr);
-       if (sw)
+       if (sw) {
+               /*
+                * To ensure the post-write is completed to eLBC, software must
+                * perform a dummy read from one valid address from eLBC space
+                * before changing the eLBC_DIU from NOR mode to DIU mode.
+                * set_mux_to_diu() includes a sync that will ensure the
+                * __raw_readb() completes before it switches the mux.
+                */
+               __raw_readb(addr);
                set_mux_to_diu();
+       }
 }
 
 void flash_write32(u32 value, void *addr)
@@ -234,18 +378,47 @@ void flash_write32(u32 value, void *addr)
        int sw = set_mux_to_lbc();
 
        __raw_writel(value, addr);
-       if (sw)
+       if (sw) {
+               /*
+                * To ensure the post-write is completed to eLBC, software must
+                * perform a dummy read from one valid address from eLBC space
+                * before changing the eLBC_DIU from NOR mode to DIU mode.
+                * set_mux_to_diu() includes a sync that will ensure the
+                * __raw_readb() completes before it switches the mux.
+                */
+               __raw_readb(addr);
                set_mux_to_diu();
+       }
 }
 
 void flash_write64(u64 value, void *addr)
 {
        int sw = set_mux_to_lbc();
+       uint32_t *p = addr;
 
-       /* There is no __raw_writeq(), so do the write manually */
-       *(volatile u64 *)addr = value;
-       if (sw)
+       /*
+        * There is no __raw_writeq(), so do the write manually.  We don't trust
+        * the compiler, so we use inline assembly.
+        */
+       __asm__ __volatile__(
+               "stw%U0%X0 %2,%0;\n"
+               "stw%U1%X1 %3,%1;\n"
+               : "=m" (*p), "=m" (*(p + 1))
+               : "r" ((uint32_t) (value >> 32)), "r" ((uint32_t) (value)));
+
+       if (sw) {
+               /*
+                * To ensure the post-write is completed to eLBC, software must
+                * perform a dummy read from one valid address from eLBC space
+                * before changing the eLBC_DIU from NOR mode to DIU mode.  We
+                * read addr+4 because we just wrote to addr+4, so that's how we
+                * maintain execution order.  set_mux_to_diu() includes a sync
+                * that will ensure the __raw_readb() completes before it
+                * switches the mux.
+                */
+               __raw_readb(addr + 4);
                set_mux_to_diu();
+       }
 }
 
 u8 flash_read8(void *addr)