/*
 * U-boot - cpu.c CPU specific functions
 *
 * Copyright (c) 2005-2007 Analog Devices Inc.
 *
 * (C) Copyright 2000-2004
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 *
 * 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., 51 Franklin St, Fifth Floor, Boston,
 * MA 02110-1301 USA
 */

#include <common.h>
#include <asm/blackfin.h>
#include <command.h>
#include <asm/entry.h>
#include <asm/cplb.h>
#include <asm/io.h>

#define CACHE_ON 1
#define CACHE_OFF 0

extern unsigned int icplb_table[page_descriptor_table_size][2];
extern unsigned int dcplb_table[page_descriptor_table_size][2];

int do_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{
	__asm__ __volatile__("cli r3;" "P0 = %0;" "JUMP (P0);"::"r"(L1_ISRAM)
	    );

	return 0;
}

/* These functions are just used to satisfy the linker */
int cpu_init(void)
{
	return 0;
}

int cleanup_before_linux(void)
{
	return 0;
}

void icache_enable(void)
{
	unsigned int *I0, *I1;
	int i, j = 0;

	if ((*pCHIPID >> 28) < 2)
		return;

	/* Before enable icache, disable it first */
	icache_disable();
	I0 = (unsigned int *)ICPLB_ADDR0;
	I1 = (unsigned int *)ICPLB_DATA0;

	/* make sure the locked ones go in first */
	for (i = 0; i < page_descriptor_table_size; i++) {
		if (CPLB_LOCK & icplb_table[i][1]) {
			debug("adding %02i %02i 0x%08x 0x%08x\n", i, j,
				 icplb_table[i][0], icplb_table[i][1]);
			*I0++ = icplb_table[i][0];
			*I1++ = icplb_table[i][1];
			j++;
		}
	}

	for (i = 0; i < page_descriptor_table_size; i++) {
		if (!(CPLB_LOCK & icplb_table[i][1])) {
			debug("adding %02i %02i 0x%08x 0x%08x\n", i, j,
				 icplb_table[i][0], icplb_table[i][1]);
			*I0++ = icplb_table[i][0];
			*I1++ = icplb_table[i][1];
			j++;
			if (j == 16) {
				break;
			}
		}
	}

	/* Fill the rest with invalid entry */
	if (j <= 15) {
		for (; j < 16; j++) {
			debug("filling %i with 0", j);
			*I1++ = 0x0;
		}

	}

	cli();
	sync();
	asm(" .align 8; ");
	*(unsigned int *)IMEM_CONTROL = IMC | ENICPLB;
	sync();
	sti();
}

void icache_disable(void)
{
	if ((*pCHIPID >> 28) < 2)
		return;
	cli();
	sync();
	asm(" .align 8; ");
	*(unsigned int *)IMEM_CONTROL &= ~(IMC | ENICPLB);
	sync();
	sti();
}

int icache_status(void)
{
	unsigned int value;
	value = *(unsigned int *)IMEM_CONTROL;

	if (value & (IMC | ENICPLB))
		return CACHE_ON;
	else
		return CACHE_OFF;
}

void dcache_enable(void)
{
	unsigned int *I0, *I1;
	unsigned int temp;
	int i, j = 0;

	/* Before enable dcache, disable it first */
	dcache_disable();
	I0 = (unsigned int *)DCPLB_ADDR0;
	I1 = (unsigned int *)DCPLB_DATA0;

	/* make sure the locked ones go in first */
	for (i = 0; i < page_descriptor_table_size; i++) {
		if (CPLB_LOCK & dcplb_table[i][1]) {
			debug("adding %02i %02i 0x%08x 0x%08x\n", i, j,
				 dcplb_table[i][0], dcplb_table[i][1]);
			*I0++ = dcplb_table[i][0];
			*I1++ = dcplb_table[i][1];
			j++;
		} else {
			debug("skip   %02i %02i 0x%08x 0x%08x\n", i, j,
				 dcplb_table[i][0], dcplb_table[i][1]);
		}
	}

	for (i = 0; i < page_descriptor_table_size; i++) {
		if (!(CPLB_LOCK & dcplb_table[i][1])) {
			debug("adding %02i %02i 0x%08x 0x%08x\n", i, j,
				 dcplb_table[i][0], dcplb_table[i][1]);
			*I0++ = dcplb_table[i][0];
			*I1++ = dcplb_table[i][1];
			j++;
			if (j == 16) {
				break;
			}
		}
	}

	/* Fill the rest with invalid entry */
	if (j <= 15) {
		for (; j < 16; j++) {
			debug("filling %i with 0", j);
			*I1++ = 0x0;
		}
	}

	cli();
	temp = *(unsigned int *)DMEM_CONTROL;
	sync();
	asm(" .align 8; ");
	*(unsigned int *)DMEM_CONTROL =
	    ACACHE_BCACHE | ENDCPLB | PORT_PREF0 | temp;
	sync();
	sti();
}

void dcache_disable(void)
{
	unsigned int *I0, *I1;
	int i;

	cli();
	sync();
	asm(" .align 8; ");
	*(unsigned int *)DMEM_CONTROL &=
	    ~(ACACHE_BCACHE | ENDCPLB | PORT_PREF0);
	sync();
	sti();

	/* after disable dcache,
	 * clear it so we don't confuse the next application
	 */
	I0 = (unsigned int *)DCPLB_ADDR0;
	I1 = (unsigned int *)DCPLB_DATA0;

	for (i = 0; i < 16; i++) {
		*I0++ = 0x0;
		*I1++ = 0x0;
	}
}

int dcache_status(void)
{
	unsigned int value;
	value = *(unsigned int *)DMEM_CONTROL;

	if (value & (ENDCPLB))
		return CACHE_ON;
	else
		return CACHE_OFF;
}
