162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * P1022DS board specific routines
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Authors: Travis Wheatley <travis.wheatley@freescale.com>
562306a36Sopenharmony_ci *          Dave Liu <daveliu@freescale.com>
662306a36Sopenharmony_ci *          Timur Tabi <timur@freescale.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright 2010 Freescale Semiconductor, Inc.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This file is taken from the Freescale P1022DS BSP, with modifications:
1162306a36Sopenharmony_ci * 2) No AMP support
1262306a36Sopenharmony_ci * 3) No PCI endpoint support
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public License
1562306a36Sopenharmony_ci * version 2.  This program is licensed "as is" without any warranty of any
1662306a36Sopenharmony_ci * kind, whether express or implied.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/fsl/guts.h>
2062306a36Sopenharmony_ci#include <linux/pci.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/of_address.h>
2362306a36Sopenharmony_ci#include <asm/div64.h>
2462306a36Sopenharmony_ci#include <asm/mpic.h>
2562306a36Sopenharmony_ci#include <asm/swiotlb.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#include <sysdev/fsl_soc.h>
2862306a36Sopenharmony_ci#include <sysdev/fsl_pci.h>
2962306a36Sopenharmony_ci#include <asm/udbg.h>
3062306a36Sopenharmony_ci#include <asm/fsl_lbc.h>
3162306a36Sopenharmony_ci#include "smp.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#include "mpc85xx.h"
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#define PMUXCR_ELBCDIU_MASK	0xc0000000
3862306a36Sopenharmony_ci#define PMUXCR_ELBCDIU_NOR16	0x80000000
3962306a36Sopenharmony_ci#define PMUXCR_ELBCDIU_DIU	0x40000000
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * Board-specific initialization of the DIU.  This code should probably be
4362306a36Sopenharmony_ci * executed when the DIU is opened, rather than in arch code, but the DIU
4462306a36Sopenharmony_ci * driver does not have a mechanism for this (yet).
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * This is especially problematic on the P1022DS because the local bus (eLBC)
4762306a36Sopenharmony_ci * and the DIU video signals share the same pins, which means that enabling the
4862306a36Sopenharmony_ci * DIU will disable access to NOR flash.
4962306a36Sopenharmony_ci */
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci/* DIU Pixel Clock bits of the CLKDVDR Global Utilities register */
5262306a36Sopenharmony_ci#define CLKDVDR_PXCKEN		0x80000000
5362306a36Sopenharmony_ci#define CLKDVDR_PXCKINV		0x10000000
5462306a36Sopenharmony_ci#define CLKDVDR_PXCKDLY		0x06000000
5562306a36Sopenharmony_ci#define CLKDVDR_PXCLK_MASK	0x00FF0000
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* Some ngPIXIS register definitions */
5862306a36Sopenharmony_ci#define PX_CTL		3
5962306a36Sopenharmony_ci#define PX_BRDCFG0	8
6062306a36Sopenharmony_ci#define PX_BRDCFG1	9
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci#define PX_BRDCFG0_ELBC_SPI_MASK	0xc0
6362306a36Sopenharmony_ci#define PX_BRDCFG0_ELBC_SPI_ELBC	0x00
6462306a36Sopenharmony_ci#define PX_BRDCFG0_ELBC_SPI_NULL	0xc0
6562306a36Sopenharmony_ci#define PX_BRDCFG0_ELBC_DIU		0x02
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci#define PX_BRDCFG1_DVIEN	0x80
6862306a36Sopenharmony_ci#define PX_BRDCFG1_DFPEN	0x40
6962306a36Sopenharmony_ci#define PX_BRDCFG1_BACKLIGHT	0x20
7062306a36Sopenharmony_ci#define PX_BRDCFG1_DDCEN	0x10
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci#define PX_CTL_ALTACC		0x80
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci/*
7562306a36Sopenharmony_ci * DIU Area Descriptor
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Note that we need to byte-swap the value before it's written to the AD
7862306a36Sopenharmony_ci * register.  So even though the registers don't look like they're in the same
7962306a36Sopenharmony_ci * bit positions as they are on the MPC8610, the same value is written to the
8062306a36Sopenharmony_ci * AD register on the MPC8610 and on the P1022.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_ci#define AD_BYTE_F		0x10000000
8362306a36Sopenharmony_ci#define AD_ALPHA_C_MASK		0x0E000000
8462306a36Sopenharmony_ci#define AD_ALPHA_C_SHIFT	25
8562306a36Sopenharmony_ci#define AD_BLUE_C_MASK		0x01800000
8662306a36Sopenharmony_ci#define AD_BLUE_C_SHIFT		23
8762306a36Sopenharmony_ci#define AD_GREEN_C_MASK		0x00600000
8862306a36Sopenharmony_ci#define AD_GREEN_C_SHIFT	21
8962306a36Sopenharmony_ci#define AD_RED_C_MASK		0x00180000
9062306a36Sopenharmony_ci#define AD_RED_C_SHIFT		19
9162306a36Sopenharmony_ci#define AD_PALETTE		0x00040000
9262306a36Sopenharmony_ci#define AD_PIXEL_S_MASK		0x00030000
9362306a36Sopenharmony_ci#define AD_PIXEL_S_SHIFT	16
9462306a36Sopenharmony_ci#define AD_COMP_3_MASK		0x0000F000
9562306a36Sopenharmony_ci#define AD_COMP_3_SHIFT		12
9662306a36Sopenharmony_ci#define AD_COMP_2_MASK		0x00000F00
9762306a36Sopenharmony_ci#define AD_COMP_2_SHIFT		8
9862306a36Sopenharmony_ci#define AD_COMP_1_MASK		0x000000F0
9962306a36Sopenharmony_ci#define AD_COMP_1_SHIFT		4
10062306a36Sopenharmony_ci#define AD_COMP_0_MASK		0x0000000F
10162306a36Sopenharmony_ci#define AD_COMP_0_SHIFT		0
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci#define MAKE_AD(alpha, red, blue, green, size, c0, c1, c2, c3) \
10462306a36Sopenharmony_ci	cpu_to_le32(AD_BYTE_F | (alpha << AD_ALPHA_C_SHIFT) | \
10562306a36Sopenharmony_ci	(blue << AD_BLUE_C_SHIFT) | (green << AD_GREEN_C_SHIFT) | \
10662306a36Sopenharmony_ci	(red << AD_RED_C_SHIFT) | (c3 << AD_COMP_3_SHIFT) | \
10762306a36Sopenharmony_ci	(c2 << AD_COMP_2_SHIFT) | (c1 << AD_COMP_1_SHIFT) | \
10862306a36Sopenharmony_ci	(c0 << AD_COMP_0_SHIFT) | (size << AD_PIXEL_S_SHIFT))
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistruct fsl_law {
11162306a36Sopenharmony_ci	u32	lawbar;
11262306a36Sopenharmony_ci	u32	reserved1;
11362306a36Sopenharmony_ci	u32	lawar;
11462306a36Sopenharmony_ci	u32	reserved[5];
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci#define LAWBAR_MASK	0x00F00000
11862306a36Sopenharmony_ci#define LAWBAR_SHIFT	12
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define LAWAR_EN	0x80000000
12162306a36Sopenharmony_ci#define LAWAR_TGT_MASK	0x01F00000
12262306a36Sopenharmony_ci#define LAW_TRGT_IF_LBC	(0x04 << 20)
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci#define LAWAR_MASK	(LAWAR_EN | LAWAR_TGT_MASK)
12562306a36Sopenharmony_ci#define LAWAR_MATCH	(LAWAR_EN | LAW_TRGT_IF_LBC)
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci#define BR_BA		0xFFFF8000
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/*
13062306a36Sopenharmony_ci * Map a BRx value to a physical address
13162306a36Sopenharmony_ci *
13262306a36Sopenharmony_ci * The localbus BRx registers only store the lower 32 bits of the address.  To
13362306a36Sopenharmony_ci * obtain the upper four bits, we need to scan the LAW table.  The entry which
13462306a36Sopenharmony_ci * maps to the localbus will contain the upper four bits.
13562306a36Sopenharmony_ci */
13662306a36Sopenharmony_cistatic phys_addr_t lbc_br_to_phys(const void *ecm, unsigned int count, u32 br)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci#ifndef CONFIG_PHYS_64BIT
13962306a36Sopenharmony_ci	/*
14062306a36Sopenharmony_ci	 * If we only have 32-bit addressing, then the BRx address *is* the
14162306a36Sopenharmony_ci	 * physical address.
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	return br & BR_BA;
14462306a36Sopenharmony_ci#else
14562306a36Sopenharmony_ci	const struct fsl_law *law = ecm + 0xc08;
14662306a36Sopenharmony_ci	unsigned int i;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
14962306a36Sopenharmony_ci		u64 lawbar = in_be32(&law[i].lawbar);
15062306a36Sopenharmony_ci		u32 lawar = in_be32(&law[i].lawar);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci		if ((lawar & LAWAR_MASK) == LAWAR_MATCH)
15362306a36Sopenharmony_ci			/* Extract the upper four bits */
15462306a36Sopenharmony_ci			return (br & BR_BA) | ((lawbar & LAWBAR_MASK) << 12);
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	return 0;
15862306a36Sopenharmony_ci#endif
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/**
16262306a36Sopenharmony_ci * p1022ds_set_monitor_port: switch the output to a different monitor port
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_cistatic void p1022ds_set_monitor_port(enum fsl_diu_monitor_port port)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct device_node *guts_node;
16762306a36Sopenharmony_ci	struct device_node *lbc_node = NULL;
16862306a36Sopenharmony_ci	struct device_node *law_node = NULL;
16962306a36Sopenharmony_ci	struct ccsr_guts __iomem *guts;
17062306a36Sopenharmony_ci	struct fsl_lbc_regs *lbc = NULL;
17162306a36Sopenharmony_ci	void *ecm = NULL;
17262306a36Sopenharmony_ci	u8 __iomem *lbc_lcs0_ba = NULL;
17362306a36Sopenharmony_ci	u8 __iomem *lbc_lcs1_ba = NULL;
17462306a36Sopenharmony_ci	phys_addr_t cs0_addr, cs1_addr;
17562306a36Sopenharmony_ci	u32 br0, or0, br1, or1;
17662306a36Sopenharmony_ci	const __be32 *iprop;
17762306a36Sopenharmony_ci	unsigned int num_laws;
17862306a36Sopenharmony_ci	u8 b;
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* Map the global utilities registers. */
18162306a36Sopenharmony_ci	guts_node = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts");
18262306a36Sopenharmony_ci	if (!guts_node) {
18362306a36Sopenharmony_ci		pr_err("p1022ds: missing global utilities device node\n");
18462306a36Sopenharmony_ci		return;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	guts = of_iomap(guts_node, 0);
18862306a36Sopenharmony_ci	if (!guts) {
18962306a36Sopenharmony_ci		pr_err("p1022ds: could not map global utilities device\n");
19062306a36Sopenharmony_ci		goto exit;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	lbc_node = of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc");
19462306a36Sopenharmony_ci	if (!lbc_node) {
19562306a36Sopenharmony_ci		pr_err("p1022ds: missing localbus node\n");
19662306a36Sopenharmony_ci		goto exit;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	lbc = of_iomap(lbc_node, 0);
20062306a36Sopenharmony_ci	if (!lbc) {
20162306a36Sopenharmony_ci		pr_err("p1022ds: could not map localbus node\n");
20262306a36Sopenharmony_ci		goto exit;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	law_node = of_find_compatible_node(NULL, NULL, "fsl,ecm-law");
20662306a36Sopenharmony_ci	if (!law_node) {
20762306a36Sopenharmony_ci		pr_err("p1022ds: missing local access window node\n");
20862306a36Sopenharmony_ci		goto exit;
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	ecm = of_iomap(law_node, 0);
21262306a36Sopenharmony_ci	if (!ecm) {
21362306a36Sopenharmony_ci		pr_err("p1022ds: could not map local access window node\n");
21462306a36Sopenharmony_ci		goto exit;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	iprop = of_get_property(law_node, "fsl,num-laws", NULL);
21862306a36Sopenharmony_ci	if (!iprop) {
21962306a36Sopenharmony_ci		pr_err("p1022ds: LAW node is missing fsl,num-laws property\n");
22062306a36Sopenharmony_ci		goto exit;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	num_laws = be32_to_cpup(iprop);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/*
22562306a36Sopenharmony_ci	 * Indirect mode requires both BR0 and BR1 to be set to "GPCM",
22662306a36Sopenharmony_ci	 * otherwise writes to these addresses won't actually appear on the
22762306a36Sopenharmony_ci	 * local bus, and so the PIXIS won't see them.
22862306a36Sopenharmony_ci	 *
22962306a36Sopenharmony_ci	 * In FCM mode, writes go to the NAND controller, which does not pass
23062306a36Sopenharmony_ci	 * them to the localbus directly.  So we force BR0 and BR1 into GPCM
23162306a36Sopenharmony_ci	 * mode, since we don't care about what's behind the localbus any
23262306a36Sopenharmony_ci	 * more.
23362306a36Sopenharmony_ci	 */
23462306a36Sopenharmony_ci	br0 = in_be32(&lbc->bank[0].br);
23562306a36Sopenharmony_ci	br1 = in_be32(&lbc->bank[1].br);
23662306a36Sopenharmony_ci	or0 = in_be32(&lbc->bank[0].or);
23762306a36Sopenharmony_ci	or1 = in_be32(&lbc->bank[1].or);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	/* Make sure CS0 and CS1 are programmed */
24062306a36Sopenharmony_ci	if (!(br0 & BR_V) || !(br1 & BR_V)) {
24162306a36Sopenharmony_ci		pr_err("p1022ds: CS0 and/or CS1 is not programmed\n");
24262306a36Sopenharmony_ci		goto exit;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/*
24662306a36Sopenharmony_ci	 * Use the existing BRx/ORx values if it's already GPCM. Otherwise,
24762306a36Sopenharmony_ci	 * force the values to simple 32KB GPCM windows with the most
24862306a36Sopenharmony_ci	 * conservative timing.
24962306a36Sopenharmony_ci	 */
25062306a36Sopenharmony_ci	if ((br0 & BR_MSEL) != BR_MS_GPCM) {
25162306a36Sopenharmony_ci		br0 = (br0 & BR_BA) | BR_V;
25262306a36Sopenharmony_ci		or0 = 0xFFFF8000 | 0xFF7;
25362306a36Sopenharmony_ci		out_be32(&lbc->bank[0].br, br0);
25462306a36Sopenharmony_ci		out_be32(&lbc->bank[0].or, or0);
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	if ((br1 & BR_MSEL) != BR_MS_GPCM) {
25762306a36Sopenharmony_ci		br1 = (br1 & BR_BA) | BR_V;
25862306a36Sopenharmony_ci		or1 = 0xFFFF8000 | 0xFF7;
25962306a36Sopenharmony_ci		out_be32(&lbc->bank[1].br, br1);
26062306a36Sopenharmony_ci		out_be32(&lbc->bank[1].or, or1);
26162306a36Sopenharmony_ci	}
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	cs0_addr = lbc_br_to_phys(ecm, num_laws, br0);
26462306a36Sopenharmony_ci	if (!cs0_addr) {
26562306a36Sopenharmony_ci		pr_err("p1022ds: could not determine physical address for CS0"
26662306a36Sopenharmony_ci		       " (BR0=%08x)\n", br0);
26762306a36Sopenharmony_ci		goto exit;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci	cs1_addr = lbc_br_to_phys(ecm, num_laws, br1);
27062306a36Sopenharmony_ci	if (!cs1_addr) {
27162306a36Sopenharmony_ci		pr_err("p1022ds: could not determine physical address for CS1"
27262306a36Sopenharmony_ci		       " (BR1=%08x)\n", br1);
27362306a36Sopenharmony_ci		goto exit;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	lbc_lcs0_ba = ioremap(cs0_addr, 1);
27762306a36Sopenharmony_ci	if (!lbc_lcs0_ba) {
27862306a36Sopenharmony_ci		pr_err("p1022ds: could not ioremap CS0 address %llx\n",
27962306a36Sopenharmony_ci		       (unsigned long long)cs0_addr);
28062306a36Sopenharmony_ci		goto exit;
28162306a36Sopenharmony_ci	}
28262306a36Sopenharmony_ci	lbc_lcs1_ba = ioremap(cs1_addr, 1);
28362306a36Sopenharmony_ci	if (!lbc_lcs1_ba) {
28462306a36Sopenharmony_ci		pr_err("p1022ds: could not ioremap CS1 address %llx\n",
28562306a36Sopenharmony_ci		       (unsigned long long)cs1_addr);
28662306a36Sopenharmony_ci		goto exit;
28762306a36Sopenharmony_ci	}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Make sure we're in indirect mode first. */
29062306a36Sopenharmony_ci	if ((in_be32(&guts->pmuxcr) & PMUXCR_ELBCDIU_MASK) !=
29162306a36Sopenharmony_ci	    PMUXCR_ELBCDIU_DIU) {
29262306a36Sopenharmony_ci		struct device_node *pixis_node;
29362306a36Sopenharmony_ci		void __iomem *pixis;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		pixis_node =
29662306a36Sopenharmony_ci			of_find_compatible_node(NULL, NULL, "fsl,p1022ds-fpga");
29762306a36Sopenharmony_ci		if (!pixis_node) {
29862306a36Sopenharmony_ci			pr_err("p1022ds: missing pixis node\n");
29962306a36Sopenharmony_ci			goto exit;
30062306a36Sopenharmony_ci		}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci		pixis = of_iomap(pixis_node, 0);
30362306a36Sopenharmony_ci		of_node_put(pixis_node);
30462306a36Sopenharmony_ci		if (!pixis) {
30562306a36Sopenharmony_ci			pr_err("p1022ds: could not map pixis registers\n");
30662306a36Sopenharmony_ci			goto exit;
30762306a36Sopenharmony_ci		}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci		/* Enable indirect PIXIS mode.  */
31062306a36Sopenharmony_ci		setbits8(pixis + PX_CTL, PX_CTL_ALTACC);
31162306a36Sopenharmony_ci		iounmap(pixis);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		/* Switch the board mux to the DIU */
31462306a36Sopenharmony_ci		out_8(lbc_lcs0_ba, PX_BRDCFG0);	/* BRDCFG0 */
31562306a36Sopenharmony_ci		b = in_8(lbc_lcs1_ba);
31662306a36Sopenharmony_ci		b |= PX_BRDCFG0_ELBC_DIU;
31762306a36Sopenharmony_ci		out_8(lbc_lcs1_ba, b);
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci		/* Set the chip mux to DIU mode. */
32062306a36Sopenharmony_ci		clrsetbits_be32(&guts->pmuxcr, PMUXCR_ELBCDIU_MASK,
32162306a36Sopenharmony_ci				PMUXCR_ELBCDIU_DIU);
32262306a36Sopenharmony_ci		in_be32(&guts->pmuxcr);
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	switch (port) {
32762306a36Sopenharmony_ci	case FSL_DIU_PORT_DVI:
32862306a36Sopenharmony_ci		/* Enable the DVI port, disable the DFP and the backlight */
32962306a36Sopenharmony_ci		out_8(lbc_lcs0_ba, PX_BRDCFG1);
33062306a36Sopenharmony_ci		b = in_8(lbc_lcs1_ba);
33162306a36Sopenharmony_ci		b &= ~(PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT);
33262306a36Sopenharmony_ci		b |= PX_BRDCFG1_DVIEN;
33362306a36Sopenharmony_ci		out_8(lbc_lcs1_ba, b);
33462306a36Sopenharmony_ci		break;
33562306a36Sopenharmony_ci	case FSL_DIU_PORT_LVDS:
33662306a36Sopenharmony_ci		/*
33762306a36Sopenharmony_ci		 * LVDS also needs backlight enabled, otherwise the display
33862306a36Sopenharmony_ci		 * will be blank.
33962306a36Sopenharmony_ci		 */
34062306a36Sopenharmony_ci		/* Enable the DFP port, disable the DVI and the backlight */
34162306a36Sopenharmony_ci		out_8(lbc_lcs0_ba, PX_BRDCFG1);
34262306a36Sopenharmony_ci		b = in_8(lbc_lcs1_ba);
34362306a36Sopenharmony_ci		b &= ~PX_BRDCFG1_DVIEN;
34462306a36Sopenharmony_ci		b |= PX_BRDCFG1_DFPEN | PX_BRDCFG1_BACKLIGHT;
34562306a36Sopenharmony_ci		out_8(lbc_lcs1_ba, b);
34662306a36Sopenharmony_ci		break;
34762306a36Sopenharmony_ci	default:
34862306a36Sopenharmony_ci		pr_err("p1022ds: unsupported monitor port %i\n", port);
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ciexit:
35262306a36Sopenharmony_ci	if (lbc_lcs1_ba)
35362306a36Sopenharmony_ci		iounmap(lbc_lcs1_ba);
35462306a36Sopenharmony_ci	if (lbc_lcs0_ba)
35562306a36Sopenharmony_ci		iounmap(lbc_lcs0_ba);
35662306a36Sopenharmony_ci	if (lbc)
35762306a36Sopenharmony_ci		iounmap(lbc);
35862306a36Sopenharmony_ci	if (ecm)
35962306a36Sopenharmony_ci		iounmap(ecm);
36062306a36Sopenharmony_ci	if (guts)
36162306a36Sopenharmony_ci		iounmap(guts);
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	of_node_put(law_node);
36462306a36Sopenharmony_ci	of_node_put(lbc_node);
36562306a36Sopenharmony_ci	of_node_put(guts_node);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/**
36962306a36Sopenharmony_ci * p1022ds_set_pixel_clock: program the DIU's clock
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * @pixclock: the wavelength, in picoseconds, of the clock
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_civoid p1022ds_set_pixel_clock(unsigned int pixclock)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct device_node *guts_np = NULL;
37662306a36Sopenharmony_ci	struct ccsr_guts __iomem *guts;
37762306a36Sopenharmony_ci	unsigned long freq;
37862306a36Sopenharmony_ci	u64 temp;
37962306a36Sopenharmony_ci	u32 pxclk;
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	/* Map the global utilities registers. */
38262306a36Sopenharmony_ci	guts_np = of_find_compatible_node(NULL, NULL, "fsl,p1022-guts");
38362306a36Sopenharmony_ci	if (!guts_np) {
38462306a36Sopenharmony_ci		pr_err("p1022ds: missing global utilities device node\n");
38562306a36Sopenharmony_ci		return;
38662306a36Sopenharmony_ci	}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	guts = of_iomap(guts_np, 0);
38962306a36Sopenharmony_ci	of_node_put(guts_np);
39062306a36Sopenharmony_ci	if (!guts) {
39162306a36Sopenharmony_ci		pr_err("p1022ds: could not map global utilities device\n");
39262306a36Sopenharmony_ci		return;
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* Convert pixclock from a wavelength to a frequency */
39662306a36Sopenharmony_ci	temp = 1000000000000ULL;
39762306a36Sopenharmony_ci	do_div(temp, pixclock);
39862306a36Sopenharmony_ci	freq = temp;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	/*
40162306a36Sopenharmony_ci	 * 'pxclk' is the ratio of the platform clock to the pixel clock.
40262306a36Sopenharmony_ci	 * This number is programmed into the CLKDVDR register, and the valid
40362306a36Sopenharmony_ci	 * range of values is 2-255.
40462306a36Sopenharmony_ci	 */
40562306a36Sopenharmony_ci	pxclk = DIV_ROUND_CLOSEST(fsl_get_sys_freq(), freq);
40662306a36Sopenharmony_ci	pxclk = clamp_t(u32, pxclk, 2, 255);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/* Disable the pixel clock, and set it to non-inverted and no delay */
40962306a36Sopenharmony_ci	clrbits32(&guts->clkdvdr,
41062306a36Sopenharmony_ci		  CLKDVDR_PXCKEN | CLKDVDR_PXCKDLY | CLKDVDR_PXCLK_MASK);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* Enable the clock and set the pxclk */
41362306a36Sopenharmony_ci	setbits32(&guts->clkdvdr, CLKDVDR_PXCKEN | (pxclk << 16));
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	iounmap(guts);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/**
41962306a36Sopenharmony_ci * p1022ds_valid_monitor_port: set the monitor port for sysfs
42062306a36Sopenharmony_ci */
42162306a36Sopenharmony_cienum fsl_diu_monitor_port
42262306a36Sopenharmony_cip1022ds_valid_monitor_port(enum fsl_diu_monitor_port port)
42362306a36Sopenharmony_ci{
42462306a36Sopenharmony_ci	switch (port) {
42562306a36Sopenharmony_ci	case FSL_DIU_PORT_DVI:
42662306a36Sopenharmony_ci	case FSL_DIU_PORT_LVDS:
42762306a36Sopenharmony_ci		return port;
42862306a36Sopenharmony_ci	default:
42962306a36Sopenharmony_ci		return FSL_DIU_PORT_DVI; /* Dual-link LVDS is not supported */
43062306a36Sopenharmony_ci	}
43162306a36Sopenharmony_ci}
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci#endif
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_civoid __init p1022_ds_pic_init(void)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	struct mpic *mpic = mpic_alloc(NULL, 0, MPIC_BIG_ENDIAN |
43862306a36Sopenharmony_ci		MPIC_SINGLE_DEST_CPU,
43962306a36Sopenharmony_ci		0, 256, " OpenPIC  ");
44062306a36Sopenharmony_ci	BUG_ON(mpic == NULL);
44162306a36Sopenharmony_ci	mpic_init(mpic);
44262306a36Sopenharmony_ci}
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci/* TRUE if there is a "video=fslfb" command-line parameter. */
44762306a36Sopenharmony_cistatic bool fslfb;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci/*
45062306a36Sopenharmony_ci * Search for a "video=fslfb" command-line parameter, and set 'fslfb' to
45162306a36Sopenharmony_ci * true if we find it.
45262306a36Sopenharmony_ci *
45362306a36Sopenharmony_ci * We need to use early_param() instead of __setup() because the normal
45462306a36Sopenharmony_ci * __setup() gets called to late.  However, early_param() gets called very
45562306a36Sopenharmony_ci * early, before the device tree is unflattened, so all we can do now is set a
45662306a36Sopenharmony_ci * global variable.  Later on, p1022_ds_setup_arch() will use that variable
45762306a36Sopenharmony_ci * to determine if we need to update the device tree.
45862306a36Sopenharmony_ci */
45962306a36Sopenharmony_cistatic int __init early_video_setup(char *options)
46062306a36Sopenharmony_ci{
46162306a36Sopenharmony_ci	fslfb = (strncmp(options, "fslfb:", 6) == 0);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return 0;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ciearly_param("video", early_video_setup);
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci#endif
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci/*
47062306a36Sopenharmony_ci * Setup the architecture
47162306a36Sopenharmony_ci */
47262306a36Sopenharmony_cistatic void __init p1022_ds_setup_arch(void)
47362306a36Sopenharmony_ci{
47462306a36Sopenharmony_ci	if (ppc_md.progress)
47562306a36Sopenharmony_ci		ppc_md.progress("p1022_ds_setup_arch()", 0);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci#if defined(CONFIG_FB_FSL_DIU) || defined(CONFIG_FB_FSL_DIU_MODULE)
47862306a36Sopenharmony_ci	diu_ops.set_monitor_port	= p1022ds_set_monitor_port;
47962306a36Sopenharmony_ci	diu_ops.set_pixel_clock		= p1022ds_set_pixel_clock;
48062306a36Sopenharmony_ci	diu_ops.valid_monitor_port	= p1022ds_valid_monitor_port;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	/*
48362306a36Sopenharmony_ci	 * Disable the NOR and NAND flash nodes if there is video=fslfb...
48462306a36Sopenharmony_ci	 * command-line parameter.  When the DIU is active, the localbus is
48562306a36Sopenharmony_ci	 * unavailable, so we have to disable these nodes before the MTD
48662306a36Sopenharmony_ci	 * driver loads.
48762306a36Sopenharmony_ci	 */
48862306a36Sopenharmony_ci	if (fslfb) {
48962306a36Sopenharmony_ci		struct device_node *np =
49062306a36Sopenharmony_ci			of_find_compatible_node(NULL, NULL, "fsl,p1022-elbc");
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci		if (np) {
49362306a36Sopenharmony_ci			struct device_node *np2;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci			of_node_get(np);
49662306a36Sopenharmony_ci			np2 = of_find_compatible_node(np, NULL, "cfi-flash");
49762306a36Sopenharmony_ci			if (np2) {
49862306a36Sopenharmony_ci				static struct property nor_status = {
49962306a36Sopenharmony_ci					.name = "status",
50062306a36Sopenharmony_ci					.value = "disabled",
50162306a36Sopenharmony_ci					.length = sizeof("disabled"),
50262306a36Sopenharmony_ci				};
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci				/*
50562306a36Sopenharmony_ci				 * of_update_property() is called before
50662306a36Sopenharmony_ci				 * kmalloc() is available, so the 'new' object
50762306a36Sopenharmony_ci				 * should be allocated in the global area.
50862306a36Sopenharmony_ci				 * The easiest way is to do that is to
50962306a36Sopenharmony_ci				 * allocate one static local variable for each
51062306a36Sopenharmony_ci				 * call to this function.
51162306a36Sopenharmony_ci				 */
51262306a36Sopenharmony_ci				pr_info("p1022ds: disabling %pOF node",
51362306a36Sopenharmony_ci					np2);
51462306a36Sopenharmony_ci				of_update_property(np2, &nor_status);
51562306a36Sopenharmony_ci				of_node_put(np2);
51662306a36Sopenharmony_ci			}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci			of_node_get(np);
51962306a36Sopenharmony_ci			np2 = of_find_compatible_node(np, NULL,
52062306a36Sopenharmony_ci						      "fsl,elbc-fcm-nand");
52162306a36Sopenharmony_ci			if (np2) {
52262306a36Sopenharmony_ci				static struct property nand_status = {
52362306a36Sopenharmony_ci					.name = "status",
52462306a36Sopenharmony_ci					.value = "disabled",
52562306a36Sopenharmony_ci					.length = sizeof("disabled"),
52662306a36Sopenharmony_ci				};
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci				pr_info("p1022ds: disabling %pOF node",
52962306a36Sopenharmony_ci					np2);
53062306a36Sopenharmony_ci				of_update_property(np2, &nand_status);
53162306a36Sopenharmony_ci				of_node_put(np2);
53262306a36Sopenharmony_ci			}
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci			of_node_put(np);
53562306a36Sopenharmony_ci		}
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci#endif
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	mpc85xx_smp_init();
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	fsl_pci_assign_primary();
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	swiotlb_detect_4g();
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	pr_info("Freescale P1022 DS reference board\n");
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cimachine_arch_initcall(p1022_ds, mpc85xx_common_publish_devices);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_cidefine_machine(p1022_ds) {
55362306a36Sopenharmony_ci	.name			= "P1022 DS",
55462306a36Sopenharmony_ci	.compatible		= "fsl,p1022ds",
55562306a36Sopenharmony_ci	.setup_arch		= p1022_ds_setup_arch,
55662306a36Sopenharmony_ci	.init_IRQ		= p1022_ds_pic_init,
55762306a36Sopenharmony_ci#ifdef CONFIG_PCI
55862306a36Sopenharmony_ci	.pcibios_fixup_bus	= fsl_pcibios_fixup_bus,
55962306a36Sopenharmony_ci	.pcibios_fixup_phb	= fsl_pcibios_fixup_phb,
56062306a36Sopenharmony_ci#endif
56162306a36Sopenharmony_ci	.get_irq		= mpic_get_irq,
56262306a36Sopenharmony_ci	.progress		= udbg_progress,
56362306a36Sopenharmony_ci};
564