view packages/devs/can/arm/lpc2xxx/current/src/can_accfilt_lpc2xxx.c @ 2509:a52d470bc691

* cdl/can_lpc2xxx.cdl: Added CYGOPT_DEVS_CAN_LPC2XXX_ALIE to make arbitration lost interrupt optional. Added option CYGNUM_DEVS_CAN_LPC2XXX_INT_PRIORITY to configure the interrupt priority for global CAN interrupt in LPC24xx variants. * include/can_lpc2xxx_baudrates.h: Replaced CYGNUM_CAN_LPC2XXX_VPB_CLK by CYGNUM_HAL_ARM_LPC2XXX_CAN_CLK because newer variants like LPC24xx do not have a global VPB_CLK. * src/can_accfilt_lpc2xxx.c: Adjusted LPC2XXX_CAN_FIRST_IN_LUT to be 0 for LPC24xxx variants. Added macro CAN_CHAN_NO_LUT(_info_) to abstract channel numbering from acceptance filter code. * src/can_lpc2xxx.c: Removed icr data field from lpc2xxx_can_info_st structure because it is not required any longer. A lot of small modifications to make the driver usable with newer LPC2xxx variants like LPC24xx. ISR and DSR code changed - instead of disabling interrupts in IER register they are disabled in VIC by using cyg_drv_interrupt_mask() function calls. Added global CAN ISR and DSR for LPC24xx variants (they do not support individual interrupt vectors for RX and TX interrupts). Moved LUT error checking code from ISR into DSR to keep ISR as short as possible and made LUT error checking code optional. * cdl/can_lpc2xxx.cdl: add CAN interrupt priorities * src/can_lpc2xxx.c: add CAN interrupt priorities, repair "chan" definition missing in rx_ISR * src/can_accfilt_lpc2xxx.c: add various types of CAN controllers numbering (depends on LPC2XXX version)
author asl
date Tue, 22 Jul 2008 19:40:02 +0000
parents e8eaf852a606
children 74dbf4c3f2e1
line wrap: on
line source

//==========================================================================
//
//      devs/can/arm/lpc2xxx/current/src/can_accfilt_lpc2xxx.c
//
//      Acceptance filter management for LPC2xxx CAN driver
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2003 Gary Thomas
//
// 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.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):    Uwe Kindler
// Contributors: Uwe Kindler
// Date:         2007-05-28
// Purpose:      Support LPC2xxx on-chip CAN acceptance filters
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================


//===========================================================================
// Data types
//===========================================================================
//
// Acceptance filter entry
//
typedef struct lpc2xxx_accfilt_entry
{
    cyg_uint32 data;          // the value inclusive channel number
    cyg_uint32 id;
    cyg_uint32 lower_id_bound;
    cyg_uint32 upper_id_bound;
    cyg_uint8  channel_no;
} lpc2xxx_accfilt_entry_t;


//===========================================================================
// Declarations
//===========================================================================
//--------------------------------------------------------------------------
// On no-suffix and /00 devices, the CAN controllers are numbered 1 to n 
// (n = 2 or 4) in the LUT tables. However, on /01 devices, the CAN controllers
// are numbered 0 to n�1 in the LUT tables.
//
// On the LPC2468 the LUT channel numbers are also numbered from 0 - 4.
//
#if defined(CYGHWR_HAL_ARM_LPC2XXX_SUFFIX_01) || (CYGHWR_HAL_ARM_LPC2XXX_VARIANT_VERSION == 4)
# define LPC2XXX_CAN_FIRST_IN_LUT   (0)
#else
# define LPC2XXX_CAN_FIRST_IN_LUT   (1) 
#endif

//
// This macro calculates the chanel number from the channel info. The channel
// number is numbered from 0 - 3 but in the LUT the channel number may differ
// depending on the device suffix. For some devices the channel number in
// LUT are numbered 0 - 3 and for other devices the channels in LUT are
// numbered 1 - 4. This macro abstrats this fact from the acceptance filter
// code
//
#define CAN_CHAN_NO_LUT(_info_) (CAN_CHAN_NO(_info_) + LPC2XXX_CAN_FIRST_IN_LUT)

//--------------------------------------------------------------------------
// Lowlevel acceptance filter access
//
#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
static bool lpc2xxx_can_accfilt_add(lpc2xxx_can_info_t *info, 
                                    cyg_uint32          lower_id, 
                                    cyg_uint32          upper_id, 
                                    cyg_can_id_type     ext);
void lpc2xxx_can_accfilt_ram_insert_entry(cyg_uint32 TableAddress, cyg_uint16 EntryNo);
void lpc2xxx_can_accfilt_ram_remove_entry(cyg_uint32 TableAddress, cyg_uint16 EntryNo);
void lpc2xxx_can_accfilt_remove_all_ctrl_entries(lpc2xxx_can_info_t *info);
#else
static void lpc2xxx_can_accfilt_simple_rx_all(void);
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
void lpc2xxx_can_accfilt_reset(void);

#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
void lpc2xxx_can_accfilt_dbg_dump(void);
void lpc2xxx_can_reg_dump(struct cyg_devtab_entry* devtab_entry);
#endif


#ifdef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Calculate address of entry in certain table
//===========================================================================
static cyg_uint32 lpc2xxx_can_accfilt_calc_entry_address(cyg_uint32 TableAddressRegister, cyg_uint16 EntryNo)
{
    cyg_uint32 EntryAddress = 0xFFFFFFFF;
    cyg_uint32 TableAddress;
    
    HAL_READ_UINT32(TableAddressRegister, TableAddress); 
    switch (TableAddressRegister) 
    {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    case CAN_ACCFILT_SFF_SA:
         EntryAddress = ((EntryNo / 2) << 2) + TableAddress; 
         break;
             
    case CAN_ACCFILT_SFF_GRP_SA:
         EntryAddress = TableAddress + (EntryNo << 2);
         break;
#endif // CYGOPT_IO_CAN_STD_CAN_ID

#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    case CAN_ACCFILT_EFF_SA:
         EntryAddress = TableAddress + (EntryNo << 2);
         break;
                    
    case CAN_ACCFILT_EFF_GRP_SA:
         EntryAddress = TableAddress + (EntryNo << 3);
         break; 
#endif // CYGOPT_IO_CAN_EXT_CAN_ID
    default:
    	CYG_ASSERT(0, "Wrong TableAddressRegister");
    }  
    
    return EntryAddress;
}


//===========================================================================
// Remove one single entry from acceptance filter table
//===========================================================================
void lpc2xxx_can_accfilt_ram_remove_entry(cyg_uint32 Table, cyg_uint16 EntryNo)
{
    cyg_int32   remove_address = lpc2xxx_can_accfilt_calc_entry_address(Table, EntryNo);
    cyg_int32   entry_address;
    lsc_buf_t   lsc_val;
    cyg_uint8   entry_size = sizeof(cyg_uint32);
    cyg_uint32  sff_sa;
    cyg_uint32  sff_grp_sa;
    cyg_uint32  eff_sa;
    cyg_uint32  eff_grp_sa;
    cyg_int32   end_of_table;  
    
    HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa);
    HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa);      
    HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
   
    //
    // Do not try to remove from an empty table
    //
    if (!end_of_table)
    {
        return;
    }
        
    entry_address = remove_address;
    
    if ((remove_address < eff_grp_sa) && (CAN_ACCFILT_EFF_GRP_SA != Table))
    {
        if ((remove_address < eff_sa) && (CAN_ACCFILT_EFF_SA != Table))
        {
            if ((remove_address < sff_grp_sa) && (CAN_ACCFILT_SFF_GRP_SA != Table))
            { 
                lsc_buf_t nextval;
                
                if (EntryNo % 2)
                {
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + remove_address, lsc_val.dword);
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + remove_address + sizeof(cyg_uint32), nextval.dword);
                    lsc_val.column.upper = nextval.column.lower;
                    entry_address += sizeof(cyg_uint32);
                }
                
                //
                // Start copy immediatelly after removed entry
                //   
                while (entry_address < sff_grp_sa)
                {
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword);
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address + sizeof(cyg_uint32), nextval.dword);
                    lsc_val.column.lower = lsc_val.column.upper;
                    lsc_val.column.upper = nextval.column.lower;
                    HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword);
                    entry_address += sizeof(cyg_uint32);
                }
                
                //
                // now check if the lower identifier is disabled - if it is disabled, then
                // also the upper identifier is invalid and we can remove the entry completely
                // if the lower identifier is not disabled, then it is valid and we need
                // to disable the upper identifier because it contains an invalid entry
                //           
                if (lsc_val.column.lower & ACCFILT_STD_DIS)
                {
                    sff_grp_sa -= sizeof(cyg_uint32);  
                    entry_address = sff_grp_sa;
                }
                else
                {
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword);
                    lsc_val.column.upper = 0xffff;
                    HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword);
                    entry_size = 0; // we do not need to remove anything
                }
            } // if (pLine < pStdGrpStart)
            
            eff_sa -= entry_size;          
        } // if (pLine < pExtIdStart)
        
        eff_grp_sa -= entry_size;         
    } // if (pLine < pExtGrpStart)
    
    //
    // If no entry was removed then we can leave immediately without changing any
    // table pointers because we only did a change inside the sff table
    //
    if (!entry_size)
    {
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
        lpc2xxx_can_accfilt_dbg_dump();
#endif
        return;
    }
    
    if (CAN_ACCFILT_EFF_GRP_SA == Table)
    {
        //
        // If we are in the area of extended groups then we need to remove
        // 2 lines because lower and upper identifier need 1 line each
        //  
        entry_size += sizeof(cyg_uint32);
    }
    
    end_of_table -= entry_size;
  
    //
    // Move all entries one or two dword downwards - that means we remove a line
    //
    while (entry_address < end_of_table)
    {
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address + entry_size, lsc_val.dword);
        HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, lsc_val.dword);
        entry_address += sizeof(cyg_uint32);
    }
    
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, sff_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, eff_sa);      
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
    
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
    lpc2xxx_can_accfilt_dbg_dump();
#endif
}

//===========================================================================
// Insert one empty line into ram - all entries behind this line will be
// moved one entry upwards
//===========================================================================
void lpc2xxx_can_accfilt_ram_insert_entry(cyg_uint32 Table, cyg_uint16 EntryNo)
{
    cyg_int16   insert_address = lpc2xxx_can_accfilt_calc_entry_address(Table, EntryNo);
    cyg_int16   entry_address;
    cyg_int16   copy_start = insert_address;
    lsc_buf_t   lsc_val;
    cyg_uint8   entry_size = sizeof(cyg_uint32);
    cyg_uint32  sff_sa;
    cyg_uint32  sff_grp_sa;
    cyg_uint32  eff_sa;
    cyg_uint32  eff_grp_sa;
    cyg_uint32  end_of_table;
    
    
    HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa);
    HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa);      
    HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
    
    if ((insert_address <= eff_grp_sa) && (CAN_ACCFILT_EFF_GRP_SA != Table))
    {
        if ((insert_address <= eff_sa) && (CAN_ACCFILT_EFF_SA != Table))
        {
            if ((insert_address <= sff_grp_sa) && (CAN_ACCFILT_SFF_GRP_SA != Table))
            {
                //
                // If we are in the range of standard identifiers then we need to
                // do some special copy procedure for this area because a standard entry
                // is only 2 byte long. Copy only til start of area with standard groups
                //
                if (sff_grp_sa)
                {
                    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32), lsc_val.dword); // read last entry
                    //
                    // now check if the upper identifier is disabled - if it is disabled, then
                    // we have an odd number of std ids in the list. Then we do not need to 
                    // insert a new line - we simply need to copy all entries 2 bytes upwards
                    // that means we only need to change the std id area and do not need to touch
                    // any other filter id area.
                    // If the last entry is not disabled, then we have a valid filter here.
                    // Then we need to insert a complete new line, that means we also have to move
                    // all following entries and filter tables one dword upwards.
                    //
                    if (lsc_val.words.low & ACCFILT_STD_DIS)
                    {
                        copy_start = end_of_table + sizeof(cyg_uint32); // we do not need to insert a new line and do not copy anything
                        entry_size = 0;          
                    }
                }
                
                if (entry_size)
                {
                    copy_start = sff_grp_sa;          // copy everything behind std id group
                    sff_grp_sa += entry_size;   
                }
            } // if (pLine < pStdGrpStart)
            
            eff_sa += entry_size;
        } // if (pLine < pExtIdStart)
        
        eff_grp_sa += entry_size;
    } // if (pLine < pExtGrpStart)
          
    if (CAN_ACCFILT_EFF_GRP_SA == Table)
    {
        //
        // If we are in the area of extended groups then we need to insert
        // 2 lines because lower and upper identifier need 1 line each
        //
        entry_size += sizeof(cyg_uint32); // one entry is 2 dword long
    }
    
    entry_address = end_of_table - sizeof(cyg_uint32);
    end_of_table  += entry_size;  // add one additional entry
        
    //
    // Move all entries one or two dwords upwards - that means we insert a new empty line
    //
    while (entry_address >= copy_start)
    {
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword);
        HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address + entry_size, lsc_val.dword);
        entry_address -= sizeof(cyg_uint32);  
    }
    
    //
    // For the std ID area we need a special procedure
    //
    if (CAN_ACCFILT_SFF_SA == Table)
    {
        lsc_buf_t preval;
        //
        // Start copy with last entry of std id table
        //
        entry_address = sff_grp_sa - sizeof(cyg_uint32);    
                
        while (entry_address > insert_address)
        {
            HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword);
            HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address - sizeof(cyg_uint32), preval.dword);
            lsc_val.column.upper = lsc_val.column.lower;
            lsc_val.column.lower = preval.column.upper;
            HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)entry_address, lsc_val.dword);
            entry_address -= sizeof(cyg_uint32);
        }
        
        //
        // If we insert an entry into the lower column, then we need to move the
        // content of the lower column into the upper column
        //
        if (!(EntryNo % 2))
        {
            HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)insert_address, lsc_val.dword);
            lsc_val.column.upper = lsc_val.column.lower;
            HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + (cyg_uint32)insert_address, lsc_val.dword);
        }
        
        //
        // If we inserted a new line, then we have an odd number of identifiers now
        // and need to disable the last (the upper) entry
        //
        if (entry_size)
        {
            HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32) , lsc_val.dword);
            lsc_val.column.upper = 0xFFFF;  // disable the entry
            HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + sff_grp_sa - sizeof(cyg_uint32) , lsc_val.dword);
        }
    }
    
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA, sff_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA, eff_sa);      
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa);
    HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
}


//===========================================================================
// Query number of entries in a certain table
//===========================================================================
static cyg_uint16 lpc2xxx_can_accfilt_get_table_entries(cyg_uint32 TableStartAddress)
{
    cyg_uint32  start;
    cyg_uint32  end;
    
    switch (TableStartAddress)
    {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    case CAN_ACCFILT_SFF_SA:
         HAL_READ_UINT32(CAN_ACCFILT_SFF_SA,     start);
         HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, end);
         if (end - start)
         {
             lsc_buf_t data;
             HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + end - sizeof(cyg_uint32), data.dword);
             if (data.column.upper & ACCFILT_STD_DIS)
             {
                 return (((end - start) >> 1) - 1);
             }
         }
         return (end - start) >> 1;
         
    case CAN_ACCFILT_SFF_GRP_SA:
         HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, start);
         HAL_READ_UINT32(CAN_ACCFILT_EFF_SA,     end);
         return (end - start) >> 2;
#endif // CYGOPT_IO_CAN_STD_CAN_ID

#ifdef CYGOPT_IO_CAN_EXT_CAN_ID        
    case CAN_ACCFILT_EFF_SA:
         HAL_READ_UINT32(CAN_ACCFILT_EFF_SA,     start);
         HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, end);
         return (end - start) >> 2;
                 
    case CAN_ACCFILT_EFF_GRP_SA:
         HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, start);
         HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end);
         return (end - start) >> 3;
#endif // CYGOPT_IO_CAN_EXT_CAN_ID
    default:
        CYG_FAIL("Invalid identifier table address");
        return 0;         
    } // switch (TableStartAddress)
}

//===========================================================================
// Query certain entry from table
//===========================================================================
static void lpc2xxx_can_accfilt_get_entry(cyg_uint32 TableStartAddress, cyg_uint16 EntryNo, lpc2xxx_accfilt_entry_t *pEntry)
{
    cyg_uint32 EntryAddress = lpc2xxx_can_accfilt_calc_entry_address(TableStartAddress, EntryNo);
    lsc_buf_t  Data;

    HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword);
    pEntry->data = Data.dword;
    switch (TableStartAddress)
    {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    case CAN_ACCFILT_SFF_SA:
    {
         cyg_uint16 column; 
         if (EntryNo % 2)
         {
             column = Data.column.upper;
         }
         else
         {
             column = Data.column.lower; 
         }
         pEntry->id         = ACCFILT_STD_GET_ID(column);
         pEntry->channel_no = ACCFILT_STD_GET_CTRL(column);
    }
    break;
                 
    case CAN_ACCFILT_SFF_GRP_SA:
         pEntry->lower_id_bound = ACCFILT_STD_GET_ID(Data.column.lower);
         pEntry->upper_id_bound = ACCFILT_STD_GET_ID(Data.column.upper);
         pEntry->channel_no     = ACCFILT_STD_GET_CTRL(Data.column.lower);
         break;
#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID
    
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    case CAN_ACCFILT_EFF_SA:
         pEntry->id         = ACCFILT_EXT_GET_ID(Data.dword);
         pEntry->channel_no = ACCFILT_EXT_GET_CTRL(Data.dword);
         break;
                        
    case CAN_ACCFILT_EFF_GRP_SA:
         pEntry->lower_id_bound = ACCFILT_EXT_GET_ID(Data.dword);
         pEntry->channel_no     = ACCFILT_EXT_GET_CTRL(Data.dword);
         HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE+ EntryAddress + sizeof(cyg_uint32), Data.dword);
         pEntry->upper_id_bound = ACCFILT_EXT_GET_ID(Data.dword);
         break;
#endif // #ifedf CYGOPT_IO_CAN_EXT_CAN_ID
    default:
        CYG_FAIL("Invalid identifier table address");
    } // switch ()
}


//===========================================================================
// Set certain entry in table
//===========================================================================
static void lpc2xxx_can_accfilt_set_entry(cyg_uint32 TableStartAddress, cyg_uint16 EntryNo, lpc2xxx_accfilt_entry_t *pEntry)
{
    cyg_uint32 EntryAddress = lpc2xxx_can_accfilt_calc_entry_address(TableStartAddress, EntryNo);
    lsc_buf_t Data;

    switch (TableStartAddress)
    {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    case CAN_ACCFILT_SFF_SA:
         {
             HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword);            
             if (EntryNo % 2)
             {
                 Data.column.upper = (pEntry->channel_no << 13) | (pEntry->id & ACCFILT_STD_ID_MASK);
             }
             else
             {
                 Data.column.lower = (pEntry->channel_no << 13) | (pEntry->id & ACCFILT_STD_ID_MASK);
             }
         }
         break;
                 
    case CAN_ACCFILT_SFF_GRP_SA:
         Data.column.lower = (pEntry->channel_no << 13) | (pEntry->lower_id_bound & ACCFILT_STD_ID_MASK);
         Data.column.upper = (pEntry->channel_no << 13) | (pEntry->upper_id_bound & ACCFILT_STD_ID_MASK);
         break;
#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID

#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    case CAN_ACCFILT_EFF_SA:
         Data.dword = (pEntry->channel_no << 29) | (pEntry->id & ACCFILT_EXT_ID_MASK); 
         break;
                        
    case CAN_ACCFILT_EFF_GRP_SA:
         {
             lsc_buf_t Data2;
             
             Data.dword  = (pEntry->channel_no << 29) | (pEntry->lower_id_bound & ACCFILT_EXT_ID_MASK);
             Data2.dword = (pEntry->channel_no << 29) | (pEntry->upper_id_bound & ACCFILT_EXT_ID_MASK);
             HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress + sizeof(cyg_uint32), Data2.dword);
         }
         break;
#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID
         
    default:
        CYG_FAIL("Invalid identifier table address");
    } // switch ()
    
    HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + EntryAddress, Data.dword);
}


//===========================================================================
// Add one entry to acceptance filter RAM
// If upper ID is > lower ID then we have to add a group filter - else we
// have to add a single message filter here
//===========================================================================
static bool lpc2xxx_can_accfilt_add(lpc2xxx_can_info_t *info, 
                                    cyg_uint32          lower_id, 
                                    cyg_uint32          upper_id, 
                                    cyg_can_id_type     ext)
{
    cyg_uint32              accfilt_bck; // acceptance filter backup
    cyg_uint32              end_of_table;
    cyg_uint32              table;
    lpc2xxx_accfilt_entry_t entry;
    lpc2xxx_accfilt_entry_t new_entry;
    
    
    //
    // first step: disable acceptance filter and prepare it for modification
    //
    HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS);
    
    //
    // Check if table is full
    //
    HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
    if (end_of_table >= ACCFILT_RAM_SIZE)
    {
        return false;   
    }
    
    new_entry.id             = lower_id;
    new_entry.lower_id_bound = lower_id;
    new_entry.upper_id_bound = upper_id;
    
    //
    // Here we rely on the ISR vector ordering for calculation of channel number
    // Maybe this is not the right way for newer LPC parts
    //
    new_entry.channel_no = CAN_CHAN_NO_LUT(info);
    
    //
    // If lower_id == upper_id then we know that we have to setup a single message filter 
    // here. If it is not equal the it is group of identifiers to receive
    //
    if ((lower_id == upper_id) || (lower_id > upper_id))
    {
        //
        // setup single message filter (standard or extended) here
        //
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        if (ext)
        {
            table = CAN_ACCFILT_EFF_SA;
        }
        else
#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
            table = CAN_ACCFILT_SFF_SA;
#endif // CYGOPT_IO_CAN_STD_CAN_ID
        }
    }
    else
    {
        //
        // setup single message filter (standard or extended) here
        //
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        if (ext)
        {
            table = CAN_ACCFILT_EFF_GRP_SA;
        }
        else
#endif // #ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        {
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
            table = CAN_ACCFILT_SFF_GRP_SA;
#endif // #ifdef CYGOPT_IO_CAN_STD_CAN_ID
        }
    }
        
    cyg_uint16 entries = lpc2xxx_can_accfilt_get_table_entries(table);
    cyg_uint16 i;
    
           
    for (i = 0; i < entries; ++i)
    {
        lpc2xxx_can_accfilt_get_entry(table, i, &entry);

        if (entry.channel_no > new_entry.channel_no)
        {
            break;
        }
        
        if ((entry.channel_no == new_entry.channel_no)
        &&  (entry.id > new_entry.id))
        {
            break;
        }
    } // for (i = 0; i < entries; ++i)
    
    lpc2xxx_can_accfilt_ram_insert_entry(table, i);
    lpc2xxx_can_accfilt_set_entry(table, i, &new_entry);

    //
    // finally restore the previous state of the acceptance filter
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
    return true;
}


//===========================================================================
// Remove all entries from a certain controller
//===========================================================================
void lpc2xxx_can_accfilt_remove_all_ctrl_entries(lpc2xxx_can_info_t *info)
{
    cyg_uint32               accfilt_bck; // acceptance filter backup
    cyg_uint16               i;
    cyg_uint16               entries;
    cyg_uint32               TableStartAddress = CAN_ACCFILT_SFF_SA;
    lpc2xxx_accfilt_entry_t  Entry;
    cyg_uint8                channel_no = CAN_CHAN_NO_LUT(info);
    cyg_uint16               entry_idx;
    
    //
    // first step: disable acceptance filter and prepare it for modification
    //
    HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS);
    
    //
    // now remove all entries for a certain controller
    //
    for (TableStartAddress = CAN_ACCFILT_SFF_SA; TableStartAddress < CAN_ACCFILT_ENDOFTABLE; TableStartAddress += 4)
    {
        entries = lpc2xxx_can_accfilt_get_table_entries(TableStartAddress);
        entry_idx = 0;
        for (i = 0; i < entries; ++i)
        {
            lpc2xxx_can_accfilt_get_entry(TableStartAddress, entry_idx, &Entry);
            if (Entry.channel_no == channel_no)
            {
               lpc2xxx_can_accfilt_ram_remove_entry(TableStartAddress, entry_idx);
            }
            else
            {
                entry_idx++;
            }
        } // for (i = 0; i < entries; ++i)
    } // for (TableStartAddress = CAN_ACCFILT_SFF_SA ...
    
    //
    // finally restore the previous state of the acceptance filter
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


#ifndef CYGOPT_IO_CAN_RUNTIME_MBOX_CFG
//===========================================================================
// Setup reception of all CAN identifiers
// If runtime acceptance filter configuration is not required then we simply
// setup the acceptance filter here to receive all CAN identifiers
//===========================================================================
static void lpc2xxx_can_accfilt_simple_rx_all(void)
{
    cyg_uint32                regval;
    
    //
    // First check if it is really necessary to setup filters. If end of table is
    // != 0 then the acceptance filter is already setup properly
    //
    HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, regval);
    if (regval)
    {
        return;
    }
    
    cyg_uint32                accfilt_bck;         // acceptance filter backup
    cyg_uint8                 i = 0;               // loop counter
    lsc_buf_t                 accfilt_entry;       // std group entry
#ifdef CYGOPT_IO_CAN_STD_CAN_ID
    cyg_uint8                 std_address = 0;     // std group entry address
#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
    cyg_uint8                 ext_address = lpc2xxx_global_can_info.init_cnt << 2;
#endif
#else
    cyg_uint8                 ext_address = 0;
#endif
    
    //
    // first step: disable acceptance filter and prepare it for modification
    //
    HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS);
    
    //
    // Write table start adresses - we use only standard group and extended filter
    // group
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA,     0);
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, 0);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA,     ext_address);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, ext_address);
    
    //
    // Now loop through all active CAN channels and setup the acceptance filter for
    // each channel to receive all standard and extended CAN identifiers
    //
    while (lpc2xxx_global_can_info.active_channels[i])
    {
        lpc2xxx_can_info_t *info = (lpc2xxx_can_info_t *)lpc2xxx_global_can_info.active_channels[i++]->dev_priv; 
        cyg_uint8           channel_no = CAN_CHAN_NO_LUT(info);

#ifdef CYGOPT_IO_CAN_STD_CAN_ID
        accfilt_entry.column.lower = (channel_no << 13) | (0x000 & ACCFILT_STD_ID_MASK);
        accfilt_entry.column.upper = (channel_no << 13) | (0x7FF & ACCFILT_STD_ID_MASK);        
        HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE + std_address, accfilt_entry.dword);   
        std_address += sizeof(cyg_uint32);
#endif // CYGOPT_IO_CAN_STD_CAN_ID

#ifdef CYGOPT_IO_CAN_EXT_CAN_ID
        accfilt_entry.dword = (channel_no << 29) | (0x00000000 & ACCFILT_EXT_ID_MASK);
        HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE +  ext_address, accfilt_entry.dword);
        ext_address += sizeof(cyg_uint32);
        accfilt_entry.dword = (channel_no << 29) | (0x1FFFFFFF & ACCFILT_EXT_ID_MASK);
        HAL_WRITE_UINT32(CAN_ACCFILT_RAM_BASE +  ext_address, accfilt_entry.dword);
        ext_address += sizeof(cyg_uint32);
#endif // CYGOPT_IO_CAN_EXT_CAN_ID
    } // while (lpc2xxx_global_can_info.active_channels[i])
       
    //
    // finally store end of table value and restore the previous state of the 
    // acceptance filter
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, ext_address);
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
}
#endif // CYGOPT_IO_CAN_RUNTIME_MBOX_CFG


//===========================================================================
// Reset acceptance filter to poweron defaults
//===========================================================================
void lpc2xxx_can_accfilt_reset(void)
{
    cyg_uint32  accfilt_bck; // acceptance filter backup
    //
    // first step: disable acceptance filter and prepare it for modification
    //
    HAL_READ_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, AFMR_OFF | AFMR_BYPASS);
    
    //
    // Now write zero to all addresses of acceptance filter table
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_SA,     0);
    HAL_WRITE_UINT32(CAN_ACCFILT_SFF_GRP_SA, 0);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_SA,     0);
    HAL_WRITE_UINT32(CAN_ACCFILT_EFF_GRP_SA, 0);
    HAL_WRITE_UINT32(CAN_ACCFILT_ENDOFTABLE, 0);
    
    //
    // finally restore the previous state of the acceptance filter
    //
    HAL_WRITE_UINT32(CAN_ACCFILT_AFMR, accfilt_bck);
}


//===========================================================================
// Dump content of acceptance filter lookup table
//===========================================================================
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
void lpc2xxx_can_accfilt_dbg_dump(void)
{
    cyg_uint32          sff_sa;
    cyg_uint32          sff_grp_sa;
    cyg_uint32          eff_sa;
    cyg_uint32          eff_grp_sa;
    cyg_uint32          end_of_table;
    cyg_uint32          entry_address;
    lsc_buf_t           data;
    
    
    HAL_READ_UINT32(CAN_ACCFILT_SFF_SA, sff_sa);
    HAL_READ_UINT32(CAN_ACCFILT_SFF_GRP_SA, sff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_EFF_SA, eff_sa);      
    HAL_READ_UINT32(CAN_ACCFILT_EFF_GRP_SA, eff_grp_sa);
    HAL_READ_UINT32(CAN_ACCFILT_ENDOFTABLE, end_of_table);
    
    entry_address = sff_sa;
    
    //
    // Print lookup table registers
    //
    diag_printf("\n\nDUMP CAN ACCEPTANCE FILTER REGISTERS\n");
    diag_printf("----------------------------------------\n");
    diag_printf("SFF_sa:\t\t0x%08x\n", sff_sa);
    diag_printf("SFF_GRP_sa:\t0x%08x\n", sff_grp_sa);
    diag_printf("EFF_sa:\t\t0x%08x\n", eff_sa);
    diag_printf("EFF_GRP_sa:\t0x%08x\n", eff_grp_sa);
    diag_printf("EOT:\t\t0x%08x\n", end_of_table);
    
    //
    // Print table of standard identifiers
    //
    diag_printf("\n\nDUMP CAN LOOKUP TABLE RAM");
    diag_printf("\nSFF_sa\t\tcolumn_lower\tcolumn_upper\traw_data\n");
    diag_printf("----------------------------------------------------------\n");
    while (entry_address < sff_grp_sa)
    {
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword);
        diag_printf("0x%04x:\t\t0x%x\t\t0x%x\t\t0x%x\n", entry_address, data.column.lower, data.column.upper, data.dword);
        entry_address += sizeof(cyg_uint32);   
    }
    
    //
    // Print table of standard identifier groups
    //
    diag_printf("\nSFF_GRP_sa\tcolumn_lower\tcolumn_upper\traw_data\n");
    diag_printf("----------------------------------------------------------\n");
    while (entry_address < eff_sa)
    {  
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword);
        diag_printf("0x%04x:\t\t0x%x\t\t0x%x\t\t0x%x\n", entry_address, data.column.lower, data.column.upper, data.dword);
        entry_address += sizeof(cyg_uint32); 
    }
    
    //
    // Print table of extended identifiers
    //
    diag_printf("\nEFF_sa\t\t-\t\t-\t\traw_data\n");
    diag_printf("----------------------------------------------------------\n");
    while (entry_address < eff_grp_sa)
    {
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword);
        diag_printf("0x%04x:\t\t\t\t\t\t0x%x\n", entry_address, data.dword);
        entry_address += sizeof(cyg_uint32);
    }
    
    //
    // Print table of extended identifier groups
    //
    diag_printf("\nEFF_GRP_sa\t-\t\t-\t\traw_data\n");
    diag_printf("----------------------------------------------------------\n");
    while (entry_address < end_of_table)
    {
        HAL_READ_UINT32(CAN_ACCFILT_RAM_BASE + entry_address, data.dword);
        diag_printf("0x%04x:\t\t\t\t\t\t0x%x\n", entry_address, data.dword);
        entry_address += sizeof(cyg_uint32);    
    }
}
#endif // CYGDBG_DEVS_CAN_LPC2XXX_DEBUG

//===========================================================================
// Dump content of acceptance filter lookup table
//===========================================================================
#ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG
void lpc2xxx_can_reg_dump(struct cyg_devtab_entry* devtab_entry)
{
    can_channel *chan    = (can_channel*)devtab_entry->priv;
    cyg_uint32   reg_val;
    CAN_DECLARE_INFO(chan);
    
    chan = chan; // avoid compiler warnings for unused variables
    //
    // Print table of extended identifier groups
    //
    diag_printf("\n\nCAN REGISTER DUMP\n");
    diag_printf("\nRegister\tValue\n");
    diag_printf("----------------------------------------------------------\n"); 
    HAL_READ_UINT32(CAN_CTRL_MOD(info), reg_val);
    diag_printf("CANMOD\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_CMR(info), reg_val);
    diag_printf("CANCMR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_GSR(info), reg_val);
    diag_printf("CANGSR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_ICR(info), reg_val);
    diag_printf("CANICR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_IER(info), reg_val);
    diag_printf("CANIER\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_BTR(info), reg_val);
    diag_printf("CANBTR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_EWL(info), reg_val);
    diag_printf("CANEWL\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_SR(info), reg_val);
    diag_printf("CANSR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_RFS(info), reg_val);
    diag_printf("CANRFS\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_RID(info), reg_val);
    diag_printf("CANRID\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_RDA(info), reg_val);
    diag_printf("CANRDA\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CTRL_RDB(info), reg_val);
    diag_printf("CANRDB\t\t0x%08x\n", reg_val);
    
    diag_printf("\n\nCAN CENTRAL REGISTER DUMP\n");
    diag_printf("\nRegister\tValue\n");
    diag_printf("----------------------------------------------------------\n");
    HAL_READ_UINT32(CAN_CENTRAL_TXSR, reg_val);
    diag_printf("CANTxSR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CENTRAL_RXSR, reg_val);
    diag_printf("CANRxSR\t\t0x%08x\n", reg_val);
    HAL_READ_UINT32(CAN_CENTRAL_MSR, reg_val);
    diag_printf("CANMSR\t\t0x%08x\n", reg_val);
    
    diag_printf("\n\nCAN ACCEPTANCE FILTER REGISTER DUMP\n");
    diag_printf("\nRegister\tValue\n");
    diag_printf("----------------------------------------------------------\n");
    HAL_READ_UINT32(CAN_ACCFILT_AFMR, reg_val);
    diag_printf("AFMR\t\t0x%08x\n", reg_val); 
    HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR, reg_val);
    diag_printf("LUTERR\t\t0x%08x\n", reg_val); 
    HAL_READ_UINT32(CAN_ACCFILT_LUT_ERR_ADDR, reg_val);
    diag_printf("LUTERRADDR\t0x%08x\n", reg_val); 
}
#endif // #ifdef CYGDBG_DEVS_CAN_LPC2XXX_DEBUG

//---------------------------------------------------------------------------
// EOF can_accfilt_lpc2xxx.c