blob: c64d4fea7580445df5050c2eb15ded77a4c2a3e0 [file] [log] [blame]
/*
* Gem5 Multi-cluster CPUFreq Interface driver
* (adapted from vexpress_big_little.c)
*
* It provides necessary opp's to arm_gem5_mc.c cpufreq driver and gets
* frequency information from gem5 energy controller device.
*
* Copyright (C) 2013 - 2014 ARM Ltd.
* Authors: Akash Bagdia <Akash.bagdia@arm.com>
* Vasileios Spiliopoulos <vasileios.spiliopoulos@arm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/cpufreq.h>
#include <linux/export.h>
#include <linux/pm_opp.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/gem5_energy_ctrl.h>
#include "arm_gem5_mc.h"
static int gem5_init_opp_table(struct device *cpu_dev)
{
int i = -1, count, cluster = cpu_to_cluster(cpu_dev->id);
u32 *freq_table;
u32 *volt_table; /* In micro volts */
int ret;
count = gem5_energy_ctrl_get_opp_table(cluster, &freq_table, &volt_table);
if (!freq_table || !count) {
pr_err("gem5 energy controller returned invalid freq table");
return -EINVAL;
}
if (!volt_table || !count) {
pr_err("gem5 energy controller returned invalid voltage table");
return -EINVAL;
}
while (++i < count) {
ret = dev_pm_opp_add(cpu_dev, freq_table[i] * 1000,
volt_table[i]);
if (ret) {
dev_warn(cpu_dev,
"%s: Failed to add OPP freq %d, u-voltage %d,\
err: %d\n",
__func__, freq_table[i] * 1000,
volt_table[i], ret);
return ret;
}
}
return 0;
}
static int gem5_get_transition_latency(struct device *cpu_dev)
{
return gem5_energy_ctrl_get_trans_latency();
}
static struct cpufreq_arm_mc_ops gem5_mc_ops = {
.name = "gem5-mc",
.get_transition_latency = gem5_get_transition_latency,
.init_opp_table = gem5_init_opp_table,
};
static int gem5_mc_init(void)
{
if (!gem5_energy_ctrl_check_loaded()) {
pr_info("%s: No energy controller found\n", __func__);
return -ENOENT;
}
if (!gem5_energy_ctrl_dvfs_enabled()) {
pr_info("%s: DVFS handler in energy controller is disabled, \
ARM gem5 multi-cluster cpufreq driver \
will not be registered\n",
__func__);
return -ENOENT;
}
return mc_cpufreq_register(&gem5_mc_ops);
}
module_init(gem5_mc_init);
static void gem5_mc_exit(void)
{
return mc_cpufreq_unregister(&gem5_mc_ops);
}
module_exit(gem5_mc_exit);
MODULE_DESCRIPTION("ARM gem5 multi-cluster cpufreq driver");
MODULE_LICENSE("GPL");