//==========================================================================
//
//      omap_l1xx_misc.c
//
//      HAL misc board support code for OMAP L1XX
//
//==========================================================================
// ####ECOSGPLCOPYRIGHTBEGIN####                                            
// -------------------------------------------                              
// This file is part of eCos, the Embedded Configurable Operating System.   
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
// Copyright (C) 2003, 2004, 2005, 2007, 2009, 2010 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):    nickg
// Date:         2009-12-09
// Purpose:      HAL board support
// Description:  Implementations of HAL board interfaces
//
//####DESCRIPTIONEND####
//
//========================================================================*/

#include <pkgconf/hal.h>
#include <pkgconf/system.h>
#include CYGBLD_HAL_PLATFORM_H

#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>           // Interrupt names
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/omap_l1xx.h>         // Platform specifics

#include <cyg/infra/diag.h>             // diag_printf


//==========================================================================
// CPU specific initialization
//

void
hal_omap_l1xx_hardware_init(void)
{
    cyg_uint32 aintc = CYGHWR_HAL_L1XX_AINTC;
    int i;

    // Set up AINTC
    // Nest mode is off

    // Enable global interrupts
    HAL_WRITE_UINT32( aintc+CYGHWR_HAL_L1XX_AINTC_GER,
                      CYGHWR_HAL_L1XX_AINTC_GER_ENABLE );

    // Enable IRQ host interrupts
    HAL_WRITE_UINT32( aintc+CYGHWR_HAL_L1XX_AINTC_HIER, CYGHWR_HAL_L1XX_AINTC_HIER_IRQ );

    // Set all channel registers to a default setting of 16
    for( i = 0 ; i < 25; i++ )
    {
        HAL_WRITE_UINT32( aintc+CYGHWR_HAL_L1XX_AINTC_CMR( i ), 0x10101010 );            
    }

    // Start the clock early so that hal_delay_us() will work.
    hal_clock_initialize( CYGNUM_HAL_RTC_PERIOD );
}

//==========================================================================
// Main system timer
//
// Use timer 0 for system clock.
// It is fed from the crystal input, which we divide down to
// 2MHz.

#define CYGHWR_HAL_L1XX_TIMER           CYGHWR_HAL_L1XX_TIMER0
#define CYGHWR_HAL_L1XX_TIMER_PRESCALE  (CYGHWR_HAL_ARM_ARM9_OMAP_L1XX_CLOCK_AUXCLK/2000000)

void
hal_clock_initialize(cyg_uint32 period)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;
    cyg_uint32 tgcr, tcr;

    // Stop and reset timer, disable interrupts
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, 0 );

    // Set counter to free-run
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_EMUMGT, CYGHWR_HAL_L1XX_TIMER_EMUMGT_FREE );    

    // Configure in 32 bit unchained mode, use only counter 3:4, and
    // prescale to 2MHz. Enable PLUS features to get period reload.
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_PSC34(CYGHWR_HAL_L1XX_TIMER_PRESCALE);
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIMMODE_32UC;
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIM34RS;
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_PLUSEN;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );    

    // Set continuous mode with reload
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, tcr );
    tcr |= CYGHWR_HAL_L1XX_TIMER_TCR_MODE34_CRL;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, tcr );

    // Initialize the counters
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TIM34, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_PRD34, period );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_REL34, period );

    // Enable interrupt
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT,
                      CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT_PRD34EN );
}

// -------------------------------------------------------------------------
// This routine is called during a clock interrupt.

void
hal_clock_reset(cyg_uint32 vector, cyg_uint32 period)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;
    cyg_uint32 intctlstat;
    
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );
    intctlstat &= ~CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT_PRD12ST;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );
                      
}

// -------------------------------------------------------------------------
// Read the current value of the clock, returning the number of hardware
// "ticks" that have occurred (i.e. how far away the current value is from
// the start)

void
hal_clock_read(cyg_uint32 *pvalue)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;    
    HAL_READ_UINT32(timer+CYGHWR_HAL_L1XX_TIMER_TIM34, *pvalue );
}

// -------------------------------------------------------------------------
//
// Delay for some number of micro-seconds

void hal_delay_us(cyg_int32 usecs)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;    
    cyg_int32 ticks;
    cyg_uint32 val1, val2;
    cyg_uint32 rel;
  
    // Calculate how many ticks the required number of microseconds
    // equate to. We know the timer runs at 2MHz, so just multiply by 2.
    ticks = usecs<<1;

    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_REL34, rel );

    HAL_READ_UINT32(timer+CYGHWR_HAL_L1XX_TIMER_TIM34, val1 );
    
    while (ticks > 0)
    {
        HAL_READ_UINT32(timer+CYGHWR_HAL_L1XX_TIMER_TIM34, val2 );

        if (val2 < val1)
            ticks -= ((rel + val2) - val1); //overflow occurred
        else 
            ticks -= (val2 - val1);
        val1 = val2;
    }
}    

//==========================================================================
// Profiling support.
//
// This uses the other half of timer 0 and assumes that
// hal_clock_initialize() has already run to perform the basic timer
// initialization.

#ifdef CYGPKG_PROFILE_GPROF
#include <cyg/profile/profile.h>
#include <cyg/hal/drv_api.h>

#define CYGNUM_HAL_INTERRUPT_PROFILE    CYGNUM_HAL_INTERRUPT_T64P0_TINT12

static cyg_interrupt    profile_interrupt;
static cyg_handle_t     profile_handle;

volatile cyg_uint32 hal_profile_count = 0;

// ----------------------------------------------------------------------------

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);
}

// ----------------------------------------------------------------------------

static cyg_uint32
profile_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters* regs)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;
    cyg_uint32 intctlstat;

    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );
    intctlstat &= ~CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT_PRD34ST;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );

    cyg_drv_interrupt_acknowledge( vector );

    hal_profile_count++;
    __profile_hit(regs->pc);
    return CYG_ISR_HANDLED;
}

// ----------------------------------------------------------------------------

int
hal_enable_profile_timer(int resolution)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER;    
    cyg_uint32 tgcr, tcr;
    cyg_uint32 intctlstat;
    
    // The requested resolution is in microseconds. The half of the
    // timer we are using has no pre-scaler, so it runs at full AUXCLOCK
    // speed. So each microsecond is (AUXCLOCK/1000000) cycles.

    cyg_uint32 profile_period = resolution * (CYGHWR_HAL_ARM_ARM9_OMAP_L1XX_CLOCK_AUXCLK/1000000);

    // Enable timer 1:2 
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIM12RS;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );    

    // Set continuous mode with reload
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, tcr );
    tcr |= CYGHWR_HAL_L1XX_TIMER_TCR_MODE12_CRL;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, tcr );

    // Initialize counters
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TIM12, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_PRD12, profile_period );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_REL12, profile_period );


    // Create interrupt to do profiling
    cyg_drv_interrupt_create( CYGNUM_HAL_INTERRUPT_PROFILE,
                              0,       // Highest priority
                              0,
                              (cyg_ISR_t*) &profile_isr,
                              &profile_dsr,
                              &profile_handle,
                              &profile_interrupt);
    cyg_drv_interrupt_attach(profile_handle);

    // Enable interrupt
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );
    intctlstat |= CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT_PRD12EN;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, intctlstat );
    
    cyg_drv_interrupt_unmask( CYGNUM_HAL_INTERRUPT_PROFILE );
    
    return resolution;
}
#endif

//==========================================================================
// This routine is called to respond to a hardware interrupt (IRQ).  It
// should interrogate the hardware and return the IRQ vector number.

int hal_IRQ_handler(void)
{
    int vector;

    HAL_READ_UINT32( CYGHWR_HAL_L1XX_AINTC+CYGHWR_HAL_L1XX_AINTC_GPIR, vector );
    
    return vector;
}

//==========================================================================
//
// Interrupt control
//

// -------------------------------------------------------------------------

void hal_interrupt_mask(int vector)
{
    CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
               vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");

    HAL_WRITE_UINT32( CYGHWR_HAL_L1XX_AINTC + CYGHWR_HAL_L1XX_AINTC_EICR, vector );
}

// -------------------------------------------------------------------------

void hal_interrupt_unmask(int vector)
{
    CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
               vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");

    HAL_WRITE_UINT32( CYGHWR_HAL_L1XX_AINTC + CYGHWR_HAL_L1XX_AINTC_EISR, vector );    
}

// -------------------------------------------------------------------------

void hal_interrupt_acknowledge(int vector)
{
    CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
               vector >= CYGNUM_HAL_ISR_MIN , "Invalid vector");

    HAL_WRITE_UINT32( CYGHWR_HAL_L1XX_AINTC + CYGHWR_HAL_L1XX_AINTC_SICR, vector );    
}

// -------------------------------------------------------------------------

void hal_interrupt_configure(int vector, int level, int up)
{
    CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
               vector >= CYGNUM_HAL_ISR_MIN, "Invalid vector");

    // Only GPIO interrupts are configurable. Currently unsupported.
}

// -------------------------------------------------------------------------

void hal_interrupt_set_level(int vector, int level)
{
    cyg_uint32 aintc = CYGHWR_HAL_L1XX_AINTC;
    cyg_uint32 cmr;
    
    CYG_ASSERT(vector <= CYGNUM_HAL_ISR_MAX &&
               vector >= CYGNUM_HAL_ISR_MIN, "Invalid vector");
    CYG_ASSERT(level <= 29 &&
               level >= 0, "Invalid interrupt priority level");

    // Skip FIQ channels
    level += 2;
    if( level > 31 ) level = 31;
    
    HAL_READ_UINT32( aintc+CYGHWR_HAL_L1XX_AINTC_CMR( vector>>2 ), cmr );
    cmr &= ~(0xFF<<((vector&3)*8));
    cmr |= (level<<((vector&3)*8));
    HAL_WRITE_UINT32( aintc+CYGHWR_HAL_L1XX_AINTC_CMR( vector>>2 ), cmr );    
}

//==========================================================================
// PINMUX setting

__externC void hal_l1xx_pinmux_set( cyg_uint32 pin )
{
    cyg_uint32 reg = CYGHWR_HAL_L1XX_PINMUX_REG( pin );
    cyg_uint32 field = CYGHWR_HAL_L1XX_PINMUX_FIELD( pin );
    cyg_uint32 func = CYGHWR_HAL_L1XX_PINMUX_FUNC( pin );
    cyg_uint32 syscfg0 = CYGHWR_HAL_L1XX_SYSCFG0;
    cyg_uint32 pinmux;

    HAL_READ_UINT32( syscfg0+CYGHWR_HAL_L1XX_SYSCFG0_PINMUX( reg ), pinmux );
    pinmux &= ~(0xF<<field);
    pinmux |= func<<field;
    HAL_WRITE_UINT32( syscfg0+CYGHWR_HAL_L1XX_SYSCFG0_PINMUX( reg ), pinmux );    
}

//==========================================================================
// GPIO

__externC void hal_l1xx_gpio_set( cyg_uint32 pin )
{
    cyg_uint32 bank = CYGHWR_HAL_L1XX_GPIO_BANK( pin );
    cyg_uint32 bit = CYGHWR_HAL_L1XX_GPIO_BIT( pin );
    cyg_uint32 mode = CYGHWR_HAL_L1XX_GPIO_MODE( pin );
    cyg_uint32 gpio = CYGHWR_HAL_L1XX_GPIO_BASE;
    cyg_uint32 reg = CYGHWR_HAL_L1XX_GPIO01 + (0x28 * (bank>>1));
    cyg_uint32 regbit = bit << ((bank&1)<<4);
    cyg_uint32 val;

    // Power up GPIO device, this is a null op if it is already running.
    CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_GPIO_POWER );    
    
    HAL_READ_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_DIR, val );
    val &= ~(1<<regbit);
    val |= (mode & CYGHWR_HAL_L1XX_GPIO_MODE_INPUT) ? (1<<regbit) : 0 ;
    HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_DIR, val );
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_gpio_out( cyg_uint32 pin, int val )
{
    cyg_uint32 bank = CYGHWR_HAL_L1XX_GPIO_BANK( pin );
    cyg_uint32 bit = CYGHWR_HAL_L1XX_GPIO_BIT( pin );
    cyg_uint32 gpio = CYGHWR_HAL_L1XX_GPIO_BASE;
    cyg_uint32 reg = CYGHWR_HAL_L1XX_GPIO01 + (0x28 * (bank>>1));
    cyg_uint32 regbit = bit << ((bank&1)<<4);

    if( val&1 )
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_SET_DATA, 1<<regbit );
    else
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_CLR_DATA, 1<<regbit );
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_gpio_in ( cyg_uint32 pin, int *val )
{
    cyg_uint32 bank = CYGHWR_HAL_L1XX_GPIO_BANK( pin );
    cyg_uint32 bit = CYGHWR_HAL_L1XX_GPIO_BIT( pin );
    cyg_uint32 gpio = CYGHWR_HAL_L1XX_GPIO_BASE;
    cyg_uint32 reg = CYGHWR_HAL_L1XX_GPIO01 + (0x28 * (bank>>1));
    cyg_uint32 regbit = bit << ((bank&1)<<4);
    cyg_uint32 v;

    HAL_READ_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_IN_DATA, v );

    *val = (v>>regbit)&1;
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_gpio_intcfg( cyg_uint32 pin, cyg_uint32 mode )
{
    cyg_uint32 bank = CYGHWR_HAL_L1XX_GPIO_BANK( pin );
    cyg_uint32 bit = CYGHWR_HAL_L1XX_GPIO_BIT( pin );
    cyg_uint32 gpio = CYGHWR_HAL_L1XX_GPIO_BASE;
    cyg_uint32 reg = CYGHWR_HAL_L1XX_GPIO01 + (0x28 * (bank>>1));
    cyg_uint32 regbit = bit << ((bank&1)<<4);

    if( mode  & 1 )
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_SET_FAL_TRIG, 1<<regbit );
    else
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_CLR_FAL_TRIG, 1<<regbit );

    if( mode  & 2 )
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_SET_RIS_TRIG, 1<<regbit );
    else
        HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_CLR_RIS_TRIG, 1<<regbit );

    if( mode != 0 )
    {
        // If interrupts are needed from this bank, enable in bank
        // interrupt enable register. At present we have no way of
        // deciding when to clear this bit, and rely on the trigger
        // registers being cleared.
        
        cyg_uint32 binten;
        HAL_READ_UINT32( gpio+CYGHWR_HAL_L1XX_GPIO_BINTEN, binten );
        binten |= 1<<bank;
        HAL_WRITE_UINT32( gpio+CYGHWR_HAL_L1XX_GPIO_BINTEN, binten );
    }
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_gpio_intstat( cyg_uint32 pin, int *stat )
{
    cyg_uint32 bank = CYGHWR_HAL_L1XX_GPIO_BANK( pin );
    cyg_uint32 bit = CYGHWR_HAL_L1XX_GPIO_BIT( pin );
    cyg_uint32 gpio = CYGHWR_HAL_L1XX_GPIO_BASE;
    cyg_uint32 reg = CYGHWR_HAL_L1XX_GPIO01 + (0x28 * (bank>>1));
    cyg_uint32 regbit = bit << ((bank&1)<<4);
    cyg_uint32 s;

    // Read and clear interrupt status bit.
    HAL_READ_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_IRQ_STAT, s );
    HAL_WRITE_UINT32( gpio+reg+CYGHWR_HAL_L1XX_GPIO_IRQ_STAT, 1<<regbit );

    *stat = (s>>regbit)&1;
    
}

//==========================================================================
// Peripheral power control

__externC void hal_l1xx_power_transition( cyg_uint32 power, cyg_uint32 next )
{
    cyg_uint32 lpsc = power&0xFF;
    cyg_uint32 psc = CYGHWR_HAL_L1XX_PSC0;
    cyg_uint32 domain = CYGHWR_HAL_L1XX_PSC_PTSTAT_GO0;
    cyg_uint32 reg;
    
    if( power & (1<<8) )
        psc = CYGHWR_HAL_L1XX_PSC1;

    if( power & (1<<16) )
        domain = CYGHWR_HAL_L1XX_PSC_PTSTAT_GO1;
    
    // Wait for any current transitions to finish.
    do {
        HAL_READ_UINT32( psc+CYGHWR_HAL_L1XX_PSC_PTSTAT, reg );
    } while( (reg & domain) != 0 );

    // Check whether we are already in the desired state for this
    // device, and if so, return.
    HAL_READ_UINT32( psc+CYGHWR_HAL_L1XX_PSC_MDSTAT(lpsc), reg );
    if( (reg & CYGHWR_HAL_L1XX_PSC_MDSTAT_STATE_MASK) == next )
        return;

    // Set the next mode in the control register for this device
    HAL_READ_UINT32( psc+CYGHWR_HAL_L1XX_PSC_MDCTL(lpsc), reg );
    reg &= ~CYGHWR_HAL_L1XX_PSC_MDCTL_NEXT_MASK;
    reg |= next;
    HAL_WRITE_UINT32( psc+CYGHWR_HAL_L1XX_PSC_MDCTL(lpsc), reg );

    // Make the transition happen
    HAL_WRITE_UINT32( psc+CYGHWR_HAL_L1XX_PSC_PTCMD, domain );
    
    // Wait for transition to finish.
    do {
        HAL_READ_UINT32( psc+CYGHWR_HAL_L1XX_PSC_PTSTAT, reg );
    } while( (reg & domain) != 0 );
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_power_enable( cyg_uint32 power )
{
    hal_l1xx_power_transition( power, CYGHWR_HAL_L1XX_PSC_MDCTL_NEXT_ENABLE );
}

//--------------------------------------------------------------------------

__externC void hal_l1xx_power_disable( cyg_uint32 power )
{
    hal_l1xx_power_transition( power, CYGHWR_HAL_L1XX_PSC_MDCTL_NEXT_DISABLE );
}

//==========================================================================
//
// Reset the processor/board
//
// We have to use the watchdog for this. If there is a proper software
// reset somewhere I have not found it.

// Only Timer 1 is attached to the reset line.
#define CYGHWR_HAL_L1XX_TIMER_WATCHDOG  CYGHWR_HAL_L1XX_TIMER1

void cyg_hal_omap_l1xx_reset(void)
{
    cyg_uint32 timer = CYGHWR_HAL_L1XX_TIMER_WATCHDOG;    
    cyg_uint32 tgcr;
    cyg_uint64 period;
    cyg_uint64 ints;

    HAL_DISABLE_INTERRUPTS( ints );
    
    // Stop and reset timer, disable interrupts
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TCR, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_INTCTLSTAT, 0 );

    // Configure timer for 64 bit watchdog mode, enable counters 1:2
    // and 3:4.
    HAL_READ_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIMMODE_64WD;
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIM12RS;
    tgcr |= CYGHWR_HAL_L1XX_TIMER_TGCR_TIM34RS;
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TGCR, tgcr );    

    // We set the timeout to 1 ms. Each ms is (AUXCLOCK/1000)
    // cycles.
    period = ((cyg_uint64)CYGHWR_HAL_ARM_ARM9_OMAP_L1XX_CLOCK_AUXCLK/1000LL);

    // Zero timer and set period
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TIM12, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_TIM34, 0 );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_PRD12, (cyg_uint32)(period&0xFFFFFFFF) );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_PRD34, (cyg_uint32)((period>>32)&0xFFFFFFFF) );

    // Enable watchdog
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_WDTCR, CYGHWR_HAL_L1XX_TIMER_WDTCR_WDKEY0|CYGHWR_HAL_L1XX_TIMER_WDTCR_WDEN );
    HAL_WRITE_UINT32( timer+CYGHWR_HAL_L1XX_TIMER_WDTCR, CYGHWR_HAL_L1XX_TIMER_WDTCR_WDKEY1|CYGHWR_HAL_L1XX_TIMER_WDTCR_WDEN );

    // Wait here for the watchdog to fire and reset us.
    while(true) CYG_EMPTY_STATEMENT;
}

//==========================================================================
// DMA support
//
// This is solely concerned with driving the EDMA channels for
// peripheral access. Only transfers into single contiguous buffers
// are supported; the more complex and esoteric features of these DMA
// engines are not handled.

//--------------------------------------------------------------------------

#include <cyg/hal/drv_api.h>

#define NCTRL   2

// Bitmap of initialized controllers
static cyg_uint8 hal_edma_controller_initialized;

// Base addresses of each controller
static cyg_uint32 hal_edma_controller[NCTRL] =
{
    CYGHWR_HAL_L1XX_EDMA0_CC, CYGHWR_HAL_L1XX_EDMA1_CC
};

// Table of active channels, indexed by controller and event number.
static hal_edma_channel *hal_edma_channel_table[NCTRL][32];

#ifdef CYGPKG_KERNEL
// Controller interrupts, one each for completion and error interrupts
// for each controller.
static cyg_handle_t hal_edma_controller_interrupt[NCTRL][2];
static cyg_interrupt hal_edma_controller_interrupt_struct[NCTRL][2];

// Interrupt vectors, completion and error interrupts for each controller.
static cyg_uint32 hal_edma_controller_interrupt_vectors[NCTRL][2] =
{
    [0] = { CYGNUM_HAL_INTERRUPT_EDMA3_0_CC0_INT0, CYGNUM_HAL_INTERRUPT_EDMA3_0_CC0_ERRINT },
    [1] = { CYGNUM_HAL_INTERRUPT_EDMA3_1_CC0_INT0, CYGNUM_HAL_INTERRUPT_EDMA3_1_CC0_ERRINT },
};
#endif

//--------------------------------------------------------------------------
// Diagnostics.

#if 0
#define dma_diag( __fmt, ... ) diag_printf("DMA: %35s[%3d]: " __fmt, __FUNCTION__, __LINE__, ## __VA_ARGS__ );
#define dma_dump_buf(__addr, __size ) diag_dump_buf( __addr, __size )
#else
#define dma_diag( __fmt, ... )
#define dma_dump_buf(__addr, __size )
#endif


//--------------------------------------------------------------------------
// forward definitions

#ifdef CYGPKG_KERNEL
static cyg_uint32 hal_edma_controller_isr(cyg_vector_t vector, cyg_addrword_t data);
static cyg_uint32 hal_edma_controller_errisr(cyg_vector_t vector, cyg_addrword_t data);
#endif
static void       hal_edma_controller_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data);

static void hal_edma_channel_callbacks( cyg_uint32 ctlr, cyg_uint32 mask, cyg_uint32 cbevent );
    
//--------------------------------------------------------------------------
// Initialize a controller

static cyg_uint32 hal_edma_controller_init( cyg_uint32 ctrl )
{
    cyg_uint32 edma_ctrl = hal_edma_controller[ctrl];
    
    if( hal_edma_controller_initialized & (1<<ctrl) )
        return edma_ctrl;

    dma_diag("edma_ctrl %08x\n", edma_ctrl );

    // Power up the controller
    if( ctrl == 0 )
    {
        CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_EDMA0_CC_POWER );
        CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_EDMA0_TC0_POWER );
        CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_EDMA0_TC1_POWER );
    }
    else
    {
        CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_EDMA1_CC_POWER );
        CYGHWR_HAL_L1XX_POWER_ENABLE( CYGHWR_HAL_L1XX_EDMA1_TC0_POWER );
    }

    hal_edma_controller_initialized |= (1<<ctrl);

#ifdef CYGPKG_KERNEL    
    // Set up interrupts

    cyg_drv_interrupt_create ( hal_edma_controller_interrupt_vectors[ctrl][0],
                               15,
                               (CYG_ADDRWORD)ctrl,
                               hal_edma_controller_isr,
                               hal_edma_controller_dsr,
                               &hal_edma_controller_interrupt[ctrl][0],
                               &hal_edma_controller_interrupt_struct[ctrl][0]);
    cyg_drv_interrupt_attach( hal_edma_controller_interrupt[ctrl][0] );
    cyg_drv_interrupt_unmask( hal_edma_controller_interrupt_vectors[ctrl][0] );

    cyg_drv_interrupt_create ( hal_edma_controller_interrupt_vectors[ctrl][1],
                               15,
                               (CYG_ADDRWORD)ctrl,
                               hal_edma_controller_errisr,
                               hal_edma_controller_dsr,
                               &hal_edma_controller_interrupt[ctrl][1],
                               &hal_edma_controller_interrupt_struct[ctrl][1]);
    cyg_drv_interrupt_attach( hal_edma_controller_interrupt[ctrl][1] );
    cyg_drv_interrupt_unmask( hal_edma_controller_interrupt_vectors[ctrl][1] );
#endif
    
    return edma_ctrl;
}

#ifdef CYGPKG_KERNEL
//--------------------------------------------------------------------------
// Completion ISR. Just request the DMA be run.

static cyg_uint32 hal_edma_controller_isr(cyg_vector_t vector, cyg_addrword_t data)
{
    dma_diag("vector %d\n", vector );
    cyg_drv_interrupt_acknowledge( vector );
    return CYG_ISR_HANDLED|CYG_ISR_CALL_DSR;
}

//--------------------------------------------------------------------------
// Error ISR. Just request the DMA be run.

static cyg_uint32 hal_edma_controller_errisr(cyg_vector_t vector, cyg_addrword_t data)
{
    cyg_uint32 edma_ctrl = hal_edma_controller[data];
    cyg_uint32 emr, ccerr;

    HAL_READ_UINT32( edma_ctrl+CYGHWR_HAL_L1XX_EDMA_EMR, emr );
    HAL_READ_UINT32( edma_ctrl+CYGHWR_HAL_L1XX_EDMA_CCERR, ccerr );
    dma_diag("vector %d emr %08x ccerr %08x\n", vector, emr, ccerr );
    
    cyg_drv_interrupt_acknowledge( vector );    
    return CYG_ISR_HANDLED|CYG_ISR_CALL_DSR;    
}
#endif

//--------------------------------------------------------------------------
// Shared DSR. Look in the IPR and EMR for channels and call their callbacks.

static void hal_edma_controller_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data)
{
    cyg_uint32 edma_ctrl = hal_edma_controller[data];
    cyg_uint32 edma_shadow = edma_ctrl + CYGHWR_HAL_L1XX_EDMA_SHADOW(0);

//    dma_diag("vector %d ctrl %08x shadow %08x\n", vector, edma_ctrl, edma_shadow );
    
    // Look for completed transfers
    {
        cyg_int32 ipr;

        HAL_READ_UINT32( edma_shadow+CYGHWR_HAL_L1XX_EDMA_IPR, ipr );
        HAL_WRITE_UINT32( edma_shadow+CYGHWR_HAL_L1XX_EDMA_ICR, ipr );

        hal_edma_channel_callbacks( data, ipr, CYGHWR_HAL_L1XX_EDMA_COMPLETE );
    }

    // Look for errors
    {
        cyg_uint32 emr;

        HAL_READ_UINT32( edma_ctrl+CYGHWR_HAL_L1XX_EDMA_EMR, emr );
        HAL_WRITE_UINT32( edma_ctrl+CYGHWR_HAL_L1XX_EDMA_EMCR, emr );

        hal_edma_channel_callbacks( data, emr, CYGHWR_HAL_L1XX_EDMA_ERROR );        
    }
}

//--------------------------------------------------------------------------
// Poll EDMA channels. We do this by simply calling the DSR for each
// controller. The DSR is designed so that calling it with no pending
// events will have no effect.

__externC void hal_edma_poll( void )
{
    int i;
    for( i = 0; i < NCTRL; i++ )
        hal_edma_controller_dsr( 0, 1, i );
}

//--------------------------------------------------------------------------
// General callback handler. Given a controller, a mask and an event
// type, calls the callbacks for the channels whose bit is set in the
// mask.

static void hal_edma_channel_callbacks( cyg_uint32 ctlr, cyg_uint32 mask, cyg_uint32 cbevent )
{
//    dma_diag("ctlr %d mask %08x cbevent %d\n", ctlr, mask, cbevent );

    while( mask != 0 )
    {
        cyg_uint32 bit = mask & -mask;
        cyg_uint32 event;
        hal_edma_channel *edma_chan;

        HAL_LSBIT_INDEX( event, bit );

        edma_chan = hal_edma_channel_table[ctlr][event];
        
        if( edma_chan != NULL )
        {
            // Disable the channel
            HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_EECR, bit );

            if( edma_chan->callback )
            {
                dma_diag("call chan %08x\n", edma_chan->channel );
                edma_chan->callback( edma_chan, cbevent, edma_chan->data );
            }
        }
        
        mask ^= bit;
    }
}

//--------------------------------------------------------------------------
// Set up a channel. This is also used to tear down a channel by
// resetting the hardware to a known state.

static void hal_edma_channel_setup( hal_edma_channel *edma_chan, cyg_uint32 enable )
{
    cyg_uint32 event = CYGHWR_HAL_L1XX_EDMA_CHANNEL_EVENT(edma_chan->channel);    
    cyg_uint32 drae;
    cyg_uint32 ebit = 1<<event;

    dma_diag("event %d ebit %08x\n", event, ebit );
    
    // Enable access to this channel for our shadow set
    HAL_READ_UINT32( edma_chan->edma_ctrl+CYGHWR_HAL_L1XX_EDMA_DRAE0, drae );
    drae &= ~ebit;
    drae |= enable<<event;
    HAL_WRITE_UINT32( edma_chan->edma_ctrl+CYGHWR_HAL_L1XX_EDMA_DRAE0, drae );

    // Clear pending events
    HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_ECR, ebit );
    HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_SECR, ebit );

    // Enable interrupt
    HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_IESR, ebit );
    
    // Set default values in PaRAM set
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_OPT, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SRC, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_ABCNT, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_DST, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDBIDX, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_LNKBCNTRLD, 0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDCIDX,  0 );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_CCNT, 0 );

    // Leave channel disabled until we want to do a transfer
}

//--------------------------------------------------------------------------
// API call to initialize a channel. Fills in the channel descriptor
// structure and sets up the hardware ready for use.

__externC void hal_edma_channel_init( hal_edma_channel *edma_chan, cyg_uint32 channel,
                                      hal_edma_callback *callback, CYG_ADDRWORD data )
{
    cyg_uint32 ctrl = CYGHWR_HAL_L1XX_EDMA_CHANNEL_CONTROLLER(channel);
    cyg_uint32 event = CYGHWR_HAL_L1XX_EDMA_CHANNEL_EVENT(channel);
    cyg_uint32 edma_ctrl = hal_edma_controller_init( ctrl );

    dma_diag("channel %08x ctrl %d event %d\n", channel, ctrl, event);
    
    edma_chan->channel = channel;
    edma_chan->edma_ctrl = edma_ctrl;
    edma_chan->edma_shadow = edma_ctrl + CYGHWR_HAL_L1XX_EDMA_SHADOW(0);
    edma_chan->param_set = edma_ctrl + CYGHWR_HAL_L1XX_EDMA_PARAM_SET( event );
    edma_chan->callback = callback;
    edma_chan->data = data;
    edma_chan->aburstsize = 1;    
    edma_chan->bburstsize = 0;    
    edma_chan->active = false;
    
    dma_diag("edma_ctrl %08x edma_shadow %08x param_set %08x\n", edma_chan->edma_ctrl, edma_chan->edma_shadow, edma_chan->param_set );
    
    hal_edma_channel_table[ctrl][event] = edma_chan;
    
    // Initialize the hardware channel
    hal_edma_channel_setup( edma_chan, 1 );
}

//--------------------------------------------------------------------------
// Tear down a channel.

__externC void hal_edma_channel_delete( hal_edma_channel *edma_chan )
{
    cyg_uint32 ctrl = CYGHWR_HAL_L1XX_EDMA_CHANNEL_CONTROLLER(edma_chan->channel);
    cyg_uint32 event = CYGHWR_HAL_L1XX_EDMA_CHANNEL_EVENT(edma_chan->channel);

    dma_diag("ctrl %d event %d\n", ctrl, event );
    
    hal_edma_channel_setup( edma_chan, 0 );
    
    hal_edma_channel_table[ctrl][event] = NULL;
}

//--------------------------------------------------------------------------
// Set the source address and indexes.

__externC void hal_edma_channel_source( hal_edma_channel *edma_chan, void *src, cyg_int16 bidx, cyg_int16 cidx )
{
    cyg_uint32 sdidx;
    
    dma_diag("channel %08x src %p\n", edma_chan->channel, src );
    
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SRC, (CYG_ADDRWORD)src );

    HAL_READ_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDBIDX, sdidx );
    sdidx &= 0xFFFF0000;
    sdidx |= bidx;
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDBIDX, sdidx );    

    HAL_READ_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDCIDX, sdidx );
    sdidx &= 0xFFFF0000;
    sdidx |= cidx;
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDCIDX, sdidx );        
}

//--------------------------------------------------------------------------
// Set the destination address and indexes.

__externC void hal_edma_channel_dest( hal_edma_channel *edma_chan, void *dest, cyg_int16 bidx, cyg_int16 cidx )
{
    cyg_uint32 sdidx;
    
    dma_diag("channel %08x dest %p\n", edma_chan->channel, dest );

    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_DST, (CYG_ADDRWORD)dest );

    HAL_READ_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDBIDX, sdidx );
    sdidx &= 0x0000FFFF;
    sdidx |= bidx<<16;
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDBIDX, sdidx );    

    HAL_READ_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDCIDX, sdidx );
    sdidx &= 0x0000FFFF;
    sdidx |= cidx<<16;
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_SDCIDX, sdidx );        
}

//--------------------------------------------------------------------------
// Set channel burst size. This is the quantity of data moved per
// hardware transfer.

__externC void hal_edma_channel_burstsize( hal_edma_channel *edma_chan, cyg_uint16 aburstsize, cyg_uint16 bburstsize )
{
    edma_chan->aburstsize = aburstsize;
    edma_chan->bburstsize = bburstsize/aburstsize;
}

//--------------------------------------------------------------------------
// Set the transfer size. This is limited to about 96Ki*burstsize maximum
// transfer size; which is adequate for what we currently need.

__externC void hal_edma_channel_size( hal_edma_channel *edma_chan, cyg_uint32 size )
{
    cyg_uint16 bcnt;
    cyg_uint16 ccnt = 1;
    cyg_uint16 bcld = 0;

    size /= edma_chan->aburstsize;
    bcnt = size;
    
    CYG_ASSERT( size < (0x8000+0xFFFF), "Transfer too large" );

    if( edma_chan->bburstsize == 0 )
    {
        // If the transfer is more than 32K, split it into two frames.
        if( size > 0x8000 )
            bcnt = 0x8000, ccnt = 2, bcld = size-bcnt;
    }
    else
    {
        // If the bburstsize is set, set ccnt to control the transfer
        bcnt = edma_chan->bburstsize;
        ccnt = size / bcnt;
        if( ccnt == 0 ) ccnt = 1;
        bcld = bcnt;
    }
    
    dma_diag("channel %08x aburst %d bburst %d size %d bcnt %d bcld %d ccnt %d\n",
             edma_chan->channel, edma_chan->aburstsize, edma_chan->bburstsize, size, bcnt, bcld, ccnt );
    
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_ABCNT, (bcnt<<16)|edma_chan->aburstsize );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_LNKBCNTRLD, (bcld<<16)|0xFFFF );
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_CCNT, ccnt );    
}

//--------------------------------------------------------------------------
// Start transfer on the channel.

__externC void hal_edma_channel_start( hal_edma_channel *edma_chan, cyg_uint32 opt )
{
    cyg_uint32 event = CYGHWR_HAL_L1XX_EDMA_CHANNEL_EVENT(edma_chan->channel);
    cyg_uint32 ebit = 1<<event;

    dma_diag("channel %08x ebit %08x\n", edma_chan->channel, ebit );

    // Enable completion interrupt and set TCC field in PaRAM entry.
    opt |= CYGHWR_HAL_L1XX_EDMA_SET_OPT_TCINT | CYGHWR_HAL_L1XX_EDMA_SET_OPT_TCC(event);
    HAL_WRITE_UINT32( edma_chan->param_set+CYGHWR_HAL_L1XX_EDMA_SET_OPT, opt );
    
    // Clear any pending errors
    HAL_WRITE_UINT32( edma_chan->edma_ctrl+CYGHWR_HAL_L1XX_EDMA_EMCR, ebit );

    // Clear any pending SERs
    HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_SECR, ebit );

    // Enable the channel
    HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_EESR, ebit );

    edma_chan->active = true;    
}

//--------------------------------------------------------------------------
// Stop transfer on the channel.

__externC void hal_edma_channel_stop( hal_edma_channel *edma_chan )
{
    cyg_uint32 event = CYGHWR_HAL_L1XX_EDMA_CHANNEL_EVENT(edma_chan->channel);
    cyg_uint32 ebit = 1<<event;

    dma_diag("channel %08x ebit %08x active %d\n", edma_chan->channel, ebit, edma_chan->active );

    if( edma_chan->active )
    {
        // Disable the channel
        HAL_WRITE_UINT32( edma_chan->edma_shadow+CYGHWR_HAL_L1XX_EDMA_EECR, ebit );

        edma_chan->active = false;
    }
}


//--------------------------------------------------------------------------
// End omap_l1xx_misc.c
