Files
android/drivers/misc/mediatek/eccci/hif/ccci_hif_ccif.c
T
threeeye bc1d038eac REVVL 6 Pro kernel bring-up: full working tree snapshot (2026-09-12)
Squashed backup commit (no shallow-clone history issues) of
xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2 branch, upstream
commit 9eece662e) plus all fixes applied to get everpal_defconfig building
clean against a modern (GCC 15.2.0) toolchain -- see the previous commit
message on the lineage-23.2 branch (also in this repo history is not
available here since this is a squashed snapshot; full incremental history
lives in the local DL380 checkout and the upstream GitHub remote).

Upstream: https://github.com/xiaomi-mt6833-dev/kernel_xiaomi_mt6833 (lineage-23.2)
2026-09-12 13:36:46 +00:00

1695 lines
51 KiB
C

/*
* Copyright (C) 2016 MediaTek Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See http://www.gnu.org/licenses/gpl-2.0.html for more details.
*/
#include <linux/list.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/slab.h>
#include <linux/skbuff.h>
#include <linux/wait.h>
#include <linux/sched/clock.h> /* local_clock() */
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/timer.h>
#include <linux/fs.h>
#include <linux/netdevice.h>
#include <linux/random.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_irq.h>
#include <linux/of_address.h>
#include "ccci_config.h"
#include "ccci_core.h"
#include "ccci_modem.h"
#include "ccci_bm.h"
#include "ccci_platform.h"
#include "ccci_hif_ccif.h"
#if (MD_GENERATION <= 6292)
#include "ccif_c2k_platform.h"
#else
#include "md_sys1_platform.h"
#endif
#define TAG "cif"
#define IS_PASS_SKB(per_md_data, qno) \
(!per_md_data->data_usb_bypass && (per_md_data->is_in_ee_dump == 0) \
&& ((1<<qno) & NET_RX_QUEUE_MASK))
/* #define RUN_WQ_BY_CHECKING_RINGBUF */
struct c2k_port {
enum c2k_channel ch;
enum c2k_channel excp_ch;
enum CCCI_CH tx_ch_mapping;
enum CCCI_CH rx_ch_mapping;
};
static struct c2k_port c2k_ports[] = {
/* c2k control channel mapping to 2 pairs of CCCI channels,
* please mind the order in this array,
* make sure CCCI_CONTROL_TX/RX be first.
*/
/*control channel */
{CTRL_CH_C2K, CTRL_CH_C2K_EXCP, CCCI_CONTROL_TX, CCCI_CONTROL_RX,},
/*control channel */
{CTRL_CH_C2K, CTRL_CH_C2K, CCCI_STATUS_TX, CCCI_STATUS_RX,},
/*audio channel */
{AUDIO_CH_C2K, AUDIO_CH_C2K, CCCI_PCM_TX, CCCI_PCM_RX,},
/*network channel for CCMNI1 */
{NET1_CH_C2K, NET1_CH_C2K, CCCI_CCMNI1_TX, CCCI_CCMNI1_RX,},
/*network channel for CCMNI1 */
{NET1_CH_C2K, NET1_CH_C2K, CCCI_CCMNI1_DL_ACK, CCCI_CCMNI1_DL_ACK,},
/*network channel for CCMNI2 */
{NET2_CH_C2K, NET2_CH_C2K, CCCI_CCMNI2_TX, CCCI_CCMNI2_RX,},
/*network channel for CCMNI2 */
{NET2_CH_C2K, NET2_CH_C2K, CCCI_CCMNI2_DL_ACK, CCCI_CCMNI2_DL_ACK,},
/*network channel for CCMNI3 */
{NET3_CH_C2K, NET3_CH_C2K, CCCI_CCMNI3_TX, CCCI_CCMNI3_RX,},
/*network channel for CCMNI3 */
{NET3_CH_C2K, NET3_CH_C2K, CCCI_CCMNI3_DL_ACK, CCCI_CCMNI3_DL_ACK,},
/*network channel for CCMNI4 */
{NET4_CH_C2K, NET4_CH_C2K, CCCI_CCMNI4_TX, CCCI_CCMNI4_RX,},
/*network channel for CCMNI5 */
{NET5_CH_C2K, NET5_CH_C2K, CCCI_CCMNI5_TX, CCCI_CCMNI5_RX,},
/*network channel for CCMNI6 */
{NET6_CH_C2K, NET6_CH_C2K, CCCI_CCMNI6_TX, CCCI_CCMNI6_RX,},
/*network channel for CCMNI7 */
{NET7_CH_C2K, NET7_CH_C2K, CCCI_CCMNI7_TX, CCCI_CCMNI7_RX,},
/*network channel for CCMNI8 */
{NET8_CH_C2K, NET8_CH_C2K, CCCI_CCMNI8_TX, CCCI_CCMNI8_RX,},
{NET10_CH_C2K, NET10_CH_C2K, CCCI_CCMNI10_TX, CCCI_CCMNI10_RX,},
{NET11_CH_C2K, NET11_CH_C2K, CCCI_CCMNI11_TX, CCCI_CCMNI11_RX,},
{NET12_CH_C2K, NET12_CH_C2K, CCCI_CCMNI12_TX, CCCI_CCMNI12_RX,},
{NET13_CH_C2K, NET13_CH_C2K, CCCI_CCMNI13_TX, CCCI_CCMNI13_RX,},
{NET14_CH_C2K, NET14_CH_C2K, CCCI_CCMNI14_TX, CCCI_CCMNI14_RX,},
{NET15_CH_C2K, NET15_CH_C2K, CCCI_CCMNI15_TX, CCCI_CCMNI15_RX,},
{NET16_CH_C2K, NET16_CH_C2K, CCCI_CCMNI16_TX, CCCI_CCMNI16_RX,},
{NET17_CH_C2K, NET17_CH_C2K, CCCI_CCMNI17_TX, CCCI_CCMNI17_RX,},
{NET18_CH_C2K, NET18_CH_C2K, CCCI_CCMNI18_TX, CCCI_CCMNI18_RX,},
{NET19_CH_C2K, NET19_CH_C2K, CCCI_CCMNI19_TX, CCCI_CCMNI19_RX,},
{NET20_CH_C2K, NET20_CH_C2K, CCCI_CCMNI20_TX, CCCI_CCMNI20_RX,},
{NET21_CH_C2K, NET21_CH_C2K, CCCI_CCMNI21_TX, CCCI_CCMNI21_RX,},
/*mdlogger ctrl channel */
{MDLOG_CTRL_CH_C2K, MDLOG_CTRL_CH_C2K, CCCI_UART1_TX, CCCI_UART1_RX,},
/*mdlogger data channel */
{MDLOG_CH_C2K, MDLOG_CH_C2K, CCCI_MD_LOG_TX, CCCI_MD_LOG_RX,},
/*flashless channel, new */
{FS_CH_C2K, FS_CH_C2K, CCCI_FS_TX, CCCI_FS_RX,},
/*ppp channel, for usb bypass */
{DATA_PPP_CH_C2K, DATA_PPP_CH_C2K,
CCCI_C2K_PPP_DATA, CCCI_C2K_PPP_DATA,},
/*AT for rild, new */
{AT_CH_C2K, AT_CH_C2K, CCCI_C2K_AT, CCCI_C2K_AT,},
/*AT2 for rild, new */
{AT2_CH_C2K, AT2_CH_C2K, CCCI_C2K_AT2, CCCI_C2K_AT2,},
/*AT3 for rild, new */
{AT3_CH_C2K, AT3_CH_C2K, CCCI_C2K_AT3, CCCI_C2K_AT3,},
/*AT4 for rild, new */
{AT4_CH_C2K, AT4_CH_C2K, CCCI_C2K_AT4, CCCI_C2K_AT4,},
/*AT5 for rild, new */
{AT5_CH_C2K, AT5_CH_C2K, CCCI_C2K_AT5, CCCI_C2K_AT5,},
/*AT6 for rild, new */
{AT6_CH_C2K, AT6_CH_C2K, CCCI_C2K_AT6, CCCI_C2K_AT6,},
/*AT7 for rild, new */
{AT7_CH_C2K, AT7_CH_C2K, CCCI_C2K_AT7, CCCI_C2K_AT7,},
/*AT8 for rild, new */
{AT8_CH_C2K, AT8_CH_C2K, CCCI_C2K_AT8, CCCI_C2K_AT8,},
/*agps channel */
{AGPS_CH_C2K, AGPS_CH_C2K, CCCI_IPC_UART_TX, CCCI_IPC_UART_RX,},
{MD2AP_LOOPBACK_C2K, MD2AP_LOOPBACK_C2K, CCCI_C2K_LB_DL,
CCCI_C2K_LB_DL,},
{LOOPBACK_C2K, LOOPBACK_C2K, CCCI_LB_IT_TX, CCCI_LB_IT_RX,},
{STATUS_CH_C2K, STATUS_CH_C2K, CCCI_CONTROL_TX, CCCI_CONTROL_RX,},
};
/*always keep this in mind:
* what if there are more than 1 modems using CLDMA...
*/
/*ccif share memory setting*/
/*need confirm with md. haow*/
#if (MD_GENERATION <= 6292)
static int rx_queue_buffer_size[QUEUE_NUM] = { 10 * 1024, 100 * 1024,
100 * 1024, 150 * 1024, 50 * 1024, 10 * 1024, 10 * 1024, 10 * 1024,
};
static int tx_queue_buffer_size[QUEUE_NUM] = { 10 * 1024, 100 * 1024,
50 * 1024, 50 * 1024, 50 * 1024, 10 * 1024, 10 * 1024, 10 * 1024,
};
#endif
#if (MD_GENERATION >= 6295)
static int rx_queue_buffer_size[QUEUE_NUM] = { 80 * 1024, 80 * 1024,
40 * 1024, 80 * 1024, 20 * 1024, 20 * 1024, 64 * 1024, 0 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024,
0 * 1024, 0 * 1024,
};
static int tx_queue_buffer_size[QUEUE_NUM] = { 128 * 1024, 40 * 1024,
8 * 1024, 40 * 1024, 20 * 1024, 20 * 1024, 64 * 1024, 0 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024,
0 * 1024, 0 * 1024,
};
static int rx_exp_buffer_size[QUEUE_NUM] = { 12 * 1024, 32 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 8 * 1024, 0 * 1024,
0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024,
0 * 1024, 0 * 1024,
};
static int tx_exp_buffer_size[QUEUE_NUM] = { 12 * 1024, 32 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 8 * 1024, 0 * 1024,
0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 0 * 1024,
0 * 1024, 0 * 1024,
};
#else
static int rx_queue_buffer_size[QUEUE_NUM] = { 80 * 1024, 80 * 1024,
40 * 1024, 80 * 1024, 20 * 1024, 20 * 1024, 64 * 1024, 0 * 1024,
};
static int tx_queue_buffer_size[QUEUE_NUM] = { 128 * 1024, 40 * 1024,
8 * 1024, 40 * 1024, 20 * 1024, 20 * 1024, 64 * 1024, 0 * 1024,
};
static int rx_exp_buffer_size[QUEUE_NUM] = { 12 * 1024, 32 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 8 * 1024, 0 * 1024,
};
static int tx_exp_buffer_size[QUEUE_NUM] = { 12 * 1024, 32 * 1024,
8 * 1024, 0 * 1024, 0 * 1024, 0 * 1024, 8 * 1024, 0 * 1024,
};
#endif
static void md_ccif_dump(unsigned char *title, unsigned char hif_id)
{
int idx;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "%s: %s\n", __func__, title);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_CON(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_CON,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_CON));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_BUSY(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_BUSY,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_BUSY));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_START(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_START,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_START));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_TCHNUM(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_TCHNUM,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_TCHNUM));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_RCHNUM(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_RCHNUM,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_RCHNUM));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "AP_ACK(%p)=0x%x\n",
md_ctrl->ccif_ap_base + APCCIF_ACK,
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_ACK));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_CON(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_CON,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_CON));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_BUSY(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_BUSY,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_BUSY));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_START(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_START,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_START));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_TCHNUM(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_TCHNUM,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_TCHNUM));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_RCHNUM(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_RCHNUM,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_RCHNUM));
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG, "MD_ACK(%p)=0x%x\n",
md_ctrl->ccif_md_base + APCCIF_ACK,
ccif_read32(md_ctrl->ccif_md_base, APCCIF_ACK));
for (idx = 0;
idx < md_ctrl->sram_size / sizeof(u32);
idx += 4) {
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"CHDATA(%p): %08X %08X %08X %08X\n",
md_ctrl->ccif_ap_base + APCCIF_CHDATA +
idx * sizeof(u32),
ccif_read32(md_ctrl->ccif_ap_base +
APCCIF_CHDATA,
(idx + 0) * sizeof(u32)),
ccif_read32(md_ctrl->ccif_ap_base +
APCCIF_CHDATA,
(idx + 1) * sizeof(u32)),
ccif_read32(md_ctrl->ccif_ap_base +
APCCIF_CHDATA,
(idx + 2) * sizeof(u32)),
ccif_read32(md_ctrl->ccif_ap_base +
APCCIF_CHDATA,
(idx + 3) * sizeof(u32)));
}
}
/*direction: 1: tx; 0: rx*/
static int c2k_ch_to_ccci_ch(int c2k_ch, int direction)
{
u16 c2k_channel_id;
int i = 0;
c2k_channel_id = (u16) c2k_ch;
for (i = 0; i < (sizeof(c2k_ports) / sizeof(struct c2k_port)); i++) {
if (c2k_channel_id == c2k_ports[i].ch) {
CCCI_DEBUG_LOG(MD_SYS3, TAG,
"%s:channel(%d)-->(T%d R%d)\n",
(direction == OUT) ? "TX" : "RX", c2k_ch,
c2k_ports[i].tx_ch_mapping,
c2k_ports[i].rx_ch_mapping);
return (direction == OUT) ? c2k_ports[i].tx_ch_mapping :
c2k_ports[i].rx_ch_mapping;
}
}
CCCI_ERROR_LOG(MD_SYS3, TAG,
"%s:ERR cannot find mapped c2k ch ID(%d)\n",
direction ? "TX" : "RX", c2k_ch);
return CCCI_OVER_MAX_CH;
}
static int ccci_ch_to_c2k_ch(int md_state, int ccci_ch, int direction)
{
u16 ccci_channel_id;
u16 channel_map;
int i = 0;
/* mp1 1, mp2 0, ro 1 */
if (md_state == INVALID)
goto md_state_invalid;
ccci_channel_id = (u16) ccci_ch;
for (i = 0; i < (sizeof(c2k_ports) / sizeof(struct c2k_port)); i++) {
channel_map = (direction == OUT) ? c2k_ports[i].tx_ch_mapping :
c2k_ports[i].rx_ch_mapping;
if (ccci_channel_id == channel_map) {
CCCI_DEBUG_LOG(MD_SYS3, TAG, "%s:channel(%d)-->(%d)\n",
(direction == OUT) ? "TX" : "RX",
ccci_channel_id, c2k_ports[i].ch);
return (md_state != EXCEPTION) ? c2k_ports[i].ch :
c2k_ports[i].excp_ch;
}
}
/* mp1 1, mp2 0, ro 1 */
md_state_invalid:
CCCI_ERROR_LOG(MD_SYS3, TAG,
"%s:ERR cannot find mapped ccci ch ID(%d)\n",
direction ? "TX" : "RX", ccci_ch);
return C2K_OVER_MAX_CH;
}
static void md_ccif_sram_rx_work(struct work_struct *work)
{
struct md_ccif_ctrl *md_ctrl =
container_of(work, struct md_ccif_ctrl, ccif_sram_work);
struct ccci_header *dl_pkg =
&md_ctrl->ccif_sram_layout->dl_header;
struct ccci_header *ccci_h = NULL;
struct ccci_header ccci_hdr;
struct sk_buff *skb = NULL;
int pkg_size, ret = 0, retry_cnt = 0;
int c2k_to_ccci_ch = 0;
u32 i = 0;
u8 *md_feature = (u8 *)(&md_ctrl->ccif_sram_layout->md_rt_data);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"%s:dk_pkg=%p, md_featrue=%p\n", __func__,
dl_pkg, md_feature);
pkg_size =
sizeof(struct ccci_header) + sizeof(struct md_query_ap_feature);
skb = ccci_alloc_skb(pkg_size, 1, 1);
if (skb == NULL) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"%s: alloc skb size=%d, failed\n", __func__,
pkg_size);
return;
}
skb_put(skb, pkg_size);
ccci_h = (struct ccci_header *)skb->data;
ccci_h->data[0] = ccif_read32(&dl_pkg->data[0], 0);
ccci_h->data[1] = ccif_read32(&dl_pkg->data[1], 0);
/*ccci_h->channel = ccif_read32(&dl_pkg->channel,0); */
*(((u32 *) ccci_h) + 2) = ccif_read32((((u32 *) dl_pkg) + 2), 0);
if (md_ctrl->md_id == MD_SYS3) {
c2k_to_ccci_ch = c2k_ch_to_ccci_ch(ccci_h->channel, IN);
ccci_h->channel = (u16) c2k_to_ccci_ch;
}
ccci_h->reserved = ccif_read32(&dl_pkg->reserved, 0);
/*warning: make sure struct md_query_ap_feature is 4 bypes align */
while (i < sizeof(struct md_query_ap_feature)) {
*((u32 *) (skb->data + sizeof(struct ccci_header) + i))
= ccif_read32(md_feature, i);
i += 4;
}
if (atomic_cmpxchg(&md_ctrl->wakeup_src, 1, 0) == 1) {
md_ctrl->wakeup_count++;
CCCI_NOTICE_LOG(md_ctrl->md_id, TAG,
"CCIF_MD wakeup source:(SRX_IDX/%d)(%u)\n",
ccci_h->channel, md_ctrl->wakeup_count);
}
ccci_hdr = *ccci_h;
ccci_md_check_rx_seq_num(md_ctrl->md_id,
&md_ctrl->traffic_info, &ccci_hdr, 0);
RETRY:
ret = ccci_md_recv_skb(md_ctrl->md_id, md_ctrl->hif_id, skb);
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG, "Rx msg %x %x %x %x ret=%d\n",
ccci_hdr.data[0], ccci_hdr.data[1],
*(((u32 *)&ccci_hdr) + 2), ccci_hdr.reserved, ret);
if (ret >= 0 || ret == -CCCI_ERR_DROP_PACKET) {
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"%s:ccci_port_recv_skb ret=%d\n", __func__,
ret);
} else {
if (retry_cnt < 20) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"%s:ccci_md_recv_skb ret=%d,retry=%d\n", __func__,
ret, retry_cnt);
udelay(5);
retry_cnt++;
goto RETRY;
}
ccci_free_skb(skb);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"%s:ccci_port_recv_skb ret=%d\n", __func__, ret);
}
}
static void c2k_mem_dump(void *start_addr, int len)
{
unsigned int *curr_p = (unsigned int *)start_addr;
unsigned char *curr_ch_p = NULL;
int _16_fix_num = len / 16;
int tail_num = len % 16;
char buf[16];
int i, j;
if (curr_p == NULL) {
CCCI_ERROR_LOG(MD_SYS3, TAG, "[C2K-DUMP]NULL point to dump!\n");
return;
}
if (len == 0) {
CCCI_ERROR_LOG(MD_SYS3, TAG, "[C2K-DUMP]Not need to dump\n");
return;
}
CCCI_DEBUG_LOG(MD_SYS3, TAG, "[C2K-DUMP]Base: 0x%lx, len: %d\n",
(unsigned long)start_addr, len);
/*Fix section */
for (i = 0; i < _16_fix_num; i++) {
CCCI_MEM_LOG(MD_SYS3, TAG,
"[C2K-DUMP]%03X: %08X %08X %08X %08X\n",
i * 16, *curr_p, *(curr_p + 1),
*(curr_p + 2), *(curr_p + 3));
curr_p += 4;
}
/*Tail section */
if (tail_num > 0) {
curr_ch_p = (unsigned char *)curr_p;
for (j = 0; j < tail_num; j++) {
buf[j] = *curr_ch_p;
curr_ch_p++;
}
for (; j < 16; j++)
buf[j] = 0;
curr_p = (unsigned int *)buf;
CCCI_MEM_LOG(MD_SYS3, TAG,
"[C2K-DUMP]%03X: %08X %08X %08X %08X\n",
i * 16, *curr_p, *(curr_p + 1),
*(curr_p + 2), *(curr_p + 3));
}
}
static int ccif_check_flow_ctrl(struct md_ccif_ctrl *md_ctrl,
struct md_ccif_queue *queue, struct ccci_ringbuf *rx_buf)
{
int is_busy, buf_size = 0;
int ret = 0;
is_busy = ccif_is_md_queue_busy(md_ctrl, queue->index);
if (is_busy < 0) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"ccif flow ctrl: check modem return %d\n",
is_busy);
return 0;
}
if (is_busy > 0) {
buf_size = rx_buf->rx_control.write - rx_buf->rx_control.read;
if (buf_size < 0)
buf_size += rx_buf->rx_control.length;
if (queue->resume_cnt < FLOW_CTRL_THRESHOLD &&
buf_size <= rx_buf->rx_control.length /
(2 << (queue->resume_cnt * 2))) {
if (ccci_fsm_get_md_state(md_ctrl->md_id) == READY) {
ret = ccci_port_send_msg_to_md(md_ctrl->md_id,
CCCI_CONTROL_TX,
C2K_FLOW_CTRL_MSG,
queue->index, 0);
if (ret < 0)
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"fail to resume md Q%d, ret0x%x\n",
queue->index, ret);
else {
queue->resume_cnt++;
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"flow ctrl: resume Q%d, buf %d, cnt %d\n",
queue->index, buf_size,
queue->resume_cnt);
}
}
}
} else
queue->resume_cnt = 0;
return ret;
}
static void md_ccif_traffic_work_func(struct work_struct *work)
{
struct ccci_hif_traffic *traffic_inf =
container_of(work, struct ccci_hif_traffic,
traffic_work_struct);
struct md_ccif_ctrl *md_ctrl =
container_of(traffic_inf, struct md_ccif_ctrl, traffic_info);
ccci_port_dump_status(md_ctrl->md_id);
ccci_channel_dump_packet_counter(md_ctrl->md_id,
&md_ctrl->traffic_info);
/*pre_cnt for tx, pkt_cont for rx*/
if (md_ctrl->md_id == MD_SYS3) {
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"traffic(AT): tx:[%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld\n",
CCCI_C2K_AT,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT],
CCCI_C2K_AT2,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT2],
CCCI_C2K_AT3,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT3],
CCCI_C2K_AT4,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT4],
CCCI_C2K_AT5,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT5],
CCCI_C2K_AT6,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT6],
CCCI_C2K_AT7,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT7],
CCCI_C2K_AT8,
md_ctrl->traffic_info.logic_ch_pkt_pre_cnt[CCCI_C2K_AT8]);
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"traffic(AT): rx:[%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld, [%d]%ld\n",
CCCI_C2K_AT,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT],
CCCI_C2K_AT2,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT2],
CCCI_C2K_AT3,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT3],
CCCI_C2K_AT4,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT4],
CCCI_C2K_AT5,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT5],
CCCI_C2K_AT6,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT6],
CCCI_C2K_AT7,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT7],
CCCI_C2K_AT8,
md_ctrl->traffic_info.logic_ch_pkt_cnt[CCCI_C2K_AT8]);
}
mod_timer(&md_ctrl->traffic_monitor,
jiffies + CCIF_TRAFFIC_MONITOR_INTERVAL * HZ);
}
static void md_ccif_traffic_monitor_func(unsigned long data)
{
struct md_ccif_ctrl *md_ctrl = (struct md_ccif_ctrl *)data;
if (!md_ctrl) {
CCCI_ERROR_LOG(0, TAG, "%s: md_ctl get fail\n",
__func__);
return;
}
schedule_work(&md_ctrl->traffic_info.traffic_work_struct);
}
atomic_t lb_dl_q;
/*this function may be called from both workqueue and softirq (NAPI)*/
static unsigned long rx_data_cnt;
static unsigned int pkg_num;
static int ccif_rx_collect(struct md_ccif_queue *queue, int budget,
int blocking, int *result)
{
struct ccci_ringbuf *rx_buf = queue->ringbuf;
unsigned char *data_ptr = NULL;
int ret = 0, count = 0, pkg_size;
unsigned long flags;
int qno = queue->index;
struct ccci_header *ccci_h = NULL;
struct ccci_header ccci_hdr;
struct sk_buff *skb = NULL;
int c2k_to_ccci_ch = 0;
unsigned char from_pool;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(queue->hif_id);
struct ccci_per_md *per_md_data =
ccci_get_per_md_data(md_ctrl->md_id);
if (atomic_read(&queue->rx_on_going)) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d rx is on-going(%d)1\n",
queue->index, atomic_read(&queue->rx_on_going));
*result = 0;
return -1;
}
atomic_set(&queue->rx_on_going, 1);
if (IS_PASS_SKB(per_md_data, qno))
from_pool = 0;
else
from_pool = 1;
while (1) {
md_ctrl->traffic_info.latest_q_rx_time[qno] = local_clock();
spin_lock_irqsave(&queue->rx_lock, flags);
pkg_size = ccci_ringbuf_readable(md_ctrl->md_id, rx_buf);
spin_unlock_irqrestore(&queue->rx_lock, flags);
if (pkg_size < 0) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d Rx:rbf readable ret=%d\n",
queue->index, pkg_size);
ret = 0;
goto OUT;
}
skb = ccci_alloc_skb(pkg_size, from_pool, blocking);
if (skb == NULL) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"Q%d Rx:ccci_alloc_skb pkg_size=%d failed,count=%d\n",
queue->index, pkg_size, count);
ret = -ENOMEM;
goto OUT;
}
data_ptr = (unsigned char *)skb_put(skb, pkg_size);
/*copy data into skb */
spin_lock_irqsave(&queue->rx_lock, flags);
ret = ccci_ringbuf_read(md_ctrl->md_id, rx_buf,
data_ptr, pkg_size);
spin_unlock_irqrestore(&queue->rx_lock, flags);
if (unlikely(ret < 0)) {
ccci_free_skb(skb);
goto OUT;
}
ccci_h = (struct ccci_header *)skb->data;
if (md_ctrl->md_id == MD_SYS3) {
/* md3(c2k) logical channel number is not
* the same as other modems,
* so we need use mapping table to
* convert channel id here.
*/
c2k_to_ccci_ch = c2k_ch_to_ccci_ch(ccci_h->channel, IN);
ccci_h->channel = (u16) c2k_to_ccci_ch;
/* heart beat msg from c2k control channel,
* but handled by ECCCI status channel handler,
* we hack the channel ID here.
*/
#if 0
if ((ccci_h->channel == CCCI_CONTROL_RX)
&& (ccci_h->data[1] == C2K_HB_MSG)) {
ccci_h->channel == CCCI_STATUS_RX;
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"heart beat msg received\n");
}
#endif
if (ccci_h->channel == CCCI_C2K_LB_DL) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d Rx lb_dl\n", queue->index);
c2k_mem_dump(data_ptr, pkg_size);
}
}
if (atomic_cmpxchg(&md_ctrl->wakeup_src, 1, 0) == 1) {
md_ctrl->wakeup_count++;
CCCI_NOTICE_LOG(md_ctrl->md_id, TAG,
"CCIF_MD wakeup source:(%d/%d/%x)(%u)\n",
queue->index, ccci_h->channel,
ccci_h->reserved, md_ctrl->wakeup_count);
if (ccci_h->channel == CCCI_FS_RX)
ccci_h->data[0] |= CCCI_FS_AP_CCCI_WAKEUP;
}
if (ccci_h->channel == CCCI_C2K_LB_DL)
atomic_set(&lb_dl_q, queue->index);
ccci_hdr = *ccci_h;
ret = ccci_port_recv_skb(md_ctrl->md_id,
queue->hif_id, skb, NORMAL_DATA);
if (ret >= 0 || ret == -CCCI_ERR_DROP_PACKET) {
count++;
ccci_md_check_rx_seq_num(md_ctrl->md_id,
&md_ctrl->traffic_info,
&ccci_hdr, queue->index);
ccci_md_add_log_history(&md_ctrl->traffic_info, IN,
(int)queue->index, &ccci_hdr,
(ret >= 0 ? 0 : 1));
ccci_channel_update_packet_counter(
md_ctrl->traffic_info.logic_ch_pkt_cnt,
&ccci_hdr);
if (queue->debug_id) {
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"Q%d Rx recv req ret=%d\n",
queue->index, ret);
queue->debug_id = 0;
}
spin_lock_irqsave(&queue->rx_lock, flags);
ccci_ringbuf_move_rpointer(md_ctrl->md_id,
rx_buf, pkg_size);
spin_unlock_irqrestore(&queue->rx_lock, flags);
if (likely(ccci_md_get_cap_by_id(md_ctrl->md_id)
& MODEM_CAP_TXBUSY_STOP))
ccif_check_flow_ctrl(md_ctrl, queue, rx_buf);
if (ccci_hdr.channel == CCCI_MD_LOG_RX) {
rx_data_cnt += pkg_size - 16;
pkg_num++;
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d Rx buf read=%d, write=%d, pkg_size=%d, log_cnt=%ld, pkg_num=%d\n",
queue->index,
rx_buf->rx_control.read,
rx_buf->rx_control.write, pkg_size,
rx_data_cnt, pkg_num);
}
ret = 0;
} else {
/*leave package into share memory,
* and waiting ccci to receive
*/
ccci_free_skb(skb);
if (queue->debug_id == 0) {
queue->debug_id = 1;
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"Q%d Rx err, ret = 0x%x\n",
queue->index, ret);
}
goto OUT;
}
if (count > budget) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d count > budget, exit now\n",
queue->index);
goto OUT;
}
}
OUT:
atomic_set(&queue->rx_on_going, 0);
*result = count;
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d rx %d pkg,ret=%d\n",
queue->index, count, ret);
spin_lock_irqsave(&queue->rx_lock, flags);
if (ret != -CCCI_ERR_PORT_RX_FULL
&& ret != -EAGAIN) {
pkg_size = ccci_ringbuf_readable(md_ctrl->md_id, rx_buf);
if (pkg_size > 0)
ret = -EAGAIN;
}
spin_unlock_irqrestore(&queue->rx_lock, flags);
return ret;
}
static void ccif_rx_work(struct work_struct *work)
{
int result = 0, ret = 0;
struct md_ccif_queue *queue =
container_of(work, struct md_ccif_queue, qwork);
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(queue->hif_id);
ret = ccif_rx_collect(queue, queue->budget, 1, &result);
if (ret == -EAGAIN) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%u queue again\n", queue->index);
queue_work(queue->worker, &queue->qwork);
} else {
ccci_port_queue_status_notify(md_ctrl->md_id, queue->hif_id,
queue->index, IN, RX_FLUSH);
}
}
void ccif_polling_ready(unsigned char hif_id, int step)
{
int cnt = 500; /*MD timeout is 10s*/
int time_once = 10;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
#ifdef CCCI_EE_HS_POLLING_TIME
cnt = CCCI_EE_HS_POLLING_TIME / time_once;
#endif
while (cnt > 0) {
if (md_ctrl->channel_id & (1U << step)) {
clear_bit(step, &md_ctrl->channel_id);
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"poll RCHNUM %ld\n", md_ctrl->channel_id);
return;
}
msleep(time_once);
cnt--;
}
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"poll EE HS timeout, RCHNUM %ld\n",
md_ctrl->channel_id);
}
int md_ccif_send(unsigned char hif_id, int channel_id)
{
int busy = 0;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
busy = ccif_read32(md_ctrl->ccif_ap_base, APCCIF_BUSY);
if (busy & (1 << channel_id)) {
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"CCIF channel %d busy\n", channel_id);
} else {
ccif_write32(md_ctrl->ccif_ap_base,
APCCIF_BUSY, 1 << channel_id);
ccif_write32(md_ctrl->ccif_ap_base,
APCCIF_TCHNUM, channel_id);
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"CCIF start=0x%x\n",
ccif_read32(md_ctrl->ccif_ap_base,
APCCIF_START));
}
return 0;
}
void md_ccif_sram_reset(unsigned char hif_id)
{
int idx = 0;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"%s\n", __func__);
for (idx = 0; idx < md_ctrl->sram_size / sizeof(u32);
idx += 1)
ccif_write32(md_ctrl->ccif_ap_base + APCCIF_CHDATA,
idx * sizeof(u32), 0);
ccci_reset_seq_num(&md_ctrl->traffic_info);
}
static void md_ccif_queue_dump(unsigned char hif_id)
{
int idx;
unsigned long long ts = 0;
unsigned long nsec_rem = 0;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump md_ctrl->channel_id 0x%lx\n",
md_ctrl->channel_id);
ts = md_ctrl->traffic_info.latest_isr_time;
nsec_rem = do_div(ts, NSEC_PER_SEC);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump CCIF latest isr %5llu.%06lu\n", ts,
nsec_rem / 1000);
#ifdef DEBUG_FOR_CCB
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump CCIF latest r_ch: 0x%x\n",
md_ctrl->traffic_info.last_ccif_r_ch);
ts = md_ctrl->traffic_info.latest_ccb_isr_time;
nsec_rem = do_div(ts, NSEC_PER_SEC);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump CCIF latest ccb_isr %5llu.%06lu\n", ts,
nsec_rem / 1000);
#endif
for (idx = 0; idx < QUEUE_NUM; idx++) {
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d TX: w=%d, r=%d, len=%d\n",
idx, md_ctrl->txq[idx].ringbuf->tx_control.write,
md_ctrl->txq[idx].ringbuf->tx_control.read,
md_ctrl->txq[idx].ringbuf->tx_control.length);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d RX: w=%d, r=%d, len=%d\n",
idx, md_ctrl->rxq[idx].ringbuf->rx_control.write,
md_ctrl->rxq[idx].ringbuf->rx_control.read,
md_ctrl->rxq[idx].ringbuf->rx_control.length);
ts = md_ctrl->traffic_info.latest_q_rx_isr_time[idx];
nsec_rem = do_div(ts, NSEC_PER_SEC);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d RX: last isr %5llu.%06lu\n", idx, ts,
nsec_rem / 1000);
ts = md_ctrl->traffic_info.latest_q_rx_time[idx];
nsec_rem = do_div(ts, NSEC_PER_SEC);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d RX: last wq %5llu.%06lu\n", idx, ts,
nsec_rem / 1000);
}
ccci_md_dump_log_history(md_ctrl->md_id,
&md_ctrl->traffic_info, 1, QUEUE_NUM, QUEUE_NUM);
}
void md_ccif_reset_queue(unsigned char hif_id, unsigned char for_start)
{
int i;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
unsigned long flags;
if (for_start) {
mod_timer(&md_ctrl->traffic_monitor,
jiffies + CCIF_TRAFFIC_MONITOR_INTERVAL * HZ);
} else {
del_timer(&md_ctrl->traffic_monitor);
}
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "%s\n", __func__);
for (i = 0; i < QUEUE_NUM; ++i) {
flush_work(&md_ctrl->rxq[i].qwork);
spin_lock_irqsave(&md_ctrl->rxq[i].rx_lock, flags);
ccci_ringbuf_reset(md_ctrl->md_id,
md_ctrl->rxq[i].ringbuf, 0);
spin_unlock_irqrestore(&md_ctrl->rxq[i].rx_lock, flags);
md_ctrl->rxq[i].resume_cnt = 0;
spin_lock_irqsave(&md_ctrl->txq[i].tx_lock, flags);
ccci_ringbuf_reset(md_ctrl->md_id,
md_ctrl->txq[i].ringbuf, 1);
spin_unlock_irqrestore(&md_ctrl->txq[i].tx_lock, flags);
ccif_wake_up_tx_queue(md_ctrl, i);
md_ctrl->txq[i].wakeup = 0;
}
ccif_reset_busy_queue(md_ctrl);
ccci_reset_seq_num(&md_ctrl->traffic_info);
}
void md_ccif_switch_ringbuf(unsigned char hif_id, enum ringbuf_id rb_id)
{
int i;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
unsigned long flags;
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "%s\n", __func__);
for (i = 0; i < QUEUE_NUM; ++i) {
spin_lock_irqsave(&md_ctrl->rxq[i].rx_lock, flags);
md_ctrl->rxq[i].ringbuf = md_ctrl->rxq[i].ringbuf_bak[rb_id];
spin_unlock_irqrestore(&md_ctrl->rxq[i].rx_lock, flags);
spin_lock_irqsave(&md_ctrl->txq[i].tx_lock, flags);
md_ctrl->txq[i].ringbuf = md_ctrl->txq[i].ringbuf_bak[rb_id];
spin_unlock_irqrestore(&md_ctrl->txq[i].tx_lock, flags);
}
}
static void md_ccif_check_ringbuf(struct md_ccif_ctrl *md_ctrl, int qno)
{
#ifdef RUN_WQ_BY_CHECKING_RINGBUF
unsigned long flags;
int data_to_read;
if (atomic_read(&md_ctrl->rxq[qno].rx_on_going)) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG, "Q%d rx is on-going(%d)3\n",
md_ctrl->rxq[qno].index,
atomic_read(&md_ctrl->rxq[qno].rx_on_going));
return;
}
spin_lock_irqsave(&md_ctrl->rxq[qno].rx_lock, flags);
data_to_read =
ccci_ringbuf_readable(md_ctrl->md_id,
md_ctrl->rxq[qno].ringbuf);
spin_unlock_irqrestore(&md_ctrl->rxq[qno].rx_lock, flags);
if (unlikely(data_to_read > 0)
&& ccci_md_napi_check_and_notice(md, qno) == 0
&& ccci_fsm_get_md_state(md_ctrl->md_id) != EXCEPTION) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"%d data remain in q%d\n", data_to_read, qno);
queue_work(md_ctrl->rxq[qno].worker,
&md_ctrl->rxq[qno].qwork);
}
#endif
}
/*exception and SRAM channel handler*/
static void md_ccif_handle_exception(struct md_ccif_ctrl *md_ctrl)
{
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"ccif_irq_tasklet1: ch %lx\n", md_ctrl->channel_id);
if (md_ctrl->channel_id & CCIF_HW_CH_RX_RESERVED) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"Interrupt from reserved ccif ch(%ld)\n",
md_ctrl->channel_id);
md_ctrl->channel_id &= ~CCIF_HW_CH_RX_RESERVED;
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"After cleared reserved ccif ch(%ld)\n",
md_ctrl->channel_id);
}
if (md_ctrl->channel_id & (1 << D2H_EXCEPTION_INIT)) {
clear_bit(D2H_EXCEPTION_INIT, &md_ctrl->channel_id);
ccci_fsm_recv_md_interrupt(md_ctrl->md_id,
MD_IRQ_CCIF_EX);
}
if (md_ctrl->channel_id & (1 << AP_MD_SEQ_ERROR)) {
clear_bit(AP_MD_SEQ_ERROR, &md_ctrl->channel_id);
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"MD check seq fail\n");
ccci_md_dump_info(md_ctrl->md_id,
DUMP_FLAG_CCIF, NULL, 0);
}
if (md_ctrl->channel_id & (1 << (D2H_SRAM))) {
clear_bit(D2H_SRAM, &md_ctrl->channel_id);
schedule_work(&md_ctrl->ccif_sram_work);
}
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"ccif_irq_tasklet2: ch %ld\n", md_ctrl->channel_id);
}
static void md_ccif_launch_work(struct md_ccif_ctrl *md_ctrl)
{
int i;
if (md_ctrl->channel_id & (1 << (D2H_SRAM))) {
clear_bit(D2H_SRAM, &md_ctrl->channel_id);
schedule_work(&md_ctrl->ccif_sram_work);
}
#if (MD_GENERATION >= 6293)
if (md_ctrl->channel_id & (1 << AP_MD_CCB_WAKEUP)) {
clear_bit(AP_MD_CCB_WAKEUP, &md_ctrl->channel_id);
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG, "CCB wakeup\n");
if (atomic_cmpxchg(&md_ctrl->wakeup_src, 1, 0) == 1) {
md_ctrl->wakeup_count++;
CCCI_NOTICE_LOG(md_ctrl->md_id, TAG,
"CCIF_MD wakeup source:(CCB)(%u)\n",
md_ctrl->wakeup_count);
}
#ifdef DEBUG_FOR_CCB
md_ctrl->traffic_info.latest_ccb_isr_time
= local_clock();
#endif
ccci_port_queue_status_notify(md_ctrl->md_id, CCIF_HIF_ID,
AP_MD_CCB_WAKEUP, -1, RX_IRQ);
}
#endif
for (i = 0; i < QUEUE_NUM; i++) {
if (md_ctrl->channel_id & (1U << (i + D2H_RINGQ0))) {
md_ctrl->traffic_info.latest_q_rx_isr_time[i]
= local_clock();
clear_bit(i + D2H_RINGQ0, &md_ctrl->channel_id);
if (atomic_read(&md_ctrl->rxq[i].rx_on_going)) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d rx is on-going(%d)2\n",
md_ctrl->rxq[i].index,
atomic_read(&md_ctrl->rxq[i].rx_on_going));
continue;
}
queue_work(md_ctrl->rxq[i].worker,
&md_ctrl->rxq[i].qwork);
} else
md_ccif_check_ringbuf(md_ctrl, i);
}
}
static irqreturn_t md_ccif_isr(int irq, void *data)
{
struct md_ccif_ctrl *md_ctrl = (struct md_ccif_ctrl *)data;
unsigned int ch_id, i;
/*disable_irq_nosync(md_ctrl->ccif_irq_id); */
/*must ack first, otherwise IRQ will rush in */
ch_id = ccif_read32(md_ctrl->ccif_ap_base, APCCIF_RCHNUM);
for (i = 0; i < CCIF_CH_NUM; i++)
if (ch_id & 0x1 << i)
set_bit(i, &md_ctrl->channel_id);
/* for 91/92, HIF CCIF is for C2K, only 16 CH;
* for 93, only lower 16 CH is for data
*/
ccif_write32(md_ctrl->ccif_ap_base,
APCCIF_ACK, ch_id & 0xFFFF);
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"%s ch_id = 0x%lX\n", __func__, md_ctrl->channel_id);
/* igore exception queue */
if (ch_id >> RINGQ_BASE) {
md_ctrl->traffic_info.latest_isr_time
= local_clock();
#ifdef DEBUG_FOR_CCB
/* infactly, maybe md_ctrl->channel_id is, which maybe cleared */
md_ctrl->traffic_info.last_ccif_r_ch = ch_id;
#endif
md_ccif_launch_work(md_ctrl);
} else
md_ccif_handle_exception(md_ctrl);
return IRQ_HANDLED;
}
static inline void md_ccif_queue_struct_init(struct md_ccif_queue *queue,
unsigned char hif_id, enum DIRECTION dir, unsigned char index)
{
queue->dir = dir;
queue->index = index;
queue->hif_id = hif_id;
init_waitqueue_head(&queue->req_wq);
spin_lock_init(&queue->rx_lock);
spin_lock_init(&queue->tx_lock);
atomic_set(&queue->rx_on_going, 0);
queue->debug_id = 0;
queue->wakeup = 0;
queue->resume_cnt = 0;
queue->budget = RX_BUGDET;
}
static int md_ccif_op_write_room(unsigned char hif_id, unsigned char qno)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
if (qno >= QUEUE_NUM)
return -CCCI_ERR_INVALID_QUEUE_INDEX;
return ccci_ringbuf_writeable(md_ctrl->md_id,
md_ctrl->txq[qno].ringbuf, 0);
}
static int md_ccif_op_send_skb(unsigned char hif_id, int qno,
struct sk_buff *skb, int skb_from_pool, int blocking)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
struct md_ccif_queue *queue = NULL;
/* struct ccci_header *ccci_h =
* (struct ccci_header *)req->skb->data;
*/
int ret;
/* struct ccci_header *ccci_h; */
unsigned long flags;
int ccci_to_c2k_ch = 0;
int md_flow_ctrl = 0;
struct ccci_header *ccci_h = NULL;
int md_cap = ccci_md_get_cap_by_id(md_ctrl->md_id);
struct ccci_per_md *per_md_data =
ccci_get_per_md_data(md_ctrl->md_id);
int md_state;
if (qno == 0xFF)
return -CCCI_ERR_INVALID_QUEUE_INDEX;
queue = &md_ctrl->txq[qno];
ccci_h = (struct ccci_header *)skb->data;
if (ccci_h->channel == CCCI_C2K_LB_DL)
qno = atomic_read(&lb_dl_q);
#if (MD_GENERATION < 6295)
if (qno > 7) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG, "qno error (%d)\n", qno);
return -CCCI_ERR_INVALID_QUEUE_INDEX;
}
#endif
queue = &md_ctrl->txq[qno];
retry:
/* we use irqsave as network require a lock in softirq,
* cause a potential deadlock
*/
spin_lock_irqsave(&queue->tx_lock, flags);
if (ccci_ringbuf_writeable(md_ctrl->md_id,
queue->ringbuf, skb->len) > 0) {
if (ccci_h->channel == CCCI_C2K_LB_DL) {
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"Q%d Tx lb_dl\n", queue->index);
c2k_mem_dump(skb->data, skb->len);
}
ccci_md_inc_tx_seq_num(md_ctrl->md_id,
&md_ctrl->traffic_info, ccci_h);
ccci_channel_update_packet_counter(
md_ctrl->traffic_info.logic_ch_pkt_cnt,
ccci_h);
if (md_ctrl->md_id == MD_SYS3) {
/* heart beat msg is sent from status channel in ECCCI,
* but from control channel in C2K,
* no status channel in C2K
*/
if (ccci_h->channel == CCCI_STATUS_TX) {
ccci_h->channel = CCCI_CONTROL_TX;
ccci_h->data[1] = C2K_HB_MSG;
ccci_h->reserved = md_ctrl->heart_beat_counter;
md_ctrl->heart_beat_counter++;
ccci_md_inc_tx_seq_num(md_ctrl->md_id,
&md_ctrl->traffic_info, ccci_h);
}
/* md3(c2k) logical channel number is not
* the same as other modems,
* so we need to use mapping table to
* convert channel id here.
*/
ccci_to_c2k_ch =
ccci_ch_to_c2k_ch(ccci_fsm_get_md_state(md_ctrl->md_id),
ccci_h->channel, OUT);
if (ccci_to_c2k_ch >= 0
&& ccci_to_c2k_ch < C2K_OVER_MAX_CH)
ccci_h->channel = (u16) ccci_to_c2k_ch;
else {
ret = -CCCI_ERR_INVALID_LOGIC_CHANNEL_ID;
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"channel num error (%d)\n",
ccci_to_c2k_ch);
spin_unlock_irqrestore(&queue->tx_lock, flags);
return ret;
}
if (ccci_h->data[1] == C2K_HB_MSG)
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"hb: 0x%x\n", ccci_h->channel);
}
/* copy skb to ringbuf */
ret = ccci_ringbuf_write(md_ctrl->md_id,
queue->ringbuf, skb->data, skb->len);
if (ret != skb->len)
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"TX:ERR rbf write: ret(%d)!=req(%d)\n",
ret, skb->len);
ccci_md_add_log_history(&md_ctrl->traffic_info, OUT,
(int)queue->index, ccci_h, 0);
/* free request */
ccci_free_skb(skb);
/* send ccif request */
md_ccif_send(hif_id, queue->ccif_ch);
spin_unlock_irqrestore(&queue->tx_lock, flags);
} else {
md_flow_ctrl = ccif_is_md_flow_ctrl_supported(md_ctrl);
if (likely((unsigned int)md_cap & MODEM_CAP_TXBUSY_STOP)
&& md_flow_ctrl > 0) {
ccif_set_busy_queue(md_ctrl, qno);
/* double check tx buffer after set busy bit.
* it is to avoid tx buffer is empty now
*/
if (unlikely(ccci_ringbuf_writeable(md_ctrl->md_id,
queue->ringbuf, skb->len) > 0)) {
ccif_clear_busy_queue(md_ctrl, qno);
spin_unlock_irqrestore(&queue->tx_lock, flags);
goto retry;
} else {
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"flow ctrl: TX busy on Q%d\n",
queue->index);
ccci_port_queue_status_notify(md_ctrl->md_id,
hif_id, queue->index, OUT, TX_FULL);
}
} else
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"flow ctrl is invalid, cap = %d, md_flow_ctrl = %d\n",
md_cap, md_flow_ctrl);
spin_unlock_irqrestore(&queue->tx_lock, flags);
if (blocking) {
if (md_flow_ctrl > 0) {
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"flow ctrl: Q%d is blocking, skb->len = %d\n",
queue->index, skb->len);
ret = wait_event_interruptible_exclusive(
queue->req_wq,
(queue->wakeup != 0));
queue->wakeup = 0;
if (ret == -ERESTARTSYS)
return -EINTR;
}
md_state = ccci_fsm_get_md_state(md_ctrl->md_id);
if (md_state == EXCEPTION
&& ccci_h->channel != CCCI_MD_LOG_TX
&& ccci_h->channel != CCCI_UART1_TX
&& ccci_h->channel != CCCI_FS_TX) {
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"tx retry break for EE for Q%d, ch %d\n",
queue->index, ccci_h->channel);
return -ETXTBSY;
} else if (md_state == GATED) {
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"tx retry break for Gated for Q%d, ch %d\n",
queue->index, ccci_h->channel);
return -ETXTBSY;
}
CCCI_REPEAT_LOG(md_ctrl->md_id, TAG,
"tx retry for Q%d, ch %d\n",
queue->index, ccci_h->channel);
goto retry;
} else {
if (per_md_data->data_usb_bypass)
return -ENOMEM;
else
return -EBUSY;
CCCI_DEBUG_LOG(md_ctrl->md_id, TAG,
"tx fail on q%d\n", qno);
}
}
return 0;
}
static int md_ccif_op_give_more(unsigned char hif_id, unsigned char qno)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
if (qno == 0xFF)
return -CCCI_ERR_INVALID_QUEUE_INDEX;
queue_work(md_ctrl->rxq[qno].worker,
&md_ctrl->rxq[qno].qwork);
return 0;
}
static void md_ccif_dump_queue_history(unsigned char hif_id, unsigned int qno)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump md_ctrl->channel_id 0x%lx\n", md_ctrl->channel_id);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Dump CCIF Queue%d Control\n", qno);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d TX: w=%d, r=%d, len=%d\n",
qno, md_ctrl->txq[qno].ringbuf->tx_control.write,
md_ctrl->txq[qno].ringbuf->tx_control.read,
md_ctrl->txq[qno].ringbuf->tx_control.length);
CCCI_MEM_LOG_TAG(md_ctrl->md_id, TAG,
"Q%d RX: w=%d, r=%d, len=%d\n",
qno, md_ctrl->rxq[qno].ringbuf->rx_control.write,
md_ctrl->rxq[qno].ringbuf->rx_control.read,
md_ctrl->rxq[qno].ringbuf->rx_control.length);
ccci_md_dump_log_history(md_ctrl->md_id,
&md_ctrl->traffic_info, 0, qno, qno);
}
static int md_ccif_stop_queue(unsigned char hif_id,
unsigned char qno, enum DIRECTION dir)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
if (dir == OUT)
ccif_set_busy_queue(md_ctrl, qno);
return 0;
}
static int md_ccif_start_queue(unsigned char hif_id,
unsigned char qno, enum DIRECTION dir)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
struct md_ccif_queue *queue = NULL;
unsigned long flags;
if (dir == OUT
&& likely((unsigned int)ccci_md_get_cap_by_id(md_ctrl->md_id)
& MODEM_CAP_TXBUSY_STOP
&& (qno < QUEUE_NUM))) {
queue = &md_ctrl->txq[qno];
spin_lock_irqsave(&queue->tx_lock, flags);
ccif_wake_up_tx_queue(md_ctrl, qno);
/*special for net queue*/
ccci_hif_queue_status_notify(md_ctrl->md_id,
hif_id, qno, OUT, TX_IRQ);
spin_unlock_irqrestore(&queue->tx_lock, flags);
}
return 0;
}
#if (MD_GENERATION >= 6293)
int md_ccif_exp_ring_buf_init(struct md_ccif_ctrl *md_ctrl)
{
int i = 0;
unsigned char *buf;
int bufsize = 0;
struct ccci_ringbuf *ringbuf;
struct ccci_smem_region *ccism;
ccism = ccci_md_get_smem_by_user_id(md_ctrl->md_id,
SMEM_USER_CCISM_MCU_EXP);
buf = (unsigned char *)ccism->base_ap_view_vir;
for (i = 0; i < QUEUE_NUM; i++) {
bufsize = CCCI_RINGBUF_CTL_LEN + rx_exp_buffer_size[i]
+ tx_exp_buffer_size[i];
ringbuf =
ccci_create_ringbuf(md_ctrl->md_id, buf, bufsize,
rx_exp_buffer_size[i], tx_exp_buffer_size[i]);
if (ringbuf == NULL) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"ccci_create_ringbuf %d failed\n", i);
return -1;
}
/*rx */
md_ctrl->rxq[i].ringbuf_bak[RB_EXP] = ringbuf;
md_ctrl->rxq[i].ccif_ch = D2H_RINGQ0 + i;
/*tx */
md_ctrl->txq[i].ringbuf_bak[RB_EXP] = ringbuf;
md_ctrl->txq[i].ccif_ch = H2D_RINGQ0 + i;
buf += bufsize;
}
return 0;
}
#endif
int md_ccif_ring_buf_init(unsigned char hif_id)
{
int i = 0;
unsigned char *buf;
int bufsize = 0;
struct md_ccif_ctrl *md_ctrl;
struct ccci_ringbuf *ringbuf = NULL;
struct ccci_smem_region *ccism;
md_ctrl = (struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
ccism = ccci_md_get_smem_by_user_id(md_ctrl->md_id,
SMEM_USER_CCISM_MCU);
md_ctrl->total_smem_size = 0;
/*CCIF_MD_SMEM_RESERVE; */
buf = (unsigned char *)ccism->base_ap_view_vir;
for (i = 0; i < QUEUE_NUM; i++) {
bufsize = CCCI_RINGBUF_CTL_LEN + rx_queue_buffer_size[i]
+ tx_queue_buffer_size[i];
if (md_ctrl->total_smem_size + bufsize > ccism->size) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"share memory too small,please check configure,smem_size=%d\n",
ccism->size);
return -1;
}
ringbuf =
ccci_create_ringbuf(md_ctrl->md_id, buf, bufsize,
rx_queue_buffer_size[i],
tx_queue_buffer_size[i]);
if (ringbuf == NULL) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"ccci_create_ringbuf %d failed\n", i);
return -1;
}
/*rx */
md_ctrl->rxq[i].ringbuf_bak[RB_NORMAL] = ringbuf;
md_ctrl->rxq[i].ringbuf = ringbuf;
md_ctrl->rxq[i].ccif_ch = D2H_RINGQ0 + i;
if (i != C2K_MD_LOG_RX_Q)
md_ctrl->rxq[i].worker =
alloc_workqueue("rx%d_worker",
WQ_UNBOUND | WQ_MEM_RECLAIM
| WQ_HIGHPRI,
1, i);
else
md_ctrl->rxq[i].worker =
alloc_workqueue("rx%d_worker",
WQ_UNBOUND | WQ_MEM_RECLAIM, 1, i);
INIT_WORK(&md_ctrl->rxq[i].qwork, ccif_rx_work);
/*tx */
md_ctrl->txq[i].ringbuf_bak[RB_NORMAL] = ringbuf;
md_ctrl->txq[i].ringbuf = ringbuf;
md_ctrl->txq[i].ccif_ch = H2D_RINGQ0 + i;
buf += bufsize;
md_ctrl->total_smem_size += bufsize;
}
#if (MD_GENERATION >= 6293)
md_ccif_exp_ring_buf_init(md_ctrl);
#endif
/*flow control zone is behind ring buffer zone*/
#ifdef FLOW_CTRL_ENABLE
if (ccci_md_get_cap_by_id(md_ctrl->md_id) & MODEM_CAP_TXBUSY_STOP) {
md_ctrl->flow_ctrl =
(struct ccif_flow_control *)(ccism->base_ap_view_vir
+ md_ctrl->total_smem_size);
md_ctrl->total_smem_size += sizeof(struct ccif_flow_control);
} else {
md_ctrl->flow_ctrl = NULL;
CCCI_INIT_LOG(md_ctrl->md_id, TAG, "No flow control for AP\n");
}
#else
md_ctrl->flow_ctrl = NULL;
CCCI_INIT_LOG(md_ctrl->md_id, TAG, "flow control is disabled\n");
#endif
ccism->size = md_ctrl->total_smem_size;
return 0;
}
static int md_ccif_op_dump_status(unsigned char hif_id,
enum MODEM_DUMP_FLAG flag, int length)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
/*runtime data, boot, long time no response EE */
if (flag & DUMP_FLAG_CCIF) {
md_ccif_dump("Dump CCIF SRAM\n", hif_id);
md_ccif_queue_dump(hif_id);
}
if (flag & DUMP_FLAG_IRQ_STATUS) {
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG,
"Dump AP CCIF IRQ status\n");
#ifdef CONFIG_MTK_GIC_V3_EXT
mt_irq_dump_status(md_ctrl->ccif_irq_id);
#endif
}
if (flag & DUMP_FLAG_QUEUE_0)
md_ccif_dump_queue_history(hif_id, 0);
if (flag & DUMP_FLAG_QUEUE_0_1) {
md_ccif_dump_queue_history(hif_id, 0);
md_ccif_dump_queue_history(hif_id, 1);
}
return 0;
}
static struct ccci_hif_ops ccci_hif_ccif_ops = {
.send_skb = &md_ccif_op_send_skb,
.give_more = &md_ccif_op_give_more,
.write_room = &md_ccif_op_write_room,
.stop_queue = &md_ccif_stop_queue,
.start_queue = &md_ccif_start_queue,
.dump_status = &md_ccif_op_dump_status,
};
static void ccif_hif_hw_init(struct md_ccif_ctrl *md_ctrl)
{
struct md_hw_info *hw_info =
(struct md_hw_info *)ccci_md_get_hw_info(md_ctrl->md_id);
int ret;
unsigned long ccif_irq_flags;
/*Copy HW info */
if (hw_info == NULL) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"%s:ccci_md_get_hw_info fail\n", __func__);
return;
}
md_ctrl->ccif_ap_base = (void __iomem *)hw_info->ap_ccif_base;
md_ctrl->ccif_md_base = (void __iomem *)hw_info->md_ccif_base;
#if (MD_GENERATION <= 6292)
md_ctrl->ccif_irq_id = hw_info->ap_ccif_irq_id;
ccif_irq_flags = hw_info->ap_ccif_irq_flags;
#else
md_ctrl->ccif_irq_id = hw_info->ap_ccif_irq0_id;
ccif_irq_flags = hw_info->ap_ccif_irq0_flags;
#endif
md_ctrl->sram_size = hw_info->sram_size;
md_ctrl->ccif_sram_layout =
(struct ccif_sram_layout *)(md_ctrl->ccif_ap_base
+ APCCIF_CHDATA);
CCCI_INIT_LOG(md_ctrl->md_id, TAG,
"CCIF_AP IRQ(%d) flag 0x%lx\n",
md_ctrl->ccif_irq_id, ccif_irq_flags);
ret = request_irq(md_ctrl->ccif_irq_id, md_ccif_isr,
ccif_irq_flags, "CCIF_AP_DATA", md_ctrl);
if (ret) {
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"request CCIF_AP_DATA IRQ(%d) error %d\n",
md_ctrl->ccif_irq_id, ret);
return;
}
atomic_set(&md_ctrl->ccif_ap_data_enabled, 1);
ret = irq_set_irq_wake(md_ctrl->ccif_irq_id, 1);
if (ret)
CCCI_ERROR_LOG(md_ctrl->md_id, TAG,
"irq_set_irq_wake ccif irq0(%d) error %d\n",
md_ctrl->ccif_irq_id, ret);
}
/* open clk->enable irq */
void ccif_set_irq_on_poweron(unsigned char hif_id)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "%s:CCIF_AP_DATA id=%d\n",
__func__, md_ctrl->ccif_irq_id);
if (atomic_cmpxchg(&md_ctrl->ccif_ap_data_enabled, 0, 1) == 0) {
enable_irq(md_ctrl->ccif_irq_id);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "enable ccif_ap_data irq\n");
}
}
/* disable irq->ack CCIF_RXCH->close clk*/
void ccif_set_irq_on_poweroff(unsigned char hif_id)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
unsigned int ch_id;
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "%s:CCIF_AP_DATA id=%d\n",
__func__, md_ctrl->ccif_irq_id);
/* disable irq */
if (atomic_cmpxchg(&md_ctrl->ccif_ap_data_enabled, 1, 0) == 1) {
disable_irq_nosync(md_ctrl->ccif_irq_id);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "disable ccif_ap_data irq\n");
}
ch_id = ccif_read32(md_ctrl->ccif_ap_base, APCCIF_RCHNUM);
CCCI_NORMAL_LOG(md_ctrl->md_id, TAG, "disable ccif irq,ch_id=%d\n", ch_id);
ccif_write32(md_ctrl->ccif_ap_base,
APCCIF_ACK, 0xFFFF);
}
int ccci_ccif_hif_init(unsigned char hif_id, unsigned char md_id)
{
int i;
struct md_ccif_ctrl *md_ctrl;
md_ctrl = kzalloc(sizeof(struct md_ccif_ctrl), GFP_KERNEL);
if (md_ctrl == NULL) {
CCCI_ERROR_LOG(md_id, TAG,
"%s:alloc md_ccif_ctrl memory fail\n",
__func__);
return -1;
}
INIT_WORK(&md_ctrl->ccif_sram_work, md_ccif_sram_rx_work);
init_timer(&md_ctrl->traffic_monitor);
md_ctrl->traffic_monitor.function = md_ccif_traffic_monitor_func;
md_ctrl->traffic_monitor.data = (unsigned long)md_ctrl;
md_ctrl->heart_beat_counter = 0;
INIT_WORK(&md_ctrl->traffic_info.traffic_work_struct,
md_ccif_traffic_work_func);
md_ctrl->channel_id = 0;
md_ctrl->md_id = md_id;
md_ctrl->hif_id = hif_id;
atomic_set(&md_ctrl->reset_on_going, 1);
atomic_set(&md_ctrl->wakeup_src, 0);
ccci_reset_seq_num(&md_ctrl->traffic_info);
/*init queue */
for (i = 0; i < QUEUE_NUM; i++) {
md_ccif_queue_struct_init(&md_ctrl->txq[i],
md_ctrl->hif_id, OUT, i);
md_ccif_queue_struct_init(&md_ctrl->rxq[i],
md_ctrl->hif_id, IN, i);
}
md_ctrl->ops = &ccci_hif_ccif_ops;
ccci_hif[hif_id] = (void *)md_ctrl;
ccif_hif_hw_init(md_ctrl);
return 0;
}
void dump_ctrl_path_hif_status(void)
{
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(CCIF_HIF_ID);
unsigned long rem_nsec;
u64 ts_nsec;
ts_nsec = sched_clock();
rem_nsec = do_div(ts_nsec, 1000000000);
if (md_ctrl != NULL) {
CCCI_HISTORY_LOG(md_ctrl->md_id, TAG, "[%5lu.%06lu]%s\n",
(unsigned long)ts_nsec, rem_nsec / 1000, __func__);
}
if (md_ctrl && md_ctrl->ccif_ap_base) {
CCCI_HISTORY_LOG(md_ctrl->md_id, TAG, "AP_RCHNUM:0x%08x\n",
ccif_read32(md_ctrl->ccif_ap_base, APCCIF_RCHNUM));
}
}
/*return ap_rt_data pointer after filling header*/
void *ccif_hif_fill_rt_header(unsigned char hif_id, int packet_size,
unsigned int tx_ch, unsigned int txqno)
{
struct ccci_header *ccci_h;
struct ccci_header ccci_h_bk;
struct md_ccif_ctrl *md_ctrl =
(struct md_ccif_ctrl *)ccci_hif_get_by_id(hif_id);
ccci_h =
(struct ccci_header *)&md_ctrl->ccif_sram_layout->up_header;
/*header */
ccif_write32(&ccci_h->data[0], 0, 0x00);
ccif_write32(&ccci_h->data[1], 0, packet_size);
ccif_write32(&ccci_h->reserved, 0, MD_INIT_CHK_ID);
/*ccif_write32(&ccci_h->channel,0,CCCI_CONTROL_TX); */
/*as Runtime data always be the first packet
* we send on control channel
*/
ccif_write32((u32 *) ccci_h + 2, 0, tx_ch);
/*ccci_header need backup for log history*/
ccci_h_bk.data[0] = ccif_read32(&ccci_h->data[0], 0);
ccci_h_bk.data[1] = ccif_read32(&ccci_h->data[1], 0);
*((u32 *)&ccci_h_bk + 2) = ccif_read32((u32 *) ccci_h + 2, 0);
ccci_h_bk.reserved = ccif_read32(&ccci_h->reserved, 0);
ccci_md_add_log_history(&md_ctrl->traffic_info, OUT,
(int)txqno, &ccci_h_bk, 0);
return (void *)&md_ctrl->ccif_sram_layout->ap_rt_data;
}