cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

loongson2_cpufreq.c (4306B)


      1/*
      2 * Cpufreq driver for the loongson-2 processors
      3 *
      4 * The 2E revision of loongson processor not support this feature.
      5 *
      6 * Copyright (C) 2006 - 2008 Lemote Inc. & Institute of Computing Technology
      7 * Author: Yanhua, yanh@lemote.com
      8 *
      9 * This file is subject to the terms and conditions of the GNU General Public
     10 * License.  See the file "COPYING" in the main directory of this archive
     11 * for more details.
     12 */
     13
     14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
     15
     16#include <linux/cpufreq.h>
     17#include <linux/module.h>
     18#include <linux/err.h>
     19#include <linux/delay.h>
     20#include <linux/platform_device.h>
     21
     22#include <asm/idle.h>
     23
     24#include <asm/mach-loongson2ef/loongson.h>
     25
     26static uint nowait;
     27
     28static void (*saved_cpu_wait) (void);
     29
     30static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
     31					unsigned long val, void *data);
     32
     33static struct notifier_block loongson2_cpufreq_notifier_block = {
     34	.notifier_call = loongson2_cpu_freq_notifier
     35};
     36
     37static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
     38					unsigned long val, void *data)
     39{
     40	if (val == CPUFREQ_POSTCHANGE)
     41		current_cpu_data.udelay_val = loops_per_jiffy;
     42
     43	return 0;
     44}
     45
     46/*
     47 * Here we notify other drivers of the proposed change and the final change.
     48 */
     49static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
     50				     unsigned int index)
     51{
     52	unsigned int freq;
     53
     54	freq =
     55	    ((cpu_clock_freq / 1000) *
     56	     loongson2_clockmod_table[index].driver_data) / 8;
     57
     58	/* setting the cpu frequency */
     59	loongson2_cpu_set_rate(freq);
     60
     61	return 0;
     62}
     63
     64static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
     65{
     66	int i;
     67	unsigned long rate;
     68	int ret;
     69
     70	rate = cpu_clock_freq / 1000;
     71	if (!rate)
     72		return -EINVAL;
     73
     74	/* clock table init */
     75	for (i = 2;
     76	     (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END);
     77	     i++)
     78		loongson2_clockmod_table[i].frequency = (rate * i) / 8;
     79
     80	ret = loongson2_cpu_set_rate(rate);
     81	if (ret)
     82		return ret;
     83
     84	cpufreq_generic_init(policy, &loongson2_clockmod_table[0], 0);
     85	return 0;
     86}
     87
     88static int loongson2_cpufreq_exit(struct cpufreq_policy *policy)
     89{
     90	return 0;
     91}
     92
     93static struct cpufreq_driver loongson2_cpufreq_driver = {
     94	.name = "loongson2",
     95	.init = loongson2_cpufreq_cpu_init,
     96	.verify = cpufreq_generic_frequency_table_verify,
     97	.target_index = loongson2_cpufreq_target,
     98	.get = cpufreq_generic_get,
     99	.exit = loongson2_cpufreq_exit,
    100	.attr = cpufreq_generic_attr,
    101};
    102
    103static const struct platform_device_id platform_device_ids[] = {
    104	{
    105		.name = "loongson2_cpufreq",
    106	},
    107	{}
    108};
    109
    110MODULE_DEVICE_TABLE(platform, platform_device_ids);
    111
    112static struct platform_driver platform_driver = {
    113	.driver = {
    114		.name = "loongson2_cpufreq",
    115	},
    116	.id_table = platform_device_ids,
    117};
    118
    119/*
    120 * This is the simple version of Loongson-2 wait, Maybe we need do this in
    121 * interrupt disabled context.
    122 */
    123
    124static DEFINE_SPINLOCK(loongson2_wait_lock);
    125
    126static void loongson2_cpu_wait(void)
    127{
    128	unsigned long flags;
    129	u32 cpu_freq;
    130
    131	spin_lock_irqsave(&loongson2_wait_lock, flags);
    132	cpu_freq = readl(LOONGSON_CHIPCFG);
    133	/* Put CPU into wait mode */
    134	writel(readl(LOONGSON_CHIPCFG) & ~0x7, LOONGSON_CHIPCFG);
    135	/* Restore CPU state */
    136	writel(cpu_freq, LOONGSON_CHIPCFG);
    137	spin_unlock_irqrestore(&loongson2_wait_lock, flags);
    138	local_irq_enable();
    139}
    140
    141static int __init cpufreq_init(void)
    142{
    143	int ret;
    144
    145	/* Register platform stuff */
    146	ret = platform_driver_register(&platform_driver);
    147	if (ret)
    148		return ret;
    149
    150	pr_info("Loongson-2F CPU frequency driver\n");
    151
    152	cpufreq_register_notifier(&loongson2_cpufreq_notifier_block,
    153				  CPUFREQ_TRANSITION_NOTIFIER);
    154
    155	ret = cpufreq_register_driver(&loongson2_cpufreq_driver);
    156
    157	if (!ret && !nowait) {
    158		saved_cpu_wait = cpu_wait;
    159		cpu_wait = loongson2_cpu_wait;
    160	}
    161
    162	return ret;
    163}
    164
    165static void __exit cpufreq_exit(void)
    166{
    167	if (!nowait && saved_cpu_wait)
    168		cpu_wait = saved_cpu_wait;
    169	cpufreq_unregister_driver(&loongson2_cpufreq_driver);
    170	cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block,
    171				    CPUFREQ_TRANSITION_NOTIFIER);
    172
    173	platform_driver_unregister(&platform_driver);
    174}
    175
    176module_init(cpufreq_init);
    177module_exit(cpufreq_exit);
    178
    179module_param(nowait, uint, 0644);
    180MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait");
    181
    182MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
    183MODULE_DESCRIPTION("cpufreq driver for Loongson2F");
    184MODULE_LICENSE("GPL");