blob: 37febc58038d13c5249e7cbc69e73e1404e171f4 [file] [log] [blame]
/*
* Copyright (c) 2016 Intel Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <zephyr.h>
#include <sys_io.h>
#include <misc/__assert.h>
#include <power.h>
#include <soc_power.h>
#include <init.h>
#include <kernel_structs.h>
#include "ss_power_states.h"
#define SLEEP_MODE_CORE_OFF (0x0)
#define SLEEP_MODE_CORE_TIMERS_RTC_OFF (0x60)
#define ENABLE_INTERRUPTS (BIT(4) | _ARC_V2_STATUS32_E(_ARC_V2_DEF_IRQ_LEVEL))
#define ARC_SS1 (SLEEP_MODE_CORE_OFF | ENABLE_INTERRUPTS)
#define ARC_SS2 (SLEEP_MODE_CORE_TIMERS_RTC_OFF | ENABLE_INTERRUPTS)
#if (CONFIG_RTC)
static unsigned int rtc_trigger;
#endif
#if (CONFIG_COUNTER)
static unsigned int aonpt_trigger;
#endif
/* QMSI does not set the interrupt enable bit in the sleep operand.
* For the time being, implement this in Zephyr.
* This will be removed once QMSI is fixed.
*/
static void enter_arc_state(int mode)
{
/* The sensor cannot be woken up with an edge triggered
* interrupt from the RTC and the AON Counter.
* Switch to Level triggered interrupts and restore
* the setting when waking up.
*/
#if (CONFIG_RTC)
rtc_trigger = _arc_v2_irq_unit_trigger_get(QM_IRQ_RTC_0_INT_VECTOR);
_arc_v2_irq_unit_trigger_set(QM_IRQ_RTC_0_INT_VECTOR,
_ARC_V2_INT_LEVEL);
#endif
#if (CONFIG_COUNTER)
aonpt_trigger = _arc_v2_irq_unit_trigger_get(QM_IRQ_AONPT_0_INT_VECTOR);
_arc_v2_irq_unit_trigger_set(QM_IRQ_AONPT_0_INT_VECTOR,
_ARC_V2_INT_LEVEL);
#endif
/* Enter SSx */
__asm__ volatile("sleep %0"
: /* No output operands. */
: /* Input operands. */
"r"(mode));
}
void _sys_soc_set_power_state(enum power_states state)
{
switch (state) {
case SYS_POWER_STATE_CPU_LPS:
ss_power_soc_lpss_enable();
enter_arc_state(ARC_SS2);
break;
case SYS_POWER_STATE_CPU_LPS_1:
enter_arc_state(ARC_SS2);
break;
case SYS_POWER_STATE_CPU_LPS_2:
enter_arc_state(ARC_SS1);
break;
case SYS_POWER_STATE_DEEP_SLEEP:
case SYS_POWER_STATE_DEEP_SLEEP_1:
/* Sleep states are not yet supported for ARC. */
break;
default:
break;
}
}
static void restore_interrupt_trigger(void)
{
/* Restore the RTC and AONC interrupt trigger after wake up */
#if (CONFIG_RTC)
_arc_v2_irq_unit_trigger_set(QM_IRQ_RTC_0_INT_VECTOR, rtc_trigger);
#endif
#if (CONFIG_COUNTER)
_arc_v2_irq_unit_trigger_set(QM_IRQ_AONPT_0_INT_VECTOR, aonpt_trigger);
#endif
}
void _sys_soc_power_state_post_ops(enum power_states state)
{
uint32_t limit;
switch (state) {
case SYS_POWER_STATE_CPU_LPS_2:
/* Restore interrupts to their previous trigger type. */
restore_interrupt_trigger();
break;
case SYS_POWER_STATE_CPU_LPS:
ss_power_soc_lpss_disable();
case SYS_POWER_STATE_CPU_LPS_1:
restore_interrupt_trigger();
/* Expire the timer as it is disabled in SS2. */
limit = _arc_v2_aux_reg_read(_ARC_V2_TMR0_LIMIT);
_arc_v2_aux_reg_write(_ARC_V2_TMR0_COUNT, limit - 1);
break;
default:
break;
}
}