对比新文件 |
| | |
| | | /* |
| | | * Copyright (c) 2019-2023 Beijing Hanwei Innovation Technology Ltd. Co. and |
| | | * its subsidiaries and affiliates (collectly called MKSEMI). |
| | | * |
| | | * All rights reserved. |
| | | * |
| | | * Redistribution and use in source and binary forms, with or without |
| | | * modification, are permitted provided that the following conditions are met: |
| | | * |
| | | * 1. Redistributions of source code must retain the above copyright notice, |
| | | * this list of conditions and the following disclaimer. |
| | | * |
| | | * 2. Redistributions in binary form, except as embedded into an MKSEMI |
| | | * integrated circuit in a product or a software update for such product, |
| | | * must reproduce the above copyright notice, this list of conditions and |
| | | * the following disclaimer in the documentation and/or other materials |
| | | * provided with the distribution. |
| | | * |
| | | * 3. Neither the name of MKSEMI nor the names of its contributors may be used |
| | | * to endorse or promote products derived from this software without |
| | | * specific prior written permission. |
| | | * |
| | | * 4. This software, with or without modification, must only be used with a |
| | | * MKSEMI integrated circuit. |
| | | * |
| | | * 5. Any software provided in binary form under this license must not be |
| | | * reverse engineered, decompiled, modified and/or disassembled. |
| | | * |
| | | * THIS SOFTWARE IS PROVIDED BY MKSEMI "AS IS" AND ANY EXPRESS OR IMPLIED |
| | | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| | | * MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| | | * DISCLAIMED. IN NO EVENT SHALL MKSEMI OR CONTRIBUTORS BE LIABLE FOR ANY |
| | | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| | | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| | | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| | | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| | | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| | | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| | | */ |
| | | |
| | | #include "wsf_queue.h" |
| | | #include "wsf_buf.h" |
| | | #include "mk_spi.h" |
| | | #include "mk_power.h" |
| | | #include "mk_misc.h" |
| | | #include "uwb_api.h" |
| | | #include "uci_tl_comm.h" |
| | | #include "uci_tl_task.h" |
| | | |
| | | #include "board.h" |
| | | |
| | | #if UCI_INTF_PORT == 1 |
| | | |
| | | #define UCI_PORT SPI_ID0 |
| | | #define UCI_SEND_RETRY_MAX (2) |
| | | |
| | | static void uci_tl_up_req(void); |
| | | static void uci_tl_timer_notify(void); |
| | | static void uci_tl_setup(void); |
| | | static bool uci_tl_up_is_active(void); |
| | | static void rx_over_callback(void *dev, uint32_t err_code); |
| | | static void tx_over_callback(void *dev, uint32_t err_code); |
| | | |
| | | extern int uci_validate(uint8_t *buf); |
| | | |
| | | uci_tl_dev_t g_uci_tl_dev = { |
| | | .uci_tl_setup = &uci_tl_setup, |
| | | .uci_tl_resume = &uci_tl_setup, |
| | | .uci_tl_up_is_active = &uci_tl_up_is_active, |
| | | .uci_tl_up_req = &uci_tl_up_req, |
| | | .uci_tl_timer_notify = &uci_tl_timer_notify, |
| | | }; |
| | | |
| | | static struct SPI_CFG_T uci_spi_cfg = { |
| | | .bit_rate = UCI_INTF_SPI_SPEED, |
| | | .data_bits = 8, |
| | | .slave = 1, |
| | | .clk_polarity = 0, |
| | | .clk_phase = 1, |
| | | .ti_mode = 0, |
| | | .dma_rx = true, |
| | | .dma_tx = true, |
| | | .int_rx = false, |
| | | .int_tx = false, |
| | | }; |
| | | |
| | | static uint8_t recv_buff[UCI_RX_BUFF_SIZE] = {0}; |
| | | static uint8_t send_buff[UCI_TX_BUFF_SIZE] = {0}; |
| | | static struct UCI_TL_MSG_T *tl_up_msg = NULL; |
| | | static bool tx_idle = true; |
| | | static bool rx_idle = true; |
| | | static uint8_t retry_cnt = 0; |
| | | static void host2slave_gpio_callback(enum IO_PIN_T pin); |
| | | |
| | | static void uci_tl_setup(void) |
| | | { |
| | | /*SCK of SPI pin pull-down.*/ |
| | | io_pull_set(IO_PIN_13, IO_PULL_DOWN, IO_PULL_UP_NONE); |
| | | /*Host to slave pin pull-up. */ |
| | | io_pull_set(HOST2SLAVE_HS_GPIO, IO_PULL_UP, IO_PULL_UP_LEVEL0); |
| | | /*Host to salve pin wake-up enalbe by low level.*/ |
| | | power_wakeup_enable((enum POWER_WAKEUP_SOURCE_T)HOST2SLAVE_HS_GPIO, POWER_WAKEUP_LEVEL_LOW); |
| | | |
| | | gpio_pin_set_dir(SLAVE2HOST_HS_GPIO, GPIO_DIR_OUT, 1); |
| | | gpio_pin_set_dir(HOST2SLAVE_HS_GPIO, GPIO_DIR_IN, 0); |
| | | gpio_enable_irq(HOST2SLAVE_HS_GPIO, GPIO_IRQ_TYPE_FALLING_EDGE, host2slave_gpio_callback); |
| | | |
| | | spi_open(UCI_PORT, &uci_spi_cfg); |
| | | } |
| | | |
| | | static void rx_over_callback(void *dev, uint32_t err_code) |
| | | { |
| | | if (uci_validate(recv_buff)) |
| | | { |
| | | uint16_t frame_len = *(recv_buff + 3) + UCI_HEADER_SIZE; |
| | | |
| | | if (WsfQueueCount(&g_uci_tl_dev.tl_down_queue) < UCI_MAX_DL_ITEMS) |
| | | { |
| | | struct UCI_TL_MSG_T *p; |
| | | |
| | | if ((p = WsfBufAlloc((uint16_t)(frame_len + sizeof(struct UCI_TL_MSG_T)))) != NULL) |
| | | { |
| | | memcpy(p->msg, recv_buff, frame_len); |
| | | p->msg_length = frame_len; |
| | | WsfQueueEnq(&g_uci_tl_dev.tl_down_queue, p); |
| | | } |
| | | else |
| | | { |
| | | LOG_INFO(TRACE_MODULE_UCI, "No buff to queue cmd\r\n"); |
| | | } |
| | | } |
| | | |
| | | /* Set UCI receive event */ |
| | | if (g_uci_tl_dev.uci_tl_down_notify != NULL) |
| | | { |
| | | g_uci_tl_dev.uci_tl_down_notify(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | static void tx_over_callback(void *dev, uint32_t err_code) |
| | | { |
| | | WsfTimerStop(&g_uci_tl_dev.tl_timer); |
| | | |
| | | if (tl_up_msg) |
| | | { |
| | | WsfBufFree(tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | } |
| | | |
| | | retry_cnt = 0; |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | } |
| | | |
| | | #if !(UCI_INTF_SPI_FD_HS) |
| | | static void host2slave_gpio_callback(enum IO_PIN_T pin) |
| | | { |
| | | if (0 == gpio_pin_get_val(pin)) |
| | | { |
| | | if (!tx_idle) |
| | | { |
| | | gpio_pin_set(SLAVE2HOST_HS_GPIO); |
| | | WsfTimerStop(&g_uci_tl_dev.tl_timer); |
| | | spi_abort_dma(UCI_PORT, SPI_DMA_ABORT_TX | SPI_DMA_ABORT_RX, NULL, NULL); |
| | | if (tl_up_msg != NULL) |
| | | { |
| | | WsfQueuePush(&g_uci_tl_dev.tl_up_queue, tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | } |
| | | tx_idle = true; |
| | | } |
| | | memset(recv_buff, 0, UCI_RX_BUFF_SIZE); |
| | | memset(send_buff, 0, UCI_TX_BUFF_SIZE); |
| | | spi_open(UCI_PORT, &uci_spi_cfg); |
| | | spi_transfer(UCI_PORT, send_buff, recv_buff, UCI_RX_BUFF_SIZE, NULL); |
| | | gpio_pin_clr(SLAVE2HOST_HS_GPIO); |
| | | rx_idle = false; |
| | | gpio_enable_irq(HOST2SLAVE_HS_GPIO, GPIO_IRQ_TYPE_RISING_EDGE, host2slave_gpio_callback); |
| | | power_mode_request(POWER_UNIT_UCI_RX, POWER_MODE_SLEEP); |
| | | } |
| | | else |
| | | { |
| | | spi_abort_dma(UCI_PORT, SPI_DMA_ABORT_TX | SPI_DMA_ABORT_RX, NULL, NULL); |
| | | rx_over_callback(NULL, 0); |
| | | memset(recv_buff, 0, UCI_RX_BUFF_SIZE); |
| | | memset(send_buff, 0, UCI_TX_BUFF_SIZE); |
| | | gpio_pin_set(SLAVE2HOST_HS_GPIO); |
| | | rx_idle = true; |
| | | gpio_enable_irq(HOST2SLAVE_HS_GPIO, GPIO_IRQ_TYPE_FALLING_EDGE, host2slave_gpio_callback); |
| | | power_mode_clear(POWER_UNIT_UCI_RX); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | #if (UCI_INTF_SPI_FD_HS) |
| | | static void host2slave_gpio_callback(enum IO_PIN_T pin) |
| | | { |
| | | if (0 == gpio_pin_get_val(pin)) |
| | | { |
| | | if (tx_idle) |
| | | { |
| | | memset(recv_buff, 0, UCI_RX_BUFF_SIZE); |
| | | memset(send_buff, 0, UCI_TX_BUFF_SIZE); |
| | | tl_up_msg = WsfQueueDeq(&g_uci_tl_dev.tl_up_queue); |
| | | if (tl_up_msg != NULL) |
| | | { |
| | | memcpy(send_buff, tl_up_msg->msg, tl_up_msg->msg_length); |
| | | tx_idle = false; |
| | | power_mode_request(POWER_UNIT_UCI_TX, POWER_MODE_SLEEP); |
| | | } |
| | | spi_open(UCI_PORT, &uci_spi_cfg); |
| | | spi_transfer(UCI_PORT, send_buff, recv_buff, UCI_RX_BUFF_SIZE, NULL); |
| | | gpio_pin_clr(SLAVE2HOST_HS_GPIO); |
| | | } |
| | | else |
| | | { |
| | | WsfTimerStop(&g_uci_tl_dev.tl_timer); |
| | | } |
| | | |
| | | rx_idle = false; |
| | | gpio_enable_irq(HOST2SLAVE_HS_GPIO, GPIO_IRQ_TYPE_RISING_EDGE, host2slave_gpio_callback); |
| | | power_mode_request(POWER_UNIT_UCI_RX, POWER_MODE_SLEEP); |
| | | } |
| | | else |
| | | { |
| | | spi_abort_dma(UCI_PORT, SPI_DMA_ABORT_TX | SPI_DMA_ABORT_RX, NULL, NULL); |
| | | rx_over_callback(NULL, 0); |
| | | if (!tx_idle) |
| | | { |
| | | tx_over_callback(NULL, 0); |
| | | tx_idle = true; |
| | | } |
| | | memset(recv_buff, 0, UCI_RX_BUFF_SIZE); |
| | | memset(send_buff, 0, UCI_TX_BUFF_SIZE); |
| | | gpio_pin_set(SLAVE2HOST_HS_GPIO); |
| | | rx_idle = true; |
| | | gpio_enable_irq(HOST2SLAVE_HS_GPIO, GPIO_IRQ_TYPE_FALLING_EDGE, host2slave_gpio_callback); |
| | | power_mode_clear(POWER_UNIT_UCI_RX); |
| | | power_mode_clear(POWER_UNIT_UCI_TX); |
| | | } |
| | | } |
| | | #endif |
| | | |
| | | static void uci_tl_up_req(void) |
| | | { |
| | | if (!tx_idle || !rx_idle) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | if (0 == gpio_pin_get_val(HOST2SLAVE_HS_GPIO)) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | uint32_t lock = int_lock(); |
| | | |
| | | /* Get messages from the up queue */ |
| | | tl_up_msg = WsfQueueDeq(&g_uci_tl_dev.tl_up_queue); |
| | | |
| | | if (tl_up_msg != NULL) |
| | | { |
| | | if (tl_up_msg->msg_length == 0) |
| | | { |
| | | WsfBufFree(tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | } |
| | | else |
| | | { |
| | | #if !(UCI_INTF_SPI_FD_HS) |
| | | if (!rx_idle || (0 == gpio_pin_get_val(HOST2SLAVE_HS_GPIO))) |
| | | { |
| | | WsfQueuePush(&g_uci_tl_dev.tl_up_queue, tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | } |
| | | else |
| | | #endif |
| | | { |
| | | tx_idle = false; |
| | | memset(recv_buff, 0, UCI_RX_BUFF_SIZE); |
| | | memset(send_buff, 0, UCI_TX_BUFF_SIZE); |
| | | memcpy(send_buff, tl_up_msg->msg, tl_up_msg->msg_length); |
| | | spi_open(UCI_PORT, &uci_spi_cfg); |
| | | #if !(UCI_INTF_SPI_FD_HS) |
| | | spi_transfer(UCI_PORT, send_buff, recv_buff, tl_up_msg->msg_length, tx_over_callback); |
| | | #else |
| | | spi_transfer(UCI_PORT, send_buff, recv_buff, UCI_RX_BUFF_SIZE, NULL); |
| | | #endif |
| | | gpio_pin_clr(SLAVE2HOST_HS_GPIO); |
| | | WsfTimerStartMs(&g_uci_tl_dev.tl_timer, UCI_HS_TIMEOUT_MS, WSF_TIMER_ONE_SHOT); |
| | | power_mode_request(POWER_UNIT_UCI_TX, POWER_MODE_SLEEP); |
| | | } |
| | | } |
| | | } |
| | | else |
| | | { |
| | | LOG_INFO(TRACE_MODULE_UCI, "Up queue is empty\r\n"); |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | } |
| | | int_unlock(lock); |
| | | } |
| | | |
| | | static bool uci_tl_up_is_active(void) |
| | | { |
| | | if (!spi_is_busy(UCI_PORT) && !tx_idle && rx_idle) |
| | | { |
| | | gpio_pin_set(SLAVE2HOST_HS_GPIO); |
| | | tx_idle = true; |
| | | power_mode_clear(POWER_UNIT_UCI_TX); |
| | | } |
| | | |
| | | return !tx_idle; |
| | | } |
| | | |
| | | static void uci_tl_timer_notify(void) |
| | | { |
| | | uint32_t lock = int_lock(); |
| | | if (!tx_idle) |
| | | { |
| | | LOG_INFO(TRACE_MODULE_UCI, "UCI Host did not ACK in time\n"); |
| | | gpio_pin_set(SLAVE2HOST_HS_GPIO); |
| | | spi_abort_dma(UCI_PORT, SPI_DMA_ABORT_TX | SPI_DMA_ABORT_RX, NULL, NULL); |
| | | |
| | | if (++retry_cnt > UCI_SEND_RETRY_MAX) |
| | | { |
| | | if (tl_up_msg) |
| | | { |
| | | WsfBufFree(tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | } |
| | | |
| | | retry_cnt = 0; |
| | | } |
| | | else |
| | | { |
| | | if (tl_up_msg != NULL) |
| | | { |
| | | WsfQueuePush(&g_uci_tl_dev.tl_up_queue, tl_up_msg); |
| | | tl_up_msg = NULL; |
| | | } |
| | | } |
| | | |
| | | g_uci_tl_dev.uci_tl_up_done_notify(); |
| | | tx_idle = true; |
| | | } |
| | | int_unlock(lock); |
| | | |
| | | power_mode_clear(POWER_UNIT_UCI_TX); |
| | | } |
| | | |
| | | #endif |