diff --git a/hif/inc/hif.h b/hif/inc/hif.h index ff27fe7a0c..a91da242a7 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -1351,4 +1351,23 @@ void hif_srng_init_phase(struct hif_opaque_softc *hif_ctx, { } #endif /* FORCE_WAKE */ + +#ifdef HIF_CE_LOG_INFO +/** + * hif_log_ce_info() - API to log ce info + * @hif_ctx: hif opaque handle + * @data: hang event data buffer + * @offset: offset at which data needs to be written + * + * Return: None + */ +void hif_log_ce_info(struct hif_opaque_softc *hif_ctx, uint8_t *data, + unsigned int *offset); +#else +static inline +void hif_log_ce_info(struct hif_opaque_softc *hif_ctx, uint8_t *data, + unsigned int *offset) +{ +} +#endif #endif /* _HIF_H_ */ diff --git a/hif/src/ce/ce_api.h b/hif/src/ce/ce_api.h index 5ff48e0d16..7b6b97be48 100644 --- a/hif/src/ce/ce_api.h +++ b/hif/src/ce/ce_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -546,6 +546,8 @@ struct ce_ops { void (*ce_prepare_shadow_register_v2_cfg)(struct hif_softc *scn, struct pld_shadow_reg_v2_cfg **shadow_config, int *num_shadow_registers_configured); + int (*ce_get_index_info)(struct hif_softc *scn, void *ce_state, + struct ce_index *info); }; int hif_ce_bus_early_suspend(struct hif_softc *scn); diff --git a/hif/src/ce/ce_main.c b/hif/src/ce/ce_main.c index 4bcec20b4a..bb09b2207e 100644 --- a/hif/src/ce/ce_main.c +++ b/hif/src/ce/ce_main.c @@ -48,6 +48,9 @@ #define PCIE_ACCESS_DUMP 4 #endif #include "mp_dev.h" +#ifdef HIF_CE_LOG_INFO +#include "qdf_hang_event_notifier.h" +#endif #if (defined(QCA_WIFI_QCA8074) || defined(QCA_WIFI_QCA6290) || \ defined(QCA_WIFI_QCA6018) || defined(QCA_WIFI_QCA5018)) && \ @@ -4197,3 +4200,59 @@ int hif_get_wake_ce_id(struct hif_softc *scn, uint8_t *ce_id) return 0; } + +#ifdef HIF_CE_LOG_INFO +/** + * ce_get_index_info(): Get CE index info + * @scn: HIF Context + * @ce_state: CE opaque handle + * @info: CE info + * + * Return: 0 for success and non zero for failure + */ +static +int ce_get_index_info(struct hif_softc *scn, void *ce_state, + struct ce_index *info) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + + return hif_state->ce_services->ce_get_index_info(scn, ce_state, info); +} + +void hif_log_ce_info(struct hif_opaque_softc *hif_ctx, uint8_t *data, + unsigned int *offset) +{ + struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); + struct hang_event_info info = {0}; + static uint32_t tracked_ce = BIT(CE_ID_1) | BIT(CE_ID_2) | + BIT(CE_ID_3) | BIT(CE_ID_4) | BIT(CE_ID_9) | BIT(CE_ID_10); + uint8_t curr_index = 0; + uint8_t i; + uint16_t size; + + info.active_tasklet_count = qdf_atomic_read(&scn->active_tasklet_cnt); + info.active_grp_tasklet_cnt = + qdf_atomic_read(&scn->active_grp_tasklet_cnt); + + for (i = 0; i < scn->ce_count; i++) { + if (!(tracked_ce & BIT(i)) || !scn->ce_id_to_state[i]) + continue; + + if (ce_get_index_info(scn, scn->ce_id_to_state[i], + &info.ce_info[curr_index])) + continue; + + curr_index++; + } + + info.ce_count = curr_index; + size = sizeof(info) - + (CE_COUNT_MAX - info.ce_count) * sizeof(struct ce_index); + + QDF_HANG_EVT_SET_HDR(&info.tlv_header, HANG_EVT_TAG_CE_INFO, + size - QDF_HANG_EVENT_TLV_HDR_SIZE); + + qdf_mem_copy(data + *offset, &info, size); + *offset = *offset + size; +} +#endif diff --git a/hif/src/ce/ce_main.h b/hif/src/ce/ce_main.h index f3feb76158..00ca3ec3e7 100644 --- a/hif/src/ce/ce_main.h +++ b/hif/src/ce/ce_main.h @@ -265,6 +265,49 @@ struct ce_info { #endif #endif +/** + * struct ce_index + * + * @id: CE id + * @sw_index: sw index + * @write_index: write index + * @hp: ring head pointer + * @tp: ring tail pointer + * @status_hp: status ring head pointer + * @status_tp: status ring tail pointer + */ +struct ce_index { + uint8_t id; + union { + struct { + uint16_t sw_index; + uint16_t write_index; + } legacy_info; + struct { + uint16_t hp; + uint16_t tp; + uint16_t status_hp; + uint16_t status_tp; + } srng_info; + } u; +} qdf_packed; + +/** + * struct hang_event_info + * + * @tlv_header: tlv header + * @active_tasklet_count: active tasklet count + * @active_grp_tasklet_cnt: active grp tasklet count + * @ce_info: CE info + */ +struct hang_event_info { + uint32_t tlv_header; + uint8_t active_tasklet_count; + uint8_t active_grp_tasklet_cnt; + uint8_t ce_count; + struct ce_index ce_info[CE_COUNT_MAX]; +} qdf_packed; + void hif_ce_stop(struct hif_softc *scn); int hif_dump_ce_registers(struct hif_softc *scn); void diff --git a/hif/src/ce/ce_service_legacy.c b/hif/src/ce/ce_service_legacy.c index 2a473b63b4..5596d0d75c 100644 --- a/hif/src/ce/ce_service_legacy.c +++ b/hif/src/ce/ce_service_legacy.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -1266,6 +1266,34 @@ static bool ce_check_int_watermark(struct CE_state *CE_state, return false; } +#ifdef HIF_CE_LOG_INFO +/** + * ce_get_index_info_legacy(): Get CE index info + * @scn: HIF Context + * @ce_state: CE opaque handle + * @info: CE info + * + * Return: 0 for success and non zero for failure + */ +static +int ce_get_index_info_legacy(struct hif_softc *scn, void *ce_state, + struct ce_index *info) +{ + struct CE_state *state = (struct CE_state *)ce_state; + + info->id = state->id; + if (state->src_ring) { + info->u.legacy_info.sw_index = state->src_ring->sw_index; + info->u.legacy_info.write_index = state->src_ring->write_index; + } else if (state->dest_ring) { + info->u.legacy_info.sw_index = state->dest_ring->sw_index; + info->u.legacy_info.write_index = state->dest_ring->write_index; + } + + return 0; +} +#endif + struct ce_ops ce_service_legacy = { .ce_get_desc_size = ce_get_desc_size_legacy, .ce_ring_setup = ce_ring_setup_legacy, @@ -1282,6 +1310,10 @@ struct ce_ops ce_service_legacy = { .ce_send_entries_done_nolock = ce_send_entries_done_nolock_legacy, .ce_prepare_shadow_register_v2_cfg = ce_prepare_shadow_register_v2_cfg_legacy, +#ifdef HIF_CE_LOG_INFO + .ce_get_index_info = + ce_get_index_info_legacy, +#endif }; struct ce_ops *ce_services_legacy() diff --git a/hif/src/ce/ce_service_srng.c b/hif/src/ce/ce_service_srng.c index 55584a7015..6fb9ed2bc4 100644 --- a/hif/src/ce/ce_service_srng.c +++ b/hif/src/ce/ce_service_srng.c @@ -941,6 +941,43 @@ static void ce_prepare_shadow_register_v2_cfg_srng(struct hif_softc *scn, num_shadow_registers_configured); } +#ifdef HIF_CE_LOG_INFO +/** + * ce_get_index_info_srng(): Get CE index info + * @scn: HIF Context + * @ce_state: CE opaque handle + * @info: CE info + * + * Return: 0 for success and non zero for failure + */ +static +int ce_get_index_info_srng(struct hif_softc *scn, void *ce_state, + struct ce_index *info) +{ + struct CE_state *CE_state = (struct CE_state *)ce_state; + uint32_t tp, hp; + + info->id = CE_state->id; + if (CE_state->src_ring) { + hal_get_sw_hptp(scn->hal_soc, CE_state->src_ring->srng_ctx, + &tp, &hp); + info->u.srng_info.tp = tp; + info->u.srng_info.hp = hp; + } else if (CE_state->dest_ring && CE_state->status_ring) { + hal_get_sw_hptp(scn->hal_soc, CE_state->status_ring->srng_ctx, + &tp, &hp); + info->u.srng_info.status_tp = tp; + info->u.srng_info.status_hp = hp; + hal_get_sw_hptp(scn->hal_soc, CE_state->dest_ring->srng_ctx, + &tp, &hp); + info->u.srng_info.tp = tp; + info->u.srng_info.hp = hp; + } + + return 0; +} +#endif + static struct ce_ops ce_service_srng = { .ce_get_desc_size = ce_get_desc_size_srng, .ce_ring_setup = ce_ring_setup_srng, @@ -957,6 +994,10 @@ static struct ce_ops ce_service_srng = { .ce_send_entries_done_nolock = ce_send_entries_done_nolock_srng, .ce_prepare_shadow_register_v2_cfg = ce_prepare_shadow_register_v2_cfg_srng, +#ifdef HIF_CE_LOG_INFO + .ce_get_index_info = + ce_get_index_info_srng, +#endif }; struct ce_ops *ce_services_srng()