brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 6b5e679 Raw
396 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * CPUFreq driver for the Loongson-3 processors.4 *5 * All revisions of Loongson-3 processor support cpu_has_scalefreq feature.6 *7 * Author: Huacai Chen <chenhuacai@loongson.cn>8 * Copyright (C) 2024 Loongson Technology Corporation Limited9 */10#include <linux/cpufreq.h>11#include <linux/delay.h>12#include <linux/module.h>13#include <linux/platform_device.h>14#include <linux/units.h>15 16#include <asm/idle.h>17#include <asm/loongarch.h>18#include <asm/loongson.h>19 20/* Message */21union smc_message {22	u32 value;23	struct {24		u32 id		: 4;25		u32 info	: 4;26		u32 val		: 16;27		u32 cmd		: 6;28		u32 extra	: 1;29		u32 complete	: 1;30	};31};32 33/* Command return values */34#define CMD_OK				0 /* No error */35#define CMD_ERROR			1 /* Regular error */36#define CMD_NOCMD			2 /* Command does not support */37#define CMD_INVAL			3 /* Invalid Parameter */38 39/* Version commands */40/*41 * CMD_GET_VERSION - Get interface version42 * Input: none43 * Output: version44 */45#define CMD_GET_VERSION			0x146 47/* Feature commands */48/*49 * CMD_GET_FEATURE - Get feature state50 * Input: feature ID51 * Output: feature flag52 */53#define CMD_GET_FEATURE			0x254 55/*56 * CMD_SET_FEATURE - Set feature state57 * Input: feature ID, feature flag58 * output: none59 */60#define CMD_SET_FEATURE			0x361 62/* Feature IDs */63#define FEATURE_SENSOR			064#define FEATURE_FAN			165#define FEATURE_DVFS			266 67/* Sensor feature flags */68#define FEATURE_SENSOR_ENABLE		BIT(0)69#define FEATURE_SENSOR_SAMPLE		BIT(1)70 71/* Fan feature flags */72#define FEATURE_FAN_ENABLE		BIT(0)73#define FEATURE_FAN_AUTO		BIT(1)74 75/* DVFS feature flags */76#define FEATURE_DVFS_ENABLE		BIT(0)77#define FEATURE_DVFS_BOOST		BIT(1)78#define FEATURE_DVFS_AUTO		BIT(2)79#define FEATURE_DVFS_SINGLE_BOOST	BIT(3)80 81/* Sensor commands */82/*83 * CMD_GET_SENSOR_NUM - Get number of sensors84 * Input: none85 * Output: number86 */87#define CMD_GET_SENSOR_NUM		0x488 89/*90 * CMD_GET_SENSOR_STATUS - Get sensor status91 * Input: sensor ID, type92 * Output: sensor status93 */94#define CMD_GET_SENSOR_STATUS		0x595 96/* Sensor types */97#define SENSOR_INFO_TYPE		098#define SENSOR_INFO_TYPE_TEMP		199 100/* Fan commands */101/*102 * CMD_GET_FAN_NUM - Get number of fans103 * Input: none104 * Output: number105 */106#define CMD_GET_FAN_NUM			0x6107 108/*109 * CMD_GET_FAN_INFO - Get fan status110 * Input: fan ID, type111 * Output: fan info112 */113#define CMD_GET_FAN_INFO		0x7114 115/*116 * CMD_SET_FAN_INFO - Set fan status117 * Input: fan ID, type, value118 * Output: none119 */120#define CMD_SET_FAN_INFO		0x8121 122/* Fan types */123#define FAN_INFO_TYPE_LEVEL		0124 125/* DVFS commands */126/*127 * CMD_GET_FREQ_LEVEL_NUM - Get number of freq levels128 * Input: CPU ID129 * Output: number130 */131#define CMD_GET_FREQ_LEVEL_NUM		0x9132 133/*134 * CMD_GET_FREQ_BOOST_LEVEL - Get the first boost level135 * Input: CPU ID136 * Output: number137 */138#define CMD_GET_FREQ_BOOST_LEVEL	0x10139 140/*141 * CMD_GET_FREQ_LEVEL_INFO - Get freq level info142 * Input: CPU ID, level ID143 * Output: level info144 */145#define CMD_GET_FREQ_LEVEL_INFO		0x11146 147/*148 * CMD_GET_FREQ_INFO - Get freq info149 * Input: CPU ID, type150 * Output: freq info151 */152#define CMD_GET_FREQ_INFO		0x12153 154/*155 * CMD_SET_FREQ_INFO - Set freq info156 * Input: CPU ID, type, value157 * Output: none158 */159#define CMD_SET_FREQ_INFO		0x13160 161/* Freq types */162#define FREQ_INFO_TYPE_FREQ		0163#define FREQ_INFO_TYPE_LEVEL		1164 165#define FREQ_MAX_LEVEL			16166 167struct loongson3_freq_data {168	unsigned int def_freq_level;169	struct cpufreq_frequency_table table[];170};171 172static struct mutex cpufreq_mutex[MAX_PACKAGES];173static struct cpufreq_driver loongson3_cpufreq_driver;174static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);175 176static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)177{178	int retries;179	unsigned int cpu = raw_smp_processor_id();180	unsigned int package = cpu_data[cpu].package;181	union smc_message msg, last;182 183	mutex_lock(&cpufreq_mutex[package]);184 185	last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);186	if (!last.complete) {187		mutex_unlock(&cpufreq_mutex[package]);188		return -EPERM;189	}190 191	msg.id		= id;192	msg.info	= info;193	msg.cmd		= cmd;194	msg.val		= val;195	msg.extra	= extra;196	msg.complete	= 0;197 198	iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);199	iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,200		      LOONGARCH_IOCSR_MISC_FUNC);201 202	for (retries = 0; retries < 10000; retries++) {203		msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);204		if (msg.complete)205			break;206 207		usleep_range(8, 12);208	}209 210	if (!msg.complete || msg.cmd != CMD_OK) {211		mutex_unlock(&cpufreq_mutex[package]);212		return -EPERM;213	}214 215	mutex_unlock(&cpufreq_mutex[package]);216 217	return msg.val;218}219 220static unsigned int loongson3_cpufreq_get(unsigned int cpu)221{222	int ret;223 224	ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);225 226	return ret * KILO;227}228 229static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)230{231	int ret;232 233	ret = do_service_request(cpu_data[policy->cpu].core,234				 FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);235 236	return (ret >= 0) ? 0 : ret;237}238 239static int configure_freq_table(int cpu)240{241	int i, ret, boost_level, max_level, freq_level;242	struct platform_device *pdev = cpufreq_get_driver_data();243	struct loongson3_freq_data *data;244 245	if (per_cpu(freq_data, cpu))246		return 0;247 248	ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);249	if (ret < 0)250		return ret;251	max_level = ret;252 253	ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);254	if (ret < 0)255		return ret;256	boost_level = ret;257 258	freq_level = min(max_level, FREQ_MAX_LEVEL);259	data = devm_kzalloc(&pdev->dev, struct_size(data, table, freq_level + 1), GFP_KERNEL);260	if (!data)261		return -ENOMEM;262 263	data->def_freq_level = boost_level - 1;264 265	for (i = 0; i < freq_level; i++) {266		ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);267		if (ret < 0) {268			devm_kfree(&pdev->dev, data);269			return ret;270		}271 272		data->table[i].frequency = ret * KILO;273		data->table[i].flags = (i >= boost_level) ? CPUFREQ_BOOST_FREQ : 0;274	}275 276	data->table[freq_level].flags = 0;277	data->table[freq_level].frequency = CPUFREQ_TABLE_END;278 279	per_cpu(freq_data, cpu) = data;280 281	return 0;282}283 284static int loongson3_cpufreq_cpu_init(struct cpufreq_policy *policy)285{286	int i, ret, cpu = policy->cpu;287 288	ret = configure_freq_table(cpu);289	if (ret < 0)290		return ret;291 292	policy->cpuinfo.transition_latency = 10000;293	policy->freq_table = per_cpu(freq_data, cpu)->table;294	policy->suspend_freq = policy->freq_table[per_cpu(freq_data, cpu)->def_freq_level].frequency;295	cpumask_copy(policy->cpus, topology_sibling_cpumask(cpu));296 297	for_each_cpu(i, policy->cpus) {298		if (i != cpu)299			per_cpu(freq_data, i) = per_cpu(freq_data, cpu);300	}301 302	if (policy_has_boost_freq(policy)) {303		ret = cpufreq_enable_boost_support();304		if (ret < 0) {305			pr_warn("cpufreq: Failed to enable boost: %d\n", ret);306			return ret;307		}308		loongson3_cpufreq_driver.boost_enabled = true;309	}310 311	return 0;312}313 314static void loongson3_cpufreq_cpu_exit(struct cpufreq_policy *policy)315{316	int cpu = policy->cpu;317 318	loongson3_cpufreq_target(policy, per_cpu(freq_data, cpu)->def_freq_level);319}320 321static int loongson3_cpufreq_cpu_online(struct cpufreq_policy *policy)322{323	return 0;324}325 326static int loongson3_cpufreq_cpu_offline(struct cpufreq_policy *policy)327{328	return 0;329}330 331static struct cpufreq_driver loongson3_cpufreq_driver = {332	.name = "loongson3",333	.flags = CPUFREQ_CONST_LOOPS,334	.init = loongson3_cpufreq_cpu_init,335	.exit = loongson3_cpufreq_cpu_exit,336	.online = loongson3_cpufreq_cpu_online,337	.offline = loongson3_cpufreq_cpu_offline,338	.get = loongson3_cpufreq_get,339	.target_index = loongson3_cpufreq_target,340	.attr = cpufreq_generic_attr,341	.verify = cpufreq_generic_frequency_table_verify,342	.suspend = cpufreq_generic_suspend,343};344 345static int loongson3_cpufreq_probe(struct platform_device *pdev)346{347	int i, ret;348 349	for (i = 0; i < MAX_PACKAGES; i++)350		devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);351 352	ret = do_service_request(0, 0, CMD_GET_VERSION, 0, 0);353	if (ret <= 0)354		return -EPERM;355 356	ret = do_service_request(FEATURE_DVFS, 0, CMD_SET_FEATURE,357				 FEATURE_DVFS_ENABLE | FEATURE_DVFS_BOOST, 0);358	if (ret < 0)359		return -EPERM;360 361	loongson3_cpufreq_driver.driver_data = pdev;362 363	ret = cpufreq_register_driver(&loongson3_cpufreq_driver);364	if (ret)365		return ret;366 367	pr_info("cpufreq: Loongson-3 CPU frequency driver.\n");368 369	return 0;370}371 372static void loongson3_cpufreq_remove(struct platform_device *pdev)373{374	cpufreq_unregister_driver(&loongson3_cpufreq_driver);375}376 377static struct platform_device_id cpufreq_id_table[] = {378	{ "loongson3_cpufreq", },379	{ /* sentinel */ }380};381MODULE_DEVICE_TABLE(platform, cpufreq_id_table);382 383static struct platform_driver loongson3_platform_driver = {384	.driver = {385		.name = "loongson3_cpufreq",386	},387	.id_table = cpufreq_id_table,388	.probe = loongson3_cpufreq_probe,389	.remove_new = loongson3_cpufreq_remove,390};391module_platform_driver(loongson3_platform_driver);392 393MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>");394MODULE_DESCRIPTION("CPUFreq driver for Loongson-3 processors");395MODULE_LICENSE("GPL");396