Merge tag 'clk-for-linus-3.14-part2' of git://git.linaro.org/people/mike.turquette...
[firefly-linux-kernel-4.4.55.git] / drivers / misc / mei / init.c
index c47fa273879e4f40e089bf965901061e2b992e3c..cdd31c2a2a2bf095e827758e3d71567d80557a5f 100644 (file)
@@ -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,30 @@ 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;
+
+       dev->reset_count++;
+       if (dev->reset_count > MEI_MAX_CONSEC_RESET) {
+               dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
+               dev->dev_state = MEI_DEV_DISABLED;
+               return -ENODEV;
        }
 
+       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 +124,128 @@ 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;
+       dev->reset_count = 0;
+       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;
+       dev->reset_count = 0;
+
+       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 +253,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 +274,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);
 
@@ -242,6 +294,7 @@ void mei_device_init(struct mei_device *dev)
        init_waitqueue_head(&dev->wait_recvd_msg);
        init_waitqueue_head(&dev->wait_stop_wd);
        dev->dev_state = MEI_DEV_INITIALIZING;
+       dev->reset_count = 0;
 
        mei_io_list_init(&dev->read_list);
        mei_io_list_init(&dev->write_list);