forked from Imagelibrary/rtems
560 lines
14 KiB
C
560 lines
14 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup m
|
|
*
|
|
* @brief MSCAN support functions code.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2008 embedded brains GmbH. All rights reserved.
|
|
* 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 <bsp.h>
|
|
#include <bsp/mscan-base.h>
|
|
|
|
#define MIN_NO_OF_TQ 7
|
|
#define TSEG_1 1
|
|
#define TSEG_2 2
|
|
#define NO_OF_TABLE_ENTRIES 4
|
|
#define SJW 3
|
|
|
|
#define CAN_MAX_NO_OF_TQ 25
|
|
#define CAN_MAX_NO_OF_TQ_TSEG1 15
|
|
#define CAN_MAX_NO_OF_TQ_TSEG2 7
|
|
#define CAN_MAX_NO_OF_TQ_SJW 2
|
|
|
|
/**
|
|
* Time segmant table.
|
|
*
|
|
* <table>
|
|
* <tr>
|
|
* <td>Number of time quantas</th>
|
|
* <td>Time Segment 1</th>
|
|
* <td>Time Segment 2</th>
|
|
* <td>Sync: Jump width</th>
|
|
* </tr>
|
|
* </table>
|
|
*/
|
|
static uint8_t can_time_segment_table
|
|
[CAN_MAX_NO_OF_TQ - MIN_NO_OF_TQ + 1] [NO_OF_TABLE_ENTRIES] = {
|
|
{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}
|
|
};
|
|
|
|
/**
|
|
* @brief Calculates the MSCAN clock prescaler value.
|
|
*/
|
|
static uint8_t prescaler_calculation(
|
|
unsigned can_bit_rate,
|
|
unsigned 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);
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the bit rate for the MSCAN module @a m to @a can_bit_rate
|
|
* in [bits/s].
|
|
*/
|
|
bool mscan_set_bit_rate( volatile mscan *m, unsigned can_bit_rate)
|
|
{
|
|
mscan_context context;
|
|
unsigned prescale_val = 0;
|
|
uint8_t tq_no,
|
|
tseg_1,
|
|
tseg_2,
|
|
sseg;
|
|
|
|
if (can_bit_rate < MSCAN_BIT_RATE_MIN || can_bit_rate > MSCAN_BIT_RATE_MAX) {
|
|
return false;
|
|
}
|
|
|
|
/* Enter initialization mode */
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
/* get optimized prescaler value */
|
|
prescale_val = prescaler_calculation(can_bit_rate, IPB_CLOCK, &tq_no);
|
|
|
|
/* Check prescaler value */
|
|
if (prescale_val > 64) {
|
|
/* Leave initialization mode */
|
|
mscan_initialization_mode_leave( m, &context);
|
|
|
|
return false;
|
|
}
|
|
|
|
/* 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 */
|
|
m->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 */
|
|
m->btr1 = (BTR1_TSEG2(tseg_2 - 1) | BTR1_TSEG1(tseg_1 - 1));
|
|
|
|
/* Leave initialization mode */
|
|
mscan_initialization_mode_leave( m, &context);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Disables all interrupts for the MSCAN module @a m.
|
|
*/
|
|
void mscan_interrupts_disable( volatile mscan *m)
|
|
{
|
|
m->rier = 0;
|
|
m->tier = 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Enter initialization mode for the MSCAN module @a m.
|
|
*
|
|
* Saves the current MSCAN context in @a context.
|
|
*/
|
|
void mscan_initialization_mode_enter( volatile mscan *m, mscan_context *context)
|
|
{
|
|
/* Save context */
|
|
context->ctl0 = m->ctl0 & CTL0_TIME;
|
|
context->rier = m->rier;
|
|
context->tier = m->tier;
|
|
|
|
/* Request initialization mode */
|
|
m->ctl0 |= CTL0_INITRQ;
|
|
|
|
/* Wait for initialization mode acknowledge */
|
|
while ((m->ctl1 & CTL1_INITAK) == 0) {
|
|
/* Wait */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Leave initialization mode for the MSCAN module @a m.
|
|
*
|
|
* Saves the previous MSCAN context saved in @a context.
|
|
*/
|
|
void mscan_initialization_mode_leave( volatile mscan *m, const mscan_context *context)
|
|
{
|
|
/* Clear initialization mode request */
|
|
m->ctl0 &= ~CTL0_INITRQ;
|
|
|
|
/* Wait for clearing of initialization mode acknowledge */
|
|
while ((m->ctl1 & CTL1_INITAK) != 0) {
|
|
/* Wait */
|
|
}
|
|
|
|
/* Leave sleep mode */
|
|
mscan_sleep_mode_leave( m);
|
|
|
|
/* Restore context */
|
|
m->ctl0 |= context->ctl0;
|
|
m->rier |= context->rier;
|
|
m->tier |= context->tier;
|
|
}
|
|
|
|
/**
|
|
* @brief Enter sleep mode for the MSCAN module @a m.
|
|
*/
|
|
void mscan_sleep_mode_enter( volatile mscan *m)
|
|
{
|
|
/* Request sleep mode */
|
|
m->ctl0 |= CTL0_SLPRQ;
|
|
}
|
|
|
|
/**
|
|
* @brief Leave sleep mode for the MSCAN module @a m.
|
|
*/
|
|
void mscan_sleep_mode_leave( volatile mscan *m)
|
|
{
|
|
/* Clear sleep mode request */
|
|
m->ctl0 &= ~CTL0_SLPRQ;
|
|
|
|
/* Wait for clearing of sleep mode acknowledge */
|
|
while ((m->ctl1 & CTL1_SLPAK) != 0) {
|
|
/* Wait */
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Enables and initializes the MSCAN module @a m.
|
|
*
|
|
* The module is set to listen only mode.
|
|
*/
|
|
bool mscan_enable( volatile mscan *m, unsigned bit_rate)
|
|
{
|
|
bool s = true;
|
|
|
|
/* Disable the module */
|
|
mscan_disable( m);
|
|
|
|
/* Enable module in listen only */
|
|
m->ctl1 = CTL1_CANE | CTL1_LISTEN;
|
|
|
|
/* Close acceptance filters */
|
|
m->idac = IDAC_IDAM1 | IDAC_IDAM0;
|
|
|
|
/* Clear filter */
|
|
mscan_filter_clear( m);
|
|
|
|
/* Set bit rate and leave initialization mode */
|
|
s = mscan_set_bit_rate( m, bit_rate);
|
|
|
|
/* Clear all flags */
|
|
m->ctl0 = CTL0_RXFRM;
|
|
|
|
/* Disable interrupts */
|
|
mscan_interrupts_disable( m);
|
|
|
|
return s;
|
|
}
|
|
|
|
/**
|
|
* @brief Disables the MSCAN module @a m.
|
|
*
|
|
* The module is set to sleep mode and disabled afterwards.
|
|
*/
|
|
void mscan_disable( volatile mscan *m)
|
|
{
|
|
mscan_context context;
|
|
|
|
/* Disable interrupts */
|
|
mscan_interrupts_disable( m);
|
|
|
|
/* Enter initialization mode */
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
/* Disable module */
|
|
m->ctl1 &= ~CTL1_CANE;
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the filter ID and mask registers of the MSCAN module @a m to
|
|
* default values.
|
|
*/
|
|
void mscan_filter_clear( volatile mscan *m)
|
|
{
|
|
mscan_context context;
|
|
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
/* Setup ID acceptance registers */
|
|
m->idar0 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar1 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar2 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar3 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar4 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar5 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar6 = MSCAN_FILTER_ID_DEFAULT;
|
|
m->idar7 = MSCAN_FILTER_ID_DEFAULT;
|
|
|
|
/* Setup ID mask registers */
|
|
m->idmr0 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr1 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr2 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr3 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr4 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr5 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr6 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
m->idmr7 = (uint8_t) MSCAN_FILTER_MASK_DEFAULT;
|
|
|
|
mscan_initialization_mode_leave( m, &context);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the number of active filters of the MSCAN module @a m.
|
|
*
|
|
* @see MSCAN_FILTER_NUMBER_MIN, MSCAN_FILTER_NUMBER_2, MSCAN_FILTER_NUMBER_4
|
|
* and MSCAN_FILTER_NUMBER_MAX.
|
|
*/
|
|
unsigned mscan_filter_number( volatile mscan *m)
|
|
{
|
|
uint8_t idam = m->idac & IDAC_IDAM;
|
|
|
|
switch (idam) {
|
|
case 0:
|
|
return MSCAN_FILTER_NUMBER_2;
|
|
case IDAC_IDAM0:
|
|
return MSCAN_FILTER_NUMBER_4;
|
|
case IDAC_IDAM1:
|
|
return MSCAN_FILTER_NUMBER_MAX;
|
|
default:
|
|
return MSCAN_FILTER_NUMBER_MIN;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the number of active filters of the MSCAN module @a m to @a
|
|
* number and returns true if @a number is valid.
|
|
*
|
|
* @see MSCAN_FILTER_NUMBER_MIN, MSCAN_FILTER_NUMBER_2, MSCAN_FILTER_NUMBER_4
|
|
* and MSCAN_FILTER_NUMBER_MAX.
|
|
*/
|
|
bool mscan_set_filter_number( volatile mscan *m, unsigned number)
|
|
{
|
|
mscan_context context;
|
|
uint8_t idac = IDAC_IDAM1 | IDAC_IDAM0;
|
|
|
|
switch (number) {
|
|
case MSCAN_FILTER_NUMBER_MIN:
|
|
break;
|
|
case MSCAN_FILTER_NUMBER_2:
|
|
idac = 0;
|
|
break;
|
|
case MSCAN_FILTER_NUMBER_4:
|
|
idac = IDAC_IDAM0;
|
|
break;
|
|
case MSCAN_FILTER_NUMBER_MAX:
|
|
idac = IDAC_IDAM1;
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
m->idac = idac;
|
|
|
|
mscan_initialization_mode_leave( m, &context);
|
|
|
|
/* Clear filter */
|
|
mscan_filter_clear( m);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the address of the CANIDAR register with index @a i of the
|
|
* MSCAN module @a m.
|
|
*
|
|
* @warning The index @a i is not checked if it is in range.
|
|
*/
|
|
volatile uint8_t *mscan_id_acceptance_register( volatile mscan *m, unsigned i)
|
|
{
|
|
volatile uint8_t *const idar [8] = {
|
|
&m->idar0,
|
|
&m->idar1,
|
|
&m->idar2,
|
|
&m->idar3,
|
|
&m->idar4,
|
|
&m->idar5,
|
|
&m->idar6,
|
|
&m->idar7
|
|
};
|
|
|
|
return idar [i];
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the address of the CANIDMR register with index @a i of the
|
|
* MSCAN module @a m.
|
|
*
|
|
* @warning The index @a i is not checked if it is in range.
|
|
*/
|
|
volatile uint8_t *mscan_id_mask_register( volatile mscan *m, unsigned i)
|
|
{
|
|
volatile uint8_t *const idmr [8] = {
|
|
&m->idmr0,
|
|
&m->idmr1,
|
|
&m->idmr2,
|
|
&m->idmr3,
|
|
&m->idmr4,
|
|
&m->idmr5,
|
|
&m->idmr6,
|
|
&m->idmr7
|
|
};
|
|
|
|
return idmr [i];
|
|
}
|
|
|
|
/**
|
|
* @brief Sets or gets the filter ID and mask in @a id and @a mask depending on
|
|
* @a set of MSCAN module @a m. The filter is selected by the value of @a
|
|
* index.
|
|
*
|
|
* Returns true if the operation was successful.
|
|
*/
|
|
bool mscan_filter_operation(
|
|
volatile mscan *m,
|
|
bool set,
|
|
unsigned index,
|
|
uint32_t *id,
|
|
uint32_t *mask
|
|
)
|
|
{
|
|
unsigned number = mscan_filter_number( m);
|
|
unsigned offset = MSCAN_FILTER_NUMBER_MAX / number;
|
|
unsigned shift = 24;
|
|
|
|
volatile uint8_t *idar = NULL;
|
|
volatile uint8_t *idmr = NULL;
|
|
|
|
if (!set) {
|
|
*id = MSCAN_FILTER_ID_DEFAULT;
|
|
*mask = MSCAN_FILTER_MASK_DEFAULT;
|
|
}
|
|
|
|
if (index < number) {
|
|
mscan_context context;
|
|
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
index *= offset;
|
|
offset += index;
|
|
while (index < offset) {
|
|
idar = mscan_id_acceptance_register( m, index);
|
|
idmr = mscan_id_mask_register( m, index);
|
|
|
|
if (set) {
|
|
*idar = (uint8_t) (*id >> shift);
|
|
*idmr = (uint8_t) (*mask >> shift);
|
|
} else {
|
|
*id = (*id & ~(0xffU << shift)) | (*idar << shift);
|
|
*mask = (*mask & ~(0xffU << shift)) | (*idmr << shift);
|
|
}
|
|
|
|
shift -= 8;
|
|
|
|
++index;
|
|
}
|
|
|
|
mscan_initialization_mode_leave( m, &context);
|
|
} else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Returns the receiver and transmitter error counter values in @a rec
|
|
* and @a tec of MSCAN module @a m.
|
|
*/
|
|
void mscan_get_error_counters( volatile mscan *m, unsigned *rec, unsigned *tec)
|
|
{
|
|
mscan_context context;
|
|
|
|
mscan_initialization_mode_enter( m, &context);
|
|
|
|
*rec = m->rxerr;
|
|
*tec = m->txerr;
|
|
|
|
mscan_initialization_mode_leave( m, &context);
|
|
}
|