0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
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
0097
0098
0099
0100 static int notrace bl_powerdown_finisher(unsigned long arg)
0101 {
0102
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
0111 return 1;
0112 }
0113
0114
0115
0116
0117
0118
0119
0120
0121
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
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
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
0186
0187
0188
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
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
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);