mei: revamp mei reset state machine
1. MEI_DEV_RESETTING device state spans only hardware reset flow while starting dev state is saved into a local variable for further reference, this let us to reduce big if statements in case we are trying to avoid nested resets 2. During initializations if the reset ended in MEI_DEV_DISABLED device state we bail out with -ENODEV 3. Remove redundant interrupts_enabled parameter as this can be deduced from the starting dev_state 4. mei_reset propagates error code to the caller 5. Add mei_restart function to wrap the pci resume Signed-off-by: Tomas Winkler <tomas.winkler@intel.com> Signed-off-by: Alexander Usyskin <alexander.usyskin@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
db50d2f65b
Коммит
33ec082631
|
@ -763,7 +763,7 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev->dev_state = MEI_DEV_DISABLED;
|
dev->dev_state = MEI_DEV_POWER_DOWN;
|
||||||
dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
|
dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
|
||||||
/* force the reset */
|
/* force the reset */
|
||||||
return -EPROTO;
|
return -EPROTO;
|
||||||
|
|
|
@ -185,7 +185,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
|
||||||
|
|
||||||
mei_me_reg_write(hw, H_CSR, hcsr);
|
mei_me_reg_write(hw, H_CSR, hcsr);
|
||||||
|
|
||||||
if (dev->dev_state == MEI_DEV_POWER_DOWN)
|
if (intr_enable == false)
|
||||||
mei_me_hw_reset_release(dev);
|
mei_me_hw_reset_release(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -482,11 +482,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||||
mei_clear_interrupts(dev);
|
mei_clear_interrupts(dev);
|
||||||
|
|
||||||
/* check if ME wants a reset */
|
/* check if ME wants a reset */
|
||||||
if (!mei_hw_is_ready(dev) &&
|
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
|
||||||
dev->dev_state != MEI_DEV_RESETTING &&
|
|
||||||
dev->dev_state != MEI_DEV_INITIALIZING &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_DOWN &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_UP) {
|
|
||||||
dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
|
dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
|
||||||
schedule_work(&dev->reset_work);
|
schedule_work(&dev->reset_work);
|
||||||
goto end;
|
goto end;
|
||||||
|
@ -514,7 +510,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
|
||||||
break;
|
break;
|
||||||
dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
|
dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
|
||||||
rets = mei_irq_read_handler(dev, &complete_list, &slots);
|
rets = mei_irq_read_handler(dev, &complete_list, &slots);
|
||||||
if (rets) {
|
if (rets && dev->dev_state != MEI_DEV_RESETTING) {
|
||||||
schedule_work(&dev->reset_work);
|
schedule_work(&dev->reset_work);
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,113 @@ const char *mei_dev_state_str(int state)
|
||||||
#undef MEI_DEV_STATE
|
#undef MEI_DEV_STATE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_cancel_work. Cancel mei background jobs
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*
|
||||||
|
* returns 0 on success or < 0 if the reset hasn't succeeded
|
||||||
|
*/
|
||||||
|
void mei_cancel_work(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
cancel_work_sync(&dev->init_work);
|
||||||
|
cancel_work_sync(&dev->reset_work);
|
||||||
|
|
||||||
|
cancel_delayed_work(&dev->timer_work);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mei_cancel_work);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mei_reset - resets host and fw.
|
||||||
|
*
|
||||||
|
* @dev: the device structure
|
||||||
|
*/
|
||||||
|
int mei_reset(struct mei_device *dev)
|
||||||
|
{
|
||||||
|
enum mei_dev_state state = dev->dev_state;
|
||||||
|
bool interrupts_enabled;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (state != MEI_DEV_INITIALIZING &&
|
||||||
|
state != MEI_DEV_DISABLED &&
|
||||||
|
state != MEI_DEV_POWER_DOWN &&
|
||||||
|
state != MEI_DEV_POWER_UP)
|
||||||
|
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
||||||
|
mei_dev_state_str(state));
|
||||||
|
|
||||||
|
/* we're already in reset, cancel the init timer
|
||||||
|
* if the reset was called due the hbm protocol error
|
||||||
|
* we need to call it before hw start
|
||||||
|
* so the hbm watchdog won't kick in
|
||||||
|
*/
|
||||||
|
mei_hbm_idle(dev);
|
||||||
|
|
||||||
|
/* enter reset flow */
|
||||||
|
interrupts_enabled = state != MEI_DEV_POWER_DOWN;
|
||||||
|
dev->dev_state = MEI_DEV_RESETTING;
|
||||||
|
|
||||||
|
ret = mei_hw_reset(dev, interrupts_enabled);
|
||||||
|
/* fall through and remove the sw state even if hw reset has failed */
|
||||||
|
|
||||||
|
/* no need to clean up software state in case of power up */
|
||||||
|
if (state != MEI_DEV_INITIALIZING &&
|
||||||
|
state != MEI_DEV_POWER_UP) {
|
||||||
|
|
||||||
|
/* remove all waiting requests */
|
||||||
|
mei_cl_all_write_clear(dev);
|
||||||
|
|
||||||
|
mei_cl_all_disconnect(dev);
|
||||||
|
|
||||||
|
/* wake up all readers and writers so they can be interrupted */
|
||||||
|
mei_cl_all_wakeup(dev);
|
||||||
|
|
||||||
|
/* remove entry if already in list */
|
||||||
|
dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
|
||||||
|
mei_cl_unlink(&dev->wd_cl);
|
||||||
|
mei_cl_unlink(&dev->iamthif_cl);
|
||||||
|
mei_amthif_reset_params(dev);
|
||||||
|
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
dev->me_clients_num = 0;
|
||||||
|
dev->rd_msg_hdr = 0;
|
||||||
|
dev->wd_pending = false;
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
|
||||||
|
dev->dev_state = MEI_DEV_DISABLED;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state == MEI_DEV_POWER_DOWN) {
|
||||||
|
dev_dbg(&dev->pdev->dev, "powering down: end of reset\n");
|
||||||
|
dev->dev_state = MEI_DEV_DISABLED;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mei_hw_start(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
|
||||||
|
dev->dev_state = MEI_DEV_DISABLED;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
|
||||||
|
|
||||||
|
dev->dev_state = MEI_DEV_INIT_CLIENTS;
|
||||||
|
ret = mei_hbm_start_req(dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
|
||||||
|
dev->dev_state = MEI_DEV_DISABLED;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(mei_reset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_start - initializes host and fw to start work.
|
* mei_start - initializes host and fw to start work.
|
||||||
*
|
*
|
||||||
|
@ -54,14 +161,20 @@ int mei_start(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
mutex_lock(&dev->device_lock);
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
/* acknowledge interrupt and stop interupts */
|
/* acknowledge interrupt and stop interrupts */
|
||||||
mei_clear_interrupts(dev);
|
mei_clear_interrupts(dev);
|
||||||
|
|
||||||
mei_hw_config(dev);
|
mei_hw_config(dev);
|
||||||
|
|
||||||
dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
|
dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
|
||||||
|
|
||||||
mei_reset(dev, 1);
|
dev->dev_state = MEI_DEV_INITIALIZING;
|
||||||
|
mei_reset(dev);
|
||||||
|
|
||||||
|
if (dev->dev_state == MEI_DEV_DISABLED) {
|
||||||
|
dev_err(&dev->pdev->dev, "reset failed");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (mei_hbm_start_wait(dev)) {
|
if (mei_hbm_start_wait(dev)) {
|
||||||
dev_err(&dev->pdev->dev, "HBM haven't started");
|
dev_err(&dev->pdev->dev, "HBM haven't started");
|
||||||
|
@ -96,106 +209,33 @@ err:
|
||||||
EXPORT_SYMBOL_GPL(mei_start);
|
EXPORT_SYMBOL_GPL(mei_start);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* mei_cancel_work. Cancel mei background jobs
|
* mei_restart - restart device after suspend
|
||||||
*
|
*
|
||||||
* @dev: the device structure
|
* @dev: the device structure
|
||||||
*/
|
|
||||||
void mei_cancel_work(struct mei_device *dev)
|
|
||||||
{
|
|
||||||
cancel_work_sync(&dev->init_work);
|
|
||||||
cancel_work_sync(&dev->reset_work);
|
|
||||||
|
|
||||||
cancel_delayed_work(&dev->timer_work);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(mei_cancel_work);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mei_reset - resets host and fw.
|
|
||||||
*
|
*
|
||||||
* @dev: the device structure
|
* returns 0 on success or -ENODEV if the restart hasn't succeeded
|
||||||
* @interrupts_enabled: if interrupt should be enabled after reset.
|
|
||||||
*/
|
*/
|
||||||
void mei_reset(struct mei_device *dev, int interrupts_enabled)
|
int mei_restart(struct mei_device *dev)
|
||||||
{
|
{
|
||||||
bool unexpected;
|
int err;
|
||||||
int ret;
|
|
||||||
|
|
||||||
unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
|
mutex_lock(&dev->device_lock);
|
||||||
dev->dev_state != MEI_DEV_DISABLED &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_DOWN &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_UP);
|
|
||||||
|
|
||||||
if (unexpected)
|
mei_clear_interrupts(dev);
|
||||||
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
|
|
||||||
mei_dev_state_str(dev->dev_state));
|
|
||||||
|
|
||||||
/* we're already in reset, cancel the init timer
|
dev->dev_state = MEI_DEV_POWER_UP;
|
||||||
* if the reset was called due the hbm protocol error
|
|
||||||
* we need to call it before hw start
|
|
||||||
* so the hbm watchdog won't kick in
|
|
||||||
*/
|
|
||||||
mei_hbm_idle(dev);
|
|
||||||
|
|
||||||
ret = mei_hw_reset(dev, interrupts_enabled);
|
err = mei_reset(dev);
|
||||||
if (ret) {
|
|
||||||
dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
|
|
||||||
interrupts_enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
if (dev->dev_state != MEI_DEV_INITIALIZING &&
|
if (err || dev->dev_state == MEI_DEV_DISABLED)
|
||||||
dev->dev_state != MEI_DEV_POWER_UP) {
|
return -ENODEV;
|
||||||
if (dev->dev_state != MEI_DEV_DISABLED &&
|
|
||||||
dev->dev_state != MEI_DEV_POWER_DOWN)
|
|
||||||
dev->dev_state = MEI_DEV_RESETTING;
|
|
||||||
|
|
||||||
/* remove all waiting requests */
|
return 0;
|
||||||
mei_cl_all_write_clear(dev);
|
|
||||||
|
|
||||||
mei_cl_all_disconnect(dev);
|
|
||||||
|
|
||||||
/* wake up all readings so they can be interrupted */
|
|
||||||
mei_cl_all_wakeup(dev);
|
|
||||||
|
|
||||||
/* remove entry if already in list */
|
|
||||||
dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
|
|
||||||
mei_cl_unlink(&dev->wd_cl);
|
|
||||||
mei_cl_unlink(&dev->iamthif_cl);
|
|
||||||
mei_amthif_reset_params(dev);
|
|
||||||
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
dev->me_clients_num = 0;
|
|
||||||
dev->rd_msg_hdr = 0;
|
|
||||||
dev->wd_pending = false;
|
|
||||||
|
|
||||||
if (!interrupts_enabled) {
|
|
||||||
dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
|
|
||||||
dev->dev_state = MEI_DEV_DISABLED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = mei_hw_start(dev);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n");
|
|
||||||
dev->dev_state = MEI_DEV_DISABLED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
|
|
||||||
/* link is established * start sending messages. */
|
|
||||||
|
|
||||||
dev->dev_state = MEI_DEV_INIT_CLIENTS;
|
|
||||||
|
|
||||||
ret = mei_hbm_start_req(dev);
|
|
||||||
if (ret) {
|
|
||||||
dev_err(&dev->pdev->dev, "hbm_start failed disabling the device\n");
|
|
||||||
dev->dev_state = MEI_DEV_DISABLED;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mei_reset);
|
EXPORT_SYMBOL_GPL(mei_restart);
|
||||||
|
|
||||||
|
|
||||||
static void mei_reset_work(struct work_struct *work)
|
static void mei_reset_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
|
@ -204,9 +244,12 @@ static void mei_reset_work(struct work_struct *work)
|
||||||
|
|
||||||
mutex_lock(&dev->device_lock);
|
mutex_lock(&dev->device_lock);
|
||||||
|
|
||||||
mei_reset(dev, true);
|
mei_reset(dev);
|
||||||
|
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
if (dev->dev_state == MEI_DEV_DISABLED)
|
||||||
|
dev_err(&dev->pdev->dev, "reset failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
void mei_stop(struct mei_device *dev)
|
void mei_stop(struct mei_device *dev)
|
||||||
|
@ -222,7 +265,7 @@ void mei_stop(struct mei_device *dev)
|
||||||
mei_wd_stop(dev);
|
mei_wd_stop(dev);
|
||||||
|
|
||||||
dev->dev_state = MEI_DEV_POWER_DOWN;
|
dev->dev_state = MEI_DEV_POWER_DOWN;
|
||||||
mei_reset(dev, 0);
|
mei_reset(dev);
|
||||||
|
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
|
|
||||||
|
|
|
@ -560,7 +560,7 @@ void mei_timer(struct work_struct *work)
|
||||||
if (--dev->init_clients_timer == 0) {
|
if (--dev->init_clients_timer == 0) {
|
||||||
dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n",
|
dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n",
|
||||||
dev->hbm_state);
|
dev->hbm_state);
|
||||||
mei_reset(dev, 1);
|
mei_reset(dev);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,8 +573,8 @@ void mei_timer(struct work_struct *work)
|
||||||
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
|
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
|
||||||
if (cl_pos->timer_count) {
|
if (cl_pos->timer_count) {
|
||||||
if (--cl_pos->timer_count == 0) {
|
if (--cl_pos->timer_count == 0) {
|
||||||
dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");
|
dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n");
|
||||||
mei_reset(dev, 1);
|
mei_reset(dev);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -582,8 +582,8 @@ void mei_timer(struct work_struct *work)
|
||||||
|
|
||||||
if (dev->iamthif_stall_timer) {
|
if (dev->iamthif_stall_timer) {
|
||||||
if (--dev->iamthif_stall_timer == 0) {
|
if (--dev->iamthif_stall_timer == 0) {
|
||||||
dev_err(&dev->pdev->dev, "reset: amthif hanged.\n");
|
dev_err(&dev->pdev->dev, "timer: amthif hanged.\n");
|
||||||
mei_reset(dev, 1);
|
mei_reset(dev);
|
||||||
dev->iamthif_msg_buf_size = 0;
|
dev->iamthif_msg_buf_size = 0;
|
||||||
dev->iamthif_msg_buf_index = 0;
|
dev->iamthif_msg_buf_index = 0;
|
||||||
dev->iamthif_canceled = false;
|
dev->iamthif_canceled = false;
|
||||||
|
@ -636,6 +636,7 @@ void mei_timer(struct work_struct *work)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out:
|
out:
|
||||||
|
if (dev->dev_state != MEI_DEV_DISABLED)
|
||||||
schedule_delayed_work(&dev->timer_work, 2 * HZ);
|
schedule_delayed_work(&dev->timer_work, 2 * HZ);
|
||||||
mutex_unlock(&dev->device_lock);
|
mutex_unlock(&dev->device_lock);
|
||||||
}
|
}
|
||||||
|
|
|
@ -472,8 +472,9 @@ static inline u32 mei_slots2data(int slots)
|
||||||
* mei init function prototypes
|
* mei init function prototypes
|
||||||
*/
|
*/
|
||||||
void mei_device_init(struct mei_device *dev);
|
void mei_device_init(struct mei_device *dev);
|
||||||
void mei_reset(struct mei_device *dev, int interrupts);
|
int mei_reset(struct mei_device *dev);
|
||||||
int mei_start(struct mei_device *dev);
|
int mei_start(struct mei_device *dev);
|
||||||
|
int mei_restart(struct mei_device *dev);
|
||||||
void mei_stop(struct mei_device *dev);
|
void mei_stop(struct mei_device *dev);
|
||||||
void mei_cancel_work(struct mei_device *dev);
|
void mei_cancel_work(struct mei_device *dev);
|
||||||
|
|
||||||
|
|
|
@ -321,16 +321,14 @@ static int mei_me_pci_resume(struct device *device)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&dev->device_lock);
|
err = mei_restart(dev);
|
||||||
dev->dev_state = MEI_DEV_POWER_UP;
|
if (err)
|
||||||
mei_clear_interrupts(dev);
|
return err;
|
||||||
mei_reset(dev, 1);
|
|
||||||
mutex_unlock(&dev->device_lock);
|
|
||||||
|
|
||||||
/* Start timer if stopped in suspend */
|
/* Start timer if stopped in suspend */
|
||||||
schedule_delayed_work(&dev->timer_work, HZ);
|
schedule_delayed_work(&dev->timer_work, HZ);
|
||||||
|
|
||||||
return err;
|
return 0;
|
||||||
}
|
}
|
||||||
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
|
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
|
||||||
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
#define MEI_ME_PM_OPS (&mei_me_pm_ops)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче