Abhijeet Dharmapurikar | 7e37e6e | 2012-08-23 18:58:44 -0700 | [diff] [blame] | 1 | /* Copyright (c) 2012, The Linux Foundation. All rights reserved. |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 2 | * |
| 3 | * This program is free software; you can redistribute it and/or modify |
| 4 | * it under the terms of the GNU General Public License version 2 and |
| 5 | * only version 2 as published by the Free Software Foundation. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | */ |
| 13 | |
| 14 | #include <linux/kernel.h> |
| 15 | #include <linux/init.h> |
| 16 | #include <linux/module.h> |
| 17 | #include <linux/mutex.h> |
| 18 | #include <linux/kobject.h> |
| 19 | #include <linux/cpufreq.h> |
| 20 | #include <linux/platform_device.h> |
| 21 | #include <mach/msm_dcvs.h> |
| 22 | |
| 23 | struct msm_gov { |
| 24 | int cpu; |
| 25 | unsigned int cur_freq; |
| 26 | unsigned int min_freq; |
| 27 | unsigned int max_freq; |
| 28 | struct msm_dcvs_freq gov_notifier; |
| 29 | struct cpufreq_policy *policy; |
| 30 | }; |
| 31 | |
| 32 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct mutex, gov_mutex); |
| 33 | static DEFINE_PER_CPU_SHARED_ALIGNED(struct msm_gov, msm_gov_info); |
| 34 | static char core_name[NR_CPUS][10]; |
| 35 | |
| 36 | static void msm_gov_check_limits(struct cpufreq_policy *policy) |
| 37 | { |
| 38 | struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu); |
Abhijeet Dharmapurikar | c43f0db | 2012-08-31 20:42:53 -0700 | [diff] [blame] | 39 | struct msm_dcvs_freq *dcvs_notifier = |
| 40 | &(per_cpu(msm_gov_info, policy->cpu).gov_notifier); |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 41 | |
| 42 | if (policy->max < gov->cur_freq) |
| 43 | __cpufreq_driver_target(policy, policy->max, |
| 44 | CPUFREQ_RELATION_H); |
Abhijeet Dharmapurikar | c43f0db | 2012-08-31 20:42:53 -0700 | [diff] [blame] | 45 | else if (policy->min > gov->cur_freq) |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 46 | __cpufreq_driver_target(policy, policy->min, |
| 47 | CPUFREQ_RELATION_L); |
| 48 | else |
| 49 | __cpufreq_driver_target(policy, gov->cur_freq, |
| 50 | CPUFREQ_RELATION_L); |
| 51 | |
| 52 | gov->cur_freq = policy->cur; |
| 53 | gov->min_freq = policy->min; |
| 54 | gov->max_freq = policy->max; |
Abhijeet Dharmapurikar | c43f0db | 2012-08-31 20:42:53 -0700 | [diff] [blame] | 55 | msm_dcvs_update_limits(dcvs_notifier); |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 56 | } |
| 57 | |
| 58 | static int msm_dcvs_freq_set(struct msm_dcvs_freq *self, |
| 59 | unsigned int freq) |
| 60 | { |
| 61 | int ret = -EINVAL; |
| 62 | struct msm_gov *gov = |
| 63 | container_of(self, struct msm_gov, gov_notifier); |
| 64 | |
| 65 | mutex_lock(&per_cpu(gov_mutex, gov->cpu)); |
| 66 | |
| 67 | if (freq < gov->min_freq) |
| 68 | freq = gov->min_freq; |
| 69 | if (freq > gov->max_freq) |
| 70 | freq = gov->max_freq; |
| 71 | |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 72 | mutex_unlock(&per_cpu(gov_mutex, gov->cpu)); |
| 73 | |
Abhijeet Dharmapurikar | 83347e0 | 2012-08-26 20:33:43 -0700 | [diff] [blame] | 74 | ret = cpufreq_driver_target(gov->policy, freq, CPUFREQ_RELATION_L); |
| 75 | |
| 76 | if (!ret) { |
| 77 | gov->cur_freq = cpufreq_quick_get(gov->cpu); |
| 78 | if (freq != gov->cur_freq) |
| 79 | pr_err("cpu %d freq %u gov->cur_freq %u didn't match", |
| 80 | gov->cpu, freq, gov->cur_freq); |
| 81 | } |
| 82 | ret = gov->cur_freq; |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 83 | |
| 84 | return ret; |
| 85 | } |
| 86 | |
| 87 | static unsigned int msm_dcvs_freq_get(struct msm_dcvs_freq *self) |
| 88 | { |
| 89 | struct msm_gov *gov = |
| 90 | container_of(self, struct msm_gov, gov_notifier); |
| 91 | |
Abhijeet Dharmapurikar | 83347e0 | 2012-08-26 20:33:43 -0700 | [diff] [blame] | 92 | /* |
| 93 | * the rw_sem in cpufreq is always held when this is called. |
| 94 | * The policy->cur won't be updated in this case - so it is safe to |
| 95 | * access policy->cur |
| 96 | */ |
Abhijeet Dharmapurikar | c43f0db | 2012-08-31 20:42:53 -0700 | [diff] [blame] | 97 | return gov->policy->cur; |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | static int cpufreq_governor_msm(struct cpufreq_policy *policy, |
| 101 | unsigned int event) |
| 102 | { |
| 103 | unsigned int cpu = policy->cpu; |
| 104 | int ret = 0; |
| 105 | int handle = 0; |
| 106 | struct msm_gov *gov = &per_cpu(msm_gov_info, policy->cpu); |
| 107 | struct msm_dcvs_freq *dcvs_notifier = |
| 108 | &(per_cpu(msm_gov_info, cpu).gov_notifier); |
| 109 | |
| 110 | switch (event) { |
| 111 | case CPUFREQ_GOV_START: |
| 112 | if (!cpu_online(cpu)) |
| 113 | return -EINVAL; |
| 114 | BUG_ON(!policy->cur); |
| 115 | mutex_lock(&per_cpu(gov_mutex, cpu)); |
| 116 | per_cpu(msm_gov_info, cpu).cpu = cpu; |
| 117 | gov->policy = policy; |
| 118 | dcvs_notifier->core_name = core_name[cpu]; |
Abhijeet Dharmapurikar | 50bcc83 | 2012-08-31 22:10:41 -0700 | [diff] [blame^] | 119 | handle = msm_dcvs_freq_sink_start(dcvs_notifier); |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 120 | BUG_ON(handle < 0); |
| 121 | msm_gov_check_limits(policy); |
| 122 | mutex_unlock(&per_cpu(gov_mutex, cpu)); |
| 123 | break; |
| 124 | |
| 125 | case CPUFREQ_GOV_STOP: |
Abhijeet Dharmapurikar | 50bcc83 | 2012-08-31 22:10:41 -0700 | [diff] [blame^] | 126 | msm_dcvs_freq_sink_stop(dcvs_notifier); |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 127 | break; |
| 128 | |
| 129 | case CPUFREQ_GOV_LIMITS: |
| 130 | mutex_lock(&per_cpu(gov_mutex, cpu)); |
| 131 | msm_gov_check_limits(policy); |
| 132 | mutex_unlock(&per_cpu(gov_mutex, cpu)); |
| 133 | break; |
| 134 | }; |
| 135 | |
| 136 | return ret; |
| 137 | } |
| 138 | |
| 139 | struct cpufreq_governor cpufreq_gov_msm = { |
| 140 | .name = "msm-dcvs", |
| 141 | .governor = cpufreq_governor_msm, |
| 142 | .owner = THIS_MODULE, |
| 143 | }; |
| 144 | |
| 145 | static int __devinit msm_gov_probe(struct platform_device *pdev) |
| 146 | { |
| 147 | int ret = 0; |
| 148 | int cpu; |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 149 | struct msm_dcvs_core_info *core = NULL; |
Abhijeet Dharmapurikar | b6c0577 | 2012-08-26 18:27:53 -0700 | [diff] [blame] | 150 | int sensor = 0; |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 151 | |
| 152 | core = pdev->dev.platform_data; |
| 153 | |
| 154 | for_each_possible_cpu(cpu) { |
| 155 | mutex_init(&per_cpu(gov_mutex, cpu)); |
| 156 | snprintf(core_name[cpu], 10, "cpu%d", cpu); |
Abhijeet Dharmapurikar | b6c0577 | 2012-08-26 18:27:53 -0700 | [diff] [blame] | 157 | if (cpu < core->num_cores) |
| 158 | sensor = core->sensors[cpu]; |
Abhijeet Dharmapurikar | 50bcc83 | 2012-08-31 22:10:41 -0700 | [diff] [blame^] | 159 | ret = msm_dcvs_register_core(core_name[cpu], core, |
| 160 | msm_dcvs_freq_set, |
| 161 | msm_dcvs_freq_get, |
| 162 | sensor); |
Praveen Chidambaram | 5c8adf2 | 2012-02-23 18:44:37 -0700 | [diff] [blame] | 163 | if (ret) |
| 164 | pr_err("Unable to register core for %d\n", cpu); |
| 165 | } |
| 166 | |
| 167 | return cpufreq_register_governor(&cpufreq_gov_msm); |
| 168 | } |
| 169 | |
| 170 | static int __devexit msm_gov_remove(struct platform_device *pdev) |
| 171 | { |
| 172 | platform_set_drvdata(pdev, NULL); |
| 173 | return 0; |
| 174 | } |
| 175 | |
| 176 | static struct platform_driver msm_gov_driver = { |
| 177 | .probe = msm_gov_probe, |
| 178 | .remove = __devexit_p(msm_gov_remove), |
| 179 | .driver = { |
| 180 | .name = "msm_dcvs_gov", |
| 181 | .owner = THIS_MODULE, |
| 182 | }, |
| 183 | }; |
| 184 | |
| 185 | static int __init cpufreq_gov_msm_init(void) |
| 186 | { |
| 187 | return platform_driver_register(&msm_gov_driver); |
| 188 | } |
| 189 | late_initcall(cpufreq_gov_msm_init); |