145 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * linux/drivers/devfreq/governor_simpleondemand.c4 *5 * Copyright (C) 2011 Samsung Electronics6 * 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)18static 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 87static 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 118static struct devfreq_governor devfreq_simple_ondemand = {119 .name = DEVFREQ_GOV_SIMPLE_ONDEMAND,120 .attrs = DEVFREQ_GOV_ATTR_POLLING_INTERVAL121 | DEVFREQ_GOV_ATTR_TIMER,122 .get_target_freq = devfreq_simple_ondemand_func,123 .event_handler = devfreq_simple_ondemand_handler,124};125 126static int __init devfreq_simple_ondemand_init(void)127{128 return devfreq_add_governor(&devfreq_simple_ondemand);129}130subsys_initcall(devfreq_simple_ondemand_init);131 132static 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}142module_exit(devfreq_simple_ondemand_exit);143MODULE_DESCRIPTION("DEVFREQ Simple On-demand governor");144MODULE_LICENSE("GPL");145