/* prom.c - emulates a sparc v0 PROM for the linux kernel.
 *
 * Copyright (C) 2003 Konrad Eisele <eiselekd@web.de>
 * Copyright (C) 2004 Stefan Holst <mail@s-holst.de>
 * Copyright (C) 2007 Daniel Hellstrom <daniel@gaisler.com>
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 *
 */

#include <common.h>
#include <asm/prom.h>
#include <asm/machines.h>
#include <asm/srmmu.h>
#include <asm/processor.h>
#include <asm/irq.h>
#include <asm/leon.h>

#include <config.h>
/*
#define PRINT_ROM_VEC
*/
extern struct linux_romvec *kernel_arg_promvec;

#define PROM_PGT __attribute__ ((__section__ (".prom.pgt")))
#define PROM_TEXT __attribute__ ((__section__ (".prom.text")))
#define PROM_DATA __attribute__ ((__section__ (".prom.data")))

/* for __va */
extern int __prom_start;
#define PAGE_OFFSET 0xf0000000
#define phys_base CONFIG_SYS_SDRAM_BASE
#define PROM_OFFS 8192
#define PROM_SIZE_MASK (PROM_OFFS-1)
#define __va(x) ( \
	(void *)( ((unsigned long)(x))-PROM_OFFS+ \
	(CONFIG_SYS_PROM_OFFSET-phys_base)+PAGE_OFFSET-CONFIG_SYS_TEXT_BASE ) \
	)
#define __phy(x) ((void *)(((unsigned long)(x))-PROM_OFFS+CONFIG_SYS_PROM_OFFSET-CONFIG_SYS_TEXT_BASE))

struct property {
	char *name;
	char *value;
	int length;
};

struct node {
	int level;
	struct property *properties;
};

static void leon_reboot(char *bcommand);
static void leon_halt(void);
static int leon_nbputchar(int c);
static int leon_nbgetchar(void);

static int no_nextnode(int node);
static int no_child(int node);
static int no_proplen(int node, char *name);
static int no_getprop(int node, char *name, char *value);
static int no_setprop(int node, char *name, char *value, int len);
static char *no_nextprop(int node, char *name);

static struct property PROM_TEXT *find_property(int node, char *name);
static int PROM_TEXT leon_strcmp(const char *s1, const char *s2);
static void *PROM_TEXT leon_memcpy(void *dest, const void *src, size_t n);
static void PROM_TEXT leon_reboot_physical(char *bcommand);

void __inline__ leon_flush_cache_all(void)
{
	__asm__ __volatile__(" flush ");
      __asm__ __volatile__("sta %%g0, [%%g0] %0\n\t"::"i"(ASI_DFLUSH):"memory");
}

void __inline__ leon_flush_tlb_all(void)
{
	leon_flush_cache_all();
	__asm__ __volatile__("sta %%g0, [%0] %1\n\t"::"r"(0x400),
			     "i"(ASI_MMUFLUSH):"memory");
}

typedef struct {
	unsigned int ctx_table[256];
	unsigned int pgd_table[256];
} sparc_srmmu_setup;

sparc_srmmu_setup srmmu_tables PROM_PGT = {
	{0},
	{0x1e,
	 0x10001e,
	 0x20001e,
	 0x30001e,
	 0x40001e,
	 0x50001e,
	 0x60001e,
	 0x70001e,
	 0x80001e,
	 0x90001e,
	 0xa0001e,
	 0xb0001e,
	 0xc0001e,
	 0xd0001e,
	 0xe0001e,
	 0xf0001e,
	 0x100001e,
	 0x110001e,
	 0x120001e,
	 0x130001e,
	 0x140001e,
	 0x150001e,
	 0x160001e,
	 0x170001e,
	 0x180001e,
	 0x190001e,
	 0x1a0001e,
	 0x1b0001e,
	 0x1c0001e,
	 0x1d0001e,
	 0x1e0001e,
	 0x1f0001e,
	 0x200001e,
	 0x210001e,
	 0x220001e,
	 0x230001e,
	 0x240001e,
	 0x250001e,
	 0x260001e,
	 0x270001e,
	 0x280001e,
	 0x290001e,
	 0x2a0001e,
	 0x2b0001e,
	 0x2c0001e,
	 0x2d0001e,
	 0x2e0001e,
	 0x2f0001e,
	 0x300001e,
	 0x310001e,
	 0x320001e,
	 0x330001e,
	 0x340001e,
	 0x350001e,
	 0x360001e,
	 0x370001e,
	 0x380001e,
	 0x390001e,
	 0x3a0001e,
	 0x3b0001e,
	 0x3c0001e,
	 0x3d0001e,
	 0x3e0001e,
	 0x3f0001e,
	 0x400001e,
	 0x410001e,
	 0x420001e,
	 0x430001e,
	 0x440001e,
	 0x450001e,
	 0x460001e,
	 0x470001e,
	 0x480001e,
	 0x490001e,
	 0x4a0001e,
	 0x4b0001e,
	 0x4c0001e,
	 0x4d0001e,
	 0x4e0001e,
	 0x4f0001e,
	 0x500001e,
	 0x510001e,
	 0x520001e,
	 0x530001e,
	 0x540001e,
	 0x550001e,
	 0x560001e,
	 0x570001e,
	 0x580001e,
	 0x590001e,
	 0x5a0001e,
	 0x5b0001e,
	 0x5c0001e,
	 0x5d0001e,
	 0x5e0001e,
	 0x5f0001e,
	 0x600001e,
	 0x610001e,
	 0x620001e,
	 0x630001e,
	 0x640001e,
	 0x650001e,
	 0x660001e,
	 0x670001e,
	 0x680001e,
	 0x690001e,
	 0x6a0001e,
	 0x6b0001e,
	 0x6c0001e,
	 0x6d0001e,
	 0x6e0001e,
	 0x6f0001e,
	 0x700001e,
	 0x710001e,
	 0x720001e,
	 0x730001e,
	 0x740001e,
	 0x750001e,
	 0x760001e,
	 0x770001e,
	 0x780001e,
	 0x790001e,
	 0x7a0001e,
	 0x7b0001e,
	 0x7c0001e,
	 0x7d0001e,
	 0x7e0001e,
	 0x7f0001e,
	 0x800001e,
	 0x810001e,
	 0x820001e,
	 0x830001e,
	 0x840001e,
	 0x850001e,
	 0x860001e,
	 0x870001e,
	 0x880001e,
	 0x890001e,
	 0x8a0001e,
	 0x8b0001e,
	 0x8c0001e,
	 0x8d0001e,
	 0x8e0001e,
	 0x8f0001e,
	 0x900001e,
	 0x910001e,
	 0x920001e,
	 0x930001e,
	 0x940001e,
	 0x950001e,
	 0x960001e,
	 0x970001e,
	 0x980001e,
	 0x990001e,
	 0x9a0001e,
	 0x9b0001e,
	 0x9c0001e,
	 0x9d0001e,
	 0x9e0001e,
	 0x9f0001e,
	 0xa00001e,
	 0xa10001e,
	 0xa20001e,
	 0xa30001e,
	 0xa40001e,
	 0xa50001e,
	 0xa60001e,
	 0xa70001e,
	 0xa80001e,
	 0xa90001e,
	 0xaa0001e,
	 0xab0001e,
	 0xac0001e,
	 0xad0001e,
	 0xae0001e,
	 0xaf0001e,
	 0xb00001e,
	 0xb10001e,
	 0xb20001e,
	 0xb30001e,
	 0xb40001e,
	 0xb50001e,
	 0xb60001e,
	 0xb70001e,
	 0xb80001e,
	 0xb90001e,
	 0xba0001e,
	 0xbb0001e,
	 0xbc0001e,
	 0xbd0001e,
	 0xbe0001e,
	 0xbf0001e,
	 0xc00001e,
	 0xc10001e,
	 0xc20001e,
	 0xc30001e,
	 0xc40001e,
	 0xc50001e,
	 0xc60001e,
	 0xc70001e,
	 0xc80001e,
	 0xc90001e,
	 0xca0001e,
	 0xcb0001e,
	 0xcc0001e,
	 0xcd0001e,
	 0xce0001e,
	 0xcf0001e,
	 0xd00001e,
	 0xd10001e,
	 0xd20001e,
	 0xd30001e,
	 0xd40001e,
	 0xd50001e,
	 0xd60001e,
	 0xd70001e,
	 0xd80001e,
	 0xd90001e,
	 0xda0001e,
	 0xdb0001e,
	 0xdc0001e,
	 0xdd0001e,
	 0xde0001e,
	 0xdf0001e,
	 0xe00001e,
	 0xe10001e,
	 0xe20001e,
	 0xe30001e,
	 0xe40001e,
	 0xe50001e,
	 0xe60001e,
	 0xe70001e,
	 0xe80001e,
	 0xe90001e,
	 0xea0001e,
	 0xeb0001e,
	 0xec0001e,
	 0xed0001e,
	 0xee0001e,
	 0xef0001e,
	 0x400001e		/* default */
	 }
};

/* a self contained prom info structure */
struct leon_reloc_func {
	struct property *(*find_property) (int node, char *name);
	int (*strcmp) (char *s1, char *s2);
	void *(*memcpy) (void *dest, const void *src, size_t n);
	void (*reboot_physical) (char *cmd);
};

struct leon_prom_info {
	int freq_khz;
	int leon_nctx;
	int mids[32];
	int baudrates[2];
	struct leon_reloc_func reloc_funcs;
	struct property root_properties[4];
	struct property cpu_properties[7];
#undef  CPUENTRY
#define CPUENTRY(idx) struct property cpu_properties##idx[4]
	 CPUENTRY(1);
	 CPUENTRY(2);
	 CPUENTRY(3);
	 CPUENTRY(4);
	 CPUENTRY(5);
	 CPUENTRY(6);
	 CPUENTRY(7);
	 CPUENTRY(8);
	 CPUENTRY(9);
	 CPUENTRY(10);
	 CPUENTRY(11);
	 CPUENTRY(12);
	 CPUENTRY(13);
	 CPUENTRY(14);
	 CPUENTRY(15);
	 CPUENTRY(16);
	 CPUENTRY(17);
	 CPUENTRY(18);
	 CPUENTRY(19);
	 CPUENTRY(20);
	 CPUENTRY(21);
	 CPUENTRY(22);
	 CPUENTRY(23);
	 CPUENTRY(24);
	 CPUENTRY(25);
	 CPUENTRY(26);
	 CPUENTRY(27);
	 CPUENTRY(28);
	 CPUENTRY(29);
	 CPUENTRY(30);
	 CPUENTRY(31);
	struct idprom idprom;
	struct linux_nodeops nodeops;
	struct linux_mlist_v0 *totphys_p;
	struct linux_mlist_v0 totphys;
	struct linux_mlist_v0 *avail_p;
	struct linux_mlist_v0 avail;
	struct linux_mlist_v0 *prommap_p;
	void (*synchook) (void);
	struct linux_arguments_v0 *bootargs_p;
	struct linux_arguments_v0 bootargs;
	struct linux_romvec romvec;
	struct node nodes[35];
	char s_device_type[12];
	char s_cpu[4];
	char s_mid[4];
	char s_idprom[7];
	char s_compatability[14];
	char s_leon2[6];
	char s_mmu_nctx[9];
	char s_frequency[16];
	char s_uart1_baud[11];
	char s_uart2_baud[11];
	char arg[256];
};

/* static prom info */
static struct leon_prom_info PROM_DATA spi = {
	CONFIG_SYS_CLK_FREQ / 1000,
	256,
	{
#undef	CPUENTRY
#define	CPUENTRY(idx) idx
	 CPUENTRY(0),
	 CPUENTRY(1),
	 CPUENTRY(2),
	 CPUENTRY(3),
	 CPUENTRY(4),
	 CPUENTRY(5),
	 CPUENTRY(6),
	 CPUENTRY(7),
	 CPUENTRY(8),
	 CPUENTRY(9),
	 CPUENTRY(10),
	 CPUENTRY(11),
	 CPUENTRY(12),
	 CPUENTRY(13),
	 CPUENTRY(14),
	 CPUENTRY(15),
	 CPUENTRY(16),
	 CPUENTRY(17),
	 CPUENTRY(18),
	 CPUENTRY(19),
	 CPUENTRY(20),
	 CPUENTRY(21),
	 CPUENTRY(22),
	 CPUENTRY(23),
	 CPUENTRY(24),
	 CPUENTRY(25),
	 CPUENTRY(26),
	 CPUENTRY(27),
	 CPUENTRY(28),
	 CPUENTRY(29),
	 CPUENTRY(30),
	 31},
	{38400, 38400},
	{
	 __va(find_property),
	 __va(leon_strcmp),
	 __va(leon_memcpy),
	 __phy(leon_reboot_physical),
	 },
	{
	 {__va(spi.s_device_type), __va(spi.s_idprom), 4},
	 {__va(spi.s_idprom), (char *)__va(&spi.idprom), sizeof(struct idprom)},
	 {__va(spi.s_compatability), __va(spi.s_leon2), 5},
	 {NULL, NULL, -1}
	 },
	{
	 {__va(spi.s_device_type), __va(spi.s_cpu), 4},
	 {__va(spi.s_mid), __va(&spi.mids[0]), 4},
	 {__va(spi.s_mmu_nctx), (char *)__va(&spi.leon_nctx), 4},
	 {__va(spi.s_frequency), (char *)__va(&spi.freq_khz), 4},
	 {__va(spi.s_uart1_baud), (char *)__va(&spi.baudrates[0]), 4},
	 {__va(spi.s_uart2_baud), (char *)__va(&spi.baudrates[1]), 4},
	 {NULL, NULL, -1}
	 },
#undef  CPUENTRY
#define CPUENTRY(idx) \
	{ /* cpu_properties */						\
		{__va(spi.s_device_type), __va(spi.s_cpu), 4},		\
		{__va(spi.s_mid), __va(&spi.mids[idx]), 4},			\
		{__va(spi.s_frequency), (char *)__va(&spi.freq_khz), 4},	\
		{NULL, NULL, -1}						\
	}
	CPUENTRY(1),
	CPUENTRY(2),
	CPUENTRY(3),
	CPUENTRY(4),
	CPUENTRY(5),
	CPUENTRY(6),
	CPUENTRY(7),
	CPUENTRY(8),
	CPUENTRY(9),
	CPUENTRY(10),
	CPUENTRY(11),
	CPUENTRY(12),
	CPUENTRY(13),
	CPUENTRY(14),
	CPUENTRY(15),
	CPUENTRY(16),
	CPUENTRY(17),
	CPUENTRY(18),
	CPUENTRY(19),
	CPUENTRY(20),
	CPUENTRY(21),
	CPUENTRY(22),
	CPUENTRY(23),
	CPUENTRY(24),
	CPUENTRY(25),
	CPUENTRY(26),
	CPUENTRY(27),
	CPUENTRY(28),
	CPUENTRY(29),
	CPUENTRY(30),
	CPUENTRY(31),
	{
	 0x01,			/* format */
	 M_LEON2 | M_LEON2_SOC,	/* machine type */
	 {0, 0, 0, 0, 0, 0},	/* eth */
	 0,			/* date */
	 0,			/* sernum */
	 0,			/* checksum */
	 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}	/* reserved */
	 },
	{
	 __va(no_nextnode),
	 __va(no_child),
	 __va(no_proplen),
	 __va(no_getprop),
	 __va(no_setprop),
	 __va(no_nextprop)
	 },
	__va(&spi.totphys),
	{
	 NULL,
	 (char *)CONFIG_SYS_SDRAM_BASE,
	 0,
	 },
	__va(&spi.avail),
	{
	 NULL,
	 (char *)CONFIG_SYS_SDRAM_BASE,
	 0,
	 },
	NULL,			/* prommap_p */
	NULL,
	__va(&spi.bootargs),
	{
	 {NULL, __va(spi.arg), NULL /*... */ },
	 /*... */
	 },
	{
	 0,
	 0,			/* sun4c v0 prom */
	 0, 0,
	 {__va(&spi.totphys_p), __va(&spi.prommap_p), __va(&spi.avail_p)},
	 __va(&spi.nodeops),
	 NULL, {NULL /* ... */ },
	 NULL, NULL,
	 NULL, NULL,		/* pv_getchar, pv_putchar */
	 __va(leon_nbgetchar), __va(leon_nbputchar),
	 NULL,
	 __va(leon_reboot),
	 NULL,
	 NULL,
	 NULL,
	 __va(leon_halt),
	 __va(&spi.synchook),
	 {NULL},
	 __va(&spi.bootargs_p)
	 /*... */
	 },
	{
	 {0, __va(spi.root_properties + 3) /* NULL, NULL, -1 */ },
	 {0, __va(spi.root_properties)},
	 /* cpu 0, must be spi.nodes[2] see leon_prom_init() */
	 {1, __va(spi.cpu_properties)},

#undef  CPUENTRY
#define CPUENTRY(idx) \
	  {1, __va(spi.cpu_properties##idx) }	/* cpu <idx> */
	 CPUENTRY(1),
	 CPUENTRY(2),
	 CPUENTRY(3),
	 CPUENTRY(4),
	 CPUENTRY(5),
	 CPUENTRY(6),
	 CPUENTRY(7),
	 CPUENTRY(8),
	 CPUENTRY(9),
	 CPUENTRY(10),
	 CPUENTRY(11),
	 CPUENTRY(12),
	 CPUENTRY(13),
	 CPUENTRY(14),
	 CPUENTRY(15),
	 CPUENTRY(16),
	 CPUENTRY(17),
	 CPUENTRY(18),
	 CPUENTRY(19),
	 CPUENTRY(20),
	 CPUENTRY(21),
	 CPUENTRY(22),
	 CPUENTRY(23),
	 CPUENTRY(24),
	 CPUENTRY(25),
	 CPUENTRY(26),
	 CPUENTRY(27),
	 CPUENTRY(28),
	 CPUENTRY(29),
	 CPUENTRY(30),
	 CPUENTRY(31),
	 {-1, __va(spi.root_properties + 3) /* NULL, NULL, -1 */ }
	 },
	"device_type",
	"cpu",
	"mid",
	"idprom",
	"compatability",
	"leon2",
	"mmu-nctx",
	"clock-frequency",
	"uart1_baud",
	"uart2_baud",
	CONFIG_DEFAULT_KERNEL_COMMAND_LINE
};

/* from arch/sparc/kernel/setup.c */
#define RAMDISK_LOAD_FLAG 0x4000
extern unsigned short root_flags;
extern unsigned short root_dev;
extern unsigned short ram_flags;
extern unsigned int sparc_ramdisk_image;
extern unsigned int sparc_ramdisk_size;
extern int root_mountflags;

extern char initrd_end, initrd_start;

/* Reboot the CPU = jump to beginning of flash again.
 *
 * Make sure that all function are inlined here.
 */
static void PROM_TEXT leon_reboot(char *bcommand)
{
	register char *arg = bcommand;
	void __attribute__ ((noreturn)) (*reboot_physical) (char *cmd);

	/* get physical address */
	struct leon_prom_info *pspi =
	    (void *)(CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	unsigned int *srmmu_ctx_table;

	/* Turn of Interrupts */
	set_pil(0xf);

	/* Set kernel's context, context zero */
	srmmu_set_context(0);

	/* Get physical address of the MMU shutdown routine */
	reboot_physical = (void *)
	    SPARC_BYPASS_READ(&pspi->reloc_funcs.reboot_physical);

	/* Now that we know the physical address of the function
	 * we can make the MMU allow jumping to it.
	 */
	srmmu_ctx_table = (unsigned int *)srmmu_get_ctable_ptr();

	srmmu_ctx_table = (unsigned int *)SPARC_BYPASS_READ(srmmu_ctx_table);

	/* get physical address of kernel's context table (assume ptd) */
	srmmu_ctx_table = (unsigned int *)
	    (((unsigned int)srmmu_ctx_table & 0xfffffffc) << 4);

	/* enable access to physical address of MMU shutdown function */
	SPARC_BYPASS_WRITE(&srmmu_ctx_table
			   [((unsigned int)reboot_physical) >> 24],
			   (((unsigned int)reboot_physical & 0xff000000) >> 4) |
			   0x1e);

	/* flush TLB cache */
	leon_flush_tlb_all();

	/* flash instruction & data cache */
	sparc_icache_flush_all();
	sparc_dcache_flush_all();

	/* jump to physical address function
	 * so that when the MMU is disabled
	 * we can continue to execute
	 */
	reboot_physical(arg);
}

static void PROM_TEXT leon_reboot_physical(char *bcommand)
{
	void __attribute__ ((noreturn)) (*reset) (void);

	/* Turn off MMU */
	srmmu_set_mmureg(0);

	/* Hardcoded start address */
	reset = CONFIG_SYS_MONITOR_BASE;

	/* flush data cache */
	sparc_dcache_flush_all();

	/* flush instruction cache */
	sparc_icache_flush_all();

	/* Jump to start in Flash */
	reset();
}

static void PROM_TEXT leon_halt(void)
{
	while (1) ;
}

/* get single char, don't care for blocking*/
static int PROM_TEXT leon_nbgetchar(void)
{
	return -1;
}

/* put single char, don't care for blocking*/
static int PROM_TEXT leon_nbputchar(int c)
{
	LEON2_regs *leon2 = (LEON2_regs *) LEON2_PREGS;

	/***** put char in buffer... ***********
	 * Make sure all functions are inline! *
	 ***************************************/

	/* Wait for last character to go. */
	while (!(SPARC_BYPASS_READ(&leon2->UART_Status_1)
		 & LEON2_UART_STAT_THE)) ;

	/* Send data */
	SPARC_BYPASS_WRITE(&leon2->UART_Channel_1, c);

	/* Wait for data to be sent */
	while (!(SPARC_BYPASS_READ(&leon2->UART_Status_1)
		 & LEON2_UART_STAT_TSE)) ;

	return 0;
}

/* node ops */

/*#define nodes ((struct node *)__va(&pspi->nodes))*/
#define nodes ((struct node *)(pspi->nodes))

static int PROM_TEXT no_nextnode(int node)
{
	/* get physical address */
	struct leon_prom_info *pspi =
	    (void *)(CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	if (nodes[node].level == nodes[node + 1].level)
		return node + 1;
	return -1;
}

static int PROM_TEXT no_child(int node)
{
	/* get physical address */
	struct leon_prom_info *pspi = (struct leon_prom_info *)
	    (CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	if (nodes[node].level == nodes[node + 1].level - 1)
		return node + 1;
	return -1;
}

static struct property PROM_TEXT *find_property(int node, char *name)
{
	/* get physical address */
	struct leon_prom_info *pspi = (struct leon_prom_info *)
	    (CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	struct property *prop = &nodes[node].properties[0];
	while (prop && prop->name) {
		if (pspi->reloc_funcs.strcmp(prop->name, name) == 0)
			return prop;
		prop++;
	}
	return NULL;
}

static int PROM_TEXT no_proplen(int node, char *name)
{
	/* get physical address */
	struct leon_prom_info *pspi = (struct leon_prom_info *)
	    (CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	struct property *prop = pspi->reloc_funcs.find_property(node, name);
	if (prop)
		return prop->length;
	return -1;
}

static int PROM_TEXT no_getprop(int node, char *name, char *value)
{
	/* get physical address */
	struct leon_prom_info *pspi = (struct leon_prom_info *)
	    (CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	struct property *prop = pspi->reloc_funcs.find_property(node, name);
	if (prop) {
		pspi->reloc_funcs.memcpy(value, prop->value, prop->length);
		return 1;
	}
	return -1;
}

static int PROM_TEXT no_setprop(int node, char *name, char *value, int len)
{
	return -1;
}

static char PROM_TEXT *no_nextprop(int node, char *name)
{
	/* get physical address */
	struct leon_prom_info *pspi = (struct leon_prom_info *)
	    (CONFIG_SYS_PROM_OFFSET + sizeof(srmmu_tables));
	struct property *prop;

	/* convert into virtual address */
	pspi = (struct leon_prom_info *)
	    (((unsigned int)pspi & 0x0fffffff) | PAGE_OFFSET);

	if (!name || !name[0])
		return nodes[node].properties[0].name;

	prop = pspi->reloc_funcs.find_property(node, name);
	if (prop)
		return prop[1].name;
	return NULL;
}

static int PROM_TEXT leon_strcmp(const char *s1, const char *s2)
{
	register char result;

	while (1) {
		result = *s1 - *s2;
		if (result || !*s1)
			break;
		s2++;
		s1++;
	}

	return result;
}

static void *PROM_TEXT leon_memcpy(void *dest, const void *src, size_t n)
{
	char *dst = (char *)dest, *source = (char *)src;

	while (n--) {
		*dst = *source;
		dst++;
		source++;
	}
	return dest;
}

#define GETREGSP(sp) __asm__ __volatile__("mov %%sp, %0" : "=r" (sp))

void leon_prom_init(struct leon_prom_info *pspi)
{
	unsigned long i;
	unsigned char cksum, *ptr;
	char *addr_str, *end;
	unsigned long sp;
	GETREGSP(sp);

	pspi->freq_khz = CONFIG_SYS_CLK_FREQ / 1000;

	/* Set Available main memory size */
	pspi->totphys.num_bytes = CONFIG_SYS_PROM_OFFSET - CONFIG_SYS_SDRAM_BASE;
	pspi->avail.num_bytes = pspi->totphys.num_bytes;

#undef nodes
	pspi->nodes[3].level = -1;
	pspi->nodes[3].properties = __va(spi.root_properties + 3);

	/* Set Ethernet MAC address from environment */
	if ((addr_str = getenv("ethaddr")) != NULL) {
		for (i = 0; i < 6; i++) {
			pspi->idprom.id_ethaddr[i] = addr_str ?
			    simple_strtoul(addr_str, &end, 16) : 0;
			if (addr_str) {
				addr_str = (*end) ? end + 1 : end;
			}
		}
	} else {
		/* HW Address not found in environment,
		 * Set default HW address
		 */
		pspi->idprom.id_ethaddr[0] = 0;
		pspi->idprom.id_ethaddr[1] = 0;
		pspi->idprom.id_ethaddr[2] = 0;
		pspi->idprom.id_ethaddr[3] = 0;
		pspi->idprom.id_ethaddr[4] = 0;
		pspi->idprom.id_ethaddr[5] = 0;
	}

	ptr = (unsigned char *)&pspi->idprom;
	for (i = cksum = 0; i <= 0x0E; i++)
		cksum ^= *ptr++;
	pspi->idprom.id_cksum = cksum;
}

static inline void set_cache(unsigned long regval)
{
	asm volatile ("sta %0, [%%g0] %1\n\t":: "r" (regval), "i"(2):"memory");
}

extern unsigned short bss_start, bss_end;

/* mark as section .img.main.text, to be referenced in linker script */
int prom_init(void)
{
	struct leon_prom_info *pspi = (void *)
	    ((((unsigned int)&spi) & PROM_SIZE_MASK) + CONFIG_SYS_PROM_OFFSET);

	/* disable mmu */
	srmmu_set_mmureg(0x00000000);
	__asm__ __volatile__("flush\n\t");

	/* init prom info struct */
	leon_prom_init(pspi);

	kernel_arg_promvec = &pspi->romvec;
#ifdef PRINT_ROM_VEC
	printf("Kernel rom vec: 0x%lx\n", (unsigned int)(&pspi->romvec));
#endif
	return 0;
}

/* Copy current kernel boot argument to ROMvec */
void prepare_bootargs(char *bootargs)
{
	struct leon_prom_info *pspi;
	char *src, *dst;
	int left;

	/* if no bootargs set, skip copying ==> default bootline */
	if (bootargs && (*bootargs != '\0')) {
		pspi = (void *)((((unsigned int)&spi) & PROM_SIZE_MASK) +
				CONFIG_SYS_PROM_OFFSET);
		src = bootargs;
		dst = &pspi->arg[0];
		left = 255;	/* max len */
		while (*src && left > 0) {
			*dst++ = *src++;
			left--;
		}
		/* terminate kernel command line string */
		*dst = 0;
	}
}

void srmmu_init_cpu(unsigned int entry)
{
	sparc_srmmu_setup *psrmmu_tables = (void *)
	    ((((unsigned int)&srmmu_tables) & PROM_SIZE_MASK) +
	     CONFIG_SYS_PROM_OFFSET);

	/* Make context 0 (kernel's context) point
	 * to our prepared memory mapping
	 */
#define PTD 1
	psrmmu_tables->ctx_table[0] =
	    ((unsigned int)&psrmmu_tables->pgd_table[0x00]) >> 4 | PTD;

	/* Set virtual kernel address 0xf0000000
	 * to SRAM/SDRAM address.
	 * Make it READ/WRITE/EXEC to SuperUser
	 */
#define PTE 2
#define ACC_SU_ALL 0x1c
	psrmmu_tables->pgd_table[0xf0] =
	    (CONFIG_SYS_SDRAM_BASE >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf1] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x1000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf2] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x2000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf3] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x3000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf4] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x4000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf5] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x5000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf6] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x6000000) >> 4) | ACC_SU_ALL | PTE;
	psrmmu_tables->pgd_table[0xf7] =
	    ((CONFIG_SYS_SDRAM_BASE + 0x7000000) >> 4) | ACC_SU_ALL | PTE;

	/* convert rom vec pointer to virtual address */
	kernel_arg_promvec = (struct linux_romvec *)
	    (((unsigned int)kernel_arg_promvec & 0x0fffffff) | 0xf0000000);

	/* Set Context pointer to point to context table
	 * 256 contexts supported.
	 */
	srmmu_set_ctable_ptr((unsigned int)&psrmmu_tables->ctx_table[0]);

	/* Set kernel's context, context zero */
	srmmu_set_context(0);

	/* Invalidate all Cache */
	__asm__ __volatile__("flush\n\t");

	srmmu_set_mmureg(0x00000001);
	leon_flush_tlb_all();
	leon_flush_cache_all();
}
