162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * arch/powerpc/boot/wii.c
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Nintendo Wii bootwrapper support
662306a36Sopenharmony_ci * Copyright (C) 2008-2009 The GameCube Linux Team
762306a36Sopenharmony_ci * Copyright (C) 2008,2009 Albert Herranz
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <stddef.h>
1162306a36Sopenharmony_ci#include "stdio.h"
1262306a36Sopenharmony_ci#include "types.h"
1362306a36Sopenharmony_ci#include "io.h"
1462306a36Sopenharmony_ci#include "ops.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include "ugecon.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ciBSS_STACK(8192);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define HW_REG(x)		((void *)(x))
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define EXI_CTRL		HW_REG(0x0d800070)
2362306a36Sopenharmony_ci#define EXI_CTRL_ENABLE		(1<<0)
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define MEM2_TOP		(0x10000000 + 64*1024*1024)
2662306a36Sopenharmony_ci#define FIRMWARE_DEFAULT_SIZE	(12*1024*1024)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistruct mipc_infohdr {
3062306a36Sopenharmony_ci	char magic[3];
3162306a36Sopenharmony_ci	u8 version;
3262306a36Sopenharmony_ci	u32 mem2_boundary;
3362306a36Sopenharmony_ci	u32 ipc_in;
3462306a36Sopenharmony_ci	size_t ipc_in_size;
3562306a36Sopenharmony_ci	u32 ipc_out;
3662306a36Sopenharmony_ci	size_t ipc_out_size;
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_cistatic int mipc_check_address(u32 pa)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	/* only MEM2 addresses */
4262306a36Sopenharmony_ci	if (pa < 0x10000000 || pa > 0x14000000)
4362306a36Sopenharmony_ci		return -EINVAL;
4462306a36Sopenharmony_ci	return 0;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic struct mipc_infohdr *mipc_get_infohdr(void)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct mipc_infohdr **hdrp, *hdr;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	/* 'mini' header pointer is the last word of MEM2 memory */
5262306a36Sopenharmony_ci	hdrp = (struct mipc_infohdr **)0x13fffffc;
5362306a36Sopenharmony_ci	if (mipc_check_address((u32)hdrp)) {
5462306a36Sopenharmony_ci		printf("mini: invalid hdrp %08X\n", (u32)hdrp);
5562306a36Sopenharmony_ci		hdr = NULL;
5662306a36Sopenharmony_ci		goto out;
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	hdr = *hdrp;
6062306a36Sopenharmony_ci	if (mipc_check_address((u32)hdr)) {
6162306a36Sopenharmony_ci		printf("mini: invalid hdr %08X\n", (u32)hdr);
6262306a36Sopenharmony_ci		hdr = NULL;
6362306a36Sopenharmony_ci		goto out;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	if (memcmp(hdr->magic, "IPC", 3)) {
6662306a36Sopenharmony_ci		printf("mini: invalid magic\n");
6762306a36Sopenharmony_ci		hdr = NULL;
6862306a36Sopenharmony_ci		goto out;
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ciout:
7262306a36Sopenharmony_ci	return hdr;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_cistatic int mipc_get_mem2_boundary(u32 *mem2_boundary)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct mipc_infohdr *hdr;
7862306a36Sopenharmony_ci	int error;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	hdr = mipc_get_infohdr();
8162306a36Sopenharmony_ci	if (!hdr) {
8262306a36Sopenharmony_ci		error = -1;
8362306a36Sopenharmony_ci		goto out;
8462306a36Sopenharmony_ci	}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if (mipc_check_address(hdr->mem2_boundary)) {
8762306a36Sopenharmony_ci		printf("mini: invalid mem2_boundary %08X\n",
8862306a36Sopenharmony_ci		       hdr->mem2_boundary);
8962306a36Sopenharmony_ci		error = -EINVAL;
9062306a36Sopenharmony_ci		goto out;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	*mem2_boundary = hdr->mem2_boundary;
9362306a36Sopenharmony_ci	error = 0;
9462306a36Sopenharmony_ciout:
9562306a36Sopenharmony_ci	return error;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void platform_fixups(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	void *mem;
10262306a36Sopenharmony_ci	u32 reg[4];
10362306a36Sopenharmony_ci	u32 mem2_boundary;
10462306a36Sopenharmony_ci	int len;
10562306a36Sopenharmony_ci	int error;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	mem = finddevice("/memory");
10862306a36Sopenharmony_ci	if (!mem)
10962306a36Sopenharmony_ci		fatal("Can't find memory node\n");
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	/* two ranges of (address, size) words */
11262306a36Sopenharmony_ci	len = getprop(mem, "reg", reg, sizeof(reg));
11362306a36Sopenharmony_ci	if (len != sizeof(reg)) {
11462306a36Sopenharmony_ci		/* nothing to do */
11562306a36Sopenharmony_ci		goto out;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* retrieve MEM2 boundary from 'mini' */
11962306a36Sopenharmony_ci	error = mipc_get_mem2_boundary(&mem2_boundary);
12062306a36Sopenharmony_ci	if (error) {
12162306a36Sopenharmony_ci		/* if that fails use a sane value */
12262306a36Sopenharmony_ci		mem2_boundary = MEM2_TOP - FIRMWARE_DEFAULT_SIZE;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (mem2_boundary > reg[2] && mem2_boundary < reg[2] + reg[3]) {
12662306a36Sopenharmony_ci		reg[3] = mem2_boundary - reg[2];
12762306a36Sopenharmony_ci		printf("top of MEM2 @ %08X\n", reg[2] + reg[3]);
12862306a36Sopenharmony_ci		setprop(mem, "reg", reg, sizeof(reg));
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ciout:
13262306a36Sopenharmony_ci	return;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_civoid platform_init(unsigned long r3, unsigned long r4, unsigned long r5)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	u32 heapsize = 24*1024*1024 - (u32)_end;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	simple_alloc_init(_end, heapsize, 32, 64);
14062306a36Sopenharmony_ci	fdt_init(_dtb_start);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	/*
14362306a36Sopenharmony_ci	 * 'mini' boots the Broadway processor with EXI disabled.
14462306a36Sopenharmony_ci	 * We need it enabled before probing for the USB Gecko.
14562306a36Sopenharmony_ci	 */
14662306a36Sopenharmony_ci	out_be32(EXI_CTRL, in_be32(EXI_CTRL) | EXI_CTRL_ENABLE);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	if (ug_probe())
14962306a36Sopenharmony_ci		console_ops.write = ug_console_write;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	platform_ops.fixups = platform_fixups;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
154