Commit 9673c354 authored by Johannes Berg's avatar Johannes Berg
Browse files

wifi: iwlwifi: implement product reset for TOP errors



The TOP is a shared (between BT and WiFi) hardware component,
and if it has an error we need to reset the whole device, i.e.
both BT and WiFi. This is achieved by calling a specific ACPI
DSM (device-specific method) with the right arguments before
doing a reset via the object referenced by _PRR.

Since this is needed here, but a function reset will always do
better than just re-enumerating the bus in case of errors, we
can always try to at least do a function reset and do the full
product reset only when needed for TOP errors.

Also, for some Bz and Sc devices where BT is PCIe/IOSF as well,
find the BT device and unbind that device as well so the BT
driver can recover from the reset that's going to happen,
rather than having to somehow detect that the device was reset.

Also add - currently unused - the function reset mode, this is
going to get used in the upcoming escalation model.

Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarMiri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20241231135726.5b0f846d3e13.Ia14ccac38ac3d48adf5f341b17c7e34ccc41c065@changeid


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 61863fab
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -79,8 +79,8 @@ static void *iwl_acpi_get_object(struct device *dev, acpi_string method)
 * method (DSM) interface. The returned acpi object must be freed by calling
 * function.
 */
static void *iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
				     union acpi_object *args,
union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,
					   int func, union acpi_object *args,
					   const guid_t *guid)
{
	union acpi_object *obj;
+31 −2
Original line number Diff line number Diff line
@@ -109,6 +109,30 @@

#define ACPI_DSM_REV 0

#define DSM_INTERNAL_FUNC_GET_PLAT_INFO	1
/* TBD: VPRO is BIT(0) in the result, but what's the result? */

#define DSM_INTERNAL_FUNC_PRODUCT_RESET	2

/* DSM_INTERNAL_FUNC_PRODUCT_RESET - product reset (aka "PLDR") */
enum iwl_dsm_internal_product_reset_cmds {
	DSM_INTERNAL_PLDR_CMD_GET_MODE = 1,
	DSM_INTERNAL_PLDR_CMD_SET_MODE = 2,
	DSM_INTERNAL_PLDR_CMD_GET_STATUS = 3,
};

enum iwl_dsm_internal_product_reset_mode {
	DSM_INTERNAL_PLDR_MODE_EN_PROD_RESET	= BIT(0),
	DSM_INTERNAL_PLDR_MODE_EN_WIFI_FLR	= BIT(1),
	DSM_INTERNAL_PLDR_MODE_EN_BT_OFF_ON	= BIT(2),
};

struct iwl_dsm_internal_product_reset_cmd {
	/* cmd is from enum iwl_dsm_internal_product_reset_cmds */
	u16 cmd;
	u16 value;
} __packed;

#define IWL_ACPI_WBEM_REV0_MASK (BIT(0) | BIT(1))
#define IWL_ACPI_WBEM_REVISION 0

@@ -118,6 +142,10 @@ struct iwl_fw_runtime;

extern const guid_t iwl_guid;

union acpi_object *iwl_acpi_get_dsm_object(struct device *dev, int rev,
					   int func, union acpi_object *args,
					   const guid_t *guid);

/**
 * iwl_acpi_get_mcc - read MCC from ACPI, if available
 *
@@ -166,8 +194,9 @@ int iwl_acpi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value);

#else /* CONFIG_ACPI */

static inline void *iwl_acpi_get_dsm_object(struct device *dev, int rev,
					    int func, union acpi_object *args)
static inline union acpi_object *
iwl_acpi_get_dsm_object(struct device *dev, int rev, int func,
			union acpi_object *args, const guid_t *guid)
{
	return ERR_PTR(-ENOENT);
}
+2 −1
Original line number Diff line number Diff line
@@ -2,7 +2,7 @@
/******************************************************************************
 *
 * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved.
 * Copyright(c) 2018 - 2021 Intel Corporation
 * Copyright(c) 2018 - 2021, 2024 Intel Corporation
 *
 * Portions of this file are derived from the ipw3945 project.
 *****************************************************************************/
@@ -209,6 +209,7 @@ do { \
#define IWL_DEBUG_RADIO(p, f, a...)	IWL_DEBUG(p, IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_DEV_RADIO(p, f, a...)	IWL_DEBUG_DEV(p, IWL_DL_RADIO, f, ## a)
#define IWL_DEBUG_POWER(p, f, a...)	IWL_DEBUG(p, IWL_DL_POWER, f, ## a)
#define IWL_DEBUG_DEV_POWER(p, f, a...)	IWL_DEBUG_DEV(p, IWL_DL_POWER, f, ## a)
#define IWL_DEBUG_11H(p, f, a...)	IWL_DEBUG(p, IWL_DL_11H, f, ## a)
#define IWL_DEBUG_TPT(p, f, a...)	IWL_DEBUG(p, IWL_DL_TPT, f, ## a)
#define IWL_DEBUG_WOWLAN(p, f, a...)	IWL_DEBUG(p, IWL_DL_WOWLAN, f, ## a)
+10 −1
Original line number Diff line number Diff line
@@ -1245,7 +1245,16 @@ static inline bool iwl_trans_is_hw_error_value(u32 val)
 *****************************************************/
int __must_check iwl_pci_register_driver(void);
void iwl_pci_unregister_driver(void);
void iwl_trans_pcie_remove(struct iwl_trans *trans, bool rescan);

/* Note: order matters */
enum iwl_reset_mode {
	IWL_RESET_MODE_REMOVE_ONLY,
	IWL_RESET_MODE_RESCAN,
	IWL_RESET_MODE_FUNC_RESET,
	IWL_RESET_MODE_PROD_RESET,
};

void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode);

int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans,
			     struct iwl_host_cmd *cmd);
+2 −1
Original line number Diff line number Diff line
@@ -642,7 +642,8 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm)
		/* if we needed reset then fail here, but notify and remove */
		if (mvm->fw_product_reset) {
			iwl_mei_alive_notif(false);
			iwl_trans_pcie_remove(mvm->trans, true);
			iwl_trans_pcie_reset(mvm->trans,
					     IWL_RESET_MODE_RESCAN);
		}

		goto error;
Loading