/*
 * hal_remote.c - implementation of Generic Remote Access API for TSAR-MIPS32
 * 
 * Authors : Mohammed Karaoui (2015)
 *           Alain Greiner    (2016)
 *
 * Copyright (c) UPMC Sorbonne Universites
 * 
 * This file is part of ALMOS-MKH..
 *
 * ALMOS-MKH. 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; version 2.0 of the License.
 *
 * ALMOS-MKH. 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 ALMOS-MKH.; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <hal_kernel_types.h>
#include <hal_irqmask.h>
#include <hal_remote.h>

////////////////////////////////
void hal_remote_sb( xptr_t   xp,
                    uint8_t  data )
{
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR      */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked   */
        "mtc0   $13,    $12          \n"  /* IRQ disabled       */
        
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */   
        "mtc2   %2,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "sb     %0,     0(%1)        \n"  /* *paddr <= value    */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   

        "mtc0   $14,    $12          \n"  /* SR restored        */

        "sync                        \n"
        ".set reorder                \n"
        : : "r" (data), "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );
}

/////////////////////////////////
void hal_remote_s32( xptr_t    xp,
                    uint32_t  data )
{
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR      */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked   */
        "mtc0   $13,    $12          \n"  /* IRQ disabled       */

        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */   
        "mtc2   %2,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "sw     %0,     0(%1)        \n"  /* *paddr <= value    */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   

        "mtc0   $14,    $12          \n"  /* SR restored        */

        "sync                        \n"
        ".set reorder                \n"
        : : "r" (data), "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );
}

//////////////////////////////////
void hal_remote_s64( xptr_t    xp,
                     uint64_t  data )
{
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    uint32_t data_lsb = (uint32_t)data;
    uint32_t data_msb = (uint32_t)(data>>32);

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR      */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked   */
        "mtc0   $13,    $12          \n"  /* IRQ disabled       */

        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */   
        "mtc2   %3,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "sw     %0,     0(%2)        \n"  /* *paddr <= lsb      */
        "sw     %1,     4(%2)        \n"  /* *(paddr+4) <= msb  */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   

        "mtc0   $14,    $12          \n"  /* SR restored        */

        "sync                        \n"
        ".set reorder                \n"
        : : "r" (data_lsb), "r" (data_msb), "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );
}

////////////////////////////////////
void hal_remote_spt( xptr_t      xp,
                     void *      pt )
{
    hal_remote_s32 ( xp , (uint32_t)pt );
}

////////////////////////////////
uint8_t hal_remote_lb( xptr_t  xp )
{
	char     data;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR      */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked   */
        "mtc0   $13,    $12          \n"  /* IRQ disabled       */

        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */ 
        "mtc2   %2,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "lb     %0,     0(%1)        \n"  /* data <= *paddr     */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   

        "mtc0   $14,    $12          \n"  /* SR restored        */
        ".set reorder                \n"
        : "=r" (data) : "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );

	return ( data );
}

////////////////////////////////////
uint32_t hal_remote_l32( xptr_t  xp )
{
	uint32_t data;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR      */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked   */
        "mtc0   $13,    $12          \n"  /* IRQ disabled       */

        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */ 
        "mtc2   %2,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "lw     %0,     0(%1)        \n"  /* data <= *paddr     */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   

        "mtc0   $14,    $12          \n"  /* SR restored        */
        ".set reorder                \n"
        : "=r" (data) : "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );

    return ( data );
}

/////////////////////////////////////
uint64_t hal_remote_l64( xptr_t  xp )
{
    uint32_t data_lsb;
    uint32_t data_msb;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    asm volatile( 
        ".set noreorder              \n"
        "mfc0   $14,    $12          \n"  /* $14 <= CP0_SR        */
        "srl    $13,    $14,    1    \n" 
        "sll    $13,    $13,    1    \n"  /* $13 <= SR masked     */
        "mtc0   $13,    $12          \n"  /* IRQ disabled         */

        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
        "mtc2   %3,     $24          \n"  /* PADDR_EXT <= cxy     */   
        "lw     %0,     0(%2)        \n"  /* data_lsb <= *paddr   */
        "lw     %1,     4(%2)        \n"  /* data_msb <= *paddr+4 */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   

        "mtc0   $14,    $12          \n"  /* SR restored          */
        ".set reorder                \n"
        : "=r" (data_lsb), "=r"(data_msb) : "r" (ptr), "r" (cxy) : "$13", "$14", "$15" );

    return ( (((uint64_t)data_msb)<<32) + (((uint64_t)data_lsb)) );

}

////////////////////////////////////
void * hal_remote_lpt( xptr_t    xp )
{
    return (void *)hal_remote_l32 ( xp );
}

///////////////////////////////////////////
bool_t hal_remote_atomic_cas( xptr_t    xp,
                              uint32_t  old,
                              uint32_t  new )
{
    uint32_t save_sr;
	bool_t   isAtomic;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    hal_disable_irq( &save_sr );

    asm volatile( 
        ".set noreorder              \n"
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT   */ 
        "mtc2   %4,     $24          \n"  /* PADDR_EXT <= cxy   */   
        "or     $8,     $0,    %3    \n"  /* $8 <= new          */
        "ll     $3,    0(%1)         \n"  /* $3 <= *ptr         */
        "bne    $3,     %2,    1f    \n"  /* if ($3 != old)     */
        "li     $7,     0            \n"  /* $7 <= 0            */
        "sc     $8,     (%1)         \n"  /* *ptr <= new        */
        "or     $7,     $8,    $0    \n"  /* $7 <= atomic       */
        "sync                        \n"
        "1:                          \n"
        "or     %0,     $7,    $0    \n"  /* isAtomic <= $7     */
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15   */   
        ".set reorder                \n"
        : "=&r" (isAtomic) : "r" (ptr), "r" (old) , "r" (new), "r" (cxy) 
		: "$3", "$7", "$8", "$15" );

    hal_restore_irq( save_sr );

	return isAtomic;

}  // end hal_remote_atomic_cas()

////////////////////////////////////////////
uint32_t hal_remote_atomic_add( xptr_t   xp, 
                                uint32_t incr )
{	
    uint32_t save_sr;
	uint32_t current;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    hal_disable_irq( &save_sr );

    asm volatile( 
        ".set noreorder              \n"
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
        "mtc2   %3,     $24          \n"  /* PADDR_EXT <= cxy     */   
        "1:                          \n"
        "ll     %0,     (%1)         \n"  /* current <= *paddr    */
        "addu   $3,     %0,     %2   \n"  /* $3 <= current + incr */
        "sc     $3,     (%1)         \n"  /* *paddr <= $3         */
        "beq    $3,     $0,     1b   \n"  /* retry if failure     */
        "nop                         \n"
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
        ".set reorder                \n"
		: "=&r" (current) : "r" (ptr), "r" (incr), "r" (cxy) : "$3", "$15" );

    hal_restore_irq( save_sr );

	return current;

}  // end hal_remote_atomic_add()

////////////////////////////////////////////
uint32_t hal_remote_atomic_and( xptr_t   xp, 
                                uint32_t mask )
{	
    uint32_t save_sr;
	uint32_t current;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    hal_disable_irq( &save_sr );

    asm volatile( 
        ".set noreorder              \n"
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
        "mtc2   %3,     $24          \n"  /* PADDR_EXT <= cxy     */   
        "1:                          \n"
        "ll     %0,     (%1)         \n"  /* current <= *paddr    */
        "and    $3,     %0,     %2   \n"  /* $3 <= current & mask */
        "sc     $3,     (%1)         \n"  /* *paddr <= $3         */
        "beq    $3,     $0,     1b   \n"  /* retry if failure     */
        "nop                         \n"
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
        ".set reorder                \n"
	    : "=&r" (current) : "r" (ptr), "r" (mask), "r" (cxy) : "$3", "$15" );

    hal_restore_irq( save_sr );

	return current;

}  // end hal_remote_atomic_and()

////////////////////////////////////////////
uint32_t hal_remote_atomic_or( xptr_t   xp, 
                               uint32_t mask )
{	
    uint32_t save_sr;
	uint32_t current;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    hal_disable_irq( &save_sr );

    asm volatile( 
        ".set noreorder              \n"
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
        "mtc2   %3,     $24          \n"  /* PADDR_EXT <= cxy     */   
        "1:                          \n"
        "ll     %0,     (%1)         \n"  /* current <= *paddr    */
        "or     $3,     %0,     %2   \n"  /* $3 <= current | mask */
        "sc     $3,     (%1)         \n"  /* *paddr <= $3         */
        "beq    $3,     $0,     1b   \n"  /* retry if failure     */
        "nop                         \n"
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
        ".set reorder                \n"
	    : "=&r" (current) : "r" (ptr), "r" (mask), "r" (cxy) : "$3", "$15" );

    hal_restore_irq( save_sr );

	return current;

}  // end hal_remote_atomic_or()

/////////////////////////////////////////////////
error_t hal_remote_atomic_try_add( xptr_t     xp,
                                   uint32_t   incr,
                                   uint32_t * old )
{
    uint32_t save_sr;
	uint32_t current;
	error_t  error;
    uint32_t ptr = (uint32_t)GET_PTR( xp );
    uint32_t cxy = (uint32_t)GET_CXY( xp );

    hal_disable_irq( &save_sr );

    asm volatile( 
        ".set noreorder              \n"
        "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
        "mtc2   %4,     $24          \n"  /* PADDR_EXT <= cxy     */   
        "ll     %0,     (%2)         \n"  /* current <= *paddr    */
        "addu   $3,     %0,     %3   \n"  /* $3 <= current + incr */
        "sc     $3,     (%2)         \n"  /* *paddr <= $3         */
        "beq    $3,     $0,     1f   \n"  /* exit if failure      */
        "ori    %1,     $0,      1   \n"  /* fail: ret <= 1       */
        "and    %1,     $0,     $0   \n"  /* success: ret <= 0    */
        "1:                          \n"
        "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
        ".set reorder                \n"
		: "=&r" (current), "=&r" (error) : "r" (ptr), "r" (incr), "r" (cxy) : "$3", "$15" );
		
    hal_restore_irq( save_sr );

    *old = current;
		
	return error;

}  // end hal_remote_atomic_try_add()

/////////////////////////////////////
void hal_remote_memcpy( xptr_t   dst,
                        xptr_t   src,
                        uint32_t size )
{
    uint32_t save_sr;
	uint32_t i;
	uint32_t wsize;
    uint32_t dptr = (uint32_t)GET_PTR( dst );
    uint32_t dcxy = (uint32_t)GET_CXY( dst );
    uint32_t sptr = (uint32_t)GET_PTR( src );
    uint32_t scxy = (uint32_t)GET_CXY( src );

    hal_disable_irq( &save_sr );

	if( (dptr & 0x3) || (sptr & 0x3) ) wsize = 0;  // do it all in bytes
    else                               wsize = size >> 2;

	for( i = 0 ; i < wsize ; i++ )          // transfer one word per iteration
	{
        asm volatile( 
            ".set noreorder              \n"
            "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
            "mtc2   %0,     $24          \n"  /* PADDR_EXT <= scxy    */   
		    "lw     $3,     0(%1)	     \n"  /* $3 <= *src           */				
            "mtc2   %2,     $24          \n"  /* PADDR_EXT <= dcxy    */	
            "sw     $3,     0(%3)        \n"  /* *dst <= $3           */
            "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
            ".set reorder                \n"
		    : : "r"(scxy), "r" (sptr+(i<<2)), "r"(dcxy), "r" (dptr+(i<<2)) : "$3", "$15" );		
	}

	for( i = wsize << 2 ; i < size ; i++ )  // transfer one byte per iteration
	{
        asm volatile( 
            ".set noreorder              \n"
            "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
            "mtc2   %0,     $24          \n"  /* PADDR_EXT <= scxy    */   
		    "lb 	$3,     0(%1)	     \n"  /* $3 <= *src           */				
            "mtc2   %2,     $24          \n"  /* PADDR_EXT <= dcxy    */	
            "sb     $3,     0(%3)        \n"  /* *dst <= $3           */
            "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
            ".set reorder                \n"
		    : : "r"(scxy), "r" (sptr+i), "r"(dcxy), "r" (dptr+i) : "$3", "$15" );		
	}

    hal_restore_irq( save_sr );

}  // end hal_remote_memcpy()

///////////////////////////////////
void hal_remote_strcpy( xptr_t dst,
                        xptr_t src )
{
    uint32_t save_sr;
    uint32_t dptr = (uint32_t)GET_PTR( dst );
    uint32_t dcxy = (uint32_t)GET_CXY( dst );
    uint32_t sptr = (uint32_t)GET_PTR( src );
    uint32_t scxy = (uint32_t)GET_CXY( src );

    hal_disable_irq( &save_sr );

    // loop on characters while non NUL 
    asm volatile(
        "mfc2   $15,   $24          \n"   /* $15 <= PADDR_EXT               */
        "1:                         \n"   /* loop entry                     */
        "mtc2   %0,    $24          \n"   /* PADDR_EXT <= scxy              */   
        "lb	    $13,   0(%1)        \n"   /* read char from src string      */
        "mtc2   %2,    $24          \n"   /* PADDR_EXT <= dcxy              */   
	    "sb	    $13,   0(%3)        \n"   /* store char to dst string       */
        "addi   %1,    %1,  1       \n"   /* increment sptr pointer         */
        "addi   %3,    %3,  1       \n"   /* increment dptr pointer         */
        "bne    $13,   $0,  1b      \n"   /* test NUL                       */
        "nop                        \n"
        "mtc2   $15,   $24          \n"   /* PADDR_EXT <= $15               */   
        : : "r"(scxy), "r"(sptr), "r"(dcxy), "r"(dptr) : "$13","$15", "memory" );

    hal_restore_irq( save_sr );

} // end hal_remote_strcpy()

////////////////////////////////////////
void hal_remote_memset( xptr_t   buf_xp,
                        uint8_t  byte,
                        uint32_t size )
{
    uint32_t save_sr;
	uint32_t i;
	uint32_t wsize;
    uint32_t buf_ptr = (uint32_t)GET_PTR( buf_xp );
    uint32_t buf_cxy = (uint32_t)GET_CXY( buf_xp );
    uint32_t word    = ((uint32_t)byte)<<24 |
                       ((uint32_t)byte)<<16 |
                       ((uint32_t)byte)<<8  |
                       ((uint32_t)byte)     ;

    hal_disable_irq( &save_sr );

	if( (buf_ptr & 0x3) ) wsize = 0;  // do it all in bytes
    else                  wsize = size >> 2;

	for( i = 0 ; i < wsize ; i++ )          // transfer one word per iteration
	{
        asm volatile( 
            ".set noreorder              \n"
            "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
            "mtc2   %0,     $24          \n"  /* PADDR_EXT <= buf_cxy */   
            "sw     %2,     0(%1)        \n"  /* set one word         */
            "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
            ".set reorder                \n"
		    : : "r"(buf_cxy), "r" (buf_ptr+(i<<2)), "r"(byte) : "$15" );		
	}

	for( i = wsize << 2 ; i < size ; i++ )  // transfer one byte per iteration
	{
        asm volatile( 
            ".set noreorder              \n"
            "mfc2   $15,    $24          \n"  /* $15 <= PADDR_EXT     */ 
            "mtc2   %0,     $24          \n"  /* PADDR_EXT <= buf_cxy */   
            "sb     %2,     0(%1)        \n"  /* set one byte         */
            "mtc2   $15,    $24          \n"  /* PADDR_EXT <= $15     */   
            ".set reorder                \n"
		    : : "r"(buf_cxy), "r"(buf_ptr+i), "r"(word) : "$15" );		
	}

    hal_restore_irq( save_sr );

}  // end hal_remote_memset()

