1320 lines
41 KiB
C
1320 lines
41 KiB
C
/**
|
|
* @file sdrv_lin.c
|
|
* @brief taishan lin driver source.
|
|
*
|
|
* @copyright Copyright (c) 2020 Semidrive Semiconductor.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <bits.h>
|
|
#include <debug.h>
|
|
#include <irq.h>
|
|
#include <string.h>
|
|
#include <types.h>
|
|
|
|
#include "sdrv_lin.h"
|
|
|
|
#include "common.h"
|
|
|
|
/***************************LIN Static Functions Start************************/
|
|
/**
|
|
* brief Sdrv lin module init.
|
|
*
|
|
* param[in] cfg lin config
|
|
* return STATUS_SUCCESS : Initializing lin success.
|
|
* STATUS_TIMEOUT : Reset lin module fail.
|
|
*/
|
|
static lin_ret_status_e sdrv_lin_init(const sdrv_lin_config_t *cfg);
|
|
|
|
/**
|
|
* brief Sdrv lin put char.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] data The data will be sent.
|
|
*/
|
|
static inline void sdrv_lin_putc(sdrv_uart_regs_t *base, uint8_t data);
|
|
|
|
/**
|
|
* brief Sdrv lin put char.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
*
|
|
* return The char get from rx fifo.
|
|
*/
|
|
static inline uint8_t sdrv_lin_getc(sdrv_uart_regs_t *base);
|
|
|
|
/**
|
|
* brief Sdrv lin calculate parity
|
|
*
|
|
* param[in] id frame id
|
|
*
|
|
* return The result of pid.
|
|
*/
|
|
static uint8_t sdrv_lin_calculate_parity(uint8_t id);
|
|
|
|
/**
|
|
* brief Sdrv lin clear tx fifo.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
*
|
|
* return The result of clearing tx fifo.
|
|
*/
|
|
static bool sdrv_lin_txfifo_clear(sdrv_uart_regs_t *base, uint32_t timeout);
|
|
|
|
/**
|
|
* brief Sdrv lin clear rx fifo.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
*
|
|
* return The result of clearing rx fifo.
|
|
*/
|
|
static bool sdrv_lin_rxfifo_clear(sdrv_uart_regs_t *base, uint32_t timeout);
|
|
|
|
/**
|
|
* brief Sdrv lin module reset.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of resetting lin module.
|
|
*/
|
|
static bool sdrv_lin_module_reset(sdrv_uart_regs_t *base, uint32_t timeout);
|
|
|
|
/**
|
|
* brief Waitting until sdrv lin tx io state changing to idle.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of Waitting.
|
|
*/
|
|
static bool sdrv_lin_wait_txio_idle(sdrv_uart_regs_t *base, uint32_t timeout);
|
|
|
|
/**
|
|
* brief Waitting until sdrv lin rx io state changing to idle.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of Waitting.
|
|
*/
|
|
static bool sdrv_lin_wait_rxio_idle(sdrv_uart_regs_t *base, uint32_t timeout);
|
|
|
|
/**
|
|
* brief Sdrv lin baudrate round-off.
|
|
*
|
|
* param [in] dividend The clock frequency.
|
|
* param [in] divisor The value of baud.
|
|
* return The result of the baudrate after rounded off.
|
|
*/
|
|
static uint32_t sdrv_lin_baudrate_roundoff(uint32_t dividend, uint32_t divisor);
|
|
/***************************LIN Static Functions End**************************/
|
|
/**
|
|
* @brief LIN controller init.
|
|
*
|
|
* This function initializes LIN controller and attach callback function.
|
|
* Callback fumction is optional. Please input NULL if user do not need
|
|
* callback function.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] cfg lin config
|
|
* @param[in] Callback callback function
|
|
* @param[in] userdata callback function user param
|
|
* @return STATUS_SUCCESS : Initializing lin success.
|
|
* STATUS_TIMEOUT : Reset lin module fail.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_controller_init(sdrv_lin_t *ctrl,
|
|
const sdrv_lin_config_t *cfg,
|
|
lin_callback_t callback,
|
|
void *userdata)
|
|
{
|
|
ASSERT(cfg != NULL);
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT((cfg->baud > 0U) || (1U == cfg->abr_en));
|
|
ASSERT(cfg->clk_freq > 0U);
|
|
ASSERT(cfg->base > 0U);
|
|
|
|
/* Initializes lin module. */
|
|
lin_ret_status_e ret = sdrv_lin_init(cfg);
|
|
if (STATUS_SUCCESS != ret) {
|
|
return ret;
|
|
}
|
|
|
|
/* Initializes the controller. */
|
|
(void)memset(ctrl, 0, sizeof(sdrv_lin_t));
|
|
ctrl->base = cfg->base;
|
|
ctrl->irq = cfg->irq;
|
|
ctrl->baud = cfg->baud;
|
|
ctrl->clk_freq = cfg->clk_freq;
|
|
ctrl->mode = cfg->mode;
|
|
ctrl->checksum = cfg->checksum;
|
|
|
|
/* set transfer callback function and user param */
|
|
ctrl->callback = callback;
|
|
ctrl->userdata = userdata;
|
|
|
|
#if CONFIG_MAINFUNCTION_ENABLE
|
|
/* Initializes lin mainfunction or irq. */
|
|
ctrl->module_mainfunction_state = STATUS_MAINFUNCTION_UNATTACHED;
|
|
if (cfg->mainfunction_polling) {
|
|
|
|
/* Attach mainfunction. */
|
|
ctrl->module_mainfunction_state =
|
|
mainfunction_attach(&(ctrl->module_node), cfg->period_ms,
|
|
sdrv_lin_mainfunction_handler, (void *)ctrl);
|
|
} else {
|
|
/* enable uart irq and attach handler */
|
|
if (ctrl->irq > 0U) {
|
|
irq_attach(ctrl->irq, sdrv_lin_irq_handler, ctrl);
|
|
irq_enable(ctrl->irq);
|
|
}
|
|
}
|
|
#else
|
|
/* enable uart irq and attach handler */
|
|
if (ctrl->irq > 0U) {
|
|
irq_attach(ctrl->irq, sdrv_lin_irq_handler, ctrl);
|
|
irq_enable(ctrl->irq);
|
|
} else {
|
|
ret = STATUS_INVALID_PARAM;
|
|
}
|
|
#endif
|
|
|
|
if (cfg->abr_en) {
|
|
ctrl->abr_state = SDRV_LIN_STATE_ABR_BUSY;
|
|
} else {
|
|
ctrl->abr_state = SDRV_LIN_STATE_ABR_READY;
|
|
}
|
|
|
|
ctrl->channel_sta = LIN_CH_SLEEP_STATE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN deinit.
|
|
*
|
|
* This function disable the lin module, and change the state of lin
|
|
* channel.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
*/
|
|
void sdrv_lin_deinit(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
/* Make sure pre operation was complete */
|
|
sdrv_lin_wait_txio_idle(lin_base, SDRV_UART_OPERATION_TIMEOUT);
|
|
sdrv_lin_wait_rxio_idle(lin_base, SDRV_UART_OPERATION_TIMEOUT);
|
|
|
|
/* Disable lin module */
|
|
lin_base->PCR0.TXEN = 0U;
|
|
lin_base->PCR0.RXEN = 0U;
|
|
lin_base->MCR0.MODEN = 0U;
|
|
#if CONFIG_MAINFUNCTION_ENABLE
|
|
/* Deinitializes lin mainfunction or irq. */
|
|
if (NULL != ctrl->module_node) {
|
|
|
|
/* Detach mainfunction service. */
|
|
mainfunction_detach(ctrl->module_node);
|
|
ctrl->module_node = NULL;
|
|
ctrl->module_mainfunction_state = STATUS_MAINFUNCTION_UNATTACHED;
|
|
|
|
} else {
|
|
/* Disable uart irq and dettach handler */
|
|
if (ctrl->irq > 0U) {
|
|
irq_disable(ctrl->irq);
|
|
irq_detach(ctrl->irq);
|
|
}
|
|
}
|
|
#else
|
|
/* Disable uart irq and dettach handler */
|
|
if (ctrl->irq > 0U) {
|
|
irq_disable(ctrl->irq);
|
|
irq_detach(ctrl->irq);
|
|
}
|
|
#endif
|
|
/* Deinitializes the controller. */
|
|
(void)memset(ctrl, 0, sizeof(sdrv_lin_t));
|
|
}
|
|
|
|
/**
|
|
* @brief Configure lin baud rate.
|
|
*
|
|
*This function configures the lin module baud rate.
|
|
*This function is used to update the lin module baud rate.
|
|
*after the lin module is initialized by the sdrv_lin_init.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @param [in] baud baud
|
|
* @return STATUS_SUCCESS : Setting lin baudrate success.
|
|
* STATUS_INVALID_PARAM : The param is unvalid.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_set_baudrate(sdrv_lin_t *ctrl, uint32_t baud)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
/* If baud is invalid. */
|
|
if (baud <= 0U) {
|
|
return STATUS_INVALID_PARAM;
|
|
}
|
|
/* If the lin controller is uninitial. */
|
|
if (LIN_CH_UNINITIAL_STATE == ctrl->channel_sta) {
|
|
return STATUS_UNINIT;
|
|
}
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
ctrl->baud = baud;
|
|
lin_base->PCR1.BAUDRATE = sdrv_lin_baudrate_roundoff(ctrl->clk_freq, baud);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief get lin current id.
|
|
*
|
|
*This function get the id of current transfer.
|
|
*This function is only used in salve mode.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @return The id of current transfer.
|
|
*/
|
|
uint8_t sdrv_lin_get_id(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
ctrl->current_id = (uint8_t)(lin_base->PSR4 & SDRV_LIN_PSR4_LINPID_ID_MASK);
|
|
|
|
return ctrl->current_id;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN transmit frame.
|
|
*
|
|
* This function transmit frame with a blocking method.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] lin_pdu the info of transmit param, include
|
|
* id: the id of current transfer;
|
|
* checksum: the checksum of current transfer;
|
|
* size: the data size of current transfer;
|
|
* data: the data buffer of current transfer.
|
|
* @param[in] times timeout count
|
|
* @return STATUS_SUCCESS : Triger lin transmit process success.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
* STATUS_SLEEP : The lin entity is sleepy.
|
|
* STATUS_BUSY : The lin bus is busy.
|
|
* STATUS_FAIL : The size of frame is bigger than 8.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_transmitframe(sdrv_lin_t *ctrl,
|
|
sdrv_lin_pdu_t *lin_pdu, uint32_t times)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
ASSERT(lin_pdu != NULL);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
if ((ctrl->channel_sta == LIN_CH_UNINITIAL_STATE)) {
|
|
return STATUS_UNINIT;
|
|
} else if ((ctrl->channel_sta == LIN_CH_SLEEP_STATE)) {
|
|
return STATUS_SLEEP;
|
|
} else if ((ctrl->tx_state != SDRV_LIN_STATE_IDLE) ||
|
|
(ctrl->rx_state != SDRV_LIN_STATE_IDLE)) {
|
|
return STATUS_BUSY;
|
|
} else if (lin_pdu->size > 8U) {
|
|
return STATUS_FAIL;
|
|
} else {
|
|
ctrl->tx_state = SDRV_LIN_STATE_TX;
|
|
|
|
/* Clear tx fifo. */
|
|
if (!sdrv_lin_txfifo_clear(lin_base, SDRV_LIN_OPERATION_TIMEOUT)) {
|
|
ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
return STATUS_FAIL;
|
|
}
|
|
/* Initialize the transfer. */
|
|
ctrl->tx_buff = lin_pdu->data;
|
|
ctrl->tx_size = lin_pdu->size;
|
|
ctrl->tx_remain = ctrl->tx_size;
|
|
|
|
/* Check checksum type.
|
|
* Set checksum type and pid in register, if current mode is master.
|
|
*/
|
|
if (ctrl->mode == LIN_MASTER) {
|
|
ctrl->current_id = lin_pdu->id;
|
|
ctrl->checksum = lin_pdu->checksum;
|
|
lin_base->CMDCSR1.PID = sdrv_lin_calculate_parity(ctrl->current_id);
|
|
} else if (ctrl->mode == LIN_SLAVE) {
|
|
if ((LIN_SLEEP_ID != ctrl->current_id) &&
|
|
(LIN_WAKEUP_ID != ctrl->current_id)) {
|
|
if (ctrl->checksum != lin_pdu->checksum) {
|
|
ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
return STATUS_FAIL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Set the registers of transfer. */
|
|
lin_base->CMDCSR1.TXBYTENUM = ctrl->tx_size;
|
|
lin_base->CMDCSR1.RXBYTENUM = 0U;
|
|
lin_base->PCR6.CHKSUMTYP = ctrl->checksum;
|
|
|
|
/* Write data to tx fifo. */
|
|
for (size_t i = 0U; i < ctrl->tx_size; i++) {
|
|
while (lin_base->FSR0.FULL) {
|
|
if (0U == times) {
|
|
ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
return STATUS_TIMEOUT;
|
|
}
|
|
--times;
|
|
}
|
|
sdrv_lin_putc(lin_base, ctrl->tx_buff[i]);
|
|
--ctrl->tx_remain;
|
|
}
|
|
|
|
/* Triger the transfer. */
|
|
lin_base->CMDCSR0.DOORBELL = SDRV_DOORBELL_TRIGGER;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get LIN transmit status.
|
|
*
|
|
* This function get status of transmit frame.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] bytes_remaining the number of bytes that still needed to
|
|
* transmit
|
|
* @return STATUS_SUCCESS : Lin transmit the frame success.
|
|
* STATUS_BUSY : Lin transmit the frame not finished.
|
|
* STATUS_FAIL : Lin transmit the frame fail.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_transmit_status_get(sdrv_lin_t *ctrl,
|
|
uint8_t *bytes_remaining)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
lin_ret_status_e ret_state = STATUS_FAIL;
|
|
|
|
if (bytes_remaining != NULL) {
|
|
*bytes_remaining = ctrl->tx_remain;
|
|
}
|
|
|
|
if (ctrl->tx_state == SDRV_LIN_STATE_IDLE) {
|
|
ret_state = STATUS_SUCCESS;
|
|
} else if (ctrl->tx_state == SDRV_LIN_STATE_TX) {
|
|
ret_state = STATUS_BUSY;
|
|
} else {
|
|
ret_state = STATUS_FAIL;
|
|
}
|
|
|
|
return ret_state;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN get rx fifo data.
|
|
*
|
|
* This function receive frame with a blocking method.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] data the first address of the source buff to received
|
|
* @param[in] size the size of bytes to receive
|
|
* @param[in] times the max count of get fifo data
|
|
* @return STATUS_SUCCESS : Get data from rx fifo success.
|
|
* STATUS_TIMEOUT : Lin rx fifo is empty until timeout.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_get_fifodata(sdrv_lin_t *ctrl, uint8_t *data,
|
|
size_t size, uint32_t times)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
/* Get data from rx fifo. */
|
|
for (size_t i = 0U; i < size; i++) {
|
|
while (lin_base->FSR1.EMPTY) {
|
|
if (0U == times) {
|
|
return STATUS_TIMEOUT;
|
|
}
|
|
--times;
|
|
}
|
|
data[i] = sdrv_lin_getc(lin_base);
|
|
--ctrl->rx_remain;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN receive frame.
|
|
*
|
|
* This function receive frame with a blocking method.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] lin_pdu the info of receive param, include
|
|
* id: the id of current transfer;
|
|
* checksum: the checksum of current transfer;
|
|
* size: the data size of current transfer;
|
|
* data: the data buffer of current transfer.
|
|
* @return STATUS_SUCCESS : Triger lin receive process success.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
* STATUS_SLEEP : The lin entity is sleepy.
|
|
* STATUS_BUSY : The lin bus is busy.
|
|
* STATUS_FAIL : The size of frame is bigger than 8.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_receiveframe(sdrv_lin_t *ctrl,
|
|
sdrv_lin_pdu_t *lin_pdu)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
if ((ctrl->channel_sta == LIN_CH_UNINITIAL_STATE)) {
|
|
return STATUS_UNINIT;
|
|
} else if ((ctrl->channel_sta == LIN_CH_SLEEP_STATE)) {
|
|
return STATUS_SLEEP;
|
|
} else if ((ctrl->tx_state != SDRV_LIN_STATE_IDLE) ||
|
|
(ctrl->rx_state != SDRV_LIN_STATE_IDLE)) {
|
|
return STATUS_BUSY;
|
|
} else if (lin_pdu->size > 8U) {
|
|
return STATUS_FAIL;
|
|
} else {
|
|
ctrl->rx_state = SDRV_LIN_STATE_RX;
|
|
|
|
/* Initialize the transfer. */
|
|
ctrl->rx_buff = lin_pdu->data;
|
|
ctrl->rx_size = lin_pdu->size;
|
|
ctrl->rx_remain = ctrl->rx_size;
|
|
/* Check checksum type.
|
|
* Set checksum type and pid in register, if current mode is master.
|
|
*/
|
|
if (ctrl->mode == LIN_MASTER) {
|
|
ctrl->current_id = lin_pdu->id;
|
|
ctrl->checksum = lin_pdu->checksum;
|
|
lin_base->CMDCSR1.PID = sdrv_lin_calculate_parity(ctrl->current_id);
|
|
} else if (ctrl->mode == LIN_SLAVE) {
|
|
if ((LIN_SLEEP_ID != ctrl->current_id) &&
|
|
(LIN_WAKEUP_ID != ctrl->current_id)) {
|
|
if (ctrl->checksum != lin_pdu->checksum) {
|
|
ctrl->rx_state = SDRV_LIN_STATE_IDLE;
|
|
return STATUS_FAIL;
|
|
}
|
|
}
|
|
}
|
|
/* Set the registers of transfer. */
|
|
lin_base->CMDCSR1.TXBYTENUM = 0U;
|
|
lin_base->CMDCSR1.RXBYTENUM = ctrl->rx_size;
|
|
lin_base->PCR6.CHKSUMTYP = ctrl->checksum;
|
|
/* Triger the transfer. */
|
|
lin_base->CMDCSR0.DOORBELL = SDRV_DOORBELL_TRIGGER;
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get LIN async receive status.
|
|
*
|
|
* This function receive frame with a interrupt method.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @param[in] bytes_remaining the number of bytes that still needed to
|
|
* receive
|
|
* @return STATUS_SUCCESS : Lin receive the frame success.
|
|
* STATUS_BUSY : Lin receive the frame not finished.
|
|
* STATUS_FAIL : Lin receive the frame fail.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_receive_status_get(sdrv_lin_t *ctrl,
|
|
uint8_t *bytes_remaining)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
lin_ret_status_e ret_state = STATUS_FAIL;
|
|
|
|
if (bytes_remaining != NULL) {
|
|
*bytes_remaining = ctrl->rx_remain;
|
|
}
|
|
if (ctrl->rx_state == SDRV_LIN_STATE_IDLE) {
|
|
ret_state = STATUS_SUCCESS;
|
|
} else if (ctrl->rx_state == SDRV_LIN_STATE_RX) {
|
|
ret_state = STATUS_BUSY;
|
|
} else {
|
|
ret_state = STATUS_FAIL;
|
|
}
|
|
return ret_state;
|
|
}
|
|
|
|
/**
|
|
* @brief Get LIN tranfer status .
|
|
*
|
|
* This function check the state of transfer.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return SDRV_LIN_STATE_IDLE : The lin bus is idle.
|
|
* SDRV_LIN_STATE_BUSY : The lin bus is busy.
|
|
*/
|
|
lin_sub_status_e sdrv_lin_get_transfer_status(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
if ((ctrl->rx_state == SDRV_LIN_STATE_IDLE) &&
|
|
(ctrl->tx_state == SDRV_LIN_STATE_IDLE)) {
|
|
return SDRV_LIN_STATE_IDLE;
|
|
}
|
|
return SDRV_LIN_STATE_BUSY;
|
|
}
|
|
|
|
/**
|
|
* @brief Clear LIN tranfer status .
|
|
*
|
|
* This function clear the state of transfer.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
*/
|
|
void sdrv_lin_clear_transfer_status(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
/* Reset module, clear register status. */
|
|
sdrv_lin_module_reset(lin_base, SDRV_LIN_OPERATION_TIMEOUT);
|
|
/* Clear tx/rx fifo. */
|
|
sdrv_lin_txfifo_clear(lin_base, SDRV_LIN_OPERATION_TIMEOUT);
|
|
sdrv_lin_rxfifo_clear(lin_base, SDRV_LIN_OPERATION_TIMEOUT);
|
|
|
|
ctrl->rx_state = SDRV_LIN_STATE_IDLE;
|
|
ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get LIN node status .
|
|
*
|
|
* This function check the state of node
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return The state of lin node
|
|
*/
|
|
lin_ch_status_e sdrv_lin_get_status(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
|
|
return ctrl->channel_sta;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN require slave node goto sleep.
|
|
*
|
|
* This function send frame require slave node goto sleep.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return STATUS_SUCCESS : Send sleep frame to slave node success.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
* STATUS_SLEEP : The lin entity is sleepy.
|
|
* STATUS_BUSY : The lin bus is busy.
|
|
* STATUS_FAIL : The size of frame is bigger than 8.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_goto_sleep(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(NULL != ctrl);
|
|
|
|
uint8_t sleep_frame[] = {0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
|
sdrv_lin_pdu_t lin_pdu = {
|
|
.id = LIN_SLEEP_ID,
|
|
.checksum = LIN_CLASSIC_CS,
|
|
.size = sizeof(sleep_frame),
|
|
.data = sleep_frame,
|
|
};
|
|
|
|
if (LIN_CH_UNINITIAL_STATE == ctrl->channel_sta) {
|
|
return STATUS_UNINIT;
|
|
}
|
|
if ((ctrl->rx_state != SDRV_LIN_STATE_IDLE) ||
|
|
(ctrl->tx_state != SDRV_LIN_STATE_IDLE)) {
|
|
return STATUS_BUSY;
|
|
}
|
|
|
|
ctrl->channel_sta = LIN_CH_OPERATIONAL_STATE;
|
|
/* Transmit sleep frame to slave node. */
|
|
lin_ret_status_e transmit_ret =
|
|
sdrv_lin_transmitframe(ctrl, &lin_pdu, NoWait);
|
|
if (STATUS_SUCCESS == transmit_ret) {
|
|
/* Change self channel state to sleep. */
|
|
ctrl->channel_sta = LIN_CH_SLEEP_STATE;
|
|
}
|
|
return transmit_ret;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN require this node goto sleep.
|
|
*
|
|
* This function send frame require this node goto sleep.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return STATUS_SUCCESS : Set it's own state to LIN_CH_SLEEP_STATE
|
|
* success. STATUS_UNINIT : The lin entity is uninitialized.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_goto_sleep_internal(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
|
|
if (LIN_CH_UNINITIAL_STATE == ctrl->channel_sta) {
|
|
return STATUS_UNINIT;
|
|
}
|
|
/* Change self channel state to sleep. */
|
|
ctrl->channel_sta = LIN_CH_SLEEP_STATE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN remote node wakeup.
|
|
*
|
|
* This function wakup the remote node.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return STATUS_SUCCESS : Send wakeup frame to slave node success.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
* STATUS_SLEEP : The lin entity is sleepy.
|
|
* STATUS_BUSY : The lin bus is busy.
|
|
* STATUS_FAIL : The size of frame is bigger than 8.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_wakeup(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
|
|
sdrv_lin_pdu_t lin_pdu = {
|
|
.id = LIN_WAKEUP_ID,
|
|
.checksum = LIN_CLASSIC_CS,
|
|
.size = 0U,
|
|
.data = NULL,
|
|
};
|
|
|
|
if (LIN_CH_UNINITIAL_STATE == ctrl->channel_sta) {
|
|
return STATUS_UNINIT;
|
|
}
|
|
if ((ctrl->rx_state != SDRV_LIN_STATE_IDLE) ||
|
|
(ctrl->tx_state != SDRV_LIN_STATE_IDLE)) {
|
|
return STATUS_BUSY;
|
|
}
|
|
|
|
/* Change self channel state to operational. */
|
|
ctrl->channel_sta = LIN_CH_OPERATIONAL_STATE;
|
|
|
|
/* Transmit wakeup frame to slave node. */
|
|
return sdrv_lin_transmitframe(ctrl, &lin_pdu, NoWait);
|
|
}
|
|
|
|
/**
|
|
* @brief LIN node wakeup.
|
|
*
|
|
* This function wakup this node.
|
|
*
|
|
* @param[in] ctrl lin controller
|
|
* @return STATUS_SUCCESS : Set it's own state to LIN_CH_OPERATIONAL_STATE
|
|
* success.
|
|
* STATUS_UNINIT : The lin entity is uninitialized.
|
|
*/
|
|
lin_ret_status_e sdrv_lin_wakeup_internal(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
|
|
if (LIN_CH_UNINITIAL_STATE == ctrl->channel_sta) {
|
|
return STATUS_UNINIT;
|
|
}
|
|
/* Change self channel state to operational. */
|
|
ctrl->channel_sta = LIN_CH_OPERATIONAL_STATE;
|
|
/* Reset transfer status. */
|
|
ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
ctrl->rx_state = SDRV_LIN_STATE_IDLE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Enable/Disable Sdrv LIN Interrupt.
|
|
*
|
|
* This function enable/disable sdrv lin interrupt with the provided mask.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @param [in] mask the interrupts to enable
|
|
* @param [in] value enable or disable
|
|
*/
|
|
void sdrv_lin_intr_ctrl(sdrv_lin_t *ctrl, uint32_t mask, bool value)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
uint32_t intr_ctrl = 0U;
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
intr_ctrl = lin_base->INTEN0.v;
|
|
intr_ctrl = value ? (intr_ctrl | mask) : (intr_ctrl & (~mask));
|
|
|
|
lin_base->INTEN0.v = intr_ctrl;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Sdrv LIN Interrupt enable/disable status.
|
|
*
|
|
* This function get sdrv lin interrupt enable/disable status with the
|
|
* provided mask.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @return Sdrv lin Interrupt enable/disable status
|
|
*/
|
|
uint32_t sdrv_lin_intr_get(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
return lin_base->INTEN0.v;
|
|
}
|
|
|
|
/**
|
|
* @brief Get Sdrv lin Interrupt flag status.
|
|
*
|
|
* This function get Sdrv lin Interrupt flag status.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @return all sdrv lin Interrupt status
|
|
*/
|
|
uint32_t sdrv_lin_intr_status_get(sdrv_lin_t *ctrl)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
return lin_base->INTR0.v;
|
|
}
|
|
|
|
/**
|
|
* @brief Clear Sdrv lin Interrupt flag status.
|
|
*
|
|
* This function clear Sdrv lin Interrupt flag status with the provided
|
|
* mask.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
* @param [in] mask the interrupts status to clear
|
|
*/
|
|
void sdrv_lin_intr_status_clear(sdrv_lin_t *ctrl, uint32_t mask)
|
|
{
|
|
ASSERT(ctrl != NULL);
|
|
ASSERT(ctrl->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(ctrl->base);
|
|
|
|
lin_base->INTR0.v = mask;
|
|
}
|
|
|
|
/**
|
|
* @brief Sdrv LIN default IRQ handle function.
|
|
*
|
|
* This function handles the sdrv lin transmit and receive
|
|
* and error IRQ request.
|
|
*
|
|
* @param [in] irq the irq number of LINx
|
|
* @param [in] ctrl sdrv lin controller
|
|
*/
|
|
int sdrv_lin_irq_handler(uint32_t irq, void *ctrl)
|
|
{
|
|
ASSERT(NULL != ctrl);
|
|
|
|
sdrv_lin_t *irq_ctrl = (sdrv_lin_t *)ctrl;
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)irq_ctrl->base;
|
|
uint32_t state = 0U;
|
|
/* Get lin interrupt status. */
|
|
state = sdrv_lin_intr_status_get(irq_ctrl);
|
|
sdrv_lin_intr_status_clear(irq_ctrl, state);
|
|
|
|
/* If error occurred while writing fifo and frame transfers.
|
|
* The transfer will cancelled when those errors occurred.
|
|
*/
|
|
if (((state & SDRV_LIN_INTR_BAUDRATEERR) != 0U) &&
|
|
(SDRV_LIN_INTR_BAUDRATEERR & sdrv_uart_intr_get(ctrl))) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_BAUDRATE_ERR;
|
|
/* Cancel the transfer. */
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (false == irq_ctrl->rx_error_invalid) {
|
|
/* Notifying user. */
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
} else if ((state & SDEV_LIN_INTR_FRAME_FIFO_ERR) != 0U) {
|
|
if ((state & SDRV_LIN_INTR_FRAMEERR) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_FRAME_ERR;
|
|
} else if ((state & SDRV_LIN_INTR_PARITYERR) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_PARITY_ERR;
|
|
} else if ((state & SDRV_LIN_INTR_NOISERERR) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_NOISE_ERR;
|
|
} else if ((state & SDRV_LIN_INTR_STARTBITERR) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_STARTBIT_ERR;
|
|
} else if ((state & SDRV_UART_INTR_TX_FOVF) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_TX_TXFOVF_ERR;
|
|
} else if ((state & SDRV_UART_INTR_RX_FOVF) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_RXFOVF_ERR;
|
|
}
|
|
/* Cancel the transfer. */
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (false == irq_ctrl->rx_error_invalid) {
|
|
/* Notifying user. */
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ABRPASS interrupt indicate the result of abr is successful.
|
|
* ABRFAIL interrupt indicate the result of abr is failed.
|
|
*/
|
|
if ((state & SDRV_LIN_INTR_ABR) != 0U) {
|
|
if ((state & SDRV_LIN_INTR_ABRPASS) != 0U) {
|
|
/* PSR1.AUTOBAUDRATE can not be zero. */
|
|
if (0U != lin_base->PSR1.AUTOBAUDRATE) {
|
|
/* Set baudrate. */
|
|
irq_ctrl->baud = sdrv_lin_baudrate_roundoff(
|
|
irq_ctrl->clk_freq, lin_base->PSR1.AUTOBAUDRATE);
|
|
#if CONFIG_E3L
|
|
if (IS_P0) {
|
|
#endif
|
|
/* disable abr pass interrupt */
|
|
lin_base->INTEN0.ABRPASS = 0U;
|
|
lin_base->INTEN0.ABRFAIL = 0U;
|
|
/* stop auto baudrate detect */
|
|
lin_base->PCR0.ABREN = 0U;
|
|
/* Updata baudrate. */
|
|
sdrv_lin_set_baudrate(irq_ctrl, irq_ctrl->baud);
|
|
/* Cancel the transfer. */
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
irq_ctrl->rx_error_invalid = true;
|
|
#if CONFIG_E3L
|
|
}
|
|
#endif
|
|
irq_ctrl->current_event = SDRV_LIN_ABR_PASS;
|
|
irq_ctrl->abr_state = SDRV_LIN_STATE_ABR_READY;
|
|
/* Notifying user. */
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
} else {
|
|
/* Cancel the transfer. */
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
irq_ctrl->abr_state = SDRV_LIN_STATE_ABR_FAIL;
|
|
irq_ctrl->current_event = SDRV_LIN_ABR_FAIL;
|
|
/* Notifying user. */
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
} else if ((state & SDRV_LIN_INTR_ABRFAIL) != 0U) {
|
|
irq_ctrl->abr_state = SDRV_LIN_STATE_ABR_FAIL;
|
|
irq_ctrl->current_event = SDRV_LIN_ABR_FAIL;
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
/* Notifying user. */
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* APBCMDDONE interrupt indicates that the transmission is completed.
|
|
* APBCMDABORT interrupt indicates that the transmission failed, when
|
|
* lin node in tx state.
|
|
*/
|
|
if ((irq_ctrl->abr_state == SDRV_LIN_STATE_ABR_READY) &&
|
|
(irq_ctrl->tx_state == SDRV_LIN_STATE_TX)) {
|
|
if ((state & SDRV_LIN_INTR_APBCMDDONE) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_TX_COMPLETE;
|
|
irq_ctrl->tx_state = SDRV_LIN_STATE_IDLE;
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
} else if ((state & SDRV_LIN_INTR_APBCMDABORT) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_TX_TIMEOUT;
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* RXCHKSUMPASS interrupt indicates that the reception is over.
|
|
* RXCHKSUMERR interrupt indicates that the reception occurs checksum
|
|
* error.
|
|
*/
|
|
if ((irq_ctrl->abr_state == SDRV_LIN_STATE_ABR_READY) &&
|
|
(irq_ctrl->rx_state == SDRV_LIN_STATE_RX)) {
|
|
if ((state & SDRV_LIN_INTR_RXCHKSUMPASS) != 0U) {
|
|
if (STATUS_SUCCESS ==
|
|
sdrv_lin_get_fifodata(irq_ctrl, irq_ctrl->rx_buff,
|
|
irq_ctrl->rx_size, NoWait)) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_COMPLETE;
|
|
irq_ctrl->rx_state = SDRV_LIN_STATE_IDLE;
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
} else {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_ERROR;
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
} else if ((state & SDRV_LIN_INTR_RXCHKSUMERR) != 0U) {
|
|
sdrv_lin_get_fifodata(irq_ctrl, irq_ctrl->rx_buff,
|
|
irq_ctrl->rx_size, NoWait);
|
|
irq_ctrl->current_event = SDRV_LIN_RX_CHECKSUM_ERR;
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
} else if ((state & SDRV_LIN_INTR_APBCMDABORT) != 0U) {
|
|
irq_ctrl->current_event = SDRV_LIN_RX_TIMEOUT;
|
|
sdrv_lin_clear_transfer_status(irq_ctrl);
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Slave node need recevie frame header from master node.
|
|
* PIDPASS interrupt indicates slave received frame header from
|
|
* master, and check pid pass. PIDERR interrupt indicates slave
|
|
* received frame header from master, but check pid fail.
|
|
*/
|
|
if ((irq_ctrl->mode == LIN_SLAVE) &&
|
|
(irq_ctrl->abr_state == SDRV_LIN_STATE_ABR_READY)) {
|
|
if ((state & SDRV_LIN_INTR_PIDPASS) != 0U) {
|
|
irq_ctrl->rx_error_invalid = false;
|
|
sdrv_lin_get_id(irq_ctrl);
|
|
irq_ctrl->current_event = SDRV_LIN_RX_PIDPASS;
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
if ((state & SDRV_LIN_INTR_PIDERR) != 0U) {
|
|
irq_ctrl->rx_error_invalid = false;
|
|
irq_ctrl->current_event = SDRV_LIN_RX_PIDERR;
|
|
if (irq_ctrl->callback != NULL) {
|
|
irq_ctrl->callback(irq_ctrl, irq_ctrl->current_event,
|
|
irq_ctrl->userdata);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if CONFIG_MAINFUNCTION_ENABLE
|
|
/**
|
|
* @brief Sdrv LIN MainFunction callback function.
|
|
*
|
|
* This function handles the transmit/receive/error IRQ of lin module.
|
|
*
|
|
* @param [in] ctrl sdrv lin controller
|
|
*/
|
|
void sdrv_lin_mainfunction_handler(void *ctrl)
|
|
{
|
|
ASSERT(NULL != ctrl);
|
|
|
|
sdrv_lin_irq_handler(0U, ctrl);
|
|
}
|
|
#endif
|
|
|
|
/*************************LIN Static Functions Start**********************/
|
|
/**
|
|
* brief Sdrv lin module init.
|
|
*
|
|
* param[in] cfg lin config
|
|
* return STATUS_SUCCESS : Initializing lin success.
|
|
* STATUS_TIMEOUT : Reset lin module fail.
|
|
*/
|
|
static lin_ret_status_e sdrv_lin_init(const sdrv_lin_config_t *cfg)
|
|
{
|
|
ASSERT(cfg != NULL);
|
|
ASSERT(cfg->base > 0U);
|
|
|
|
sdrv_uart_regs_t *lin_base = (sdrv_uart_regs_t *)(cfg->base);
|
|
|
|
/* initializes sdrv uart module */
|
|
/* disable uart module */
|
|
lin_base->PCR0.TXEN = 0U;
|
|
lin_base->PCR0.RXEN = 0U;
|
|
lin_base->MCR0.MODEN = 0U;
|
|
|
|
/* Set lin mode, lin master or salve. */
|
|
lin_base->MCR0.OPMOD = cfg->mode;
|
|
/* Set transmission mode. */
|
|
lin_base->PCR0.TRANSFERMODE = SDRV_HALF_DUPLEX;
|
|
/* Set software flow control. */
|
|
lin_base->PCR0.FCM = SDRV_SOFTWARE_FC;
|
|
/* Set 8 bit data. */
|
|
lin_base->PCR0.DATABIT = SDRV_CHAR_8_BITS;
|
|
/* Set 1 bit stop. */
|
|
lin_base->PCR0.STOPBIT = SDRV_STOP_1_BIT;
|
|
/* Set no parity mode. */
|
|
lin_base->PCR0.PARITYBIT = SDRV_NO_PARITY;
|
|
|
|
/* Clear interrupt and interrupt status registers. */
|
|
lin_base->INTEN0.v = 0U;
|
|
lin_base->INTR0.v = 0xFFFFFFFFU;
|
|
|
|
/* Clear TX/RX fifo. */
|
|
sdrv_lin_txfifo_clear(lin_base, SDRV_LIN_OPERATION_TIMEOUT);
|
|
sdrv_lin_rxfifo_clear(lin_base, SDRV_LIN_OPERATION_TIMEOUT);
|
|
|
|
/* set rx noise filter */
|
|
lin_base->PCR8.RXFILTCTL = 3U;
|
|
lin_base->PCR8.RXSYNCEN = 1U;
|
|
|
|
/* Set rxbreak configuration. */
|
|
lin_base->PCR4.TXBREAKCTL = SDRV_LIN_TXBREAK_BIT(SDRV_LIN_TXBREAK_LEN) + 1U;
|
|
if (LIN_MASTER == cfg->mode) {
|
|
#if CONFIG_E3L
|
|
if (IS_P1) {
|
|
lin_base->PCR1.RXBREAKCTL =
|
|
SDRV_LIN_RXBREAK_BIT(SDRV_LIN_RXBREAK_LEN);
|
|
} else {
|
|
lin_base->PCR1.RXBREAKCTL = 0U;
|
|
}
|
|
#else
|
|
lin_base->PCR1.RXBREAKCTL = 0U;
|
|
#endif
|
|
} else {
|
|
lin_base->PCR1.RXBREAKCTL = SDRV_LIN_RXBREAK_BIT(SDRV_LIN_RXBREAK_LEN);
|
|
}
|
|
|
|
/* Set hardware timeout cycle value. */
|
|
if (cfg->transfer_timeout) {
|
|
lin_base->PCR7.PCMDTO = cfg->timeout_value & SDRV_LIN_PCR7_PCMDTO_MASK;
|
|
}
|
|
|
|
/* Enable related interrupt. */
|
|
lin_base->INTEN0.PARITYERR = 1U;
|
|
lin_base->INTEN0.FRAMEERR = 1U;
|
|
lin_base->INTEN0.BAUDRATEERR = 1U;
|
|
lin_base->INTEN0.NOISEERR = 1U;
|
|
lin_base->INTEN0.APBCMDDONE = 1U;
|
|
lin_base->INTEN0.APBCMDABORT = 1U;
|
|
lin_base->INTEN0.RXCHKSUMERR = 1U;
|
|
lin_base->INTEN0.RXCHKSUMPASS = 1U;
|
|
lin_base->INTEN0.RXPIDERR = 1U;
|
|
lin_base->INTEN0.RXPIDPASS = 1U;
|
|
|
|
#if CONFIG_E3L
|
|
if (IS_P1) {
|
|
lin_base->INTEN0.BAUDRATEERR = 0U;
|
|
}
|
|
#endif
|
|
|
|
/* Set checksum type. */
|
|
lin_base->PCR6.CHKSUMTYP = cfg->checksum;
|
|
|
|
/* Enable RX/TX. */
|
|
lin_base->PCR0.TXEN = 1U;
|
|
lin_base->PCR0.RXEN = 1U;
|
|
|
|
/* Reset module, clear register status. */
|
|
if (!sdrv_lin_module_reset(lin_base, SDRV_LIN_OPERATION_TIMEOUT)) {
|
|
return STATUS_TIMEOUT;
|
|
}
|
|
|
|
/* Enable Lin module. */
|
|
lin_base->MCR0.MODEN = 1U;
|
|
|
|
/* Auto baudrate configuration. */
|
|
if (1U == cfg->abr_en) {
|
|
lin_base->PCR0.ABRCTL0 = ABR_PATTERN;
|
|
lin_base->PCR0.ABRCTL1 = cfg->match_num;
|
|
lin_base->PCR1.BAUDRATE = 0U;
|
|
|
|
lin_base->INTEN0.ABRPASS = 1U;
|
|
lin_base->INTEN0.ABRFAIL = 1U;
|
|
|
|
lin_base->PCR0.ABREN = 1U;
|
|
} else {
|
|
lin_base->INTEN0.ABRPASS = 0U;
|
|
lin_base->INTEN0.ABRFAIL = 0U;
|
|
|
|
lin_base->PCR0.ABREN = 0U;
|
|
lin_base->PCR1.BAUDRATE =
|
|
sdrv_lin_baudrate_roundoff(cfg->clk_freq, cfg->baud);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin put char.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] data The data will be sent.
|
|
*/
|
|
static inline void sdrv_lin_putc(sdrv_uart_regs_t *base, uint8_t data)
|
|
{
|
|
base->TXDR = data;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin put char.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* return The char get from rx fifo.
|
|
*/
|
|
static inline uint8_t sdrv_lin_getc(sdrv_uart_regs_t *base)
|
|
{
|
|
return base->RXDR;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin calculate parity
|
|
*
|
|
* param[in] id frame id
|
|
* return The result of pid.
|
|
*/
|
|
static uint8_t sdrv_lin_calculate_parity(uint8_t id)
|
|
{
|
|
uint8_t p0 =
|
|
BIT_SET(id, 0U) ^ BIT_SET(id, 1U) ^ BIT_SET(id, 2U) ^ BIT_SET(id, 4U);
|
|
uint8_t p1 = !(BIT_SET(id, 1U) ^ BIT_SET(id, 3U) ^ BIT_SET(id, 4U) ^
|
|
BIT_SET(id, 5U));
|
|
|
|
return (id & BIT_MASK(6U)) | (p0 << 6U) | (p1 << 7U);
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin clear rx fifo.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of clearing rx fifo.
|
|
*/
|
|
static bool sdrv_lin_rxfifo_clear(sdrv_uart_regs_t *base, uint32_t timeout)
|
|
{
|
|
/* clear RX Fifo */
|
|
base->FCR1.CLR = 1U;
|
|
while (base->FCR1.CLR) {
|
|
if (0U == timeout) {
|
|
return false;
|
|
}
|
|
--timeout;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin clear tx fifo.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of clearing tx fifo.
|
|
*/
|
|
static bool sdrv_lin_txfifo_clear(sdrv_uart_regs_t *base, uint32_t timeout)
|
|
{
|
|
/* clear RX Fifo */
|
|
base->FCR0.CLR = 1U;
|
|
while (base->FCR0.CLR) {
|
|
if (0U == timeout) {
|
|
return false;
|
|
}
|
|
--timeout;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin module reset.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of resetting lin module.
|
|
*/
|
|
static bool sdrv_lin_module_reset(sdrv_uart_regs_t *base, uint32_t timeout)
|
|
{
|
|
/* clear RX Fifo */
|
|
base->MCR0.MODRST = 1U;
|
|
while (base->MCR0.MODRST) {
|
|
if (0U == timeout) {
|
|
return false;
|
|
}
|
|
--timeout;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* brief Waitting until sdrv lin tx io state changing to idle.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of Waitting.
|
|
*/
|
|
static bool sdrv_lin_wait_txio_idle(sdrv_uart_regs_t *base, uint32_t timeout)
|
|
{
|
|
/* Wait for tx io idle. */
|
|
while (base->PSR0.IOTX_STATE) {
|
|
if (0U == timeout) {
|
|
return false;
|
|
}
|
|
--timeout;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* brief Waitting until sdrv lin rx io state changing to idle.
|
|
*
|
|
* param[in] base The address of lin's register.
|
|
* param[in] timeout The value of timeout.
|
|
* return The result of Waitting.
|
|
*/
|
|
static bool sdrv_lin_wait_rxio_idle(sdrv_uart_regs_t *base, uint32_t timeout)
|
|
{
|
|
/* Wait for rx io idle. */
|
|
while (base->PSR0.IORX_STATE) {
|
|
if (0U == timeout) {
|
|
return false;
|
|
}
|
|
--timeout;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* brief Sdrv lin baudrate round-off.
|
|
*
|
|
* param [in] dividend The clock frequency.
|
|
* param [in] divisor The value of baud.
|
|
* return The result of the baudrate after rounded off.
|
|
*/
|
|
static uint32_t sdrv_lin_baudrate_roundoff(uint32_t dividend, uint32_t divisor)
|
|
{
|
|
uint32_t temp = dividend * 10U / divisor;
|
|
return ((temp % 10U) < 5U) ? (temp / 10U) : (temp / 10U + 1U);
|
|
}
|
|
|
|
/***********************LIN Static Functions Start************************/
|