1 /* 2 * spu aware cpufreq governor for the cell processor 3 * 4 * © Copyright IBM Corporation 2006-2008 5 * 6 * Author: Christian Krafft <krafft@de.ibm.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2, or (at your option) 11 * any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 */ 22 23 #include <linux/cpufreq.h> 24 #include <linux/sched.h> 25 #include <linux/timer.h> 26 #include <linux/workqueue.h> 27 #include <asm/atomic.h> 28 #include <asm/machdep.h> 29 #include <asm/spu.h> 30 31 #define POLL_TIME 100000 /* in µs */ 32 #define EXP 753 /* exp(-1) in fixed-point */ 33 34 struct spu_gov_info_struct { 35 unsigned long busy_spus; /* fixed-point */ 36 struct cpufreq_policy *policy; 37 struct delayed_work work; 38 unsigned int poll_int; /* µs */ 39 }; 40 static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); 41 42 static int calc_freq(struct spu_gov_info_struct *info) 43 { 44 int cpu; 45 int busy_spus; 46 47 cpu = info->policy->cpu; 48 busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); 49 50 CALC_LOAD(info->busy_spus, EXP, busy_spus * FIXED_1); 51 pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n", 52 cpu, busy_spus, info->busy_spus); 53 54 return info->policy->max * info->busy_spus / FIXED_1; 55 } 56 57 static void spu_gov_work(struct work_struct *work) 58 { 59 struct spu_gov_info_struct *info; 60 int delay; 61 unsigned long target_freq; 62 63 info = container_of(work, struct spu_gov_info_struct, work.work); 64 65 /* after cancel_delayed_work_sync we unset info->policy */ 66 BUG_ON(info->policy == NULL); 67 68 target_freq = calc_freq(info); 69 __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); 70 71 delay = usecs_to_jiffies(info->poll_int); 72 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 73 } 74 75 static void spu_gov_init_work(struct spu_gov_info_struct *info) 76 { 77 int delay = usecs_to_jiffies(info->poll_int); 78 INIT_DELAYED_WORK_DEFERRABLE(&info->work, spu_gov_work); 79 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 80 } 81 82 static void spu_gov_cancel_work(struct spu_gov_info_struct *info) 83 { 84 cancel_delayed_work_sync(&info->work); 85 } 86 87 static int spu_gov_govern(struct cpufreq_policy *policy, unsigned int event) 88 { 89 unsigned int cpu = policy->cpu; 90 struct spu_gov_info_struct *info, *affected_info; 91 int i; 92 int ret = 0; 93 94 info = &per_cpu(spu_gov_info, cpu); 95 96 switch (event) { 97 case CPUFREQ_GOV_START: 98 if (!cpu_online(cpu)) { 99 printk(KERN_ERR "cpu %d is not online\n", cpu); 100 ret = -EINVAL; 101 break; 102 } 103 104 if (!policy->cur) { 105 printk(KERN_ERR "no cpu specified in policy\n"); 106 ret = -EINVAL; 107 break; 108 } 109 110 /* initialize spu_gov_info for all affected cpus */ 111 for_each_cpu(i, policy->cpus) { 112 affected_info = &per_cpu(spu_gov_info, i); 113 affected_info->policy = policy; 114 } 115 116 info->poll_int = POLL_TIME; 117 118 /* setup timer */ 119 spu_gov_init_work(info); 120 121 break; 122 123 case CPUFREQ_GOV_STOP: 124 /* cancel timer */ 125 spu_gov_cancel_work(info); 126 127 /* clean spu_gov_info for all affected cpus */ 128 for_each_cpu (i, policy->cpus) { 129 info = &per_cpu(spu_gov_info, i); 130 info->policy = NULL; 131 } 132 133 break; 134 } 135 136 return ret; 137 } 138 139 static struct cpufreq_governor spu_governor = { 140 .name = "spudemand", 141 .governor = spu_gov_govern, 142 .owner = THIS_MODULE, 143 }; 144 145 /* 146 * module init and destoy 147 */ 148 149 static int __init spu_gov_init(void) 150 { 151 int ret; 152 153 ret = cpufreq_register_governor(&spu_governor); 154 if (ret) 155 printk(KERN_ERR "registration of governor failed\n"); 156 return ret; 157 } 158 159 static void __exit spu_gov_exit(void) 160 { 161 cpufreq_unregister_governor(&spu_governor); 162 } 163 164 165 module_init(spu_gov_init); 166 module_exit(spu_gov_exit); 167 168 MODULE_LICENSE("GPL"); 169 MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); 170 171