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