/*==========================================================================
//
//      lm3s_misc.c
//
//      Cortex-M LM3S HAL functions
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 2008, 2009 Free Software Foundation, Inc.                        
//
// eCos 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 or (at your option) any later      
// version.                                                                 
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,    
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
//
// As a special exception, if other files instantiate templates or use      
// macros or inline functions from this file, or you compile this file      
// and link it with other works to produce a work based on this file,       
// this file does not by itself cause the resulting work to be covered by   
// the GNU General Public License. However the source code for this file    
// must still be made available in accordance with section (3) of the GNU   
// General Public License v2.                                               
//
// This exception does not invalidate any other reasons why a work based    
// on this file might be covered by the GNU General Public License.         
// -------------------------------------------                              
// ####ECOSGPLCOPYRIGHTEND####                                              
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    nickg
// Date:         2009-07-30
// Description:  
//
//####DESCRIPTIONEND####
//
//========================================================================*/

#include <pkgconf/hal.h>
#include <pkgconf/hal_cortexm.h>
#include <pkgconf/hal_cortexm_lm3s.h>
#ifdef CYGPKG_KERNEL
#include <pkgconf/kernel.h>
#endif

#include <cyg/infra/diag.h>
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_trac.h>         // tracing macros
#include <cyg/infra/cyg_ass.h>          // assertion macros

#include <cyg/hal/hal_arch.h>           // HAL header
#include <cyg/hal/hal_intr.h>           // HAL header
#include <cyg/hal/hal_if.h>             // HAL header

//==========================================================================
// Clock Initialization values

#if defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_INT)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_INT
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS CYGHWR_HAL_LM3S_SYSTEM_RCC_MOSCDIS
#define CYGHWR_HAL_LM3S_INPUT_CLOCK 12000000
#elif defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_INTby4)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_INT_4
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS CYGHWR_HAL_LM3S_SYSTEM_RCC_MOSCDIS
#define CYGHWR_HAL_LM3S_INPUT_CLOCK 3000000
#elif defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_MAIN)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_MAIN
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS CYGHWR_HAL_LM3S_SYSTEM_RCC_IOSCDIS
#define CYGHWR_HAL_LM3S_INPUT_CLOCK CYGARC_HAL_CORTEXM_LM3S_INPUT_CLOCK
#elif defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_PLL)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE 0
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS 0
#define CYGHWR_HAL_LM3S_INPUT_CLOCK 200000000
#elif defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_30K)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_30K
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS CYGHWR_HAL_LM3S_SYSTEM_RCC_IOSCDIS|CYGHWR_HAL_LM3S_SYSTEM_RCC_MOSCDIS
#define CYGHWR_HAL_LM3S_INPUT_CLOCK 30000
#elif defined(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SOURCE_32K)
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_32K
#define CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS CYGHWR_HAL_LM3S_SYSTEM_RCC_IOSCDIS|CYGHWR_HAL_LM3S_SYSTEM_RCC_MOSCDIS
#define CYGHWR_HAL_LM3S_INPUT_CLOCK 32000
#endif

//==========================================================================
// Clock frequencies
//
// These are set to the frequencies of the various system clocks.

cyg_uint32 hal_lm3s_sysclk;
cyg_uint32 hal_cortexm_systick_clock;

void hal_start_clocks( void );

//==========================================================================

static void init_delay( volatile cyg_uint32 delay )
{
    while( --delay );
}

//==========================================================================

void hal_variant_init( void )
{
    CYG_ADDRESS sys = CYGHWR_HAL_LM3S_SYSTEM;

    // Enable base peripherals in RCGCX registers
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCGC2, 0x0000007F );        

#if !defined(CYG_HAL_STARTUP_RAM)
    hal_start_clocks();
#endif

#ifdef CYGSEM_HAL_VIRTUAL_VECTOR_SUPPORT
    hal_if_init();
#endif
    
}

//==========================================================================
// Setup up system clocks
//
// Set up clocks from configuration. In the future this should be extended so
// that clock rates can be changed at runtime.

void hal_start_clocks( void )
{
    CYG_ADDRESS sys = CYGHWR_HAL_LM3S_SYSTEM;
    cyg_uint32 rcc, rcc2, ris;

    hal_lm3s_sysclk = CYGHWR_HAL_LM3S_INPUT_CLOCK;

    // Switch to an undivided non-PLL clock
    HAL_READ_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC, rcc );
    HAL_READ_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC2, rcc2 );

    rcc |= CYGHWR_HAL_LM3S_SYSTEM_RCC_BYPASS;
    rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_USESYSDIV;
    rcc &= ~(CYGHWR_HAL_LM3S_SYSTEM_RCC_IOSCDIS);
        
    rcc2 |= CYGHWR_HAL_LM3S_SYSTEM_RCC2_BYPASS2;
    rcc2 &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_MASK;
    rcc2 |= CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_INT;
    
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC, rcc );
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC2, rcc2 );

    // Wait for things to stabilize
    init_delay( 50000 );        
        
    // Ensure the ocillators we want are enabled.
    rcc &= ~(CYGHWR_HAL_LM3S_SYSTEM_RCC_IOSCDIS|CYGHWR_HAL_LM3S_SYSTEM_RCC_MOSCDIS);
    rcc |= CYGHWR_HAL_LM3S_SYSTEM_RCC_OSCDIS;
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC, rcc );

    // Wait a while for the oscillator to stabilize
    init_delay( 50000 );

    // Select XTAL value based on main input clock
        
    rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_XTAL_MASK;
            
    if( CYGARC_HAL_CORTEXM_LM3S_INPUT_CLOCK == 8000000 )
        rcc |= CYGHWR_HAL_LM3S_SYSTEM_RCC_XTAL_8;
    // FIXME: add all frequencies
        
            
    if( CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE == 0 )
    {
        // Using PLL for source
            
        rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_PWRDN;
        rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC2_PWRDN2;

        // Write values
        HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC, rcc );
        HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC2, rcc2 );
        
        // Wait for things to settle
        init_delay( 1000 );
            
        // Wait for PLL to lock
        do
        {
            HAL_READ_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RIS, ris );
        } while( ( ris & CYGHWR_HAL_LM3S_SYSTEM_RIS_PLLL ) == 0 );
            
        // Clear bypass to use PLL
        rcc  &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_BYPASS;
        rcc2 &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC2_BYPASS2;
    }
    else
    {
        // Set oscillator source in RCC2
        rcc2 &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC2_OSCSRC2_MASK;
        rcc2 |= CYGHWR_HAL_LM3S_SYSTEM_RCC_SOURCE;
    }

    // Set system divider
    if( CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SYSCLK_DIV != 1 )
    {
        rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_SYSDIV_MASK;            
        rcc |= CYGHWR_HAL_LM3S_SYSTEM_RCC_SYSDIV(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SYSCLK_DIV-1);
        rcc |= CYGHWR_HAL_LM3S_SYSTEM_RCC_USESYSDIV;
        rcc2 &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC2_SYSDIV2_MASK;
        rcc2 |= CYGHWR_HAL_LM3S_SYSTEM_RCC2_SYSDIV2(CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SYSCLK_DIV-1);
        hal_lm3s_sysclk /= CYGHWR_HAL_CORTEXM_LM3S_CLOCK_SYSCLK_DIV;
    }
    else
    {
        // SYSCLK_DIV == 1, disable divider
        rcc &= ~CYGHWR_HAL_LM3S_SYSTEM_RCC_USESYSDIV;            
    }

    // Write final config registers
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC, rcc );
    HAL_WRITE_UINT32( sys+CYGHWR_HAL_LM3S_SYSTEM_RCC2, rcc2 );
            
    // Wait for things to settle
    init_delay( 1000 );

    // Although there is no documentation to the effect, it
    // appears that the SYSTICK timer is fed by SYSCLK/4.
    hal_cortexm_systick_clock = hal_lm3s_sysclk / 4;
    
}

//==========================================================================
// GPIO support
//
// These functions provide configuration and IO for GPIO pins.

__externC void hal_lm3s_gpio_set( cyg_uint32 pin )
{
    cyg_uint32 port = CYGHWR_HAL_LM3S_GPIO_PORT(pin);
    int bit = CYGHWR_HAL_LM3S_GPIO_BIT(pin);
    cyg_uint32 bitmask = 1<<bit;
    cyg_uint32 mode = CYGHWR_HAL_LM3S_GPIO_MODE(pin);
    cyg_uint32 reg;
    
    if( pin == CYGHWR_HAL_LM3S_GPIO_NONE )
        return;

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DIR, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_OUT) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DIR, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_AFSEL, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_ALT) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_AFSEL, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR2R, reg );
    reg &= ~bitmask;
    reg |= ((mode & CYGHWR_HAL_LM3S_GPIO_MODE_DRIVE) == CYGHWR_HAL_LM3S_GPIO_MODE_D2mA) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR2R, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR4R, reg );
    reg &= ~bitmask;
    reg |= ((mode & CYGHWR_HAL_LM3S_GPIO_MODE_DRIVE) == CYGHWR_HAL_LM3S_GPIO_MODE_D4mA) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR4R, reg );
    
    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR8R, reg );
    reg &= ~bitmask;
    reg |= ((mode & CYGHWR_HAL_LM3S_GPIO_MODE_DRIVE) == CYGHWR_HAL_LM3S_GPIO_MODE_D8mA) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DR8R, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_ODR, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_OPENDRAIN) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_ODR, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_PUR, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_PULLUP) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_PUR, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_PDR, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_PULLDOWN) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_PDR, reg );
        
    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_SLR, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_SLEW) ? bitmask : 0;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_SLR, reg );

    HAL_READ_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DEN, reg );
    reg &= ~bitmask;
    reg |= (mode & CYGHWR_HAL_LM3S_GPIO_MODE_ANALOG) ? 0 : bitmask;
    HAL_WRITE_UINT32( port+CYGHWR_HAL_LM3S_GPIO_DEN, reg );
    
}
    
__externC void hal_lm3s_gpio_out( cyg_uint32 pin, int val )
{
    cyg_uint32 port = CYGHWR_HAL_LM3S_GPIO_PORT(pin);
    int bit = CYGHWR_HAL_LM3S_GPIO_BIT(pin);

    port += 4<<bit;

    HAL_WRITE_UINT32( port, (val&1)<<bit );
}
    
__externC void hal_lm3s_gpio_in ( cyg_uint32 pin, int *val )
{
    cyg_uint32 port = CYGHWR_HAL_LM3S_GPIO_PORT(pin);
    int bit = CYGHWR_HAL_LM3S_GPIO_BIT(pin);
    cyg_uint32 data;
    
    port += 4<<bit;

    HAL_READ_UINT32( port, data );

    *val = (data>>bit)&1;
}

//==========================================================================
// Clock support.
//
// These functions provide support for enabling and disabling clock
// control bits.

__externC void hal_lm3s_clock_enable( cyg_uint32 desc )
{
    cyg_uint32 r;
    cyg_uint32 reg = CYGHWR_HAL_LM3S_SYSTEM+CYGHWR_HAL_LM3S_CLOCK_REG(desc);
    HAL_READ_UINT32( reg, r );
    r |= BIT_(CYGHWR_HAL_LM3S_CLOCK_PIN(desc));
    HAL_WRITE_UINT32( reg, r );
}

__externC void hal_lm3s_clock_disable( cyg_uint32 desc )
{
    cyg_uint32 r;
    cyg_uint32 reg = CYGHWR_HAL_LM3S_SYSTEM+CYGHWR_HAL_LM3S_CLOCK_REG(desc);
    HAL_READ_UINT32( reg, r );
    r &= ~BIT_(CYGHWR_HAL_LM3S_CLOCK_PIN(desc));
    HAL_WRITE_UINT32( reg, r );
}

//==========================================================================
// UART baud rate
//
// Set the baud rate divider of a UART based on the requested rate and
// the current SYSCLK clock settings.

void hal_lm3s_uart_setbaud( cyg_uint32 base, cyg_uint32 baud )
{
    cyg_uint32 div = ((( hal_lm3s_sysclk * 8) / baud) + 1) / 2;

    HAL_WRITE_UINT32( base+CYGHWR_HAL_LM3S_UART_IBRD, div / 64 );
    HAL_WRITE_UINT32( base+CYGHWR_HAL_LM3S_UART_FBRD, div % 64 );
}

// start-sanitize-ecospro
//==========================================================================
// Profiling timer

#ifdef CYGPKG_PROFILE_GPROF

#include <cyg/profile/profile.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/drv_api.h>

// -------------------------------------------------------------------------
// These defines select the timer to be used. By default we use
// Timer0.

#define CYGHWR_HAL_LM3S_TIMER                  CYGHWR_HAL_LM3S_TIMER0
#define CYGNUM_HAL_INTERRUPT_TIMER             CYGNUM_HAL_INTERRUPT_TIMER0A
#define CYGHWR_HAL_LM3S_TIMER_CLOCK            CYGHWR_HAL_LM3S_TIMER0_CLOCK

// -------------------------------------------------------------------------
// Interrupt object

static cyg_interrupt    profile_interrupt;
static cyg_handle_t     profile_handle;

// -------------------------------------------------------------------------
// Saved state pointer.
//
// If break or Ctrl-C support is enabled, this is defined
// elsewhere. If not then we must define it ourself.

#if defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT) || \
    defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT)

__externC HAL_SavedRegisters *hal_saved_interrupt_state;

#else

HAL_SavedRegisters *hal_saved_interrupt_state;

#endif

// -------------------------------------------------------------------------
// Profile DSR. This is not actually called.

static  void
profile_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    CYG_UNUSED_PARAM(cyg_vector_t, vector);
    CYG_UNUSED_PARAM(cyg_ucount32, count);
    CYG_UNUSED_PARAM(cyg_addrword_t, data);
}

// -------------------------------------------------------------------------
// Profile ISR.

static cyg_uint32
profile_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    CYG_ADDRESS base = CYGHWR_HAL_LM3S_TIMER;    
    HAL_SavedRegisters *regs = hal_saved_interrupt_state;

    // Clear the interrupt
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_ICR, CYGHWR_HAL_LM3S_TIMER_INT_TATO );

    __profile_hit(regs->u.interrupt.pc);
    HAL_INTERRUPT_ACKNOWLEDGE(vector);
    return CYG_ISR_HANDLED;
}

// -------------------------------------------------------------------------
// Enable profile timer.
//

int
hal_enable_profile_timer(int resolution)
{
    CYG_ADDRESS base = CYGHWR_HAL_LM3S_TIMER;
    cyg_uint32 period;

    // Set initial period to system clock
    period = hal_lm3s_sysclk;
    
    // Divide by 1000000 to give the number of counts per microsecond.
    period /= 1000000;

    // Mutiply by resolution, which is in us, to give required timeout
    // period.
    period *= resolution;

    // Ensure timer is clocked
    CYGHWR_HAL_LM3S_CLOCK_ENABLE( CYGHWR_HAL_LM3S_TIMER_CLOCK );
    
    // Disable the timer
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_CTL, 0 );

    // Set 32 bit mode
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_CFG, CYGHWR_HAL_LM3S_TIMER_CFG_32BIT );

    // Set periodic mode
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_TAMR, CYGHWR_HAL_LM3S_TIMER_TxMR_PERIODIC );

    // Set timeout period
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_TAILR, period );    

    // Enable interrupt
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_IMR, CYGHWR_HAL_LM3S_TIMER_INT_TATO );
    
    // Create interrupt object.
    cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_TIMER, 1, 0, (cyg_ISR_t*) &profile_isr, &profile_dsr, &profile_handle, &profile_interrupt);
    cyg_drv_interrupt_attach(profile_handle);
    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_TIMER);

    // Start the timer counting
    HAL_WRITE_UINT32(base+CYGHWR_HAL_LM3S_TIMER_CTL, CYGHWR_HAL_LM3S_TIMER_CTL_TAEN );
    
    return resolution;
}

#endif // CYGPKG_PROFILE_GPROF
// end-sanitize-ecospro

//==========================================================================
// EOF lm3s_misc.c
