Linux preempt-rt

Check our new training course

Real-Time Linux with PREEMPT_RT

Check our new training course
with Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * 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;
	}
}