2006-10-24 23:39:45 +07:00
|
|
|
/*
|
|
|
|
* cpufreq driver for the cell processor
|
|
|
|
*
|
2007-07-21 02:39:22 +07:00
|
|
|
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005-2007
|
2006-10-24 23:39:45 +07:00
|
|
|
*
|
|
|
|
* Author: Christian Krafft <krafft@de.ibm.com>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
* any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/cpufreq.h>
|
2011-05-28 00:23:32 +07:00
|
|
|
#include <linux/module.h>
|
2007-11-14 00:10:58 +07:00
|
|
|
#include <linux/of_platform.h>
|
|
|
|
|
2007-04-28 09:49:03 +07:00
|
|
|
#include <asm/machdep.h>
|
2007-07-21 02:39:22 +07:00
|
|
|
#include <asm/prom.h>
|
2007-10-04 12:40:42 +07:00
|
|
|
#include <asm/cell-regs.h>
|
2013-03-25 12:50:23 +07:00
|
|
|
|
|
|
|
#include "ppc_cbe_cpufreq.h"
|
2006-10-24 23:39:45 +07:00
|
|
|
|
|
|
|
/* the CBE supports an 8 step frequency scaling */
|
|
|
|
static struct cpufreq_frequency_table cbe_freqs[] = {
|
|
|
|
{1, 0},
|
|
|
|
{2, 0},
|
|
|
|
{3, 0},
|
|
|
|
{4, 0},
|
|
|
|
{5, 0},
|
|
|
|
{6, 0},
|
|
|
|
{8, 0},
|
|
|
|
{10, 0},
|
|
|
|
{0, CPUFREQ_TABLE_END},
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* hardware specific functions
|
|
|
|
*/
|
|
|
|
|
2007-07-21 02:39:22 +07:00
|
|
|
static int set_pmode(unsigned int cpu, unsigned int slow_mode)
|
2007-07-21 02:39:18 +07:00
|
|
|
{
|
2007-07-21 02:39:21 +07:00
|
|
|
int rc;
|
2007-07-21 02:39:22 +07:00
|
|
|
|
2007-07-21 02:39:18 +07:00
|
|
|
if (cbe_cpufreq_has_pmi)
|
2007-07-21 02:39:22 +07:00
|
|
|
rc = cbe_cpufreq_set_pmode_pmi(cpu, slow_mode);
|
2007-07-21 02:39:21 +07:00
|
|
|
else
|
2007-07-21 02:39:22 +07:00
|
|
|
rc = cbe_cpufreq_set_pmode(cpu, slow_mode);
|
2007-07-21 02:39:21 +07:00
|
|
|
|
2007-07-21 02:39:22 +07:00
|
|
|
pr_debug("register contains slow mode %d\n", cbe_cpufreq_get_pmode(cpu));
|
2007-07-21 02:39:21 +07:00
|
|
|
|
|
|
|
return rc;
|
2007-04-24 02:35:42 +07:00
|
|
|
}
|
|
|
|
|
2006-10-24 23:39:45 +07:00
|
|
|
/*
|
|
|
|
* cpufreq functions
|
|
|
|
*/
|
|
|
|
|
2007-04-28 09:49:03 +07:00
|
|
|
static int cbe_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
2006-10-24 23:39:45 +07:00
|
|
|
{
|
2007-04-03 19:50:59 +07:00
|
|
|
const u32 *max_freqp;
|
|
|
|
u32 max_freq;
|
2006-10-24 23:39:45 +07:00
|
|
|
int i, cur_pmode;
|
|
|
|
struct device_node *cpu;
|
|
|
|
|
|
|
|
cpu = of_get_cpu_node(policy->cpu, NULL);
|
|
|
|
|
2007-04-28 09:49:03 +07:00
|
|
|
if (!cpu)
|
2006-10-24 23:39:45 +07:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
pr_debug("init cpufreq on CPU %d\n", policy->cpu);
|
|
|
|
|
2007-07-21 02:39:23 +07:00
|
|
|
/*
|
|
|
|
* Let's check we can actually get to the CELL regs
|
|
|
|
*/
|
|
|
|
if (!cbe_get_cpu_pmd_regs(policy->cpu) ||
|
|
|
|
!cbe_get_cpu_mic_tm_regs(policy->cpu)) {
|
|
|
|
pr_info("invalid CBE regs pointers for cpufreq\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2007-04-03 19:50:59 +07:00
|
|
|
max_freqp = of_get_property(cpu, "clock-frequency", NULL);
|
2006-10-24 23:39:45 +07:00
|
|
|
|
2007-07-21 02:39:21 +07:00
|
|
|
of_node_put(cpu);
|
|
|
|
|
2007-04-03 19:50:59 +07:00
|
|
|
if (!max_freqp)
|
2006-10-24 23:39:45 +07:00
|
|
|
return -EINVAL;
|
|
|
|
|
2007-04-28 09:49:03 +07:00
|
|
|
/* we need the freq in kHz */
|
2007-04-03 19:50:59 +07:00
|
|
|
max_freq = *max_freqp / 1000;
|
2006-10-24 23:39:45 +07:00
|
|
|
|
2007-04-03 19:50:59 +07:00
|
|
|
pr_debug("max clock-frequency is at %u kHz\n", max_freq);
|
2006-10-24 23:39:45 +07:00
|
|
|
pr_debug("initializing frequency table\n");
|
|
|
|
|
2007-04-28 09:49:03 +07:00
|
|
|
/* initialize frequency table */
|
2006-10-24 23:39:45 +07:00
|
|
|
for (i=0; cbe_freqs[i].frequency!=CPUFREQ_TABLE_END; i++) {
|
2013-03-30 17:55:15 +07:00
|
|
|
cbe_freqs[i].frequency = max_freq / cbe_freqs[i].driver_data;
|
2006-10-24 23:39:45 +07:00
|
|
|
pr_debug("%d: %d\n", i, cbe_freqs[i].frequency);
|
|
|
|
}
|
|
|
|
|
2007-07-21 02:39:21 +07:00
|
|
|
/* if DEBUG is enabled set_pmode() measures the latency
|
|
|
|
* of a transition */
|
2006-10-24 23:39:45 +07:00
|
|
|
policy->cpuinfo.transition_latency = 25000;
|
|
|
|
|
2007-07-21 02:39:22 +07:00
|
|
|
cur_pmode = cbe_cpufreq_get_pmode(policy->cpu);
|
2006-10-24 23:39:45 +07:00
|
|
|
pr_debug("current pmode is at %d\n",cur_pmode);
|
|
|
|
|
|
|
|
policy->cur = cbe_freqs[cur_pmode].frequency;
|
|
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
2010-04-26 22:32:41 +07:00
|
|
|
cpumask_copy(policy->cpus, cpu_sibling_mask(policy->cpu));
|
2006-10-24 23:39:45 +07:00
|
|
|
#endif
|
|
|
|
|
2007-07-21 02:39:21 +07:00
|
|
|
/* this ensures that policy->cpuinfo_min
|
|
|
|
* and policy->cpuinfo_max are set correctly */
|
2013-09-16 20:26:28 +07:00
|
|
|
return cpufreq_table_validate_and_show(policy, cbe_freqs);
|
2006-10-24 23:39:45 +07:00
|
|
|
}
|
|
|
|
|
2007-07-21 02:39:21 +07:00
|
|
|
static int cbe_cpufreq_target(struct cpufreq_policy *policy,
|
2013-10-25 21:15:48 +07:00
|
|
|
unsigned int cbe_pmode_new)
|
2006-10-24 23:39:45 +07:00
|
|
|
{
|
2007-07-21 02:39:21 +07:00
|
|
|
pr_debug("setting frequency for cpu %d to %d kHz, " \
|
|
|
|
"1/%d of max frequency\n",
|
2006-10-24 23:39:45 +07:00
|
|
|
policy->cpu,
|
|
|
|
cbe_freqs[cbe_pmode_new].frequency,
|
2013-03-30 17:55:15 +07:00
|
|
|
cbe_freqs[cbe_pmode_new].driver_data);
|
2006-10-24 23:39:45 +07:00
|
|
|
|
2013-08-14 21:08:24 +07:00
|
|
|
return set_pmode(policy->cpu, cbe_pmode_new);
|
2006-10-24 23:39:45 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct cpufreq_driver cbe_cpufreq_driver = {
|
2013-10-03 21:58:19 +07:00
|
|
|
.verify = cpufreq_generic_frequency_table_verify,
|
2013-10-25 21:15:48 +07:00
|
|
|
.target_index = cbe_cpufreq_target,
|
2006-10-24 23:39:45 +07:00
|
|
|
.init = cbe_cpufreq_cpu_init,
|
2013-10-03 21:58:19 +07:00
|
|
|
.exit = cpufreq_generic_exit,
|
2006-10-24 23:39:45 +07:00
|
|
|
.name = "cbe-cpufreq",
|
|
|
|
.flags = CPUFREQ_CONST_LOOPS,
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* module init and destoy
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int __init cbe_cpufreq_init(void)
|
|
|
|
{
|
2007-04-28 09:49:03 +07:00
|
|
|
if (!machine_is(cell))
|
|
|
|
return -ENODEV;
|
2007-04-24 02:35:42 +07:00
|
|
|
|
2006-10-24 23:39:45 +07:00
|
|
|
return cpufreq_register_driver(&cbe_cpufreq_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit cbe_cpufreq_exit(void)
|
|
|
|
{
|
|
|
|
cpufreq_unregister_driver(&cbe_cpufreq_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(cbe_cpufreq_init);
|
|
|
|
module_exit(cbe_cpufreq_exit);
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
MODULE_AUTHOR("Christian Krafft <krafft@de.ibm.com>");
|