Merge "msm: ipa: add support for WDI stats"

This commit is contained in:
Linux Build Service Account 2014-08-15 04:51:32 -07:00 committed by Gerrit - the friendly Code Review server
commit 63be24402f
6 changed files with 436 additions and 3 deletions

View File

@ -1445,7 +1445,7 @@ static int ipa_init_sram(void)
IPA_SRAM_SET(IPA_v2_RAM_MODEM_HDR_OFST, IPA_CANARY_VAL);
IPA_SRAM_SET(IPA_v2_RAM_MODEM_OFST, IPA_CANARY_VAL);
IPA_SRAM_SET(IPA_v2_RAM_APPS_V4_FLT_OFST, IPA_CANARY_VAL);
IPA_SRAM_SET(IPA_v2_RAM_END_OFST, IPA_CANARY_VAL);
IPA_SRAM_SET(IPA_v2_RAM_UC_INFO_OFST, IPA_CANARY_VAL);
iounmap(ipa_sram_mmio);

View File

@ -171,8 +171,11 @@ enum ipa_hw_2_cpu_responses {
enum ipa_hw_2_cpu_events {
IPA_HW_2_CPU_EVENT_ERROR =
FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
IPA_HW_2_CPU_EVENT_LOG_INFO =
FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
IPA_HW_2_CPU_EVENT_WDI_ERROR =
FEATURE_ENUM_VAL(IPA_HW_FEATURE_WDI, 0),
};
/**
@ -439,6 +442,216 @@ union IpaHwWdiErrorEventData_t {
u32 raw32b;
} __packed;
/**
* union IpaHwFeatureInfoData_t - parameters for stats/config blob
*
* @offset : Location of a feature within the EventInfoData
* @size : Size of the feature
*/
union IpaHwFeatureInfoData_t {
struct IpaHwFeatureInfoParams_t {
u32 offset:16;
u32 size:16;
} __packed params;
u32 raw32b;
} __packed;
/**
* struct IpaHwEventInfoData_t - Structure holding the parameters for
* statistics and config info
*
* @baseAddrOffset : Base Address Offset of the statistics or config
* structure from IPA_WRAPPER_BASE
* @IpaHwFeatureInfoData_t : Location and size of each feature within
* the statistics or config structure
*
* @note Information about each feature in the featureInfo[]
* array is populated at predefined indices per the IPA_HW_FEATURES
* enum definition
*/
struct IpaHwEventInfoData_t {
u32 baseAddrOffset;
union IpaHwFeatureInfoData_t featureInfo[IPA_HW_NUM_FEATURES];
} __packed;
/**
* struct IpaHwEventLogInfoData_t - Structure holding the parameters for
* IPA_HW_2_CPU_EVENT_LOG_INFO Event
*
* @featureMask : Mask indicating the features enabled in HW.
* Refer IPA_HW_FEATURE_MASK
* @circBuffBaseAddrOffset : Base Address Offset of the Circular Event
* Log Buffer structure
* @statsInfo : Statistics related information
* @configInfo : Configuration related information
*
* @note The offset location of this structure from IPA_WRAPPER_BASE
* will be provided as Event Params for the IPA_HW_2_CPU_EVENT_LOG_INFO
* Event
*/
struct IpaHwEventLogInfoData_t {
u32 featureMask;
u32 circBuffBaseAddrOffset;
struct IpaHwEventInfoData_t statsInfo;
struct IpaHwEventInfoData_t configInfo;
} __packed;
/**
* ipa_get_wdi_stats() - Query WDI statistics from uc
* @stats: [inout] stats blob from client populated by driver
*
* Returns: 0 on success, negative on failure
*
* @note Cannot be called from atomic context
*
*/
int ipa_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats)
{
#define TX_STATS(y) stats->tx_ch_stats.y = \
ipa_ctx->wdi.uc_wdi_stats_mmio->tx_ch_stats.y
#define RX_STATS(y) stats->rx_ch_stats.y = \
ipa_ctx->wdi.uc_wdi_stats_mmio->rx_ch_stats.y
if (!stats || !ipa_ctx->wdi.uc_top_mmio ||
!ipa_ctx->wdi.uc_wdi_stats_mmio) {
IPAERR("bad parms stats=%p uc_top=%p wdi_stats=%p\n",
stats,
ipa_ctx->wdi.uc_top_mmio,
ipa_ctx->wdi.uc_wdi_stats_mmio);
return -EINVAL;
}
ipa_inc_client_enable_clks();
TX_STATS(num_pkts_processed);
TX_STATS(copy_engine_doorbell_value);
TX_STATS(num_db_fired);
TX_STATS(tx_comp_ring_stats.ringFull);
TX_STATS(tx_comp_ring_stats.ringEmpty);
TX_STATS(tx_comp_ring_stats.ringUsageHigh);
TX_STATS(tx_comp_ring_stats.ringUsageLow);
TX_STATS(bam_stats.bamFifoFull);
TX_STATS(bam_stats.bamFifoEmpty);
TX_STATS(bam_stats.bamFifoUsageHigh);
TX_STATS(bam_stats.bamFifoUsageLow);
TX_STATS(num_db);
TX_STATS(num_unexpected_db);
TX_STATS(num_bam_int_handled);
TX_STATS(num_bam_int_in_non_runnning_state);
TX_STATS(num_qmb_int_handled);
RX_STATS(max_outstanding_pkts);
RX_STATS(num_pkts_processed);
RX_STATS(rx_ring_rp_value);
RX_STATS(rx_ind_ring_stats.ringFull);
RX_STATS(rx_ind_ring_stats.ringEmpty);
RX_STATS(rx_ind_ring_stats.ringUsageHigh);
RX_STATS(rx_ind_ring_stats.ringUsageLow);
RX_STATS(bam_stats.bamFifoFull);
RX_STATS(bam_stats.bamFifoEmpty);
RX_STATS(bam_stats.bamFifoUsageHigh);
RX_STATS(bam_stats.bamFifoUsageLow);
RX_STATS(num_bam_int_handled);
RX_STATS(num_db);
RX_STATS(num_unexpected_db);
RX_STATS(reserved1);
RX_STATS(reserved2);
ipa_dec_client_disable_clks();
return 0;
}
EXPORT_SYMBOL(ipa_get_wdi_stats);
/* TODO: add support for IPA_HW_v2_5 */
static void ipa_log_evt_hdlr(void)
{
if (!ipa_ctx->wdi.uc_top_ofst) {
ipa_ctx->wdi.uc_top_ofst =
ipa_ctx->wdi.ipa_sram_mmio->common.eventParams;
if (ipa_ctx->wdi.uc_top_ofst +
sizeof(struct IpaHwEventLogInfoData_t) >=
ipa_ctx->ctrl->ipa_reg_base_ofst +
IPA_SRAM_DIRECT_ACCESS_N_OFST_v2_0(0) +
ipa_ctx->smem_sz) {
IPAERR("uc_top 0x%x outside SRAM\n",
ipa_ctx->wdi.uc_top_ofst);
goto bad_uc_top_ofst;
}
ipa_ctx->wdi.uc_top_mmio = ioremap(ipa_ctx->ipa_wrapper_base +
ipa_ctx->wdi.uc_top_ofst,
sizeof(struct IpaHwEventLogInfoData_t));
if (!ipa_ctx->wdi.uc_top_mmio) {
IPAERR("fail to ioremap uc top\n");
goto bad_uc_top_ofst;
}
if ((ipa_ctx->wdi.uc_top_mmio->featureMask &
(1 << IPA_HW_FEATURE_WDI)) == 0) {
IPAERR("WDI feature missing 0x%x\n",
ipa_ctx->wdi.uc_top_mmio->featureMask);
goto feat_miss;
}
if (ipa_ctx->wdi.uc_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_WDI].params.size !=
sizeof(struct IpaHwStatsWDIInfoData_t)) {
IPAERR("wdi stats size invalid exp=%zu is=%u\n",
sizeof(struct IpaHwStatsWDIInfoData_t),
ipa_ctx->wdi.uc_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_WDI].
params.size);
goto feat_miss;
}
ipa_ctx->wdi.uc_wdi_stats_ofst = ipa_ctx->wdi.
uc_top_mmio->statsInfo.baseAddrOffset +
ipa_ctx->wdi.uc_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_WDI].params.offset;
IPAERR("WDI stats ofst=0x%x\n",
ipa_ctx->wdi.uc_wdi_stats_ofst);
if (ipa_ctx->wdi.uc_wdi_stats_ofst +
sizeof(struct IpaHwStatsWDIInfoData_t) >=
ipa_ctx->ctrl->ipa_reg_base_ofst +
IPA_SRAM_DIRECT_ACCESS_N_OFST_v2_0(0) +
ipa_ctx->smem_sz) {
IPAERR("uc_wdi_stats 0x%x outside SRAM\n",
ipa_ctx->wdi.uc_wdi_stats_ofst);
goto bad_stats_ofst;
}
ipa_ctx->wdi.uc_wdi_stats_mmio =
ioremap(ipa_ctx->ipa_wrapper_base +
ipa_ctx->wdi.uc_wdi_stats_ofst,
sizeof(struct IpaHwStatsWDIInfoData_t));
if (!ipa_ctx->wdi.uc_wdi_stats_mmio) {
IPAERR("fail to ioremap uc wdi stats\n");
goto bad_stats_ofst;
}
} else {
if (ipa_ctx->wdi.ipa_sram_mmio->common.eventParams !=
ipa_ctx->wdi.uc_top_ofst) {
IPAERR("uc top ofst changed new=%u cur=%u\n",
ipa_ctx->wdi.uc_top_mmio->statsInfo.
featureInfo[IPA_HW_FEATURE_WDI].params.size,
ipa_ctx->wdi.uc_top_ofst);
}
}
return;
bad_stats_ofst:
ipa_ctx->wdi.uc_wdi_stats_ofst = 0;
feat_miss:
iounmap(ipa_ctx->wdi.uc_top_mmio);
bad_uc_top_ofst:
ipa_ctx->wdi.uc_top_ofst = 0;
return;
}
static void ipa_wdi_evt_handler(enum ipa_irq_type interrupt,
void *private_data,
void *interrupt_data)
@ -467,6 +680,11 @@ static void ipa_wdi_evt_handler(enum ipa_irq_type interrupt,
ipa_ctx->wdi.ipa_sram_mmio->wdi_tx_ch_0_state,
ipa_ctx->wdi.ipa_sram_mmio->wdi_rx_ch_0_state);
} else if (ipa_ctx->wdi.ipa_sram_mmio->common.eventOp ==
IPA_HW_2_CPU_EVENT_LOG_INFO) {
IPAERR("WDI evt log info ofst=0x%x\n",
ipa_ctx->wdi.ipa_sram_mmio->common.eventParams);
ipa_log_evt_hdlr();
} else {
IPAERR("unsupported WDI evt opcode=%u\n",
ipa_ctx->wdi.ipa_sram_mmio->common.eventOp);

View File

@ -73,6 +73,7 @@ static struct dentry *dfile_ip4_flt;
static struct dentry *dfile_ip6_flt;
static struct dentry *dfile_stats;
static struct dentry *dfile_wstats;
static struct dentry *dfile_wdi_stats;
static struct dentry *dfile_dbg_cnt;
static struct dentry *dfile_msg;
static struct dentry *dfile_ip4_nat;
@ -933,6 +934,91 @@ nxt_clnt_cons:
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
static ssize_t ipa_read_wdi(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct IpaHwStatsWDIInfoData_t stats;
int nbytes;
int cnt = 0;
if (!ipa_get_wdi_stats(&stats)) {
nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
"TX num_pkts_processed=%u\n"
"TX copy_engine_doorbell_value=%u\n"
"TX num_db_fired=%u\n"
"TX ringFull=%u\n"
"TX ringEmpty=%u\n"
"TX ringUsageHigh=%u\n"
"TX ringUsageLow=%u\n"
"TX bamFifoFull=%u\n"
"TX bamFifoEmpty=%u\n"
"TX bamFifoUsageHigh=%u\n"
"TX bamFifoUsageLow=%u\n"
"TX num_db=%u\n"
"TX num_unexpected_db=%u\n"
"TX num_bam_int_handled=%u\n"
"TX num_bam_int_in_non_runnning_state=%u\n"
"TX num_qmb_int_handled=%u\n",
stats.tx_ch_stats.num_pkts_processed,
stats.tx_ch_stats.copy_engine_doorbell_value,
stats.tx_ch_stats.num_db_fired,
stats.tx_ch_stats.tx_comp_ring_stats.ringFull,
stats.tx_ch_stats.tx_comp_ring_stats.ringEmpty,
stats.tx_ch_stats.tx_comp_ring_stats.ringUsageHigh,
stats.tx_ch_stats.tx_comp_ring_stats.ringUsageLow,
stats.tx_ch_stats.bam_stats.bamFifoFull,
stats.tx_ch_stats.bam_stats.bamFifoEmpty,
stats.tx_ch_stats.bam_stats.bamFifoUsageHigh,
stats.tx_ch_stats.bam_stats.bamFifoUsageLow,
stats.tx_ch_stats.num_db,
stats.tx_ch_stats.num_unexpected_db,
stats.tx_ch_stats.num_bam_int_handled,
stats.tx_ch_stats.num_bam_int_in_non_runnning_state,
stats.tx_ch_stats.num_qmb_int_handled);
cnt += nbytes;
nbytes = scnprintf(dbg_buff + cnt, IPA_MAX_MSG_LEN - cnt,
"RX max_outstanding_pkts=%u\n"
"RX num_pkts_processed=%u\n"
"RX rx_ring_rp_value=%u\n"
"RX ringFull=%u\n"
"RX ringEmpty=%u\n"
"RX ringUsageHigh=%u\n"
"RX ringUsageLow=%u\n"
"RX bamFifoFull=%u\n"
"RX bamFifoEmpty=%u\n"
"RX bamFifoUsageHigh=%u\n"
"RX bamFifoUsageLow=%u\n"
"RX num_bam_int_handled=%u\n"
"RX num_db=%u\n"
"RX num_unexpected_db=%u\n"
"RX reserved1=%u\n"
"RX reserved2=%u\n",
stats.rx_ch_stats.max_outstanding_pkts,
stats.rx_ch_stats.num_pkts_processed,
stats.rx_ch_stats.rx_ring_rp_value,
stats.rx_ch_stats.rx_ind_ring_stats.ringFull,
stats.rx_ch_stats.rx_ind_ring_stats.ringEmpty,
stats.rx_ch_stats.rx_ind_ring_stats.ringUsageHigh,
stats.rx_ch_stats.rx_ind_ring_stats.ringUsageLow,
stats.rx_ch_stats.bam_stats.bamFifoFull,
stats.rx_ch_stats.bam_stats.bamFifoEmpty,
stats.rx_ch_stats.bam_stats.bamFifoUsageHigh,
stats.rx_ch_stats.bam_stats.bamFifoUsageLow,
stats.rx_ch_stats.num_bam_int_handled,
stats.rx_ch_stats.num_db,
stats.rx_ch_stats.num_unexpected_db,
stats.rx_ch_stats.reserved1,
stats.rx_ch_stats.reserved2);
cnt += nbytes;
} else {
nbytes = scnprintf(dbg_buff, IPA_MAX_MSG_LEN,
"Fail to read WDI stats\n");
cnt += nbytes;
}
return simple_read_from_buffer(ubuf, count, ppos, dbg_buff, cnt);
}
void _ipa_write_dbg_cnt_v1(int option)
{
if (option == 1)
@ -1249,6 +1335,10 @@ const struct file_operations ipa_wstats_ops = {
.read = ipa_read_wstats,
};
const struct file_operations ipa_wdi_ops = {
.read = ipa_read_wdi,
};
const struct file_operations ipa_msg_ops = {
.read = ipa_read_msg,
};
@ -1358,6 +1448,13 @@ void ipa_debugfs_init(void)
goto fail;
}
dfile_wdi_stats = debugfs_create_file("wdi", read_only_mode, dent, 0,
&ipa_wdi_ops);
if (!dfile_wdi_stats || IS_ERR(dfile_wdi_stats)) {
IPAERR("fail to create file for debug_fs wdi stats\n");
goto fail;
}
dfile_dbg_cnt = debugfs_create_file("dbg_cnt", read_write_mode, dent, 0,
&ipa_dbg_cnt_ops);
if (!dfile_dbg_cnt || IS_ERR(dfile_dbg_cnt)) {

View File

@ -645,6 +645,10 @@ struct ipa_wdi_ctx {
u32 pending_cmd;
u32 last_resp;
struct dma_pool *dma_pool;
u32 uc_top_ofst;
struct IpaHwEventLogInfoData_t *uc_top_mmio;
u32 uc_wdi_stats_ofst;
struct IpaHwStatsWDIInfoData_t *uc_wdi_stats_mmio;
};
/**

View File

@ -80,9 +80,12 @@
#define IPA_v2_RAM_APPS_V4_FLT_SIZE 2176
#define IPA_v2_RAM_APPS_V6_FLT_OFST (IPA_v2_RAM_APPS_V4_FLT_OFST + \
IPA_v2_RAM_APPS_V4_FLT_SIZE)
#define IPA_v2_RAM_APPS_V6_FLT_SIZE 1664
#define IPA_v2_RAM_END_OFST (IPA_v2_RAM_APPS_V6_FLT_OFST + \
#define IPA_v2_RAM_APPS_V6_FLT_SIZE 1372
#define IPA_v2_RAM_UC_INFO_OFST (IPA_v2_RAM_APPS_V6_FLT_OFST + \
IPA_v2_RAM_APPS_V6_FLT_SIZE + IPA_CANARY_SIZE)
#define IPA_v2_RAM_UC_INFO_SIZE 292
#define IPA_v2_RAM_END_OFST (IPA_v2_RAM_UC_INFO_OFST + \
IPA_v2_RAM_UC_INFO_SIZE)
#define IPA_v2_RAM_APPS_V4_RT_OFST IPA_v2_RAM_END_OFST
#define IPA_v2_RAM_APPS_V4_RT_SIZE 0
#define IPA_v2_RAM_APPS_V6_RT_OFST IPA_v2_RAM_END_OFST

View File

@ -736,6 +736,111 @@ typedef void (*ipa_irq_handler_t)(enum ipa_irq_type interrupt,
void *private_data,
void *interrupt_data);
/**
* struct IpaHwBamStats_t - Strucuture holding the BAM statistics
*
* @bamFifoFull : Number of times Bam Fifo got full - For In Ch: Good,
* For Out Ch: Bad
* @bamFifoEmpty : Number of times Bam Fifo got empty - For In Ch: Bad,
* For Out Ch: Good
* @bamFifoUsageHigh : Number of times Bam fifo usage went above 75% -
* For In Ch: Good, For Out Ch: Bad
* @bamFifoUsageLow : Number of times Bam fifo usage went below 25% -
* For In Ch: Bad, For Out Ch: Good
*/
struct IpaHwBamStats_t {
u32 bamFifoFull;
u32 bamFifoEmpty;
u32 bamFifoUsageHigh;
u32 bamFifoUsageLow;
} __packed;
/**
* struct IpaHwRingStats_t - Strucuture holding the Ring statistics
*
* @ringFull : Number of times Transfer Ring got full - For In Ch: Good,
* For Out Ch: Bad
* @ringEmpty : Number of times Transfer Ring got empty - For In Ch: Bad,
* For Out Ch: Good
* @ringUsageHigh : Number of times Transfer Ring usage went above 75% -
* For In Ch: Good, For Out Ch: Bad
* @ringUsageLow : Number of times Transfer Ring usage went below 25% -
* For In Ch: Bad, For Out Ch: Good
*/
struct IpaHwRingStats_t {
u32 ringFull;
u32 ringEmpty;
u32 ringUsageHigh;
u32 ringUsageLow;
} __packed;
/**
* struct IpaHwStatsWDIRxInfoData_t - Structure holding the WDI Rx channel
* structures
*
* @max_outstanding_pkts : Number of outstanding packets in Rx Ring
* @num_pkts_processed : Number of packets processed - cumulative
* @rx_ring_rp_value : Read pointer last advertized to the WLAN FW
* @rx_ind_ring_stats : Ring info
* @bam_stats : BAM info
* @num_bam_int_handled : Number of Bam Interrupts handled by FW
* @num_db : Number of times the doorbell was rung
* @num_unexpected_db : Number of unexpected doorbells
*/
struct IpaHwStatsWDIRxInfoData_t {
u32 max_outstanding_pkts;
u32 num_pkts_processed;
u32 rx_ring_rp_value;
struct IpaHwRingStats_t rx_ind_ring_stats;
struct IpaHwBamStats_t bam_stats;
u32 num_bam_int_handled;
u32 num_db;
u32 num_unexpected_db;
u32 reserved1;
u32 reserved2;
} __packed;
/**
* struct IpaHwStatsWDITxInfoData_t - Structure holding the WDI Tx channel
* structures
*
* @num_pkts_processed : Number of packets processed - cumulative
* @copy_engine_doorbell_value : latest value of doorbell written to copy engine
* @num_db_fired : Number of DB from uC FW to Copy engine
* @tx_comp_ring_stats : ring info
* @bam_stats : BAM info
* @num_db : Number of times the doorbell was rung
* @num_unexpected_db : Number of unexpected doorbells
* @num_bam_int_handled : Number of Bam Interrupts handled by FW
* @num_bam_int_in_non_runnning_state : Number of Bam interrupts while not in
* Running state
* @num_qmb_int_handled : Number of QMB interrupts handled
*/
struct IpaHwStatsWDITxInfoData_t {
u32 num_pkts_processed;
u32 copy_engine_doorbell_value;
u32 num_db_fired;
struct IpaHwRingStats_t tx_comp_ring_stats;
struct IpaHwBamStats_t bam_stats;
u32 num_db;
u32 num_unexpected_db;
u32 num_bam_int_handled;
u32 num_bam_int_in_non_runnning_state;
u32 num_qmb_int_handled;
} __packed;
/**
* struct IpaHwStatsWDIInfoData_t - Structure holding the WDI channel structures
*
* @rx_ch_stats : RX stats
* @tx_ch_stats : TX stats
*/
struct IpaHwStatsWDIInfoData_t {
struct IpaHwStatsWDIRxInfoData_t rx_ch_stats;
struct IpaHwStatsWDITxInfoData_t tx_ch_stats;
} __packed;
/**
* struct ipa_wdi_ul_params - WDI_RX configuration
* @rdy_ring_base_pa: physical address of the base of the Rx ring (containing
@ -956,6 +1061,7 @@ int ipa_enable_wdi_pipe(u32 clnt_hdl);
int ipa_disable_wdi_pipe(u32 clnt_hdl);
int ipa_resume_wdi_pipe(u32 clnt_hdl);
int ipa_suspend_wdi_pipe(u32 clnt_hdl);
int ipa_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats);
/*
* Resource manager
@ -1542,6 +1648,11 @@ static inline bool ipa_emb_ul_pipes_empty(void)
return false;
}
static inline int ipa_get_wdi_stats(struct IpaHwStatsWDIInfoData_t *stats)
{
return -EPERM;
}
static inline int ipa_get_ep_mapping(enum ipa_client_type client)
{
return -EPERM;