Skip to content

Commit

Permalink
jz4740 cpufreq driver
Browse files Browse the repository at this point in the history
Signed-off-by: Ulrich Hecht <ulrich.hecht@gmail.com>
  • Loading branch information
uli committed Jun 19, 2010
1 parent 6f5fdc4 commit 9fd12ba
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/mips/jz4740/Kconfig
Expand Up @@ -35,6 +35,7 @@ config SOC_JZ4740
select IRQ_CPU
select DMA_NONCOHERENT
select HAVE_PWM
select CPU_SUPPORTS_CPUFREQ

config JZSOC
bool
Expand Down
1 change: 1 addition & 0 deletions arch/mips/jz4740/Makefile
Expand Up @@ -20,5 +20,6 @@ obj-$(CONFIG_JZ4740_A320) += board-a320.o
# PM support

obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o

EXTRA_CFLAGS += -Werror -Wall
253 changes: 253 additions & 0 deletions arch/mips/jz4740/cpufreq.c
@@ -0,0 +1,253 @@
/*
* linux/arch/mips/jz4740/cpufreq.c
*
* cpufreq driver for JZ4740
*
* Copyright (c) 2006-2007 Ingenic Semiconductor Inc.
* Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.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.
*/

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>

#include <linux/cpufreq.h>

#include <linux/clk.h>
#include <asm/mach-jz4740/jz4740.h>


struct jz4740_freq_percpu_info {
struct cpufreq_frequency_table table[12];
};

static struct clk *cclk = NULL;

static struct jz4740_freq_percpu_info jz4740_freq_table;

#if 1
#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */

inline int sdram_convert(unsigned int pllin,unsigned int *sdram_freq)
{
register unsigned int ns, tmp;

ns = 1000000000 / pllin;
/* Set refresh registers */
tmp = SDRAM_TREF/ns;
tmp = tmp/64 + 1;
if (tmp > 0xff) tmp = 0xff;
*sdram_freq = tmp;

return 0;

}

#define CPM_CPCCR_CLKOEN (1 << 30)
#define CPM_CPCCR_LDIV_BIT 16
#define CPM_CPCCR_MDIV_BIT 12
#define CPM_CPCCR_PDIV_BIT 8
#define CPM_CPCCR_HDIV_BIT 4
#define CPM_CPCCR_CDIV_BIT 0
#define CPM_CPCCR_PCS (1 << 21)
#define CFG_EXTAL 12000000 /* EXT clock: 12 Mhz */
#define CPM_CPPCR_PLLM_BIT 23
#define CPM_CPPCR_PLLN_BIT 18
#define CPM_CPPCR_PLLOD_BIT 16
#define CPM_CPPCR_PLLEN (1 << 8)
#define CPM_CPPCR_PLLST_BIT 0
#define PLL_WAIT_500NS (500*(__cpm_get_cclk()/1000000000))

void pll_init(unsigned int clock)
{

register unsigned int cfcr, plcr1;
unsigned int sdramclock = 0;
unsigned int tmp = 0, wait = PLL_WAIT_500NS;

int n2FR[33] = {
0, 0, 1, 2, 3, 0, 4, 0, 5, 0, 0, 0, 6, 0, 0, 0,
7, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0,
9
};
//int div[5] = {1, 4, 4, 4, 4}; /* divisors of I:S:P:L:M */
int div[5] = {1, 3, 3, 3, 3}; /* divisors of I:S:P:L:M */
int nf, pllout2;

cfcr = CPM_CPCCR_CLKOEN |
(n2FR[div[0]] << CPM_CPCCR_CDIV_BIT) |
(n2FR[div[1]] << CPM_CPCCR_HDIV_BIT) |
(n2FR[div[2]] << CPM_CPCCR_PDIV_BIT) |
(n2FR[div[3]] << CPM_CPCCR_MDIV_BIT) |
(n2FR[div[4]] << CPM_CPCCR_LDIV_BIT);

pllout2 = (cfcr & CPM_CPCCR_PCS) ? clock : (clock / 2);

/* Init UHC clock */
REG_CPM_UHCCDR = pllout2 / 48000000 - 1;

nf = clock * 2 / CFG_EXTAL;
plcr1 = ((nf - 2) << CPM_CPPCR_PLLM_BIT) | /* FD */
(0 << CPM_CPPCR_PLLN_BIT) | /* RD=0, NR=2 */
(0 << CPM_CPPCR_PLLOD_BIT) | /* OD=0, NO=1 */
(0x20 << CPM_CPPCR_PLLST_BIT) | /* PLL stable time */
CPM_CPPCR_PLLEN; /* enable PLL */

sdram_convert(clock,&sdramclock);
if(sdramclock > 0)
{
REG_EMC_RTCOR = sdramclock;
REG_EMC_RTCNT = sdramclock;

}else
{
printk("sdram init fail!\n");
while(1);
}
/* init PLL */
//REG_CPM_CPCCR = cfcr;
__asm__ __volatile__(
".set noreorder\n\t"
".align 5\n"
"sw %1,0(%0)\n\t"
"li %3,0\n\t"
"1:\n\t"
"bne %3,%2,1b\n\t"
"addi %3, 1\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
".set reorder\n\t"
:
: "r" (CPM_CPCCR), "r" (cfcr), "r" (wait), "r" (tmp));

REG_CPM_LPCDR = clock / 12000000 / 2 - 1; /* pixclock */
//REG_CPM_CPPCR = plcr1;
__asm__ __volatile__(
".set noreorder\n\t"
".align 5\n"
"sw %1,0(%0)\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
"nop\n\t"
".set reorder\n\t"
:
: "r" (CPM_CPPCR), "r" (plcr1));


}
#endif

static unsigned int jz4740_freq_get(unsigned int cpu)
{
unsigned long ret;
cclk = clk_get(NULL, "cclk");
ret = clk_get_rate(cclk);
clk_put(cclk);
return ret / 1000;
}

static int jz4740_freq_target(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int relation)
{
struct cpufreq_frequency_table *table = &jz4740_freq_table.table[0];
unsigned int new_index = 0;

if (cpufreq_frequency_table_target(policy,
&jz4740_freq_table.table[0],
target_freq, relation, &new_index))
{
return -EINVAL;
}

printk(KERN_INFO "%s: target_freq %d new_index %d\n", __FUNCTION__, target_freq, new_index);

if (table[new_index].frequency == jz4740_freq_get(policy->cpu))
return 0;

cclk = clk_get(NULL, "cclk");
printk(KERN_INFO "%s: cclk %p, setting to %d\n", __FUNCTION__, cclk, table[new_index].frequency);
if (cclk) {
//clk_set_rate(cclk, table[new_index].frequency);
pll_init(table[new_index].frequency * 1000);
clk_put(cclk);
}
//jz4740_set_cpu_divider_index(policy->cpu, new_index);

return 0;
}

static int jz4740_freq_verify(struct cpufreq_policy *policy)
{
return cpufreq_frequency_table_verify(policy,
&jz4740_freq_table.table[0]);
}

static int __init jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *table = &jz4740_freq_table.table[0];
unsigned int MAX_FREQ;
int i;

printk(KERN_INFO "Jz4740 cpufreq driver\n");

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

policy->cur = 336000; /* in kHz. Current and max frequency is determined by u-boot */
MAX_FREQ = 432000;
policy->governor = CPUFREQ_DEFAULT_GOVERNOR;

policy->cpuinfo.min_freq = 192000;
policy->cpuinfo.max_freq = MAX_FREQ;
policy->cpuinfo.transition_latency = 100000; /* in 10^(-9) s = nanoseconds */

for (i = 0; i < 11; i++) {
table[i].index = i;
table[i].frequency = policy->cpuinfo.min_freq + i * 24000;
}
table[i].index = i;
table[i].frequency = CPUFREQ_TABLE_END;

#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
cpufreq_frequency_table_get_attr(table, policy->cpu); /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
#endif

return cpufreq_frequency_table_cpuinfo(policy, table);
}

static struct cpufreq_driver cpufreq_jz4740_driver = {
// .flags = CPUFREQ_STICKY,
.init = jz4740_cpufreq_driver_init,
.verify = jz4740_freq_verify,
.target = jz4740_freq_target,
.get = jz4740_freq_get,
.name = "jz4740",
};

static int __init jz4740_cpufreq_init(void)
{
return cpufreq_register_driver(&cpufreq_jz4740_driver);
}

static void __exit jz4740_cpufreq_exit(void)
{
cpufreq_unregister_driver(&cpufreq_jz4740_driver);
}

module_init(jz4740_cpufreq_init);
module_exit(jz4740_cpufreq_exit);

MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>");
MODULE_DESCRIPTION("cpufreq driver for Jz4740");
MODULE_LICENSE("GPL");
12 changes: 11 additions & 1 deletion arch/mips/kernel/cpufreq/Kconfig
Expand Up @@ -8,7 +8,7 @@ config MIPS_EXTERNAL_TIMER
config MIPS_CPUFREQ
bool
default y
depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER
depends on CPU_SUPPORTS_CPUFREQ

if MIPS_CPUFREQ

Expand All @@ -24,6 +24,7 @@ config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
select CPU_FREQ_TABLE
depends on MIPS_CPUFREQ
depends on MIPS_EXTERNAL_TIMER
help
This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency.
Expand All @@ -34,6 +35,15 @@ config LOONGSON2_CPUFREQ

If in doubt, say N.

config CPU_FREQ_JZ
tristate "CPUfreq driver for JZ CPUs"
depends on JZSOC
default n
help
This enables the CPUfreq driver for JZ CPUs.

If in doubt, say N.

endif # CPU_FREQ

endmenu
Expand Down

0 comments on commit 9fd12ba

Please sign in to comment.