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