Home | History | Annotate | Download | only in t210
      1 /*
      2  * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved.
      3  *
      4  * SPDX-License-Identifier: BSD-3-Clause
      5  */
      6 
      7 #include <arch_helpers.h>
      8 #include <assert.h>
      9 #include <debug.h>
     10 #include <delay_timer.h>
     11 #include <flowctrl.h>
     12 #include <mmio.h>
     13 #include <platform.h>
     14 #include <platform_def.h>
     15 #include <pmc.h>
     16 #include <psci.h>
     17 #include <tegra_def.h>
     18 #include <tegra_private.h>
     19 
     20 /*
     21  * Register used to clear CPU reset signals. Each CPU has two reset
     22  * signals: CPU reset (3:0) and Core reset (19:16).
     23  */
     24 #define CPU_CMPLX_RESET_CLR		0x454
     25 #define CPU_CORE_RESET_MASK		0x10001
     26 
     27 /* Clock and Reset controller registers for system clock's settings */
     28 #define SCLK_RATE			0x30
     29 #define SCLK_BURST_POLICY		0x28
     30 #define SCLK_BURST_POLICY_DEFAULT	0x10000000
     31 
     32 static int cpu_powergate_mask[PLATFORM_MAX_CPUS_PER_CLUSTER];
     33 
     34 int32_t tegra_soc_validate_power_state(unsigned int power_state,
     35 					psci_power_state_t *req_state)
     36 {
     37 	int state_id = psci_get_pstate_id(power_state);
     38 
     39 	/* Sanity check the requested state id */
     40 	switch (state_id) {
     41 	case PSTATE_ID_CORE_POWERDN:
     42 		/*
     43 		 * Core powerdown request only for afflvl 0
     44 		 */
     45 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id & 0xff;
     46 
     47 		break;
     48 
     49 	case PSTATE_ID_CLUSTER_IDLE:
     50 	case PSTATE_ID_CLUSTER_POWERDN:
     51 		/*
     52 		 * Cluster powerdown/idle request only for afflvl 1
     53 		 */
     54 		req_state->pwr_domain_state[MPIDR_AFFLVL1] = state_id;
     55 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = state_id;
     56 
     57 		break;
     58 
     59 	case PSTATE_ID_SOC_POWERDN:
     60 		/*
     61 		 * System powerdown request only for afflvl 2
     62 		 */
     63 		for (uint32_t i = MPIDR_AFFLVL0; i < PLAT_MAX_PWR_LVL; i++)
     64 			req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
     65 
     66 		req_state->pwr_domain_state[PLAT_MAX_PWR_LVL] =
     67 			PLAT_SYS_SUSPEND_STATE_ID;
     68 
     69 		break;
     70 
     71 	default:
     72 		ERROR("%s: unsupported state id (%d)\n", __func__, state_id);
     73 		return PSCI_E_INVALID_PARAMS;
     74 	}
     75 
     76 	return PSCI_E_SUCCESS;
     77 }
     78 
     79 /*******************************************************************************
     80  * Platform handler to calculate the proper target power level at the
     81  * specified affinity level
     82  ******************************************************************************/
     83 plat_local_state_t tegra_soc_get_target_pwr_state(unsigned int lvl,
     84 					     const plat_local_state_t *states,
     85 					     unsigned int ncpu)
     86 {
     87 	plat_local_state_t target = *states;
     88 	int cpu = plat_my_core_pos();
     89 	int core_pos = read_mpidr() & MPIDR_CPU_MASK;
     90 
     91 	/* get the power state at this level */
     92 	if (lvl == MPIDR_AFFLVL1)
     93 		target = *(states + core_pos);
     94 	if (lvl == MPIDR_AFFLVL2)
     95 		target = *(states + cpu);
     96 
     97 	/* Cluster idle/power-down */
     98 	if ((lvl == MPIDR_AFFLVL1) && ((target == PSTATE_ID_CLUSTER_IDLE) ||
     99 	    (target == PSTATE_ID_CLUSTER_POWERDN))) {
    100 		return target;
    101 	}
    102 
    103 	/* System Suspend */
    104 	if (((lvl == MPIDR_AFFLVL2) || (lvl == MPIDR_AFFLVL1)) &&
    105 	    (target == PSTATE_ID_SOC_POWERDN))
    106 		return PSTATE_ID_SOC_POWERDN;
    107 
    108 	/* default state */
    109 	return PSCI_LOCAL_STATE_RUN;
    110 }
    111 
    112 int tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state)
    113 {
    114 	u_register_t mpidr = read_mpidr();
    115 	const plat_local_state_t *pwr_domain_state =
    116 		target_state->pwr_domain_state;
    117 	unsigned int stateid_afflvl2 = pwr_domain_state[MPIDR_AFFLVL2];
    118 	unsigned int stateid_afflvl1 = pwr_domain_state[MPIDR_AFFLVL1];
    119 	unsigned int stateid_afflvl0 = pwr_domain_state[MPIDR_AFFLVL0];
    120 
    121 	if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) {
    122 
    123 		assert((stateid_afflvl0 == PLAT_MAX_OFF_STATE) ||
    124 		       (stateid_afflvl0 == PSTATE_ID_SOC_POWERDN));
    125 		assert((stateid_afflvl1 == PLAT_MAX_OFF_STATE) ||
    126 		       (stateid_afflvl1 == PSTATE_ID_SOC_POWERDN));
    127 
    128 		/* suspend the entire soc */
    129 		tegra_fc_soc_powerdn(mpidr);
    130 
    131 	} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_IDLE) {
    132 
    133 		assert(stateid_afflvl0 == PSTATE_ID_CLUSTER_IDLE);
    134 
    135 		/* Prepare for cluster idle */
    136 		tegra_fc_cluster_idle(mpidr);
    137 
    138 	} else if (stateid_afflvl1 == PSTATE_ID_CLUSTER_POWERDN) {
    139 
    140 		assert(stateid_afflvl0 == PSTATE_ID_CLUSTER_POWERDN);
    141 
    142 		/* Prepare for cluster powerdn */
    143 		tegra_fc_cluster_powerdn(mpidr);
    144 
    145 	} else if (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN) {
    146 
    147 		/* Prepare for cpu powerdn */
    148 		tegra_fc_cpu_powerdn(mpidr);
    149 
    150 	} else {
    151 		ERROR("%s: Unknown state id\n", __func__);
    152 		return PSCI_E_NOT_SUPPORTED;
    153 	}
    154 
    155 	return PSCI_E_SUCCESS;
    156 }
    157 
    158 int tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state)
    159 {
    160 	uint32_t val;
    161 
    162 	/*
    163 	 * Check if we are exiting from SOC_POWERDN.
    164 	 */
    165 	if (target_state->pwr_domain_state[PLAT_MAX_PWR_LVL] ==
    166 			PLAT_SYS_SUSPEND_STATE_ID) {
    167 
    168 		/*
    169 		 * Lock scratch registers which hold the CPU vectors
    170 		 */
    171 		tegra_pmc_lock_cpu_vectors();
    172 
    173 		/*
    174 		 * Enable WRAP to INCR burst type conversions for
    175 		 * incoming requests on the AXI slave ports.
    176 		 */
    177 		val = mmio_read_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG);
    178 		val &= ~ENABLE_UNSUP_TX_ERRORS;
    179 		val |= ENABLE_WRAP_TO_INCR_BURSTS;
    180 		mmio_write_32(TEGRA_MSELECT_BASE + MSELECT_CONFIG, val);
    181 
    182 		/*
    183 		 * Restore Boot and Power Management Processor (BPMP) reset
    184 		 * address and reset it.
    185 		 */
    186 		tegra_fc_reset_bpmp();
    187 	}
    188 
    189 	/*
    190 	 * T210 has a dedicated ARMv7 boot and power mgmt processor, BPMP. It's
    191 	 * used for power management and boot purposes. Inform the BPMP that
    192 	 * we have completed the cluster power up.
    193 	 */
    194 	tegra_fc_lock_active_cluster();
    195 
    196 	return PSCI_E_SUCCESS;
    197 }
    198 
    199 int tegra_soc_pwr_domain_on(u_register_t mpidr)
    200 {
    201 	int cpu = mpidr & MPIDR_CPU_MASK;
    202 	uint32_t mask = CPU_CORE_RESET_MASK << cpu;
    203 
    204 	/* Deassert CPU reset signals */
    205 	mmio_write_32(TEGRA_CAR_RESET_BASE + CPU_CMPLX_RESET_CLR, mask);
    206 
    207 	/* Turn on CPU using flow controller or PMC */
    208 	if (cpu_powergate_mask[cpu] == 0) {
    209 		tegra_pmc_cpu_on(cpu);
    210 		cpu_powergate_mask[cpu] = 1;
    211 	} else {
    212 		tegra_fc_cpu_on(cpu);
    213 	}
    214 
    215 	return PSCI_E_SUCCESS;
    216 }
    217 
    218 int tegra_soc_pwr_domain_off(const psci_power_state_t *target_state)
    219 {
    220 	tegra_fc_cpu_off(read_mpidr() & MPIDR_CPU_MASK);
    221 	return PSCI_E_SUCCESS;
    222 }
    223 
    224 int tegra_soc_prepare_system_reset(void)
    225 {
    226 	/*
    227 	 * Set System Clock (SCLK) to POR default so that the clock source
    228 	 * for the PMC APB clock would not be changed due to system reset.
    229 	 */
    230 	mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_BURST_POLICY,
    231 		       SCLK_BURST_POLICY_DEFAULT);
    232 	mmio_write_32((uintptr_t)TEGRA_CAR_RESET_BASE + SCLK_RATE, 0);
    233 
    234 	/* Wait 1 ms to make sure clock source/device logic is stabilized. */
    235 	mdelay(1);
    236 
    237 	return PSCI_E_SUCCESS;
    238 }
    239