Re: RFC on cpufreq implementation
Mason <[email protected]> Thu, 29 Jan 2015 17:43:19 +0100
| Newsgroups | org.kernel.vger.cpufreq,org.infradead.lists.linux-arm-kernel,org.kernel.vger.linux-pm |
|---|---|
| Message-ID | <[email protected]> |
On 15/01/2015 18:24, Mason wrote: > This is a follow-up to my previous thread. > "How many frequencies would cpufreq optimally like to manage?" > http://thread.gmane.org/gmane.linux.ports.arm.kernel/373669 OK, so this is my TAKE 2 on the cpufreq driver, trying to remove some dependencies on machine-specific definitions by getting the virtual address at init via ioremap. (Is -EFAULT the right error to return if ioremap fails?) I'm not sure where machine-specific information is supposed to be stored though? Such as register definitions, or the physical addresses/offsets. What if they need to be shared among several source files? I can't just duplicate them... (I've tentatively called it temp.h for the time being.) Regards.
cpufreq.c
(text/x-csrc, 2.5 KB)
/*
* Copyright 2015 Sigma Designs
*
* 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.
*
*/
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/io.h>
#include "temp.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sigma Designs");
MODULE_DESCRIPTION("cpufreq driver for Tangox 87xx");
#define TANGOX_XTAL_FREQ 27000 /* in kHz */
#define CLKGEN_BASE 0x10000
#define SYS_clkgen0_pll (clkgen_base + 0x00)
#define SYS_cpuclk_div_ctrl (clkgen_base + 0x24)
static void __iomem *clkgen_base;
static struct cpufreq_frequency_table freq_table[] = {
{ .driver_data = 1 },
{ .driver_data = 2 },
{ .driver_data = 3 },
{ .driver_data = 5 },
{ .driver_data = 9 },
{ .frequency = CPUFREQ_TABLE_END },
};
static unsigned int tangox_get_freq(unsigned int cpu)
{
union SYS_clkgen_pll pll;
union SYS_clk_div_ctrl div;
pll.val = readl_relaxed(SYS_clkgen0_pll);
if (pll.f.Isel != 1 || pll.f.M != 0) return 0;
div.val = readl_relaxed(SYS_cpuclk_div_ctrl);
if (div.f.BP != 0 || div.f.F != 0) return 0;
return TANGOX_XTAL_FREQ * (pll.f.N + 1) / div.f.I >> pll.f.K;
}
static int tangox_target(struct cpufreq_policy *policy, unsigned int index)
{
while (readl_relaxed(SYS_cpuclk_div_ctrl) >> 31) cpu_relax();
writel_relaxed(freq_table[index].driver_data, SYS_cpuclk_div_ctrl);
return 0;
}
static int tangox_cpu_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *p;
unsigned int freq = tangox_get_freq(0);
unsigned int transition_latency_ns = freq / SYS_FAST_RAMP_SPEED;
if ((clkgen_base = ioremap(CLKGEN_BASE, 0x40)) == NULL) return -EFAULT;
for (p = freq_table; p->frequency != CPUFREQ_TABLE_END; ++p) {
unsigned int I = p->driver_data;
union SYS_clk_div_ctrl div = SYS_CLK_DIV_CTRL(I);
p->driver_data = div.val;
p->frequency = freq / I;
}
return cpufreq_generic_init(policy, freq_table, transition_latency_ns);
}
static struct cpufreq_driver tangox_cpufreq_driver = {
.name = "tangox-cpufreq",
.init = tangox_cpu_init,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = tangox_target,
.get = tangox_get_freq,
.exit = cpufreq_generic_exit,
.attr = cpufreq_generic_attr,
};
static int __init tangox_cpufreq_init(void)
{
return cpufreq_register_driver(&tangox_cpufreq_driver);
}
static void __exit tangox_cpufreq_exit(void)
{
cpufreq_unregister_driver(&tangox_cpufreq_driver);
}
module_init(tangox_cpufreq_init);
module_exit(tangox_cpufreq_exit);