#ident "kit/ATMRT/sbdiram.c: Copyright (c) MIPS Technologies, Inc. All rights reserved."

/*
 * Copyright (c) 1996-2003 MIPS Technologies, Inc.
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 *      * Redistributions of source code must retain the above copyright
 *        notice, this list of conditions and the following disclaimer.
 *      * Redistributions in binary form must reproduce the above
 *      copyright
 *        notice, this list of conditions and the following disclaimer
 *        in the documentation and/or other materials provided with
 *        the distribution.
 *      * Neither the name of MIPS Technologies Inc. nor the names of its
 *        contributors may be used to endorse or promote products derived
 *        from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/* 
 * sbdiram.c: IRAM support LSI ATMizer/RT board
 */


#if !#cpu(l64360)
#error use -Acpu(l64360) option when compiling this file
#endif

#include <sys/types.h>
#include <mips/cpu.h>
#include <mips/endian.h>


/* Low-level cache functions in sbdmisc.sx */
void		_iram_writeword (vaddr_t, unsigned int);
unsigned int	_iram_readword (vaddr_t);


#define BIU_IRAM_WR     (BIU_NOSTR|BIU_LDSCH|BIU_BGNT|BIU_IS1)
			/*
                        DS=0 => Disable D-Cache
                        IS1=1 => Enable I-Cache Set 1
                        IBLKSZ=0 => I-cache block fill size 2 words
                        INTP=0, NOPAD=0, BGNT=1 =>
                        INT[5:0]activeLow,WaitState,EnableBusGrant
                        LDSCH=1 => Enable load scheduling
                        NOSTR=1 => Enable instruction streaming.
                        */

/*
 * Copy n words into IRAM
 */
void
iram_write (vaddr_t dst, unsigned int *src, size_t n)
{
    unsigned long save = BIU;
    BIU = BIU_IRAM_WR;	
    while (n--) {
	_iram_writeword (dst, *src++);
	dst += sizeof (unsigned int);
    }
    BIU = save;

}


/*
 * Copy n words from IRAM
 */
void
iram_read (unsigned int *dst, vaddr_t src, size_t n)
{
    unsigned long save = BIU;
    BIU = BIU_IRAM_WR;
    while (n--) {
	*dst++ = _iram_readword (src);
	src += sizeof (unsigned int);
    }
    BIU = save;
}


/*
 * Implementations of mips_get_*() and mips_put_*() which will
 * handle reads and writes to host port, secondary port and IRAM.
 */

unsigned int
_iram_get (void *addr, size_t size, int *err)
{
    unsigned int offset = ((unsigned)addr & 3);
    unsigned int seg, v;

    seg = (unsigned) addr >> 20;
    if ((offset & (size - 1)) || size > 4 
	|| (!IS_IRAM (addr)
	    && !(seg >= 0x008 && seg <= 0x00f)	/* cacheable host/sec port */
	    && !(seg >= 0xa08 && seg <= 0xa0f)	/* uncacheable host/sec port */
	    && !(seg >= 0xfff))) {	  	/* internal devices */
	/* invalid address */
	*err = 1;
	return 0;
    }

    addr -= offset; 
    if (IS_IRAM (addr)) {
	unsigned long save = BIU;
	BIU = BIU_IRAM_WR;
	v = _iram_readword ((vaddr_t) addr);
	BIU = save;
    } 
    else {
	v = *(unsigned int *)addr;
    }

    
    if (size < sizeof (unsigned int)) {
#if BYTE_ORDER == BIG_ENDIAN
	v >>= 32 - 8 * (offset + size);
#else
        v >>= 8 * offset;
#endif
	v &= (0x1 << (size * 8)) - 1;
    }

    return v;
}


int
_iram_put (void *addr, size_t size, unsigned int val)
{
    unsigned int offset = ((unsigned)addr & 3);
    unsigned int seg;

    seg = (unsigned) addr >> 20;
    if ((offset & (size - 1))  || size > 4 
	|| (!IS_IRAM (addr)
	    && !(seg >= 0x008 && seg <= 0x00f)	/* cacheable host/sec port */
	    && !(seg >= 0xa08 && seg <= 0xa0f)	/* uncacheable host/sec port */
	    && !(seg >= 0xfff))) {	  	/* internal devices */
	/* invalid address */
	return 1;
    }

    addr -= offset;
    if (size < sizeof (unsigned int)) {
#if BYTE_ORDER == BIG_ENDIAN
	unsigned int shift = 32 - 8 * (offset + size); 
#else
	unsigned int shift = 8 * offset;
#endif
	unsigned int mask = (0x1 << (size * 8)) - 1;
	unsigned int v;

	if (IS_IRAM (addr)) {
	    unsigned long save = BIU;
	    BIU = BIU_IRAM_WR;
	    v = _iram_readword ((vaddr_t) addr);
	    BIU = save;
	}
	else {
	    v = *(unsigned int *)addr;
	}
    
	/* shift mask and new value to correct position in word */
	val <<= shift;
	mask <<= shift;

	/* do the funky bit-field insert */
	val = v ^ ((v ^ val) & mask);
    }

    if (IS_IRAM (addr)) {
	unsigned long save = BIU;
	BIU = BIU_IRAM_WR;
	_iram_writeword ((vaddr_t) addr, val);
	BIU = save;
    } 
    else {
	*(unsigned int *)addr = val;
    }	

    return 0;
}
