Skip to content

Commit

Permalink
mei: me: emit error only if reset was unexpected
Browse files Browse the repository at this point in the history
GSC devices perform legal firmware initiated resets due to state transition
that may appear as unexpected to the driver. Lower the log level for those
devices to debug level and save the firmware status registers.
When the device comes out of the reset it is possible to check whether the
resets was due to a firmware error or an exception
and only than produce a warning.

Signed-off-by: Vitaly Lubart <vitaly.lubart@intel.com>
Signed-off-by: Tomas Winkler <tomas.winkler@intel.com>
Link: https://lore.kernel.org/r/20231015080540.95922-1-tomas.winkler@intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
vlubart authored and gregkh committed Oct 18, 2023
1 parent 3b54a11 commit 34a674e
Show file tree
Hide file tree
Showing 4 changed files with 133 additions and 5 deletions.
3 changes: 3 additions & 0 deletions drivers/misc/mei/hw-me-regs.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@
# define PCI_CFG_HFS_1_OPMODE_MSK 0xf0000 /* OP MODE Mask: SPS <= 4.0 */
# define PCI_CFG_HFS_1_OPMODE_SPS 0xf0000 /* SPS SKU : SPS <= 4.0 */
#define PCI_CFG_HFS_2 0x48
# define PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR 0x1000000 /* CMoff->CMx wake after an error */
# define PCI_CFG_HFS_2_PM_CM_RESET_ERROR 0x5000000 /* CME reset due to exception */
# define PCI_CFG_HFS_2_PM_EVENT_MASK 0xf000000
#define PCI_CFG_HFS_3 0x60
# define PCI_CFG_HFS_3_FW_SKU_MSK 0x00000070
# define PCI_CFG_HFS_3_FW_SKU_IGN 0x00000000
Expand Down
61 changes: 58 additions & 3 deletions drivers/misc/mei/hw-me.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,11 +443,22 @@ static void mei_gsc_pxp_check(struct mei_device *dev)
struct mei_me_hw *hw = to_me_hw(dev);
u32 fwsts5 = 0;

if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
if (!kind_is_gsc(dev) && !kind_is_gscfi(dev))
return;

hw->read_fws(dev, PCI_CFG_HFS_5, &fwsts5);
trace_mei_pci_cfg_read(dev->dev, "PCI_CFG_HFS_5", PCI_CFG_HFS_5, fwsts5);

if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_DEFAULT)
dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_PERFORMED;
} else {
dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DEFAULT;
}

if (dev->pxp_mode == MEI_DEV_PXP_DEFAULT)
return;

if ((fwsts5 & GSC_CFG_HFS_5_BOOT_TYPE_MSK) == GSC_CFG_HFS_5_BOOT_TYPE_PXP) {
dev_dbg(dev->dev, "pxp mode is ready 0x%08x\n", fwsts5);
dev->pxp_mode = MEI_DEV_PXP_READY;
Expand Down Expand Up @@ -482,6 +493,43 @@ static int mei_me_hw_ready_wait(struct mei_device *dev)
return 0;
}

/**
* mei_me_check_fw_reset - check for the firmware reset error and exception conditions
*
* @dev: mei device
*/
static void mei_me_check_fw_reset(struct mei_device *dev)
{
struct mei_fw_status fw_status;
char fw_sts_str[MEI_FW_STATUS_STR_SZ] = {0};
int ret;
u32 fw_pm_event = 0;

if (!dev->saved_fw_status_flag)
goto end;

if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED) {
ret = mei_fw_status(dev, &fw_status);
if (!ret) {
fw_pm_event = fw_status.status[1] & PCI_CFG_HFS_2_PM_EVENT_MASK;
if (fw_pm_event != PCI_CFG_HFS_2_PM_CMOFF_TO_CMX_ERROR &&
fw_pm_event != PCI_CFG_HFS_2_PM_CM_RESET_ERROR)
goto end;
} else {
dev_err(dev->dev, "failed to read firmware status: %d\n", ret);
}
}

mei_fw_status2str(&dev->saved_fw_status, fw_sts_str, sizeof(fw_sts_str));
dev_warn(dev->dev, "unexpected reset: fw_pm_event = 0x%x, dev_state = %u fw status = %s\n",
fw_pm_event, dev->saved_dev_state, fw_sts_str);

end:
if (dev->gsc_reset_to_pxp == MEI_DEV_RESET_TO_PXP_PERFORMED)
dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DONE;
dev->saved_fw_status_flag = false;
}

/**
* mei_me_hw_start - hw start routine
*
Expand All @@ -492,6 +540,8 @@ static int mei_me_hw_start(struct mei_device *dev)
{
int ret = mei_me_hw_ready_wait(dev);

if (kind_is_gsc(dev) || kind_is_gscfi(dev))
mei_me_check_fw_reset(dev);
if (ret)
return ret;
dev_dbg(dev->dev, "hw is ready\n");
Expand Down Expand Up @@ -1300,8 +1350,13 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)

/* check if ME wants a reset */
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d pxp = %d\n",
dev->dev_state, dev->pxp_mode);
if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
dev_dbg(dev->dev, "FW not ready: resetting: dev_state = %d\n",
dev->dev_state);
} else {
dev_warn(dev->dev, "FW not ready: resetting: dev_state = %d\n",
dev->dev_state);
}
if (dev->dev_state == MEI_DEV_POWERING_DOWN ||
dev->dev_state == MEI_DEV_POWER_DOWN)
mei_cl_all_disconnect(dev);
Expand Down
27 changes: 25 additions & 2 deletions drivers/misc/mei/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,22 @@ void mei_cancel_work(struct mei_device *dev)
}
EXPORT_SYMBOL_GPL(mei_cancel_work);

static void mei_save_fw_status(struct mei_device *dev)
{
struct mei_fw_status fw_status;
int ret;

ret = mei_fw_status(dev, &fw_status);
if (ret) {
dev_err(dev->dev, "failed to read firmware status: %d\n", ret);
return;
}

dev->saved_dev_state = dev->dev_state;
dev->saved_fw_status_flag = true;
memcpy(&dev->saved_fw_status, &fw_status, sizeof(fw_status));
}

/**
* mei_reset - resets host and fw.
*
Expand All @@ -109,8 +125,14 @@ int mei_reset(struct mei_device *dev)
char fw_sts_str[MEI_FW_STATUS_STR_SZ];

mei_fw_status_str(dev, fw_sts_str, MEI_FW_STATUS_STR_SZ);
dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
mei_dev_state_str(state), fw_sts_str);
if (kind_is_gsc(dev) || kind_is_gscfi(dev)) {
dev_dbg(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
mei_dev_state_str(state), fw_sts_str);
mei_save_fw_status(dev);
} else {
dev_warn(dev->dev, "unexpected reset: dev_state = %s fw status = %s\n",
mei_dev_state_str(state), fw_sts_str);
}
}

mei_clear_interrupts(dev);
Expand Down Expand Up @@ -394,6 +416,7 @@ void mei_device_init(struct mei_device *dev,
dev->open_handle_count = 0;

dev->pxp_mode = MEI_DEV_PXP_DEFAULT;
dev->gsc_reset_to_pxp = MEI_DEV_RESET_TO_PXP_DEFAULT;

/*
* Reserving the first client ID
Expand Down
47 changes: 47 additions & 0 deletions drivers/misc/mei/mei_dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ enum mei_dev_pxp_mode {
MEI_DEV_PXP_READY = 3,
};

/**
* enum mei_dev_reset_to_pxp - reset to PXP mode performed
*
* @MEI_DEV_RESET_TO_PXP_DEFAULT: before reset
* @MEI_DEV_RESET_TO_PXP_PERFORMED: reset performed
* @MEI_DEV_RESET_TO_PXP_DONE: reset processed
*/
enum mei_dev_reset_to_pxp {
MEI_DEV_RESET_TO_PXP_DEFAULT = 0,
MEI_DEV_RESET_TO_PXP_PERFORMED = 1,
MEI_DEV_RESET_TO_PXP_DONE = 2,
};

const char *mei_dev_state_str(int state);

enum mei_file_transaction_states {
Expand Down Expand Up @@ -534,6 +547,11 @@ struct mei_dev_timeouts {
*
* @dbgfs_dir : debugfs mei root directory
*
* @saved_fw_status : saved firmware status
* @saved_dev_state : saved device state
* @saved_fw_status_flag : flag indicating that firmware status was saved
* @gsc_reset_to_pxp : state of reset to the PXP mode
*
* @ops: : hw specific operations
* @hw : hw specific data
*/
Expand Down Expand Up @@ -630,6 +648,11 @@ struct mei_device {
struct dentry *dbgfs_dir;
#endif /* CONFIG_DEBUG_FS */

struct mei_fw_status saved_fw_status;
enum mei_dev_state saved_dev_state;
bool saved_fw_status_flag;
enum mei_dev_reset_to_pxp gsc_reset_to_pxp;

const struct mei_hw_ops *ops;
char hw[] __aligned(sizeof(void *));
};
Expand Down Expand Up @@ -874,5 +897,29 @@ static inline ssize_t mei_fw_status_str(struct mei_device *dev,
return ret;
}

/**
* kind_is_gsc - checks whether the device is gsc
*
* @dev: the device structure
*
* Return: whether the device is gsc
*/
static inline bool kind_is_gsc(struct mei_device *dev)
{
/* check kind for NULL because it may be not set, like at the fist call to hw_start */
return dev->kind && (strcmp(dev->kind, "gsc") == 0);
}

/**
* kind_is_gscfi - checks whether the device is gscfi
*
* @dev: the device structure
*
* Return: whether the device is gscfi
*/
static inline bool kind_is_gscfi(struct mei_device *dev)
{
/* check kind for NULL because it may be not set, like at the fist call to hw_start */
return dev->kind && (strcmp(dev->kind, "gscfi") == 0);
}
#endif

0 comments on commit 34a674e

Please sign in to comment.