aboutsummaryrefslogtreecommitdiff
path: root/drivers/misc/mei/init.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/mei/init.c')
-rw-r--r--drivers/misc/mei/init.c209
1 files changed, 126 insertions, 83 deletions
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index c47fa273879..059133d8cac 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);