Files
test/drivers/source/lin/sdrv_lin.c
2025-11-07 20:19:23 +08:00

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************************/