/*
- * 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>
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)
{
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;
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) |
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);
/*
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);
/* 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
*
* 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));
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;
}
{
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. */
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();