From 82d1369b9a70230fdbfa3e94194de58d7c53532c Mon Sep 17 00:00:00 2001 From: Illyas Mansoor Date: Fri, 4 May 2012 20:48:45 +0530 Subject: [PATCH] [PORT FROM R2] mid_pmu: retry d0ix if completion fails 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 Reviewed-on: http://android.intel.com:8080/44452 Reviewed-by: Gross, Mark Tested-by: Martin, LoicX Reviewed-by: buildbot Tested-by: buildbot --- arch/x86/platform/intel-mid/intel_soc_pmu.c | 155 +++++++++++++++++----------- 1 file changed, 95 insertions(+), 60 deletions(-) diff --git a/arch/x86/platform/intel-mid/intel_soc_pmu.c b/arch/x86/platform/intel-mid/intel_soc_pmu.c index 1d8c3b2..5abfd95 100644 --- a/arch/x86/platform/intel-mid/intel_soc_pmu.c +++ b/arch/x86/platform/intel-mid/intel_soc_pmu.c @@ -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; -- 2.7.4