/** * @file sdrv_lin.c * @brief taishan lin driver source. * * @copyright Copyright (c) 2020 Semidrive Semiconductor. * All rights reserved. */ #include #include #include #include #include #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************************/