// SPDX-License-Identifier: GPL-2.0-only /* * CPU idle driver for Tegra CPUs * * Copyright (c) 2010-2013, NVIDIA Corporation. * Copyright (c) 2011 Google, Inc. * Author: Colin Cross <ccross@android.com> * Gary King <gking@nvidia.com> * * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com> * * Tegra20/124 driver unification by Dmitry Osipenko <digetx@gmail.com>
*/
/* * The primary CPU0 core shall wait for the secondaries * shutdown in order to power-off CPU's cluster safely. * The timeout value depends on the current CPU frequency, * it takes about 40-150us in average and over 1000us in * a worst case scenario.
*/ do { if (tegra_cpu_rail_off_ready()) return 0;
udelay(delay_us);
} while (timeout_us--);
pr_err("secondary CPU taking too long to park\n");
tegra_cpuidle_report_cpus_state();
}
pr_err("timed out waiting secondaries to park\n");
staticint tegra_cpuidle_cc6_enter(unsignedint cpu)
{ int ret;
if (cpu > 0) {
ret = cpu_suspend(cpu, tegra_pm_park_secondary_cpu);
} else {
ret = tegra_cpuidle_wait_for_secondary_cpus_parking(); if (!ret)
ret = tegra_pm_enter_lp2();
staticint tegra_cpuidle_coupled_barrier(struct cpuidle_device *dev)
{ if (tegra_pending_sgi()) { /* * CPU got local interrupt that will be lost after GIC's * shutdown because GIC driver doesn't save/restore the * pending SGI state across CPU cluster PM. Abort and retry * next time.
*/
atomic_set(&tegra_abort_flag, 1);
}
if (atomic_read(&tegra_abort_flag)) {
cpuidle_coupled_parallel_barrier(dev, &tegra_idle_barrier);
atomic_set(&tegra_abort_flag, 0); return -EINTR;
}
return 0;
}
static __cpuidle int tegra_cpuidle_state_enter(struct cpuidle_device *dev, int index, unsignedint cpu)
{ int err;
/* * CC6 state is the "CPU cluster power-off" state. In order to * enter this state, at first the secondary CPU cores need to be * parked into offline mode, then the last CPU should clean out * remaining dirty cache lines into DRAM and trigger Flow Controller * logic that turns off the cluster's power domain (which includes * CPU cores, GIC and L2 cache).
*/ if (index == TEGRA_CC6) {
err = tegra_cpuidle_coupled_barrier(dev); if (err) return err;
}
staticint tegra_cpuidle_adjust_state_index(int index, unsignedint cpu)
{ /* * On Tegra30 CPU0 can't be power-gated separately from secondary * cores because it gates the whole CPU cluster.
*/ if (cpu > 0 || index != TEGRA_C7 || tegra_get_chip_id() != TEGRA30) return index;
/* put CPU0 into C1 if C7 is requested and secondaries are online */ if (!IS_ENABLED(CONFIG_PM_SLEEP) || num_online_cpus() > 1)
index = TEGRA_C1; else
index = TEGRA_CC6;
return index;
}
static __cpuidle int tegra_cpuidle_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index)
{ bool do_rcu = drv->states[index].flags & CPUIDLE_FLAG_RCU_IDLE; unsignedint cpu = cpu_logical_map(dev->cpu); int ret;
index = tegra_cpuidle_adjust_state_index(index, cpu); if (dev->states_usage[index].disable) return -1;
if (index == TEGRA_C1) { if (do_rcu)
ct_cpuidle_enter();
ret = arm_cpuidle_simple_enter(dev, drv, index); if (do_rcu)
ct_cpuidle_exit();
} else
ret = tegra_cpuidle_state_enter(dev, index, cpu);
if (ret < 0) { if (ret != -EINTR || index != TEGRA_CC6)
pr_err_once("failed to enter state %d err: %d\n",
index, ret);
index = -1;
} else {
index = ret;
}
/* * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether * they are legacy IRQs or MSI, are lost when CC6 is enabled. To work around * this, simply disable CC6 if the PCI driver and DT node are both enabled.
*/ void tegra_cpuidle_pcie_irqs_in_use(void)
{ struct cpuidle_state *state_cc6 = &tegra_idle_driver.states[TEGRA_CC6];
if ((state_cc6->flags & CPUIDLE_FLAG_UNUSABLE) ||
tegra_get_chip_id() != TEGRA20) return;
pr_info("disabling CC6 state, since PCIe IRQs are in use\n");
tegra_cpuidle_disable_state(TEGRA_CC6);
}
/* LP2 could be disabled in device-tree */ if (tegra_pmc_get_suspend_mode() < TEGRA_SUSPEND_LP2)
tegra_cpuidle_disable_state(TEGRA_CC6);
/* * Required suspend-resume functionality, which is provided by the * Tegra-arch core and PMC driver, is unavailable if PM-sleep option * is disabled.
*/ if (!IS_ENABLED(CONFIG_PM_SLEEP)) {
tegra_cpuidle_disable_state(TEGRA_C7);
tegra_cpuidle_disable_state(TEGRA_CC6);
}
/* * Generic WFI state (also known as C1 or LP3) and the coupled CPU * cluster power-off (CC6 or LP2) states are common for all Tegra SoCs.
*/ switch (tegra_get_chip_id()) { case TEGRA20: /* Tegra20 isn't capable to power-off individual CPU cores */
tegra_cpuidle_disable_state(TEGRA_C7); break;
case TEGRA30: break;
case TEGRA114: case TEGRA124:
tegra_cpuidle_setup_tegra114_c7_state();
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.