[PORT FROM R2] mid_pmu: retry d0ix if completion fails
authorIllyas Mansoor <illyas.mansoor@intel.com>
Fri, 4 May 2012 15:18:45 +0000 (20:48 +0530)
committerbuildbot <buildbot@intel.com>
Tue, 15 May 2012 18:53:32 +0000 (11:53 -0700)
BZ: 32634

We get kernel panic in pmu_pci_set_power_state() while
waiting for d0ix command completion interrupt, we timeout for
the d0ix completion interrupt and see that transition is still
not complete.

<2>[ 8519.381692] pmu_pci_set_power_state: completion timeout: 8086 0829 0000:00:02.3          penwell_otg:
<2>[ 8519.381934] interrupt pending = 0
<2>[ 8519.382019] pmu_busy_status = 0
<2>[ 8519.382097] suspend_started = 0
<2>[ 8519.382175] shutdown_started = 0
<2>[ 8519.382254] interactive_cmd_sent = 0
<2>[ 8519.382343] camera_off = 1 display_off = 1
<2>[ 8519.382442] s0ix_possible = 0xf
<2>[ 8519.382520] s0ix_entered = 0x0
<2>[ 8519.382596] pmu_current_state = 0

We will avoid setting the d0ix completion interrupt.

This should remove one of the variable that may affect
this issue.

Also we retry sending d0ix command, until it fails thrice or succeeds.

Change-Id: I00355647a4cba2f737f1d4ba6737eb40bdf465e0
Orig-Change-Id: I3804ee03ef8964cc37c151ce973a81f5ef82c286
Signed-off-by: Illyas Mansoor <illyas.mansoor@intel.com>
Reviewed-on: http://android.intel.com:8080/44452
Reviewed-by: Gross, Mark <mark.gross@intel.com>
Tested-by: Martin, LoicX <loicx.martin@intel.com>
Reviewed-by: buildbot <buildbot@intel.com>
Tested-by: buildbot <buildbot@intel.com>
arch/x86/platform/intel-mid/intel_soc_pmu.c

index 1d8c3b2..5abfd95 100644 (file)
@@ -351,6 +351,23 @@ int _pmu2_wait_not_busy(void)
        return -EBUSY;
 }
 
+static int _pmu2_wait_not_busy_yield(void)
+{
+       int pmu_busy_retry = 20;
+
+       /* wait 10ms that the latest pmu command finished */
+       do {
+               usleep_range(10, 500);
+
+               if (!_pmu_read_status(PMU_BUSY_STATUS))
+                       return 0;
+       } while (--pmu_busy_retry);
+
+       WARN(1, "pmu2 busy!");
+
+       return -EBUSY;
+}
+
 static void pmu_write_subsys_config(struct pmu_ss_states *pm_ssc)
 {
        /* South complex in Penwell has multiple registers for
@@ -1221,6 +1238,7 @@ int __ref pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t state)
        int pmu_num;
        struct pmu_ss_states cur_pmssc;
        int status = 0;
+       int retry_count = 3;
 
        /* Ignore callback from devices until we have initialized */
        if (unlikely((!pmu_initialized)))
@@ -1236,8 +1254,6 @@ int __ref pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t state)
        if (unlikely(mid_pmu_cxt->shutdown_started))
                goto unlock;
 
-       mid_pmu_cxt->interactive_cmd_sent = true;
-
        /*get LSS index corresponding to pdev, its position in
         *32 bit register and its register numer*/
        status =
@@ -1295,68 +1311,89 @@ int __ref pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t state)
        cur_pmssc.pmu2_states[2] &= ~IGNORE_SSS2;
        cur_pmssc.pmu2_states[3] &= ~IGNORE_SSS3;
 
-       /* Issue the pmu command to PMU 2
-        * flag is needed to distinguish between
-        * S0ix vs interactive command in pmu_sc_irq()
-        */
-       status = pmu_issue_interactive_command(&cur_pmssc, true);
+       do {
+               /* Issue the pmu command to PMU 2
+                * flag is needed to distinguish between
+                * S0ix vs interactive command in pmu_sc_irq()
+                */
+               status = pmu_issue_interactive_command(&cur_pmssc, false);
 
-       if (unlikely(status != PMU_SUCCESS)) {
-               dev_dbg(&mid_pmu_cxt->pmu_dev->dev,
-                        "Failed to Issue a PM command to PMU2\n");
-               goto unlock;
-       }
+               if (unlikely(status != PMU_SUCCESS)) {
+                       dev_dbg(&mid_pmu_cxt->pmu_dev->dev,
+                                "Failed to Issue a PM command to PMU2\n");
+                       goto unlock;
+               }
 
-       /*
-        * Wait for interactive command to complete.
-        * If we dont wait, there is a possibility that
-        * the driver may access the device before its
-        * powered on in SCU.
-        *
-        */
-       if (!wait_for_completion_timeout(
-                   &mid_pmu_cxt->set_mode_complete, 2 * HZ)) {
+               /*
+                * Wait for interactive command to complete.
+                * If we dont wait, there is a possibility that
+                * the driver may access the device before its
+                * powered on in SCU.
+                *
+                */
+               status = _pmu2_wait_not_busy_yield();
+
+               if (likely(!status))
+                       break;
 
-               /* Since we didn't receive the completion
-                * interrupt, check if the power transition
+               printk(KERN_CRIT "%s: D0ix transition failure:"
+                               " %04x %04X %s %20s:\n", __func__,
+                               pdev->vendor, pdev->device,
+                               dev_name(&pdev->dev),
+                               dev_driver_string(&pdev->dev));
+               printk(KERN_CRIT "pmu_busy_status = %d\n",
+               _pmu_read_status(PMU_BUSY_STATUS));
+               printk(KERN_CRIT "suspend_started = %d\n",
+                               mid_pmu_cxt->suspend_started);
+               printk(KERN_CRIT "shutdown_started = %d\n",
+                               mid_pmu_cxt->shutdown_started);
+               printk(KERN_CRIT "Retrying... attempt(%d):\n",
+                               retry_count);
+       } while (--retry_count);
+
+       if (unlikely(status)) {
+               struct pmu_ss_states new_pmsss;
+               u32 post_transition_val;
+
+               /*
+                * Check if the power transition
                 * indeed happend, if yes continue, else BUG
                 */
-               struct pmu_ss_states new_pmsss;
+               new_value &=
+                       (D0I3_MASK << (sub_sys_pos * BITS_PER_LSS));
                pmu_read_sss(&new_pmsss);
-
-               if (new_value == new_pmsss.pmu2_states[sub_sys_index]) {
-                       WARN(1,
-                       "%s: completion timed out.\n", __func__);
-                       init_completion
-                               (&mid_pmu_cxt->set_mode_complete);
-                       } else {
-                               printk(KERN_CRIT "%s: completion timeout:"
-                                       " %04x %04X %s %20s:\n", __func__,
-                                       pdev->vendor, pdev->device,
-                                       dev_name(&pdev->dev),
-                                       dev_driver_string(&pdev->dev));
-                               printk(KERN_CRIT "interrupt pending = %d\n",
-                                       pmu_interrupt_pending());
-                               printk(KERN_CRIT "pmu_busy_status = %d\n",
-                               _pmu_read_status(PMU_BUSY_STATUS));
-                               printk(KERN_CRIT "suspend_started = %d\n",
-                                               mid_pmu_cxt->suspend_started);
-                               printk(KERN_CRIT "shutdown_started = %d\n",
-                                               mid_pmu_cxt->shutdown_started);
-                               printk(KERN_CRIT "interactive_cmd_sent = %d\n",
-                                       (int)mid_pmu_cxt->interactive_cmd_sent);
-                               printk(KERN_CRIT "camera_off = %d"
-                               " display_off = %d\n", mid_pmu_cxt->camera_off,
-                                               mid_pmu_cxt->display_off);
-                               printk(KERN_CRIT "s0ix_possible = 0x%x\n",
-                                               mid_pmu_cxt->s0ix_possible);
-                               printk(KERN_CRIT "s0ix_entered = 0x%x\n",
-                                               mid_pmu_cxt->s0ix_entered);
-                               printk(KERN_CRIT "pmu_current_state = %d\n",
-                                               mid_pmu_cxt->pmu_current_state);
-                               pmu_dump_logs();
-
-                               BUG();
+               post_transition_val =
+                       (new_pmsss.pmu2_states[sub_sys_index] &
+                       (D0I3_MASK << (sub_sys_pos * BITS_PER_LSS)));
+
+               if (new_value != post_transition_val) {
+                       printk(KERN_CRIT "%s: D0ix transition failure:"
+                               " %04x %04X %s %20s:\n", __func__,
+                               pdev->vendor, pdev->device,
+                               dev_name(&pdev->dev),
+                               dev_driver_string(&pdev->dev));
+                       printk(KERN_CRIT "interrupt pending = %d\n",
+                               pmu_interrupt_pending());
+                       printk(KERN_CRIT "pmu_busy_status = %d\n",
+                       _pmu_read_status(PMU_BUSY_STATUS));
+                       printk(KERN_CRIT "suspend_started = %d\n",
+                                       mid_pmu_cxt->suspend_started);
+                       printk(KERN_CRIT "shutdown_started = %d\n",
+                                       mid_pmu_cxt->shutdown_started);
+                       printk(KERN_CRIT "interactive_cmd_sent = %d\n",
+                               (int)mid_pmu_cxt->interactive_cmd_sent);
+                       printk(KERN_CRIT "camera_off = %d"
+                       " display_off = %d\n", mid_pmu_cxt->camera_off,
+                                       mid_pmu_cxt->display_off);
+                       printk(KERN_CRIT "s0ix_possible = 0x%x\n",
+                                       mid_pmu_cxt->s0ix_possible);
+                       printk(KERN_CRIT "s0ix_entered = 0x%x\n",
+                                       mid_pmu_cxt->s0ix_entered);
+                       printk(KERN_CRIT "pmu_current_state = %d\n",
+                                       mid_pmu_cxt->pmu_current_state);
+                       pmu_dump_logs();
+
+                       BUG();
                }
        }
 
@@ -1367,7 +1404,6 @@ int __ref pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t state)
                                pci_to_platform_state(state));
 
 unlock:
-       mid_pmu_cxt->interactive_cmd_sent = false;
        up(&mid_pmu_cxt->scu_ready_sem);
 
        /*
@@ -1389,7 +1425,6 @@ unlock:
        return status;
 }
 
-
 pci_power_t platfrom_pmu_choose_state(int lss)
 {
        pci_power_t state = PCI_D3hot;