linux/drivers/cpufreq/sparc-us3-cpufreq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* us3_cpufreq.c: UltraSPARC-III cpu frequency support
   3 *
   4 * Copyright (C) 2003 David S. Miller (davem@redhat.com)
   5 *
   6 * Many thanks to Dominik Brodowski for fixing up the cpufreq
   7 * infrastructure in order to make this driver easier to implement.
   8 */
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/sched.h>
  13#include <linux/smp.h>
  14#include <linux/cpufreq.h>
  15#include <linux/threads.h>
  16#include <linux/slab.h>
  17#include <linux/init.h>
  18
  19#include <asm/head.h>
  20#include <asm/timer.h>
  21
  22static struct cpufreq_driver *cpufreq_us3_driver;
  23
  24struct us3_freq_percpu_info {
  25        struct cpufreq_frequency_table table[4];
  26};
  27
  28/* Indexed by cpu number. */
  29static struct us3_freq_percpu_info *us3_freq_table;
  30
  31/* UltraSPARC-III has three dividers: 1, 2, and 32.  These are controlled
  32 * in the Safari config register.
  33 */
  34#define SAFARI_CFG_DIV_1        0x0000000000000000UL
  35#define SAFARI_CFG_DIV_2        0x0000000040000000UL
  36#define SAFARI_CFG_DIV_32       0x0000000080000000UL
  37#define SAFARI_CFG_DIV_MASK     0x00000000C0000000UL
  38
  39static void read_safari_cfg(void *arg)
  40{
  41        unsigned long ret, *val = arg;
  42
  43        __asm__ __volatile__("ldxa      [%%g0] %1, %0"
  44                             : "=&r" (ret)
  45                             : "i" (ASI_SAFARI_CONFIG));
  46        *val = ret;
  47}
  48
  49static void update_safari_cfg(void *arg)
  50{
  51        unsigned long reg, *new_bits = arg;
  52
  53        read_safari_cfg(&reg);
  54        reg &= ~SAFARI_CFG_DIV_MASK;
  55        reg |= *new_bits;
  56
  57        __asm__ __volatile__("stxa      %0, [%%g0] %1\n\t"
  58                             "membar    #Sync"
  59                             : /* no outputs */
  60                             : "r" (reg), "i" (ASI_SAFARI_CONFIG)
  61                             : "memory");
  62}
  63
  64static unsigned long get_current_freq(unsigned int cpu, unsigned long safari_cfg)
  65{
  66        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
  67        unsigned long ret;
  68
  69        switch (safari_cfg & SAFARI_CFG_DIV_MASK) {
  70        case SAFARI_CFG_DIV_1:
  71                ret = clock_tick / 1;
  72                break;
  73        case SAFARI_CFG_DIV_2:
  74                ret = clock_tick / 2;
  75                break;
  76        case SAFARI_CFG_DIV_32:
  77                ret = clock_tick / 32;
  78                break;
  79        default:
  80                BUG();
  81        }
  82
  83        return ret;
  84}
  85
  86static unsigned int us3_freq_get(unsigned int cpu)
  87{
  88        unsigned long reg;
  89
  90        if (smp_call_function_single(cpu, read_safari_cfg, &reg, 1))
  91                return 0;
  92        return get_current_freq(cpu, reg);
  93}
  94
  95static int us3_freq_target(struct cpufreq_policy *policy, unsigned int index)
  96{
  97        unsigned int cpu = policy->cpu;
  98        unsigned long new_bits, new_freq;
  99
 100        new_freq = sparc64_get_clock_tick(cpu) / 1000;
 101        switch (index) {
 102        case 0:
 103                new_bits = SAFARI_CFG_DIV_1;
 104                new_freq /= 1;
 105                break;
 106        case 1:
 107                new_bits = SAFARI_CFG_DIV_2;
 108                new_freq /= 2;
 109                break;
 110        case 2:
 111                new_bits = SAFARI_CFG_DIV_32;
 112                new_freq /= 32;
 113                break;
 114
 115        default:
 116                BUG();
 117        }
 118
 119        return smp_call_function_single(cpu, update_safari_cfg, &new_bits, 1);
 120}
 121
 122static int __init us3_freq_cpu_init(struct cpufreq_policy *policy)
 123{
 124        unsigned int cpu = policy->cpu;
 125        unsigned long clock_tick = sparc64_get_clock_tick(cpu) / 1000;
 126        struct cpufreq_frequency_table *table =
 127                &us3_freq_table[cpu].table[0];
 128
 129        table[0].driver_data = 0;
 130        table[0].frequency = clock_tick / 1;
 131        table[1].driver_data = 1;
 132        table[1].frequency = clock_tick / 2;
 133        table[2].driver_data = 2;
 134        table[2].frequency = clock_tick / 32;
 135        table[3].driver_data = 0;
 136        table[3].frequency = CPUFREQ_TABLE_END;
 137
 138        policy->cpuinfo.transition_latency = 0;
 139        policy->cur = clock_tick;
 140        policy->freq_table = table;
 141
 142        return 0;
 143}
 144
 145static int us3_freq_cpu_exit(struct cpufreq_policy *policy)
 146{
 147        if (cpufreq_us3_driver)
 148                us3_freq_target(policy, 0);
 149
 150        return 0;
 151}
 152
 153static int __init us3_freq_init(void)
 154{
 155        unsigned long manuf, impl, ver;
 156        int ret;
 157
 158        if (tlb_type != cheetah && tlb_type != cheetah_plus)
 159                return -ENODEV;
 160
 161        __asm__("rdpr %%ver, %0" : "=r" (ver));
 162        manuf = ((ver >> 48) & 0xffff);
 163        impl  = ((ver >> 32) & 0xffff);
 164
 165        if (manuf == CHEETAH_MANUF &&
 166            (impl == CHEETAH_IMPL ||
 167             impl == CHEETAH_PLUS_IMPL ||
 168             impl == JAGUAR_IMPL ||
 169             impl == PANTHER_IMPL)) {
 170                struct cpufreq_driver *driver;
 171
 172                ret = -ENOMEM;
 173                driver = kzalloc(sizeof(*driver), GFP_KERNEL);
 174                if (!driver)
 175                        goto err_out;
 176
 177                us3_freq_table = kzalloc((NR_CPUS * sizeof(*us3_freq_table)),
 178                        GFP_KERNEL);
 179                if (!us3_freq_table)
 180                        goto err_out;
 181
 182                driver->init = us3_freq_cpu_init;
 183                driver->verify = cpufreq_generic_frequency_table_verify;
 184                driver->target_index = us3_freq_target;
 185                driver->get = us3_freq_get;
 186                driver->exit = us3_freq_cpu_exit;
 187                strcpy(driver->name, "UltraSPARC-III");
 188
 189                cpufreq_us3_driver = driver;
 190                ret = cpufreq_register_driver(driver);
 191                if (ret)
 192                        goto err_out;
 193
 194                return 0;
 195
 196err_out:
 197                if (driver) {
 198                        kfree(driver);
 199                        cpufreq_us3_driver = NULL;
 200                }
 201                kfree(us3_freq_table);
 202                us3_freq_table = NULL;
 203                return ret;
 204        }
 205
 206        return -ENODEV;
 207}
 208
 209static void __exit us3_freq_exit(void)
 210{
 211        if (cpufreq_us3_driver) {
 212                cpufreq_unregister_driver(cpufreq_us3_driver);
 213                kfree(cpufreq_us3_driver);
 214                cpufreq_us3_driver = NULL;
 215                kfree(us3_freq_table);
 216                us3_freq_table = NULL;
 217        }
 218}
 219
 220MODULE_AUTHOR("David S. Miller <davem@redhat.com>");
 221MODULE_DESCRIPTION("cpufreq driver for UltraSPARC-III");
 222MODULE_LICENSE("GPL");
 223
 224module_init(us3_freq_init);
 225module_exit(us3_freq_exit);
 226