Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2013 ARM/Linaro
0004  *
0005  * Authors: Daniel Lezcano <daniel.lezcano@linaro.org>
0006  *          Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
0007  *          Nicolas Pitre <nicolas.pitre@linaro.org>
0008  *
0009  * Maintainer: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
0010  * Maintainer: Daniel Lezcano <daniel.lezcano@linaro.org>
0011  */
0012 #include <linux/cpuidle.h>
0013 #include <linux/cpu_pm.h>
0014 #include <linux/slab.h>
0015 #include <linux/of.h>
0016 
0017 #include <asm/cpu.h>
0018 #include <asm/cputype.h>
0019 #include <asm/cpuidle.h>
0020 #include <asm/mcpm.h>
0021 #include <asm/smp_plat.h>
0022 #include <asm/suspend.h>
0023 
0024 #include "dt_idle_states.h"
0025 
0026 static int bl_enter_powerdown(struct cpuidle_device *dev,
0027                   struct cpuidle_driver *drv, int idx);
0028 
0029 /*
0030  * NB: Owing to current menu governor behaviour big and LITTLE
0031  * index 1 states have to define exit_latency and target_residency for
0032  * cluster state since, when all CPUs in a cluster hit it, the cluster
0033  * can be shutdown. This means that when a single CPU enters this state
0034  * the exit_latency and target_residency values are somewhat overkill.
0035  * There is no notion of cluster states in the menu governor, so CPUs
0036  * have to define CPU states where possibly the cluster will be shutdown
0037  * depending on the state of other CPUs. idle states entry and exit happen
0038  * at random times; however the cluster state provides target_residency
0039  * values as if all CPUs in a cluster enter the state at once; this is
0040  * somewhat optimistic and behaviour should be fixed either in the governor
0041  * or in the MCPM back-ends.
0042  * To make this driver 100% generic the number of states and the exit_latency
0043  * target_residency values must be obtained from device tree bindings.
0044  *
0045  * exit_latency: refers to the TC2 vexpress test chip and depends on the
0046  * current cluster operating point. It is the time it takes to get the CPU
0047  * up and running when the CPU is powered up on cluster wake-up from shutdown.
0048  * Current values for big and LITTLE clusters are provided for clusters
0049  * running at default operating points.
0050  *
0051  * target_residency: it is the minimum amount of time the cluster has
0052  * to be down to break even in terms of power consumption. cluster
0053  * shutdown has inherent dynamic power costs (L2 writebacks to DRAM
0054  * being the main factor) that depend on the current operating points.
0055  * The current values for both clusters are provided for a CPU whose half
0056  * of L2 lines are dirty and require cleaning to DRAM, and takes into
0057  * account leakage static power values related to the vexpress TC2 testchip.
0058  */
0059 static struct cpuidle_driver bl_idle_little_driver = {
0060     .name = "little_idle",
0061     .owner = THIS_MODULE,
0062     .states[0] = ARM_CPUIDLE_WFI_STATE,
0063     .states[1] = {
0064         .enter          = bl_enter_powerdown,
0065         .exit_latency       = 700,
0066         .target_residency   = 2500,
0067         .flags          = CPUIDLE_FLAG_TIMER_STOP,
0068         .name           = "C1",
0069         .desc           = "ARM little-cluster power down",
0070     },
0071     .state_count = 2,
0072 };
0073 
0074 static const struct of_device_id bl_idle_state_match[] __initconst = {
0075     { .compatible = "arm,idle-state",
0076       .data = bl_enter_powerdown },
0077     { },
0078 };
0079 
0080 static struct cpuidle_driver bl_idle_big_driver = {
0081     .name = "big_idle",
0082     .owner = THIS_MODULE,
0083     .states[0] = ARM_CPUIDLE_WFI_STATE,
0084     .states[1] = {
0085         .enter          = bl_enter_powerdown,
0086         .exit_latency       = 500,
0087         .target_residency   = 2000,
0088         .flags          = CPUIDLE_FLAG_TIMER_STOP,
0089         .name           = "C1",
0090         .desc           = "ARM big-cluster power down",
0091     },
0092     .state_count = 2,
0093 };
0094 
0095 /*
0096  * notrace prevents trace shims from getting inserted where they
0097  * should not. Global jumps and ldrex/strex must not be inserted
0098  * in power down sequences where caches and MMU may be turned off.
0099  */
0100 static int notrace bl_powerdown_finisher(unsigned long arg)
0101 {
0102     /* MCPM works with HW CPU identifiers */
0103     unsigned int mpidr = read_cpuid_mpidr();
0104     unsigned int cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
0105     unsigned int cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
0106 
0107     mcpm_set_entry_vector(cpu, cluster, cpu_resume);
0108     mcpm_cpu_suspend();
0109 
0110     /* return value != 0 means failure */
0111     return 1;
0112 }
0113 
0114 /**
0115  * bl_enter_powerdown - Programs CPU to enter the specified state
0116  * @dev: cpuidle device
0117  * @drv: The target state to be programmed
0118  * @idx: state index
0119  *
0120  * Called from the CPUidle framework to program the device to the
0121  * specified target state selected by the governor.
0122  */
0123 static int bl_enter_powerdown(struct cpuidle_device *dev,
0124                 struct cpuidle_driver *drv, int idx)
0125 {
0126     cpu_pm_enter();
0127 
0128     cpu_suspend(0, bl_powerdown_finisher);
0129 
0130     /* signals the MCPM core that CPU is out of low power state */
0131     mcpm_cpu_powered_up();
0132 
0133     cpu_pm_exit();
0134 
0135     return idx;
0136 }
0137 
0138 static int __init bl_idle_driver_init(struct cpuidle_driver *drv, int part_id)
0139 {
0140     struct cpumask *cpumask;
0141     int cpu;
0142 
0143     cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
0144     if (!cpumask)
0145         return -ENOMEM;
0146 
0147     for_each_possible_cpu(cpu)
0148         if (smp_cpuid_part(cpu) == part_id)
0149             cpumask_set_cpu(cpu, cpumask);
0150 
0151     drv->cpumask = cpumask;
0152 
0153     return 0;
0154 }
0155 
0156 static const struct of_device_id compatible_machine_match[] = {
0157     { .compatible = "arm,vexpress,v2p-ca15_a7" },
0158     { .compatible = "google,peach" },
0159     {},
0160 };
0161 
0162 static int __init bl_idle_init(void)
0163 {
0164     int ret;
0165     struct device_node *root = of_find_node_by_path("/");
0166     const struct of_device_id *match_id;
0167 
0168     if (!root)
0169         return -ENODEV;
0170 
0171     /*
0172      * Initialize the driver just for a compliant set of machines
0173      */
0174     match_id = of_match_node(compatible_machine_match, root);
0175 
0176     of_node_put(root);
0177 
0178     if (!match_id)
0179         return -ENODEV;
0180 
0181     if (!mcpm_is_available())
0182         return -EUNATCH;
0183 
0184     /*
0185      * For now the differentiation between little and big cores
0186      * is based on the part number. A7 cores are considered little
0187      * cores, A15 are considered big cores. This distinction may
0188      * evolve in the future with a more generic matching approach.
0189      */
0190     ret = bl_idle_driver_init(&bl_idle_little_driver,
0191                   ARM_CPU_PART_CORTEX_A7);
0192     if (ret)
0193         return ret;
0194 
0195     ret = bl_idle_driver_init(&bl_idle_big_driver, ARM_CPU_PART_CORTEX_A15);
0196     if (ret)
0197         goto out_uninit_little;
0198 
0199     /* Start at index 1, index 0 standard WFI */
0200     ret = dt_init_idle_driver(&bl_idle_big_driver, bl_idle_state_match, 1);
0201     if (ret < 0)
0202         goto out_uninit_big;
0203 
0204     /* Start at index 1, index 0 standard WFI */
0205     ret = dt_init_idle_driver(&bl_idle_little_driver,
0206                   bl_idle_state_match, 1);
0207     if (ret < 0)
0208         goto out_uninit_big;
0209 
0210     ret = cpuidle_register(&bl_idle_little_driver, NULL);
0211     if (ret)
0212         goto out_uninit_big;
0213 
0214     ret = cpuidle_register(&bl_idle_big_driver, NULL);
0215     if (ret)
0216         goto out_unregister_little;
0217 
0218     return 0;
0219 
0220 out_unregister_little:
0221     cpuidle_unregister(&bl_idle_little_driver);
0222 out_uninit_big:
0223     kfree(bl_idle_big_driver.cpumask);
0224 out_uninit_little:
0225     kfree(bl_idle_little_driver.cpumask);
0226 
0227     return ret;
0228 }
0229 device_initcall(bl_idle_init);