Date:2011-03-16 03:16:04 (9 years 2 months ago)
Author:Maarten ter Huurne
Commit:6723dea2dfdeff02c84e699431b9e28c309dbf74
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)
drivers/cpufreq/Kconfig (1 diff)

Change Details

arch/mips/Kconfig
237237    select SYS_HAS_EARLY_PRINTK
238238    select HAVE_CLK
239239    select GENERIC_IRQ_CHIP
240    select CPU_SUPPORTS_CPUFREQ
240241
241242config LANTIQ
242243    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");
drivers/cpufreq/Kconfig
248248
249249      If in doubt, say N.
250250
251config CPU_FREQ_JZ
252    tristate "CPUfreq driver for JZ CPUs"
253    select CPU_FREQ_TABLE
254    depends on MACH_JZ4740
255    default n
256    help
257      This enables the CPUfreq driver for JZ CPUs.
258
259      If in doubt, say N.
260
251261endmenu
252262
253263menu "PowerPC CPU frequency scaling drivers"

Archive Download the corresponding diff file



interactive