Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59984de7 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: ipa3: Add SSR support for IPA3.1"

parents 01939add 014b2d9b
Loading
Loading
Loading
Loading
+58 −108
Original line number Diff line number Diff line
@@ -1647,39 +1647,40 @@ static void ipa3_destroy_imm(void *user1, int user2)
	ipahal_destroy_imm_cmd(user1);
}

static int ipa3_q6_pipe_delay(void)
static void ipa3_q6_pipe_delay(bool delay)
{
	int client_idx;
	int ep_idx;
	struct ipa_ep_cfg_ctrl ep_ctrl;

	memset(&ep_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
	ep_ctrl.ipa_ep_delay = delay;

	for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) {
		if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
			ep_idx = ipa3_get_ep_mapping(client_idx);
			if (ep_idx == -1)
				continue;

			ep_ctrl.ipa_ep_delay = 1;

			ipahal_write_reg_n_fields(IPA_ENDP_INIT_CTRL_n,
				ep_idx, &ep_ctrl);
		}
	}

	return 0;
}

static int ipa3_q6_avoid_holb(void)
static void ipa3_q6_avoid_holb(void)
{
	int ep_idx;
	int client_idx;
	struct ipa_ep_cfg_ctrl avoid_holb;
	struct ipa_ep_cfg_ctrl ep_suspend;
	struct ipa_ep_cfg_holb ep_holb;

	memset(&avoid_holb, 0, sizeof(avoid_holb));
	memset(&ep_suspend, 0, sizeof(ep_suspend));
	memset(&ep_holb, 0, sizeof(ep_holb));
	avoid_holb.ipa_ep_suspend = true;

	ep_suspend.ipa_ep_suspend = true;
	ep_holb.tmr_val = 0;
	ep_holb.en = 1;

	for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) {
		if (IPA_CLIENT_IS_Q6_CONS(client_idx)) {
@@ -1693,8 +1694,6 @@ static int ipa3_q6_avoid_holb(void)
			 * they are not valid, therefore, the above function
			 * will fail.
			 */
			ep_holb.tmr_val = 0;
			ep_holb.en = 1;
			ipahal_write_reg_n_fields(
				IPA_ENDP_INIT_HOL_BLOCK_TIMER_n,
				ep_idx, &ep_holb);
@@ -1702,11 +1701,11 @@ static int ipa3_q6_avoid_holb(void)
				IPA_ENDP_INIT_HOL_BLOCK_EN_n,
				ep_idx, &ep_holb);

			ipa3_cfg_ep_ctrl(ep_idx, &avoid_holb);
			ipahal_write_reg_n_fields(
				IPA_ENDP_INIT_CTRL_n,
				ep_idx, &ep_suspend);
		}
	}

	return 0;
}

static u32 ipa3_get_max_flt_rt_cmds(u32 num_pipes)
@@ -1785,8 +1784,7 @@ static int ipa3_q6_clean_q6_tables(void)
			 */
			cmd.is_read = false;
			cmd.skip_pipeline_clear = 0;
			cmd.pipeline_clear_options =
				IPAHAL_FULL_PIPELINE_CLEAR;
			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
			cmd.size = mem.size;
			cmd.system_addr = mem.phys_base;
			cmd.local_addr =
@@ -1810,8 +1808,7 @@ static int ipa3_q6_clean_q6_tables(void)

			cmd.is_read = false;
			cmd.skip_pipeline_clear = false;
			cmd.pipeline_clear_options =
				IPAHAL_FULL_PIPELINE_CLEAR;
			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
			cmd.size = mem.size;
			cmd.system_addr =  mem.phys_base;
			cmd.local_addr =
@@ -1839,8 +1836,7 @@ static int ipa3_q6_clean_q6_tables(void)
			 */
			cmd.is_read = false;
			cmd.skip_pipeline_clear = false;
			cmd.pipeline_clear_options =
				IPAHAL_FULL_PIPELINE_CLEAR;
			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
			cmd.size = mem.size;
			cmd.system_addr = mem.phys_base;
			cmd.local_addr =
@@ -1864,8 +1860,7 @@ static int ipa3_q6_clean_q6_tables(void)

			cmd.is_read = false;
			cmd.skip_pipeline_clear = 0;
			cmd.pipeline_clear_options =
				IPAHAL_FULL_PIPELINE_CLEAR;
			cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
			cmd.size = mem.size;
			cmd.system_addr =  mem.phys_base;
			cmd.local_addr =
@@ -1897,7 +1892,7 @@ static int ipa3_q6_clean_q6_tables(void)
		 index++) {
		cmd.is_read = false;
		cmd.skip_pipeline_clear = false;
		cmd.pipeline_clear_options = IPAHAL_FULL_PIPELINE_CLEAR;
		cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
		cmd.size = mem.size;
		cmd.system_addr =  mem.phys_base;
		cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
@@ -1919,7 +1914,7 @@ static int ipa3_q6_clean_q6_tables(void)

		cmd.is_read = false;
		cmd.skip_pipeline_clear = false;
		cmd.pipeline_clear_options = IPAHAL_FULL_PIPELINE_CLEAR;
		cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
		cmd.size = mem.size;
		cmd.system_addr =  mem.phys_base;
		cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
@@ -1945,8 +1940,7 @@ static int ipa3_q6_clean_q6_tables(void)
		 index++) {
		cmd.is_read = false;
		cmd.skip_pipeline_clear = false;
		cmd.pipeline_clear_options =
			IPAHAL_FULL_PIPELINE_CLEAR;
		cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
		cmd.size = mem.size;
		cmd.system_addr =  mem.phys_base;
		cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
@@ -1968,8 +1962,7 @@ static int ipa3_q6_clean_q6_tables(void)

		cmd.is_read = false;
		cmd.skip_pipeline_clear = false;
		cmd.pipeline_clear_options =
			IPAHAL_FULL_PIPELINE_CLEAR;
		cmd.pipeline_clear_options = IPAHAL_HPS_CLEAR;
		cmd.size = mem.size;
		cmd.system_addr =  mem.phys_base;
		cmd.local_addr = ipa3_ctx->smem_restricted_bytes +
@@ -2008,21 +2001,7 @@ bail_dma:
	return retval;
}

static void ipa3_q6_disable_agg_reg(
	struct ipahal_imm_cmd_register_write *reg_write, int ep_idx)
{
	struct ipahal_reg_valmask valmask;

	reg_write->skip_pipeline_clear = false;
	reg_write->pipeline_clear_options = IPAHAL_FULL_PIPELINE_CLEAR;
	reg_write->offset =
		ipahal_get_reg_n_ofst(IPA_ENDP_INIT_AGGR_n, ep_idx);
	ipahal_get_disable_aggr_valmask(&valmask);
	reg_write->value = valmask.val;
	reg_write->value_mask = valmask.mask;
}

static int ipa3_q6_set_ex_path_dis_agg(void)
static int ipa3_q6_set_ex_path_to_apps(void)
{
	int ep_idx;
	int client_idx;
@@ -2053,7 +2032,7 @@ static int ipa3_q6_set_ex_path_dis_agg(void)

			reg_write.skip_pipeline_clear = false;
			reg_write.pipeline_clear_options =
				IPAHAL_FULL_PIPELINE_CLEAR;
				IPAHAL_HPS_CLEAR;
			reg_write.offset =
				ipahal_get_reg_ofst(IPA_ENDP_STATUS_n);
			ipahal_get_status_ep_valmask(
@@ -2079,30 +2058,6 @@ static int ipa3_q6_set_ex_path_dis_agg(void)
		}
	}

	/* Disable AGGR on IPA->Q6 pipes */
	for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++) {
		if (IPA_CLIENT_IS_Q6_CONS(client_idx)) {

			ipa3_q6_disable_agg_reg(&reg_write,
				ipa3_get_ep_mapping(client_idx));
			cmd_pyld = ipahal_construct_imm_cmd(
				IPA_IMM_CMD_REGISTER_WRITE, &reg_write, false);
			if (!cmd_pyld) {
				IPAERR("fail construct register_write cmd\n");
				BUG();
			}

			desc[num_descs].opcode = ipahal_imm_cmd_get_opcode(
				IPA_IMM_CMD_REGISTER_WRITE);
			desc[num_descs].type = IPA_IMM_CMD_DESC;
			desc[num_descs].callback = ipa3_destroy_imm;
			desc[num_descs].user1 = cmd_pyld;
			desc[num_descs].pyld = cmd_pyld->data;
			desc[num_descs].len = cmd_pyld->len;
			num_descs++;
		}
	}

	/* Will wait 150msecs for IPA tag process completion */
	retval = ipa3_tag_process(desc, num_descs,
		msecs_to_jiffies(CLEANUP_TAG_PROCESS_TIMEOUT));
@@ -2127,70 +2082,65 @@ static int ipa3_q6_set_ex_path_dis_agg(void)
* ipa3_q6_cleanup() - A cleanup for all Q6 related configuration
*                    in IPA HW. This is performed in case of SSR.
*
* Return codes:
* 0: success
* This is a mandatory procedure, in case one of the steps fails, the
* AP needs to restart.
*/
int ipa3_q6_cleanup(void)
void ipa3_q6_cleanup(void)
{
	/* If uC has notified the APPS upon a ZIP engine error,
	 * APPS need to assert (This is a non recoverable error).
	 */
	if (ipa3_ctx->uc_ctx.uc_zip_error)
		BUG();
	IPADBG_LOW("ENTER\n");

	IPA_ACTIVE_CLIENTS_INC_SPECIAL("Q6");
	IPA_ACTIVE_CLIENTS_INC_SIMPLE();

	if (ipa3_q6_pipe_delay()) {
		IPAERR("Failed to delay Q6 pipes\n");
		BUG();
	}
	if (ipa3_q6_avoid_holb()) {
		IPAERR("Failed to set HOLB on Q6 pipes\n");
		BUG();
	}
	ipa3_q6_pipe_delay(true);
	ipa3_q6_avoid_holb();
	if (ipa3_q6_clean_q6_tables()) {
		IPAERR("Failed to clean Q6 tables\n");
		BUG();
	}
	if (ipa3_q6_set_ex_path_dis_agg()) {
		IPAERR("Failed to disable aggregation on Q6 pipes\n");
	if (ipa3_q6_set_ex_path_to_apps()) {
		IPAERR("Failed to redirect exceptions to APPS\n");
		BUG();
	}
	/* Remove delay from Q6 PRODs to avoid pending descriptors
	  * on pipe reset procedure
	  */
	ipa3_q6_pipe_delay(false);

	ipa3_ctx->q6_proxy_clk_vote_valid = true;

	return 0;
	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
	IPADBG_LOW("Exit with success\n");
}

/**
* ipa3_q6_pipe_reset() - A cleanup for the Q6 pipes
*                    in IPA HW. This is performed in case of SSR.
/*
 * ipa3_validate_q6_gsi_channel_empty() - Check if GSI channel related to Q6
 *  producer client is empty. This is used in case of SSR.
 *
* Return codes:
* 0: success
* This is a mandatory procedure, in case one of the steps fails, the
* AP needs to restart.
 * Q6 GSI channel emptiness is needed to garantee no descriptors with invalid
 *  info are injected into IPA RX from IPA_IF, while modem is restarting.
 */
int ipa3_q6_pipe_reset(void)
void ipa3_validate_q6_gsi_channel_empty(void)
{
	int client_idx;
	int res;

	IPADBG_LOW("ENTER\n");
	IPA_ACTIVE_CLIENTS_INC_SIMPLE();

	if (!ipa3_ctx->uc_ctx.uc_loaded) {
		IPAERR("uC is not loaded, won't reset Q6 pipes\n");
		return 0;
		IPAERR("uC is not loaded. Skipping\n");
		return;
	}

	for (client_idx = 0; client_idx < IPA_CLIENT_MAX; client_idx++)
		if (IPA_CLIENT_IS_Q6_CONS(client_idx) ||
			IPA_CLIENT_IS_Q6_PROD(client_idx)) {
			res = ipa3_uc_reset_pipe(client_idx);
			if (res)
		if (IPA_CLIENT_IS_Q6_PROD(client_idx)) {
			if (ipa3_uc_is_gsi_channel_empty(client_idx)) {
				IPAERR("fail to validate Q6 ch emptiness %d\n",
					client_idx);
				BUG();
				return;
			}
	return 0;
		}

	IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
	IPADBG_LOW("Exit with success\n");
}

static inline void ipa3_sram_set_canary(u32 *sram_mmio, int offset)
+29 −17
Original line number Diff line number Diff line
@@ -204,6 +204,10 @@
#define IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC (1000)
#define IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC (2000)

#define IPA_GSI_CHANNEL_EMPTY_MAX_RETRY 15
#define IPA_GSI_CHANNEL_EMPTY_SLEEP_MIN_USEC (1000)
#define IPA_GSI_CHANNEL_EMPTY_SLEEP_MAX_USEC (2000)

#define IPA_SLEEP_CLK_RATE_KHZ (32)

#define IPA_ACTIVE_CLIENTS_PREP_EP(log_info, client) \
@@ -1081,22 +1085,29 @@ struct ipa3_controller;
 * enum ipa3_hw_features - Values that represent the features supported in IPA HW
 * @IPA_HW_FEATURE_COMMON : Feature related to common operation of IPA HW
 * @IPA_HW_FEATURE_MHI : Feature related to MHI operation in IPA HW
 * @IPA_HW_FEATURE_POWER_COLLAPSE: Feature related to IPA Power collapse
 * @IPA_HW_FEATURE_WDI : Feature related to WDI operation in IPA HW
 * @IPA_HW_FEATURE_ZIP: Feature related to CMP/DCMP operation in IPA HW
*/
enum ipa3_hw_features {
	IPA_HW_FEATURE_COMMON		=	0x0,
	IPA_HW_FEATURE_MHI		=	0x1,
	IPA_HW_FEATURE_POWER_COLLAPSE	=	0x2,
	IPA_HW_FEATURE_WDI		=	0x3,
	IPA_HW_FEATURE_ZIP		=	0x4,
	IPA_HW_FEATURE_MAX		=	IPA_HW_NUM_FEATURES
};

/**
 * enum ipa3_hw_2_cpu_events - Values that represent HW event to be sent to CPU.
 * @IPA_HW_2_CPU_EVENT_NO_OP : No event present
 * @IPA_HW_2_CPU_EVENT_ERROR : Event specify a system error is detected by the
 *  device
 * @IPA_HW_2_CPU_EVENT_LOG_INFO : Event providing logging specific information
 */
enum ipa3_hw_2_cpu_events {
	IPA_HW_2_CPU_EVENT_NO_OP     =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
	IPA_HW_2_CPU_EVENT_ERROR     =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
	IPA_HW_2_CPU_EVENT_LOG_INFO  =
@@ -1110,7 +1121,8 @@ enum ipa3_hw_2_cpu_events {
 * @IPA_HW_DMA_ERROR : Unexpected DMA error
 * @IPA_HW_FATAL_SYSTEM_ERROR : HW has crashed and requires reset.
 * @IPA_HW_INVALID_OPCODE : Invalid opcode sent
 * @IPA_HW_ZIP_ENGINE_ERROR : ZIP engine error
 * @IPA_HW_INVALID_PARAMS : Invalid params for the requested command
 * @IPA_HW_GSI_CH_NOT_EMPTY_FAILURE : GSI channel emptiness validation failed
 */
enum ipa3_hw_errors {
	IPA_HW_ERROR_NONE              =
@@ -1123,12 +1135,14 @@ enum ipa3_hw_errors {
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
	IPA_HW_INVALID_OPCODE          =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 4),
	IPA_HW_ZIP_ENGINE_ERROR        =
	IPA_HW_INVALID_PARAMS        =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 5),
	IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 6),
	IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 7)
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 7),
	IPA_HW_GSI_CH_NOT_EMPTY_FAILURE =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 8)
};

/**
@@ -1162,14 +1176,13 @@ struct IpaHwSharedMemCommonMapping_t {
	u32 cmdParams;
	u32 cmdParams_hi;
	u8  responseOp;
	u8  reserved_09;
	u16 reserved_0B_0A;
	u8  reserved_0D;
	u16 reserved_0F_0E;
	u32 responseParams;
	u8  eventOp;
	u8  reserved_11;
	u16 reserved_13_12;
	u8  reserved_15;
	u16 reserved_17_16;
	u32 eventParams;
	u32 reserved_1B_18;
	u32 firstErrorAddress;
	u8  hwState;
	u8  warningCounter;
@@ -1355,7 +1368,6 @@ union IpaHwMhiDlUlSyncCmdData_t {
 * @uc_sram_mmio: Pointer to uC mapped memory
 * @pending_cmd: The last command sent waiting to be ACKed
 * @uc_status: The last status provided by the uC
 * @uc_zip_error: uC has notified the APPS upon a ZIP engine error
 * @uc_error_type: error type from uC error event
 * @uc_error_timestamp: tag timer sampled after uC crashed
 */
@@ -1371,7 +1383,6 @@ struct ipa3_uc_ctx {
	u32 uc_event_top_ofst;
	u32 pending_cmd;
	u32 uc_status;
	bool uc_zip_error;
	u32 uc_error_type;
	u32 uc_error_timestamp;
};
@@ -2293,8 +2304,8 @@ int ipa3_write_qmapid_wdi_pipe(u32 clnt_hdl, u8 qmap_id);
int ipa3_tag_process(struct ipa3_desc *desc, int num_descs,
		    unsigned long timeout);

int ipa3_q6_cleanup(void);
int ipa3_q6_pipe_reset(void);
void ipa3_q6_cleanup(void);
void ipa3_validate_q6_gsi_channel_empty(void);
int ipa3_init_q6_smem(void);

int ipa3_sps_connect_safe(struct sps_pipe *h, struct sps_connect *connect,
@@ -2304,6 +2315,7 @@ int ipa3_mhi_handle_ipa_config_req(struct ipa_config_req_msg_v01 *config_req);

int ipa3_uc_interface_init(void);
int ipa3_uc_reset_pipe(enum ipa_client_type ipa_client);
int ipa3_uc_is_gsi_channel_empty(enum ipa_client_type ipa_client);
int ipa3_uc_state_check(void);
int ipa3_uc_loaded_check(void);
void ipa3_uc_load_notify(void);
+3 −1
Original line number Diff line number Diff line
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
@@ -34,6 +34,8 @@
	pr_debug(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
#define IPAWANERR(fmt, args...) \
	pr_err(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)
#define IPAWANINFO(fmt, args...) \
	pr_info(DEV_NAME " %s:%d " fmt, __func__, __LINE__, ## args)

extern struct ipa3_qmi_context *ipa3_qmi_ctx;

+96 −11
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@
#include <linux/delay.h>

#define IPA_RAM_UC_SMEM_SIZE 128
#define IPA_HW_INTERFACE_VERSION     0x0111
#define IPA_HW_INTERFACE_VERSION     0x2000
#define IPA_PKT_FLUSH_TO_US 100
#define IPA_UC_POLL_SLEEP_USEC 100
#define IPA_UC_POLL_MAX_RETRY 10000
@@ -40,6 +40,7 @@
 * IPA_CPU_2_HW_CMD_CLK_UNGATE : CPU instructs HW to goto Clock Ungated state.
 * IPA_CPU_2_HW_CMD_MEMCPY : CPU instructs HW to do memcopy using QMB.
 * IPA_CPU_2_HW_CMD_RESET_PIPE : Command to reset a pipe - SW WA for a HW bug.
 * IPA_CPU_2_HW_CMD_GSI_CH_EMPTY : Command to check for GSI channel emptiness.
 */
enum ipa3_cpu_2_hw_commands {
	IPA_CPU_2_HW_CMD_NO_OP                     =
@@ -62,20 +63,29 @@ enum ipa3_cpu_2_hw_commands {
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 8),
	IPA_CPU_2_HW_CMD_REG_WRITE                 =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 9),
	IPA_CPU_2_HW_CMD_GSI_CH_EMPTY              =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 10),
};

/**
 * enum ipa3_hw_2_cpu_responses -  Values that represent common HW responses
 *  to CPU commands.
 * @IPA_HW_2_CPU_RESPONSE_NO_OP : No operation response
 * @IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED : HW shall send this command once
 *  boot sequence is completed and HW is ready to serve commands from CPU
 * @IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED: Response to CPU commands
 * @IPA_HW_2_CPU_RESPONSE_DEBUG_GET_INFO : Response to
 *  IPA_CPU_2_HW_CMD_DEBUG_GET_INFO command
 */
enum ipa3_hw_2_cpu_responses {
	IPA_HW_2_CPU_RESPONSE_NO_OP          =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 0),
	IPA_HW_2_CPU_RESPONSE_INIT_COMPLETED =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 1),
	IPA_HW_2_CPU_RESPONSE_CMD_COMPLETED  =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 2),
	IPA_HW_2_CPU_RESPONSE_DEBUG_GET_INFO =
		FEATURE_ENUM_VAL(IPA_HW_FEATURE_COMMON, 3),
};

/**
@@ -152,6 +162,23 @@ union IpaHwUpdateFlagsCmdData_t {
	u32 raw32b;
};

/**
 * union IpaHwChkChEmptyCmdData_t -  Structure holding the parameters for
 *  IPA_CPU_2_HW_CMD_GSI_CH_EMPTY command. Parameters are sent as 32b
 *  immediate parameters.
 * @ee_n : EE owner of the channel
 * @vir_ch_id : GSI virtual channel ID of the channel to checked of emptiness
 * @reserved_02_04 : Reserved
 */
union IpaHwChkChEmptyCmdData_t {
	struct IpaHwChkChEmptyCmdParams_t {
		u8 ee_n;
		u8 vir_ch_id;
		u16 reserved_02_04;
	} __packed params;
	u32 raw32b;
} __packed;

/**
 * When resource group 10 limitation mitigation is enabled, uC send
 * cmd should be able to run in interrupt context, so using spin lock
@@ -186,14 +213,26 @@ const char *ipa_hw_error_str(enum ipa3_hw_errors err_type)
	case IPA_HW_INVALID_DOORBELL_ERROR:
		str = "IPA_HW_INVALID_DOORBELL_ERROR";
		break;
	case IPA_HW_DMA_ERROR:
		str = "IPA_HW_DMA_ERROR";
		break;
	case IPA_HW_FATAL_SYSTEM_ERROR:
		str = "IPA_HW_FATAL_SYSTEM_ERROR";
		break;
	case IPA_HW_INVALID_OPCODE:
		str = "IPA_HW_INVALID_OPCODE";
		break;
	case IPA_HW_ZIP_ENGINE_ERROR:
		str = "IPA_HW_ZIP_ENGINE_ERROR";
	case IPA_HW_INVALID_PARAMS:
		str = "IPA_HW_INVALID_PARAMS";
		break;
	case IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE:
		str = "IPA_HW_CONS_DISABLE_CMD_GSI_STOP_FAILURE";
		break;
	case IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE:
		str = "IPA_HW_PROD_DISABLE_CMD_GSI_STOP_FAILURE";
		break;
	case IPA_HW_GSI_CH_NOT_EMPTY_FAILURE:
		str = "IPA_HW_GSI_CH_NOT_EMPTY_FAILURE";
		break;
	default:
		str = "INVALID ipa_hw_errors type";
@@ -324,10 +363,6 @@ static void ipa3_uc_event_handler(enum ipa_irq_type interrupt,
			ipa_hw_error_str(evt.params.errorType));
		ipa3_ctx->uc_ctx.uc_failed = true;
		ipa3_ctx->uc_ctx.uc_error_type = evt.params.errorType;
		if (evt.params.errorType == IPA_HW_ZIP_ENGINE_ERROR) {
			IPAERR("IPA has encountered a ZIP engine error\n");
			ipa3_ctx->uc_ctx.uc_zip_error = true;
		}
		ipa3_ctx->uc_ctx.uc_error_timestamp =
			ipahal_read_reg(IPA_TAG_TIMER);
		BUG();
@@ -466,7 +501,7 @@ static int ipa3_uc_send_cmd_64b_param(u32 cmd_lo, u32 cmd_hi, u32 opcode,
	unsigned long flags;
	int retries = 0;

send_cmd:
send_cmd_lock:
	IPA3_UC_LOCK(flags);

	if (ipa3_uc_state_check()) {
@@ -474,7 +509,7 @@ send_cmd:
		IPA3_UC_UNLOCK(flags);
		return -EBADF;
	}

send_cmd:
	if (ipa3_ctx->apply_rg10_wa) {
		if (!polling_mode)
			IPADBG("Overriding mode to polling mode\n");
@@ -563,6 +598,25 @@ send_cmd:
			/* sleep for short period to flush IPA */
			usleep_range(IPA_GSI_CHANNEL_STOP_SLEEP_MIN_USEC,
				IPA_GSI_CHANNEL_STOP_SLEEP_MAX_USEC);
			goto send_cmd_lock;
		}

		if (ipa3_ctx->uc_ctx.uc_status ==
			IPA_HW_GSI_CH_NOT_EMPTY_FAILURE) {
			retries++;
			if (retries >= IPA_GSI_CHANNEL_EMPTY_MAX_RETRY) {
				IPAERR("Failed after %d tries\n", retries);
				IPA3_UC_UNLOCK(flags);
				return -EFAULT;
			}
			if (ipa3_ctx->apply_rg10_wa)
				udelay(
				IPA_GSI_CHANNEL_EMPTY_SLEEP_MAX_USEC / 2 +
				IPA_GSI_CHANNEL_EMPTY_SLEEP_MIN_USEC / 2);
			else
				usleep_range(
				IPA_GSI_CHANNEL_EMPTY_SLEEP_MIN_USEC,
				IPA_GSI_CHANNEL_EMPTY_SLEEP_MAX_USEC);
			goto send_cmd;
		}

@@ -784,6 +838,37 @@ int ipa3_uc_reset_pipe(enum ipa_client_type ipa_client)
	return ret;
}

int ipa3_uc_is_gsi_channel_empty(enum ipa_client_type ipa_client)
{
	struct ipa_gsi_ep_config *gsi_ep_info;
	union IpaHwChkChEmptyCmdData_t cmd;
	int ret;

	gsi_ep_info = ipa3_get_gsi_ep_info(ipa3_get_ep_mapping(ipa_client));
	if (!gsi_ep_info) {
		IPAERR("Invalid IPA ep index\n");
		return 0;
	}

	if (ipa3_uc_state_check()) {
		IPADBG("uC cannot be used to validate ch emptiness clnt=%d\n"
			, ipa_client);
		return 0;
	}

	cmd.params.ee_n = gsi_ep_info->ee;
	cmd.params.vir_ch_id = gsi_ep_info->ipa_gsi_chan_num;

	IPADBG("uC emptiness check for IPA GSI Channel %d\n",
	       gsi_ep_info->ipa_gsi_chan_num);

	ret = ipa3_uc_send_cmd(cmd.raw32b, IPA_CPU_2_HW_CMD_GSI_CH_EMPTY, 0,
			      false, 10*HZ);

	return ret;
}


/**
 * ipa3_uc_notify_clk_state() - notify to uC of clock enable / disable
 * @enabled: true if clock are enabled
+47 −40
Original line number Diff line number Diff line
@@ -2290,9 +2290,12 @@ static int ipa3_ssr_notifier_cb(struct notifier_block *this,
			   unsigned long code,
			   void *data)
{
	if (ipa3_rmnet_ctx.ipa_rmnet_ssr) {
		if (SUBSYS_BEFORE_SHUTDOWN == code) {
			pr_info("IPA received MPSS BEFORE_SHUTDOWN\n");
	if (!ipa3_rmnet_ctx.ipa_rmnet_ssr)
		return NOTIFY_DONE;

	switch (code) {
	case SUBSYS_BEFORE_SHUTDOWN:
		IPAWANINFO("IPA received MPSS BEFORE_SHUTDOWN\n");
		atomic_set(&rmnet_ipa3_ctx->is_ssr, 1);
		ipa3_q6_cleanup();
		if (IPA_NETDEV())
@@ -2300,37 +2303,41 @@ static int ipa3_ssr_notifier_cb(struct notifier_block *this,
		ipa3_qmi_stop_workqueues();
		ipa3_wan_ioctl_stop_qmi_messages();
		ipa_stop_polling_stats();
			atomic_set(&rmnet_ipa3_ctx->is_ssr, 1);
		if (atomic_read(&rmnet_ipa3_ctx->is_initialized))
			platform_driver_unregister(&rmnet_ipa_driver);
			pr_info("IPA BEFORE_SHUTDOWN handling is complete\n");
			return NOTIFY_DONE;
		}
		if (SUBSYS_AFTER_SHUTDOWN == code) {
			pr_info("IPA received MPSS AFTER_SHUTDOWN\n");
		IPAWANINFO("IPA BEFORE_SHUTDOWN handling is complete\n");
		break;
	case SUBSYS_AFTER_SHUTDOWN:
		IPAWANINFO("IPA Received MPSS AFTER_SHUTDOWN\n");
		if (atomic_read(&rmnet_ipa3_ctx->is_ssr))
				ipa3_q6_pipe_reset();
			pr_info("IPA AFTER_SHUTDOWN handling is complete\n");
			return NOTIFY_DONE;
		}
		if (SUBSYS_AFTER_POWERUP == code) {
			pr_info("IPA received MPSS AFTER_POWERUP\n");
			if (!atomic_read(&rmnet_ipa3_ctx->is_initialized)
				&& atomic_read(&rmnet_ipa3_ctx->is_ssr))
				platform_driver_register(&rmnet_ipa_driver);
			pr_info("IPA AFTER_POWERUP handling is complete\n");
			return NOTIFY_DONE;
		}
		if (SUBSYS_BEFORE_POWERUP == code) {
			pr_info("IPA received MPSS BEFORE_POWERUP\n");
			ipa3_validate_q6_gsi_channel_empty();
		IPAWANINFO("IPA AFTER_SHUTDOWN handling is complete\n");
		break;
	case SUBSYS_BEFORE_POWERUP:
		IPAWANINFO("IPA received MPSS BEFORE_POWERUP\n");
		if (atomic_read(&rmnet_ipa3_ctx->is_ssr))
			/* clean up cached QMI msg/handlers */
			ipa3_qmi_service_exit();
		/*hold a proxy vote for the modem*/
		ipa3_proxy_clk_vote();
			pr_info("IPA BEFORE_POWERUP handling is complete\n");
			return NOTIFY_DONE;
		}
		IPAWANINFO("IPA BEFORE_POWERUP handling is complete\n");
		break;
	case SUBSYS_AFTER_POWERUP:
		IPAWANINFO("%s:%d IPA received MPSS AFTER_POWERUP\n",
			__func__, __LINE__);
		if (!atomic_read(&rmnet_ipa3_ctx->is_initialized) &&
		       atomic_read(&rmnet_ipa3_ctx->is_ssr))
			platform_driver_register(&rmnet_ipa_driver);

		IPAWANINFO("IPA AFTER_POWERUP handling is complete\n");
		break;
	default:
		IPAWANDBG("Unsupported subsys notification, IPA received: %lu",
			code);
		break;
	}

	IPAWANDBG("Exit\n");
	return NOTIFY_DONE;
}