2020-02-25 01:40:52 +03:00
// 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 >
*/
# define pr_fmt(fmt) "tegra-cpuidle: " fmt
# include <linux/atomic.h>
# include <linux/cpuidle.h>
# include <linux/cpumask.h>
# include <linux/cpu_pm.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/platform_device.h>
# include <linux/types.h>
# include <linux/clk/tegra.h>
2020-02-25 01:40:54 +03:00
# include <linux/firmware/trusted_foundations.h>
2020-02-25 01:40:52 +03:00
# include <soc/tegra/cpuidle.h>
# include <soc/tegra/flowctrl.h>
# include <soc/tegra/fuse.h>
# include <soc/tegra/irq.h>
# include <soc/tegra/pm.h>
2020-02-25 01:40:55 +03:00
# include <soc/tegra/pmc.h>
2020-02-25 01:40:52 +03:00
# include <asm/cpuidle.h>
2020-02-25 01:40:54 +03:00
# include <asm/firmware.h>
2020-02-25 01:40:52 +03:00
# include <asm/smp_plat.h>
# include <asm/suspend.h>
enum tegra_state {
TEGRA_C1 ,
2020-02-25 01:40:53 +03:00
TEGRA_C7 ,
2020-02-25 01:40:52 +03:00
TEGRA_CC6 ,
TEGRA_STATE_COUNT ,
} ;
static atomic_t tegra_idle_barrier ;
static atomic_t tegra_abort_flag ;
static void tegra_cpuidle_report_cpus_state ( void )
{
unsigned long cpu , lcpu , csr ;
for_each_cpu ( lcpu , cpu_possible_mask ) {
cpu = cpu_logical_map ( lcpu ) ;
csr = flowctrl_read_cpu_csr ( cpu ) ;
pr_err ( " cpu%lu: online=%d flowctrl_csr=0x%08lx \n " ,
cpu , cpu_online ( lcpu ) , csr ) ;
}
}
static int tegra_cpuidle_wait_for_secondary_cpus_parking ( void )
{
unsigned int retries = 3 ;
while ( retries - - ) {
unsigned int delay_us = 10 ;
unsigned int timeout_us = 500 * 1000 / delay_us ;
/*
* 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 - 150u s in average and over 1000u s 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 " ) ;
return - ETIMEDOUT ;
}
static void tegra_cpuidle_unpark_secondary_cpus ( void )
{
unsigned int cpu , lcpu ;
for_each_cpu ( lcpu , cpu_online_mask ) {
cpu = cpu_logical_map ( lcpu ) ;
if ( cpu > 0 ) {
tegra_enable_cpu_clock ( cpu ) ;
tegra_cpu_out_of_reset ( cpu ) ;
flowctrl_write_cpu_halt ( cpu , 0 ) ;
}
}
}
static int tegra_cpuidle_cc6_enter ( unsigned int 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 ( ) ;
tegra_cpuidle_unpark_secondary_cpus ( ) ;
}
return ret ;
}
2020-02-25 01:40:53 +03:00
static int tegra_cpuidle_c7_enter ( void )
{
2020-02-25 01:40:54 +03:00
int err ;
2021-03-02 12:54:04 +03:00
err = call_firmware_op ( prepare_idle , TF_PM_MODE_LP2_NOFLUSH_L2 ) ;
if ( err & & err ! = - ENOSYS )
return err ;
2020-02-25 01:40:54 +03:00
2020-02-25 01:40:53 +03:00
return cpu_suspend ( 0 , tegra30_pm_secondary_cpu_suspend ) ;
}
2020-02-25 01:40:52 +03:00
static int 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 ) ;
}
cpuidle_coupled_parallel_barrier ( dev , & tegra_idle_barrier ) ;
if ( atomic_read ( & tegra_abort_flag ) ) {
cpuidle_coupled_parallel_barrier ( dev , & tegra_idle_barrier ) ;
atomic_set ( & tegra_abort_flag , 0 ) ;
return - EINTR ;
}
return 0 ;
}
2023-01-12 22:44:02 +03:00
static __cpuidle int tegra_cpuidle_state_enter ( struct cpuidle_device * dev ,
int index , unsigned int cpu )
2020-02-25 01:40:52 +03:00
{
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
int err ;
2020-02-25 01:40:52 +03:00
/*
* 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 ) {
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
err = tegra_cpuidle_coupled_barrier ( dev ) ;
if ( err )
return err ;
2020-02-25 01:40:52 +03:00
}
local_fiq_disable ( ) ;
2023-01-12 22:43:20 +03:00
tegra_pm_set_cpu_in_lp2 ( ) ;
2020-02-25 01:40:52 +03:00
cpu_pm_enter ( ) ;
2023-01-12 22:43:27 +03:00
ct_cpuidle_enter ( ) ;
2023-01-12 22:43:20 +03:00
2020-02-25 01:40:52 +03:00
switch ( index ) {
2020-02-25 01:40:53 +03:00
case TEGRA_C7 :
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
err = tegra_cpuidle_c7_enter ( ) ;
2020-02-25 01:40:53 +03:00
break ;
2020-02-25 01:40:52 +03:00
case TEGRA_CC6 :
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
err = tegra_cpuidle_cc6_enter ( cpu ) ;
2020-02-25 01:40:52 +03:00
break ;
default :
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
err = - EINVAL ;
2020-02-25 01:40:52 +03:00
break ;
}
2023-01-12 22:43:27 +03:00
ct_cpuidle_exit ( ) ;
2023-01-12 22:43:20 +03:00
2020-02-25 01:40:52 +03:00
cpu_pm_exit ( ) ;
2023-01-12 22:43:20 +03:00
tegra_pm_clear_cpu_in_lp2 ( ) ;
2020-02-25 01:40:52 +03:00
local_fiq_enable ( ) ;
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
return err ? : index ;
2020-02-25 01:40:52 +03:00
}
2020-02-25 01:40:53 +03:00
static int tegra_cpuidle_adjust_state_index ( int index , unsigned int 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 ;
}
2023-01-12 22:44:02 +03:00
static __cpuidle int tegra_cpuidle_enter ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
2020-02-25 01:40:52 +03:00
{
2023-01-12 22:43:20 +03:00
bool do_rcu = drv - > states [ index ] . flags & CPUIDLE_FLAG_RCU_IDLE ;
2020-02-25 01:40:52 +03:00
unsigned int cpu = cpu_logical_map ( dev - > cpu ) ;
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
int ret ;
2020-02-25 01:40:52 +03:00
2020-02-25 01:40:53 +03:00
index = tegra_cpuidle_adjust_state_index ( index , cpu ) ;
if ( dev - > states_usage [ index ] . disable )
return - 1 ;
2023-01-12 22:43:20 +03:00
if ( index = = TEGRA_C1 ) {
if ( do_rcu )
2023-01-12 22:43:27 +03:00
ct_cpuidle_enter ( ) ;
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
ret = arm_cpuidle_simple_enter ( dev , drv , index ) ;
2023-01-12 22:43:20 +03:00
if ( do_rcu )
2023-01-12 22:43:27 +03:00
ct_cpuidle_exit ( ) ;
2023-01-12 22:43:20 +03:00
} else
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
ret = tegra_cpuidle_state_enter ( dev , index , cpu ) ;
2020-02-25 01:40:53 +03:00
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
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 ;
}
2020-02-25 01:40:52 +03:00
cpuidle: tegra: Correctly handle result of arm_cpuidle_simple_enter()
The enter() callback of CPUIDLE drivers returns index of the entered idle
state on success or a negative value on failure. The negative value could
any negative value, i.e. it doesn't necessarily needs to be a error code.
That's because CPUIDLE core only cares about the fact of failure and not
about the reason of the enter() failure.
Like every other enter() callback, the arm_cpuidle_simple_enter() returns
the entered idle-index on success. Unlike some of other drivers, it never
fails. It happened that TEGRA_C1=index=err=0 in the code of cpuidle-tegra
driver, and thus, there is no problem for the cpuidle-tegra driver created
by the typo in the code which assumes that the arm_cpuidle_simple_enter()
returns a error code.
The arm_cpuidle_simple_enter() also may return a -ENODEV error if CPU_IDLE
is disabled in a kernel's config, but all CPUIDLE drivers are disabled if
CPU_IDLE is disabled, including the cpuidle-tegra driver. So we can't ever
see the error code from arm_cpuidle_simple_enter() today.
Of course the code may get some changes in the future and then the
typo may transform into a real bug, so let's correct the typo! The
tegra_cpuidle_state_enter() is now changed to make it return the entered
idle-index on success and negative error code on fail, which puts it on
par with the arm_cpuidle_simple_enter(), making code consistent in regards
to the error handling.
This patch fixes a minor typo in the code, it doesn't fix any bugs.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2020-07-09 20:35:32 +03:00
return index ;
2020-02-25 01:40:52 +03:00
}
2020-07-27 06:25:46 +03:00
static int tegra114_enter_s2idle ( struct cpuidle_device * dev ,
struct cpuidle_driver * drv ,
int index )
2020-02-25 01:40:54 +03:00
{
tegra_cpuidle_enter ( dev , drv , index ) ;
2020-07-27 06:25:46 +03:00
return 0 ;
2020-02-25 01:40:54 +03:00
}
2020-02-25 01:40:52 +03:00
/*
* The previous versions of Tegra CPUIDLE driver used a different " legacy "
* terminology for naming of the idling states , while this driver uses the
* new terminology .
*
* Mapping of the old terms into the new ones :
*
* Old | New
* - - - - - - - - -
* LP3 | C1 ( CPU core clock gating )
* LP2 | C7 ( CPU core power gating )
* LP2 | CC6 ( CPU cluster power gating )
*
* Note that that the older CPUIDLE driver versions didn ' t explicitly
* differentiate the LP2 states because these states either used the same
* code path or because CC6 wasn ' t supported .
*/
static struct cpuidle_driver tegra_idle_driver = {
. name = " tegra_idle " ,
. states = {
[ TEGRA_C1 ] = ARM_CPUIDLE_WFI_STATE_PWR ( 600 ) ,
2020-02-25 01:40:53 +03:00
[ TEGRA_C7 ] = {
. enter = tegra_cpuidle_enter ,
. exit_latency = 2000 ,
. target_residency = 2200 ,
. power_usage = 100 ,
2023-01-12 22:43:20 +03:00
. flags = CPUIDLE_FLAG_TIMER_STOP |
CPUIDLE_FLAG_RCU_IDLE ,
2020-02-25 01:40:53 +03:00
. name = " C7 " ,
. desc = " CPU core powered off " ,
} ,
2020-02-25 01:40:52 +03:00
[ TEGRA_CC6 ] = {
. enter = tegra_cpuidle_enter ,
. exit_latency = 5000 ,
. target_residency = 10000 ,
. power_usage = 0 ,
. flags = CPUIDLE_FLAG_TIMER_STOP |
2023-01-12 22:43:20 +03:00
CPUIDLE_FLAG_RCU_IDLE |
2020-02-25 01:40:52 +03:00
CPUIDLE_FLAG_COUPLED ,
. name = " CC6 " ,
. desc = " CPU cluster powered off " ,
} ,
} ,
. state_count = TEGRA_STATE_COUNT ,
. safe_state_index = TEGRA_C1 ,
} ;
static inline void tegra_cpuidle_disable_state ( enum tegra_state state )
{
cpuidle_driver_state_disabled ( & tegra_idle_driver , state , true ) ;
}
/*
* 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 ) ;
}
2020-02-25 01:40:54 +03:00
static void tegra_cpuidle_setup_tegra114_c7_state ( void )
{
struct cpuidle_state * s = & tegra_idle_driver . states [ TEGRA_C7 ] ;
s - > enter_s2idle = tegra114_enter_s2idle ;
s - > target_residency = 1000 ;
s - > exit_latency = 500 ;
}
2020-02-25 01:40:52 +03:00
static int tegra_cpuidle_probe ( struct platform_device * pdev )
{
2021-09-12 23:29:07 +03:00
if ( tegra_pmc_get_suspend_mode ( ) = = TEGRA_SUSPEND_NOT_READY )
return - EPROBE_DEFER ;
2020-02-25 01:40:55 +03:00
/* LP2 could be disabled in device-tree */
if ( tegra_pmc_get_suspend_mode ( ) < TEGRA_SUSPEND_LP2 )
tegra_cpuidle_disable_state ( TEGRA_CC6 ) ;
2020-02-25 01:40:52 +03:00
/*
* Required suspend - resume functionality , which is provided by the
* Tegra - arch core and PMC driver , is unavailable if PM - sleep option
* is disabled .
*/
2020-02-25 01:40:53 +03:00
if ( ! IS_ENABLED ( CONFIG_PM_SLEEP ) ) {
2021-03-02 12:54:05 +03:00
tegra_cpuidle_disable_state ( TEGRA_C7 ) ;
2020-02-25 01:40:52 +03:00
tegra_cpuidle_disable_state ( TEGRA_CC6 ) ;
2020-02-25 01:40:53 +03:00
}
/*
* 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 ;
2020-02-25 01:40:54 +03:00
case TEGRA114 :
case TEGRA124 :
tegra_cpuidle_setup_tegra114_c7_state ( ) ;
/* coupled CC6 (LP2) state isn't implemented yet */
tegra_cpuidle_disable_state ( TEGRA_CC6 ) ;
break ;
2020-02-25 01:40:53 +03:00
default :
return - EINVAL ;
}
2020-02-25 01:40:52 +03:00
return cpuidle_register ( & tegra_idle_driver , cpu_possible_mask ) ;
}
static struct platform_driver tegra_cpuidle_driver = {
. probe = tegra_cpuidle_probe ,
. driver = {
. name = " tegra-cpuidle " ,
} ,
} ;
builtin_platform_driver ( tegra_cpuidle_driver ) ;