summaryrefslogblamecommitdiffstats
path: root/c/src/lib/libbsp/powerpc/gen5200/mscan/mscan.c
blob: f92c78e13aabc0506b3dbeafc66953aa48ecb657 (plain) (tree)
1
2
3
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710


                                                                   













                                                                   







                                                                   
                           

                               
                  


                            

                                      
                                                               
                                                           




























                                                                                                          
                                                                          

                                
                                                           

































                                                                                                        
                                                                                                          




























                                                                                             








                                                                         
                                                         
                                                    
                       

    
                        

     














































































                                                                                                                                      
 


            
 











                                                          
























                                                                       

                                    


                 
                 
                 



                                                                                                            











































































































































































































































































































































































































































                                                                                                                                  
 


                                                  



         



                                                                                              

                                                                       






                                                                    
















                                                

                                 
                         

                                               
                               



      

                                                 

                                                 
                                      

                                             
                             



           







                                                    
 



                                      
                                                                                                                                 

                            








                                                                            










                                                                      
                                                              










                                                                      
                                                                         










                                                                                   



                                                                        






































                                                                                                          








                                           

































































                                                                                       






                                                                        




                                                

         

                                                                      




                                          

                                                 
             
 
                          




                                          


                          
                         
                                       

                                                























                                                                                                            
 





                                                             
                                                                

                                                   




                                                                                                  

                          








                                                                                                                



                                                                 
                                                                




                                                                                                      












                                                                                                                






                               














                                                                                                                                 



                                                                            




                                                                         






















                                                                            
                                                                                            

                                       
                                                                                            














                                                                 
                                              














                                         







                                                              

































































                                                                                     








                                                     






























                                                                                  






                                                                                         


               











                                                       

                                            

     

                                                     

     

                          
 




                                                                  

     

                                                                        
 

                                                  
 

                                                          
 

                                                      


     
                











                                                                    
                           
                 





                                                                                          












































































































                                                                                                  

                                                              


                         

                                                              


                         

                                                              


                         

                                                              
























                                                                                                  
                                                                                       


                         
                                                                                       


                         
                                                                                       


                         
                                                                                       












































                                                                              



























































































                                                                                                                                    








                          
/*===============================================================*\
| Project: RTEMS generic MPC5200 BSP                              |
+-----------------------------------------------------------------+
|                    Copyright (c) 2005                           |
|                    Embedded Brains GmbH                         |
|                    Obere Lagerstr. 30                           |
|                    D-82178 Puchheim                             |
|                    Germany                                      |
|                    rtems@embedded-brains.de                     |
+-----------------------------------------------------------------+
| The license and distribution terms for this file may be         |
| found in the file LICENSE in this distribution or at            |
|                                                                 |
| http://www.rtems.com/license/LICENSE.                           |
|                                                                 |
+-----------------------------------------------------------------+
| this file contains the MSCAN driver                             |
\*===============================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <rtems.h>
#include <rtems/error.h>
#include <rtems/libio.h>
#include <string.h>
#include "../include/bsp.h"
#include "../irq/irq.h"
#include "../include/mpc5200.h"
#include "mscan.h"

/* #define MSCAN_LOOPBACK */

volatile uint32_t tx_int_wr_count = 0;

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

/* time segmant table  */
uint8_t can_time_segment_table[CAN_MAX_NO_OF_TQ - MIN_NO_OF_TQ + 1][NO_OF_TABLE_ENTRIES] = {

/* Total no. of time quantas */   /* Time Segment 1*/    /* Time Segment 2 */     /* Sync: Jump width */
{         7,                                4,                     2,                       1          },
{         8,                                5,                     2,                       1          },
{         9,                                6,                     2,                       2          },
{        10,                                6,                     3,                       2          },
{        11,                                7,                     3,                       2          },
{        12,                                8,                     3,                       2          },
{        13,                                8,                     4,                       2          },
{        14,                                9,                     4,                       2          },
{        15,                               10,                     4,                       2          },
{        16,                               10,                     5,                       2          },
{        17,                               11,                     5,                       2          },
{        18,                               12,                     5,                       2          },
{        19,                               12,                     6,                       2          },
{        20,                               13,                     6,                       2          },
{        21,                               14,                     6,                       2          },
{        22,                               14,                     7,                       2          },
{        23,                               15,                     7,                       2          },
{        24,                               15,                     8,                       2          },
{        25,                               16,                     8,                       2          }};


/*
 * 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;
  volatile struct mpc5200_mscan *mscan = 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((mscan->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 */
        mscan->bsel  = idx;

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

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

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

        /* select one free tx buffer if TOUCAN not registered) */
        if(((mscan_hdl->toucan_callback) == NULL) || (((mscan_hdl->toucan_callback) != NULL) && ((tx_mess_ptr->toucan_tx_id) == idx)))
          {

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

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

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

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

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

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

	 	  tx_int_wr_count++;

          }
        else
          {

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

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

  /*
   handle rx interrupts
   */

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

    /* can messages received ? */
    while(mscan->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 = (mscan->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 */
            mscan->rflg |= RFLG_RXF;

            return;
            break;
	      }

        }

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

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

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

      /* get the data */
	  switch(rx_mess_ptr->mess_len)
	    {

	    case 8:
	      rx_mess_ptr->mess_data[7] = mscan->rxdsr7;
	    case 7:
	      rx_mess_ptr->mess_data[6] = mscan->rxdsr6;
	    case 6:
	      rx_mess_ptr->mess_data[5] = mscan->rxdsr5;
	    case 5:
	      rx_mess_ptr->mess_data[4] = mscan->rxdsr4;
	    case 4:
	      rx_mess_ptr->mess_data[3] = mscan->rxdsr3;
	    case 3:
	      rx_mess_ptr->mess_data[2] = mscan->rxdsr2;
	    case 2:
	      rx_mess_ptr->mess_data[1] = mscan->rxdsr1;
	    case 1:
	      rx_mess_ptr->mess_data[0] = mscan->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)(((mscan->idac) & 0x7) + 3));

	    }

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

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

    }

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

      mscan->rflg |= RFLG_CSCIF;

      if(mscan_hdl->toucan_callback != NULL)
        {

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

        }

      }

  }


/*
 * Disable MSCAN ints.
 */
void mpc5200_mscan_int_disable(volatile struct mpc5200_mscan *mscan)
  {

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

  /* TX Interrupt Enable on MSCAN_A/_B -----------------------------*/
  /*    [07]:res.          0 : reserved                             */
  /*    [06]:res.          0 : reserved                             */
  /*    [05]:res.          0 : reserved                             */
  /*    [04]:res.          0 : reserved                             */
  /*    [03]:res.          0 : reserved                             */
  /*    [02]:TSEG12        0 : TX2message buffer int. is disabled   */
  /*    [01]:TSEG11        0 : TX1 message buffer int. is disabled  */
  /*    [00]:TSEG10        0 : TX0 message buffer int. is disabled  */
  mscan->tier &= ~(TIER_TXEI2 | TIER_TXEI1 | TIER_TXEI0);

  return;

  }


/*
 * Enable MSCAN ints.
 */
void mpc5200_mscan_int_enable(volatile struct mpc5200_mscan *mscan)
  {

  /* 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      */
  mscan->rier |= (RIER_CSCIE  | RIER_RXFIE | RIER_RSTAT(1) | RIER_TSTAT(1));

  return;

  }

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

  mpc5200_mscan_int_enable(mscan);

  return;

  }


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

  mpc5200_mscan_int_disable(mscan);

  return;

  }


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

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

  return RTEMS_SUCCESSFUL;

  }


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

  mpc5200_mscan_int_enable(mscan);

  return;

  }


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

  mpc5200_mscan_int_disable(mscan);

  return;

  }


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

  if((mscan->rier & RIER_CSCIE) && (mscan->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
  }};


/*
 * Enter MSCAN sleep mode
 */
void mpc5200_mscan_enter_sleep_mode(volatile struct mpc5200_mscan *mscan)
  {

  /* 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        1 : WakeUp Enabled                         */
  /*    [01]:SLPRQ    0->1 : Sleep Mode Request                     */
  /*    [00]:INITRQ      0 : No init. Mode Request                  */
  /* select sleep mode */
  mscan->ctl0 |= (CTL0_SLPRQ);

  /* Control Register 1 --------------------------------------------*/
  /*    [07]:CANE        0 : MSCAN Module is disabled               */
  /*    [06]:CLKSRC      1 : Clock Source -> IPB-Clock (33 MHz)     */
  /*    [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->1 : Sleep Mode Acknowledge (rd. only)      */
  /*    [00]:INITAK      0 : Init. Mode Acknowledge (rd. only)      */
  /* wait for sleep mode acknowledge */
  while(!(mscan->ctl1 & CTL1_SLPAK));

  return;

  }


/*
 * Exit MSCAN sleep mode
 */
void mpc5200_mscan_exit_sleep_mode(volatile struct mpc5200_mscan *mscan)
  {

  /* 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        1 : WakeUp Enabled                         */
  /*    [01]:SLPRQ    0->1 : Sleep Mode Request                     */
  /*    [00]:INITRQ      0 : No init. Mode Request                  */
  /* select sleep mode */
  mscan->ctl0 &= ~(CTL0_SLPRQ);

  /* Control Register 1 --------------------------------------------*/
  /*    [07]:CANE        0 : MSCAN Module is disabled               */
  /*    [06]:CLKSRC      1 : Clock Source -> IPB-Clock (33 MHz)     */
  /*    [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->1 : Sleep Mode Acknowledge (rd. only)      */
  /*    [00]:INITAK      0 : Init. Mode Acknowledge (rd. only)      */
  /* wait for sleep mode acknowledge */
  while((mscan->ctl1 & CTL1_SLPAK));

  return;

  }


/*
 * Enter MSCAN init mode and disconnect from bus
 */
void mpc5200_mscan_enter_init_mode(volatile struct mpc5200_mscan *mscan)
  {

  /* 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->1 : Init. Mode Request                     */
  /* select init mode */
  mscan->ctl0 |= (CTL0_INITRQ);

  /* Control Register 1 --------------------------------------------*/
  /*    [07]:CANE        0 : MSCAN Module is disabled               */
  /*    [06]:CLKSRC      1 : Clock Source -> IPB-Clock (33 MHz)     */
  /*    [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->1 : Init. Mode Acknowledge (rd. only)      */
  /* wait for init mode acknowledge */
  while(!(mscan->ctl1 & CTL1_INITAK));

  return;

  }


/*
 * Exit MSCAN init mode
 */
void mpc5200_mscan_exit_init_mode(volatile struct mpc5200_mscan *mscan)
  {

  /* 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   1->0 : Init. Mode Request                     */
  /* select normal mode */
  mscan->ctl0 &= ~(CTL0_INITRQ);

  /* Control Register 1 --------------------------------------------*/
  /*    [07]:CANE        0 : MSCAN Module is disabled               */
  /*    [06]:CLKSRC      1 : Clock Source -> IPB-Clock (33 MHz)     */
  /*    [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   1->0 : Init. Mode Acknowledge (rd. only)      */
  /* wait for normal mode acknowledge */
  while(mscan->ctl1 & CTL1_INITAK);

  return;

  }


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

  /* 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(!((mscan->ctl0) & CTL0_SYNCH));
#endif
  return;

  }

/* calculate the can clock prescaler value */
uint8_t prescaler_calculation(uint32_t can_bit_rate, uint32_t can_clock_frq, uint8_t *tq_no) {

/* local variables */
uint8_t presc_val, tq_no_dev_min = 0;
uint32_t bit_rate, bit_rate_dev, frq_tq, bit_rate_dev_min = 0xFFFFFFFF;

/* loop through all values of time quantas */
for(*tq_no = CAN_MAX_NO_OF_TQ; *tq_no >= MIN_NO_OF_TQ; (*tq_no)--) {

  /* calculate time quanta freq. */
  frq_tq = *tq_no * can_bit_rate;

  /* calculate the optimized prescal. val. */
  presc_val = (can_clock_frq+frq_tq/2)/frq_tq;

  /* calculate the bitrate */
  bit_rate = can_clock_frq/(*tq_no * presc_val);

  /* calculate the bitrate deviation */
  if(can_bit_rate >= bit_rate)
    {
    /* calculate the bitrate deviation */
    bit_rate_dev = can_bit_rate - bit_rate;
    }
  else
    {
    /* calculate the bitrate deviation */
    bit_rate_dev = bit_rate - can_bit_rate;
    }

  /* check the deviation freq. */
  if(bit_rate_dev == 0) {

    /* return if best match (zero deviation) */
   return (uint8_t)(presc_val);
    }
  else
    {

    /* check for minimum of bit rate deviation */
    if(bit_rate_dev < bit_rate_dev_min) {

      /* recognize the minimum freq. deviation */
      bit_rate_dev_min = bit_rate_dev;

      /* recognize the no. of time quantas */
      tq_no_dev_min = *tq_no;
	  }
	}
  }

  /* get the no of tq's */
  *tq_no = tq_no_dev_min;

  /* calculate time quanta freq. */
  frq_tq = *tq_no * can_bit_rate;

  /* return the optimized prescaler value */
  return (uint8_t)((can_clock_frq+frq_tq/2)/frq_tq);
}

/*
 * MPC5x00 MSCAN set up the bit timing
 */
void mpc5200_mscan_perform_bit_time_settings(volatile struct mpc5200_mscan *mscan, uint32_t can_bit_rate, uint32_t can_clock_frq)
{
  uint32_t prescale_val = 0;
  uint8_t tq_no, tseg_1, tseg_2, sseg;

  /* get optimized prescaler value */
  prescale_val = prescaler_calculation(can_bit_rate, can_clock_frq, &tq_no);

  /* get time segment length from time segment table */
  tseg_1 = can_time_segment_table[tq_no - MIN_NO_OF_TQ][TSEG_1];
  tseg_2 = can_time_segment_table[tq_no - MIN_NO_OF_TQ][TSEG_2];
  sseg    = can_time_segment_table[tq_no - MIN_NO_OF_TQ][SJW];

  /* Bus Timing Register 0 MSCAN_A/_B ------------------------------*/
  /*    [07]:SJW1        1 : Synchronization jump width, Bit1       */
  /*    [06]:SJW0        0 : Synchronization jump width, Bit0       */
  /*                         SJW = 2 -> 3 Tq clock cycles           */
  /*    [05]:BRP5        0 : Baud Rate Prescaler, Bit 5             */
  /*    [04]:BRP4        0 : Baud Rate Prescaler, Bit 4             */
  /*    [03]:BRP3        0 : Baud Rate Prescaler, Bit 3             */
  /*    [02]:BRP2        1 : Baud Rate Prescaler, Bit 2             */
  /*    [01]:BRP1        0 : Baud Rate Prescaler, Bit 1             */
  /*    [00]:BRP0        1 : Baud Rate Prescaler, Bit 0             */
  mscan->btr0 = (BTR0_SJW(sseg-1) | BTR0_BRP(prescale_val-1));

  /* Bus Timing Register 1 MSCAN_A/_B ------------------------------*/
  /*    [07]:SAMP        0 : One Sample per bit                     */
  /*    [06]:TSEG22      0 : Time Segment 2, Bit 2                  */
  /*    [05]:TSEG21      1 : Time Segment 2, Bit 1                  */
  /*    [04]:TSEG20      0 : Time Segment 2, Bit 0                  */
  /*                         -> PHASE_SEG2 = 3 Tq                   */
  /*    [03]:TSEG13      0 : Time Segment 1, Bit 3                  */
  /*    [02]:TSEG12      1 : Time Segment 1, Bit 2                  */
  /*    [01]:TSEG11      1 : Time Segment 1, Bit 1                  */
  /*    [00]:TSEG10      0 : Time Segment 1, Bit 0                  */
  mscan->btr1 =  (BTR1_TSEG_22_20(tseg_2-1) | BTR1_TSEG_13_10(tseg_1-1));

  return;

  }


/*
 * MPC5x00 MSCAN perform settings in init mode
 */
void mpc5200_mscan_perform_init_mode_settings(volatile struct mpc5200_mscan *mscan)
  {

  /* perform all can bit time settings */
  mpc5200_mscan_perform_bit_time_settings(mscan,CAN_BIT_RATE,IPB_CLOCK);

  /* 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
  mscan->ctl1 &= ~(CTL1_LISTEN | CTL1_LOOPB | CTL1_CLKSRC);
#else
  mscan->ctl1 &= ~(CTL1_LISTEN | CTL1_CLKSRC);
  mscan->ctl1 |=  (CTL1_LOOPB);
#endif

  /* IPB clock                  -> IPB_CLOCK (bsp.h)                                                    */
  /* bitrate                    -> CAN_BIT_RATE (mscan.h)                                               */
  /* Max. no of Tq              -> CAN_MAX_NO_OF_TQ (mscan.h)                                           */
  /* 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 (mscan.h)    */
  /* time segment 2             ->  8 tq (PHASE_SEG2)        , CAN_MAX_NO_OF_TQ_TSEG2 =  7 (mscan.h)    */
  /* SJW                        ->  3 (fixed 0...3)          , CAN_MAX_NO_OF_TQ_SJW   =  2 (mscan.h)    */

  /* 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    */
  mscan->idac &= ~(IDAC_IDAM1);
  mscan->idac |=  (IDAC_IDAM0);

  /* initialize rx filter masks (16 bit) */
  mscan->idmr0  = SET_IDMR0(0x07FF);
  mscan->idmr1  = SET_IDMR1(0x07FF);
  mscan->idmr2  = SET_IDMR2(0x07FF);
  mscan->idmr3  = SET_IDMR3(0x07FF);
  mscan->idmr4  = SET_IDMR4(0x07FF);
  mscan->idmr5  = SET_IDMR5(0x07FF);
  mscan->idmr6  = SET_IDMR6(0x07FF);
  mscan->idmr7  = SET_IDMR7(0x07FF);

  /* 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 */
  mscan->ctl1 |= (CTL1_CANE);

  return;

  }


/*
 * MPC5x00 MSCAN perform settings in normal mode
 */
void mpc5200_mscan_perform_normal_mode_settings(volatile struct mpc5200_mscan *mscan)
  {

  /* 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 */
  mscan->ctl0 &= ~(CTL0_CSWAI);
  mscan->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;
  volatile struct mpc5200_mscan *mscan     = NULL;

  switch(minor)
    {

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

    default:
      return RTEMS_UNSATISFIED;
      break;
    }

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

  switch(mode)
    {

    case MSCAN_INIT_NORMAL_MODE:
      /* if not already set enter init mode */
	  mpc5200_mscan_enter_init_mode(mscan);
	  /* perform initialization which has to be done in init mode */
      mpc5200_mscan_perform_init_mode_settings(mscan);
      break;

    case MSCAN_NORMAL_MODE:
      /* if not already set enter init mode */
      mpc5200_mscan_enter_init_mode(mscan);

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

        /* perform initialization which has to be done in init mode */
        mpc5200_mscan_perform_init_mode_settings(mscan);
	    }

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

        /* exit sleep mode */
	    mpc5200_mscan_exit_sleep_mode(mscan);
	    }

      /* exit init mode */
      mpc5200_mscan_exit_init_mode(mscan);
      /* enable ints. */
      mpc5200_mscan_int_enable(mscan);
      /* wait for bus sync. */
      mpc5200_mscan_wait_sync(mscan);
      break;

    case MSCAN_SLEEP_MODE:
      /* disable ints. */
      mpc5200_mscan_int_disable(mscan);
      /* exit sleep mode */
	  mpc5200_mscan_enter_sleep_mode(mscan);
      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 %d\n", minor);

  /* basic setup for channel info. struct. */
  chan->regs           = (struct mpc5200_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)
    rtems_fatal_error_occurred(status);

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

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

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

  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;
  struct mscan_channel_info *chan = NULL;

  switch(minor)
    {

    case MSCAN_A:
    case MSCAN_B:
	  chan = &chan_info[minor];
      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;
  uint32_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),
                                          (uint32_t *)&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_handle                  *mscan_hdl   = NULL;
  volatile struct mpc5200_mscan *mscan       = NULL;

  switch(minor)
    {
    case MSCAN_A:
    case MSCAN_B:
	  chan      = &chan_info[minor];
      mscan_hdl = mpc5200_mscan_irq_data[minor].handle;
      mscan     = 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_id = tx_parms->tx_id;

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

    /* enable message buffer specific interrupt */
    mscan->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;
  volatile struct mpc5200_mscan *mscan       = NULL;
  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;
      mscan = 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 */
      mpc5200_mscan_enter_init_mode(mscan);

      switch(ctrl_parms->ctrl_reg_no)
        {

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

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

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

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

        default:
        break;

        }

      /* exit init mode and perform further initialization which is required in the normal mode */
      mpc5200_mscan_exit_init_mode(mscan);

      /* enable ints. */
      mpc5200_mscan_int_enable(mscan);

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

      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(mscan->idar0) | GET_IDR1(mscan->idar1);
          break;

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

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

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

        default:
        break;

        }

      break;

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

      /* enter init mode */
      mpc5200_mscan_enter_init_mode(mscan);

      switch(ctrl_parms->ctrl_reg_no)
        {

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

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

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

        case RX_BUFFER_3:
          mscan->idmr6  = SET_IDMR6(ctrl_parms->ctrl_id_mask);
          mscan->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 */
      mpc5200_mscan_exit_init_mode(mscan);

      /* enable ints. */
      mpc5200_mscan_int_enable(mscan);

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

      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(mscan->idmr0) | GET_IDMR1(mscan->idmr1);
          break;

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

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

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

        default:
        break;

        }

    /* set tx buffer ID */
    case MSCAN_SET_TX_ID:

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

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

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

        }

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

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

      break;

    /* get tx buffer ID */
    case MSCAN_GET_TX_ID:

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

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

      break;

    /* set can bitrate */
    case MSCAN_SET_BAUDRATE:

      /* check bitrate settings */
      if(((ctrl_parms->ctrl_can_bitrate) >= CAN_BIT_RATE_MIN) && ((ctrl_parms->ctrl_can_bitrate) <= CAN_BIT_RATE_MAX)) {

        /* enter init mode */
        mpc5200_mscan_enter_init_mode(mscan);

        /* perform all can bit time settings */
        mpc5200_mscan_perform_bit_time_settings(mscan,(uint32_t)(ctrl_parms->ctrl_can_bitrate),IPB_CLOCK);

        /* exit init mode and perform further initialization which is required in the normal mode */
        mpc5200_mscan_exit_init_mode(mscan);

        /* enable ints. */
        mpc5200_mscan_int_enable(mscan);

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

	    return RTEMS_SUCCESSFUL;
	    }
	  else {

	     return RTEMS_UNSATISFIED;
	     }

      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;

  }