Date:2011-03-16 03:16:04 (8 years 8 months ago)
Author:Maarten ter Huurne
Commit:7fc863389f8fabc8eb33dafa375b8984c0b829fd
Message:MIPS: JZ4740: Add cpufreq support

This is a squashed version of Uli's driver that was further developed
in the qi-kernel repository.
Files: arch/mips/Kconfig (1 diff)
arch/mips/jz4740/Makefile (1 diff)
arch/mips/jz4740/cpufreq.c (1 diff)
arch/mips/kernel/cpufreq/Kconfig (3 diffs)

Change Details

arch/mips/Kconfig
223223    select HAVE_PWM
224224    select HAVE_CLK
225225    select GENERIC_IRQ_CHIP
226    select CPU_SUPPORTS_CPUFREQ
226227
227228config LANTIQ
228229    bool "Lantiq based platforms"
arch/mips/jz4740/Makefile
1616# PM support
1717
1818obj-$(CONFIG_PM) += pm.o
19obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o
arch/mips/jz4740/cpufreq.c
1/*
2 * linux/arch/mips/jz4740/cpufreq.c
3 *
4 * cpufreq driver for JZ4740
5 *
6 * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com>
7 * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/init.h>
17#include <linux/err.h>
18
19#include <linux/cpufreq.h>
20
21#include <linux/clk.h>
22#include <asm/mach-jz4740/base.h>
23
24#include "clock.h"
25
26#define DEBUG_CPUFREQ
27
28#ifdef DEBUG_CPUFREQ
29#define dprintk(X...) printk(KERN_INFO X)
30#else
31#define dprintk(X...) do { } while(0)
32#endif
33
34#define HCLK_MIN 30000
35/* TODO: The maximum MCLK most likely depends on the SDRAM chips used,
36         so it is board-specific. */
37#define MCLK_MAX 140000
38
39/* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware
40   spec states those dividers must not be used for CCLK or HCLK. */
41static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16};
42
43struct jz4740_freq_percpu_info {
44    unsigned int pll_rate;
45    struct cpufreq_frequency_table table[
46        ARRAY_SIZE(jz4740_freq_cpu_divs) + 1];
47};
48
49static struct clk *pll;
50static struct clk *cclk;
51
52static struct jz4740_freq_percpu_info jz4740_freq_info;
53
54static struct cpufreq_driver cpufreq_jz4740_driver;
55
56static void jz4740_freq_fill_table(struct cpufreq_policy *policy,
57                   unsigned int pll_rate)
58{
59    struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
60    int i;
61
62#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
63    /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
64    static bool init = false;
65    if (init)
66        cpufreq_frequency_table_put_attr(policy->cpu);
67    else
68        init = true;
69#endif
70
71    jz4740_freq_info.pll_rate = pll_rate;
72
73    for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) {
74        unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i];
75        if (freq < HCLK_MIN) break;
76        table[i].index = i;
77        table[i].frequency = freq;
78    }
79    table[i].index = i;
80    table[i].frequency = CPUFREQ_TABLE_END;
81
82    policy->min = table[i - 1].frequency;
83    policy->max = table[0].frequency;
84
85#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
86    cpufreq_frequency_table_get_attr(table, policy->cpu);
87#endif
88}
89
90static unsigned int jz4740_freq_get(unsigned int cpu)
91{
92    return clk_get_rate(cclk) / 1000;
93}
94
95static int jz4740_freq_verify(struct cpufreq_policy *policy)
96{
97    unsigned int new_pll;
98
99    cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
100                     policy->cpuinfo.max_freq);
101
102    new_pll = clk_round_rate(pll, policy->max * 1000) / 1000;
103    if (jz4740_freq_info.pll_rate != new_pll)
104        jz4740_freq_fill_table(policy, new_pll);
105
106    return 0;
107}
108
109static int jz4740_freq_target(struct cpufreq_policy *policy,
110              unsigned int target_freq,
111              unsigned int relation)
112{
113    struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
114    struct cpufreq_freqs freqs;
115    unsigned int new_index = 0;
116    unsigned int old_pll = clk_get_rate(pll) / 1000;
117    unsigned int new_pll = jz4740_freq_info.pll_rate;
118    int ret = 0;
119
120    if (cpufreq_frequency_table_target(policy, table,
121                       target_freq, relation, &new_index))
122        return -EINVAL;
123    freqs = (struct cpufreq_freqs) {
124        .old = jz4740_freq_get(policy->cpu),
125        .new = table[new_index].frequency,
126        .cpu = policy->cpu,
127        .flags = cpufreq_jz4740_driver.flags,
128    };
129    if (freqs.new != freqs.old || new_pll != old_pll) {
130        unsigned int cdiv, hdiv, mdiv, pdiv;
131        cdiv = jz4740_freq_cpu_divs[new_index];
132        hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3;
133        while (new_pll < HCLK_MIN * hdiv)
134            hdiv -= cdiv;
135        mdiv = hdiv;
136        if (new_pll > MCLK_MAX * mdiv) {
137            /* 4,4 performs better than 3,6 */
138            if (new_pll > MCLK_MAX * 4)
139                mdiv *= 2;
140            else
141                hdiv = mdiv = cdiv * 4;
142        }
143        pdiv = mdiv;
144        dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, "
145            "dividers %d, %d, %d, %d\n",
146            __FUNCTION__, cclk, freqs.old, freqs.new,
147            cdiv, hdiv, mdiv, pdiv);
148        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
149        ret = clk_main_set_dividers(new_pll == old_pll,
150                        cdiv, hdiv, mdiv, pdiv);
151        if (ret) {
152            dprintk(KERN_INFO "failed to set dividers\n");
153        } else if (new_pll != old_pll) {
154            dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n",
155                __FUNCTION__, pll, old_pll, new_pll);
156            ret = clk_set_rate(pll, new_pll * 1000);
157        }
158        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
159    }
160
161    return ret;
162}
163
164static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
165{
166    int ret;
167
168    dprintk(KERN_INFO "Jz4740 cpufreq driver\n");
169
170    if (policy->cpu != 0)
171        return -EINVAL;
172
173    pll = clk_get(NULL, "pll");
174    if (IS_ERR(pll)) {
175        ret = PTR_ERR(pll);
176        goto err_exit;
177    }
178
179    cclk = clk_get(NULL, "cclk");
180    if (IS_ERR(cclk)) {
181        ret = PTR_ERR(cclk);
182        goto err_clk_put_pll;
183    }
184
185    policy->cpuinfo.min_freq = HCLK_MIN;
186    policy->cpuinfo.max_freq = 500000;
187    policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */
188    policy->cur = jz4740_freq_get(policy->cpu);
189    policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
190    /* min and max are set by jz4740_freq_fill_table() */
191
192    jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */);
193
194    return 0;
195
196err_clk_put_pll:
197    clk_put(pll);
198err_exit:
199    return ret;
200}
201
202static struct cpufreq_driver cpufreq_jz4740_driver = {
203    .init = jz4740_cpufreq_driver_init,
204    .verify = jz4740_freq_verify,
205    .target = jz4740_freq_target,
206    .get = jz4740_freq_get,
207    .name = "jz4740",
208};
209
210static int __init jz4740_cpufreq_init(void)
211{
212    return cpufreq_register_driver(&cpufreq_jz4740_driver);
213}
214
215static void __exit jz4740_cpufreq_exit(void)
216{
217    cpufreq_unregister_driver(&cpufreq_jz4740_driver);
218}
219
220module_init(jz4740_cpufreq_init);
221module_exit(jz4740_cpufreq_exit);
222
223MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, "
224          "Maarten ter Huurne <maarten@treewalker.org>");
225MODULE_DESCRIPTION("cpufreq driver for Jz4740");
226MODULE_LICENSE("GPL");
arch/mips/kernel/cpufreq/Kconfig
88config MIPS_CPUFREQ
99    bool
1010    default y
11    depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER
11    depends on CPU_SUPPORTS_CPUFREQ
1212
1313if MIPS_CPUFREQ
1414
...... 
2424    tristate "Loongson2 CPUFreq Driver"
2525    select CPU_FREQ_TABLE
2626    depends on MIPS_CPUFREQ
27    depends on MIPS_EXTERNAL_TIMER
2728    help
2829      This option adds a CPUFreq driver for loongson processors which
2930      support software configurable cpu frequency.
...... 
3435
3536      If in doubt, say N.
3637
38config CPU_FREQ_JZ
39    tristate "CPUfreq driver for JZ CPUs"
40    select CPU_FREQ_TABLE
41    depends on MACH_JZ4740
42    default n
43    help
44      This enables the CPUfreq driver for JZ CPUs.
45
46      If in doubt, say N.
47
3748endif # CPU_FREQ
3849
3950endmenu

Archive Download the corresponding diff file



interactive