Linux Audio

Check our new training course

Embedded Linux Audio

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

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2017, Intel Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the Intel Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE INTEL CORPORATION OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "power_states.h"
#include "ss_power_states.h"
#include "qm_isr.h"
#include "qm_sensor_regs.h"
#include "soc_watch.h"
#include "qm_common.h"

/* Sensor Subsystem sleep operand definition.
 * Only a subset applies as internal sensor RTC
 * is not available.
 *
 *  OP | Core | Timers | RTC
 * 000 |    0 |      1 |   1 <-- used for SS1
 * 001 |    0 |      0 |   1
 * 010 |    0 |      1 |   0
 * 011 |    0 |      0 |   0 <-- used for SS2
 * 100 |    0 |      0 |   0
 * 101 |    0 |      0 |   0
 * 110 |    0 |      0 |   0
 * 111 |    0 |      0 |   0
 *
 * sleep opcode argument:
 *  - [7:5] : Sleep Operand
 *  - [4]   : Interrupt enable
 *  - [3:0] : Interrupt threshold value
 */

#define SLEEP_INT_EN BIT(4)
#define SLEEP_TIMER_ON (0x0)
#define SLEEP_TIMER_OFF (0x20)
#define SLEEP_TIMER_RTC_OFF (0x60)

#define SS_STATE_1_TIMER_ON (SLEEP_TIMER_ON | SLEEP_INT_EN)
#define SS_STATE_1_TIMER_OFF (SLEEP_TIMER_OFF | SLEEP_INT_EN)
#define SS_STATE_2 (SLEEP_TIMER_RTC_OFF | SLEEP_INT_EN)

void qm_ss_power_soc_lpss_enable()
{
	uint32_t creg_mst0_ctrl = 0;

	creg_mst0_ctrl = __builtin_arc_lr(QM_SS_CREG_BASE);

	/*
	 * Clock gate the sensor peripherals at CREG level.
	 * This clock gating is independent of the peripheral-specific clock
	 * gating provided in ss_clk.h .
	 */
	creg_mst0_ctrl |= (QM_SS_IO_CREG_MST0_CTRL_ADC_CLK_GATE |
			   QM_SS_IO_CREG_MST0_CTRL_I2C1_CLK_GATE |
			   QM_SS_IO_CREG_MST0_CTRL_I2C0_CLK_GATE |
			   QM_SS_IO_CREG_MST0_CTRL_SPI1_CLK_GATE |
			   QM_SS_IO_CREG_MST0_CTRL_SPI0_CLK_GATE);

	__builtin_arc_sr(creg_mst0_ctrl, QM_SS_CREG_BASE);

	QM_SCSS_CCU->ccu_lp_clk_ctl |= QM_SCSS_CCU_SS_LPS_EN;
	SOC_WATCH_LOG_EVENT(SOCW_EVENT_REGISTER, SOCW_REG_CCU_LP_CLK_CTL);
}

void qm_ss_power_soc_lpss_disable()
{
	uint32_t creg_mst0_ctrl = 0;

	creg_mst0_ctrl = __builtin_arc_lr(QM_SS_CREG_BASE);

	/*
	 * Restore clock gate of the sensor peripherals at CREG level.
	 * CREG is not used anywhere else so we can safely restore
	 * the configuration to its POR default.
	 */
	creg_mst0_ctrl &= ~(QM_SS_IO_CREG_MST0_CTRL_ADC_CLK_GATE |
			    QM_SS_IO_CREG_MST0_CTRL_I2C1_CLK_GATE |
			    QM_SS_IO_CREG_MST0_CTRL_I2C0_CLK_GATE |
			    QM_SS_IO_CREG_MST0_CTRL_SPI1_CLK_GATE |
			    QM_SS_IO_CREG_MST0_CTRL_SPI0_CLK_GATE);

	__builtin_arc_sr(creg_mst0_ctrl, QM_SS_CREG_BASE);

	QM_SCSS_CCU->ccu_lp_clk_ctl &= ~QM_SCSS_CCU_SS_LPS_EN;
	SOC_WATCH_LOG_EVENT(SOCW_EVENT_REGISTER, SOCW_REG_CCU_LP_CLK_CTL);
}

/* Enter SS1 :
 * SLEEP + sleep operand
 * __builtin_arc_sleep is not used here as it does not propagate sleep operand.
 */
void qm_ss_power_cpu_ss1(const qm_ss_power_cpu_ss1_mode_t mode)
{
	uint32_t priority;

	priority =
	    (__builtin_arc_lr(QM_SS_AUX_STATUS32) & QM_SS_STATUS32_E_MASK) >> 1;

	SOC_WATCH_LOG_EVENT(SOCW_ARC_EVENT_SS1, 0);

	/* Enter SS1 */
	switch (mode) {
	case QM_SS_POWER_CPU_SS1_TIMER_OFF:
		__asm__ __volatile__("sleep %0"
				     :
				     : "r"(SS_STATE_1_TIMER_OFF | priority)
				     : "memory", "cc");
		break;
	case QM_SS_POWER_CPU_SS1_TIMER_ON:
	default:
		__asm__ __volatile__("sleep %0"
				     :
				     : "r"(SS_STATE_1_TIMER_ON | priority)
				     : "memory", "cc");
		break;
	}
}

/* Enter SS2 :
 * SLEEP + sleep operand
 * __builtin_arc_sleep is not used here as it does not propagate sleep operand.
 */
void qm_ss_power_cpu_ss2(void)
{
	uint32_t priority;

	priority =
	    (__builtin_arc_lr(QM_SS_AUX_STATUS32) & QM_SS_STATUS32_E_MASK) >> 1;

	SOC_WATCH_LOG_EVENT(SOCW_ARC_EVENT_SS2, 0);

	/* Enter SS2 */
	__asm__ __volatile__("sleep %0"
			     :
			     : "r"(SS_STATE_2 | priority)
			     : "memory", "cc");
}

#if (ENABLE_RESTORE_CONTEXT)
extern uint32_t arc_restore_addr;
uint32_t cpu_context[33];
void qm_ss_power_soc_sleep_restore(void)
{
	/*
	 * Save sensor restore trap address.
	 * The first parameter in this macro represents the label defined in
	 * the qm_ss_restore_context() macro, which is actually the restore
	 * trap address.
	 */
	qm_ss_set_resume_vector(sleep_restore_trap, arc_restore_addr);

	/* Save ARC execution context. */
	qm_ss_save_context(cpu_context);

	/* Set restore flags. */
	qm_power_soc_set_ss_restore_flag();

	/* Enter sleep. */
	qm_power_soc_sleep();

	/*
	 * Restore sensor execution context.
	 * The sensor startup code will jump to this location after waking up
	 * from sleep. The restore trap address is the label defined in the
	 * macro and the label is exposed here through the first parameter.
	 */
	qm_ss_restore_context(sleep_restore_trap, cpu_context);
}
void qm_ss_power_soc_deep_sleep_restore(void)
{
	/*
	 * Save sensor restore trap address.
	 * The first parameter in this macro represents the label defined in
	 * the qm_ss_restore_context() macro, which is actually the restore
	 * trap address.
	 */
	qm_ss_set_resume_vector(deep_sleep_restore_trap, arc_restore_addr);

	/* Save ARC execution context. */
	qm_ss_save_context(cpu_context);

	/* Set restore flags. */
	qm_power_soc_set_ss_restore_flag();

	/* Enter sleep. */
	qm_power_soc_deep_sleep();

	/*
	 * Restore sensor execution context.
	 * The sensor startup code will jump to this location after waking up
	 * from sleep. The restore trap address is the label defined in the
	 * macro and the label is exposed here through the first parameter.
	 */
	qm_ss_restore_context(deep_sleep_restore_trap, cpu_context);
}

void qm_ss_power_sleep_wait(void)
{
	/*
	 * Save sensor restore trap address.
	 * The first parameter in this macro represents the label defined in
	 * the qm_ss_restore_context() macro, which is actually the restore
	 * trap address.
	 */
	qm_ss_set_resume_vector(sleep_restore_trap, arc_restore_addr);

	/* Save ARC execution context. */
	qm_ss_save_context(cpu_context);

	/* Set restore flags. */
	qm_power_soc_set_ss_restore_flag();

	/* Enter SS1 and stay in it until sleep and wake-up. */
	while (1) {
		qm_ss_power_cpu_ss1(QM_SS_POWER_CPU_SS1_TIMER_ON);
	}

	/*
	 * Restore sensor execution context.
	 * The sensor startup code will jump to this location after waking up
	 * from sleep. The restore trap address is the label defined in the
	 * macro and the label is exposed here through the first parameter.
	 */
	qm_ss_restore_context(sleep_restore_trap, cpu_context);
}

void qm_power_soc_set_ss_restore_flag(void)
{
	QM_SCSS_GP->gps0 |= BIT(QM_GPS0_BIT_SENSOR_WAKEUP);
}

#endif /* ENABLE_RESTORE_CONTEXT */