void soc_sleep_requested(bool ready_to_sleep) { unsigned int key; QM_PUTS("SoC sleep requested"); if (!ready_to_sleep) { QM_PUTS("Sleep NAK'd"); send_msg(SLEEP_NAK); } else { key = qm_irq_lock(); send_msg(SLEEP_ACK); soc_sleep(CORE_SLEEP); qm_irq_unlock(key); } return; }
void request_soc_sleep(void) { unsigned int key; qm_mbox_ch_status_t stat; QM_PUTS("Request SoC sleep"); key = qm_irq_lock(); /* Send sleep request. */ send_msg(SLEEP_REQ); /* Wait for the answer from other core. */ do { if (qm_mbox_ch_get_status(mbox_rx, &stat) != 0) { QM_PUTS("Unable to get mailbox status"); } } while (stat == QM_MBOX_CH_IDLE); qm_mbox_ch_read(mbox_rx, &rx_data); switch (rx_data.ctrl) { case SLEEP_NAK: QM_PUTS("MBox NAK"); qm_irq_unlock(key); CORE_HALT(); break; case SLEEP_REQ: QM_PUTS("MBox REQ"); /* * There is a race condition where both cores want to go to * sleep. The policy is to make the x86 perform the SoC sleep * transition. Sensor will go to core sleep. */ #if QM_SENSOR soc_sleep(CORE_SLEEP); qm_irq_unlock(key); #else /* Wait for other core to be ready. */ while (!other_core_sleep_flag()) { } soc_sleep(SOC_SLEEP); qm_irq_unlock(key); #endif break; case SLEEP_ACK: QM_PUTS("MBox ACK"); /* Wait for other core to be actually ready for sleep mode. */ while (!other_core_sleep_flag()) { } soc_sleep(SOC_SLEEP); qm_irq_unlock(key); break; default: QM_PUTS("Wrong message received"); break; } }
/* Low power app example */ int main(void) { /* Setup the RTC to get out of sleep mode. deep sleep will require an */ /* analog comparator interrupt to wake up the system. */ /* Variables */ uint32_t pmux_sel_save[2], pmux_in_en_save, pmux_pullup_save; qm_ac_config_t ac_cfg; qm_rtc_config_t rtc_cfg; QM_PUTS("Low power mode example."); clk_periph_enable(CLK_PERIPH_RTC_REGISTER | CLK_PERIPH_CLK); /* Initialise RTC configuration. */ rtc_cfg.init_val = 0; rtc_cfg.alarm_en = 1; rtc_cfg.alarm_val = QM_RTC_ALARM_SECOND; rtc_cfg.callback = rtc_example_callback; qm_rtc_set_config(QM_RTC_0, &rtc_cfg); qm_irq_request(QM_IRQ_RTC_0, qm_rtc_isr_0); QM_PUTS("CPU Halt."); /* Halt the CPU, RTC alarm will wake me up. */ cpu_halt(); QM_PUTS("CPU Halt wakeup."); /* Set another alarm one minute from now. */ qm_rtc_set_alarm(QM_RTC_0, QM_RTC[QM_RTC_0].rtc_ccvr + QM_RTC_ALARM_SECOND); QM_PUTS("Go to sleep."); /* Go to sleep, RTC will wake me up. */ soc_sleep(); QM_PUTS("Wake up from sleep."); /* Physical step at this stage to raise the V on the comparator pin. */ /* Go to deep sleep, a comparator should wake me up. */ QM_PUTS("Go to deep sleep."); ac_cfg.reference = BIT(WAKEUP_COMPARATOR_PIN); /* Ref internal voltage */ ac_cfg.polarity = 0x0; /* Fire if greater than ref (high level) */ ac_cfg.power = BIT(WAKEUP_COMPARATOR_PIN); /* Normal operation mode */ ac_cfg.int_en = BIT(WAKEUP_COMPARATOR_PIN); /* Enable comparator */ ac_cfg.callback = ac_example_callback; qm_ac_set_config(&ac_cfg); qm_irq_request(QM_IRQ_AC, qm_ac_isr); /* * Comparator pin will fire an interrupt when the input voltage is * greater than the reference voltage (0.95V). */ /* * In order to minimise power, pmux_sel must be set to 0, input enable * must be cleared for any pins not expecting to be used to wake the * SoC from deep sleep mode, in this example we are using AC 6. */ pmux_sel_save[0] = QM_SCSS_PMUX->pmux_sel[0]; pmux_sel_save[1] = QM_SCSS_PMUX->pmux_sel[1]; pmux_in_en_save = QM_SCSS_PMUX->pmux_in_en[0]; pmux_pullup_save = QM_SCSS_PMUX->pmux_pullup[0]; QM_SCSS_PMUX->pmux_sel[0] = QM_SCSS_PMUX->pmux_sel[1] = 0; QM_SCSS_PMUX->pmux_in_en[0] = BIT(WAKEUP_COMPARATOR_PIN); QM_SCSS_PMUX->pmux_pullup[0] = 0; /* Mux out comparator */ qm_pmux_select(QM_PIN_ID_6, QM_PMUX_FN_1); qm_pmux_input_en(QM_PIN_ID_6, true); soc_deep_sleep(); /* Restore previous pinmuxing settings. */ QM_SCSS_PMUX->pmux_sel[0] = pmux_sel_save[0]; QM_SCSS_PMUX->pmux_sel[1] = pmux_sel_save[1]; QM_SCSS_PMUX->pmux_in_en[0] = pmux_in_en_save; QM_SCSS_PMUX->pmux_pullup[0] = pmux_pullup_save; QM_PUTS("Wake up from deep sleep."); return 0; }