This is the mail archive of the ecos-discuss@sources.redhat.com mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

interrupt handlers for the ARM SA1110


I have a few comments and a suggestion.

Per Gary Thomas's suggestion, I have added  some hooks to the file
/hal/arm/sa11x0/var/current/src/sa11x0_misc.c so that I could extend the

hal_interrupt_handle table to include 32 extra interrupts from my DSP.
The file
is attached.   Changes are like:

/* macro for board specific IRQ handler */
#ifdef HAL_EXTENDED_IRQ_HANDLER
      HAL_EXTENDED_IRQ_HANDLER(index);
 #endif

Please consider wrapping these changes into the main distribution.
That way, hooks will be
available for other variants of the sa11x0  to incorporate extra
interrupts.

The user needs to replace
hal/arm/sa11x0/var/current/include/hal_var_ints.h with
a modified version:
hal/arm/sa11x0/variant_board_hal/include/platform_hal_ints.h

and in the cdl for the variant, include the lines:

    define_proc {
        puts $::cdl_header \
       "#define CYGBLD_HAL_PLF_INTS_H <cyg/hal/hal_platform_ints.h>"
    }

Unfortunately, doing this may or may not work.  There is a somewhat
cryptic comment
(I guess cryptic comments are better than no comments ???) in
/hal/arm/arch/current/src/vectors.S  near the end:

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

// Interrupt vector tables.
// These tables contain the isr, data and object pointers used to
deliver
// interrupts to user code.

// Despite appearances, their sizes are not #defines, but .equ symbols
// generated by magic without proper dependencies in arm.inc
// Recompiling will not DTRT without manual intervention.

        .data

init_flag:
        .balign 4
        .long   0

 .extern hal_default_isr

 .globl  hal_interrupt_handlers
hal_interrupt_handlers:
        .rept   CYGNUM_HAL_ISR_COUNT
        .long   hal_default_isr
        .endr

Once  CYGNUM_HAL_ISR_COUNT is redefined, it sure would be nice to know
exactly how to
 increase the size of the table, and to know up front that the following
wont' work:

ecosconfig tree
make clean
make

I ended up disassembling the code and counting the size of
hal_interrupt_handlers before I
felt confident that that size of the table (which is kind of important)
was what I thought it was.

Warren









//==========================================================================
//
//      sa11x0_misc.c
//
//      HAL misc board support code for StrongARM SA11x0
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Red Hat eCos Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License.  You may obtain a copy of the License at
// http://www.redhat.com/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the
// License for the specific language governing rights and limitations under
// the License. 
//
// The Original Code is eCos - Embedded Configurable Operating System,
// released September 30, 1998.
//
// The Initial Developer of the Original Code is Red Hat.
// Portions created by Red Hat are
// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    gthomas
// Contributors: hmt
//               Travis C. Furrer <furrer@mit.edu>
// Date:         2000-05-08
// 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_misc.h>           // Size constants
#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>          // Cache control
#include <cyg/hal/hal_sa11x0.h>         // Hardware definitions
#include <cyg/hal/hal_mm.h>             // MMap table definitions

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

// Most initialization has already been done before we get here.
// All we do here is set up the interrupt environment.
// FIXME: some of the stuff in hal_platform_setup could be moved here.

externC void plf_hardware_init(void);

void hal_hardware_init(void)
{
    // Mask all interrupts
    *SA11X0_ICMR = 0;
     
    // Make all interrupts do IRQ and not FIQ
    // FIXME: Change this if you use FIQs.
    *SA11X0_ICLR = 0;
     
    // Prevent masked interrupts from bringing us out of idle mode
    *SA11X0_ICCR = 1;

    // Disable all GPIO interrupt sources
    *SA11X0_GPIO_RISING_EDGE_DETECT = 0;
    *SA11X0_GPIO_FALLING_EDGE_DETECT = 0;
    *SA11X0_GPIO_EDGE_DETECT_STATUS = 0x0FFFFFFF;

    // Perform any platform specific initializations
    plf_hardware_init();

    // Let the "OS" counter run
    *SA11X0_OSCR = 0;
    *SA11X0_OSMR0 = 0;

    // Set up eCos/ROM interfaces
    hal_if_init();

    // Enable caches
    HAL_DCACHE_ENABLE();
    HAL_ICACHE_ENABLE();
}

// -------------------------------------------------------------------------
static cyg_uint32  clock_period;

void hal_clock_initialize(cyg_uint32 period)
{
    // Load match value
    *SA11X0_OSMR0 = period;
    clock_period = period;

    // Start the counter
    *SA11X0_OSCR = 0;

    // Clear any pending interrupt
    *SA11X0_OSSR = SA11X0_OSSR_TIMER0;

    // Enable timer 0 interrupt    
    *SA11X0_OIER |= SA11X0_OIER_TIMER0;

    // Unmask timer 0 interrupt
    HAL_INTERRUPT_UNMASK( CYGNUM_HAL_INTERRUPT_TIMER0 );

    // That's all.
}

// This routine is called during a clock interrupt.

// Define this if you want to ensure that the clock is perfect (i.e. does
// not drift).  One reason to leave it turned off is that it costs some
// us per system clock interrupt for this maintenance.
#undef COMPENSATE_FOR_CLOCK_DRIFT

void hal_clock_reset(cyg_uint32 vector, cyg_uint32 period)
{
#ifdef COMPENSATE_FOR_CLOCK_DRIFT
    cyg_uint32 next = *SA11X0_OSMR0 + period;    // Next interrupt time
    *SA11X0_OSSR = SA11X0_OSSR_TIMER0;           // Clear any pending interrupt
    *SA11X0_OSMR0 = next;                        // Load new match value
    {
        cyg_uint32 ctr = *SA11X0_OSCR;
        clock_period = next - ctr;
        if ((clock_period - 1) >= period) {      // Adjust for missed interrupts
            *SA11X0_OSMR0 = ctr + period;
            *SA11X0_OSSR = SA11X0_OSSR_TIMER0;   // Clear pending interrupt
            clock_period = period;
        }
    }
#else
    *SA11X0_OSMR0 = *SA11X0_OSCR + period;       // Load new match value
    *SA11X0_OSSR = SA11X0_OSSR_TIMER0;           // Clear any pending interrupt
#endif
}

// 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)

// Note: The "contract" for this function is that the value is the number
// of hardware clocks that have happened since the last interrupt (i.e.
// when it was reset).  This value is used to measure interrupt latencies.
// However, since the hardware counter runs freely, this routine computes
// the difference between the current clock period and the number of hardware
// ticks left before the next timer interrupt.
void hal_clock_read(cyg_uint32 *pvalue)
{
    int orig;
    HAL_DISABLE_INTERRUPTS(orig);
    *pvalue = clock_period + *SA11X0_OSCR - *SA11X0_OSMR0;
    HAL_RESTORE_INTERRUPTS(orig);

}

//
// Delay for some number of micro-seconds
//
void hal_delay_us(cyg_int32 usecs)
{
    cyg_uint32 val = 0;
    cyg_uint32 ctr = *SA11X0_OSCR;
    while (usecs-- > 0) {
        do {
            if (ctr != *SA11X0_OSCR) {
                val += 271267;          // 271267ps (3.6865Mhz -> 271.267ns)
                ++ctr;
            }
        } while (val < 1000000);
        val -= 1000000;
    }
}

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

// 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)
{
    cyg_uint32 sources, index;

#if 0 // test FIQ and print alert if active - really for debugging
    sources = *SA11X0_ICFP;
    if ( 0 != sources )
        diag_printf( "FIQ source active!!! - fiqstatus %08x irqstatus %08x\n",
                     sources, *SA11X0_ICIP );
    else
#endif // Scan FIQ sources also

    sources = *SA11X0_ICIP;

// FIXME
// if we come to support FIQ properly...
//    if ( 0 == sources )
//        sources = *SA11X0_ICFP;

    /* macro for board specific IRQ handler */
    #ifdef HAL_EXTENDED_IRQ_HANDLER
      HAL_EXTENDED_IRQ_HANDLER(index);
    #endif

    // Nothing wrong with scanning them in any order we choose...
    // So here we try to make the serial devices steal fewer cycles.
    // So, knowing this is an ARM:
    if ( sources & 0xff0000 )
        index = 16;
    else if ( sources & 0xff00 )
        index = 8;
    else if ( sources & 0xff )
        index = 0;
    else // if ( sources & 0xff000000 )
      index = 24;

    do {
        if ( (1 << index) & sources ) {
            if (index == CYGNUM_HAL_INTERRUPT_GPIO) {
                // Special case of GPIO cascade.  Search for lowest set bit
                sources = *SA11X0_GPIO_EDGE_DETECT_STATUS & 0x0FFFF800;
                index = 11;
                do {
                    if (sources & (1 << index)) {
                        index += 32;
                        break;
                    }
                    index++;
                } while (index < 28);
            }
            return index;
        }
      index++;
    } while ( index & 7 );
    
    return CYGNUM_HAL_INTERRUPT_NONE; // This shouldn't happen!
}

//
// Interrupt control
//

void hal_interrupt_mask(int vector)
{
    #ifdef HAL_EXTENDED_INTERRUPT_MASK
      HAL_EXTENDED_INTERRUPT_MASK(vector);
    #endif

    // Non-GPIO interrupt sources can be masked separately.
    // Note: masking any non-unique GPIO signal (31..11) results in
    // all GPIO signals (31..11) being masked as only the "lump"
    // source will be changed.
    
    if (vector >= CYGNUM_HAL_INTERRUPT_GPIO11) {
        vector = CYGNUM_HAL_INTERRUPT_GPIO;
    }
    *SA11X0_ICMR &= ~(1 << vector);
}

void hal_interrupt_unmask(int vector)
{
    #ifdef HAL_EXTENDED_INTERRUPT_UNMASK
      HAL_EXTENDED_INTERRUPT_UNMASK(vector);
    #endif

    if (vector >= CYGNUM_HAL_INTERRUPT_GPIO11) {
        vector = CYGNUM_HAL_INTERRUPT_GPIO;
    }
    *SA11X0_ICMR |= (1 << vector);
}

void hal_interrupt_acknowledge(int vector)
{
    #ifdef HAL_EXTENDED_INTERRUPT_UNMASK
      HAL_EXTENDED_INTERRUPT_ACKNOWLEDGE(vector);
    #endif

    // GPIO interrupts are driven by an edge detection mechanism.  This
    // is latching so these interrupts must be acknowledged directly.
    // All other interrupts simply go away when the interrupting unit
    // has been serviced by the ISR.
    if ((vector < CYGNUM_HAL_INTERRUPT_GPIO) || 
        (vector >= CYGNUM_HAL_INTERRUPT_GPIO11)) {
        *SA11X0_GPIO_EDGE_DETECT_STATUS  = (1 << (vector & 0x1F));
    } else {
        // Not a GPIO interrupt
        return;
    }
}

void hal_interrupt_configure(int vector, int level, int up)
{
    #ifdef HAL_EXTENDED_INTERRUPT_CONFIGURE
      HAL_EXTENDED_INTERRUPT_CONFIGURE(vector, level, up);
    #endif

    // This function can be used to configure the GPIO interrupts.  All
    // of these pins can potentially generate an interrupt, but only
    // 0..10 are unique.  Thus the discontinuity in the numbers.
    // Also, if 'level' is true, then both edges are enabled if 'up' is
    // true, otherwise they will be disabled.
    // Non GPIO sources are ignored.
    if ((vector < CYGNUM_HAL_INTERRUPT_GPIO) || 
        (vector >= CYGNUM_HAL_INTERRUPT_GPIO11)) {
        if (level) {
            if (up) {
                // Enable both edges
                *SA11X0_GPIO_RISING_EDGE_DETECT |= (1 << (vector & 0x1F));
                *SA11X0_GPIO_FALLING_EDGE_DETECT |= (1 << (vector & 0x1F));
            } else {
                // Disable both edges
                *SA11X0_GPIO_RISING_EDGE_DETECT &= ~(1 << (vector & 0x1F));
                *SA11X0_GPIO_FALLING_EDGE_DETECT &= ~(1 << (vector & 0x1F));
            }
        } else {
            // Only interested in one edge
            if (up) {
                // Set rising edge detect and clear falling edge detect.
                *SA11X0_GPIO_RISING_EDGE_DETECT |= (1 << (vector & 0x1F));
                *SA11X0_GPIO_FALLING_EDGE_DETECT &= ~(1 << (vector & 0x1F));
            } else {
                // Set falling edge detect and clear rising edge detect.
                *SA11X0_GPIO_FALLING_EDGE_DETECT |= (1 << (vector & 0x1F));
                *SA11X0_GPIO_RISING_EDGE_DETECT &= ~(1 << (vector & 0x1F));
            }
        }
    }
}

void hal_interrupt_set_level(int vector, int level)
{
    #ifdef HAL_EXTENDED_INTERRUPT_SET_LEVEL
      HAL_EXTENDED_INTERRUPT_SET_LEVEL(vector, level);
    #endif

    // Interrupt priorities are not configurable on the SA11X0.
}

/*------------------------------------------------------------------------*/
// These routines are for testing the equivalent efficient macros of the
// same names.  They actually inspect the MMap installed and tell the
// truth - including about the validity of the address at all.

cyg_uint32 hal_virt_to_phys_address( cyg_uint32 vaddr )
{
    register cyg_uint32 *ttb_base;
    cyg_uint32 noise;
    register union ARM_MMU_FIRST_LEVEL_DESCRIPTOR desc;

    // Get the TTB register
    asm volatile ("mrc  p15,0,%0,c2,c0,0;"
                  "mov  %0, %0, lsr #14;" // Lower 14 bits are undefined
                  "mov  %0, %0, asl #14;" // ...so clear them
                  : "=r"(ttb_base)
                  :
                  /*:*/);

    noise = vaddr & (SZ_1M - 1);
    vaddr /= SZ_1M; // Page size/Entry size is Mb.

    desc.word = *ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS( ttb_base, vaddr );

    // Is this a valid entry that we understand?
    if ( ARM_MMU_FIRST_LEVEL_SECTION_ID == desc.section.id )
        return noise + desc.section.base_address * SZ_1M;

    return 0; // Not available.
}

cyg_uint32 hal_phys_to_virt_address( cyg_uint32 paddr )
{
    cyg_uint32 *ttb_base;
    cyg_uint32 i, noise;
    register union ARM_MMU_FIRST_LEVEL_DESCRIPTOR desc;
    cyg_bool identity_found = false;

    // Get the TTB register
    asm volatile ("mrc  p15,0,%0,c2,c0,0;"
                  "mov  %0, %0, lsr #14;" // Lower 14 bits are undefined
                  "mov  %0, %0, asl #14;" // ...so clear them
                  : "=r"(ttb_base)
                  :
                  /*:*/);


    noise = paddr & (SZ_1M - 1);
    paddr /= SZ_1M; // Page size/Entry size is Mb.

    for ( i = 0; i <= 0xfff; i++ ) {
        desc.word = *ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS( ttb_base, i );

        // Is this a valid entry that we understand?
        if ( ARM_MMU_FIRST_LEVEL_SECTION_ID == desc.section.id ) {
            if ( paddr == desc.section.base_address ) {
                // Then the virtual address is i (in Mb).
                if ( i == paddr ) {
                    // We found a direct map first.  Do not report that
                    // immediately because it may be double mapped to a
                    // distinct virtual address, which we should return in
                    // preference.  But remember that we saw it.
                    identity_found = true;
                    continue;
                }
                // Otherwise report that we found it:
                return noise + i * SZ_1M;
            }
        }
    }
    // No non-identity matches were found.
    if ( identity_found )
        return noise + paddr * SZ_1M;

    return 0; // Not available.
}

cyg_uint32 hal_virt_to_uncached_address( cyg_uint32 vaddr )
{
    cyg_uint32 *ttb_base;
    cyg_uint32 noise, paddr, i;
    register union ARM_MMU_FIRST_LEVEL_DESCRIPTOR desc;

    // Get the TTB register
    asm volatile ("mrc  p15,0,%0,c2,c0,0;"
                  "mov  %0, %0, lsr #14;" // Lower 14 bits are undefined
                  "mov  %0, %0, asl #14;" // ...so clear them
                  : "=r"(ttb_base)
                  :
                  /*:*/);


    noise = vaddr & (SZ_1M - 1);
    vaddr /= SZ_1M; // Page size/Entry size is Mb.

    desc.word = *ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS( ttb_base, vaddr );

    // Is this a valid entry that we understand?
    if ( ARM_MMU_FIRST_LEVEL_SECTION_ID != desc.section.id )
        return 0; // Not available.

    // Is this very address uncacheable already?
    if ( ARM_UNCACHEABLE == desc.section.c )
        return noise + vaddr * SZ_1M;

    paddr = desc.section.base_address;

    // We could look straight at a direct mapped slot for the physical
    // address as per convention...

    // Now scan through for a virtual address that maps to the same
    // physical memory, but uncached.
    for ( i = 0; i <= 0xfff; i++ ) {
        desc.word = *ARM_MMU_FIRST_LEVEL_DESCRIPTOR_ADDRESS( ttb_base, i );

        // Is this a valid entry that we understand?
        if ( ARM_MMU_FIRST_LEVEL_SECTION_ID == desc.section.id )
            if ( paddr == desc.section.base_address )
                // Then the virtual address is i (in Mb).
                if ( ARM_UNCACHEABLE == desc.section.c )
                    // Then this one is not cacheable.
                    return noise + i * SZ_1M;
    }

    return 0; // Not available.
}


/*------------------------------------------------------------------------*/
// EOF sa11x0_misc.c

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]