Commit d626a13c authored by Mao Minkai's avatar Mao Minkai Committed by guzitao
Browse files

drivers: cpufreq: add sw64 support

Sunway inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I8YQY2



--------------------------------

Add cpufreq drivers for SW64.

Signed-off-by: default avatarMao Minkai <maominkai@wxiat.com>
Reviewed-by: default avatarHe Sheng <hesheng@wxiat.com>
Signed-off-by: default avatarGu Zitao <guzitao@wxiat.com>
parent bf224e69
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -314,6 +314,29 @@ config SH_CPU_FREQ
	  If unsure, say N.
endif

if SW64
config SW64_CPUFREQ
	bool "SW64 CPU Frequency interface"
	depends on UNCORE_XUELANG
	default y
	help
	  This adds the CPUFreq driver for SW64 processor which supports
	  software configurable cpu frequency.

	  For details, take a look at <file:Documentation/cpu-freq>.

	  If unsure, say N.

config SW64_CPUFREQ_DEBUGFS
	bool "SW64 CPU Frequency debugfs interface"
	depends on SW64_CPUFREQ && DEBUG_FS
	default y
	help
	  Turns on the DebugFS interface for CPU Frequency.

	  If you don't know what to do here, say N.
endif

config QORIQ_CPUFREQ
	tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
	depends on OF && COMMON_CLK
+2 −0
Original line number Diff line number Diff line
@@ -108,3 +108,5 @@ obj-$(CONFIG_LOONGSON3_ACPI_CPUFREQ) += loongson3-acpi-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_SW64_CPUFREQ)		+= sw64_cpufreq.o
obj-$(CONFIG_SW64_CPUFREQ_DEBUGFS)	+= sw64_cpufreq_debugfs.o
+175 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/*
 *  linux/arch/sw/kernel/setup.c
 *
 *  Copyright (C) 1995  Linus Torvalds
 */

/*
 * Cpufreq driver for the sw64 processors
 *
 */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/cpufreq.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/sched.h>	/* set_cpus_allowed() */
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>

#include <asm/hw_init.h>
#include <asm/cpufreq.h>
#include <asm/sw64io.h>

static uint nowait;

static struct clk *cpuclk;


static int sw64_cpu_freq_notifier(struct notifier_block *nb,
					unsigned long val, void *data);

static struct notifier_block sw64_cpufreq_notifier_block = {
	.notifier_call = sw64_cpu_freq_notifier
};

static int sw64_cpu_freq_notifier(struct notifier_block *nb,
					unsigned long val, void *data)
{
	struct cpufreq_freqs *freqs = (struct cpufreq_freqs *)data;
	unsigned long cpu = freqs->policy->cpu;

	if (val == CPUFREQ_POSTCHANGE)
		sw64_update_clockevents(cpu, freqs->new * 1000);

	return 0;
}

static unsigned int sw64_cpufreq_get(unsigned int cpu)
{
	struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu);

	if (!policy || IS_ERR(policy->clk)) {
		pr_err("%s: No %s associated to cpu: %d\n",
			__func__, policy ? "clk" : "policy", cpu);
		return 0;
	}

	return __sw64_cpufreq_get(policy);
}

/*
 * Here we notify other drivers of the proposed change and the final change.
 */
static int sw64_cpufreq_target(struct cpufreq_policy *policy,
				     unsigned int index)
{
	unsigned int cpu = policy->cpu;

	if (!cpu_online(cpu))
		return -ENODEV;

	/* setting the cpu frequency */
	sw64_set_rate(index);
	update_cpu_freq(freq_table[index].frequency);

	return 0;
}

static int sw64_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
	cpuclk = sw64_clk_get(NULL, "cpu_clk");
	if (IS_ERR(cpuclk)) {
		pr_err("couldn't get CPU clk\n");
		return PTR_ERR(cpuclk);
	}

	policy->clk = cpuclk;

	cpufreq_generic_init(policy, freq_table, 0);

	return 0;
}

static int sw64_cpufreq_verify(struct cpufreq_policy_data *policy)
{
	return cpufreq_frequency_table_verify(policy, freq_table);
}

static int sw64_cpufreq_exit(struct cpufreq_policy *policy)
{
	return 0;
}

static struct freq_attr *sw64_table_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs, NULL,
};

static struct cpufreq_driver sw64_cpufreq_driver = {
	.name = "sw64",
	.init = sw64_cpufreq_cpu_init,
	.verify = sw64_cpufreq_verify,
	.target_index = sw64_cpufreq_target,
	.get = sw64_cpufreq_get,
	.exit = sw64_cpufreq_exit,
	.attr = sw64_table_attr,
};

static const struct platform_device_id platform_device_ids[] = {
	{
		.name = "sw64_cpufreq",
	},
	{}
};

MODULE_DEVICE_TABLE(platform, platform_device_ids);

static struct platform_driver platform_driver = {
	.driver = {
		.name = "sw64_cpufreq",
	},
	.id_table = platform_device_ids,
};


static int __init cpufreq_init(void)
{
	int ret;

	if (is_in_guest()) {
		pr_warn("Now sw_64 CPUFreq does not support virtual machines\n");
		return -ENODEV;
	}

	/* Register platform stuff */
	ret = platform_driver_register(&platform_driver);
	if (ret)
		return ret;

	pr_info("SW-64 CPU frequency driver\n");

	cpufreq_register_notifier(&sw64_cpufreq_notifier_block,
				  CPUFREQ_TRANSITION_NOTIFIER);

	return cpufreq_register_driver(&sw64_cpufreq_driver);
}

static void __exit cpufreq_exit(void)
{
	cpufreq_unregister_driver(&sw64_cpufreq_driver);
	cpufreq_unregister_notifier(&sw64_cpufreq_notifier_block,
				    CPUFREQ_TRANSITION_NOTIFIER);

	platform_driver_unregister(&platform_driver);
}

module_init(cpufreq_init);
module_exit(cpufreq_exit);

module_param(nowait, uint, 0644);
MODULE_PARM_DESC(nowait, "Disable SW-64 specific wait");

MODULE_DESCRIPTION("cpufreq driver for sw64");
MODULE_LICENSE("GPL");
+101 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0

#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/uaccess.h>

#include <asm/sw64io.h>
#include <asm/debug.h>
#include <asm/cpufreq.h>

static int cpufreq_show(struct seq_file *m, void *v)
{
	int i;
	u64 val;
	int freq;

	val = sw64_io_read(0, CLK_CTL);
	val = val >> CORE_PLL2_CFG_SHIFT;

	for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
		if (freq_table[i].frequency != CPUFREQ_ENTRY_INVALID)
			freq = freq_table[i].frequency;
		else
			freq = freq_table[i].driver_data;

		if (val == i)
			seq_printf(m, "[%d] ", freq);
		else
			seq_printf(m, "%d ", freq);
	}
	seq_puts(m, "\n");

	return 0;
}

static int cpufreq_open(struct inode *inode, struct file *file)
{
	return single_open(file, cpufreq_show, NULL);
}

static ssize_t cpufreq_set(struct file *file, const char __user *user_buf,
			size_t len, loff_t *ppos)
{
	char buf[5];
	size_t size;
	int cf, i, err, index, freq;

	size = min(sizeof(buf) - 1, len);
	if (copy_from_user(buf, user_buf, size))
		return -EFAULT;
	buf[size] = '\0';

	err = kstrtoint(buf, 10, &cf);
	if (err)
		return err;

	index = -1;
	for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
		if (freq_table[i].frequency != CPUFREQ_ENTRY_INVALID)
			freq = freq_table[i].frequency;
		else
			freq = freq_table[i].driver_data;

		if (cf == freq) {
			index = i;
			break;
		}
	}

	if (index < 0)
		return -EINVAL;

	sw64_set_rate(index);
	update_cpu_freq(freq);
	return len;
}

static const struct file_operations set_cpufreq_fops = {
	.open		= cpufreq_open,
	.read		= seq_read,
	.write		= cpufreq_set,
	.llseek         = seq_lseek,
	.release        = single_release,
};

static int __init cpufreq_debugfs_init(void)
{
	struct dentry *cpufreq_entry;

	if (!sw64_debugfs_dir)
		return -ENODEV;

	cpufreq_entry = debugfs_create_file("cpufreq", 0600,
				       sw64_debugfs_dir, NULL,
				       &set_cpufreq_fops);
	if (!cpufreq_entry)
		return -ENOMEM;

	return 0;
}
late_initcall(cpufreq_debugfs_init);