diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c index d29fd5f3adcd..f4b63ec4b325 100644 --- a/drivers/soc/qcom/icnss.c +++ b/drivers/soc/qcom/icnss.c @@ -105,6 +105,7 @@ enum icnss_driver_event_type { ICNSS_DRIVER_EVENT_FW_READY_IND, ICNSS_DRIVER_EVENT_REGISTER_DRIVER, ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER, + ICNSS_DRIVER_EVENT_MAX, }; struct icnss_driver_event { @@ -116,7 +117,7 @@ struct icnss_driver_event { void *data; }; -enum cnss_driver_state { +enum icnss_driver_state { ICNSS_WLFW_QMI_CONNECTED, ICNSS_POWER_ON, ICNSS_FW_READY, @@ -137,6 +138,44 @@ struct icnss_vreg_info { bool state; }; +struct icnss_stats { + struct { + uint32_t posted; + uint32_t processed; + } events[ICNSS_DRIVER_EVENT_MAX]; + + struct { + uint32_t request; + uint32_t free; + uint32_t enable; + uint32_t disable; + } ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS]; + + uint32_t ind_register_req; + uint32_t ind_register_resp; + uint32_t ind_register_err; + uint32_t msa_info_req; + uint32_t msa_info_resp; + uint32_t msa_info_err; + uint32_t msa_ready_req; + uint32_t msa_ready_resp; + uint32_t msa_ready_err; + uint32_t msa_ready_ind; + uint32_t cap_req; + uint32_t cap_resp; + uint32_t cap_err; + uint32_t pin_connect_result; + uint32_t cfg_req; + uint32_t cfg_resp; + uint32_t cfg_req_err; + uint32_t mode_req; + uint32_t mode_resp; + uint32_t mode_req_err; + uint32_t ini_req; + uint32_t ini_resp; + uint32_t ini_req_err; +}; + static struct icnss_data { struct platform_device *pdev; struct icnss_driver_ops *ops; @@ -173,6 +212,7 @@ static struct icnss_data { bool skip_qmi; struct dentry *root_dentry; spinlock_t on_off_lock; + struct icnss_stats stats; } *penv; static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) @@ -188,6 +228,8 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) return "REGISTER_DRIVER"; case ICNSS_DRIVER_EVENT_UNREGISTER_DRIVER: return "UNREGISTER_DRIVER"; + case ICNSS_DRIVER_EVENT_MAX: + return "EVENT_MAX"; } return "UNKNOWN"; @@ -196,7 +238,7 @@ static char *icnss_driver_event_to_str(enum icnss_driver_event_type type) static int icnss_driver_event_post(enum icnss_driver_event_type type, bool sync, void *data) { - struct icnss_driver_event *event = NULL; + struct icnss_driver_event *event; unsigned long flags; int gfp = GFP_KERNEL; int ret = 0; @@ -205,6 +247,11 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, icnss_driver_event_to_str(type), type, sync ? "-sync" : "", penv->state); + if (type >= ICNSS_DRIVER_EVENT_MAX) { + icnss_pr_err("Invalid Event type: %d, can't post", type); + return -EINVAL; + } + if (in_interrupt() || irqs_disabled()) gfp = GFP_ATOMIC; @@ -221,6 +268,7 @@ static int icnss_driver_event_post(enum icnss_driver_event_type type, list_add_tail(&event->list, &penv->event_list); spin_unlock_irqrestore(&penv->event_lock, flags); + penv->stats.events[type].posted++; queue_work(penv->event_wq, &penv->event_work); if (sync) { ret = wait_for_completion_interruptible(&event->complete); @@ -265,6 +313,8 @@ static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len) icnss_pr_dbg("Pin connect Result: pwr_pin: 0x%x phy_io_pin: 0x%x rf_io_pin: 0x%x\n", ind_msg.pwr_pin_result, ind_msg.phy_io_pin_result, ind_msg.rf_pin_result); + + penv->stats.pin_connect_result++; out: return ret; } @@ -485,7 +535,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) goto out; } - icnss_pr_dbg("Sending MSA info, state: 0x%lx\n", penv->state); + icnss_pr_dbg("Sending MSA mem info, state: 0x%lx\n", penv->state); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); @@ -501,6 +551,8 @@ static int wlfw_msa_mem_info_send_sync_msg(void) resp_desc.msg_id = QMI_WLFW_MSA_INFO_RESP_V01; resp_desc.ei_array = wlfw_msa_info_resp_msg_v01_ei; + penv->stats.msa_info_req++; + ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { @@ -512,6 +564,7 @@ static int wlfw_msa_mem_info_send_sync_msg(void) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.msa_info_err++; goto out; } @@ -522,9 +575,11 @@ static int wlfw_msa_mem_info_send_sync_msg(void) icnss_pr_err("Invalid memory region length received%d\n", resp.mem_region_info_len); ret = -EINVAL; + penv->stats.msa_info_err++; goto out; } + penv->stats.msa_info_resp++; for (i = 0; i < resp.mem_region_info_len; i++) { penv->icnss_mem_region[i].reg_addr = resp.mem_region_info[i].region_addr; @@ -554,7 +609,8 @@ static int wlfw_msa_ready_send_sync_msg(void) goto out; } - icnss_pr_dbg("Sending MSA sync message, state: 0x%lx\n", penv->state); + icnss_pr_dbg("Sending MSA ready request message, state: 0x%lx\n", + penv->state); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); @@ -567,9 +623,11 @@ static int wlfw_msa_ready_send_sync_msg(void) resp_desc.msg_id = QMI_WLFW_MSA_READY_RESP_V01; resp_desc.ei_array = wlfw_msa_ready_resp_msg_v01_ei; + penv->stats.msa_ready_req++; ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { + penv->stats.msa_ready_err++; icnss_pr_err("Send req failed %d\n", ret); goto out; } @@ -577,9 +635,11 @@ static int wlfw_msa_ready_send_sync_msg(void) if (resp.resp.result != QMI_RESULT_SUCCESS_V01) { icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); + penv->stats.msa_ready_err++; ret = resp.resp.result; goto out; } + penv->stats.msa_ready_resp++; out: return ret; } @@ -596,7 +656,8 @@ static int wlfw_ind_register_send_sync_msg(void) goto out; } - icnss_pr_dbg("Sending Sync message, state: 0x%lx\n", penv->state); + icnss_pr_dbg("Sending indication register message, state: 0x%lx\n", + penv->state); memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); @@ -616,11 +677,15 @@ static int wlfw_ind_register_send_sync_msg(void) resp_desc.msg_id = QMI_WLFW_IND_REGISTER_RESP_V01; resp_desc.ei_array = wlfw_ind_register_resp_msg_v01_ei; + penv->stats.ind_register_req++; + ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); + penv->stats.ind_register_resp++; if (ret < 0) { icnss_pr_err("Send req failed %d\n", ret); + penv->stats.ind_register_err++; goto out; } @@ -628,6 +693,7 @@ static int wlfw_ind_register_send_sync_msg(void) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.ind_register_err++; goto out; } out: @@ -658,11 +724,13 @@ static int wlfw_cap_send_sync_msg(void) resp_desc.msg_id = QMI_WLFW_CAP_RESP_V01; resp_desc.ei_array = wlfw_cap_resp_msg_v01_ei; + penv->stats.cap_req++; ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { icnss_pr_err("Send req failed %d\n", ret); + penv->stats.cap_err++; goto out; } @@ -670,9 +738,11 @@ static int wlfw_cap_send_sync_msg(void) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.cap_err++; goto out; } + penv->stats.cap_resp++; /* store cap locally */ if (resp.chip_info_valid) penv->chip_info = resp.chip_info; @@ -685,11 +755,11 @@ static int wlfw_cap_send_sync_msg(void) if (resp.fw_version_info_valid) penv->fw_version_info = resp.fw_version_info; - icnss_pr_dbg("Capability, chip_id: 0x%0x, chip_family: 0x%0x, board_id: 0x%0x, soc_id: 0x%0x, fw_version: 0x%0x, fw_build_timestamp: %s", - penv->chip_info.chip_id, penv->chip_info.chip_family, - penv->board_info.board_id, penv->soc_info.soc_id, - penv->fw_version_info.fw_version, - penv->fw_version_info.fw_build_timestamp); + icnss_pr_dbg("Capability, chip_id: 0x%x, chip_family: 0x%x, board_id: 0x%x, soc_id: 0x%x, fw_version: 0x%x, fw_build_timestamp: %s", + penv->chip_info.chip_id, penv->chip_info.chip_family, + penv->board_info.board_id, penv->soc_info.soc_id, + penv->fw_version_info.fw_version, + penv->fw_version_info.fw_build_timestamp); out: return ret; } @@ -722,11 +792,13 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) resp_desc.msg_id = QMI_WLFW_WLAN_MODE_RESP_V01; resp_desc.ei_array = wlfw_wlan_mode_resp_msg_v01_ei; + penv->stats.mode_req++; ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { icnss_pr_err("Send req failed %d\n", ret); + penv->stats.mode_req_err++; goto out; } @@ -734,8 +806,10 @@ static int wlfw_wlan_mode_send_sync_msg(enum wlfw_driver_mode_enum_v01 mode) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.mode_req_err++; goto out; } + penv->stats.mode_resp++; out: return ret; } @@ -767,11 +841,13 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data) resp_desc.msg_id = QMI_WLFW_WLAN_CFG_RESP_V01; resp_desc.ei_array = wlfw_wlan_cfg_resp_msg_v01_ei; + penv->stats.cfg_req++; ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { icnss_pr_err("Send req failed %d\n", ret); + penv->stats.cfg_req_err++; goto out; } @@ -779,8 +855,10 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.cfg_req_err++; goto out; } + penv->stats.cfg_resp++; out: return ret; } @@ -814,10 +892,13 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log) resp_desc.msg_id = QMI_WLFW_INI_RESP_V01; resp_desc.ei_array = wlfw_ini_resp_msg_v01_ei; + penv->stats.ini_req++; + ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req), &resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS); if (ret < 0) { icnss_pr_err("send req failed %d\n", ret); + penv->stats.ini_req_err++; goto out; } @@ -825,8 +906,10 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log) icnss_pr_err("QMI request failed %d %d\n", resp.resp.result, resp.resp.error); ret = resp.resp.result; + penv->stats.ini_req_err++; goto out; } + penv->stats.ini_resp++; out: return ret; } @@ -884,6 +967,7 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle, case QMI_WLFW_MSA_READY_IND_V01: icnss_pr_dbg("Received MSA Ready Indication msg_id 0x%x\n", msg_id); + penv->stats.msa_ready_ind++; break; case QMI_WLFW_PIN_CONNECT_RESULT_IND_V01: icnss_pr_dbg("Received Pin Connect Test Result msg_id 0x%x\n", @@ -1131,13 +1215,18 @@ static void icnss_driver_event_work(struct work_struct *work) break; default: icnss_pr_err("Invalid Event type: %d", event->type); - break; + kfree(event); + continue; } + + penv->stats.events[event->type].processed++; + if (event->sync) { event->ret = ret; complete(&event->complete); } else kfree(event); + spin_lock_irqsave(&penv->event_lock, flags); } spin_unlock_irqrestore(&penv->event_lock, flags); @@ -1245,7 +1334,7 @@ int icnss_ce_request_irq(unsigned int ce_id, icnss_pr_dbg("CE request IRQ: %d, state: 0x%lx\n", ce_id, penv->state); if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { - icnss_pr_err("Invalid CE ID %d\n", ce_id); + icnss_pr_err("Invalid CE ID, ce_id: %d\n", ce_id); ret = -EINVAL; goto out; } @@ -1267,7 +1356,10 @@ int icnss_ce_request_irq(unsigned int ce_id, } irq_entry->irq = irq; irq_entry->handler = handler; + icnss_pr_dbg("IRQ requested: %d, ce_id: %d\n", irq, ce_id); + + penv->stats.ce_irqs[ce_id].request++; out: return ret; } @@ -1286,6 +1378,12 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx) icnss_pr_dbg("CE free IRQ: %d, state: 0x%lx\n", ce_id, penv->state); + if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { + icnss_pr_err("Invalid CE ID to free, ce_id: %d\n", ce_id); + ret = -EINVAL; + goto out; + } + irq = penv->ce_irqs[ce_id]; irq_entry = &penv->ce_irq_list[ce_id]; if (!irq_entry->handler || !irq_entry->irq) { @@ -1296,6 +1394,8 @@ int icnss_ce_free_irq(unsigned int ce_id, void *ctx) free_irq(irq, ctx); irq_entry->irq = 0; irq_entry->handler = NULL; + + penv->stats.ce_irqs[ce_id].free++; out: return ret; } @@ -1313,6 +1413,13 @@ void icnss_enable_irq(unsigned int ce_id) icnss_pr_dbg("Enable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); + if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { + icnss_pr_err("Invalid CE ID to enable IRQ, ce_id: %d\n", ce_id); + return; + } + + penv->stats.ce_irqs[ce_id].enable++; + irq = penv->ce_irqs[ce_id]; enable_irq(irq); } @@ -1330,8 +1437,16 @@ void icnss_disable_irq(unsigned int ce_id) icnss_pr_dbg("Disable IRQ: ce_id: %d, state: 0x%lx\n", ce_id, penv->state); + if (ce_id >= ICNSS_MAX_IRQ_REGISTRATIONS) { + icnss_pr_err("Invalid CE ID to disable IRQ, ce_id: %d\n", + ce_id); + return; + } + irq = penv->ce_irqs[ce_id]; disable_irq(irq); + + penv->stats.ce_irqs[ce_id].disable++; } EXPORT_SYMBOL(icnss_disable_irq); @@ -1806,6 +1921,9 @@ static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf, if (ret) return ret; + if (ret == 0) + memset(&priv->stats, 0, sizeof(priv->stats)); + return count; } @@ -1823,6 +1941,172 @@ static const struct file_operations icnss_test_mode_fops = { .llseek = seq_lseek, }; +static ssize_t icnss_stats_write(struct file *fp, const char __user *buf, + size_t count, loff_t *off) +{ + struct icnss_data *priv = + ((struct seq_file *)fp->private_data)->private; + int ret; + u32 val; + + ret = kstrtou32_from_user(buf, count, 0, &val); + if (ret) + return ret; + + if (ret == 0) + memset(&priv->stats, 0, sizeof(priv->stats)); + + return count; +} + +static int icnss_stats_show_state(struct seq_file *s, struct icnss_data *priv) +{ + int i; + int skip = 0; + unsigned long state; + + seq_printf(s, "\nState: 0x%lx(", priv->state); + for (i = 0, state = priv->state; state != 0; state >>= 1, i++) { + + if (!(state & 0x1)) + continue; + + if (skip++) + seq_puts(s, " | "); + + switch (i) { + case ICNSS_WLFW_QMI_CONNECTED: + seq_puts(s, "QMI CONN"); + continue; + case ICNSS_POWER_ON: + seq_puts(s, "POWER ON"); + continue; + case ICNSS_FW_READY: + seq_puts(s, "FW READY"); + continue; + case ICNSS_DRIVER_PROBED: + seq_puts(s, "DRIVER PROBED"); + continue; + case ICNSS_FW_TEST_MODE: + seq_puts(s, "FW TEST MODE"); + continue; + } + + seq_printf(s, "UNKNOWN-%d", i); + } + seq_puts(s, ")\n"); + + return 0; +} + +static int icnss_stats_show_capability(struct seq_file *s, + struct icnss_data *priv) +{ + if (test_bit(ICNSS_FW_READY, &priv->state)) { + seq_puts(s, "\n<---------------- FW Capability ----------------->\n"); + seq_printf(s, "Chip ID: 0x%x\n", priv->chip_info.chip_id); + seq_printf(s, "Chip family: 0x%x\n", + priv->chip_info.chip_family); + seq_printf(s, "Board ID: 0x%x\n", priv->board_info.board_id); + seq_printf(s, "SOC Info: 0x%x\n", priv->soc_info.soc_id); + seq_printf(s, "Firmware Version: 0x%x\n", + priv->fw_version_info.fw_version); + seq_printf(s, "Firmware Build Timestamp: %s\n", + priv->fw_version_info.fw_build_timestamp); + } + + return 0; +} + +static int icnss_stats_show_events(struct seq_file *s, struct icnss_data *priv) +{ + int i; + + seq_puts(s, "\n<----------------- Events stats ------------------->\n"); + seq_printf(s, "%24s %16s %16s\n", "Events", "Posted", "Processed"); + for (i = 0; i < ICNSS_DRIVER_EVENT_MAX; i++) + seq_printf(s, "%24s %16u %16u\n", + icnss_driver_event_to_str(i), + priv->stats.events[i].posted, + priv->stats.events[i].processed); + + return 0; +} + +static int icnss_stats_show_irqs(struct seq_file *s, struct icnss_data *priv) +{ + int i; + + seq_puts(s, "\n<------------------ IRQ stats ------------------->\n"); + seq_printf(s, "%4s %4s %8s %8s %8s %8s\n", "CE_ID", "IRQ", "Request", + "Free", "Enable", "Disable"); + for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) + seq_printf(s, "%4d: %4u %8u %8u %8u %8u\n", i, + priv->ce_irqs[i], priv->stats.ce_irqs[i].request, + priv->stats.ce_irqs[i].free, + priv->stats.ce_irqs[i].enable, + priv->stats.ce_irqs[i].disable); + + return 0; +} + +static int icnss_stats_show(struct seq_file *s, void *data) +{ +#define ICNSS_STATS_DUMP(_s, _priv, _x) \ + seq_printf(_s, "%24s: %u\n", #_x, _priv->stats._x) + + struct icnss_data *priv = s->private; + + ICNSS_STATS_DUMP(s, priv, ind_register_req); + ICNSS_STATS_DUMP(s, priv, ind_register_resp); + ICNSS_STATS_DUMP(s, priv, ind_register_err); + ICNSS_STATS_DUMP(s, priv, msa_info_req); + ICNSS_STATS_DUMP(s, priv, msa_info_resp); + ICNSS_STATS_DUMP(s, priv, msa_info_err); + ICNSS_STATS_DUMP(s, priv, msa_ready_req); + ICNSS_STATS_DUMP(s, priv, msa_ready_resp); + ICNSS_STATS_DUMP(s, priv, msa_ready_err); + ICNSS_STATS_DUMP(s, priv, msa_ready_ind); + ICNSS_STATS_DUMP(s, priv, cap_req); + ICNSS_STATS_DUMP(s, priv, cap_resp); + ICNSS_STATS_DUMP(s, priv, cap_err); + ICNSS_STATS_DUMP(s, priv, pin_connect_result); + ICNSS_STATS_DUMP(s, priv, cfg_req); + ICNSS_STATS_DUMP(s, priv, cfg_resp); + ICNSS_STATS_DUMP(s, priv, cfg_req_err); + ICNSS_STATS_DUMP(s, priv, mode_req); + ICNSS_STATS_DUMP(s, priv, mode_resp); + ICNSS_STATS_DUMP(s, priv, mode_req_err); + ICNSS_STATS_DUMP(s, priv, ini_req); + ICNSS_STATS_DUMP(s, priv, ini_resp); + ICNSS_STATS_DUMP(s, priv, ini_req_err); + + icnss_stats_show_irqs(s, priv); + + icnss_stats_show_capability(s, priv); + + icnss_stats_show_events(s, priv); + + icnss_stats_show_state(s, priv); + + return 0; +#undef ICNSS_STATS_DUMP +} + +static int icnss_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, icnss_stats_show, inode->i_private); +} + +static const struct file_operations icnss_stats_fops = { + .read = seq_read, + .write = icnss_stats_write, + .release = single_release, + .open = icnss_stats_open, + .owner = THIS_MODULE, + .llseek = seq_lseek, +}; + static int icnss_debugfs_create(struct icnss_data *priv) { int ret = 0; @@ -1838,8 +2122,11 @@ static int icnss_debugfs_create(struct icnss_data *priv) priv->root_dentry = root_dentry; - debugfs_create_file("test_mode", S_IRUSR | S_IWUSR, - root_dentry, priv, &icnss_test_mode_fops); + debugfs_create_file("test_mode", 0644, root_dentry, priv, + &icnss_test_mode_fops); + + debugfs_create_file("stats", 0644, root_dentry, priv, + &icnss_stats_fops); out: return ret;