Skip to content

Commit

Permalink
wifi: iwlwifi: implement product reset for TOP errors
Browse files Browse the repository at this point in the history
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: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20241231135726.5b0f846d3e13.Ia14ccac38ac3d48adf5f341b17c7e34ccc41c065@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg committed Jan 13, 2025
1 parent 61863fa commit 9673c35
Show file tree
Hide file tree
Showing 9 changed files with 309 additions and 18 deletions.
6 changes: 3 additions & 3 deletions drivers/net/wireless/intel/iwlwifi/fw/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ 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,
const guid_t *guid)
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;

Expand Down
33 changes: 31 additions & 2 deletions drivers/net/wireless/intel/iwlwifi/fw/acpi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
*
Expand Down Expand Up @@ -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);
}
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/iwl-debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*****************************************************************************/
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 10 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/iwl-trans.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/mvm/fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/pcie/drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -1480,6 +1480,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)

trans_pcie = IWL_TRANS_GET_PCIE_TRANS(iwl_trans);

iwl_trans_pcie_check_product_reset_status(pdev);
iwl_trans_pcie_check_product_reset_mode(pdev);

/*
* Let's try to grab NIC access early here. Sometimes, NICs may
* fail to initialize, and if that happens it's better if we see
Expand Down
3 changes: 3 additions & 0 deletions drivers/net/wireless/intel/iwlwifi/pcie/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,9 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans);
__cond_lock(nic_access_nobh, \
likely(__iwl_trans_pcie_grab_nic_access(trans)))

void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev);
void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev);

/*****************************************************
* RX
******************************************************/
Expand Down
4 changes: 3 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/pcie/rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -2297,7 +2297,9 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
if (inta_hw & MSIX_HW_INT_CAUSES_REG_TOP_FATAL_ERR) {
IWL_ERR(trans, "TOP Fatal error detected, inta_hw=0x%x.\n",
inta_hw);
/* TODO: PLDR flow required here for >= Bz */
if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ)
iwl_trans_pcie_reset(trans,
IWL_RESET_MODE_PROD_RESET);
}

/* Error detected by uCode */
Expand Down
Loading

0 comments on commit 9673c35

Please sign in to comment.