Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=cb5ec75b8b0410bba4ae612d13a2f26e938bc49c
Commit:     cb5ec75b8b0410bba4ae612d13a2f26e938bc49c
Parent:     fe04d7798f611bca333df27d44484bdd62eef53f
Author:     Paul Mundt <[EMAIL PROTECTED]>
AuthorDate: Fri Jul 20 13:38:19 2007 +0900
Committer:  Paul Mundt <[EMAIL PROTECTED]>
CommitDate: Fri Jul 20 13:38:19 2007 +0900

    sh: cpufreq: clock framework support.
    
    This gets the SH cpufreq working again. We follow the changes
    in the AVR32 implementation for wrapping in to the clock framework.
    CPUs that wish to use this are required to define rate rounding
    primitives in order to satisfy clk_round_rate().
    
    This works well enough for the common case, though we should
    look at unifying this driver across all of the platforms that
    implement clock framework support in one capacity or another.
    
    Signed-off-by: Paul Mundt <[EMAIL PROTECTED]>
---
 arch/sh/Kconfig          |    2 +-
 arch/sh/kernel/cpufreq.c |  214 +++++++++++++++++-----------------------------
 2 files changed, 81 insertions(+), 135 deletions(-)

diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig
index 2a9682e..ddfd358 100644
--- a/arch/sh/Kconfig
+++ b/arch/sh/Kconfig
@@ -475,7 +475,7 @@ source "drivers/cpufreq/Kconfig"
 
 config SH_CPU_FREQ
        tristate "SuperH CPU Frequency driver"
-       depends on CPU_FREQ && CPU_SH4 && BROKEN
+       depends on CPU_FREQ
        select CPU_FREQ_TABLE
        help
          This adds the cpufreq driver for SuperH. At present, only
diff --git a/arch/sh/kernel/cpufreq.c b/arch/sh/kernel/cpufreq.c
index 47abf6e..675118d 100644
--- a/arch/sh/kernel/cpufreq.c
+++ b/arch/sh/kernel/cpufreq.c
@@ -3,89 +3,51 @@
  *
  * cpufreq driver for the SuperH processors.
  *
- * Copyright (C) 2002, 2003, 2004, 2005 Paul Mundt
+ * Copyright (C) 2002 - 2007 Paul Mundt
  * Copyright (C) 2002 M. R. Brown
  *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
+ * Clock framework bits from arch/avr32/mach-at32ap/cpufreq.c
+ *
+ *   Copyright (C) 2004-2007 Atmel Corporation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
  */
 #include <linux/types.h>
 #include <linux/cpufreq.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/err.h>
 #include <linux/cpumask.h>
 #include <linux/smp.h>
 #include <linux/sched.h>       /* set_cpus_allowed() */
-
+#include <linux/io.h>
+#include <linux/clk.h>
 #include <asm/processor.h>
 #include <asm/watchdog.h>
 #include <asm/freq.h>
-#include <asm/io.h>
-
-/*
- * For SuperH, each policy change requires that we change the IFC, BFC, and
- * PFC at the same time.  Here we define sane values that won't trash the
- * system.
- *
- * Note the max set is computed at runtime, we use the divisors that we booted
- * with to setup our maximum operating frequencies.
- */
-struct clock_set {
-       unsigned int ifc;
-       unsigned int bfc;
-       unsigned int pfc;
-} clock_sets[] = {
-#if defined(CONFIG_CPU_SH3) || defined(CONFIG_CPU_SH2)
-       { 0, 0, 0 },    /* not implemented yet */
-#elif defined(CONFIG_CPU_SH4)
-       { 4, 8, 8 },    /* min - IFC: 1/4, BFC: 1/8, PFC: 1/8 */
-       { 1, 2, 2 },    /* max - IFC: 1, BFC: 1/2, PFC: 1/2 */
-#endif
-};
-
-#define MIN_CLOCK_SET  0
-#define MAX_CLOCK_SET  (ARRAY_SIZE(clock_sets) - 1)
-
-/*
- * For the time being, we only support two frequencies, which in turn are
- * aimed at the POWERSAVE and PERFORMANCE policies, which in turn are derived
- * directly from the respective min/max clock sets. Technically we could
- * support a wider range of frequencies, but these vary far too much for each
- * CPU subtype (and we'd have to construct a frequency table for each subtype).
- *
- * Maybe something to implement in the future..
- */
-#define SH_FREQ_MAX    0
-#define SH_FREQ_MIN    1
+#include <asm/clock.h>
 
-static struct cpufreq_frequency_table sh_freqs[] = {
-       { SH_FREQ_MAX,  0 },
-       { SH_FREQ_MIN,  0 },
-       { 0,            CPUFREQ_TABLE_END },
-};
+static struct clk *cpuclk;
 
-static void sh_cpufreq_update_clocks(unsigned int set)
+static unsigned int sh_cpufreq_get(unsigned int cpu)
 {
-       current_cpu_data.cpu_clock = current_cpu_data.master_clock / 
clock_sets[set].ifc;
-       current_cpu_data.bus_clock = current_cpu_data.master_clock / 
clock_sets[set].bfc;
-       current_cpu_data.module_clock = current_cpu_data.master_clock / 
clock_sets[set].pfc;
-       current_cpu_data.loops_per_jiffy = loops_per_jiffy;
+       return (clk_get_rate(cpuclk) + 500) / 1000;
 }
 
-/* XXX: This needs to be split out per CPU and CPU subtype. */
 /*
  * Here we notify other drivers of the proposed change and the final change.
  */
-static int sh_cpufreq_setstate(unsigned int cpu, unsigned int set)
+static int sh_cpufreq_target(struct cpufreq_policy *policy,
+                            unsigned int target_freq,
+                            unsigned int relation)
 {
-       unsigned short frqcr = ctrl_inw(FRQCR);
+       unsigned int cpu = policy->cpu;
        cpumask_t cpus_allowed;
        struct cpufreq_freqs freqs;
+       long freq;
 
        if (!cpu_online(cpu))
                return -ENODEV;
@@ -95,125 +57,109 @@ static int sh_cpufreq_setstate(unsigned int cpu, unsigned 
int set)
 
        BUG_ON(smp_processor_id() != cpu);
 
-       freqs.cpu = cpu;
-       freqs.old = current_cpu_data.cpu_clock / 1000;
-       freqs.new = (current_cpu_data.master_clock / clock_sets[set].ifc) / 
1000;
+       /* Convert target_freq from kHz to Hz */
+       freq = clk_round_rate(cpuclk, target_freq * 1000);
 
-       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-#if defined(CONFIG_CPU_SH3)
-       frqcr |= (newstate & 0x4000) << 14;
-       frqcr |= (newstate & 0x000c) <<  2;
-#elif defined(CONFIG_CPU_SH4)
-       /*
-        * FRQCR.PLL2EN is 1, we need to allow the PLL to stabilize by
-        * initializing the WDT.
-        */
-       if (frqcr & (1 << 9)) {
-               __u8 csr;
-
-               /*
-                * Set the overflow period to the highest available,
-                * in this case a 1/4096 division ratio yields a 5.25ms
-                * overflow period. See asm-sh/watchdog.h for more
-                * information and a range of other divisors.
-                */
-               csr = sh_wdt_read_csr();
-               csr |= WTCSR_CKS_4096;
-               sh_wdt_write_csr(csr);
-
-               sh_wdt_write_cnt(0);
-       }
-       frqcr &= 0x0e00;        /* Clear ifc, bfc, pfc */
-       frqcr |= get_ifc_value(clock_sets[set].ifc) << 6;
-       frqcr |= get_bfc_value(clock_sets[set].bfc) << 3;
-       frqcr |= get_pfc_value(clock_sets[set].pfc);
-#endif
-       ctrl_outw(frqcr, FRQCR);
-       sh_cpufreq_update_clocks(set);
+       if (freq < (policy->min * 1000) || freq > (policy->max * 1000))
+               return -EINVAL;
+
+       pr_debug("cpufreq: requested frequency %u Hz\n", target_freq * 1000);
 
+       freqs.cpu       = cpu;
+       freqs.old       = sh_cpufreq_get(cpu);
+       freqs.new       = (freq + 500) / 1000;
+       freqs.flags     = 0;
+
+       cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
        set_cpus_allowed(current, cpus_allowed);
+       clk_set_rate(cpuclk, freq);
        cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
 
+       pr_debug("cpufreq: set frequency %lu Hz\n", freq);
+
        return 0;
 }
 
 static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy)
 {
-       unsigned int min_freq, max_freq;
-       unsigned int ifc, bfc, pfc;
+       printk(KERN_INFO "cpufreq: SuperH CPU frequency driver.\n");
 
        if (!cpu_online(policy->cpu))
                return -ENODEV;
 
-       /* Update our maximum clock set */
-       get_current_frequency_divisors(&ifc, &bfc, &pfc);
-       clock_sets[MAX_CLOCK_SET].ifc = ifc;
-       clock_sets[MAX_CLOCK_SET].bfc = bfc;
-       clock_sets[MAX_CLOCK_SET].pfc = pfc;
-
-       /* Convert from Hz to kHz */
-       max_freq = current_cpu_data.cpu_clock / 1000;
-       min_freq = (current_cpu_data.master_clock / 
clock_sets[MIN_CLOCK_SET].ifc) / 1000;
-       
-       sh_freqs[SH_FREQ_MAX].frequency = max_freq;
-       sh_freqs[SH_FREQ_MIN].frequency = min_freq;
+       cpuclk = clk_get(NULL, "cpu_clk");
+       if (IS_ERR(cpuclk)) {
+               printk(KERN_ERR "cpufreq: couldn't get CPU clk\n");
+               return PTR_ERR(cpuclk);
+       }
 
        /* cpuinfo and default policy values */
-       policy->governor                   = CPUFREQ_DEFAULT_GOVERNOR;
+       policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000;
+       policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000;
        policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
-       policy->cur                        = max_freq;
 
-       return cpufreq_frequency_table_cpuinfo(policy, &sh_freqs[0]);
-}
+       policy->governor        = CPUFREQ_DEFAULT_GOVERNOR;
+       policy->cur             = sh_cpufreq_get(policy->cpu);
+       policy->min             = policy->cpuinfo.min_freq;
+       policy->max             = policy->cpuinfo.max_freq;
 
-static int sh_cpufreq_verify(struct cpufreq_policy *policy)
-{
-       return cpufreq_frequency_table_verify(policy, &sh_freqs[0]);
-}
 
-static int sh_cpufreq_target(struct cpufreq_policy *policy,
-                            unsigned int target_freq,
-                            unsigned int relation)
-{
-       unsigned int set, idx = 0;
+       /*
+        * Catch the cases where the clock framework hasn't been wired up
+        * properly to support scaling.
+        */
+       if (unlikely(policy->min == policy->max)) {
+               printk(KERN_ERR "cpufreq: clock framework rate rounding "
+                      "not supported on this CPU.\n");
 
-       if (cpufreq_frequency_table_target(policy, &sh_freqs[0], target_freq, 
relation, &idx))
+               clk_put(cpuclk);
                return -EINVAL;
+       }
 
-       set = (idx == SH_FREQ_MIN) ? MIN_CLOCK_SET : MAX_CLOCK_SET;
+       printk(KERN_INFO "cpufreq: Frequencies - Minimum %u.%03u MHz, "
+              "Maximum %u.%03u MHz.\n",
+              policy->min / 1000, policy->min % 1000,
+              policy->max / 1000, policy->max % 1000);
 
-       sh_cpufreq_setstate(policy->cpu, set);
+       return 0;
+}
 
+static int sh_cpufreq_verify(struct cpufreq_policy *policy)
+{
+       cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+                                    policy->cpuinfo.max_freq);
+       return 0;
+}
+
+static int sh_cpufreq_exit(struct cpufreq_policy *policy)
+{
+       clk_put(cpuclk);
        return 0;
 }
 
 static struct cpufreq_driver sh_cpufreq_driver = {
        .owner          = THIS_MODULE,
-       .name           = "SH cpufreq",
+       .name           = "sh",
        .init           = sh_cpufreq_cpu_init,
        .verify         = sh_cpufreq_verify,
        .target         = sh_cpufreq_target,
+       .get            = sh_cpufreq_get,
+       .exit           = sh_cpufreq_exit,
 };
 
-static int __init sh_cpufreq_init(void)
+static int __init sh_cpufreq_module_init(void)
 {
-       if (!current_cpu_data.cpu_clock)
-               return -EINVAL;
-       if (cpufreq_register_driver(&sh_cpufreq_driver))
-               return -EINVAL;
-
-       return 0;
+       return cpufreq_register_driver(&sh_cpufreq_driver);
 }
 
-static void __exit sh_cpufreq_exit(void)
+static void __exit sh_cpufreq_module_exit(void)
 {
        cpufreq_unregister_driver(&sh_cpufreq_driver);
 }
 
-module_init(sh_cpufreq_init);
-module_exit(sh_cpufreq_exit);
+module_init(sh_cpufreq_module_init);
+module_exit(sh_cpufreq_module_exit);
 
 MODULE_AUTHOR("Paul Mundt <[EMAIL PROTECTED]>");
 MODULE_DESCRIPTION("cpufreq driver for SuperH");
 MODULE_LICENSE("GPL");
-
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to