//==========================================================================
//
//      devs/serial/m68k/flexcan/current/src/can_mcf_flexcan.c
//
//      CAN driver for Motorola coldfire processors
//
//==========================================================================
//####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:         2005-05-12
// Purpose:      support coldfire on-chip flexcan moduls
// Description: 
//
//####DESCRIPTIONEND####
//
//==========================================================================


//==========================================================================
//                              INCLUDES
//==========================================================================
#include <pkgconf/system.h>
#include <pkgconf/io_can.h>
#include <pkgconf/devs_can_mcf52xx_flexcan.h>

#include <cyg/infra/diag.h>

#include <cyg/io/io.h>
#include <cyg/io/devtab.h>
#include <cyg/io/can.h>

#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/hal_io.h>


//===========================================================================
//                                DEFINES  
//===========================================================================

//
// we define our own ste of register bits in order to be independent from
// platform specific names
//

//---------------------------------------------------------------------------
// MCR regsiter bits
//
#define FLEXCAN_MCR_STOP                    (0x01 << 15)
#define FLEXCAN_MCR_FRZ                     (0x01 << 14)
#define FLEXCAN_MCR_HALT                    (0x01 << 12)
#define FLEXCAN_MCR_NOTRDY                  (0x01 << 11)
#define FLEXCAN_MCR_WAKEMSK                 (0x01 << 10)
#define FLEXCAN_MCR_SOFTRST                 (0x01 << 9)
#define FLEXCAN_MCR_FRZACK                  (0x01 << 8)
#define FLEXCAN_MCR_SUPV                    (0x01 << 7)
#define FLEXCAN_MCR_SELFWAKE                (0x01 << 6)
#define FLEXCAN_MCR_APS                     (0x01 << 5)
#define FLEXCAN_MCR_STOPACK                 (0x01 << 4)


//---------------------------------------------------------------------------
// CTRL0 register bits
//
#define FLEXCAN_CTRL0_BOFFMSK                   (0x01 << 7)
#define FLEXCAN_CTRL0_ERRMASK                   (0x01 << 6)
#define FLEXCAN_CTRL0_RXMODE                    (0x01 << 2)
#define FLEXCAN_CTRL0_RXMODE_0_DOMINANT         (0x00 << 2)
#define FLEXCAN_CTRL0_RXMODE_1_DOMINANT         (0x01 << 2)
#define FLEXCAN_CTRL0_TXMODE_MASK               (0x03 << 0)
#define FLEXCAN_CTRL0_TXMODE_SHIFT              0
#define FLEXCAN_CTRL0_TXMODE_FULL_0_DOMINANT    (0x00 << 0)
#define FLEXCAN_CTRL0_TXMODE_FULL_1_DOMINANT    (0x01 << 0)
#define FLEXCAN_CTRL0_TXMODE_OPEN_0_DOMINANT    (0x02 << 0)


//---------------------------------------------------------------------------
// CTRL1 register bits
//
#define FLEXCAN_CTRL1_SAMP                      (0x01 << 7)
#define FLEXCAN_CTRL1_TSYNC                     (0x01 << 5)
#define FLEXCAN_CTRL1_LBUF                      (0x01 << 4)
#define FLEXCAN_CTRL1_LOM                       (0x01 << 3)
#define FLEXCAN_CTRL1_PROPSEG_MASK              (0x07 << 0)
#define FLEXCAN_CTRL1_PROPSEG_SHIFT             0


//---------------------------------------------------------------------------
// CTRL2 register bits
//
#define FLEXCAN_CTRL2_RJW_MASK                  (0x03 << 6)
#define FLEXCAN_CTRL2_RJW_SHIFT                 6
#define FLEXCAN_CTRL2_PSEG1_MASK                (0x07 << 3)
#define FLEXCAN_CTRL2_PSEG1_SHIFT               3
#define FLEXCAN_CTRL2_PSEG2_MASK                (0x07 << 0)
#define FLEXCAN_CTRL2_PSEG2_SHIFT               0

//---------------------------------------------------------------------------
// ESTAT register bits
//
#define FLEXCAN_ESTAT_BITERR_MASK               (0x03 << 14)
#define FLEXCAN_ESTAT_BITERR_SHIFT              14
#define FLEXCAN_ESTAT_BITERR_NONE               (0x00 << 14)
#define FLEXCAN_ESTAT_BITERR_DOMINANT_RECESSIVE (0x01 << 14)
#define FLEXCAN_ESTAT_BITERR_RECESSIVE_DOMINANT (0x02 << 14)
#define FLEXCAN_ESTAT_ACKERR                    (0x01 << 13)
#define FLEXCAN_ESTAT_CRCERR                    (0x01 << 12)
#define FLEXCAN_ESTAT_FORMERR                   (0x01 << 11)
#define FLEXCAN_ESTAT_STUFFERR                  (0x01 << 10)
#define FLEXCAN_ESTAT_TXWARN                    (0x01 << 9)
#define FLEXCAN_ESTAT_RXWARN                    (0x01 << 8)
#define FLEXCAN_ESTAT_IDLE                      (0x01 << 7)
#define FLEXCAN_ESTAT_TX_RX                     (0x01 << 6)
#define FLEXCAN_ESTAT_FCS_MASK                  (0x03 << 4)
#define FLEXCAN_ESTAT_FCS_SHIFT                 4
#define FLEXCAN_ESTAT_FCS_ERROR_ACTIVE          (0x00 << 4)
#define FLEXCAN_ESTAT_FCS_ERROR_PASSIVE         (0x01 << 4)
#define FLEXCAN_ESTAT_BOFFINT                   (0x01 << 2)
#define FLEXCAN_ESTAT_ERRINT                    (0x01 << 1)
#define FLEXCAN_ESTAT_WAKEINT                   (0x01 << 0)

//
// For receive event calls we use these two identifiers for
// err and bus off events - message boxes use 0 - 15
//
#define FLEXCAN_ERR_EVENT                       16
#define FLEXCAN_BUSOFF_EVENT                    17
#define FLEXCAN_WAKE_EVENT                      18


//---------------------------------------------------------------------------
// message buffer cfg bits
//
#define MBOX_RXCODE_NOT_ACTIVE 0x00
#define MBOX_RXCODE_BUSY       0x10
#define MBOX_RXCODE_EMPTY      0x40
#define MBOX_RXCODE_FULL       0x20
#define MBOX_RXCODE_OVERRUN    0x60

#define MBOX_TXCODE_NOT_READY  0x80
#define MBOX_TXCODE_TRANSMIT   0xC0
#define MBOX_TXCODE_RESPONSE   0xA0

#define MBOX_DATA_FRAME        0x00 // data frame
#define MBOX_REMOTE_FRAME      0x01 // remote frame
#define MBOX_STD_ID            0x00 // standard identifier
#define MBOX_EXT_ID            0x01 // remote identifier
#define MBOX_TX                0x08 // tx message box
#define MBOX_RX                0x00 // rx messge box

#define MBOX_CFG_IDE           0x08
#define MBOX_CFG_RTR_EXT       0x01
#define MBOX_CFG_RTR_STD       0x10
#define MBOX_CFG_SSR           0x10
#define MBOX_CFG_DLC_MASK      0x0F
#define MBOX_CFG_STAT_MASK     0xF0


//---------------------------------------------------------------------------
// define
//
#define FLEXCAN_MBOX_NO        16
#define FLEXCAN_MBOX_MAX       15
#define FLEXCAN_MBOX_LAST_FREE 13


//===========================================================================
//                                 DATA TYPES
//===========================================================================
//
// Type of message buffer - required for function getevent in order to
// identify the type of message box that cause event
//
typedef enum
{
    MBOX_STATE_DISABLED,  // message box unused (free)
    MBOX_STATE_TX,        // TX message box
    MBOX_STATE_REMOTE_TX, // remote TX msaage box (data will be sent on reception of rtr frame) 
    MBOX_STATE_RX         // RX message box
} flexcan_mbox_state;


//
// configuration info for flexcan message buffer
//
typedef struct flexcan_mbox_info_st
{
    cyg_vector_t       isr_vec;           // isr vector
    int                isr_priority;      // isr priority
    cyg_interrupt      interrupt;         // stores interrupt data
    cyg_handle_t       interrupt_handle;  // stores interrupt number 
    cyg_uint8          num;               // number of message buffer
    bool               tx_busy;           // if true, then transmission is in progress
    flexcan_mbox_state state;             // message box state
} flexcan_mbox_info; 

//
// flexcan interrupt (busoff, err, wake) data - stores interrupt data for
// a non message box interrupt (bus off, err or wake interrupt)
//
typedef struct flexcan_int_st
{
    cyg_vector_t        isr_vec;  
    int                 isr_priority;
    cyg_interrupt       interrupt;
    cyg_handle_t        interrupt_handle;
} flexcan_int;

//
// flexcan message box initialisation
//
#define FLEXCAN_MBOX_INIT(_mbox0_vec, _prio, _mbox_no) { \
    isr_vec      : (_mbox0_vec) + (_mbox_no),            \
    isr_priority : (_prio),                              \
    num          : (_mbox_no),                           \
    tx_busy      : false                                 \
}

//
// Interrupt initialisation
//
#define FLEXCAN_INT_INIT(_vec, _prio) \
{                                     \
    isr_vec      : (_vec),            \
    isr_priority : (_prio)            \
}

//
// flexcan configuration 
//
typedef struct flexcan_info 
{
    cyg_uint8          *base;            // base address of flexcan modul
    cyg_vector_t        isr_vec_mbox0;   // vector number of ISR vector of first message box
    flexcan_mbox_info   mboxes[FLEXCAN_MBOX_NO];// message boxes
    cyg_uint32          last_tx_id;      // last transmitted message identifier
    
    cyg_uint32          rxgmask;         // acceptance filter for message box 0 - 13
    cyg_uint32          rx14mask;        // acceptance filter for message box 14
    cyg_uint32          rx15mask;        // acceptance filter for message box 15
    
    flexcan_int         boff_int;        // bus off interrupt data
    flexcan_int         err_int;         // error interrupt data
    flexcan_int         wake_int;        // wake interrupt data
    
    cyg_uint8           tx_all_mbox;     // number of message box for all transmit messages
    cyg_uint8           rx_all_mbox_std; // number of message box for reception of all standard CAN messages
    cyg_uint8           rx_all_mbox_ext; // number of message box for reception of all extended CAN messages
    cyg_uint8           free_mboxes;     // number of free message boxes for msg filters and rtr buffers
    cyg_can_state       state;           // state of CAN controller
    
    cyg_uint16          imask_shadow;    // interrupt mask shadow register
#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT 
    cyg_can_message     last_tx_msg;     // stores last transmitted message for TX events
#endif

#ifdef FLEXCAN_CAN_STATS
    cyg_uint32          isr_count;
    cyg_uint32          dsr_count;
    cyg_uint32          rx_bytes;
    cyg_uint32          tx_bytes;
    cyg_uint32          rx_errors;
#endif  
} flexcan_info;


//
// flexcan info initialisation
//
#define FLEXCAN_INFO(_l,                                                \
                     _baseaddr,                                         \
                     _isr_vec_mbox0,                                    \
                     _mbox0_isr_prio,                                   \
                     _mbox1_isr_prio,                                   \
                     _mbox2_isr_prio,                                   \
                     _mbox3_isr_prio,                                   \
                     _mbox4_isr_prio,                                   \
                     _mbox5_isr_prio,                                   \
                     _mbox6_isr_prio,                                   \
                     _mbox7_isr_prio,                                   \
                     _mbox8_isr_prio,                                   \
                     _mbox9_isr_prio,                                   \
                     _mbox10_isr_prio,                                  \
                     _mbox11_isr_prio,                                  \
                     _mbox12_isr_prio,                                  \
                     _mbox13_isr_prio,                                  \
                     _mbox14_isr_prio,                                  \
                     _mbox15_isr_prio,                                  \
                     _rxgmask,                                          \
                     _rx14mask,                                         \
                     _rx15mask,                                         \
                     _boff_isr_vec, _boff_isr_prio,                     \
                     _err_isr_vec, _err_isr_prio,                       \
                     _wake_isr_vec, _wake_isr_prio,                     \
                     _tx_all_mbox, _rx_all_mbox_std, _rx_all_mbox_ext)  \
flexcan_info _l = {                                                     \
    (void *)( _baseaddr),                                               \
    (_isr_vec_mbox0),                                                   \
    mboxes : {                                                          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox0_isr_prio), 0),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox1_isr_prio), 1),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox2_isr_prio), 2),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox3_isr_prio), 3),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox4_isr_prio), 4),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox5_isr_prio), 5),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox6_isr_prio), 6),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox7_isr_prio), 7),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox8_isr_prio), 8),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox9_isr_prio), 9),          \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox10_isr_prio),10),         \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox11_isr_prio),11),         \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox12_isr_prio),12),         \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox13_isr_prio),13),         \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox14_isr_prio),14),         \
    FLEXCAN_MBOX_INIT((_isr_vec_mbox0), (_mbox15_isr_prio),15),         \
    },                                                                  \
    last_tx_id      : 0xFFFFFFFF,                                       \
    rxgmask         : _rxgmask,                                         \
    rx14mask        : _rx14mask,                                        \
    rx15mask        : _rx15mask,                                        \
    boff_int        : FLEXCAN_INT_INIT(_boff_isr_vec, _boff_isr_prio),  \
    err_int         : FLEXCAN_INT_INIT(_err_isr_vec, _err_isr_prio),    \
    wake_int        : FLEXCAN_INT_INIT(_wake_isr_vec, _wake_isr_prio),  \
    tx_all_mbox     : _tx_all_mbox,                                     \
    rx_all_mbox_std : _rx_all_mbox_std,                                 \
    rx_all_mbox_ext : _rx_all_mbox_ext,                                 \
    free_mboxes     : FLEXCAN_MBOX_LAST_FREE,                           \
    state           : CYGNUM_CAN_STATE_ACTIVE                           \
};


//===========================================================================
//                          GLOBAL DATA
//===========================================================================
//
// Note: two levels of macro are required to get proper expansion.
//
#define _FLEXCAN_MBOX_INTPRIO(n) CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX##n
#define FLEXCAN_MBOX_INTPRIO(n) _FLEXCAN_MBOX_INTPRIO(n)


//
// FlexCAN channel initialisation
//
FLEXCAN_INFO(flexcan_can0_info, 
             HAL_MCF52xx_MBAR + HAL_MCF52xx_FLEXCAN0_BASE,
             HAL_MCF52xx_FLEXCAN0_MBOX0_ISRVEC,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX0,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX1,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX2,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX3,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX4,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX5,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX6,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX7,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX8,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX9,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX10,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX11,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX12,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX13,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX14,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_MBOX15,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_RXMASK_GLOBAL,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_RXMASK_14,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_RXMASK_15,
             HAL_MCF52xx_FLEXCAN0_BOFF_ISRVEC,
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_BOFFINT,  
             HAL_MCF52xx_FLEXCAN0_ERR_ISRVEC,          
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_ERRINT,
             HAL_MCF52xx_FLEXCAN0_WAKE_ISRVEC,       
             CYGNUM_DEVS_CAN_MCF52xx_ISR_PRIORITY_FLEXCAN0_WAKEINT,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_DEFAULT_TX_MBOX,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_DEFAULT_RX_MBOX_STD,
             CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_DEFAULT_RX_MBOX_EXT);

//
// message box structure for hardware access of message box
//
typedef struct flexcan_mbox
{
    cyg_uint8  timestamp; 
    cyg_uint8  ctrlstat;
    cyg_uint16 id_hi;
    cyg_uint16 id_lo;
    cyg_uint8  data[8];
    cyg_uint16 reserved;
} flexcan_mbox;

//
// flexcan register layout for hardware register access of FlexCAN modul
//
typedef struct flexcan_regs
{
    cyg_uint16 CANMCR;       // 0x00
    cyg_uint16 reserved0[2]; // 0x02
    cyg_uint8  CANCTRL0;     // 0x06
    cyg_uint8  CANCTRL1;     // 0x07
    cyg_uint8  PRESDIV;      // 0x08
    cyg_uint8  CANCTRL2;     // 0x09
    cyg_uint16 TIMER;        // 0x0A
    cyg_uint16 reserved1[2]; // 0x0C
    cyg_uint16 RXGMASK_HI;   // 0x10
    cyg_uint16 RXGMASK_LO;   // 0x12
    cyg_uint16 RX14MASK_HI;  // 0x14
    cyg_uint16 RX14MASK_LO;  // 0x16
    cyg_uint16 RX15MASK_HI;  // 0x18
    cyg_uint16 RX15MASK_LO;  // 0x1A
    cyg_uint16 reserved2[2]; // 0x1C
    cyg_uint16 ESTAT;        // 0x20
    cyg_uint16 IMASK;        // 0x22
    cyg_uint16 IFLAG;        // 0x24
    cyg_uint8  RXERRCNT;     // 0x26
    cyg_uint8  TXERRCNT;     // 0x27
    cyg_uint16 reserved3[44];// 0x28
    flexcan_mbox mbox[16]; // 0x80
} flexcan_regs;


//===========================================================================
//                                 LOCAL DATA
//===========================================================================
static cyg_uint16 flexcan_baud_rates[] = {
    0,      // Unused
    10,     // 10 kbit/s
    20,     // 20
    50,     // 50
    100,    // 100
    125,    // 125
    250,    // 250
    500,    // 500
    800,    // 800
    1000,   // 1000 kbit/s
};


//===========================================================================
//                              PROTOTYPES
//===========================================================================
static bool        flexcan_init(struct cyg_devtab_entry* devtab_entry);
static Cyg_ErrNo   flexcan_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name);
static Cyg_ErrNo   flexcan_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static Cyg_ErrNo   flexcan_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len);
static bool        flexcan_putmsg(can_channel *priv, cyg_can_message *pmsg, void *pdata);
static bool        flexcan_getevent(can_channel *priv, cyg_can_event *pevent, void *pdata);
static void        flexcan_start_xmit(can_channel* chan);
static void        flexcan_stop_xmit(can_channel* chan);

//
// TX and RX ISRs and DSRs
//
static cyg_uint32  flexcan_mbox_rx_isr(cyg_vector_t, cyg_addrword_t);
static void        flexcan_mbox_rx_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
static cyg_uint32  flexcan_mbox_tx_isr(cyg_vector_t, cyg_addrword_t);
static void        flexcan_mbox_tx_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);

//
// All other flexcan interrupt handlers
//
static cyg_uint32  flexcan_err_isr(cyg_vector_t, cyg_addrword_t);
static void        flexcan_err_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
static cyg_uint32  flexcan_busoff_isr(cyg_vector_t, cyg_addrword_t);
static void        flexcan_busoff_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);
static cyg_uint32  flexcan_wake_isr(cyg_vector_t, cyg_addrword_t);
static void        flexcan_wake_dsr(cyg_vector_t, cyg_ucount32, cyg_addrword_t);


static bool flexcan_cfg_mbox_tx(flexcan_mbox *pmbox, cyg_can_message  *pmsg, bool rtr);
static void flexcan_cfg_mbox_rx(flexcan_mbox *pmbox, cyg_can_message  *pmsg);
static void flexcan_read_from_mbox(can_channel *chan, cyg_uint8 mbox, cyg_can_event *pevent, cyg_uint8 *ctrlstat);
static void flexcan_set_acceptance_mask(cyg_uint16 *rxmask_reg, cyg_uint32 mask, cyg_can_id_type ext);
static void flexcan_start_chip(can_channel *chan);
static void flexcan_enter_standby(can_channel *chan, bool selfwake);
static void flexcan_stop_chip(can_channel *chan);
static void flexcan_leave_standby(can_channel *chan);
static bool flexcan_set_baud(can_channel *chan, cyg_uint16 baudrate);
static bool flexcan_config(can_channel* chan, cyg_can_info_t* config, cyg_bool init);
static cyg_int8 flexcan_alloc_mbox(flexcan_info *info);
static void flexcan_disable_mbox(can_channel *chan, cyg_uint32 mbox_id);
static void flexcan_setup_rxmbox(can_channel *chan, cyg_uint32 mbox_id, cyg_can_message *pmsg);
static void flexcan_setup_txmbox(can_channel *chan, cyg_uint32 mbox_id, cyg_can_message *pmsg);
static void flexcan_setup_rtrmbox(can_channel *chan, cyg_uint32 mbox_id, cyg_can_message *pmsg);
static void flexcan_mboxint_enable(flexcan_info *info, cyg_uint32 mbox_id);
static void flexcan_mboxint_disable(flexcan_info *info, cyg_uint32 mbox_id);


CAN_LOWLEVEL_FUNS(flexcan_lowlevel_funs,
                  flexcan_putmsg,
                  flexcan_getevent,
                  flexcan_get_config,
                  flexcan_set_config,
                  flexcan_start_xmit,
                  flexcan_stop_xmit
     );


cyg_can_event    flexcan_can0_rxbuf[CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_QUEUESIZE_RX]; // buffer for 32 rx can events
cyg_can_message  flexcan_can0_txbuf[CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_QUEUESIZE_TX]; // buffer for 32 tx can messageds


CAN_CHANNEL_USING_INTERRUPTS(flexcan_can0_chan,
                             flexcan_lowlevel_funs,
                             flexcan_can0_info,
                             CYG_CAN_BAUD_RATE(CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_KBAUD),
                             flexcan_can0_txbuf, CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_QUEUESIZE_TX,
                             flexcan_can0_rxbuf, CYGNUM_DEVS_CAN_MCF52xx_FLEXCAN0_QUEUESIZE_RX
    );


DEVTAB_ENTRY(flexcan_devtab, 
             CYGDAT_DEVS_CAN_MCF52xx_FLEXCAN0_NAME,
             0,                     // Does not depend on a lower level interface
             &cyg_io_can_devio, 
             flexcan_init, 
             flexcan_lookup,        // CAN driver may need initializing
             &flexcan_can0_chan
    );


//===========================================================================
//  Lookup the device and return its handle
//===========================================================================
static Cyg_ErrNo
flexcan_lookup(struct cyg_devtab_entry** tab, struct cyg_devtab_entry* sub_tab, const char* name)
{
    can_channel* chan    = (can_channel*) (*tab)->priv;
    (chan->callbacks->can_init)(chan);
    
    return ENOERR;
}


//===========================================================================
//  Enable message box interrupt for one message box
//===========================================================================
static void flexcan_mboxint_enable(flexcan_info *info, cyg_uint32 mbox_id)
{
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    
    info->imask_shadow |= (0x0001 << mbox_id);
    HAL_WRITE_UINT16(&flexcan->IMASK, info->imask_shadow);
}


//===========================================================================
//  Disable message box interrupt for one message box
//===========================================================================
static void flexcan_mboxint_disable(flexcan_info *info, cyg_uint32 mbox_id)
{
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    
    info->imask_shadow &= ~(0x0001 << mbox_id);
    HAL_WRITE_UINT16(&flexcan->IMASK, info->imask_shadow);
}

//===========================================================================
// Allocate message box
// Try to find a free message box and return its ID
//===========================================================================
static cyg_int8 flexcan_alloc_mbox(flexcan_info *info)
{
    cyg_uint8     i;
    cyg_int8      res = -1;
    
    if (info->free_mboxes)
    {  
        for (i = 0; i < FLEXCAN_MBOX_LAST_FREE; ++i)
        {
            if (MBOX_STATE_DISABLED == info->mboxes[i].state)
            {
                info->free_mboxes--;
                res = i;
                break;
            }               
        }  // for (i = 0; i < FLEXCAN_MBOX_LAST_FREE; ++i)
    } // if (info->free_mboxes)
    
    return res;
}


//===========================================================================
// Prepare message buffer filter
// Setup a RX message box for reception of a certain CAN identifier
//===========================================================================
static void flexcan_setup_rxmbox(can_channel     *chan, 
                                 cyg_uint32       mbox_id,
                                 cyg_can_message *pmsg)
{
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    flexcan_mbox_info *pmbox;
    
    info->mboxes[mbox_id].state = MBOX_STATE_RX;
    pmbox = &info->mboxes[mbox_id];
    flexcan_cfg_mbox_rx(&flexcan->mbox[mbox_id], pmsg);
    
    //
    // prepare message box interrupt for message box 
    //
    cyg_drv_interrupt_create(pmbox->isr_vec,
                             pmbox->isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_mbox_rx_isr,
                             &flexcan_mbox_rx_dsr,
                             &(pmbox->interrupt_handle),
                             &(pmbox->interrupt));
    cyg_drv_interrupt_attach(pmbox->interrupt_handle);
    cyg_drv_interrupt_unmask(pmbox->isr_vec);
    
    //
    // now enable interrupt for this message box
    //
    flexcan_mboxint_enable(info, mbox_id);
}


//===========================================================================
// Disable a message box - after this call a message box is available
// again for message filters or remote buffers
//===========================================================================
static void flexcan_disable_mbox(can_channel *chan, cyg_uint32 mbox_id)
{
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    flexcan_mbox_info *pmbox;
    
    //
    // if message box is already disabled we do not need to disable it
    // here
    //
    if (MBOX_STATE_DISABLED == info->mboxes[mbox_id].state)
    {
        return;
    }
    
    HAL_WRITE_UINT8(&flexcan->mbox[mbox_id].ctrlstat, MBOX_RXCODE_NOT_ACTIVE);
    info->mboxes[mbox_id].state = MBOX_STATE_DISABLED;
    pmbox = &info->mboxes[mbox_id];
    
    //
    // now disable interrupts for this message box and free all
    // interrupt resources
    //
    flexcan_mboxint_disable(info, mbox_id);
    cyg_drv_interrupt_mask(pmbox->isr_vec);
    cyg_drv_interrupt_detach(pmbox->interrupt_handle);
    cyg_drv_interrupt_delete(pmbox->interrupt_handle);
    
    info->free_mboxes++;
}


//===========================================================================
// Setup a transmit message box
//===========================================================================
static void flexcan_setup_txmbox(can_channel      *chan,
                                 cyg_uint32        mbox_id,
                                 cyg_can_message  *pmsg)
{
    flexcan_info      *info   = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    flexcan_mbox_info *pmbox;
    
    info->mboxes[mbox_id].state = MBOX_STATE_TX;
    pmbox = &info->mboxes[mbox_id];
    flexcan_cfg_mbox_tx(&flexcan->mbox[mbox_id], pmsg, false);

    //
    // prepare message box interrupt for message box 
    //
    cyg_drv_interrupt_create(pmbox->isr_vec,
                             pmbox->isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_mbox_tx_isr,
                             &flexcan_mbox_tx_dsr,
                             &(pmbox->interrupt_handle),
                             &(pmbox->interrupt));
    cyg_drv_interrupt_attach(pmbox->interrupt_handle);
    cyg_drv_interrupt_unmask(pmbox->isr_vec);
    
    //
    // now enable interrupt for this message box
    //
    flexcan_mboxint_enable(info, mbox_id);
}

//===========================================================================
// Setup a RTR response message box
//===========================================================================
static void flexcan_setup_rtrmbox(can_channel      *chan,
                                  cyg_uint32        mbox_id,
                                  cyg_can_message  *pmsg)
{
    flexcan_info      *info   = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    flexcan_mbox_info *pmbox;
    
    info->mboxes[mbox_id].state = MBOX_STATE_REMOTE_TX;
    pmbox = &info->mboxes[mbox_id];
    flexcan_cfg_mbox_tx(&flexcan->mbox[mbox_id], pmsg, true);

    //
    // prepare message box interrupt for message box 
    //
    cyg_drv_interrupt_create(pmbox->isr_vec,
                             pmbox->isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_mbox_rx_isr,
                             &flexcan_mbox_rx_dsr,
                             &(pmbox->interrupt_handle),
                             &(pmbox->interrupt));
    cyg_drv_interrupt_attach(pmbox->interrupt_handle);
    cyg_drv_interrupt_unmask(pmbox->isr_vec);
}


//===========================================================================
// Set device configuration
//===========================================================================
static Cyg_ErrNo
flexcan_set_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
    Cyg_ErrNo     res = ENOERR;
    flexcan_info *info       = (flexcan_info *)chan->dev_priv;
    
    switch(key) 
    {
        //
        //Setup a new CAN configuration. This will i.e. setup a new baud rate
        //
        case CYG_IO_SET_CONFIG_CAN_INFO:
             {
                 cyg_can_info_t*  config = (cyg_can_info_t*) buf;
                 if (*len < sizeof(cyg_can_info_t)) 
                 {
                    return -EINVAL;
                 }
                 *len = sizeof(cyg_can_info_t);
                 if (!flexcan_config(chan, config, false)) 
                 {
                    return -EINVAL;
                 }
            }
            break;
       
        //
        // Try to add a new RTR response message buffer for automatic transmisson
        // of data frame on reception of a remote frame
        //     
        case CYG_IO_SET_CONFIG_CAN_REMOTE_BUF:
             { 
                 cyg_can_remote_buf *rtr_buf    = (cyg_can_remote_buf*) buf;
                             
                 if (*len != sizeof(cyg_can_remote_buf)) 
                 {
                    return -EINVAL;
                 }
                 *len = sizeof(cyg_can_remote_buf);
                 
                 //
                 // If we need to create a new remote buffer then we check if there
                 // is one message box free and then setup this box for remote frame
                 // reception
                 //
                 if (CYGNUM_CAN_MSGBUF_INIT == rtr_buf->handle)
                 {
                     cyg_int8 mbox_index = flexcan_alloc_mbox(info);
                     
                     if (mbox_index > -1)
                     {
                         //
                         // if we have a free message buffer then we setup this buffer
                         // for remote frame reception
                         //
                         rtr_buf->handle = mbox_index;
                         flexcan_setup_rtrmbox(chan, rtr_buf->handle, &rtr_buf->msg);
                     }
                     else
                     {
                         rtr_buf->handle = CYGNUM_CAN_MSGBUF_NA;
                     }
                 } // if (CYGNUM_CAN_RTR_BUF_INIT == rtr_buf->handle)
                 else // if (!(CYGNUM_CAN_MBUF_INIT == rtr_buf->handle))
                 {
                     //
                     // If we have a valid rtr buf handle then we can store data into 
                     // rtr message box
                     //
                     if ((rtr_buf->handle >= 0) && (rtr_buf->handle < FLEXCAN_MBOX_LAST_FREE))
                     {
                         flexcan_regs *flexcan = (flexcan_regs *)info->base;
                         flexcan_cfg_mbox_tx(&flexcan->mbox[rtr_buf->handle], &rtr_buf->msg, true); 
                     }
                     else
                     {
                        return -EINVAL;
                     }
                 } // if (!(CYGNUM_CAN_MBUF_INIT == rtr_buf->handle))        
             }
             break;
        
        //
        // Add a new message filter for reception of a certain CAN id. A call
        // to this function will disable the RX all message boxes
        //     
        case CYG_IO_SET_CONFIG_CAN_FILTER_MSG :
             {
                 cyg_int8        mbox_index; 
                 cyg_can_filter *filter   = (cyg_can_filter*) buf;
                 
                 if (*len != sizeof(cyg_can_filter)) 
                 {
                    return -EINVAL;
                 }
                 *len = sizeof(cyg_can_filter);
                 
                 //
                 // try to allocate a free message box - if we have a free one
                 // then we can prepare the message box for reception of the
                 // desired message id
                 //
                 mbox_index = flexcan_alloc_mbox(info);
                 if (mbox_index > -1)
                 {
                     //
                     // if message filtering is used the generic RX mbox should
                     // be disabled
                     //
                     if (MBOX_STATE_DISABLED != info->mboxes[info->rx_all_mbox_std].state)
                     {
                         flexcan_disable_mbox(chan, info->rx_all_mbox_std);
                     }
                     
                     if (MBOX_STATE_DISABLED != info->mboxes[info->rx_all_mbox_ext].state)
                     {
                         flexcan_disable_mbox(chan, info->rx_all_mbox_ext);
                     }
                     
                     //
                     // generic RX mbox is disabled and now we can setup message filter
                     // 
                     filter->handle = mbox_index;
                     flexcan_setup_rxmbox(chan, mbox_index, &filter->msg);
                 }
                 else
                 {
                     filter->handle = CYGNUM_CAN_MSGBUF_NA;            
                 }    
             }
             break; // CYG_IO_SET_CONFIG_CAN_FILTER_MSG
        
        //
        // Setup the FlexCAN modul for reception of any kind of CAN messages 
        // This call will setup 2 message boxes, one for all standad frames and one 
        // for all extended frames. This function will diable all other message 
        // filters
        //
        case CYG_IO_SET_CONFIG_CAN_FILTER_ALL:
             {
                 cyg_uint8          i;
                 flexcan_mbox_info *pmbox;
                 cyg_can_message    filter_param;
                 //
                 // before we can enable the generic receive message box
                 // we have to disable all other RX message boxes
                 //
                 for (i = 0; i < FLEXCAN_MBOX_LAST_FREE; ++i)
                 {
                     pmbox = &info->mboxes[i];
                     if (MBOX_STATE_RX == pmbox->state)
                     {
                         flexcan_disable_mbox(chan, i);
                     }
                 }
                 
                 //
                 // id does not matter here because the message buffers 14 and 15
                 // have their own acceptance mask
                 //
                 filter_param.id  = 0;
                 filter_param.ext = CYGNUM_CAN_ID_STD;
                 flexcan_setup_rxmbox(chan, info->rx_all_mbox_std, &filter_param);
                 filter_param.ext = CYGNUM_CAN_ID_EXT;
                 flexcan_setup_rxmbox(chan, info->rx_all_mbox_ext, &filter_param);
             }
             break;
        
        //
        // Change CAN state of FlexCAN modul. This function will set the FlexCAN
        // modul into STOPPED, ACTIVE or STANDBY state
        //    
        case CYG_IO_SET_CONFIG_CAN_MODE :
             {
                cyg_can_mode   *can_mode  = (cyg_can_mode*) buf;
                
                if (*len != sizeof(cyg_can_mode)) 
                {
                    return -EINVAL;
                }
                *len = sizeof(cyg_can_mode);
                
                //
                // decide what to do acording to mode
                //
                switch (*can_mode)
                {
                    case CYGNUM_CAN_MODE_STOP :      // stop FlexCANm modul
                         flexcan_stop_chip(chan);
                         break;
                         
                    case CYGNUM_CAN_MODE_START :     // start FlexCAN modul
                         flexcan_leave_standby(chan);
                         break;
                          
                    case CYGNUM_CAN_MODE_STANDBY :   // set FlexCAN modul into standby state
                         flexcan_enter_standby(chan, true);
                         break;
                }
             }
             break; // case CYG_IO_SET_CONFIG_CAN_MODE :         
    } // switch (key)
    
    return res;
}


//===========================================================================
//  Query device configuration
//===========================================================================
static Cyg_ErrNo
flexcan_get_config(can_channel *chan, cyg_uint32 key, const void* buf, cyg_uint32* len)
{
    Cyg_ErrNo     res  = ENOERR;
    flexcan_info *info = (flexcan_info *)chan->dev_priv;
    
    switch(key)
    {
        //
        // query state of CAN controller
        //
        case CYG_IO_GET_CONFIG_CAN_STATE :
             {
                cyg_can_state *can_state  = (cyg_can_state*) buf;
                
                if (*len != sizeof(cyg_can_state)) 
                {
                    return -EINVAL;
                }
                *len = sizeof(cyg_can_state);
                *can_state = info->state;
             }
             break;
        
        //
        // Query message box information - returns available and free message
        // boxes
        //     
        case CYG_IO_GET_CONFIG_CAN_MSGBUF_INFO :
             {
                 cyg_can_msgbuf_info *mbox_info  = (cyg_can_msgbuf_info*) buf;
                
                 if (*len != sizeof(cyg_can_msgbuf_info)) 
                 {
                     return -EINVAL;
                 }
                *len = sizeof(cyg_can_msgbuf_info);
                
                 mbox_info->count = FLEXCAN_MBOX_LAST_FREE;
                 mbox_info->free  = info->free_mboxes;
             }
             break;
        
        //
        // Query hardware description of FlexCAN device driver
        //     
        case CYG_IO_GET_CONFIG_CAN_HDI :
             {
                cyg_can_hdi *hdi = (cyg_can_hdi *)buf;
                //
                // comes from high level driver so we do not need to
                // check buffer size here
                //             
                hdi->support_flags = CYGNUM_CAN_HDI_FRAMETYPE_EXT_ACTIVE
                                   | CYGNUM_CAN_HDI_FULLCAN;
#ifdef CYGOPT_IO_CAN_SUPPORT_TIMESTAMP 
                hdi->support_flags |= CYGNUM_CAN_HDI_TIMESTAMP;
#endif
             }
             break;
             
        default :
            res = -EINVAL;
    }// switch(key)
    
    return res;
} 


//===========================================================================
//  Read one event from can hardware - called from high level I/O CAN driver
//===========================================================================
static bool flexcan_getevent(can_channel *chan, cyg_can_event *pevent, void *pdata)
{
    flexcan_info *info           = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan        = (flexcan_regs *)info->base;
    cyg_uint8     mbox_ctrlstat;
    bool          res            = true;
    cyg_uint8     event_id       = *((cyg_uint8 *)pdata);
    cyg_uint16    estat;
     
    //   
    // if event_id is 0 - 15 the we have a message box event
    //
    if (event_id < FLEXCAN_ERR_EVENT)
    {   
        flexcan_mbox_info *pmbox_info;
        
        pmbox_info = &info->mboxes[event_id];
        
        //
        // Deceide what to do according to type of message box that caused this event
        //
        switch (pmbox_info->state)
        {
            //
            // If we have an RX event then we need to read the received data from
            // message box that caused this event and fill it into message queue of
            // high level I/O CAN driver
            //
            case MBOX_STATE_RX:
                 //
                 // read data from message box - during processing of this function
                 // the message box is locked and cannot receive further messages
                 //
                 flexcan_read_from_mbox(chan, event_id, pevent, &mbox_ctrlstat); 

                 //
                 // If we received a self transmitted frame
                 // then this is not really an rx event and we return false. We rely on the
                 // fact here that two devices in network do not send the same identifier
                 //
                 if (pevent->msg.id == info->last_tx_id)    
                 {   
                     info->last_tx_id = 0xFFFFFFFF; // set last received ID to an invalid value
                     res = false; 
                 }
                 else
                 {                
                     pevent->flags = CYGNUM_CAN_EVENT_RX;

                     //
                     // check if an overun occured in this message box
                     //
                     if ((mbox_ctrlstat & MBOX_RXCODE_OVERRUN) == MBOX_RXCODE_OVERRUN)
                     {
                         pevent->flags |= CYGNUM_CAN_EVENT_OVERRUN_RX;
                     }
                 }
                 break;           

#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT
//
            // If a TX message box cause the event then we store the last transmitted
            // message into the receive message queue
            //
            case MBOX_STATE_TX:  
                 pevent->flags = CYGNUM_CAN_EVENT_TX;
                 pevent->msg = info->last_tx_msg;
                 break;
#endif // CYGOPT_IO_CAN_TX_EVENT_SUPPORT
                            
            case MBOX_STATE_REMOTE_TX:
                 break;
                 
            default:
                 res = false;
        } // switch (pmbox->state)
    }
    else // (event_id >= FLEXCAN_ERR_EVENT)
    {
        //
        // We have an status event - check if it is an bus off interrupt or an
        // error interrupt and provide error information to upper layer
        //
        HAL_READ_UINT16(&flexcan->ESTAT, estat);
        pevent->msg.data[0] = estat & 0xFF;
        pevent->msg.data[1] = (estat >> 8) & 0xFF; 
        HAL_READ_UINT8(&flexcan->RXERRCNT, pevent->msg.data[2]);
        HAL_READ_UINT8(&flexcan->TXERRCNT, pevent->msg.data[3]);
        switch (event_id)
        {
            case FLEXCAN_ERR_EVENT :
                 //
                 // indicate error passive event and provide content of estat register
                 // for a more precise error information
                 //
                 if (estat & FLEXCAN_ESTAT_FCS_ERROR_PASSIVE)
                 {
                    pevent->flags = CYGNUM_CAN_EVENT_ERR_PASSIVE;
                 }
                 //
                 // If we are not in error passive state then we check if the
                 // error counters reached the warning level
                 //
                 else
                 {               
                     //
                     // indicate tx error counter warning level reached
                     //
                     if (estat & FLEXCAN_ESTAT_TXWARN)
                     {
                         pevent->flags |= CYGNUM_CAN_EVENT_WARNING_TX;
                     }
                 
                     //
                     // indicate rx error counter warning level reached
                     //
                     if (estat & FLEXCAN_ESTAT_RXWARN)
                     {
                         pevent->flags |= CYGNUM_CAN_EVENT_WARNING_RX;
                     }
                 }
                 break;
                 
            case FLEXCAN_BUSOFF_EVENT:
                 pevent->flags = CYGNUM_CAN_EVENT_BUS_OFF;
                 break;
                 
            case FLEXCAN_WAKE_EVENT:
                 pevent->flags = CYGNUM_CAN_EVENT_LEAVING_STANDBY;
                 break;
        } // switch (event_id)
    }
    
    return res;
}


//===========================================================================
// Send one CAN message to CAN hardware
//===========================================================================
static bool flexcan_putmsg(can_channel *chan, cyg_can_message *pmsg, void *pdata)
{
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    cyg_uint8          mbox    = *((cyg_uint8 *)pdata);
    flexcan_mbox_info *pmbox   = &info->mboxes[mbox];
    cyg_uint16         iflag;
   
    HAL_READ_UINT16(&flexcan->IFLAG, iflag);
    
    //
    // check if device is busy sending a message
    //
    if (pmbox->tx_busy)
    {
        //
        // if device is busy and the interrupt flag is set, then we know
        // that device is not busy any longer
        //
        if (iflag & (0x0001 << mbox))
        {
            HAL_WRITE_UINT16(&flexcan->IFLAG, (0x0001 << mbox));
        }
        else
        {
            return false;
        }
    }
    
    pmbox->tx_busy    = true;     // mark transmitter as busy
    info->last_tx_id  = pmsg->id; // store message in order to identify self recieved frames 

#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT 
    info->last_tx_msg = *pmsg;    // store the transmitted message for TX events
#endif  
  
    flexcan_cfg_mbox_tx(&flexcan->mbox[mbox], pmsg, false); // send message
    
    return true;
}


//===========================================================================
// Flexcan start xmit - If the chip is in standby mode then a call to this
// function will cause the FlexCAN modul to leave the standby mode. So
// the output queue should be empty before entering stadby mode
//===========================================================================
static void flexcan_start_xmit(can_channel* chan)
{
    flexcan_info         *info    = (flexcan_info *)chan->dev_priv;
    CYG_INTERRUPT_STATE   saved_state;

    HAL_DISABLE_INTERRUPTS(saved_state);
    
    //
    // if we are in standby state the we leave standby state now. This is
    // the reason that the user should wait until the TX queue is empty before 
    // entering standby mode- or he should drain or flush the TX queue
    //
    if (CYGNUM_CAN_STATE_STANDBY == info->state)
    {
        flexcan_leave_standby(chan);
    }
    
    //
    // Now enable message box 15 interrupts
    //
    flexcan_mboxint_enable(info, info->tx_all_mbox);
    
    //
    // kick transmitter
    //
    chan->callbacks->xmt_msg(chan, &info->tx_all_mbox);  // Kick transmitter (if necessary)
    
    HAL_RESTORE_INTERRUPTS(saved_state);
}


//===========================================================================
// Flexcan stop transmission
//===========================================================================
static void flexcan_stop_xmit(can_channel* chan)
{
    flexcan_info         *info    = (flexcan_info *)chan->dev_priv;
    CYG_INTERRUPT_STATE  saved_state;

    HAL_DISABLE_INTERRUPTS(saved_state);
    
    //
    // Now disable message box 15 interrupts
    //
    flexcan_mboxint_disable(info, info->tx_all_mbox);

    HAL_RESTORE_INTERRUPTS(saved_state);
}


//===========================================================================
// Configure flexcan channel
//===========================================================================
static bool flexcan_config(can_channel* chan, cyg_can_info_t* config, cyg_bool init)
{
    flexcan_info    *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs    *flexcan = (flexcan_regs *)info->base;
    cyg_uint16       tmp16;
    cyg_uint8        tmp8;
    cyg_uint8        i;
    cyg_can_message  rx_all_filter =
    {
        id  : 0,                 // CAN identifier      
    };
    
    //
    // the first thing we need to do is to stop the chip
    //
    flexcan_stop_chip(chan);  
    
    //
    // if this is the first initialisation of the FlexCAN modul we need to execute
    // some extra steps here
    //
    if (init)
    {
#if defined(CYGPKG_DEVS_CAN_MCF52xx_FLEXCAN0) && defined(HAL_MCF52xx_FLEXCAN0_PROC_INIT)
        if (info == &flexcan_can0_info) {
            HAL_MCF52xx_FLEXCAN0_PROC_INIT();
        }
#endif
        
        //
        // Issue a reset in order to go into halt mode. The reset will set the
        // the halt bit in mcr
        //
        HAL_READ_UINT16(&flexcan->CANMCR, tmp16);
        tmp16 &= ~FLEXCAN_MCR_FRZ;
        HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16);
        tmp16 |= FLEXCAN_MCR_SOFTRST;
        HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16);
        HAL_DELAY_US(10);
    
        //
        // Check reset status
        //
        HAL_READ_UINT16(&flexcan->CANMCR, tmp16);
        if (tmp16 & FLEXCAN_MCR_SOFTRST)
        {
            return false;
        }
    
        //
        // Initialize the transmit and receive pin modes
        //
        HAL_WRITE_UINT8(&flexcan->CANCTRL0 , (FLEXCAN_CTRL0_TXMODE_FULL_0_DOMINANT  
                                            | FLEXCAN_CTRL0_RXMODE_0_DOMINANT)
                                            & ~FLEXCAN_CTRL0_BOFFMSK
                                            & ~FLEXCAN_CTRL0_ERRMASK);
                                            
        //
        // setup message box acceptance filter
        //
        flexcan_set_acceptance_mask(&flexcan->RXGMASK_HI,  info->rxgmask,  CYGNUM_CAN_ID_EXT);
        flexcan_set_acceptance_mask(&flexcan->RX14MASK_HI, info->rx14mask, CYGNUM_CAN_ID_EXT);
        flexcan_set_acceptance_mask(&flexcan->RX15MASK_HI, info->rx15mask, CYGNUM_CAN_ID_EXT);  
        
        //
        // deactivate all message buffers - this is mandatory for configuration
        // of message buffers
        //
        for (i = 0; i < 16; ++i)
        {
            HAL_WRITE_UINT16(&flexcan->mbox[i], MBOX_RXCODE_NOT_ACTIVE);
        }   
        
        //
        // mask all interrupts
        //
        info->imask_shadow = 0x0000;
        HAL_WRITE_UINT16(&flexcan->IMASK, info->imask_shadow);
        HAL_READ_UINT8(&flexcan->CANCTRL0, tmp8);
        HAL_WRITE_UINT8(&flexcan->CANCTRL0, tmp8 & ~(FLEXCAN_CTRL0_BOFFMSK | FLEXCAN_CTRL0_ERRMASK));    
        
        //
        // setup bus arbitration mode - the LBUF bit defines the 
        // transmit-first scheme 0 = message buffer with lowest ID
        // 1 = message buffer with lowest number. We use lowest ID here
        //
        HAL_READ_UINT8(&flexcan->CANCTRL1, tmp8);
        HAL_WRITE_UINT8(&flexcan->CANCTRL1, (tmp8 & ~FLEXCAN_CTRL1_LBUF));    
        
        //
        // Message box 14 is our receiv message box. We configure it for
        // reception of any message
        //
        rx_all_filter.ext = CYGNUM_CAN_ID_STD;
        flexcan_setup_rxmbox(chan, info->rx_all_mbox_std, &rx_all_filter);
        rx_all_filter.ext = CYGNUM_CAN_ID_EXT;
        flexcan_setup_rxmbox(chan, info->rx_all_mbox_ext, &rx_all_filter);
    
        //
        // bus off interrupt and error interrupt
        //
        HAL_READ_UINT8(&flexcan->CANCTRL0, tmp8);
        HAL_WRITE_UINT8(&flexcan->CANCTRL0, tmp8 | (FLEXCAN_CTRL0_BOFFMSK | FLEXCAN_CTRL0_ERRMASK));                             
    } // if (init)
                      
    flexcan_set_baud(chan, config->baud); // setup baud rate
    
    //
    // store new config values
    //
    if (config != &chan->config) 
    {
        chan->config = *config;
    }
    flexcan_start_chip(chan);             // now we can start the chip again
    
    return true;
}

//===========================================================================
/// First initialisation and reset of CAN modul.
//===========================================================================
static bool flexcan_init(struct cyg_devtab_entry* devtab_entry)
{
    can_channel       *chan    = (can_channel*)devtab_entry->priv;
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_mbox_info *pmbox;

    if (!flexcan_config(chan, &chan->config, true))
    {
        return false;
    }
       
    //
    // prepare message box interrupt for message box 15 - the tx message box
    //
    pmbox = &info->mboxes[info->tx_all_mbox];
    cyg_drv_interrupt_create(pmbox->isr_vec,
                             pmbox->isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_mbox_tx_isr,
                             &flexcan_mbox_tx_dsr,
                             &(pmbox->interrupt_handle),
                             &(pmbox->interrupt));
    cyg_drv_interrupt_attach(pmbox->interrupt_handle);
    cyg_drv_interrupt_unmask(pmbox->isr_vec);
    
    //
    // prepare error interrupt
    //
    cyg_drv_interrupt_create(info->err_int.isr_vec,
                             info->err_int.isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_err_isr,
                             &flexcan_err_dsr,
                             &(info->err_int.interrupt_handle),
                             &(info->err_int.interrupt));
    cyg_drv_interrupt_attach(info->err_int.interrupt_handle);
    cyg_drv_interrupt_unmask(info->err_int.isr_vec);
    
    //
    // prepare busoff interrupt
    //
    cyg_drv_interrupt_create(info->boff_int.isr_vec,
                             info->boff_int.isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_busoff_isr,
                             &flexcan_busoff_dsr,
                             &(info->boff_int.interrupt_handle),
                             &(info->boff_int.interrupt));
    cyg_drv_interrupt_attach(info->boff_int.interrupt_handle);
    cyg_drv_interrupt_unmask(info->boff_int.isr_vec);
    
    //
    // prepare wake interrupt
    //
    cyg_drv_interrupt_create(info->wake_int.isr_vec,
                             info->wake_int.isr_priority,
                             (cyg_addrword_t) chan,
                             &flexcan_wake_isr,
                             &flexcan_wake_dsr,
                             &(info->wake_int.interrupt_handle),
                             &(info->wake_int.interrupt));
    cyg_drv_interrupt_attach(info->wake_int.interrupt_handle);
    cyg_drv_interrupt_unmask(info->wake_int.isr_vec);
        
    return true;
}


//===========================================================================
// Flexcan error interrupt handler
//===========================================================================
static cyg_uint32  flexcan_err_isr(cyg_vector_t vec, cyg_addrword_t data)
{
    can_channel  *chan    = (can_channel *)data;
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint8     ctrl0;
    cyg_uint16    estat;
    
    //
    // first we disable error interrupts - DSR will reenable it later
    //
    HAL_READ_UINT8(&flexcan->CANCTRL0, ctrl0);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, ctrl0 & ~FLEXCAN_CTRL0_ERRMASK);
    
    //
    // for clearing the interrupt we first read the flag register as 1
    // and then write it as 1 (and not as zero like the manual stated)
    // we clear only the flag of this interrupt and leave all other
    // message box interrupts untouched
    //
    HAL_READ_UINT16(&flexcan->ESTAT, estat);
    HAL_WRITE_UINT16(&flexcan->ESTAT, FLEXCAN_ESTAT_ERRINT); 
    
    //
    // On the mcf5272 there is no need to acknowledge internal
    // interrupts, only external ones.
    // cyg_drv_interrupt_acknowledge(vec);
    //
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// DSR for all interrupts that are no message box interrupts
//===========================================================================
static void flexcan_err_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel  *chan     = (can_channel *)data;
    flexcan_info *info     = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan  = (flexcan_regs *)info->base;
    cyg_uint8     ctrl0;
    cyg_uint8     event_id = FLEXCAN_ERR_EVENT;

    //
    // signal CAN event to generic IO CAN driver - it will do any further 
    // processing
    //            
    chan->callbacks->rcv_event(chan, &event_id);
    
    //
    // reenable bus off interrupts
    //
    HAL_READ_UINT8(&flexcan->CANCTRL0, ctrl0);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, ctrl0 | FLEXCAN_CTRL0_ERRMASK);
}


//===========================================================================
// Bus off interrupt handler
//===========================================================================
static cyg_uint32  flexcan_busoff_isr(cyg_vector_t vec, cyg_addrword_t data)
{
    can_channel  *chan    = (can_channel *)data;
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint8     ctrl0;
    cyg_uint16    estat;
    
    //
    // first we disable bus off interrupts - DSR will reenable it later
    //
    HAL_READ_UINT8(&flexcan->CANCTRL0, ctrl0);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, ctrl0 & ~FLEXCAN_CTRL0_BOFFMSK);
    
    //
    // for clearing the interrupt we first read the flag register as 1
    // and then write it as 1 (and not as zero like the manual stated)
    // we clear only the flag of this interrupt and leave all other
    // message box interrupts untouched
    //
    HAL_READ_UINT16(&flexcan->ESTAT, estat);
    HAL_WRITE_UINT16(&flexcan->ESTAT, FLEXCAN_ESTAT_BOFFINT);  
    
    //
    // On the mcf5272 there is no need to acknowledge internal
    // interrupts, only external ones.
    // cyg_drv_interrupt_acknowledge(vec);
    //
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// DSR for all interrupts that are no message box interrupts
//===========================================================================
static void flexcan_busoff_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel  *chan     = (can_channel *)data;
    flexcan_info *info     = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan  = (flexcan_regs *)info->base;
    cyg_uint8     ctrl0;
    cyg_uint8     event_id = FLEXCAN_BUSOFF_EVENT;

    //
    // signal CAN event to generic IO CAN driver - it will do any further 
    // processing
    //            
    chan->callbacks->rcv_event(chan, &event_id);
    
    //
    // reenable bus off interrupts
    //
    HAL_READ_UINT8(&flexcan->CANCTRL0, ctrl0);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, ctrl0 | FLEXCAN_CTRL0_BOFFMSK);  
}


//===========================================================================
// Bus off interrupt handler
//===========================================================================
static cyg_uint32  flexcan_wake_isr(cyg_vector_t vec, cyg_addrword_t data)
{
    can_channel  *chan    = (can_channel *)data;
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint16    estat;
    
    //
    // first we disable wake interrupts - we set the mcr register to
    // zero in order to bring it back to normal state
    //
    HAL_WRITE_UINT16(&flexcan->CANMCR, 0);
    
    //
    // for clearing the interrupt we first read the flag register as 1
    // and then write it as 1 (and not as zero like the manual stated)
    // we clear only the flag of this interrupt and leave all other
    // message box interrupts untouched
    //
    HAL_READ_UINT16(&flexcan->ESTAT, estat);
    HAL_WRITE_UINT16(&flexcan->ESTAT, FLEXCAN_ESTAT_WAKEINT);
       
    //
    // On the mcf5272 there is no need to acknowledge internal
    // interrupts, only external ones.
    // cyg_drv_interrupt_acknowledge(vec);
    //
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// DSR for all interrupts that are no message box interrupts
//===========================================================================
static void flexcan_wake_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel  *chan     = (can_channel *)data;
    flexcan_info *info     = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan  = (flexcan_regs *)info->base;
    cyg_uint8     event_id = FLEXCAN_WAKE_EVENT;
    cyg_uint8     ctrl0;

    //
    // signal CAN event to generic IO CAN driver - it will do any further 
    // processing we will enable all other interrupts after the call to 
    // rcv_event because the user should receive this event as the first
    // event after FlexCAN leaves standby.
    //            
    chan->callbacks->rcv_event(chan, &event_id);
    
    //
    // for standby we disabled all interrut source so we have to enable
    // it here again for normal operation
    //
    HAL_WRITE_UINT16(&flexcan->IMASK, info->imask_shadow);
    HAL_READ_UINT8(&flexcan->CANCTRL0, ctrl0);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, ctrl0 |(FLEXCAN_CTRL0_BOFFMSK | FLEXCAN_CTRL0_ERRMASK));
}


//===========================================================================
// Flexcan message box isr
//===========================================================================
static cyg_uint32 flexcan_mbox_rx_isr(cyg_vector_t vec, cyg_addrword_t data)
{
    can_channel  *chan    = (can_channel *)data;
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint16    iflag;
    
    //
    // number of message box can be calculated from vector that cause
    // interrupt - we pass this message box number as additional data to the
    // callback because it is required in the receive event function later
    //
    cyg_uint8 mbox = vec - info->isr_vec_mbox0;    
    
    //
    // first we disable interrupts of this message box - the DSR will
    // reenable it later
    //
    flexcan_mboxint_disable(info, mbox);
    
    //
    // for clearing the interrupt we first read the flag register as 1
    // and then write it as 1 (and not as zero like the manual stated)
    // we clear only the flag of this interrupt and leave all other
    // message box interrupts untouched
    //
    HAL_READ_UINT16(&flexcan->IFLAG, iflag);
    HAL_WRITE_UINT16(&flexcan->IFLAG, (0x0001 << mbox));    
    
    //
    // On the mcf5272 there is no need to acknowledge internal
    // interrupts, only external ones.
    // cyg_drv_interrupt_acknowledge(vec);
    //
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// Flexcan message box dsr
//===========================================================================
static void flexcan_mbox_rx_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel  *chan    = (can_channel *)data;
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    
    //
    // number of message box can be calculated from vector that caused
    // interrupt - we pass this message box number as additional data to the
    // callback
    //
    cyg_uint8 mbox = vec - info->isr_vec_mbox0;    
    
    //
    // signal CAN event to generic IO CAN driver - it will do any further 
    // processing
    //            
    chan->callbacks->rcv_event(chan, &mbox);

    //
    // reenable interrupts for the message box that caused the DSR to run
    //
    flexcan_mboxint_enable(info, mbox);
}

//===========================================================================
// Flexcan message box isr
//===========================================================================
static cyg_uint32 flexcan_mbox_tx_isr(cyg_vector_t vec, cyg_addrword_t data)
{
    can_channel       *chan    = (can_channel *)data;
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs      *flexcan = (flexcan_regs *)info->base;
    cyg_uint16         iflag;
    
    // number of message box can be calculated from vector that cause
    // interrupt - we pass this message box number as additional data to the
    // callback
    //
    cyg_uint8 mbox = vec - info->isr_vec_mbox0;
    
    //
    // the first thing we do here is to disable this message box. we do this
    // because if we just sent a remote transmission request with this message
    // box then the FLEXCAN modul will automatically set up this message box
    // for reception of the requested data frame. But we won't receive messages
    // with the TX message box and deactivate it here immediatelly.
    // 
    HAL_WRITE_UINT8(&flexcan->mbox[mbox].ctrlstat, MBOX_TXCODE_NOT_READY);   
    
    //
    // first we disable interrupts of this message box - the DSR will
    // reenable it later
    //
    flexcan_mboxint_disable(info, mbox);
    
    //
    // for clearing the interrupt we first read the flag register as 1
    // and then write it as 1 (and not as zero like the manual stated)
    // we clear only the flag of this interrupt and leave all other
    // message box interrupts untouched
    //
    HAL_READ_UINT16(&flexcan->IFLAG, iflag);
    HAL_WRITE_UINT16(&flexcan->IFLAG, (0x0001 << mbox));   
    
    //
    // On the mcf5272 there is no need to acknowledge internal
    // interrupts, only external ones.
    // cyg_drv_interrupt_acknowledge(vec);
    //
    return CYG_ISR_CALL_DSR;
}


//===========================================================================
// Flexcan message box dsr
//===========================================================================
static void flexcan_mbox_tx_dsr(cyg_vector_t vec, cyg_ucount32 count, cyg_addrword_t data)
{
    can_channel       *chan    = (can_channel *)data;
    flexcan_info      *info    = (flexcan_info *)chan->dev_priv;
    flexcan_mbox_info *pmbox;
    
    //
    // number of message box can be calculated from vector that caused
    // interrupt - we pass this message box number as additional data to the
    // callback
    //
    cyg_uint8 mbox = vec - info->isr_vec_mbox0; 
    pmbox = &info->mboxes[mbox];
    
#ifdef CYGOPT_IO_CAN_TX_EVENT_SUPPORT 
    //
    // signal CAN TX event to generic IO CAN driver - it will do any further 
    // processing
    //            
    chan->callbacks->rcv_event(chan, &mbox);
#endif
    
    pmbox->tx_busy = false; 
       
    //
    // send next message 
    //            
    chan->callbacks->xmt_msg(chan, &mbox);
    
    //
    // reenable interrupts for the message box that caused the DSR to run
    //
    flexcan_mboxint_enable(info, mbox);
}


//===========================================================================
// Start FlexCAN modul
//===========================================================================
static void flexcan_start_chip(can_channel *chan)
{
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    
    cyg_uint16 tmp16;
    
    info->state = CYGNUM_CAN_STATE_ACTIVE;
    HAL_READ_UINT16(&flexcan->CANMCR, tmp16);    
    HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16 
                 & ~(FLEXCAN_MCR_FRZ | FLEXCAN_MCR_HALT));
}


//===========================================================================
// Stop FlexCAN modul
//===========================================================================
static void flexcan_stop_chip(can_channel *chan)
{
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint16    tmp16;
    
    info->state = CYGNUM_CAN_STATE_STOPPED;
    HAL_READ_UINT16(&flexcan->CANMCR, tmp16);    
    HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16 | FLEXCAN_MCR_HALT);
}


//===========================================================================
// Set FlexCAN modul into standby mode
// If the flag selfwake is active then the FlexCAN modul will be set into
// standby mode with selwake. This means the FlexCAN modul will leave
// standby as soon as a message box will receive a message
//===========================================================================
static void flexcan_enter_standby(can_channel *chan, bool selfwake)
{
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint16    tmp16;
    cyg_uint8     tmp8;
    cyg_uint8     i;
    
    //
    // The CPU should disable all interrupts in the FlexCAN before entering low-power
    // stop mode. Otherwise it may be interrupted while in STOP mode upon a non
    // wake-up condition; If desired, the WAKEMASK bit should be set to enable the
    // WAKEINT.
    //
    HAL_READ_UINT8(&flexcan->CANCTRL0, tmp8);
    HAL_WRITE_UINT8(&flexcan->CANCTRL0, tmp8 & ~(FLEXCAN_CTRL0_BOFFMSK | FLEXCAN_CTRL0_ERRMASK));
    
    //
    // We disable all message box interrupts. The WAKE DSR will reenable it later
    // after processing the WAKE event. This ensures that the wake event will be the
    // first event if a message arrives
    //
    HAL_WRITE_UINT16(&flexcan->IMASK, 0); 
    
    HAL_READ_UINT16(&flexcan->CANMCR, tmp16);  
    
    tmp16 |= FLEXCAN_MCR_STOP;
    
    //
    // if we should go to standby then we activate the SELWAKE bit so that a received
    // frame will bring us back to live
    //
    if (selfwake)
    {
        tmp16 |= FLEXCAN_MCR_SELFWAKE;
    }
      
    HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16);
    
    //
    // we have to poll the STOPACK bit in order to determine if chip
    // has entered stop mode. We poll 10 times - se we spent a maximum
    // of 2 ms here
    //
    for (i = 0; i < 10; ++i)
    {
        HAL_READ_UINT16(&flexcan->CANMCR, tmp16);
        
        if (tmp16 & FLEXCAN_MCR_STOPACK)
        {
            info->state = CYGNUM_CAN_STATE_STANDBY;
            break;
        }
        HAL_DELAY_US(200);
    }
    
    //
    // if we are not in low power stop mode then we have to reenable interrupts
    //
    if (10 == i)
    {
        HAL_READ_UINT8(&flexcan->CANCTRL0, tmp8);
        HAL_WRITE_UINT8(&flexcan->CANCTRL0, tmp8 | (FLEXCAN_CTRL0_BOFFMSK | FLEXCAN_CTRL0_ERRMASK));
    }
    else
    {
        //
        // if we go into standby then we activate the wake interrupt so we will receive
        // a wake interrupt if we leave standby and can reenable interrups
        //
        if (selfwake)
        {
            HAL_READ_UINT16(&flexcan->CANMCR, tmp16);  
            HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16 | FLEXCAN_MCR_WAKEMSK);  
        }
    }
}


//===========================================================================
// Leave standby mode
//===========================================================================
static void flexcan_leave_standby(can_channel *chan)
{
    flexcan_info *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs *flexcan = (flexcan_regs *)info->base;
    cyg_uint16    tmp16;
    cyg_uint8     i;
    
    HAL_READ_UINT16(&flexcan->CANMCR, tmp16);  
    HAL_WRITE_UINT16(&flexcan->CANMCR, tmp16 & ~(FLEXCAN_MCR_STOP | FLEXCAN_MCR_SELFWAKE));
    
    //
    // we have to poll the STOPACK bit in order to determine if chip
    // has leaved stop mode. We poll 10 times - se we spent a maximum
    // of 2 ms here
    //
    for (i = 0; i < 10; ++i)
    {
        HAL_READ_UINT16(&flexcan->CANMCR, tmp16);
        
        if (!(tmp16 & FLEXCAN_MCR_STOPACK))
        {
            HAL_WRITE_UINT16(&flexcan->IMASK, info->imask_shadow);
            info->state = CYGNUM_CAN_STATE_ACTIVE;
            break;    
        }
        HAL_DELAY_US(200);
    } // for (i = 0; i < 10; ++i)
}


//===========================================================================
// Set acceptance mask for message buffer
//===========================================================================
static void flexcan_set_acceptance_mask(cyg_uint16 *rxmask_reg, cyg_uint32 mask, cyg_can_id_type ext)
{
    cyg_uint16 id;
    //
    // 32 bit access to RXMASK filters is broken so we use  16 Bit
    // access here
    //
    if (CYGNUM_CAN_ID_EXT == ext)
    {
        id  = ((mask >> 13) & 0xFFE0);       // set mask bits 18 - 28
        id |= ((mask >> 15) & 0x7);          // set mask bits 15 -17
        HAL_WRITE_UINT16(&rxmask_reg[0], id);
        
        id = (mask << 1) & 0xFFFE;
        HAL_WRITE_UINT16(&rxmask_reg[1], id);
    }
    else // (CYGNUM_CAN_ID_STD == ext)
    {
        id = ((mask << 5) & 0xFFE0); 
        HAL_WRITE_UINT16(&rxmask_reg[0], id);
        HAL_WRITE_UINT16(&rxmask_reg[1], 0xFFFF);  
    }
    
}



//===========================================================================
// Configure message box for transmission
//===========================================================================
static bool flexcan_cfg_mbox_tx(flexcan_mbox     *pmbox,
                                cyg_can_message  *pmsg,
                                bool              rtr)
{
    cyg_uint16 id;
  
    HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_TXCODE_NOT_READY); 
    
    if (CYGNUM_CAN_ID_EXT == pmsg->ext)
    { 
        id  = ((pmsg->id >> 13) & 0xFFE0);   // setup id bits 18 - 28
        id |= (MBOX_CFG_IDE | MBOX_CFG_SSR); // set SSR and IDE bit to 1
        id |= ((pmsg->id >> 15) & 0x7);      // set id bits 15 - 17
        HAL_WRITE_UINT16(&pmbox->id_hi, id);
        
        id = ((pmsg->id << 1) & 0xFFFE);
        
        if (pmsg->rtr)
        {
            id |= MBOX_CFG_RTR_EXT;
        }   
        
        HAL_WRITE_UINT16(&pmbox->id_lo, id);
    }   
    else
    {
        id = ((pmsg->id << 5) & 0xFFE0);
        if (pmsg->rtr)
        {
            id |= MBOX_CFG_RTR_STD;           // set rtr bit for standard ID
        }
        HAL_WRITE_UINT16(&pmbox->id_hi, id);
        HAL_WRITE_UINT16(&pmbox->id_lo, 0);
    }
    
    pmsg->dlc %= 9; // limit data length to 8 bytes
    
    //
    // Now copy data bytes into buffer and start transmission
    //
    HAL_WRITE_UINT8_VECTOR(&pmbox->data, pmsg->data, pmsg->dlc, 1); 
    
   
    if (rtr)
    {
        HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_TXCODE_RESPONSE | pmsg->dlc);
    }
    else
    {
        HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_TXCODE_TRANSMIT | pmsg->dlc);
    }
    
    return true;
}



//===========================================================================
// Configure message box for reception of a certain CAN identifier
//===========================================================================
static void flexcan_cfg_mbox_rx(flexcan_mbox     *pmbox,
                                cyg_can_message  *pmsg)
{
    cyg_uint16 id;
    
    HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_RXCODE_NOT_ACTIVE); 
    
    if (CYGNUM_CAN_ID_EXT == pmsg->ext)
    { 
        id  = ((pmsg->id >> 13) & 0xFFE0);      // setup id bits 18 - 28
        id |= (MBOX_CFG_IDE | MBOX_CFG_SSR);    // set SSR and IDE bit to 1
        id |= ((pmsg->id >> 15) & 0x7);         // set id bits 15 - 17
        HAL_WRITE_UINT16(&pmbox->id_hi, id);    // write ID high
        
        id = ((pmsg->id << 1) & 0xFFFE);
        
        HAL_WRITE_UINT16(&pmbox->id_lo, id);// write ID low
    }   
    else
    {
        id = ((pmsg->id << 5) & 0xFFE0);

        HAL_WRITE_UINT16(&pmbox->id_hi, id);
        HAL_WRITE_UINT16(&pmbox->id_lo, 0);
    }
    
    HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_RXCODE_EMPTY);
}


//===========================================================================
// Read date from a message box
//==========================================================================
static void flexcan_read_from_mbox(can_channel  *chan,
                            cyg_uint8            mbox,
                            cyg_can_event       *pevent,
                            cyg_uint8           *ctrlstat)
{
    flexcan_info    *info    = (flexcan_info *)chan->dev_priv;
    flexcan_regs    *flexcan = (flexcan_regs *)info->base;
    flexcan_mbox    *pmbox   = &flexcan->mbox[mbox];
    cyg_can_message *pmsg    = &pevent->msg;
    cyg_uint16       id;
    cyg_uint8        i;
    
    HAL_READ_UINT8(&pmbox->ctrlstat, *ctrlstat); // this read will lock the mbox
    
    //
    // If message buffer is busy then it is now beeing filled with a new message
    // This condition will be cleared within 20 cycles - we simply do a 20 µs
    // delay here, that should be enougth
    //
    if (*ctrlstat & MBOX_RXCODE_BUSY)
    {
        HAL_DELAY_US(20);
    }
    
    pmsg->dlc = (*ctrlstat & MBOX_CFG_DLC_MASK); // store received data len
    
    HAL_READ_UINT16(&pmbox->id_hi, id);          // read ID high
    
    if (id & MBOX_CFG_IDE)
    {
        pmsg->ext = CYGNUM_CAN_ID_EXT;
        pmsg->id  = (id & 0xFFE0) << 13;
        pmsg->id |= (id & 0x07) << 15;
        
        HAL_READ_UINT16(&pmbox->id_lo, id);
        pmsg->id |= (id & 0xFFFE) >> 1;
        
        if (id & MBOX_CFG_RTR_EXT)
        {
            pmsg->rtr = CYGNUM_CAN_FRAME_RTR;
        }
        else
        {
            pmsg->rtr = CYGNUM_CAN_FRAME_DATA;
        }
    }
    else
    {
        pmsg->ext = CYGNUM_CAN_ID_STD;
        pmsg->id  = (id & 0xFFE0) >> 5;
        
        if (id & MBOX_CFG_RTR_STD)
        {
            pmsg->rtr = CYGNUM_CAN_FRAME_RTR;
        }
        else
        {
            pmsg->rtr = CYGNUM_CAN_FRAME_DATA;
        }
    }
    
    //
    // now finally copy data
    //
    HAL_READ_UINT8_VECTOR(&pmbox->data, pmsg->data, pmsg->dlc, 1);
       
    //
    // now zero out the remaining bytes in can message in order
    // to deliver a defined state
    //
    for (i = pmsg->dlc; i < 8; ++i)
    {
        pmsg->data[i] = 0;
    }
  
    //
    // now mark this mbox as empty and read the free running timer
    // to unlock this mbox
    //
    HAL_WRITE_UINT8(&pmbox->ctrlstat, MBOX_RXCODE_EMPTY);
    HAL_READ_UINT16(&flexcan->TIMER, id);
#ifdef CYGOPT_IO_CAN_SUPPORT_TIMESTAMP    
    pevent->timestamp = id;
#endif
}


//===========================================================================
//                           INIT CAN BAUD RATE
//===========================================================================
static bool flexcan_set_baud(can_channel *chan, cyg_uint16 baud)
{
    flexcan_info             *info = (flexcan_info *)chan->dev_priv;
    flexcan_regs             *flexcan = (flexcan_regs *)info->base;
    cyg_uint16                mcr_bck;
    cyg_uint8                 tmp8;
    cyg_uint8                 presdiv;
    cyg_uint8                 propseg;
    cyg_uint8                 pseg1_2;
    cyg_uint16                baudrate = flexcan_baud_rates[baud];
    
    //
    // Get bit timings from HAL because bit timings depend on sysclock
    //
    HAL_MCF52xx_FLEXCAN_GET_BIT_TIMINGS(&baudrate, &presdiv, &propseg, &pseg1_2);
    
    //
    // return false if baudrate is not supported
    //
    if (0 == baudrate)
    {
        return false;
    }
    
    //
    // For setting the bit timings we have to stop the flexcan modul
    //
    HAL_READ_UINT16(&flexcan->CANMCR, mcr_bck);
    HAL_WRITE_UINT16(&flexcan->CANMCR, mcr_bck | FLEXCAN_MCR_HALT);
    
    //
    // now we setup bit timings
    //
    HAL_WRITE_UINT8(&flexcan->PRESDIV, presdiv);
    HAL_READ_UINT8(&flexcan->CANCTRL1, tmp8);
    HAL_WRITE_UINT8(&flexcan->CANCTRL1, (tmp8 & 0xF8) | propseg); 
    HAL_READ_UINT8(&flexcan->CANCTRL2, tmp8);
    HAL_WRITE_UINT8(&flexcan->CANCTRL2, (tmp8 & 0xC0) | pseg1_2); 
    
    //
    // Now restore the previous state - if the modul was started then
    // it will no be started again, if it was stopped, then it remains stopped
    //
    HAL_WRITE_UINT16(&flexcan->CANMCR, mcr_bck);
    
    return true;
}

//---------------------------------------------------------------------------
// end of can_mcf_flexcan.c

