summaryrefslogblamecommitdiffstats
path: root/bsps/powerpc/gen5200/mscan/mscan.c
blob: 19fba1e8bdb5d5e271b5f4e5ab99ccd50da46619 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                           






                                       
                                                   
  



















                                                                              
   






                        
 
                
                      
                    
                               



                                                               
                                                           
 


                                                                                                        

                                                                         
                                

                                          




                                             
                                                    







                                         
                                                                          



                                                         
     


                                                  
   


                                       
 



                                                                                                        


                                                                          
                                                                                  
                                                                    

                                           



                                                                       

                                   






                                                                  


                                                
   


                                                     
 




                                                                       
 
                           
                                                    
                                                                         



                             
                       

    
                          

     
                                  
                                                           

                                      
                          




                                        
                                

                                  
                      

                                        
                                                   

                         



                                                     
 
         
 
                                                                                                                      


                                                            
 

                                                                  
 
                                             
                                                               
                                                 
                               
                     
                                                      
                     
                                                      
                     
                                                      
                     
                                                      
                     
                                                      
                     
                                                      
                     
                                                      
                     
                                                      


                      

             

                                                        
                             
 

                              

                                                            
                                                                
 
                
 


                                                          



                                        

       
   

    
                         


                                     
                             

                                 
                                
 
                                               



                                        
              


                                                                       

                              

                 


                 
                         


                                                                 
                  
 

                                               
                                              
                                


                   

         

       
                     
                                                                       

                      
                                                  

                          
                                                                      
 
                                         

                                                         
 






















                                                  
 
                                               
 



                                                                         
 
                             
 
         
 
              
 
                                                                      
 
       
 

                                        
 
                                                                      


   

                              
 
                          
 
                                             
 


                                                 


   
 
 





                                                            
   

                                             
 
                                                                       









                                                                      
                                                                       


         
 



                                    
                                                                  

                                         
 
                             


         
 



                                  
                                                                   

                                         
 
                              


         
 



                                      
                                                                   

                                         
 
                                                       





                             
 



                                    
                                                                  

                                         
 
                             


         
 



                                  
                                                                   

                                         
 
                              


         
 



                                      
                                                                   

                                         
 
                                                       





                             
 
 
                                      

          
  
 
                                      

          
  



                              
                                                                        
   












                                                    
   
  



                                            

                                      
 
                                                                       








                                                                      
 

                                                                            
      

         
 

  

                                              
                                                                        

                        

                                         
                                                       


                                                
 
                                                                       









                                                                      
                                                       
     

                                          

      







                                                                                               
 
                                                                       








                                                                      

                           
 
                                                                     









                                                                       








                                                                      
                         
 

                                                
 
         
 
 



                                                


                                                     
 
                                                                       








                                                                      

                           


         
 
 


                                                                         
                                         
                  
 
                  


                 

                               




                               
   
 
                         

                            
                 
 
                                

                                                                    

            
                           
                                                   
                                                                      

                                                              
 
                                             
 
                             

                                  
                        
                                 
                              
                                 


                          
                         


                                  





                               
   





                            
 



                                  


                                                                           



                                                            
                  

                 

                                                               

                                                   



                                                                

                                                   













                                                                              


                 

                                                               
 




                                                                
 
                                                   



                                                                

                                                         







                                                                              




                               
   
 
                                     





                                                                               

                                                      





                                                                               

                         
                                                                       
                                                                             

                                             




                                                 


                
 




                                                                     

                                                                                


                                          
                                                                              
                                          
 
                                                                              
                                          
 


                                                                           
                                              
 


                                                                           
                                              


                
 



                            


                                                                          
                                              

                                         
                  








                               
   

                  
                                         


                                                              
   


                
 



                             


                                                                           
                           
 
                  


                 




                               
   





                                                           
 



                            








                                                                              
 
                  


                 
                               




                               
   

                                         
                                               


                                                     
   
 





                                                                            


                           
          
 
                                                     
 
   


                
 



                             


                                                                           
                           



                                                                              


                  

                 
                               
                     




                               
   
 
                                          
                                               
 

                                                     
   
 

                          
 
                                                   



                                                                          
 
                                                                        
                                              
 

                                                  
 
                                                  
                                                      
 

                                                      
 
   
 
                
 
 



                               


                                                                             
                           
                 







                                                                     
 
                  


                 


                                                       




                               
   
 
                           





                                                             
                            


                           
                                                   
 
                                        

                         

                                                   


                         

                                                   


                         

                                                   


                         

                                                   


                
                
 
       

                                                                                                  
                                                   

                        
                                 

                              
                                 



                              
                            

                         
                                        

                         
                                                                        


                         
                                                                        


                         
                                                                        


                         
                                                                        


                
                
 
       


            
                                 


                              
                                                   
 
                                        

                         

                                                         


                         

                                                         


                         

                                                         


                         

                                                         


                
                
 
       

                                                                                                  
                                                   

                        
                                 

                              
                                 


            
                                 

                              
                                        

                         

                                                        


                         

                                                        


                         

                                                        


                         

                                                        


                
                
 
       
 
                            


                                               
                                                               

                                      
                                                      

                                         
                                                                
 
       

                          
                                                    


                                          



                                  


            
                            


                          
                                                    

                                       
                                                                      


            
                           
                            

                                                                 

                                 
 





                                 




                                                          

                                                                        




                                                                              
                                  

                                                       



                                                                               
 
                           


           

         
                                            
                                                 

                                                       







                                                                             

                                              
                                                                 

                                                          
                                                
 

                                                                
 
                         
 
         
 
              


                                
       

            


            
   


                          
 
/* SPDX-License-Identifier: BSD-2-Clause */

/*
 * RTEMS generic MPC5200 BSP
 *
 * This file contains the MSCAN driver.
 */

/*
 * Copyright (c) 2005 embedded brains GmbH & Co. KG
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <rtems.h>
#include <rtems/error.h>
#include <rtems/libio.h>
#include <string.h>

#include <bsp.h>
#include <bsp/fatal.h>
#include <bsp/irq.h>
#include "../mscan/mscan_int.h"

/* #define MSCAN_LOOPBACK */

struct mpc5200_rx_cntrl mpc5200_mscan_rx_cntrl[MPC5200_CAN_NO];
static struct mscan_channel_info chan_info[MPC5200_CAN_NO];

/*
 * MPC5x00 MSCAN tx ring buffer function to get a can message buffer from the head of the tx ring buffer
 */
static struct can_message *get_tx_buffer(struct mscan_channel_info *chan)
{
  /* define a temp. mess ptr. */
  struct can_message *tmp_mess_ptr = NULL,
    *temp_head_ptr;

  /* set temp. head pointer */
  temp_head_ptr = chan->tx_ring_buf.head_ptr;

  /* check buffer empty condition */
  if (temp_head_ptr != chan->tx_ring_buf.tail_ptr) {

    /* current buffer head. ptr. */
    tmp_mess_ptr = temp_head_ptr;

    /* increment the head pointer */
    temp_head_ptr++;

    /* check for wrap around condition */
    if (temp_head_ptr > chan->tx_ring_buf.buf_ptr + NO_OF_MSCAN_TX_BUFF) {

      /* set head ptr. to the begin of the ring buffer */
      temp_head_ptr = chan->tx_ring_buf.buf_ptr;

    }

    /* end of crtical section restore head ptr. */
    chan->tx_ring_buf.head_ptr = temp_head_ptr;
  }

  /* return the current head pointer */
  return tmp_mess_ptr;
}

/*
 * MPC5x00 MSCAN tx ring buffer function to write a can message buffer to the tail of the tx ring buffer
 */
static struct can_message *fill_tx_buffer(struct mscan_channel_info *chan,
                                          struct can_message *mess_ptr)
{
  /* define a temp. mess ptr. to the entry which follows the current tail entry */
  struct can_message *tmp_mess_ptr = chan->tx_ring_buf.tail_ptr + 1;

  /* check for the wrap around condition */
  if (tmp_mess_ptr > chan->tx_ring_buf.buf_ptr + NO_OF_MSCAN_TX_BUFF) {
    /* set temp. mess. ptr to the begin of the ring buffer */
    tmp_mess_ptr = chan->tx_ring_buf.buf_ptr;
  }

  /* check buffer full condition */
  if (tmp_mess_ptr == chan->tx_ring_buf.head_ptr) {
    /* return NULL in case buffer is full */
    return NULL;
  } else {
    /* copy the can mess. to the tail of the buffer */
    memcpy((void *) chan->tx_ring_buf.tail_ptr, (void *) mess_ptr,
           sizeof (struct can_message));

    /* set new tail equal to temp. mess. ptr. */
    chan->tx_ring_buf.tail_ptr = tmp_mess_ptr;
  }

  /* return the actual tail ptr. (next free entry) */
  return chan->tx_ring_buf.tail_ptr;
}

/*
 * MPC5x00 MSCAN interrupt handler
 */
static void mpc5200_mscan_interrupt_handler(rtems_irq_hdl_param handle)
{
  rtems_status_code status;
  mscan_handle *mscan_hdl = (mscan_handle *) handle;
  struct mscan_channel_info *chan = &chan_info[mscan_hdl->mscan_channel];
  struct can_message rx_mess,
   *rx_mess_ptr,
   *tx_mess_ptr;
  mscan *m = chan->regs;
  register uint8_t idx;

  /*
     handle tx ring buffer
   */

  /* loop over all 3 tx buffers */
  for (idx = TFLG_TXE0; idx <= TFLG_TXE2; idx = idx << 1) {

    /* check for tx buffer vacation */
    if ((m->tflg) & idx) {

      /* try to get a message */
      tx_mess_ptr = get_tx_buffer(chan);

      /* check for new tx message */
      if (tx_mess_ptr != NULL) {

        /* select the tx buffer */
        m->bsel = idx;

        /* check for toucan interface */
        if ((mscan_hdl->toucan_callback) == NULL) {

          /* set tx id */
          m->txidr0 = SET_IDR0(tx_mess_ptr->mess_id);
          m->txidr1 = SET_IDR1(tx_mess_ptr->mess_id);
          m->txidr2 = 0;
          m->txidr3 = 0;

        }

        /* fill in tx data if TOUCAN is activ an TOUCAN index have a match with the tx buffer or TOUCAN is disabled */
        if (((mscan_hdl->toucan_callback) == NULL)
            || (((mscan_hdl->toucan_callback) != NULL)
                && ((tx_mess_ptr->toucan_tx_idx) == idx))) {

          /* insert dlc into m register */
          m->txdlr = (uint8_t) ((tx_mess_ptr->mess_len) & 0x000F);

          /* skip data copy in case of RTR */
          if (!(MSCAN_MESS_ID_HAS_RTR(tx_mess_ptr->mess_id))) {
            /* copy tx data to MSCAN registers */
            switch (m->txdlr) {
              case 8:
                m->txdsr7 = tx_mess_ptr->mess_data[7];
              case 7:
                m->txdsr6 = tx_mess_ptr->mess_data[6];
              case 6:
                m->txdsr5 = tx_mess_ptr->mess_data[5];
              case 5:
                m->txdsr4 = tx_mess_ptr->mess_data[4];
              case 4:
                m->txdsr3 = tx_mess_ptr->mess_data[3];
              case 3:
                m->txdsr2 = tx_mess_ptr->mess_data[2];
              case 2:
                m->txdsr1 = tx_mess_ptr->mess_data[1];
              case 1:
                m->txdsr0 = tx_mess_ptr->mess_data[0];
                break;
              default:
                break;
            }
          }

          /* enable message buffer specific interrupt */
          m->tier |= m->bsel;

          /* start transfer */
          m->tflg = m->bsel;

          /* release counting semaphore of tx ring buffer */
          rtems_semaphore_release((rtems_id) (chan->tx_rb_sid));

        } else {

          /* refill the tx ring buffer with the message */
          fill_tx_buffer(chan, tx_mess_ptr);

        }
      } else {
        /* reset interrupt enable bit */
        m->tier &= ~(idx);
      }
    }
  }

  /*
     handle rx interrupts
   */

  /* check for rx interrupt source */
  if (m->rier & RIER_RXFIE) {

    /* can messages received ? */
    while (m->rflg & RFLG_RXF) {

      if (mscan_hdl->toucan_callback == NULL) {

        /* select temporary rx buffer */
        rx_mess_ptr = &rx_mess;

      } else {

        /* check the rx fliter-match indicators (16-bit filter mode) */
        /* in case of more than one hit, lower hit has priority */
        idx = (m->idac) & 0x7;
        switch (idx) {

          case 0:
          case 1:
          case 2:
          case 3:
            rx_mess_ptr =
              (struct can_message *)
              &(mpc5200_mscan_rx_cntrl[mscan_hdl->mscan_channel].
                can_rx_message[idx]);
            break;

            /* this case should never happen */
          default:
            /* reset the rx indication flag */
            m->rflg |= RFLG_RXF;

            return;
            break;
        }

      }

      /* get rx ID */
      rx_mess_ptr->mess_id = GET_IDR0(m->rxidr0) | GET_IDR1(m->rxidr1);

      /* get rx len */
      rx_mess_ptr->mess_len = ((m->rxdlr) & 0x0F);

      /* get time stamp */
      rx_mess_ptr->mess_time_stamp = ((m->rxtimh << 8) | (m->rxtiml));

      /* skip data copy in case of RTR */
      if (!(MSCAN_MESS_ID_HAS_RTR(rx_mess_ptr->mess_id)))
      {

        /* get the data */
        switch (rx_mess_ptr->mess_len) {
          case 8:
            rx_mess_ptr->mess_data[7] = m->rxdsr7;
          case 7:
            rx_mess_ptr->mess_data[6] = m->rxdsr6;
          case 6:
            rx_mess_ptr->mess_data[5] = m->rxdsr5;
          case 5:
            rx_mess_ptr->mess_data[4] = m->rxdsr4;
          case 4:
            rx_mess_ptr->mess_data[3] = m->rxdsr3;
          case 3:
            rx_mess_ptr->mess_data[2] = m->rxdsr2;
          case 2:
            rx_mess_ptr->mess_data[1] = m->rxdsr1;
          case 1:
            rx_mess_ptr->mess_data[0] = m->rxdsr0;
          case 0:
          default:
            break;
        }
      }

      if (mscan_hdl->toucan_callback == NULL) {

        if ((status =
             rtems_message_queue_send(chan->rx_qid, (void *) rx_mess_ptr,
                                      sizeof (struct can_message))) !=
            RTEMS_SUCCESSFUL) {

          chan->int_rx_err++;

        }

      } else {

        mscan_hdl->toucan_callback((int16_t) (((m->idac) & 0x7) + 3));

      }

      /* reset the rx indication flag */
      m->rflg |= RFLG_RXF;

    }                           /* end of while(m->rflg & RFLG_RXF) */

  }

  /* status change detected */
  if (m->rflg & RFLG_CSCIF) {

    m->rflg |= RFLG_CSCIF;

    if (mscan_hdl->toucan_callback != NULL) {

      mscan_hdl->toucan_callback((int16_t) (-1));

    }

  }

}

/**
 * @brief Enables some interrupts for the MSCAN module @a m.
 *
 * Enabled interrupts:
 *  - Receiver or transmitter enters or leaves bus off state
 *  - Receiver buffer full
 */
static void mscan_interrupts_enable(mscan *m)
{

  /* RX Interrupt Enable on MSCAN_A/_B ----------------------------- */
  /*    [07]:WUPIE         0 : WakeUp interrupt disabled            */
  /*    [06]:CSCIE         1 : Status Change interrupt enabled      */
  /*    [05]:RSTATE1       0 : Recv. Status Change int. ,Bit 1      */
  /*    [04]:RSTATE0       1 : Recv. Status Change int. ,Bit 0      */
  /*                           -> 01 BusOff status changes enabled  */
  /*    [03]:TSTAT1        0 : Transmit. Status Change int. , Bit 1 */
  /*    [02]:TSTAT0        1 : Transmit. Status Change int. , Bit 0 */
  /*                           -> 01 BusOff status changes enabled  */
  /*    [01]:OVRIE         0 : Overrun Interrupt is disabled        */
  /*    [00]:RXFIE         1 : Recv. Full interrupt is enabled      */
  m->rier |= (RIER_CSCIE | RIER_RXFIE | RIER_RSTAT(1) | RIER_TSTAT(1));

  return;

}

/*
 * Unmask MPC5x00 MSCAN_A interrupts
 */
static void mpc5200_mscan_a_on(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_A])->regs;

  mscan_interrupts_enable(m);

  return;

}

/*
 * Mask MPC5x00 MSCAN_A interrupts
 */
static void mpc5200_mscan_a_off(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_A])->regs;

  mscan_interrupts_disable(m);

  return;

}

/*
 *  Get MSCAN_A interrupt mask setting
 */
static int mpc5200_mscan_a_isOn(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_A])->regs;

  if ((m->rier & RIER_CSCIE) && (m->rier & RIER_RXFIE))
    return RTEMS_SUCCESSFUL;
  else
    return RTEMS_UNSATISFIED;

  return RTEMS_SUCCESSFUL;

}

/*
 * Unmask MPC5x00 MSCAN_B interrupts
 */
static void mpc5200_mscan_b_on(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_B])->regs;

  mscan_interrupts_enable(m);

  return;

}

/*
 * Mask MPC5x00 MSCAN_B interrupts
 */
static void mpc5200_mscan_b_off(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_B])->regs;

  mscan_interrupts_disable(m);

  return;

}

/*
 *  Get MSCAN_B interrupt mask setting
 */
static int mpc5200_mscan_b_isOn(const rtems_irq_connect_data * ptr)
{
  mscan *m = (&chan_info[MSCAN_B])->regs;

  if ((m->rier & RIER_CSCIE) && (m->rier & RIER_RXFIE))
    return RTEMS_SUCCESSFUL;
  else
    return RTEMS_UNSATISFIED;

  return RTEMS_SUCCESSFUL;

}

static mscan_handle mscan_a_handle = {
  MSCAN_A,
  NULL
};

static mscan_handle mscan_b_handle = {
  MSCAN_B,
  NULL
};

/*
 * MPC5x00 MSCAN_A/_B irq data
 */
static rtems_irq_connect_data mpc5200_mscan_irq_data[MPC5200_CAN_NO] = {
  {
    BSP_SIU_IRQ_MSCAN1,
    (rtems_irq_hdl) mpc5200_mscan_interrupt_handler,
    (rtems_irq_hdl_param) & mscan_a_handle,
    (rtems_irq_enable) mpc5200_mscan_a_on,
    (rtems_irq_disable) mpc5200_mscan_a_off,
    (rtems_irq_is_enabled) mpc5200_mscan_a_isOn
  }, {
    BSP_SIU_IRQ_MSCAN2,
    (rtems_irq_hdl) mpc5200_mscan_interrupt_handler,
    (rtems_irq_hdl_param) & mscan_b_handle,
    (rtems_irq_enable) mpc5200_mscan_b_on,
    (rtems_irq_disable) mpc5200_mscan_b_off,
    (rtems_irq_is_enabled) mpc5200_mscan_b_isOn
  }
};

/*
 * MPC5x00 MSCAN wait for sync. with CAN bus
 */
void mpc5200_mscan_wait_sync(mscan *m)
{

  /* Control Register 0 -------------------------------------------- */
  /*    [07]:RXFRM       0 : Recv. Frame, Flag Bit (rd.&clear only) */
  /*    [06]:RXACT       0 : Recv. Active, Status Bit (rd. only)    */
  /*    [05]:CSWAI       0 : CAN Stops in Wait Mode                 */
  /*    [04]:SYNCH    0->1 : Synchronized, Status Bit (rd. only)    */
  /*    [03]:TIME        1 : Generate Timestamps                    */
  /*    [02]:WUPE        0 : WakeUp Disabled                        */
  /*    [01]:SLPRQ       0 : No Sleep Mode Request                  */
  /*    [00]:INITRQ      0 : No init. Mode Request                  */
  /* wait for MSCAN A_/_B bus synch. */

#if 0                           /* we don't have a need to wait for sync. */
  while (!((m->ctl0) & CTL0_SYNCH));
#endif
  return;

}

/*
 * MPC5x00 MSCAN perform settings in init mode
 */
static void mpc5200_mscan_perform_initialization_mode_settings(mscan *m)
{
  mscan_context context;

  /* perform all can bit time settings */
  (void) mscan_set_bit_rate(m, MSCAN_BIT_RATE_DEFAULT);

  /* Enter initialization mode */
  mscan_initialization_mode_enter( m, &context);

  /* Control Register 1 -------------------------------------------- */
  /*    [07]:CANE        0 : MSCAN Module is disabled               */
  /*    [06]:CLKSRC      0 : Clock Source -> IPB_CLOCK (bsp.h)      */
  /*    [05]:LOOPB       0 : No Loopback                            */
  /*    [04]:LISTEN      0 : Normal Operation                       */
  /*    [03]:res         0 : reserved                               */
  /*    [02]:WUPM        0 : No protect. from spurious WakeUp       */
  /*    [01]:SLPAK    1->0 : Sleep Mode Acknowledge (rd. only)      */
  /*    [00]:INITAK      0 : Init. Mode Acknowledge (rd. only)      */
  /* Set CLK source, disable loopback & listen-only mode */
#ifndef MSCAN_LOOPBACK
  m->ctl1 &= ~(CTL1_LISTEN | CTL1_LOOPB | CTL1_CLKSRC);
#else
  m->ctl1 &= ~(CTL1_LISTEN | CTL1_CLKSRC);
  m->ctl1 |= (CTL1_LOOPB);
#endif

  /* IPB clock       -> IPB_CLOCK                                                            */
  /* bitrate         -> CAN_BIT_RATE                                                         */
  /* Max. no of Tq   -> CAN_MAX_NO_OF_TQ                                                     */
  /* Prescaler value -> prescale_val = ROUND_UP(IPB_CLOCK/(CAN_BIT_RATE * CAN_MAX_NO_OF_TQ)) */
  /* SYNC_SEG        ->  1 tq                                                                */
  /* time segment 1  -> 16 tq (PROP_SEG+PHASE_SEG), CAN_MAX_NO_OF_TQ_TSEG1 = 15              */
  /* time segment 2  ->  8 tq (PHASE_SEG2)        , CAN_MAX_NO_OF_TQ_TSEG2 =  7              */
  /* SJW             ->  3 (fixed 0...3)          , CAN_MAX_NO_OF_TQ_SJW   =  2              */

  /* ID Acceptance Control MSCAN_A/_B ------------------------------ */
  /*    [07]:res.        0 : reserved                               */
  /*    [06]:res.        0 : reserved                               */
  /*    [05]:IDAM1       0 : ID acceptance control, Bit1            */
  /*    [04]:IDAM0       1 : ID acceptance control, Bit0            */
  /*                         -> filter 16 bit mode                  */
  /*    [03]:res.        0 : reserved                               */
  /*    [02]:IDHIT2      0 : ID acceptance hit indication, Bit 2    */
  /*    [01]:IDHIT1      0 : ID acceptance hit indication, Bit 1    */
  /*    [00]:IDHIT0      0 : ID acceptance hit indication, Bit 0    */
  m->idac &= ~(IDAC_IDAM1);
  m->idac |= (IDAC_IDAM0);

  /* initialize rx filter masks (16 bit), don't care including rtr */
  m->idmr0 = SET_IDMR0(0x7FF);
  m->idmr1 = SET_IDMR1(0x7FF);
  m->idmr2 = SET_IDMR2(0x7FF);
  m->idmr3 = SET_IDMR3(0x7FF);
  m->idmr4 = SET_IDMR4(0x7FF);
  m->idmr5 = SET_IDMR5(0x7FF);
  m->idmr6 = SET_IDMR6(0x7FF);
  m->idmr7 = SET_IDMR7(0x7FF);

  /* Control Register 1 -------------------------------------------- */
  /*    [07]:CANE     0->1 : MSCAN Module is enabled                */
  /*    [06]:CLKSRC      1 : Clock Source -> IPB_CLOCK (bsp.h)      */
  /*    [05]:LOOPB       0 : No Loopback                            */
  /*    [04]:LISTEN      0 : Normal Operation                       */
  /*    [03]:res         0 : reserved                               */
  /*    [02]:WUPM        0 : No protect. from spurious WakeUp       */
  /*    [01]:SLPAK       0 : Sleep Mode Acknowledge (rd. only)      */
  /*    [00]:INITAK      0 : Init. Mode Acknowledge (rd. only)      */
  /* enable MSCAN A_/_B */
  m->ctl1 |= (CTL1_CANE);

  /* Leave initialization mode */
  mscan_initialization_mode_leave( m, &context);

  return;

}

/*
 * MPC5x00 MSCAN perform settings in normal mode
 */
void mpc5200_mscan_perform_normal_mode_settings(mscan
                                                *m)
{

  /* Control Register 0 -------------------------------------------- */
  /*    [07]:RXFRM       0 : Recv. Frame, Flag Bit (rd.&clear only) */
  /*    [06]:RXACT       0 : Recv. Active, Status Bit (rd. only)    */
  /*    [05]:CSWAI       0 : CAN Stops in Wait Mode                 */
  /*    [04]:SYNCH       0 : Synchronized, Status Bit (rd. only)    */
  /*    [03]:TIME        1 : Generate Timestamps                    */
  /*    [02]:WUPE        0 : WakeUp Disabled                        */
  /*    [01]:SLPRQ       0 : No Sleep Mode Request                  */
  /*    [00]:INITRQ      0 : No init. Mode Request                  */
  /* Disable wait mode, enable timestamps */
  m->ctl0 &= ~(CTL0_CSWAI);
  m->ctl0 |= (CTL0_TIME);

  return;

}

rtems_status_code mpc5200_mscan_set_mode(rtems_device_minor_number minor,
                                         uint8_t mode)
{
  struct mscan_channel_info *chan = NULL;
  mscan *m = NULL;

  switch (minor) {

    case MSCAN_A:
    case MSCAN_B:
      chan = &chan_info[minor];
      m = chan->regs;
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  if (chan->mode == mode)
    return RTEMS_SUCCESSFUL;

  switch (mode) {

    case MSCAN_INIT_NORMAL_MODE:
      /* perform initialization which has to be done in init mode */
      mpc5200_mscan_perform_initialization_mode_settings(m);
      break;

    case MSCAN_NORMAL_MODE:
      if ((chan->mode) == MSCAN_INITIALIZED_MODE) {
        /* perform initialization which has to be done in init mode */
        mpc5200_mscan_perform_initialization_mode_settings(m);
      }

      if ((chan->mode) == MSCAN_SLEEP_MODE) {

        /* exit sleep mode */
        mscan_sleep_mode_leave(m);
      }
      /* enable ints. */
      mscan_interrupts_enable(m);
      /* wait for bus sync. */
      mpc5200_mscan_wait_sync(m);
      break;

    case MSCAN_SLEEP_MODE:
      /* disable ints. */
      mscan_interrupts_disable(m);
      /* knter sleep mode */
      mscan_sleep_mode_enter(m);
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;

  }

  /* set new channel mode */
  chan->mode = mode;

  return RTEMS_SUCCESSFUL;

}

/*
 * initialization of channel info.
 */
rtems_status_code mscan_channel_initialize(rtems_device_major_number major,
                                           rtems_device_minor_number minor)
{
  rtems_status_code status;
  struct mscan_channel_info *chan = &chan_info[minor];

  /* set registers according to MSCAN channel information */
  switch (minor) {

    case MSCAN_A:
      chan->rx_qname = rtems_build_name('C', 'N', 'A', 'Q');
      chan->tx_rb_sname = rtems_build_name('C', 'N', 'A', 'S');

      /* register RTEMS device names for MSCAN A */
      if ((status =
           rtems_io_register_name(MSCAN_A_DEV_NAME, major,
                                  MSCAN_A)) != RTEMS_SUCCESSFUL)
        return status;

      /* register RTEMS device names for MSCAN 0 */
      if ((status =
           rtems_io_register_name(MSCAN_0_DEV_NAME, major,
                                  MSCAN_A)) != RTEMS_SUCCESSFUL)
        return status;

      /* allocate the space for MSCAN A tx ring buffer */
      if (((chan->tx_ring_buf.buf_ptr) =
           malloc(sizeof (struct can_message) * (NO_OF_MSCAN_TX_BUFF + 1))) !=
          NULL) {
        chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr =
          chan->tx_ring_buf.buf_ptr;
      } else {
        return RTEMS_UNSATISFIED;
      }
      break;

    case MSCAN_B:
      chan->rx_qname = rtems_build_name('C', 'N', 'B', 'Q');
      chan->tx_rb_sname = rtems_build_name('C', 'N', 'B', 'S');

      /* register RTEMS device names for MSCAN B */
      if ((status =
           rtems_io_register_name(MSCAN_B_DEV_NAME, major,
                                  MSCAN_B)) != RTEMS_SUCCESSFUL)
        return status;

      /* register RTEMS device names for MSCAN 1 */
      if ((status =
           rtems_io_register_name(MSCAN_1_DEV_NAME, major,
                                  MSCAN_B)) != RTEMS_SUCCESSFUL)
        return status;

      /* allocate the space for MSCAN B tx ring buffer */
      if (((chan->tx_ring_buf.buf_ptr) =
           malloc(sizeof (struct can_message) * (NO_OF_MSCAN_TX_BUFF + 1))) !=
          NULL) {
        chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr =
          chan->tx_ring_buf.buf_ptr;
      } else {
        return RTEMS_UNSATISFIED;
      }
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  /* create RTEMS rx message queue */
  status =
    rtems_message_queue_create(chan->rx_qname, (uint32_t) NO_OF_MSCAN_RX_BUFF,
                               (uint32_t)
                               MSCAN_MESSAGE_SIZE(sizeof (struct can_message)),
                               (rtems_attribute) RTEMS_LOCAL | RTEMS_FIFO,
                               (rtems_id *) & (chan->rx_qid));

  /* create counting RTEMS tx ring buffer semaphore */
  status =
    rtems_semaphore_create(chan->tx_rb_sname, (uint32_t) (NO_OF_MSCAN_TX_BUFF),
                           RTEMS_COUNTING_SEMAPHORE | RTEMS_NO_INHERIT_PRIORITY
                           | RTEMS_NO_PRIORITY_CEILING | RTEMS_LOCAL,
                           (rtems_task_priority) 0,
                           (rtems_id *) & (chan->tx_rb_sid));

  /* Set up interrupts */
  if (!BSP_install_rtems_irq_handler(&(mpc5200_mscan_irq_data[minor])))
    rtems_panic("Can't attach MPC5x00 MSCAN interrupt handler %ld\n", minor);

  /* basic setup for channel info. struct. */
  chan->regs = (mscan *) &(mpc5200.mscan[minor]);
  chan->int_rx_err = 0;
  chan->id_extended = FALSE;
  chan->mode = MSCAN_INITIALIZED_MODE;
  chan->tx_buf_no = NO_OF_MSCAN_TX_BUFF;

  return status;

}

/*
 * MPC5x00 MSCAN device initialization
 */
rtems_device_driver mscan_initialize(rtems_device_major_number major,
                                     rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status;

  /* Initialization requested via RTEMS */
  if ((status = mscan_channel_initialize(major, MSCAN_A)) != RTEMS_SUCCESSFUL)
    bsp_fatal(MPC5200_FATAL_MSCAN_A_INIT);

  if ((status = mscan_channel_initialize(major, MSCAN_B)) != RTEMS_SUCCESSFUL)
    bsp_fatal(MPC5200_FATAL_MSCAN_B_INIT);

  if ((status =
       mpc5200_mscan_set_mode(MSCAN_A,
                              MSCAN_INIT_NORMAL_MODE)) != RTEMS_SUCCESSFUL)
    bsp_fatal(MPC5200_FATAL_MSCAN_A_SET_MODE);

  if ((status =
       mpc5200_mscan_set_mode(MSCAN_B,
                              MSCAN_INIT_NORMAL_MODE)) != RTEMS_SUCCESSFUL)
    bsp_fatal(MPC5200_FATAL_MSCAN_B_SET_MODE);

  return status;

}

/*
 * MPC5x00 MSCAN device open
 */
rtems_device_driver mscan_open(rtems_device_major_number major,
                               rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status = RTEMS_SUCCESSFUL;
  struct mscan_channel_info *chan = NULL;

  switch (minor) {

    case MSCAN_A:
    case MSCAN_B:
      chan = &chan_info[minor];
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  /* check mode */
  if ((chan->mode) == MSCAN_SLEEP_MODE) {

    /* if not already set enter init mode */
    status = mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE);
  }

  return status;

}

/*
 * MPC5x00 MSCAN device close
 */
rtems_device_driver mscan_close(rtems_device_major_number major,
                                rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status;

  switch (minor) {

    case MSCAN_A:
    case MSCAN_B:
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  /* enter deep sleep mode */
  status = mpc5200_mscan_set_mode(minor, MSCAN_SLEEP_MODE);

  return status;

}

/*
 * MPC5x00 MSCAN device read
 */
rtems_device_driver mscan_read(rtems_device_major_number major,
                               rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status;
  size_t message_size = 0;
  rtems_libio_rw_args_t *parms = (rtems_libio_rw_args_t *) arg;
  struct mscan_rx_parms *rx_parms = (struct mscan_rx_parms *) (parms->buffer);
  struct can_message *rx_mess = (struct can_message *) (rx_parms->rx_mess);
  struct mscan_channel_info *chan = NULL;

  switch (minor) {

    case MSCAN_A:
    case MSCAN_B:
      chan = &chan_info[minor];
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  /* end init mode if it is first read */
  if ((chan->mode) == MSCAN_INIT_NORMAL_MODE) {

    /* if not already set enter init mode */
    mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE);
  }

  if ((status =
       rtems_message_queue_receive(chan->rx_qid, (void *) (rx_mess),
                                   &message_size,
                                   (uint32_t) (rx_parms->rx_flags),
                                   (rtems_interval) (rx_parms->rx_timeout)))
      != RTEMS_SUCCESSFUL) {

    parms->bytes_moved = 0;

  } else {

    parms->bytes_moved = sizeof (struct can_message);

  }

  return status;

}

/*
 * MPC5x00 MSCAN device write
 */
rtems_device_driver mscan_write(rtems_device_major_number major,
                                rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status;
  rtems_libio_rw_args_t *parms = (rtems_libio_rw_args_t *) arg;
  struct mscan_tx_parms *tx_parms = (struct mscan_tx_parms *) (parms->buffer);
  struct can_message *tx_mess = (struct can_message *) (tx_parms->tx_mess);
  struct mscan_channel_info *chan = NULL;
  mscan *m = NULL;

  switch (minor) {
    case MSCAN_A:
    case MSCAN_B:
      chan = &chan_info[minor];
      m = chan->regs;
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  /* end init mode if it is first write */
  if ((chan->mode) == MSCAN_INIT_NORMAL_MODE) {

    /* if not already set enter init mode */
    mpc5200_mscan_set_mode(minor, MSCAN_NORMAL_MODE);
  }

  /* preset moved bytes */
  parms->bytes_moved = 0;

  /* obtain counting semaphore of tx ring buffer */
  if ((status =
       rtems_semaphore_obtain((rtems_id) (chan->tx_rb_sid), RTEMS_NO_WAIT,
                              (rtems_interval) 0))
      == RTEMS_SUCCESSFUL) {

    /* append the TOUCAN tx_id to the mess. due to interrupt handling */
    tx_mess->toucan_tx_idx = tx_parms->tx_idx;

    /* fill the tx ring buffer with the message */
    fill_tx_buffer(chan, tx_mess);

    /* enable message buffer specific interrupt */
    m->tier |= (TIER_TXEI0 | TIER_TXEI1 | TIER_TXEI2);

    /* calculate moved bytes */
    parms->bytes_moved = (tx_mess->mess_len) & 0x000F;

  }

  return status;

}

/*
 * MPC5x00 MSCAN device control
 */
rtems_device_driver mscan_control(rtems_device_major_number major,
                                  rtems_device_minor_number minor, void *arg)
{
  rtems_status_code status;
  uint16_t tx_id;
  rtems_libio_ioctl_args_t *parms = (rtems_libio_ioctl_args_t *) arg;
  struct mscan_ctrl_parms *ctrl_parms =
    (struct mscan_ctrl_parms *) (parms->buffer);
  struct mscan_channel_info *chan = NULL;
  mscan_handle *mscan_hdl = NULL;
  mscan *m = NULL;
  mscan_context context;
  uint8_t tx_buf_count = 0;

  switch (minor) {

    case MSCAN_A:
    case MSCAN_B:
      chan = &chan_info[minor];
      mscan_hdl = mpc5200_mscan_irq_data[minor].handle;
      m = chan->regs;
      break;

    default:
      return RTEMS_UNSATISFIED;
      break;
  }

  switch (parms->command) {

      /* TOUCAN callback initialization for MSCAN */
    case TOUCAN_MSCAN_INIT:
      mscan_hdl->toucan_callback = ctrl_parms->toucan_cb_fnc;
      break;

      /* set rx buffer ID */
    case MSCAN_SET_RX_ID:

      /* enter init mode */
      mscan_initialization_mode_enter(m, &context);

      switch (ctrl_parms->ctrl_reg_no) {

        case RX_BUFFER_0:
          m->idar0 = SET_IDR0(ctrl_parms->ctrl_id);
          m->idar1 = SET_IDR1(ctrl_parms->ctrl_id);
          break;

        case RX_BUFFER_1:
          m->idar2 = SET_IDR2(ctrl_parms->ctrl_id);
          m->idar3 = SET_IDR3(ctrl_parms->ctrl_id);
          break;

        case RX_BUFFER_2:
          m->idar4 = SET_IDR4(ctrl_parms->ctrl_id);
          m->idar5 = SET_IDR5(ctrl_parms->ctrl_id);
          break;

        case RX_BUFFER_3:
          m->idar6 = SET_IDR6(ctrl_parms->ctrl_id);
          m->idar7 = SET_IDR7(ctrl_parms->ctrl_id);
          break;

        default:
          break;

      }

      /* exit init mode and perform further initialization which is required in the normal mode */
      mscan_initialization_mode_leave(m, &context);

      /* enable ints. */
      mscan_interrupts_enable(m);

      /* wait for bus sync. */
      mpc5200_mscan_wait_sync(m);

      return RTEMS_SUCCESSFUL;
      break;

      /* get rx buffer ID */
    case MSCAN_GET_RX_ID:

      switch (ctrl_parms->ctrl_reg_no) {

        case RX_BUFFER_0:
          ctrl_parms->ctrl_id = GET_IDR0(m->idar0) | GET_IDR1(m->idar1);
          break;

        case RX_BUFFER_1:
          ctrl_parms->ctrl_id = GET_IDR2(m->idar2) | GET_IDR3(m->idar3);
          break;

        case RX_BUFFER_2:
          ctrl_parms->ctrl_id = GET_IDR4(m->idar4) | GET_IDR5(m->idar5);
          break;

        case RX_BUFFER_3:
          ctrl_parms->ctrl_id = GET_IDR6(m->idar6) | GET_IDR7(m->idar7);
          break;

        default:
          break;

      }

      break;

      /* set rx buffer ID mask */
    case MSCAN_SET_RX_ID_MASK:

      /* enter init mode */
      mscan_initialization_mode_enter(m, &context);

      switch (ctrl_parms->ctrl_reg_no) {

        case RX_BUFFER_0:
          m->idmr0 = SET_IDMR0(ctrl_parms->ctrl_id_mask);
          m->idmr1 = SET_IDMR1(ctrl_parms->ctrl_id_mask);
          break;

        case RX_BUFFER_1:
          m->idmr2 = SET_IDMR2(ctrl_parms->ctrl_id_mask);
          m->idmr3 = SET_IDMR3(ctrl_parms->ctrl_id_mask);
          break;

        case RX_BUFFER_2:
          m->idmr4 = SET_IDMR4(ctrl_parms->ctrl_id_mask);
          m->idmr5 = SET_IDMR5(ctrl_parms->ctrl_id_mask);
          break;

        case RX_BUFFER_3:
          m->idmr6 = SET_IDMR6(ctrl_parms->ctrl_id_mask);
          m->idmr7 = SET_IDMR7(ctrl_parms->ctrl_id_mask);
          break;

        default:
          break;

      }

      /* exit init mode and perform further initialization which is required in the normal mode */
      mscan_initialization_mode_leave(m, &context);

      /* enable ints. */
      mscan_interrupts_enable(m);

      /* wait for bus sync. */
      mpc5200_mscan_wait_sync(m);

      break;

      /* get rx buffer ID mask */
    case MSCAN_GET_RX_ID_MASK:

      switch (ctrl_parms->ctrl_reg_no) {

        case RX_BUFFER_0:
          ctrl_parms->ctrl_id_mask =
            (GET_IDMR0(m->idmr0) | GET_IDMR1(m->idmr1));
          break;

        case RX_BUFFER_1:
          ctrl_parms->ctrl_id_mask =
            (GET_IDMR2(m->idmr2) | GET_IDMR3(m->idmr3));
          break;

        case RX_BUFFER_2:
          ctrl_parms->ctrl_id_mask =
            (GET_IDMR4(m->idmr4) | GET_IDMR5(m->idmr5));
          break;

        case RX_BUFFER_3:
          ctrl_parms->ctrl_id_mask =
            (GET_IDMR6(m->idmr6) | GET_IDMR7(m->idmr7));
          break;

        default:
          break;

      }

      /* set tx buffer ID */
    case MSCAN_SET_TX_ID:

      /* check for availability of tx buffer */
      if (!((m->tflg) & (uint8_t) (ctrl_parms->ctrl_reg_no))) {

        /* do abort tx buf. request */
        m->tarq = (uint8_t) (ctrl_parms->ctrl_reg_no);

        /* wait for abort tx buf. ack. */
        while ((m->taak) & (uint8_t) (ctrl_parms->ctrl_reg_no));

      }

      /* select tx buf. */
      m->bsel = (uint8_t) (ctrl_parms->ctrl_reg_no);

      /* set the tx id of selected buf. */
      tx_id = ctrl_parms->ctrl_id;
      m->txidr0 = SET_IDR0(tx_id);
      m->txidr1 = SET_IDR1(tx_id);
      m->txidr2 = 0;
      m->txidr3 = 0;

      break;

      /* get tx buffer ID */
    case MSCAN_GET_TX_ID:

      /* select tx buf. */
      m->bsel = (uint8_t) (ctrl_parms->ctrl_reg_no);

      /* get tx id. of selected buf. */
      ctrl_parms->ctrl_id = GET_IDR0(m->txidr0) | GET_IDR1(m->txidr1);

      break;

      /* set can bitrate */
    case MSCAN_SET_BAUDRATE:
      /* perform all can bit time settings */
      if (!mscan_set_bit_rate(m, ctrl_parms->ctrl_can_bitrate)) {
        return RTEMS_UNSATISFIED;
      }

      /* enable ints. */
      mscan_interrupts_enable(m);

      /* wait for bus sync. */
      mpc5200_mscan_wait_sync(m);

      break;

    case SET_TX_BUF_NO:

      /* check for different settings of tx ring buffer */
      if ((tx_buf_count =
           chan->tx_buf_no) != (uint8_t) (ctrl_parms->ctrl_tx_buf_no)) {

        /* preset the channel specific no of messages in the tx ring buffer */
        tx_buf_count = chan->tx_buf_no;

        /* try to obtain all of the tx ring buffers */
        while (tx_buf_count > 0) {

          /* obtain semaphore of all tx ring buffers */
          if ((status =
               rtems_semaphore_obtain((rtems_id) (chan->tx_rb_sid), RTEMS_WAIT,
                                      (rtems_interval) 10))
              == RTEMS_SUCCESSFUL) {

            tx_buf_count--;

          }

        }

        /* free the former tx ring buffer */
        free((void *) chan->tx_ring_buf.buf_ptr);

        /* allocate the tx ring buffer with new size */
        if (((chan->tx_ring_buf.buf_ptr) =
             malloc(sizeof (struct can_message) *
                    ((uint8_t) (ctrl_parms->ctrl_tx_buf_no) + 1))) != NULL) {
          chan->tx_ring_buf.head_ptr = chan->tx_ring_buf.tail_ptr =
            chan->tx_ring_buf.buf_ptr;
        } else {
          return RTEMS_UNSATISFIED;
        }

        /* set the new amount of tx buffers */
        chan->tx_buf_no = (uint8_t) (ctrl_parms->ctrl_tx_buf_no);

        /* release the semaphore of all tx ring buffers */
        while (tx_buf_count < chan->tx_buf_no) {

          /* obtain semaphore of all tx ring buffers */
          rtems_semaphore_release((rtems_id) (chan->tx_rb_sid));

          tx_buf_count++;

        }

      } else {

        return RTEMS_SUCCESSFUL;

      }
      break;

    default:
      break;

  }

  return RTEMS_SUCCESSFUL;

}