1 /* 2 * CPPC (Collaborative Processor Performance Control) driver for 3 * interfacing with the CPUfreq layer and governors. See 4 * cppc_acpi.c for CPPC specific methods. 5 * 6 * (C) Copyright 2014, 2015 Linaro Ltd. 7 * Author: Ashwin Chaugule <ashwin.chaugule@linaro.org> 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; version 2 12 * of the License. 13 */ 14 15 #define pr_fmt(fmt) "CPPC Cpufreq:" fmt 16 17 #include <linux/kernel.h> 18 #include <linux/module.h> 19 #include <linux/delay.h> 20 #include <linux/cpu.h> 21 #include <linux/cpufreq.h> 22 #include <linux/vmalloc.h> 23 24 #include <acpi/cppc_acpi.h> 25 26 /* 27 * These structs contain information parsed from per CPU 28 * ACPI _CPC structures. 29 * e.g. For each CPU the highest, lowest supported 30 * performance capabilities, desired performance level 31 * requested etc. 32 */ 33 static struct cpudata **all_cpu_data; 34 35 static int cppc_cpufreq_set_target(struct cpufreq_policy *policy, 36 unsigned int target_freq, 37 unsigned int relation) 38 { 39 struct cpudata *cpu; 40 struct cpufreq_freqs freqs; 41 int ret = 0; 42 43 cpu = all_cpu_data[policy->cpu]; 44 45 cpu->perf_ctrls.desired_perf = target_freq; 46 freqs.old = policy->cur; 47 freqs.new = target_freq; 48 49 cpufreq_freq_transition_begin(policy, &freqs); 50 ret = cppc_set_perf(cpu->cpu, &cpu->perf_ctrls); 51 cpufreq_freq_transition_end(policy, &freqs, ret != 0); 52 53 if (ret) 54 pr_debug("Failed to set target on CPU:%d. ret:%d\n", 55 cpu->cpu, ret); 56 57 return ret; 58 } 59 60 static int cppc_verify_policy(struct cpufreq_policy *policy) 61 { 62 cpufreq_verify_within_cpu_limits(policy); 63 return 0; 64 } 65 66 static void cppc_cpufreq_stop_cpu(struct cpufreq_policy *policy) 67 { 68 int cpu_num = policy->cpu; 69 struct cpudata *cpu = all_cpu_data[cpu_num]; 70 int ret; 71 72 cpu->perf_ctrls.desired_perf = cpu->perf_caps.lowest_perf; 73 74 ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); 75 if (ret) 76 pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", 77 cpu->perf_caps.lowest_perf, cpu_num, ret); 78 } 79 80 static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) 81 { 82 struct cpudata *cpu; 83 unsigned int cpu_num = policy->cpu; 84 int ret = 0; 85 86 cpu = all_cpu_data[policy->cpu]; 87 88 cpu->cpu = cpu_num; 89 ret = cppc_get_perf_caps(policy->cpu, &cpu->perf_caps); 90 91 if (ret) { 92 pr_debug("Err reading CPU%d perf capabilities. ret:%d\n", 93 cpu_num, ret); 94 return ret; 95 } 96 97 policy->min = cpu->perf_caps.lowest_perf; 98 policy->max = cpu->perf_caps.highest_perf; 99 policy->cpuinfo.min_freq = policy->min; 100 policy->cpuinfo.max_freq = policy->max; 101 policy->shared_type = cpu->shared_type; 102 103 if (policy->shared_type == CPUFREQ_SHARED_TYPE_ANY) 104 cpumask_copy(policy->cpus, cpu->shared_cpu_map); 105 else if (policy->shared_type == CPUFREQ_SHARED_TYPE_ALL) { 106 /* Support only SW_ANY for now. */ 107 pr_debug("Unsupported CPU co-ord type\n"); 108 return -EFAULT; 109 } 110 111 cpumask_set_cpu(policy->cpu, policy->cpus); 112 cpu->cur_policy = policy; 113 114 /* Set policy->cur to max now. The governors will adjust later. */ 115 policy->cur = cpu->perf_ctrls.desired_perf = cpu->perf_caps.highest_perf; 116 117 ret = cppc_set_perf(cpu_num, &cpu->perf_ctrls); 118 if (ret) 119 pr_debug("Err setting perf value:%d on CPU:%d. ret:%d\n", 120 cpu->perf_caps.highest_perf, cpu_num, ret); 121 122 return ret; 123 } 124 125 static struct cpufreq_driver cppc_cpufreq_driver = { 126 .flags = CPUFREQ_CONST_LOOPS, 127 .verify = cppc_verify_policy, 128 .target = cppc_cpufreq_set_target, 129 .init = cppc_cpufreq_cpu_init, 130 .stop_cpu = cppc_cpufreq_stop_cpu, 131 .name = "cppc_cpufreq", 132 }; 133 134 static int __init cppc_cpufreq_init(void) 135 { 136 int i, ret = 0; 137 struct cpudata *cpu; 138 139 if (acpi_disabled) 140 return -ENODEV; 141 142 all_cpu_data = kzalloc(sizeof(void *) * num_possible_cpus(), GFP_KERNEL); 143 if (!all_cpu_data) 144 return -ENOMEM; 145 146 for_each_possible_cpu(i) { 147 all_cpu_data[i] = kzalloc(sizeof(struct cpudata), GFP_KERNEL); 148 if (!all_cpu_data[i]) 149 goto out; 150 151 cpu = all_cpu_data[i]; 152 if (!zalloc_cpumask_var(&cpu->shared_cpu_map, GFP_KERNEL)) 153 goto out; 154 } 155 156 ret = acpi_get_psd_map(all_cpu_data); 157 if (ret) { 158 pr_debug("Error parsing PSD data. Aborting cpufreq registration.\n"); 159 goto out; 160 } 161 162 ret = cpufreq_register_driver(&cppc_cpufreq_driver); 163 if (ret) 164 goto out; 165 166 return ret; 167 168 out: 169 for_each_possible_cpu(i) 170 kfree(all_cpu_data[i]); 171 172 kfree(all_cpu_data); 173 return -ENODEV; 174 } 175 176 static void __exit cppc_cpufreq_exit(void) 177 { 178 struct cpudata *cpu; 179 int i; 180 181 cpufreq_unregister_driver(&cppc_cpufreq_driver); 182 183 for_each_possible_cpu(i) { 184 cpu = all_cpu_data[i]; 185 free_cpumask_var(cpu->shared_cpu_map); 186 kfree(cpu); 187 } 188 189 kfree(all_cpu_data); 190 } 191 192 module_exit(cppc_cpufreq_exit); 193 MODULE_AUTHOR("Ashwin Chaugule"); 194 MODULE_DESCRIPTION("CPUFreq driver based on the ACPI CPPC v5.0+ spec"); 195 MODULE_LICENSE("GPL"); 196 197 late_initcall(cppc_cpufreq_init); 198