/*
 * boot_tty_driver.c - TSAR bootloader TTY driver implementation.
 * 
 * Authors :   Alain Greiner / Vu Son  (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 <boot_config.h>
#include <boot_tty_driver.h>
#include <hal_kernel_types.h>
#include <boot_utils.h>

#ifndef SEG_TXT_BASE
# error "The SEG_TXT_BASE value should be defined in the 'hard_config.h' file"
#endif

#ifndef X_IO  
# error "The X_IO value should be defined in the 'hard_config.h' file"
#endif

#ifndef Y_IO  
# error "The Y_IO value should be defined in the 'hard_config.h' file"
#endif

#ifndef Y_WIDTH
# error "The Y_WIDTH value should be defined in the 'hard_config.h' file"
#endif

#ifdef IS_LETI
  #define TARGET_CLUSTER    0
#else
  #define TARGET_CLUSTER    ((X_IO << Y_WIDTH) + Y_IO)
#endif


/////////////////////////////////////////////////////////////////////////////
// This function returns the value contained in a TTY0 register. 
// @ reg    : register to be read. 
// @ returns the value stored in 'reg'.
/////////////////////////////////////////////////////////////////////////////
static uint32_t boot_tty_get_register( uint32_t reg )
{
    cxy_t      cxy = TARGET_CLUSTER;
    uint32_t * ptr = (uint32_t *)SEG_TXT_BASE + reg;
    
    return boot_remote_lw( XPTR( cxy , ptr ) ); 
    
} // boot_tty_get_register()

/////////////////////////////////////////////////////////////////////////////
// This function sets a new value to a TTY0 register.
// @ reg    : register to be configured.
// @ val    : new value to be written to 'reg'.
/////////////////////////////////////////////////////////////////////////////
static void boot_tty_set_register( uint32_t reg, 
                                   uint32_t val )
{
    cxy_t      cxy = TARGET_CLUSTER;
    uint32_t * ptr = (uint32_t *)SEG_TXT_BASE + reg;

    boot_remote_sw( XPTR( cxy , ptr ) , val ); 

} // boot_tty_set_register()

//////////////////////////////////
int boot_tty_write( const char * buf,
                    uint32_t     nbytes )
{
    uint32_t nb_printed;
    uint32_t nb_test;
    uint32_t error; 

    // Print nbytes to TTY0 terminal
    for (nb_printed = 0; nb_printed < nbytes; nb_printed++)
    {
        // Poll the TTY0 status.
        if ((boot_tty_get_register(TTY_STATUS_REG) & TTY_STATUS_TX_FULL))
        {
            error = 1;
            for (nb_test = 0; nb_test < 10000; nb_test++)
            {
                if ((boot_tty_get_register(TTY_STATUS_REG) & TTY_STATUS_TX_FULL) == 0)
                {
                    error = 0;
                    break;
                }
            }

            // Report error after 10000 retries.
            if (error) return -1;
        }

        // Write one character to TTY0 terminal.
        // Special treatment for a newline: Carriage Return before Line Feed.
        if (buf[nb_printed] == '\n') boot_tty_set_register(TTY_WRITE_REG , (uint32_t)'\r');
        boot_tty_set_register(TTY_WRITE_REG , (uint32_t)buf[nb_printed]);
    }

    return 0;

} // boot_tty_write()
