/*==========================================================================
//
//      at91sam7s_misc.c
//
//      HAL misc board support code for Atmel AT91sam7s
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2006, 2007 Free Software Foundation, Inc.
// Copyright (C) 2006, 2007 eCosCentric Limited                             
//
// 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):    gthomas
// Contributors: gthomas, jskov, nickg, tkoeller, Oliver Munz, Andrew Lunn
// Date:         2001-07-12
// Purpose:      HAL board support
// Description:  Implementations of HAL board interfaces
//
//####DESCRIPTIONEND####
//
//========================================================================*/

#include <pkgconf/system.h>
#include <pkgconf/hal.h>
#if defined(CYGPKG_DEVICES_WATCHDOG_ARM_AT91WDTC)
# include <pkgconf/devs_watchdog_arm_at91wdtc.h>
#endif

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

#include <cyg/hal/hal_io.h>             // IO macros
#include <cyg/hal/hal_arch.h>           // Register state info
#include <cyg/hal/hal_diag.h>
#include <cyg/hal/hal_intr.h>           // necessary?
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/hal_if.h>             // calling interface
#include <cyg/hal/hal_misc.h>           // helper functions
#ifdef CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT
#include <cyg/hal/drv_api.h>            // HAL ISR support
#endif

extern void hal_at91_led(int val);

void
hal_at91_set_leds (int val)
{
    hal_at91_led(val);
}

// -------------------------------------------------------------------------
// Hardware init

void hal_plf_hardware_init (void) 
{
#if !defined(CYGHWR_HAL_ARM_AT91SAM7A)
  /* Enable the Serial devices to driver the serial port pins */
  HAL_ARM_AT91_PIO_CFG(AT91_USART_RXD0);
  HAL_ARM_AT91_PIO_CFG(AT91_USART_TXD0);
  HAL_ARM_AT91_PIO_CFG(AT91_USART_RTS0);
  HAL_ARM_AT91_PIO_CFG(AT91_USART_CTS0);
  HAL_ARM_AT91_PIO_CFG(AT91_DBG_DTXD);
  HAL_ARM_AT91_PIO_CFG(AT91_DBG_DRXD);

#if !defined(CYGHWR_HAL_ARM_AT91SAM7S_at91sam7s32)
  /* Enable the Serial devices to driver the serial port pins */
  HAL_ARM_AT91_PIO_CFG(AT91_USART_RXD1);
  HAL_ARM_AT91_PIO_CFG(AT91_USART_TXD1);
#endif

  /* Setup the Reset controller. Allow user resets */
  HAL_WRITE_UINT32(AT91_RST+AT91_RST_RMR, 
                   AT91_RST_RMR_URSTEN | 
                   10 << 8 | 
                   AT91_RST_RMR_KEY);

  /* Enable peripheral clocks for USART 0 and 1 if they are to be used */  
#if CYGINT_HAL_ARM_AT91_SERIAL_UART0
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, AT91_PMC_PCER_US0 );
#endif
#if CYGINT_HAL_ARM_AT91_SERIAL_UART1
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER,  AT91_PMC_PCER_US1);
#endif

#ifdef CYGBLD_HAL_ARM_AT91_TIMER_TC
  /* Enable peripheral clocks for TC 0 and 1 if they are to be used */
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER, 
                   AT91_PMC_PCER_TC0 |
                   AT91_PMC_PCER_TC2); 
#endif

#ifdef CYGPKG_HAL_AT91SAM7_TWI
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER,  AT91_PMC_PCER_TWI);
#endif

#if defined(CYGHWR_HAL_ARM_AT91SAM7X) && defined(CYGPKG_IO_CAN)
  /* Turn on CAN controller in PMC and set up GPIO pins for CANTX and
   * CANRX. On the AT91SAM7X-EK board we also need to set PA2 low to
   * enable the CAN transceiver.
   */
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER,  AT91_PMC_PCER_CAN);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANRX);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANTX);
# if defined(CYGPKG_HAL_ARM_AT91SAM7XEK)
  HAL_ARM_AT91_GPIO_CFG_DIRECTION(AT91_GPIO_PA2, AT91_PIN_OUT);
  HAL_ARM_AT91_GPIO_RESET(AT91_GPIO_PA2);
# endif
#endif

#if defined(CYGHWR_HAL_ARM_AT91SAM7A3) && defined(CYGPKG_IO_CAN)
  /* Turn on CAN controllers in PMC and set up GPIO pins for CANTX and
   * CANRX. On the AT91SAM7A3-EK board we also need to set PA23 low to
   * enable the CAN transceivers.
   */
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER,  AT91_PMC_PCER_CAN0);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANRX0);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANTX0);
  HAL_WRITE_UINT32(AT91_PMC+AT91_PMC_PCER,  AT91_PMC_PCER_CAN1);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANRX1);
  HAL_ARM_AT91_PIO_CFG(AT91_CAN_CANTX1);
# if defined(CYGPKG_HAL_ARM_AT91SAM7A3EK)
  HAL_ARM_AT91_GPIO_CFG_DIRECTION(AT91_GPIO_PA23, AT91_PIN_OUT);
  HAL_ARM_AT91_GPIO_RESET(AT91_GPIO_PA23);
# endif
#endif

  
#ifndef CYGIMP_WATCHDOG_HARDWARE
  /* Disable the watchdog. The eCos philosophy is that the watchdog is
     disabled unless the watchdog driver is used to enable it.
     However on some SAMs,if we disable it here we cannot re-enable it in the
     watchdog driver, hence the conditional compilation. */
  HAL_WRITE_UINT32(AT91_WDTC + AT91_WDTC_WDMR, AT91_WDTC_WDMR_DIS); 
#endif

#else

  // SAM7A setup
  
  /* Enable peripheral clocks for USART 0 and 1 if they are to be used */
#if CYGINT_HAL_ARM_AT91_SERIAL_UART0
  HAL_WRITE_UINT32( AT91_USART0+AT91_PMC_ECR, AT91_ECR_PIO|AT91_ECR_CLOCK );
# if defined(CYG_HAL_STARTUP_ROM)
  HAL_WRITE_UINT32( AT91_USART0+AT91_US_CR, AT91_US_CR_SWRST );
# endif
  // Enable PIO lines for UART device
  HAL_WRITE_UINT32( AT91_USART0+AT91_US_PDR, 0xFFFFFFFF );  
#endif
#if CYGINT_HAL_ARM_AT91_SERIAL_UART1
  HAL_WRITE_UINT32( AT91_USART1+AT91_PMC_ECR, AT91_ECR_PIO|AT91_ECR_CLOCK );
# if defined(CYG_HAL_STARTUP_ROM)
  HAL_WRITE_UINT32( AT91_USART1+AT91_US_CR, AT91_US_CR_SWRST );
# endif
  // Enable PIO lines for UART device
  HAL_WRITE_UINT32( AT91_USART1+AT91_US_PDR, 0xFFFFFFFF );  
#endif
  
  // Enable PDC
  HAL_WRITE_UINT32( AT91_PMC+AT91_PMC_ECR, AT91_PMC_ARM|AT91_PMC_PDC );

#endif // !defined(CYGHWR_HAL_ARM_AT91SAM7A)

}

// -------------------------------------------------------------------------
// Calculate the baud value to be programmed into the serial port baud
// rate generators. This function will determine what the clock speed
// is that is driving the generator so it can be used in situations
// when the application dynamically changes the clock speed.

cyg_uint32 
hal_at91_us_baud(cyg_uint32 baud_rate)
{
#ifndef CYGHWR_HAL_ARM_AT91SAM7A
  cyg_uint32 val, pll;
  cyg_uint32 main_clock = 0;
  cyg_uint32 baud_value = 0;

  HAL_READ_UINT32((AT91_PMC+AT91_PMC_MCKR), val);
  switch (val & 0x03) {
    /* Slow clock */
    case AT91_PMC_MCKR_SLOW_CLK:
      main_clock = CYGNUM_HAL_ARM_AT91_SLOW_CLOCK;
      break;
      
      /* Main clock */
    case AT91_PMC_MCKR_MAIN_CLK:
      main_clock = CYGNUM_HAL_ARM_AT91_CLOCK_OSC_MAIN;
      break;
      /* PLL */
    case AT91_PMC_MCKR_PLL_CLK:
      HAL_READ_UINT32((AT91_PMC+AT91_PMC_PLLR), pll);
      main_clock = CYGNUM_HAL_ARM_AT91_CLOCK_OSC_MAIN * 
        (((pll & 0x7FF0000) >> 16) + 1) / (pll & 0xFF);
      break;
  }
  
  // Process prescale
  val = (val & 0x1C) >> 2;
  main_clock = main_clock >> val;

  /* Define the baud rate divisor register, (round) */
  baud_value = (main_clock/(8*baud_rate)+1)/2;
  
  return baud_value;

#else

  cyg_uint32 baud_value = 0;
  cyg_uint32 main_clock = CYGNUM_HAL_ARM_AT91_CLOCK_OSC_MAIN;
  cyg_uint32 cs, pdiv, mdiv;

  HAL_READ_UINT32( AT91_CM+AT91_CM_CS, cs );
  HAL_READ_UINT32( AT91_CM+AT91_CM_PDIV, pdiv );
  HAL_READ_UINT32( AT91_CM+AT91_CM_MDIV, mdiv );

  if( cs & AT91_CM_CS_DIVSLCT )
  {
      main_clock /= (mdiv&AT91_CM_MDIV_MDIV);
  }
  else if( cs & AT91_CM_CS_PLLSLCT )
  {
      main_clock *= pdiv&AT91_CM_PDIV_PLLMUL;
      if( pdiv & AT91_CM_PDIV_PLLDIV2 )
          main_clock /= 2;
  }
  else if( cs & AT91_CM_CS_LFSLCT )
  {
      if( cs & AT91_CM_CS_RTCSEL )
      {
          main_clock = CYGNUM_HAL_ARM_AT91SAM7A_RTCLK;
      }
      else
      {
          main_clock /= (mdiv&AT91_CM_MDIV_MDIV);
      }
  }
  // else we are using MCLK directly.
  
  /* Define the baud rate divisor register, (round) */
  baud_value = (main_clock/(8*baud_rate)+1)/2;
  
  return baud_value;
  

#endif
  
}

//--------------------------------------------------------------------------
// EOF at91sam7s_misc.c
