diff options
Diffstat (limited to 'drivers/misc/mei/init.c')
-rw-r--r-- | drivers/misc/mei/init.c | 209 |
1 files changed, 126 insertions, 83 deletions
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index c47fa273879e..059133d8caca 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -43,62 +43,13 @@ const char *mei_dev_state_str(int state) #undef MEI_DEV_STATE } -/** - * mei_start - initializes host and fw to start work. - * - * @dev: the device structure - * - * returns 0 on success, <0 on failure. - */ -int mei_start(struct mei_device *dev) -{ - mutex_lock(&dev->device_lock); - - /* acknowledge interrupt and stop interupts */ - mei_clear_interrupts(dev); - - mei_hw_config(dev); - - dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); - - mei_reset(dev, 1); - - if (mei_hbm_start_wait(dev)) { - dev_err(&dev->pdev->dev, "HBM haven't started"); - goto err; - } - - if (!mei_host_is_ready(dev)) { - dev_err(&dev->pdev->dev, "host is not ready.\n"); - goto err; - } - - if (!mei_hw_is_ready(dev)) { - dev_err(&dev->pdev->dev, "ME is not ready.\n"); - goto err; - } - - if (!mei_hbm_version_is_supported(dev)) { - dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); - goto err; - } - - dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); - - mutex_unlock(&dev->device_lock); - return 0; -err: - dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); - dev->dev_state = MEI_DEV_DISABLED; - mutex_unlock(&dev->device_lock); - return -ENODEV; -} -EXPORT_SYMBOL_GPL(mei_start); /** * 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) { @@ -113,21 +64,19 @@ EXPORT_SYMBOL_GPL(mei_cancel_work); * mei_reset - resets host and fw. * * @dev: the device structure - * @interrupts_enabled: if interrupt should be enabled after reset. */ -void mei_reset(struct mei_device *dev, int interrupts_enabled) +int mei_reset(struct mei_device *dev) { - bool unexpected; + enum mei_dev_state state = dev->dev_state; + bool interrupts_enabled; int ret; - unexpected = (dev->dev_state != MEI_DEV_INITIALIZING && - dev->dev_state != MEI_DEV_DISABLED && - dev->dev_state != MEI_DEV_POWER_DOWN && - dev->dev_state != MEI_DEV_POWER_UP); - - if (unexpected) + 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(dev->dev_state)); + mei_dev_state_str(state)); /* we're already in reset, cancel the init timer * if the reset was called due the hbm protocol error @@ -136,25 +85,23 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) */ mei_hbm_idle(dev); - ret = mei_hw_reset(dev, interrupts_enabled); - if (ret) { - dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n"); - interrupts_enabled = false; - } + /* 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 */ - if (dev->dev_state != MEI_DEV_INITIALIZING && - dev->dev_state != MEI_DEV_POWER_UP) { - if (dev->dev_state != MEI_DEV_DISABLED && - dev->dev_state != MEI_DEV_POWER_DOWN) - dev->dev_state = MEI_DEV_RESETTING; + /* 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 readings so they can be interrupted */ + /* wake up all readers and writers so they can be interrupted */ mei_cl_all_wakeup(dev); /* remove entry if already in list */ @@ -170,33 +117,126 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled) dev->rd_msg_hdr = 0; dev->wd_pending = false; - if (!interrupts_enabled) { - dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n"); + if (ret) { + dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret); dev->dev_state = MEI_DEV_DISABLED; - return; + 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 disabling the device\n"); + dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_DISABLED; - return; + return ret; } 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_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret); dev->dev_state = MEI_DEV_DISABLED; - return; + return ret; } + + return 0; } EXPORT_SYMBOL_GPL(mei_reset); +/** + * mei_start - initializes host and fw to start work. + * + * @dev: the device structure + * + * returns 0 on success, <0 on failure. + */ +int mei_start(struct mei_device *dev) +{ + mutex_lock(&dev->device_lock); + + /* acknowledge interrupt and stop interrupts */ + mei_clear_interrupts(dev); + + mei_hw_config(dev); + + dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n"); + + 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)) { + dev_err(&dev->pdev->dev, "HBM haven't started"); + goto err; + } + + if (!mei_host_is_ready(dev)) { + dev_err(&dev->pdev->dev, "host is not ready.\n"); + goto err; + } + + if (!mei_hw_is_ready(dev)) { + dev_err(&dev->pdev->dev, "ME is not ready.\n"); + goto err; + } + + if (!mei_hbm_version_is_supported(dev)) { + dev_dbg(&dev->pdev->dev, "MEI start failed.\n"); + goto err; + } + + dev_dbg(&dev->pdev->dev, "link layer has been established.\n"); + + mutex_unlock(&dev->device_lock); + return 0; +err: + dev_err(&dev->pdev->dev, "link layer initialization failed.\n"); + dev->dev_state = MEI_DEV_DISABLED; + mutex_unlock(&dev->device_lock); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(mei_start); + +/** + * mei_restart - restart device after suspend + * + * @dev: the device structure + * + * returns 0 on success or -ENODEV if the restart hasn't succeeded + */ +int mei_restart(struct mei_device *dev) +{ + int err; + + mutex_lock(&dev->device_lock); + + mei_clear_interrupts(dev); + + dev->dev_state = MEI_DEV_POWER_UP; + + err = mei_reset(dev); + + mutex_unlock(&dev->device_lock); + + if (err || dev->dev_state == MEI_DEV_DISABLED) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(mei_restart); + + static void mei_reset_work(struct work_struct *work) { struct mei_device *dev = @@ -204,9 +244,12 @@ static void mei_reset_work(struct work_struct *work) mutex_lock(&dev->device_lock); - mei_reset(dev, true); + mei_reset(dev); 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) @@ -222,7 +265,7 @@ void mei_stop(struct mei_device *dev) mei_wd_stop(dev); dev->dev_state = MEI_DEV_POWER_DOWN; - mei_reset(dev, 0); + mei_reset(dev); mutex_unlock(&dev->device_lock); |