Skip to content
Permalink
Browse files

[RFC] MIPS: JZ4770: Added basic cpufreq driver

This driver only supports switching between two dividers (1 and 3).
Using different dividers for the CPU clock requires changing the
other main clocks (PCLK, H0CLK, H1CLK, H2CLK, C1CLK) according to
those rules:

- CCLK must be integral multiple of H0CLK, H1CLK, H2CLK
- C1CLK must be CCLK/2 or equal to H1CLK
- H2CLK must be PCLK or PCLK*2
- H0CLK must be H2CLK or H2CLK*2
- H1CLK must be H0CLK or H0CLK*2

For this basic cpufreq driver to work, a modified bootloader is
required. The default dividers of UBIBoot are
1, 6, 6, 6, 6, 2 (CDIV, H0DIV, H1DIV, H2DIV, PDIV, C1DIV)

According to the previous rules, the following dividers should
work: 2, 6, 6, 6, 6, 6
But this is not the case.

Maybe the multiple between CCLK and H0CLK/H1CLK/H2CLK must be
a power of two, because this works: 3, 6, 6, 6, 6, 6
And so does this: 2, 8, 8, 8, 8, 4

Thus, to get this basic cpufreq driver to work, you need a bootloader
with the following dividers: 1, 6, 6, 6, 6, 6
  • Loading branch information...
pcercuei authored and mthuurne committed Feb 4, 2014
1 parent 04da79a commit 58cf5778deb2b72e45cca6b670d8b25809ecdc82
Showing with 169 additions and 0 deletions.
  1. +2 −0 arch/mips/Kconfig
  2. +9 −0 drivers/cpufreq/Kconfig
  3. +1 −0 drivers/cpufreq/Makefile
  4. +157 −0 drivers/cpufreq/jz4770-cpufreq.c
@@ -263,6 +263,8 @@ config MACH_JZ4770
select JZSOC
select JZRISC
select PINCTRL
select CPU_SUPPORTS_CPUFREQ
select MIPS_EXTERNAL_TIMER

config LANTIQ
bool "Lantiq based platforms"
@@ -238,6 +238,15 @@ endmenu
menu "MIPS CPUFreq processor drivers"
depends on MIPS

config JZ4770_CPUFREQ
tristate "Ingenic JZ4770 CPUFreq Driver"
select CPU_FREQ_TABLE
help
This option adds a CPUFreq driver for the JZ4770 processor
from Ingenic.

If in doubt, say N.

config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
help
@@ -97,6 +97,7 @@ obj-$(CONFIG_BFIN_CPU_FREQ) += blackfin-cpufreq.o
obj-$(CONFIG_CRIS_MACH_ARTPEC3) += cris-artpec3-cpufreq.o
obj-$(CONFIG_ETRAXFS) += cris-etraxfs-cpufreq.o
obj-$(CONFIG_IA64_ACPI_CPUFREQ) += ia64-acpi-cpufreq.o
obj-$(CONFIG_JZ4770_CPUFREQ) += jz4770-cpufreq.o
obj-$(CONFIG_LOONGSON2_CPUFREQ) += loongson2_cpufreq.o
obj-$(CONFIG_SH_CPU_FREQ) += sh-cpufreq.o
obj-$(CONFIG_SPARC_US2E_CPUFREQ) += sparc-us2e-cpufreq.o
@@ -0,0 +1,157 @@
/*
* drivers/cpufreq/jz4770-cpufreq.c
*
* cpufreq driver for JZ4770
*
* Copyright (c) 2014 Paul Cercueil <paul@crapouillou.net>
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>

#include <linux/cpufreq.h>

#include <linux/clk.h>

static struct clk *cclk;
static struct cpufreq_driver cpufreq_jz4770_driver;

/* TODO: Support all dividers: 1, 2, 3, 4, 6, 8, 12 */
static const unsigned int jz4770_cpu_divs[] = { 1, 3, };
static struct cpufreq_frequency_table table[ARRAY_SIZE(jz4770_cpu_divs) + 1];

static void jz4770_freq_fill_table(struct cpufreq_policy *policy)
{
int i;

#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
/* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
static bool init = false;
if (init)
cpufreq_frequency_table_put_attr(policy->cpu);
else
init = true;
#endif

for (i = 0; i < ARRAY_SIZE(jz4770_cpu_divs); i++) {
unsigned int freq = policy->cpuinfo.max_freq / jz4770_cpu_divs[i];
if (freq < policy->cpuinfo.min_freq)
break;
table[i].frequency = freq;
}
table[i].frequency = CPUFREQ_TABLE_END;

policy->min = table[i - 1].frequency;
policy->max = table[0].frequency;

#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
cpufreq_frequency_table_get_attr(table, policy->cpu);
#endif
}

static unsigned int jz4770_freq_get(unsigned int cpu)
{
if (cpu > 0)
return -EINVAL;

return clk_get_rate(cclk) / 1000;
}

static int jz4770_freq_verify(struct cpufreq_policy *policy)
{
cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
return 0;
}

static int jz4770_freq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpufreq_freqs freqs;
unsigned int new_index = 0;
int ret = 0;

if (cpufreq_frequency_table_target(policy, table,
target_freq, relation, &new_index))
return -EINVAL;

freqs = (struct cpufreq_freqs) {
.old = jz4770_freq_get(policy->cpu),
.new = table[new_index].frequency,
.cpu = policy->cpu,
.flags = cpufreq_jz4770_driver.flags,
};

if (freqs.old == freqs.new && policy->cur == freqs.new)
return 0;

cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
pr_debug("%s: setting from %d to %d\n",
__FUNCTION__, freqs.old, freqs.new);
ret = clk_set_rate(cclk, freqs.new * 1000);
cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
return ret;
}

static int jz4770_cpufreq_driver_init(struct cpufreq_policy *policy)
{
struct clk *pll0;
unsigned int max_divider = jz4770_cpu_divs[ARRAY_SIZE(jz4770_cpu_divs) - 1];

pr_debug("Jz4770 cpufreq driver\n");

if (policy->cpu != 0)
return -EINVAL;

pll0 = clk_get(NULL, "pll0");
if (IS_ERR(pll0))
return PTR_ERR(pll0);

cclk = clk_get(NULL, "cclk");
if (IS_ERR(cclk))
return PTR_ERR(cclk);

policy->cpuinfo.max_freq = clk_get_rate(pll0) / 1000;
policy->cpuinfo.min_freq = policy->cpuinfo.max_freq / max_divider;
jz4770_freq_fill_table(policy);
clk_put(pll0);

policy->cpuinfo.transition_latency = 400000; /* in nanoseconds */
policy->cur = jz4770_freq_get(policy->cpu);
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
/* min and max are set by jz4770_freq_fill_table() */

return 0;
}

static struct cpufreq_driver cpufreq_jz4770_driver = {
.init = jz4770_cpufreq_driver_init,
.verify = jz4770_freq_verify,
.target = jz4770_freq_target,
.get = jz4770_freq_get,
.name = "jz4770-cpufreq",
};

static int __init jz4770_cpufreq_init(void)
{
return cpufreq_register_driver(&cpufreq_jz4770_driver);
}

static void __exit jz4770_cpufreq_exit(void)
{
cpufreq_unregister_driver(&cpufreq_jz4770_driver);
}

module_init(jz4770_cpufreq_init);
module_exit(jz4770_cpufreq_exit);

MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
MODULE_DESCRIPTION("cpufreq driver for JZ4770");
MODULE_LICENSE("GPL");

0 comments on commit 58cf577

Please sign in to comment.
You can’t perform that action at this time.