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/module.h> 26#include <linux/timer.h> 27#include <linux/workqueue.h> 28#include <linux/atomic.h> 29#include <asm/machdep.h> 30#include <asm/spu.h> 31 32#define POLL_TIME 100000 /* in µs */ 33#define EXP 753 /* exp(-1) in fixed-point */ 34 35struct spu_gov_info_struct { 36 unsigned long busy_spus; /* fixed-point */ 37 struct cpufreq_policy *policy; 38 struct delayed_work work; 39 unsigned int poll_int; /* µs */ 40}; 41static DEFINE_PER_CPU(struct spu_gov_info_struct, spu_gov_info); 42 43static int calc_freq(struct spu_gov_info_struct *info) 44{ 45 int cpu; 46 int busy_spus; 47 48 cpu = info->policy->cpu; 49 busy_spus = atomic_read(&cbe_spu_info[cpu_to_node(cpu)].busy_spus); 50 51 CALC_LOAD(info->busy_spus, EXP, busy_spus * FIXED_1); 52 pr_debug("cpu %d: busy_spus=%d, info->busy_spus=%ld\n", 53 cpu, busy_spus, info->busy_spus); 54 55 return info->policy->max * info->busy_spus / FIXED_1; 56} 57 58static void spu_gov_work(struct work_struct *work) 59{ 60 struct spu_gov_info_struct *info; 61 int delay; 62 unsigned long target_freq; 63 64 info = container_of(work, struct spu_gov_info_struct, work.work); 65 66 /* after cancel_delayed_work_sync we unset info->policy */ 67 BUG_ON(info->policy == NULL); 68 69 target_freq = calc_freq(info); 70 __cpufreq_driver_target(info->policy, target_freq, CPUFREQ_RELATION_H); 71 72 delay = usecs_to_jiffies(info->poll_int); 73 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 74} 75 76static void spu_gov_init_work(struct spu_gov_info_struct *info) 77{ 78 int delay = usecs_to_jiffies(info->poll_int); 79 INIT_DEFERRABLE_WORK(&info->work, spu_gov_work); 80 schedule_delayed_work_on(info->policy->cpu, &info->work, delay); 81} 82 83static void spu_gov_cancel_work(struct spu_gov_info_struct *info) 84{ 85 cancel_delayed_work_sync(&info->work); 86} 87 88static int spu_gov_govern(struct cpufreq_policy *policy, unsigned int event) 89{ 90 unsigned int cpu = policy->cpu; 91 struct spu_gov_info_struct *info, *affected_info; 92 int i; 93 int ret = 0; 94 95 info = &per_cpu(spu_gov_info, cpu); 96 97 switch (event) { 98 case CPUFREQ_GOV_START: 99 if (!cpu_online(cpu)) { 100 printk(KERN_ERR "cpu %d is not online\n", cpu); 101 ret = -EINVAL; 102 break; 103 } 104 105 if (!policy->cur) { 106 printk(KERN_ERR "no cpu specified in policy\n"); 107 ret = -EINVAL; 108 break; 109 } 110 111 /* initialize spu_gov_info for all affected cpus */ 112 for_each_cpu(i, policy->cpus) { 113 affected_info = &per_cpu(spu_gov_info, i); 114 affected_info->policy = policy; 115 } 116 117 info->poll_int = POLL_TIME; 118 119 /* setup timer */ 120 spu_gov_init_work(info); 121 122 break; 123 124 case CPUFREQ_GOV_STOP: 125 /* cancel timer */ 126 spu_gov_cancel_work(info); 127 128 /* clean spu_gov_info for all affected cpus */ 129 for_each_cpu (i, policy->cpus) { 130 info = &per_cpu(spu_gov_info, i); 131 info->policy = NULL; 132 } 133 134 break; 135 } 136 137 return ret; 138} 139 140static struct cpufreq_governor spu_governor = { 141 .name = "spudemand", 142 .governor = spu_gov_govern, 143 .owner = THIS_MODULE, 144}; 145 146/* 147 * module init and destoy 148 */ 149 150static int __init spu_gov_init(void) 151{ 152 int ret; 153 154 ret = cpufreq_register_governor(&spu_governor); 155 if (ret) 156 printk(KERN_ERR "registration of governor failed\n"); 157 return ret; 158} 159 160static void __exit spu_gov_exit(void) 161{ 162 cpufreq_unregister_governor(&spu_governor); 163} 164 165 166module_init(spu_gov_init); 167module_exit(spu_gov_exit); 168 169MODULE_LICENSE("GPL"); 170MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>"); 171 172