Subject: [PATCH 3/5] PowerPC 74xx: Katana Qp bootwrapper

From: Andrei Dolnikov <[email-masked]>
Date: Thu, 29 Nov 2007 18:39:51 +0300

Bootwrapper sources for Emerson Katana Qp

Signed-off-by: Andrei Dolnikov <[email-masked]>

---
 Makefile          |    3
 cuboot-katanaqp.c |  470 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 472 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/boot/Makefile b/arch/powerpc/boot/Makefile
index 18e3271..92b8fac 100644
--- a/arch/powerpc/boot/Makefile
+++ b/arch/powerpc/boot/Makefile
@@ -56,7 +56,7 @@ src-plat := of.c cuboot-52xx.c cuboot-83xx.c cuboot-85xx.c holly.c \
 		cuboot-ebony.c treeboot-ebony.c prpmc2800.c \
 		ps3-head.S ps3-hvcall.S ps3.c treeboot-bamboo.c cuboot-8xx.c \
 		cuboot-pq2.c cuboot-sequoia.c treeboot-walnut.c cuboot-bamboo.c \
-		fixed-head.S ep88xc.c cuboot-hpc2.c
+		fixed-head.S ep88xc.c cuboot-hpc2.c cuboot-katanaqp.c
 src-boot := $(src-wlib) $(src-plat) empty.c
 
 src-boot := $(addprefix $(obj)/, $(src-boot))
@@ -159,6 +159,7 @@ image-$(CONFIG_EBONY)			+= treeImage.ebony cuImage.ebony
 image-$(CONFIG_BAMBOO)			+= treeImage.bamboo cuImage.bamboo
 image-$(CONFIG_SEQUOIA)			+= cuImage.sequoia
 image-$(CONFIG_WALNUT)			+= treeImage.walnut
+image-$(CONFIG_PPC_KATANAQP)		+= cuImage.katanaqp
 endif
 
 # For 32-bit powermacs, build the COFF and miboot images
diff --git a/arch/powerpc/boot/cuboot-katanaqp.c b/arch/powerpc/boot/cuboot-katanaqp.c
new file mode 100644
index 0000000..19ba901
--- /dev/null
+++ b/arch/powerpc/boot/cuboot-katanaqp.c
@@ -0,0 +1,470 @@
+/*
+ * Emerson Katana Qp platform code.
+ *
+ * Authors: Vladislav Buzov <[email-masked]>
+ *	    Andrei Dolnikov <[email-masked]>
+ *
+ * Based on prpmc2800.c by Mark A. Greer <[email-masked]>
+ *
+ * 2007 (c) MontaVista, Software, Inc.  This file is licensed under
+ * the terms of the GNU General Public License version 2.  This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <stdarg.h>
+#include <stddef.h>
+#include "cuboot.h"
+#include "ppcboot.h"
+#include "types.h"
+#include "page.h"
+#include "string.h"
+#include "stdio.h"
+#include "io.h"
+#include "ops.h"
+#include "mv64x60.h"
+
+#define Mb	(1024U * 1024U)
+#define Gb	(Mb * 1024U)
+
+#define MHz	(1000U * 1000U)
+#define GHz	(MHz * 1000U)
+
+#define BOARD_MODEL		"Katana-Qp"
+#define BOARD_MODEL_MAX		12	/* max strlen(BOARD_MODEL) + 1 */
+#define BOARD_CFG_MAX		28	/* max strlen(BOARD_CFG) + 1 */
+#define BOARD_MODEL_LEN		(BOARD_MODEL_MAX + BOARD_CFG_MAX)
+
+#define MTD_PART_NODE		"partition"
+#define MTD_PART_NUM		3
+#define MTD_PART_NODE_LEN	20
+#define MTD_PART_MONITOR_SIZE	(1*Mb)
+#define MTD_PART_KERNEL_SIZE	(2*Mb)
+
+/*
+ * CPLD registers definitions
+ */
+#define KATANAQP_CPLD_RCR	0x0004	/* Reset command */
+#define KATANAQP_CPLD_RCR_CPUHR	(1 << 7)
+
+#define KATANAQP_CPLD_JSR	0x0020	/* Jumper settings */
+#define KATANAQP_CPLD_JSR_EBFM	(1 << 6)
+
+#define KATANAQP_CPLD_PSR	0x0030	/* PCI status */
+#define KATANAQP_CPLD_PSR_PMCM	(1 << 1)
+
+#define KATANAQP_CPLD_HCR	0x0044	/* Hardware config */
+
+static bd_t bd;
+
+static u8 *bridge_base;
+static u8 *cpld_base;
+
+typedef enum {
+	KATANAQP_UNKNOWN,
+	KATANAQP_CFG_PRPMC_SINGLE,
+	KATANAQP_CFG_PRPMC_DUAL,
+	KATANAQP_CFG_PT2CC_SINGLE,
+	KATANAQP_CFG_PT5CC_SINGLE,
+	KATANAQP_CFG_MEDIA_DUAL,
+	KATANAQP_CFG_PT2CC_DUAL,
+	KATANAQP_CFG_PT5CC_DUAL,
+	KATANAQP_CFG_PT5CC_CUSTOM,
+	KATANAQP_CFG_MEDIA_SINGLE,
+	KATANAQP_CFG_UNKNOWN,
+} katanaqp_board_model;
+
+static katanaqp_board_model katanaqp_cfg;
+
+struct katanaqp_board_info {
+	char *cfg_name;
+	char eth_phys[3];
+};
+
+struct katanaqp_mtd_part {
+	char *name;
+	u32 size;
+	u32 ro;
+};
+
+static struct katanaqp_board_info katanaqp_board_info[] = {
+
+	[KATANAQP_CFG_PRPMC_SINGLE] = {
+		.cfg_name = "PrPMC Single Core",
+		.eth_phys = {10, 13, 6},
+	},
+
+	[KATANAQP_CFG_PRPMC_DUAL] = {
+		.cfg_name = "PrPMC Dual Core",
+		.eth_phys = {10, 13, 6}
+	},
+
+	[KATANAQP_CFG_PT2CC_SINGLE] = {
+		.cfg_name = "PT2CC Single Core",
+		.eth_phys = {9, 8, 6},
+	},
+
+	[KATANAQP_CFG_PT5CC_SINGLE] = {
+		.cfg_name = "PT5CC Single Core",
+		.eth_phys = {10, 13, 6},
+	},
+
+	[KATANAQP_CFG_MEDIA_DUAL] = {
+		.cfg_name = "Dual Core Media Blade",
+		.eth_phys = {10, 13, 6},
+	},
+
+	[KATANAQP_CFG_PT2CC_DUAL] = {
+		.cfg_name = "PT2CC Dual Core",
+		.eth_phys = {9, 8, 6},
+	},
+
+	[KATANAQP_CFG_PT5CC_DUAL] = {
+		.cfg_name = "PT5CC Dual Core",
+		.eth_phys = {10, 13, 6},
+	},
+
+	[KATANAQP_CFG_MEDIA_SINGLE] = {
+		.cfg_name = "Single Core Media Blade",
+		.eth_phys = {10, 13, 6},
+	},
+};
+
+/*
+ * Second flash bank partition layout.
+ */
+static struct katanaqp_mtd_part katanaqp_mtd_parts[MTD_PART_NUM] = {
+	{
+		.name = "Secondary Monitor",
+		.size = MTD_PART_MONITOR_SIZE,
+		.ro = 1,
+	},
+
+	{
+		.name = "Secondary Kernel",
+		.size = MTD_PART_KERNEL_SIZE,
+	},
+
+	{
+		/* Size depends on actual flash bank size */
+		.name = "Secondary FS",
+	},
+};
+
+static u8 *katanaqp_get_cpld_base(void)
+{
+	u32 v;
+	void *devp;
+
+	devp = finddevice("cpld");
+	if (devp == NULL)
+		fatal("Error: Missing CPLD device tree node\n\r");
+
+	if (getprop(devp, "virtual-reg", &v, sizeof(v)) != sizeof(v))
+		fatal("Error: Can't get CPLD base address\n\r");
+
+	return (u8 *) v;
+}
+
+static void katanaqp_get_cfg(void)
+{
+	katanaqp_cfg = in_8(cpld_base + KATANAQP_CPLD_HCR) & 0xf;
+
+	if (katanaqp_cfg > 9)
+		katanaqp_cfg = KATANAQP_UNKNOWN;
+}
+
+static int katanaqp_is_monarch(void)
+{
+	return !(in_8(cpld_base + KATANAQP_CPLD_PSR) &
+		 KATANAQP_CPLD_PSR_PMCM);
+}
+
+static void katanaqp_bridge_setup(void)
+{
+	u32 i, v[12], enables, acc_bits;
+	u32 pci_base_hi, pci_base_lo, size, buf[2];
+	unsigned long cpu_base;
+	int rc;
+	void *devp;
+	u8 *bridge_pbase, is_coherent;
+	struct mv64x60_cpu2pci_win *tbl;
+
+	bridge_pbase = mv64x60_get_bridge_pbase();
+	is_coherent = mv64x60_is_coherent();
+
+	if (is_coherent)
+		acc_bits = MV64x60_PCI_ACC_CNTL_SNOOP_WB
+		    | MV64x60_PCI_ACC_CNTL_SWAP_NONE
+		    | MV64x60_PCI_ACC_CNTL_MBURST_32_BYTES
+		    | MV64x60_PCI_ACC_CNTL_RDSIZE_32_BYTES;
+	else
+		acc_bits = MV64x60_PCI_ACC_CNTL_SNOOP_NONE
+		    | MV64x60_PCI_ACC_CNTL_SWAP_NONE
+		    | MV64x60_PCI_ACC_CNTL_MBURST_128_BYTES
+		    | MV64x60_PCI_ACC_CNTL_RDSIZE_256_BYTES;
+
+	/*
+	 * MV64x60 boot code expects PCI host bridge to have 0 device
+	 * number and access PCI configuration bridge registers by
+	 * DEVFN(0, fn). This is not correct for bridges working in PCI-X
+	 * mode since by default it has 0x1f device number stored in P2P
+	 * configuration register.
+	 */
+	mv64x60_set_pci_bus(bridge_base, 1, 0, 0);
+	
+	mv64x60_config_ctlr_windows(bridge_base, bridge_pbase, is_coherent);
+	mv64x60_config_pci_windows(bridge_base, bridge_pbase, 1, 0, acc_bits);
+
+	/* Get the cpu -> pci i/o & mem mappings from the device tree */
+	devp = finddevice("/mv64x60/pci");
+	if (devp == NULL)
+		fatal("Error: Missing /mv64x60/pci device tree node\n\r");
+
+	rc = getprop(devp, "ranges", v, sizeof(v));
+	if (rc != sizeof(v))
+		fatal("Error: Can't find /mv64x60/pci/ranges property\n\r");
+
+	/* Get the cpu -> pci i/o & mem mappings from the device tree */
+	devp = finddevice("/mv64x60");
+	if (devp == NULL)
+		fatal("Error: Missing /mv64x60 device tree node\n\r");
+
+
+	enables = in_le32((u32 *) (bridge_base + MV64x60_CPU_BAR_ENABLE));
+	enables |= 0x0007fe00;	/* Disable all cpu->pci windows */
+	out_le32((u32 *) (bridge_base + MV64x60_CPU_BAR_ENABLE), enables);
+
+	for (i = 0; i < 12; i += 6) {
+		switch (v[i] & 0xff000000) {
+		case 0x01000000:	/* PCI I/O Space */
+			tbl = mv64x60_cpu2pci_io;
+			break;
+		case 0x02000000:	/* PCI MEM Space */
+			tbl = mv64x60_cpu2pci_mem;
+			break;
+		default:
+			continue;
+		}
+
+		pci_base_hi = v[i + 1];
+		pci_base_lo = v[i + 2];
+		cpu_base = v[i + 3];
+		size = v[i + 5];
+
+		buf[0] = cpu_base;
+		buf[1] = size;
+
+		if (!dt_xlate_addr(devp, buf, sizeof(buf), &cpu_base))
+			fatal("Error: Can't translate PCI address 0x%x\n\r",
+			      (u32) cpu_base);
+
+		mv64x60_config_cpu2pci_window(bridge_base, 1, pci_base_hi,
+					      pci_base_lo, cpu_base, size, tbl);
+	}
+
+	/* Enable cpu->pci1 i/o, cpu->pci1 mem0 */
+	enables &= ~0x0000c000;
+	out_le32((u32 *) (bridge_base + MV64x60_CPU_BAR_ENABLE), enables);
+
+}
+
+/*
+ * Different Katana Qp configurations have different flash sizes varying
+ * from 16Mb to 64Mb. This routine determines an exact flash size and
+ * updates the device tree accordingly.
+ */
+static void katanaqp_flash_fixup(void)
+{
+	u32 flash0_size = 0, flash1_size = 0;
+	u32 total_size, size_left;
+	u32 part, part_offset;
+	u32 rc, v[2];
+	void *devp = NULL, *pp = NULL;
+	char part_node[MTD_PART_NODE_LEN];
+
+	devp = finddevice("/mv64x60/flash");
+	if (devp == NULL) {
+		printf("Missing flash device tree node\n\r");
+		return;
+	}
+
+	/*
+	 * Get fist flash size: bank0, bank1 and total
+	 */
+	flash0_size = mv64x60_get_devcs_size(bridge_base, 0);
+	if (flash0_size == 0)
+		return;
+
+	flash1_size = mv64x60_get_devcs_size(bridge_base, 1);
+	total_size = flash0_size + flash1_size;
+
+	/*
+	 * Set total flash size
+	 */
+	rc = getprop(devp, "reg", v, sizeof(v));
+	if (rc != sizeof(v))
+		fatal("Error: Can't find /mv64x60/flash/reg property\n\r");
+	v[1] = total_size;
+	setprop(devp, "reg", v, sizeof(v));
+
+	/*
+	 * Set Primary FS partition size: up to the end of first flash bank
+	 */
+	pp = find_node_by_prop_value_str(NULL, "label", "Primary FS");
+	if (pp == NULL)
+		fatal("Error: Missing flash Primary FS device tree node\n\r");
+
+	rc = getprop(pp, "reg", v, sizeof(v));
+	if (rc != sizeof(v))
+		fatal("Error: Can't find /mv64x60/flash/partition@3000000 "
+		      "property\n\r");
+
+	v[1] = flash0_size - MTD_PART_MONITOR_SIZE - MTD_PART_KERNEL_SIZE;
+	setprop(pp, "reg", v, sizeof(v));
+
+	if (flash1_size == 0)
+		/* Only 1 flash bank is presented */
+		return;
+
+	/*
+	 * Ok, there is a second flash bank. Let's split it to partitions.
+	 */
+
+	part_offset = flash0_size;
+	size_left = flash1_size;
+
+	/* Skip Secondary Monitor if Boot Failover mechanism is disabled */
+	rc = in_8(cpld_base + KATANAQP_CPLD_JSR) & KATANAQP_CPLD_JSR_EBFM;
+	part = rc ? 0 : 1;
+
+	for (; part < MTD_PART_NUM; part++) {
+
+		sprintf(part_node, "%s@%x", MTD_PART_NODE, part_offset);
+		pp = create_node(devp, part_node);
+		if (pp == NULL)
+			fatal("Error: Can't create new partition node\n\r");
+
+		setprop_str(pp, "label", katanaqp_mtd_parts[part].name);
+
+		if (katanaqp_mtd_parts[part].ro)
+			setprop(pp, "read-only", NULL, 0);
+
+		v[0] = part_offset;
+		v[1] = katanaqp_mtd_parts[part].size;
+		if (v[1] == 0)
+			/* Take all remaining space */
+			v[1] = size_left;
+
+		part_offset += v[1];
+		size_left -= v[1];
+
+		setprop(pp, "reg", v, sizeof(v));
+
+	}
+}
+
+static void katanaqp_fixups(void)
+{
+	u32 l, p, pnum;
+	void *devp = NULL;
+	struct katanaqp_board_info katanaqp_bif;
+	char model[BOARD_MODEL_LEN];
+
+	/* Check Katana Qp configuration */
+	katanaqp_get_cfg();
+	if (katanaqp_cfg == KATANAQP_CFG_UNKNOWN)
+		fatal("Error: Unsupported Katana Qp board configuration\n\r");
+
+	if (katanaqp_cfg == KATANAQP_CFG_PT5CC_CUSTOM) {
+		printf("Katana Qp board custom configuration detected, "
+		       "using device tree defaults. Please, supply a correct "
+		       "device tree\n\r");
+		return;
+	}
+
+	katanaqp_bif = katanaqp_board_info[katanaqp_cfg];
+
+	/*
+	 * Set /model appropriately
+	 */
+	devp = finddevice("/");
+	if (devp == NULL)
+		fatal("Error: Missing '/' device tree node\n\r");
+
+	/*
+	 * Fix Board model name in device tree
+	 */
+	memset(model, 0, BOARD_MODEL_LEN);
+
+	strncpy(model, BOARD_MODEL, BOARD_MODEL_MAX - 1);
+	l = strlen(model);
+	model[l++] = ' ';
+
+	strncpy(&model[l], katanaqp_bif.cfg_name, BOARD_CFG_MAX - 1);
+	l += strlen(&model[l]);
+	model[l++] = '\0';
+
+	setprop(devp, "model", model, l);
+
+	/*
+	 * Do necessary bridge setup if we are monarch
+	 */
+	if (katanaqp_is_monarch())
+		katanaqp_bridge_setup();
+
+	/*
+	 * Fix RAM size and setup MV64460 bridge
+	 */
+	dt_fixup_memory(bd.bi_memstart, bd.bi_memsize);
+
+	/*
+	 * Fix flash size and partition layout
+	 */
+	katanaqp_flash_fixup();
+
+	/*
+	 * Fix clocks
+	 */
+	dt_fixup_cpu_clocks(bd.bi_intfreq, bd.bi_busfreq / 4, bd.bi_busfreq);
+
+	/*
+	 * Fix phy addresses
+	 */
+	while ((devp = find_node_by_prop_value_str(devp, "comaptible",
+						   "marvell,mv88e1111"))) {
+		getprop(devp, "block-index", &p, sizeof(p));
+		pnum = katanaqp_bif.eth_phys[p];
+		setprop_val(devp, "reg", pnum);
+	}
+}
+
+static void katanaqp_reset(void)
+{
+
+	/* issue hard reset to the reset command register */
+	if (cpld_base)
+		out_8(cpld_base + KATANAQP_CPLD_RCR,
+		      KATANAQP_CPLD_RCR_CPUHR);
+
+	for (;;) ;
+}
+
+void platform_init(unsigned long r3, unsigned long r4, unsigned long r5,
+		   unsigned long r6, unsigned long r7)
+{
+
+	CUBOOT_INIT();
+
+	if (ft_init(_dtb_start, _dtb_end - _dtb_start, 16))
+		exit();
+
+	bridge_base = mv64x60_get_bridge_base();
+	cpld_base = katanaqp_get_cpld_base();
+
+	platform_ops.fixups = katanaqp_fixups;
+	platform_ops.exit = katanaqp_reset;
+
+	if (serial_console_init() < 0)
+		exit();
+}