/* SPDX-License-Identifier: GPL-2.0 */ #include "sdiohal.h" #define SDIOHAL_TX_RETRY_MAX 3 #define SDIOHAL_TX_NO_RETRY static void sdiohal_tx_retrybuf_left(unsigned int suc_pac_cnt) { struct sdiohal_data_t *p_data = sdiohal_get_data(); unsigned int cnt = 0; struct sdio_puh_t *puh = NULL; unsigned char *p = NULL; if (suc_pac_cnt == 0) return; puh = (struct sdio_puh_t *)p_data->send_buf.retry_buf; for (cnt = 0; cnt < suc_pac_cnt;) { if (puh->eof == 0) { p = (unsigned char *)puh; cnt++; p_data->send_buf.retry_len = p_data->send_buf.retry_len - sizeof(struct sdio_puh_t) - SDIOHAL_ALIGN_4BYTE(puh->len); /* pointer to next packet */ p += sizeof(struct sdio_puh_t) + SDIOHAL_ALIGN_4BYTE(puh->len); puh = (struct sdio_puh_t *)p; p_data->send_buf.retry_buf = (unsigned char *)p; } else break; } sdiohal_debug("sdiohal_tx_retrybuf_left [%p] retry_len[%d]\n", p_data->send_buf.retry_buf, p_data->send_buf.retry_len); } static int sdiohal_send_try(struct sdiohal_sendbuf_t *send_buf) { unsigned int tx_pac_cnt = 0; unsigned int try_cnt = 0; int ret = 0; #ifdef SDIOHAL_TX_NO_RETRY return 0; #endif sdiohal_tx_init_retrybuf(); try_send: try_cnt++; if (try_cnt < SDIOHAL_TX_RETRY_MAX) { tx_pac_cnt = sdiohal_get_trans_pac_num(); /* get the buf ptr and length right now */ sdiohal_tx_retrybuf_left(tx_pac_cnt); usleep_range(4000, 6000); ret = sdiohal_sdio_pt_write(send_buf->retry_buf, SDIOHAL_ALIGN_BLK(send_buf->retry_len)); if (ret != 0) goto try_send; } return 0; } static int sdiohal_send(struct sdiohal_sendbuf_t *send_buf, struct sdiohal_list_t *data_list) { int ret = 0; if ((!send_buf) || (!data_list)) return -EINVAL; ret = sdiohal_sdio_pt_write(send_buf->buf, SDIOHAL_ALIGN_BLK(send_buf->used_len)); if (ret != 0) { sdiohal_err("tyr send,type:%d subtype:%d node_num:%d\n", data_list->type, data_list->subtype, data_list->node_num); ret = sdiohal_send_try(send_buf); } return ret; } int sdiohal_tx_data_list_send(struct sdiohal_list_t *data_list, bool pop_flag) { struct sdiohal_data_t *p_data = sdiohal_get_data(); struct mbuf_t *mbuf_node; unsigned int i; int ret = 0; sdiohal_sdma_enter(); sdiohal_list_check(data_list, __func__, SDIOHAL_WRITE); mbuf_node = data_list->mbuf_head; for (i = 0; i < data_list->node_num; i++, mbuf_node = mbuf_node->next) { if (p_data->send_buf.used_len + sizeof(struct sdio_puh_t) + SDIOHAL_ALIGN_4BYTE(mbuf_node->len) > SDIOHAL_TX_SENDBUF_LEN) { sdiohal_tx_set_eof(&p_data->send_buf, p_data->eof_buf); ret = sdiohal_send(&p_data->send_buf, data_list); if (ret) sdiohal_err("err1,type:%d subtype:%d num:%d\n", data_list->type, data_list->subtype, data_list->node_num); p_data->send_buf.used_len = 0; } sdiohal_tx_packer(&p_data->send_buf, data_list, mbuf_node); } sdiohal_tx_set_eof(&p_data->send_buf, p_data->eof_buf); if (pop_flag == true) sdiohal_tx_list_denq(data_list); ret = sdiohal_send(&p_data->send_buf, data_list); if (ret) sdiohal_err("err2,type:%d subtype:%d num:%d\n", data_list->type, data_list->subtype, data_list->node_num); p_data->send_buf.used_len = 0; sdiohal_sdma_leave(); return ret; } int sdiohal_tx_thread(void *data) { struct sdiohal_data_t *p_data = sdiohal_get_data(); struct sdiohal_list_t data_list; struct sched_param param; struct timespec tm_begin, tm_end; static long time_total_ns; static int times_count; param.sched_priority = SDIO_TX_TASK_PRIO; sched_setscheduler(current, SCHED_FIFO, ¶m); while (1) { /* Wait the semaphore */ sdiohal_tx_down(); if (p_data->exit_flag) break; if (!WCN_CARD_EXIST(&p_data->xmit_cnt)) { sdiohal_err("%s line %d not have card\n", __func__, __LINE__); continue; } getnstimeofday(&p_data->tm_end_sch); sdiohal_pr_perf("tx sch time:%ld\n", (long)(timespec_to_ns(&p_data->tm_end_sch) - timespec_to_ns(&p_data->tm_begin_sch))); sdiohal_lock_tx_ws(); sdiohal_resume_wait(); /* wakeup cp */ sdiohal_cp_tx_wakeup(PACKER_TX); while (!sdiohal_is_tx_list_empty()) { getnstimeofday(&tm_begin); sdiohal_tx_find_data_list(&data_list); if (p_data->adma_tx_enable) { sdiohal_adma_pt_write(&data_list); sdiohal_tx_list_denq(&data_list); } else sdiohal_tx_data_list_send(&data_list, true); getnstimeofday(&tm_end); time_total_ns += timespec_to_ns(&tm_end) - timespec_to_ns(&tm_begin); times_count++; if (!(times_count % PERFORMANCE_COUNT)) { sdiohal_pr_perf("tx list avg time:%ld\n", (time_total_ns / PERFORMANCE_COUNT)); time_total_ns = 0; times_count = 0; } } sdiohal_cp_tx_sleep(PACKER_TX); sdiohal_unlock_tx_ws(); } return 0; }