xref: /openbmc/linux/drivers/cpufreq/loongson2_cpufreq.c (revision ecc23d0a422a3118fcf6e4f0a46e17a6c2047b02)
17a998935SViresh Kumar /*
27a998935SViresh Kumar  * Cpufreq driver for the loongson-2 processors
37a998935SViresh Kumar  *
47a998935SViresh Kumar  * The 2E revision of loongson processor not support this feature.
57a998935SViresh Kumar  *
60bb383a2SRalf Baechle  * Copyright (C) 2006 - 2008 Lemote Inc. & Institute of Computing Technology
77a998935SViresh Kumar  * Author: Yanhua, yanh@lemote.com
87a998935SViresh Kumar  *
97a998935SViresh Kumar  * This file is subject to the terms and conditions of the GNU General Public
107a998935SViresh Kumar  * License.  See the file "COPYING" in the main directory of this archive
117a998935SViresh Kumar  * for more details.
127a998935SViresh Kumar  */
131c5864e2SJoe Perches 
141c5864e2SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
151c5864e2SJoe Perches 
167a998935SViresh Kumar #include <linux/cpufreq.h>
177a998935SViresh Kumar #include <linux/module.h>
187a998935SViresh Kumar #include <linux/err.h>
197a998935SViresh Kumar #include <linux/delay.h>
207a998935SViresh Kumar #include <linux/platform_device.h>
217a998935SViresh Kumar 
22bdc92d74SRalf Baechle #include <asm/idle.h>
237a998935SViresh Kumar 
2471e2f4ddSJiaxun Yang #include <asm/mach-loongson2ef/loongson.h>
257a998935SViresh Kumar 
267a998935SViresh Kumar static uint nowait;
277a998935SViresh Kumar 
287a998935SViresh Kumar static void (*saved_cpu_wait) (void);
297a998935SViresh Kumar 
307a998935SViresh Kumar static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
317a998935SViresh Kumar 					unsigned long val, void *data);
327a998935SViresh Kumar 
337a998935SViresh Kumar static struct notifier_block loongson2_cpufreq_notifier_block = {
347a998935SViresh Kumar 	.notifier_call = loongson2_cpu_freq_notifier
357a998935SViresh Kumar };
367a998935SViresh Kumar 
loongson2_cpu_freq_notifier(struct notifier_block * nb,unsigned long val,void * data)377a998935SViresh Kumar static int loongson2_cpu_freq_notifier(struct notifier_block *nb,
387a998935SViresh Kumar 					unsigned long val, void *data)
397a998935SViresh Kumar {
407a998935SViresh Kumar 	if (val == CPUFREQ_POSTCHANGE)
417a998935SViresh Kumar 		current_cpu_data.udelay_val = loops_per_jiffy;
427a998935SViresh Kumar 
437a998935SViresh Kumar 	return 0;
447a998935SViresh Kumar }
457a998935SViresh Kumar 
467a998935SViresh Kumar /*
477a998935SViresh Kumar  * Here we notify other drivers of the proposed change and the final change.
487a998935SViresh Kumar  */
loongson2_cpufreq_target(struct cpufreq_policy * policy,unsigned int index)497a998935SViresh Kumar static int loongson2_cpufreq_target(struct cpufreq_policy *policy,
509c0ebcf7SViresh Kumar 				     unsigned int index)
517a998935SViresh Kumar {
527a998935SViresh Kumar 	unsigned int freq;
537a998935SViresh Kumar 
547a998935SViresh Kumar 	freq =
557a998935SViresh Kumar 	    ((cpu_clock_freq / 1000) *
569c0ebcf7SViresh Kumar 	     loongson2_clockmod_table[index].driver_data) / 8;
577a998935SViresh Kumar 
587a998935SViresh Kumar 	/* setting the cpu frequency */
59c02e9630SArnd Bergmann 	loongson2_cpu_set_rate(freq);
607a998935SViresh Kumar 
617a998935SViresh Kumar 	return 0;
627a998935SViresh Kumar }
637a998935SViresh Kumar 
loongson2_cpufreq_cpu_init(struct cpufreq_policy * policy)647a998935SViresh Kumar static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy)
657a998935SViresh Kumar {
667a998935SViresh Kumar 	int i;
677a998935SViresh Kumar 	unsigned long rate;
687a998935SViresh Kumar 	int ret;
697a998935SViresh Kumar 
707a998935SViresh Kumar 	rate = cpu_clock_freq / 1000;
71c02e9630SArnd Bergmann 	if (!rate)
727a998935SViresh Kumar 		return -EINVAL;
737a998935SViresh Kumar 
747a998935SViresh Kumar 	/* clock table init */
757a998935SViresh Kumar 	for (i = 2;
767a998935SViresh Kumar 	     (loongson2_clockmod_table[i].frequency != CPUFREQ_TABLE_END);
777a998935SViresh Kumar 	     i++)
787a998935SViresh Kumar 		loongson2_clockmod_table[i].frequency = (rate * i) / 8;
797a998935SViresh Kumar 
80c02e9630SArnd Bergmann 	ret = loongson2_cpu_set_rate(rate);
81c02e9630SArnd Bergmann 	if (ret)
82f54fe64dSAaro Koskinen 		return ret;
83f54fe64dSAaro Koskinen 
84c4dcc8a1SViresh Kumar 	cpufreq_generic_init(policy, &loongson2_clockmod_table[0], 0);
85c4dcc8a1SViresh Kumar 	return 0;
867a998935SViresh Kumar }
877a998935SViresh Kumar 
loongson2_cpufreq_exit(struct cpufreq_policy * policy)887a998935SViresh Kumar static int loongson2_cpufreq_exit(struct cpufreq_policy *policy)
897a998935SViresh Kumar {
907a998935SViresh Kumar 	return 0;
917a998935SViresh Kumar }
927a998935SViresh Kumar 
937a998935SViresh Kumar static struct cpufreq_driver loongson2_cpufreq_driver = {
947a998935SViresh Kumar 	.name = "loongson2",
957a998935SViresh Kumar 	.init = loongson2_cpufreq_cpu_init,
966b0f8d73SViresh Kumar 	.verify = cpufreq_generic_frequency_table_verify,
979c0ebcf7SViresh Kumar 	.target_index = loongson2_cpufreq_target,
98652ed95dSViresh Kumar 	.get = cpufreq_generic_get,
997a998935SViresh Kumar 	.exit = loongson2_cpufreq_exit,
1006b0f8d73SViresh Kumar 	.attr = cpufreq_generic_attr,
1017a998935SViresh Kumar };
1027a998935SViresh Kumar 
103a804d510SArvind Yadav static const struct platform_device_id platform_device_ids[] = {
1047a998935SViresh Kumar 	{
1057a998935SViresh Kumar 		.name = "loongson2_cpufreq",
1067a998935SViresh Kumar 	},
1077a998935SViresh Kumar 	{}
1087a998935SViresh Kumar };
1097a998935SViresh Kumar 
1107a998935SViresh Kumar MODULE_DEVICE_TABLE(platform, platform_device_ids);
1117a998935SViresh Kumar 
1127a998935SViresh Kumar static struct platform_driver platform_driver = {
1137a998935SViresh Kumar 	.driver = {
1147a998935SViresh Kumar 		.name = "loongson2_cpufreq",
1157a998935SViresh Kumar 	},
1167a998935SViresh Kumar 	.id_table = platform_device_ids,
1177a998935SViresh Kumar };
1187a998935SViresh Kumar 
1197a998935SViresh Kumar /*
1207a998935SViresh Kumar  * This is the simple version of Loongson-2 wait, Maybe we need do this in
1217a998935SViresh Kumar  * interrupt disabled context.
1227a998935SViresh Kumar  */
1237a998935SViresh Kumar 
1247a998935SViresh Kumar static DEFINE_SPINLOCK(loongson2_wait_lock);
1257a998935SViresh Kumar 
loongson2_cpu_wait(void)1267a998935SViresh Kumar static void loongson2_cpu_wait(void)
1277a998935SViresh Kumar {
1287a998935SViresh Kumar 	unsigned long flags;
1297a998935SViresh Kumar 	u32 cpu_freq;
1307a998935SViresh Kumar 
1317a998935SViresh Kumar 	spin_lock_irqsave(&loongson2_wait_lock, flags);
1321250c1a5SAlexandre Oliva 	cpu_freq = readl(LOONGSON_CHIPCFG);
1331250c1a5SAlexandre Oliva 	/* Put CPU into wait mode */
1341250c1a5SAlexandre Oliva 	writel(readl(LOONGSON_CHIPCFG) & ~0x7, LOONGSON_CHIPCFG);
1351250c1a5SAlexandre Oliva 	/* Restore CPU state */
1361250c1a5SAlexandre Oliva 	writel(cpu_freq, LOONGSON_CHIPCFG);
1377a998935SViresh Kumar 	spin_unlock_irqrestore(&loongson2_wait_lock, flags);
138fb40bc3eSRalf Baechle 	local_irq_enable();
1397a998935SViresh Kumar }
1407a998935SViresh Kumar 
cpufreq_init(void)1417a998935SViresh Kumar static int __init cpufreq_init(void)
1427a998935SViresh Kumar {
1437a998935SViresh Kumar 	int ret;
1447a998935SViresh Kumar 
1457a998935SViresh Kumar 	/* Register platform stuff */
1467a998935SViresh Kumar 	ret = platform_driver_register(&platform_driver);
1477a998935SViresh Kumar 	if (ret)
1487a998935SViresh Kumar 		return ret;
1497a998935SViresh Kumar 
1501c5864e2SJoe Perches 	pr_info("Loongson-2F CPU frequency driver\n");
1517a998935SViresh Kumar 
1527a998935SViresh Kumar 	cpufreq_register_notifier(&loongson2_cpufreq_notifier_block,
1537a998935SViresh Kumar 				  CPUFREQ_TRANSITION_NOTIFIER);
1547a998935SViresh Kumar 
1557a998935SViresh Kumar 	ret = cpufreq_register_driver(&loongson2_cpufreq_driver);
1567a998935SViresh Kumar 
157*783df315SYuan Can 	if (ret) {
158*783df315SYuan Can 		platform_driver_unregister(&platform_driver);
159*783df315SYuan Can 	} else if (!nowait) {
1607a998935SViresh Kumar 		saved_cpu_wait = cpu_wait;
1617a998935SViresh Kumar 		cpu_wait = loongson2_cpu_wait;
1627a998935SViresh Kumar 	}
1637a998935SViresh Kumar 
1647a998935SViresh Kumar 	return ret;
1657a998935SViresh Kumar }
1667a998935SViresh Kumar 
cpufreq_exit(void)1677a998935SViresh Kumar static void __exit cpufreq_exit(void)
1687a998935SViresh Kumar {
1697a998935SViresh Kumar 	if (!nowait && saved_cpu_wait)
1707a998935SViresh Kumar 		cpu_wait = saved_cpu_wait;
1717a998935SViresh Kumar 	cpufreq_unregister_driver(&loongson2_cpufreq_driver);
1727a998935SViresh Kumar 	cpufreq_unregister_notifier(&loongson2_cpufreq_notifier_block,
1737a998935SViresh Kumar 				    CPUFREQ_TRANSITION_NOTIFIER);
1747a998935SViresh Kumar 
1757a998935SViresh Kumar 	platform_driver_unregister(&platform_driver);
1767a998935SViresh Kumar }
1777a998935SViresh Kumar 
1787a998935SViresh Kumar module_init(cpufreq_init);
1797a998935SViresh Kumar module_exit(cpufreq_exit);
1807a998935SViresh Kumar 
1817a998935SViresh Kumar module_param(nowait, uint, 0644);
1827a998935SViresh Kumar MODULE_PARM_DESC(nowait, "Disable Loongson-2F specific wait");
1837a998935SViresh Kumar 
1847a998935SViresh Kumar MODULE_AUTHOR("Yanhua <yanh@lemote.com>");
1857a998935SViresh Kumar MODULE_DESCRIPTION("cpufreq driver for Loongson2F");
1867a998935SViresh Kumar MODULE_LICENSE("GPL");
187