Re: RFC on cpufreq implementation

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.

/*
 * 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);

[Index of Archives]     [Linux Kernel Devel]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite Forum]     [Linux SCSI]

  Powered by Linux