cpufreq: sparc: move cpufreq driver to drivers/cpufreq
authorViresh Kumar <viresh.kumar@linaro.org>
Thu, 4 Apr 2013 12:54:24 +0000 (12:54 +0000)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Wed, 10 Apr 2013 11:19:26 +0000 (13:19 +0200)
This patch moves cpufreq driver of SPARC architecture to drivers/cpufreq.

Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Acked-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
arch/sparc/Kconfig
arch/sparc/kernel/Makefile
arch/sparc/kernel/us2e_cpufreq.c [deleted file]
arch/sparc/kernel/us3_cpufreq.c [deleted file]
drivers/cpufreq/Kconfig
drivers/cpufreq/Makefile
drivers/cpufreq/sparc-us2e-cpufreq.c [new file with mode: 0644]
drivers/cpufreq/sparc-us3-cpufreq.c [new file with mode: 0644]

index 3d361f236308c13cde14286379ec418af3e52990..c85b76100e3f64ba37ae90fdc07b94a18da57409 100644 (file)
@@ -254,29 +254,6 @@ config HOTPLUG_CPU
 
 if SPARC64
 source "drivers/cpufreq/Kconfig"
-
-config US3_FREQ
-       tristate "UltraSPARC-III CPU Frequency driver"
-       depends on CPU_FREQ
-       select CPU_FREQ_TABLE
-       help
-         This adds the CPUFreq driver for UltraSPARC-III processors.
-
-         For details, take a look at <file:Documentation/cpu-freq>.
-
-         If in doubt, say N.
-
-config US2E_FREQ
-       tristate "UltraSPARC-IIe CPU Frequency driver"
-       depends on CPU_FREQ
-       select CPU_FREQ_TABLE
-       help
-         This adds the CPUFreq driver for UltraSPARC-IIe processors.
-
-         For details, take a look at <file:Documentation/cpu-freq>.
-
-         If in doubt, say N.
-
 endif
 
 config US3_MC
index 6cf591b7e1c67e2b82c4e28bf177197ce0f602a7..5276fd4e9d03a730f975f9394588ad091c6d683a 100644 (file)
@@ -102,9 +102,6 @@ obj-$(CONFIG_PCI_MSI)        += pci_msi.o
 
 obj-$(CONFIG_COMPAT)         += sys32.o sys_sparc32.o signal32.o
 
-# sparc64 cpufreq
-obj-$(CONFIG_US3_FREQ)  += us3_cpufreq.o
-obj-$(CONFIG_US2E_FREQ) += us2e_cpufreq.o
 obj-$(CONFIG_US3_MC)    += chmc.o
 
 obj-$(CONFIG_KPROBES)   += kprobes.o
diff --git a/arch/sparc/kernel/us2e_cpufreq.c b/arch/sparc/kernel/us2e_cpufreq.c
deleted file mode 100644 (file)
index 306ae46..0000000
+++ /dev/null
@@ -1,408 +0,0 @@
-/* us2e_cpufreq.c: UltraSPARC-IIe cpu frequency support
- *
- * Copyright (C) 2003 David S. Miller (davem@redhat.com)
- *
- * Many thanks to Dominik Brodowski for fixing up the cpufreq
- * infrastructure in order to make this driver easier to implement.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/cpufreq.h>
-#include <linux/threads.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/init.h>
-
-#include <asm/asi.h>
-#include <asm/timer.h>
-
-static struct cpufreq_driver *cpufreq_us2e_driver;
-
-struct us2e_freq_percpu_info {
-       struct cpufreq_frequency_table table[6];
-};
-
-/* Indexed by cpu number. */
-static struct us2e_freq_percpu_info *us2e_freq_table;
-
-#define HBIRD_MEM_CNTL0_ADDR   0x1fe0000f010UL
-#define HBIRD_ESTAR_MODE_ADDR  0x1fe0000f080UL
-
-/* UltraSPARC-IIe has five dividers: 1, 2, 4, 6, and 8.  These are controlled
- * in the ESTAR mode control register.
- */
-#define ESTAR_MODE_DIV_1       0x0000000000000000UL
-#define ESTAR_MODE_DIV_2       0x0000000000000001UL
-#define ESTAR_MODE_DIV_4       0x0000000000000003UL
-#define ESTAR_MODE_DIV_6       0x0000000000000002UL
-#define ESTAR_MODE_DIV_8       0x0000000000000004UL
-#define ESTAR_MODE_DIV_MASK    0x0000000000000007UL
-
-#define MCTRL0_SREFRESH_ENAB   0x0000000000010000UL
-#define MCTRL0_REFR_COUNT_MASK 0x0000000000007f00UL
-#define MCTRL0_REFR_COUNT_SHIFT        8
-#define MCTRL0_REFR_INTERVAL   7800
-#define MCTRL0_REFR_CLKS_P_CNT 64
-
-static unsigned long read_hbreg(unsigned long addr)
-{
-       unsigned long ret;
-
-       __asm__ __volatile__("ldxa      [%1] %2, %0"
-                            : "=&r" (ret)
-                            : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
-       return ret;
-}
-
-static void write_hbreg(unsigned long addr, unsigned long val)
-{
-       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
-                            "membar    #Sync"
-                            : /* no outputs */
-                            : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
-                            : "memory");
-       if (addr == HBIRD_ESTAR_MODE_ADDR) {
-               /* Need to wait 16 clock cycles for the PLL to lock.  */
-               udelay(1);
-       }
-}
-
-static void self_refresh_ctl(int enable)
-{
-       unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
-
-       if (enable)
-               mctrl |= MCTRL0_SREFRESH_ENAB;
-       else
-               mctrl &= ~MCTRL0_SREFRESH_ENAB;
-       write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
-       (void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
-}
-
-static void frob_mem_refresh(int cpu_slowing_down,
-                            unsigned long clock_tick,
-                            unsigned long old_divisor, unsigned long divisor)
-{
-       unsigned long old_refr_count, refr_count, mctrl;
-
-       refr_count  = (clock_tick * MCTRL0_REFR_INTERVAL);
-       refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
-
-       mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
-       old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
-               >> MCTRL0_REFR_COUNT_SHIFT;
-
-       mctrl &= ~MCTRL0_REFR_COUNT_MASK;
-       mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
-       write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
-       mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
-
-       if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
-               unsigned long usecs;
-
-               /* We have to wait for both refresh counts (old
-                * and new) to go to zero.
-                */
-               usecs = (MCTRL0_REFR_CLKS_P_CNT *
-                        (refr_count + old_refr_count) *
-                        1000000UL *
-                        old_divisor) / clock_tick;
-               udelay(usecs + 1UL);
-       }
-}
-
-static void us2e_transition(unsigned long estar, unsigned long new_bits,
-                           unsigned long clock_tick,
-                           unsigned long old_divisor, unsigned long divisor)
-{
-       unsigned long flags;
-
-       local_irq_save(flags);
-
-       estar &= ~ESTAR_MODE_DIV_MASK;
-
-       /* This is based upon the state transition diagram in the IIe manual.  */
-       if (old_divisor == 2 && divisor == 1) {
-               self_refresh_ctl(0);
-               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
-               frob_mem_refresh(0, clock_tick, old_divisor, divisor);
-       } else if (old_divisor == 1 && divisor == 2) {
-               frob_mem_refresh(1, clock_tick, old_divisor, divisor);
-               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
-               self_refresh_ctl(1);
-       } else if (old_divisor == 1 && divisor > 2) {
-               us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
-                               1, 2);
-               us2e_transition(estar, new_bits, clock_tick,
-                               2, divisor);
-       } else if (old_divisor > 2 && divisor == 1) {
-               us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
-                               old_divisor, 2);
-               us2e_transition(estar, new_bits, clock_tick,
-                               2, divisor);
-       } else if (old_divisor < divisor) {
-               frob_mem_refresh(0, clock_tick, old_divisor, divisor);
-               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
-       } else if (old_divisor > divisor) {
-               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
-               frob_mem_refresh(1, clock_tick, old_divisor, divisor);
-       } else {
-               BUG();
-       }
-
-       local_irq_restore(flags);
-}
-
-static unsigned long index_to_estar_mode(unsigned int index)
-{
-       switch (index) {
-       case 0:
-               return ESTAR_MODE_DIV_1;
-
-       case 1:
-               return ESTAR_MODE_DIV_2;
-
-       case 2:
-               return ESTAR_MODE_DIV_4;
-
-       case 3:
-               return ESTAR_MODE_DIV_6;
-
-       case 4:
-               return ESTAR_MODE_DIV_8;
-
-       default:
-               BUG();
-       }
-}
-
-static unsigned long index_to_divisor(unsigned int index)
-{
-       switch (index) {
-       case 0:
-               return 1;
-
-       case 1:
-               return 2;
-
-       case 2:
-               return 4;
-
-       case 3:
-               return 6;
-
-       case 4:
-               return 8;
-
-       default:
-               BUG();
-       }
-}
-
-static unsigned long estar_to_divisor(unsigned long estar)
-{
-       unsigned long ret;
-
-       switch (estar & ESTAR_MODE_DIV_MASK) {
-       case ESTAR_MODE_DIV_1:
-               ret = 1;
-               break;
-       case ESTAR_MODE_DIV_2:
-               ret = 2;
-               break;
-       case ESTAR_MODE_DIV_4:
-               ret = 4;
-               break;
-       case ESTAR_MODE_DIV_6:
-               ret = 6;
-               break;
-       case ESTAR_MODE_DIV_8:
-               ret = 8;
-               break;
-       default:
-               BUG();
-       }
-
-       return ret;
-}
-
-static unsigned int us2e_freq_get(unsigned int cpu)
-{
-       cpumask_t cpus_allowed;
-       unsigned long clock_tick, estar;
-
-       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
-       set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
-       clock_tick = sparc64_get_clock_tick(cpu) / 1000;
-       estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
-
-       set_cpus_allowed_ptr(current, &cpus_allowed);
-
-       return clock_tick / estar_to_divisor(estar);
-}
-
-static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy,
-               unsigned int index)
-{
-       unsigned int cpu = policy->cpu;
-       unsigned long new_bits, new_freq;
-       unsigned long clock_tick, divisor, old_divisor, estar;
-       cpumask_t cpus_allowed;
-       struct cpufreq_freqs freqs;
-
-       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
-       set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
-       new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
-       new_bits = index_to_estar_mode(index);
-       divisor = index_to_divisor(index);
-       new_freq /= divisor;
-
-       estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
-
-       old_divisor = estar_to_divisor(estar);
-
-       freqs.old = clock_tick / old_divisor;
-       freqs.new = new_freq;
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
-       if (old_divisor != divisor)
-               us2e_transition(estar, new_bits, clock_tick * 1000,
-                               old_divisor, divisor);
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
-       set_cpus_allowed_ptr(current, &cpus_allowed);
-}
-
-static int us2e_freq_target(struct cpufreq_policy *policy,
-                         unsigned int target_freq,
-                         unsigned int relation)
-{
-       unsigned int new_index = 0;
-
-       if (cpufreq_frequency_table_target(policy,
-                                          &us2e_freq_table[policy->cpu].table[0],
-                                          target_freq, relation, &new_index))
-               return -EINVAL;
-
-       us2e_set_cpu_divider_index(policy, new_index);
-
-       return 0;
-}
-
-static int us2e_freq_verify(struct cpufreq_policy *policy)
-{
-       return cpufreq_frequency_table_verify(policy,
-                                             &us2e_freq_table[policy->cpu].table[0]);
-}
-
-static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
-{
-       unsigned int cpu = policy->cpu;
-       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
-       struct cpufreq_frequency_table *table =
-               &us2e_freq_table[cpu].table[0];
-
-       table[0].index = 0;
-       table[0].frequency = clock_tick / 1;
-       table[1].index = 1;
-       table[1].frequency = clock_tick / 2;
-       table[2].index = 2;
-       table[2].frequency = clock_tick / 4;
-       table[2].index = 3;
-       table[2].frequency = clock_tick / 6;
-       table[2].index = 4;
-       table[2].frequency = clock_tick / 8;
-       table[2].index = 5;
-       table[3].frequency = CPUFREQ_TABLE_END;
-
-       policy->cpuinfo.transition_latency = 0;
-       policy->cur = clock_tick;
-
-       return cpufreq_frequency_table_cpuinfo(policy, table);
-}
-
-static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
-{
-       if (cpufreq_us2e_driver)
-               us2e_set_cpu_divider_index(policy, 0);
-
-       return 0;
-}
-
-static int __init us2e_freq_init(void)
-{
-       unsigned long manuf, impl, ver;
-       int ret;
-
-       if (tlb_type != spitfire)
-               return -ENODEV;
-
-       __asm__("rdpr %%ver, %0" : "=r" (ver));
-       manuf = ((ver >> 48) & 0xffff);
-       impl  = ((ver >> 32) & 0xffff);
-
-       if (manuf == 0x17 && impl == 0x13) {
-               struct cpufreq_driver *driver;
-
-               ret = -ENOMEM;
-               driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
-               if (!driver)
-                       goto err_out;
-
-               us2e_freq_table = kzalloc(
-                       (NR_CPUS * sizeof(struct us2e_freq_percpu_info)),
-                       GFP_KERNEL);
-               if (!us2e_freq_table)
-                       goto err_out;
-
-               driver->init = us2e_freq_cpu_init;
-               driver->verify = us2e_freq_verify;
-               driver->target = us2e_freq_target;
-               driver->get = us2e_freq_get;
-               driver->exit = us2e_freq_cpu_exit;
-               driver->owner = THIS_MODULE,
-               strcpy(driver->name, "UltraSPARC-IIe");
-
-               cpufreq_us2e_driver = driver;
-               ret = cpufreq_register_driver(driver);
-               if (ret)
-                       goto err_out;
-
-               return 0;
-
-err_out:
-               if (driver) {
-                       kfree(driver);
-                       cpufreq_us2e_driver = NULL;
-               }
-               kfree(us2e_freq_table);
-               us2e_freq_table = NULL;
-               return ret;
-       }
-
-       return -ENODEV;
-}
-
-static void __exit us2e_freq_exit(void)
-{
-       if (cpufreq_us2e_driver) {
-               cpufreq_unregister_driver(cpufreq_us2e_driver);
-               kfree(cpufreq_us2e_driver);
-               cpufreq_us2e_driver = NULL;
-               kfree(us2e_freq_table);
-               us2e_freq_table = NULL;
-       }
-}
-
-MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
-MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
-MODULE_LICENSE("GPL");
-
-module_init(us2e_freq_init);
-module_exit(us2e_freq_exit);
diff --git a/arch/sparc/kernel/us3_cpufreq.c b/arch/sparc/kernel/us3_cpufreq.c
deleted file mode 100644 (file)
index c71ee14..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/* us3_cpufreq.c: UltraSPARC-III cpu frequency support
- *
- * Copyright (C) 2003 David S. Miller (davem@redhat.com)
- *
- * Many thanks to Dominik Brodowski for fixing up the cpufreq
- * infrastructure in order to make this driver easier to implement.
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/sched.h>
-#include <linux/smp.h>
-#include <linux/cpufreq.h>
-#include <linux/threads.h>
-#include <linux/slab.h>
-#include <linux/init.h>
-
-#include <asm/head.h>
-#include <asm/timer.h>
-
-static struct cpufreq_driver *cpufreq_us3_driver;
-
-struct us3_freq_percpu_info {
-       struct cpufreq_frequency_table table[4];
-};
-
-/* Indexed by cpu number. */
-static struct us3_freq_percpu_info *us3_freq_table;
-
-/* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
- * in the Safari config register.
- */
-#define SAFARI_CFG_DIV_1       0x0000000000000000UL
-#define SAFARI_CFG_DIV_2       0x0000000040000000UL
-#define SAFARI_CFG_DIV_32      0x0000000080000000UL
-#define SAFARI_CFG_DIV_MASK    0x00000000C0000000UL
-
-static unsigned long read_safari_cfg(void)
-{
-       unsigned long ret;
-
-       __asm__ __volatile__("ldxa      [%%g0] %1, %0"
-                            : "=&r" (ret)
-                            : "i" (ASI_SAFARI_CONFIG));
-       return ret;
-}
-
-static void write_safari_cfg(unsigned long val)
-{
-       __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
-                            "membar    #Sync"
-                            : /* no outputs */
-                            : "r" (val), "i" (ASI_SAFARI_CONFIG)
-                            : "memory");
-}
-
-static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
-{
-       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
-       unsigned long ret;
-
-       switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
-       case SAFARI_CFG_DIV_1:
-               ret = clock_tick / 1;
-               break;
-       case SAFARI_CFG_DIV_2:
-               ret = clock_tick / 2;
-               break;
-       case SAFARI_CFG_DIV_32:
-               ret = clock_tick / 32;
-               break;
-       default:
-               BUG();
-       }
-
-       return ret;
-}
-
-static unsigned int us3_freq_get(unsigned int cpu)
-{
-       cpumask_t cpus_allowed;
-       unsigned long reg;
-       unsigned int ret;
-
-       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
-       set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
-       reg = read_safari_cfg();
-       ret = get_current_freq(cpu, reg);
-
-       set_cpus_allowed_ptr(current, &cpus_allowed);
-
-       return ret;
-}
-
-static void us3_set_cpu_divider_index(struct cpufreq_policy *policy,
-               unsigned int index)
-{
-       unsigned int cpu = policy->cpu;
-       unsigned long new_bits, new_freq, reg;
-       cpumask_t cpus_allowed;
-       struct cpufreq_freqs freqs;
-
-       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
-       set_cpus_allowed_ptr(current, cpumask_of(cpu));
-
-       new_freq = sparc64_get_clock_tick(cpu) / 1000;
-       switch (index) {
-       case 0:
-               new_bits = SAFARI_CFG_DIV_1;
-               new_freq /= 1;
-               break;
-       case 1:
-               new_bits = SAFARI_CFG_DIV_2;
-               new_freq /= 2;
-               break;
-       case 2:
-               new_bits = SAFARI_CFG_DIV_32;
-               new_freq /= 32;
-               break;
-
-       default:
-               BUG();
-       }
-
-       reg = read_safari_cfg();
-
-       freqs.old = get_current_freq(cpu, reg);
-       freqs.new = new_freq;
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
-
-       reg &= ~SAFARI_CFG_DIV_MASK;
-       reg |= new_bits;
-       write_safari_cfg(reg);
-
-       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
-
-       set_cpus_allowed_ptr(current, &cpus_allowed);
-}
-
-static int us3_freq_target(struct cpufreq_policy *policy,
-                         unsigned int target_freq,
-                         unsigned int relation)
-{
-       unsigned int new_index = 0;
-
-       if (cpufreq_frequency_table_target(policy,
-                                          &us3_freq_table[policy->cpu].table[0],
-                                          target_freq,
-                                          relation,
-                                          &new_index))
-               return -EINVAL;
-
-       us3_set_cpu_divider_index(policy, new_index);
-
-       return 0;
-}
-
-static int us3_freq_verify(struct cpufreq_policy *policy)
-{
-       return cpufreq_frequency_table_verify(policy,
-                                             &us3_freq_table[policy->cpu].table[0]);
-}
-
-static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
-{
-       unsigned int cpu = policy->cpu;
-       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
-       struct cpufreq_frequency_table *table =
-               &us3_freq_table[cpu].table[0];
-
-       table[0].index = 0;
-       table[0].frequency = clock_tick / 1;
-       table[1].index = 1;
-       table[1].frequency = clock_tick / 2;
-       table[2].index = 2;
-       table[2].frequency = clock_tick / 32;
-       table[3].index = 0;
-       table[3].frequency = CPUFREQ_TABLE_END;
-
-       policy->cpuinfo.transition_latency = 0;
-       policy->cur = clock_tick;
-
-       return cpufreq_frequency_table_cpuinfo(policy, table);
-}
-
-static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
-{
-       if (cpufreq_us3_driver)
-               us3_set_cpu_divider_index(policy, 0);
-
-       return 0;
-}
-
-static int __init us3_freq_init(void)
-{
-       unsigned long manuf, impl, ver;
-       int ret;
-
-       if (tlb_type != cheetah && tlb_type != cheetah_plus)
-               return -ENODEV;
-
-       __asm__("rdpr %%ver, %0" : "=r" (ver));
-       manuf = ((ver >> 48) & 0xffff);
-       impl  = ((ver >> 32) & 0xffff);
-
-       if (manuf == CHEETAH_MANUF &&
-           (impl == CHEETAH_IMPL ||
-            impl == CHEETAH_PLUS_IMPL ||
-            impl == JAGUAR_IMPL ||
-            impl == PANTHER_IMPL)) {
-               struct cpufreq_driver *driver;
-
-               ret = -ENOMEM;
-               driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
-               if (!driver)
-                       goto err_out;
-
-               us3_freq_table = kzalloc(
-                       (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
-                       GFP_KERNEL);
-               if (!us3_freq_table)
-                       goto err_out;
-
-               driver->init = us3_freq_cpu_init;
-               driver->verify = us3_freq_verify;
-               driver->target = us3_freq_target;
-               driver->get = us3_freq_get;
-               driver->exit = us3_freq_cpu_exit;
-               driver->owner = THIS_MODULE,
-               strcpy(driver->name, "UltraSPARC-III");
-
-               cpufreq_us3_driver = driver;
-               ret = cpufreq_register_driver(driver);
-               if (ret)
-                       goto err_out;
-
-               return 0;
-
-err_out:
-               if (driver) {
-                       kfree(driver);
-                       cpufreq_us3_driver = NULL;
-               }
-               kfree(us3_freq_table);
-               us3_freq_table = NULL;
-               return ret;
-       }
-
-       return -ENODEV;
-}
-
-static void __exit us3_freq_exit(void)
-{
-       if (cpufreq_us3_driver) {
-               cpufreq_unregister_driver(cpufreq_us3_driver);
-               kfree(cpufreq_us3_driver);
-               cpufreq_us3_driver = NULL;
-               kfree(us3_freq_table);
-               us3_freq_table = NULL;
-       }
-}
-
-MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
-MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
-MODULE_LICENSE("GPL");
-
-module_init(us3_freq_init);
-module_exit(us3_freq_exit);
index 602d5dbc434599eca6a443eb709eab3c5a6d05c8..a1488f58f6ca40eb0b660a82e97a54ce0246a430 100644 (file)
@@ -258,6 +258,29 @@ depends on PPC32 || PPC64
 source "drivers/cpufreq/Kconfig.powerpc"
 endmenu
 
+menu "SPARC CPU frequency scaling drivers"
+depends on SPARC64
+config SPARC_US3_CPUFREQ
+       tristate "UltraSPARC-III CPU Frequency driver"
+       select CPU_FREQ_TABLE
+       help
+         This adds the CPUFreq driver for UltraSPARC-III processors.
+
+         For details, take a look at <file:Documentation/cpu-freq>.
+
+         If in doubt, say N.
+
+config SPARC_US2E_CPUFREQ
+       tristate "UltraSPARC-IIe CPU Frequency driver"
+       select CPU_FREQ_TABLE
+       help
+         This adds the CPUFreq driver for UltraSPARC-IIe processors.
+
+         For details, take a look at <file:Documentation/cpu-freq>.
+
+         If in doubt, say N.
+endmenu
+
 menu "SH CPU Frequency scaling"
 depends on SUPERH
 config SH_CPU_FREQ
index 18479b27f75937d6327d106ddb1ba837f4f3f47c..3a9ff23ff9d976d2b51198c1e4b733e59050be68 100644 (file)
@@ -84,4 +84,6 @@ obj-$(CONFIG_ETRAXFS)                 += cris-etraxfs-cpufreq.o
 obj-$(CONFIG_IA64_ACPI_CPUFREQ)                += ia64-acpi-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
+obj-$(CONFIG_SPARC_US3_CPUFREQ)                += sparc-us3-cpufreq.o
 obj-$(CONFIG_UNICORE32)                        += unicore2-cpufreq.o
diff --git a/drivers/cpufreq/sparc-us2e-cpufreq.c b/drivers/cpufreq/sparc-us2e-cpufreq.c
new file mode 100644 (file)
index 0000000..306ae46
--- /dev/null
@@ -0,0 +1,408 @@
+/* us2e_cpufreq.c: UltraSPARC-IIe cpu frequency support
+ *
+ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
+ *
+ * Many thanks to Dominik Brodowski for fixing up the cpufreq
+ * infrastructure in order to make this driver easier to implement.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/threads.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+
+#include <asm/asi.h>
+#include <asm/timer.h>
+
+static struct cpufreq_driver *cpufreq_us2e_driver;
+
+struct us2e_freq_percpu_info {
+       struct cpufreq_frequency_table table[6];
+};
+
+/* Indexed by cpu number. */
+static struct us2e_freq_percpu_info *us2e_freq_table;
+
+#define HBIRD_MEM_CNTL0_ADDR   0x1fe0000f010UL
+#define HBIRD_ESTAR_MODE_ADDR  0x1fe0000f080UL
+
+/* UltraSPARC-IIe has five dividers: 1, 2, 4, 6, and 8.  These are controlled
+ * in the ESTAR mode control register.
+ */
+#define ESTAR_MODE_DIV_1       0x0000000000000000UL
+#define ESTAR_MODE_DIV_2       0x0000000000000001UL
+#define ESTAR_MODE_DIV_4       0x0000000000000003UL
+#define ESTAR_MODE_DIV_6       0x0000000000000002UL
+#define ESTAR_MODE_DIV_8       0x0000000000000004UL
+#define ESTAR_MODE_DIV_MASK    0x0000000000000007UL
+
+#define MCTRL0_SREFRESH_ENAB   0x0000000000010000UL
+#define MCTRL0_REFR_COUNT_MASK 0x0000000000007f00UL
+#define MCTRL0_REFR_COUNT_SHIFT        8
+#define MCTRL0_REFR_INTERVAL   7800
+#define MCTRL0_REFR_CLKS_P_CNT 64
+
+static unsigned long read_hbreg(unsigned long addr)
+{
+       unsigned long ret;
+
+       __asm__ __volatile__("ldxa      [%1] %2, %0"
+                            : "=&r" (ret)
+                            : "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
+       return ret;
+}
+
+static void write_hbreg(unsigned long addr, unsigned long val)
+{
+       __asm__ __volatile__("stxa      %0, [%1] %2\n\t"
+                            "membar    #Sync"
+                            : /* no outputs */
+                            : "r" (val), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E)
+                            : "memory");
+       if (addr == HBIRD_ESTAR_MODE_ADDR) {
+               /* Need to wait 16 clock cycles for the PLL to lock.  */
+               udelay(1);
+       }
+}
+
+static void self_refresh_ctl(int enable)
+{
+       unsigned long mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
+
+       if (enable)
+               mctrl |= MCTRL0_SREFRESH_ENAB;
+       else
+               mctrl &= ~MCTRL0_SREFRESH_ENAB;
+       write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
+       (void) read_hbreg(HBIRD_MEM_CNTL0_ADDR);
+}
+
+static void frob_mem_refresh(int cpu_slowing_down,
+                            unsigned long clock_tick,
+                            unsigned long old_divisor, unsigned long divisor)
+{
+       unsigned long old_refr_count, refr_count, mctrl;
+
+       refr_count  = (clock_tick * MCTRL0_REFR_INTERVAL);
+       refr_count /= (MCTRL0_REFR_CLKS_P_CNT * divisor * 1000000000UL);
+
+       mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
+       old_refr_count = (mctrl & MCTRL0_REFR_COUNT_MASK)
+               >> MCTRL0_REFR_COUNT_SHIFT;
+
+       mctrl &= ~MCTRL0_REFR_COUNT_MASK;
+       mctrl |= refr_count << MCTRL0_REFR_COUNT_SHIFT;
+       write_hbreg(HBIRD_MEM_CNTL0_ADDR, mctrl);
+       mctrl = read_hbreg(HBIRD_MEM_CNTL0_ADDR);
+
+       if (cpu_slowing_down && !(mctrl & MCTRL0_SREFRESH_ENAB)) {
+               unsigned long usecs;
+
+               /* We have to wait for both refresh counts (old
+                * and new) to go to zero.
+                */
+               usecs = (MCTRL0_REFR_CLKS_P_CNT *
+                        (refr_count + old_refr_count) *
+                        1000000UL *
+                        old_divisor) / clock_tick;
+               udelay(usecs + 1UL);
+       }
+}
+
+static void us2e_transition(unsigned long estar, unsigned long new_bits,
+                           unsigned long clock_tick,
+                           unsigned long old_divisor, unsigned long divisor)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       estar &= ~ESTAR_MODE_DIV_MASK;
+
+       /* This is based upon the state transition diagram in the IIe manual.  */
+       if (old_divisor == 2 && divisor == 1) {
+               self_refresh_ctl(0);
+               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
+               frob_mem_refresh(0, clock_tick, old_divisor, divisor);
+       } else if (old_divisor == 1 && divisor == 2) {
+               frob_mem_refresh(1, clock_tick, old_divisor, divisor);
+               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
+               self_refresh_ctl(1);
+       } else if (old_divisor == 1 && divisor > 2) {
+               us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
+                               1, 2);
+               us2e_transition(estar, new_bits, clock_tick,
+                               2, divisor);
+       } else if (old_divisor > 2 && divisor == 1) {
+               us2e_transition(estar, ESTAR_MODE_DIV_2, clock_tick,
+                               old_divisor, 2);
+               us2e_transition(estar, new_bits, clock_tick,
+                               2, divisor);
+       } else if (old_divisor < divisor) {
+               frob_mem_refresh(0, clock_tick, old_divisor, divisor);
+               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
+       } else if (old_divisor > divisor) {
+               write_hbreg(HBIRD_ESTAR_MODE_ADDR, estar | new_bits);
+               frob_mem_refresh(1, clock_tick, old_divisor, divisor);
+       } else {
+               BUG();
+       }
+
+       local_irq_restore(flags);
+}
+
+static unsigned long index_to_estar_mode(unsigned int index)
+{
+       switch (index) {
+       case 0:
+               return ESTAR_MODE_DIV_1;
+
+       case 1:
+               return ESTAR_MODE_DIV_2;
+
+       case 2:
+               return ESTAR_MODE_DIV_4;
+
+       case 3:
+               return ESTAR_MODE_DIV_6;
+
+       case 4:
+               return ESTAR_MODE_DIV_8;
+
+       default:
+               BUG();
+       }
+}
+
+static unsigned long index_to_divisor(unsigned int index)
+{
+       switch (index) {
+       case 0:
+               return 1;
+
+       case 1:
+               return 2;
+
+       case 2:
+               return 4;
+
+       case 3:
+               return 6;
+
+       case 4:
+               return 8;
+
+       default:
+               BUG();
+       }
+}
+
+static unsigned long estar_to_divisor(unsigned long estar)
+{
+       unsigned long ret;
+
+       switch (estar & ESTAR_MODE_DIV_MASK) {
+       case ESTAR_MODE_DIV_1:
+               ret = 1;
+               break;
+       case ESTAR_MODE_DIV_2:
+               ret = 2;
+               break;
+       case ESTAR_MODE_DIV_4:
+               ret = 4;
+               break;
+       case ESTAR_MODE_DIV_6:
+               ret = 6;
+               break;
+       case ESTAR_MODE_DIV_8:
+               ret = 8;
+               break;
+       default:
+               BUG();
+       }
+
+       return ret;
+}
+
+static unsigned int us2e_freq_get(unsigned int cpu)
+{
+       cpumask_t cpus_allowed;
+       unsigned long clock_tick, estar;
+
+       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
+       set_cpus_allowed_ptr(current, cpumask_of(cpu));
+
+       clock_tick = sparc64_get_clock_tick(cpu) / 1000;
+       estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
+
+       set_cpus_allowed_ptr(current, &cpus_allowed);
+
+       return clock_tick / estar_to_divisor(estar);
+}
+
+static void us2e_set_cpu_divider_index(struct cpufreq_policy *policy,
+               unsigned int index)
+{
+       unsigned int cpu = policy->cpu;
+       unsigned long new_bits, new_freq;
+       unsigned long clock_tick, divisor, old_divisor, estar;
+       cpumask_t cpus_allowed;
+       struct cpufreq_freqs freqs;
+
+       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
+       set_cpus_allowed_ptr(current, cpumask_of(cpu));
+
+       new_freq = clock_tick = sparc64_get_clock_tick(cpu) / 1000;
+       new_bits = index_to_estar_mode(index);
+       divisor = index_to_divisor(index);
+       new_freq /= divisor;
+
+       estar = read_hbreg(HBIRD_ESTAR_MODE_ADDR);
+
+       old_divisor = estar_to_divisor(estar);
+
+       freqs.old = clock_tick / old_divisor;
+       freqs.new = new_freq;
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+
+       if (old_divisor != divisor)
+               us2e_transition(estar, new_bits, clock_tick * 1000,
+                               old_divisor, divisor);
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+
+       set_cpus_allowed_ptr(current, &cpus_allowed);
+}
+
+static int us2e_freq_target(struct cpufreq_policy *policy,
+                         unsigned int target_freq,
+                         unsigned int relation)
+{
+       unsigned int new_index = 0;
+
+       if (cpufreq_frequency_table_target(policy,
+                                          &us2e_freq_table[policy->cpu].table[0],
+                                          target_freq, relation, &new_index))
+               return -EINVAL;
+
+       us2e_set_cpu_divider_index(policy, new_index);
+
+       return 0;
+}
+
+static int us2e_freq_verify(struct cpufreq_policy *policy)
+{
+       return cpufreq_frequency_table_verify(policy,
+                                             &us2e_freq_table[policy->cpu].table[0]);
+}
+
+static int __init us2e_freq_cpu_init(struct cpufreq_policy *policy)
+{
+       unsigned int cpu = policy->cpu;
+       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
+       struct cpufreq_frequency_table *table =
+               &us2e_freq_table[cpu].table[0];
+
+       table[0].index = 0;
+       table[0].frequency = clock_tick / 1;
+       table[1].index = 1;
+       table[1].frequency = clock_tick / 2;
+       table[2].index = 2;
+       table[2].frequency = clock_tick / 4;
+       table[2].index = 3;
+       table[2].frequency = clock_tick / 6;
+       table[2].index = 4;
+       table[2].frequency = clock_tick / 8;
+       table[2].index = 5;
+       table[3].frequency = CPUFREQ_TABLE_END;
+
+       policy->cpuinfo.transition_latency = 0;
+       policy->cur = clock_tick;
+
+       return cpufreq_frequency_table_cpuinfo(policy, table);
+}
+
+static int us2e_freq_cpu_exit(struct cpufreq_policy *policy)
+{
+       if (cpufreq_us2e_driver)
+               us2e_set_cpu_divider_index(policy, 0);
+
+       return 0;
+}
+
+static int __init us2e_freq_init(void)
+{
+       unsigned long manuf, impl, ver;
+       int ret;
+
+       if (tlb_type != spitfire)
+               return -ENODEV;
+
+       __asm__("rdpr %%ver, %0" : "=r" (ver));
+       manuf = ((ver >> 48) & 0xffff);
+       impl  = ((ver >> 32) & 0xffff);
+
+       if (manuf == 0x17 && impl == 0x13) {
+               struct cpufreq_driver *driver;
+
+               ret = -ENOMEM;
+               driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
+               if (!driver)
+                       goto err_out;
+
+               us2e_freq_table = kzalloc(
+                       (NR_CPUS * sizeof(struct us2e_freq_percpu_info)),
+                       GFP_KERNEL);
+               if (!us2e_freq_table)
+                       goto err_out;
+
+               driver->init = us2e_freq_cpu_init;
+               driver->verify = us2e_freq_verify;
+               driver->target = us2e_freq_target;
+               driver->get = us2e_freq_get;
+               driver->exit = us2e_freq_cpu_exit;
+               driver->owner = THIS_MODULE,
+               strcpy(driver->name, "UltraSPARC-IIe");
+
+               cpufreq_us2e_driver = driver;
+               ret = cpufreq_register_driver(driver);
+               if (ret)
+                       goto err_out;
+
+               return 0;
+
+err_out:
+               if (driver) {
+                       kfree(driver);
+                       cpufreq_us2e_driver = NULL;
+               }
+               kfree(us2e_freq_table);
+               us2e_freq_table = NULL;
+               return ret;
+       }
+
+       return -ENODEV;
+}
+
+static void __exit us2e_freq_exit(void)
+{
+       if (cpufreq_us2e_driver) {
+               cpufreq_unregister_driver(cpufreq_us2e_driver);
+               kfree(cpufreq_us2e_driver);
+               cpufreq_us2e_driver = NULL;
+               kfree(us2e_freq_table);
+               us2e_freq_table = NULL;
+       }
+}
+
+MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
+MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-IIe");
+MODULE_LICENSE("GPL");
+
+module_init(us2e_freq_init);
+module_exit(us2e_freq_exit);
diff --git a/drivers/cpufreq/sparc-us3-cpufreq.c b/drivers/cpufreq/sparc-us3-cpufreq.c
new file mode 100644 (file)
index 0000000..c71ee14
--- /dev/null
@@ -0,0 +1,269 @@
+/* us3_cpufreq.c: UltraSPARC-III cpu frequency support
+ *
+ * Copyright (C) 2003 David S. Miller (davem@redhat.com)
+ *
+ * Many thanks to Dominik Brodowski for fixing up the cpufreq
+ * infrastructure in order to make this driver easier to implement.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/cpufreq.h>
+#include <linux/threads.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+
+#include <asm/head.h>
+#include <asm/timer.h>
+
+static struct cpufreq_driver *cpufreq_us3_driver;
+
+struct us3_freq_percpu_info {
+       struct cpufreq_frequency_table table[4];
+};
+
+/* Indexed by cpu number. */
+static struct us3_freq_percpu_info *us3_freq_table;
+
+/* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
+ * in the Safari config register.
+ */
+#define SAFARI_CFG_DIV_1       0x0000000000000000UL
+#define SAFARI_CFG_DIV_2       0x0000000040000000UL
+#define SAFARI_CFG_DIV_32      0x0000000080000000UL
+#define SAFARI_CFG_DIV_MASK    0x00000000C0000000UL
+
+static unsigned long read_safari_cfg(void)
+{
+       unsigned long ret;
+
+       __asm__ __volatile__("ldxa      [%%g0] %1, %0"
+                            : "=&r" (ret)
+                            : "i" (ASI_SAFARI_CONFIG));
+       return ret;
+}
+
+static void write_safari_cfg(unsigned long val)
+{
+       __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
+                            "membar    #Sync"
+                            : /* no outputs */
+                            : "r" (val), "i" (ASI_SAFARI_CONFIG)
+                            : "memory");
+}
+
+static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
+{
+       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
+       unsigned long ret;
+
+       switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
+       case SAFARI_CFG_DIV_1:
+               ret = clock_tick / 1;
+               break;
+       case SAFARI_CFG_DIV_2:
+               ret = clock_tick / 2;
+               break;
+       case SAFARI_CFG_DIV_32:
+               ret = clock_tick / 32;
+               break;
+       default:
+               BUG();
+       }
+
+       return ret;
+}
+
+static unsigned int us3_freq_get(unsigned int cpu)
+{
+       cpumask_t cpus_allowed;
+       unsigned long reg;
+       unsigned int ret;
+
+       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
+       set_cpus_allowed_ptr(current, cpumask_of(cpu));
+
+       reg = read_safari_cfg();
+       ret = get_current_freq(cpu, reg);
+
+       set_cpus_allowed_ptr(current, &cpus_allowed);
+
+       return ret;
+}
+
+static void us3_set_cpu_divider_index(struct cpufreq_policy *policy,
+               unsigned int index)
+{
+       unsigned int cpu = policy->cpu;
+       unsigned long new_bits, new_freq, reg;
+       cpumask_t cpus_allowed;
+       struct cpufreq_freqs freqs;
+
+       cpumask_copy(&cpus_allowed, tsk_cpus_allowed(current));
+       set_cpus_allowed_ptr(current, cpumask_of(cpu));
+
+       new_freq = sparc64_get_clock_tick(cpu) / 1000;
+       switch (index) {
+       case 0:
+               new_bits = SAFARI_CFG_DIV_1;
+               new_freq /= 1;
+               break;
+       case 1:
+               new_bits = SAFARI_CFG_DIV_2;
+               new_freq /= 2;
+               break;
+       case 2:
+               new_bits = SAFARI_CFG_DIV_32;
+               new_freq /= 32;
+               break;
+
+       default:
+               BUG();
+       }
+
+       reg = read_safari_cfg();
+
+       freqs.old = get_current_freq(cpu, reg);
+       freqs.new = new_freq;
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_PRECHANGE);
+
+       reg &= ~SAFARI_CFG_DIV_MASK;
+       reg |= new_bits;
+       write_safari_cfg(reg);
+
+       cpufreq_notify_transition(policy, &freqs, CPUFREQ_POSTCHANGE);
+
+       set_cpus_allowed_ptr(current, &cpus_allowed);
+}
+
+static int us3_freq_target(struct cpufreq_policy *policy,
+                         unsigned int target_freq,
+                         unsigned int relation)
+{
+       unsigned int new_index = 0;
+
+       if (cpufreq_frequency_table_target(policy,
+                                          &us3_freq_table[policy->cpu].table[0],
+                                          target_freq,
+                                          relation,
+                                          &new_index))
+               return -EINVAL;
+
+       us3_set_cpu_divider_index(policy, new_index);
+
+       return 0;
+}
+
+static int us3_freq_verify(struct cpufreq_policy *policy)
+{
+       return cpufreq_frequency_table_verify(policy,
+                                             &us3_freq_table[policy->cpu].table[0]);
+}
+
+static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
+{
+       unsigned int cpu = policy->cpu;
+       unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
+       struct cpufreq_frequency_table *table =
+               &us3_freq_table[cpu].table[0];
+
+       table[0].index = 0;
+       table[0].frequency = clock_tick / 1;
+       table[1].index = 1;
+       table[1].frequency = clock_tick / 2;
+       table[2].index = 2;
+       table[2].frequency = clock_tick / 32;
+       table[3].index = 0;
+       table[3].frequency = CPUFREQ_TABLE_END;
+
+       policy->cpuinfo.transition_latency = 0;
+       policy->cur = clock_tick;
+
+       return cpufreq_frequency_table_cpuinfo(policy, table);
+}
+
+static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
+{
+       if (cpufreq_us3_driver)
+               us3_set_cpu_divider_index(policy, 0);
+
+       return 0;
+}
+
+static int __init us3_freq_init(void)
+{
+       unsigned long manuf, impl, ver;
+       int ret;
+
+       if (tlb_type != cheetah && tlb_type != cheetah_plus)
+               return -ENODEV;
+
+       __asm__("rdpr %%ver, %0" : "=r" (ver));
+       manuf = ((ver >> 48) & 0xffff);
+       impl  = ((ver >> 32) & 0xffff);
+
+       if (manuf == CHEETAH_MANUF &&
+           (impl == CHEETAH_IMPL ||
+            impl == CHEETAH_PLUS_IMPL ||
+            impl == JAGUAR_IMPL ||
+            impl == PANTHER_IMPL)) {
+               struct cpufreq_driver *driver;
+
+               ret = -ENOMEM;
+               driver = kzalloc(sizeof(struct cpufreq_driver), GFP_KERNEL);
+               if (!driver)
+                       goto err_out;
+
+               us3_freq_table = kzalloc(
+                       (NR_CPUS * sizeof(struct us3_freq_percpu_info)),
+                       GFP_KERNEL);
+               if (!us3_freq_table)
+                       goto err_out;
+
+               driver->init = us3_freq_cpu_init;
+               driver->verify = us3_freq_verify;
+               driver->target = us3_freq_target;
+               driver->get = us3_freq_get;
+               driver->exit = us3_freq_cpu_exit;
+               driver->owner = THIS_MODULE,
+               strcpy(driver->name, "UltraSPARC-III");
+
+               cpufreq_us3_driver = driver;
+               ret = cpufreq_register_driver(driver);
+               if (ret)
+                       goto err_out;
+
+               return 0;
+
+err_out:
+               if (driver) {
+                       kfree(driver);
+                       cpufreq_us3_driver = NULL;
+               }
+               kfree(us3_freq_table);
+               us3_freq_table = NULL;
+               return ret;
+       }
+
+       return -ENODEV;
+}
+
+static void __exit us3_freq_exit(void)
+{
+       if (cpufreq_us3_driver) {
+               cpufreq_unregister_driver(cpufreq_us3_driver);
+               kfree(cpufreq_us3_driver);
+               cpufreq_us3_driver = NULL;
+               kfree(us3_freq_table);
+               us3_freq_table = NULL;
+       }
+}
+
+MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
+MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
+MODULE_LICENSE("GPL");
+
+module_init(us3_freq_init);
+module_exit(us3_freq_exit);