static int dev_init_state;
static struct intel_mid_base_addr base_addr;
-static struct mrst_pmu_reg __iomem *pmu_reg;
#ifdef CONFIG_HAS_WAKELOCK
static struct wake_lock pmu_wake_lock;
static u32 ospm_base;
static spinlock_t nc_ready_lock;
+/* debug counters */
+static u32 pmu_wait_done_calls;
+static u32 pmu_wait_done_udelays;
+static u32 pmu_wait_done_udelays_max;
+
/*
* Locking strategy::
*
- * Two semaphores are used to lock the global variables used in
- * the code. The entry points in pmu driver are mfld_pmu_pci_set_power_state()
- * and PMU interrupt handler contexts, so here is the flow of how
- * the semaphores are used.
- *
- * In D0ix command case::
- * set_power_state process context:
- * set_power_state()->acquire_scu_ready_sem()->issue_interactive_cmd->
- * wait_for_interactive_complete_sem->release scu_ready sem
- *
- * PMU Interrupt context:
- * pmu_interrupt_handler()->release interactive_complete_sem->return
+ * pmu_pci_set_power_state() is synchronous -- it waits
+ * for the SCU to be not-busy before and after commands.
+ * Drivers may access devices immediately upon its return.
*
* In Idle handler case::
* Idle context:
*/
static struct semaphore scu_ready_sem =
__SEMAPHORE_INITIALIZER(scu_ready_sem, 1);
-static struct semaphore set_mode_complete_sem =
- __SEMAPHORE_INITIALIZER(set_mode_complete_sem, 0);
+
+/* handle concurrent SMP invokations of pmu_pci_set_power_state() */
+static spinlock_t mrst_pmu_power_state_lock;
static int scu_comms_okay = 1;
static int lpmp3_possible;
static int s0i3_possible;
static int c6_demoted;
-static int interactive_cmd_sent;
static int s0ix_entered;
static u32 pmu1_max_devs, pmu2_max_devs, ss_per_reg;
/* clear the interrupt pending bit */
pmu_clear_interrupt_pending(PMU_NUM_2);
- /*
- * In case of interactive command
- * let the waiting set_power_state()
- * release scu_ready_sem
- */
- if (interactive_cmd_sent) {
- interactive_cmd_sent = 0;
-
- /* unblock set_power_state() */
- up(&set_mode_complete_sem);
- } else {
- s0ix_entered = 0;
+ s0ix_entered = 0;
- /* S0ix case release it */
- up(&scu_ready_sem);
- }
+ /* S0ix case release it */
+ up(&scu_ready_sem);
status = IRQ_HANDLED;
ret_no_clear:
}
EXPORT_SYMBOL(pmu_nc_set_power_state);
+/* poll for maximum of 50ms us for busy bit to clear */
+static int pmu_wait_done(void)
+{
+ int udelays;
+
+ pmu_wait_done_calls++;
+
+ for (udelays = 0; udelays < 500; ++udelays) {
+ if (udelays > pmu_wait_done_udelays_max)
+ pmu_wait_done_udelays_max = udelays;
+
+ if (pmu_read_busy_status() == 0)
+ return 0;
+
+ udelay(100);
+ pmu_wait_done_udelays++;
+ }
+
+ WARN_ONCE(1, "SCU not done for 50ms");
+ return -EBUSY;
+}
+
/**
* mfld_pmu_pci_set_power_state - Callback function is used by all the PCI devices
* for a platform specific device power on/shutdown.
}
down_scu_timed(&scu_ready_sem);
- interactive_cmd_sent = 1;
+
+ spin_lock(&mrst_pmu_power_state_lock);
status =
pmu_pci_to_indexes(pdev, &i, &pmu_num, &sub_sys_index, &sub_sys_pos);
* flag is needed to distinguish between
* S0ix vs interactive command in pmu_sc_irq()
*/
- status = _pmu_issue_command(&cur_pmssc, SET_MODE, 1, PMU_NUM_2);
+ status = _pmu_issue_command(&cur_pmssc, SET_MODE, 0, PMU_NUM_2);
if (unlikely(status != PMU_SUCCESS)) {
dev_dbg(&pmu_dev->dev,
* powered on in SCU.
*
*/
- down_scu_timed(&set_mode_complete_sem);
+ pmu_wait_done();
pmu_set_s0ix_possible(state);
}
unlock:
- interactive_cmd_sent = 0;
+ spin_unlock(&mrst_pmu_power_state_lock);
up(&scu_ready_sem);
return status;
}
(needed && !val) ? "blocking s0ix" : "");
}
+ seq_printf(s, "pmu_wait_done_calls %8d\n",
+ pmu_wait_done_calls);
+ seq_printf(s, "pmu_wait_done_udelays %8d\n",
+ pmu_wait_done_udelays);
+ seq_printf(s, "pmu_wait_done_udelays_max %8d\n",
+ pmu_wait_done_udelays_max);
+
unlock:
up(&scu_ready_sem);
update_all_lss_states(&pmu_config);
/* send a interactive command to fw */
- interactive_cmd_sent = 1;
- status = _pmu_issue_command(&pmu_config, SET_MODE, 1, PMU_NUM_2);
+ status = _pmu_issue_command(&pmu_config, SET_MODE, 0, PMU_NUM_2);
if (status != PMU_SUCCESS) {
- interactive_cmd_sent = 0;
dev_dbg(&pmu_dev->dev,\
"Failure from pmu mode change to interactive."
" = %d\n", status);
* powered on in SCU.
*
*/
- down_scu_timed(&set_mode_complete_sem);
+ pmu_wait_done();
/* In cases were gfx is not enabled
* this will enable s0ix immediately
goto out_err4;
}
+ spin_lock_init(&mrst_pmu_power_state_lock);
+
pmu_init_time =
cpu_clock(raw_smp_processor_id());