1 /* 2 * linux/drivers/devfreq/governor_simpleondemand.c 3 * 4 * Copyright (C) 2011 Samsung Electronics 5 * MyungJoo Ham <myungjoo.ham@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 */ 11 12 #include <linux/errno.h> 13 #include <linux/module.h> 14 #include <linux/devfreq.h> 15 #include <linux/math64.h> 16 #include "governor.h" 17 18 /* Default constants for DevFreq-Simple-Ondemand (DFSO) */ 19 #define DFSO_UPTHRESHOLD (90) 20 #define DFSO_DOWNDIFFERENCTIAL (5) 21 static int devfreq_simple_ondemand_func(struct devfreq *df, 22 unsigned long *freq) 23 { 24 int err; 25 struct devfreq_dev_status *stat; 26 unsigned long long a, b; 27 unsigned int dfso_upthreshold = DFSO_UPTHRESHOLD; 28 unsigned int dfso_downdifferential = DFSO_DOWNDIFFERENCTIAL; 29 struct devfreq_simple_ondemand_data *data = df->data; 30 31 err = devfreq_update_stats(df); 32 if (err) 33 return err; 34 35 stat = &df->last_status; 36 37 if (data) { 38 if (data->upthreshold) 39 dfso_upthreshold = data->upthreshold; 40 if (data->downdifferential) 41 dfso_downdifferential = data->downdifferential; 42 } 43 if (dfso_upthreshold > 100 || 44 dfso_upthreshold < dfso_downdifferential) 45 return -EINVAL; 46 47 /* Assume MAX if it is going to be divided by zero */ 48 if (stat->total_time == 0) { 49 *freq = DEVFREQ_MAX_FREQ; 50 return 0; 51 } 52 53 /* Prevent overflow */ 54 if (stat->busy_time >= (1 << 24) || stat->total_time >= (1 << 24)) { 55 stat->busy_time >>= 7; 56 stat->total_time >>= 7; 57 } 58 59 /* Set MAX if it's busy enough */ 60 if (stat->busy_time * 100 > 61 stat->total_time * dfso_upthreshold) { 62 *freq = DEVFREQ_MAX_FREQ; 63 return 0; 64 } 65 66 /* Set MAX if we do not know the initial frequency */ 67 if (stat->current_frequency == 0) { 68 *freq = DEVFREQ_MAX_FREQ; 69 return 0; 70 } 71 72 /* Keep the current frequency */ 73 if (stat->busy_time * 100 > 74 stat->total_time * (dfso_upthreshold - dfso_downdifferential)) { 75 *freq = stat->current_frequency; 76 return 0; 77 } 78 79 /* Set the desired frequency based on the load */ 80 a = stat->busy_time; 81 a *= stat->current_frequency; 82 b = div_u64(a, stat->total_time); 83 b *= 100; 84 b = div_u64(b, (dfso_upthreshold - dfso_downdifferential / 2)); 85 *freq = (unsigned long) b; 86 87 return 0; 88 } 89 90 static int devfreq_simple_ondemand_handler(struct devfreq *devfreq, 91 unsigned int event, void *data) 92 { 93 switch (event) { 94 case DEVFREQ_GOV_START: 95 devfreq_monitor_start(devfreq); 96 break; 97 98 case DEVFREQ_GOV_STOP: 99 devfreq_monitor_stop(devfreq); 100 break; 101 102 case DEVFREQ_GOV_INTERVAL: 103 devfreq_interval_update(devfreq, (unsigned int *)data); 104 break; 105 106 case DEVFREQ_GOV_SUSPEND: 107 devfreq_monitor_suspend(devfreq); 108 break; 109 110 case DEVFREQ_GOV_RESUME: 111 devfreq_monitor_resume(devfreq); 112 break; 113 114 default: 115 break; 116 } 117 118 return 0; 119 } 120 121 static struct devfreq_governor devfreq_simple_ondemand = { 122 .name = DEVFREQ_GOV_SIMPLE_ONDEMAND, 123 .get_target_freq = devfreq_simple_ondemand_func, 124 .event_handler = devfreq_simple_ondemand_handler, 125 }; 126 127 static int __init devfreq_simple_ondemand_init(void) 128 { 129 return devfreq_add_governor(&devfreq_simple_ondemand); 130 } 131 subsys_initcall(devfreq_simple_ondemand_init); 132 133 static void __exit devfreq_simple_ondemand_exit(void) 134 { 135 int ret; 136 137 ret = devfreq_remove_governor(&devfreq_simple_ondemand); 138 if (ret) 139 pr_err("%s: failed remove governor %d\n", __func__, ret); 140 141 return; 142 } 143 module_exit(devfreq_simple_ondemand_exit); 144 MODULE_LICENSE("GPL"); 145