12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Copyright 2001 MontaVista Software Inc.
41da177e4SLinus Torvalds * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
51da177e4SLinus Torvalds * Copyright (c) 2003, 2004 Maciej W. Rozycki
61da177e4SLinus Torvalds *
7d9eec1a5SAtsushi Nemoto * Common time service routines for MIPS machines.
81da177e4SLinus Torvalds */
9656db506SRalf Baechle #include <linux/bug.h>
107bcf7717SRalf Baechle #include <linux/clockchips.h>
111da177e4SLinus Torvalds #include <linux/types.h>
121da177e4SLinus Torvalds #include <linux/kernel.h>
131da177e4SLinus Torvalds #include <linux/init.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
151da177e4SLinus Torvalds #include <linux/param.h>
161da177e4SLinus Torvalds #include <linux/time.h>
171da177e4SLinus Torvalds #include <linux/timex.h>
181da177e4SLinus Torvalds #include <linux/smp.h>
191da177e4SLinus Torvalds #include <linux/spinlock.h>
2073bc256dSPaul Gortmaker #include <linux/export.h>
21ed26aacfSSerge Semin #include <linux/cpufreq.h>
22ed26aacfSSerge Semin #include <linux/delay.h>
231da177e4SLinus Torvalds
241da177e4SLinus Torvalds #include <asm/cpu-features.h>
2569f24d17SRalf Baechle #include <asm/cpu-type.h>
261da177e4SLinus Torvalds #include <asm/div64.h>
271da177e4SLinus Torvalds #include <asm/time.h>
281da177e4SLinus Torvalds
29ed26aacfSSerge Semin #ifdef CONFIG_CPU_FREQ
30ed26aacfSSerge Semin
31ed26aacfSSerge Semin static DEFINE_PER_CPU(unsigned long, pcp_lpj_ref);
32ed26aacfSSerge Semin static DEFINE_PER_CPU(unsigned long, pcp_lpj_ref_freq);
33ed26aacfSSerge Semin static unsigned long glb_lpj_ref;
34ed26aacfSSerge Semin static unsigned long glb_lpj_ref_freq;
35ed26aacfSSerge Semin
cpufreq_callback(struct notifier_block * nb,unsigned long val,void * data)36ed26aacfSSerge Semin static int cpufreq_callback(struct notifier_block *nb,
37ed26aacfSSerge Semin unsigned long val, void *data)
38ed26aacfSSerge Semin {
39ed26aacfSSerge Semin struct cpufreq_freqs *freq = data;
40ed26aacfSSerge Semin struct cpumask *cpus = freq->policy->cpus;
41ed26aacfSSerge Semin unsigned long lpj;
42ed26aacfSSerge Semin int cpu;
43ed26aacfSSerge Semin
44ed26aacfSSerge Semin /*
45ed26aacfSSerge Semin * Skip lpj numbers adjustment if the CPU-freq transition is safe for
46ed26aacfSSerge Semin * the loops delay. (Is this possible?)
47ed26aacfSSerge Semin */
48ed26aacfSSerge Semin if (freq->flags & CPUFREQ_CONST_LOOPS)
49ed26aacfSSerge Semin return NOTIFY_OK;
50ed26aacfSSerge Semin
51ed26aacfSSerge Semin /* Save the initial values of the lpjes for future scaling. */
52ed26aacfSSerge Semin if (!glb_lpj_ref) {
53ed26aacfSSerge Semin glb_lpj_ref = boot_cpu_data.udelay_val;
54ed26aacfSSerge Semin glb_lpj_ref_freq = freq->old;
55ed26aacfSSerge Semin
56ed26aacfSSerge Semin for_each_online_cpu(cpu) {
57ed26aacfSSerge Semin per_cpu(pcp_lpj_ref, cpu) =
58ed26aacfSSerge Semin cpu_data[cpu].udelay_val;
59ed26aacfSSerge Semin per_cpu(pcp_lpj_ref_freq, cpu) = freq->old;
60ed26aacfSSerge Semin }
61ed26aacfSSerge Semin }
62ed26aacfSSerge Semin
63ed26aacfSSerge Semin /*
64ed26aacfSSerge Semin * Adjust global lpj variable and per-CPU udelay_val number in
65ed26aacfSSerge Semin * accordance with the new CPU frequency.
66ed26aacfSSerge Semin */
67ed26aacfSSerge Semin if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
68ed26aacfSSerge Semin (val == CPUFREQ_POSTCHANGE && freq->old > freq->new)) {
69ed26aacfSSerge Semin loops_per_jiffy = cpufreq_scale(glb_lpj_ref,
70ed26aacfSSerge Semin glb_lpj_ref_freq,
71ed26aacfSSerge Semin freq->new);
72ed26aacfSSerge Semin
73ed26aacfSSerge Semin for_each_cpu(cpu, cpus) {
74ed26aacfSSerge Semin lpj = cpufreq_scale(per_cpu(pcp_lpj_ref, cpu),
75ed26aacfSSerge Semin per_cpu(pcp_lpj_ref_freq, cpu),
76ed26aacfSSerge Semin freq->new);
77ed26aacfSSerge Semin cpu_data[cpu].udelay_val = (unsigned int)lpj;
78ed26aacfSSerge Semin }
79ed26aacfSSerge Semin }
80ed26aacfSSerge Semin
81ed26aacfSSerge Semin return NOTIFY_OK;
82ed26aacfSSerge Semin }
83ed26aacfSSerge Semin
84ed26aacfSSerge Semin static struct notifier_block cpufreq_notifier = {
85ed26aacfSSerge Semin .notifier_call = cpufreq_callback,
86ed26aacfSSerge Semin };
87ed26aacfSSerge Semin
register_cpufreq_notifier(void)88ed26aacfSSerge Semin static int __init register_cpufreq_notifier(void)
89ed26aacfSSerge Semin {
90ed26aacfSSerge Semin return cpufreq_register_notifier(&cpufreq_notifier,
91ed26aacfSSerge Semin CPUFREQ_TRANSITION_NOTIFIER);
92ed26aacfSSerge Semin }
93ed26aacfSSerge Semin core_initcall(register_cpufreq_notifier);
94ed26aacfSSerge Semin
95ed26aacfSSerge Semin #endif /* CONFIG_CPU_FREQ */
96ed26aacfSSerge Semin
971da177e4SLinus Torvalds /*
981da177e4SLinus Torvalds * forward reference
991da177e4SLinus Torvalds */
1001da177e4SLinus Torvalds DEFINE_SPINLOCK(rtc_lock);
1014b550488SRalf Baechle EXPORT_SYMBOL(rtc_lock);
1021da177e4SLinus Torvalds
null_perf_irq(void)10346684734SDmitri Vorobiev static int null_perf_irq(void)
104ba339c03SRalf Baechle {
105ba339c03SRalf Baechle return 0;
106ba339c03SRalf Baechle }
107ba339c03SRalf Baechle
1087d12e780SDavid Howells int (*perf_irq)(void) = null_perf_irq;
109ba339c03SRalf Baechle
110ba339c03SRalf Baechle EXPORT_SYMBOL(perf_irq);
111ba339c03SRalf Baechle
112ffe9ee47SChris Dearman /*
1131da177e4SLinus Torvalds * time_init() - it does the following things.
1141da177e4SLinus Torvalds *
1154b550488SRalf Baechle * 1) plat_time_init() -
1161da177e4SLinus Torvalds * a) (optional) set up RTC routines,
1171da177e4SLinus Torvalds * b) (optional) calibrate and set the mips_hpt_frequency
11816b7b2acSAtsushi Nemoto * (only needed if you intended to use cpu counter as timer interrupt
11916b7b2acSAtsushi Nemoto * source)
1204b550488SRalf Baechle * 2) calculate a couple of cached variables for later usage
1211da177e4SLinus Torvalds */
1221da177e4SLinus Torvalds
1231da177e4SLinus Torvalds unsigned int mips_hpt_frequency;
124c992a4f6SJames Hogan EXPORT_SYMBOL_GPL(mips_hpt_frequency);
1251da177e4SLinus Torvalds
cpu_has_mfc0_count_bug(void)1265aa85c9fSRalf Baechle static __init int cpu_has_mfc0_count_bug(void)
1275aa85c9fSRalf Baechle {
1285aa85c9fSRalf Baechle switch (current_cpu_type()) {
1295aa85c9fSRalf Baechle case CPU_R4000PC:
1305aa85c9fSRalf Baechle case CPU_R4000SC:
1315aa85c9fSRalf Baechle case CPU_R4000MC:
1325aa85c9fSRalf Baechle /*
1335aa85c9fSRalf Baechle * V3.0 is documented as suffering from the mfc0 from count bug.
1345aa85c9fSRalf Baechle * Afaik this is the last version of the R4000. Later versions
1355aa85c9fSRalf Baechle * were marketed as R4400.
1365aa85c9fSRalf Baechle */
1375aa85c9fSRalf Baechle return 1;
1385aa85c9fSRalf Baechle
1395aa85c9fSRalf Baechle case CPU_R4400PC:
1405aa85c9fSRalf Baechle case CPU_R4400SC:
1415aa85c9fSRalf Baechle case CPU_R4400MC:
1425aa85c9fSRalf Baechle /*
1435aa85c9fSRalf Baechle * The published errata for the R4400 up to 3.0 say the CPU
144*f0a6c68fSMaciej W. Rozycki * has the mfc0 from count bug. This seems the last version
145*f0a6c68fSMaciej W. Rozycki * produced.
1465aa85c9fSRalf Baechle */
1475aa85c9fSRalf Baechle return 1;
1485aa85c9fSRalf Baechle }
1495aa85c9fSRalf Baechle
1505aa85c9fSRalf Baechle return 0;
1515aa85c9fSRalf Baechle }
1525aa85c9fSRalf Baechle
time_init(void)1531da177e4SLinus Torvalds void __init time_init(void)
1541da177e4SLinus Torvalds {
1554b550488SRalf Baechle plat_time_init();
1561da177e4SLinus Torvalds
157afddce0cSMaciej W. Rozycki /*
158afddce0cSMaciej W. Rozycki * The use of the R4k timer as a clock event takes precedence;
159afddce0cSMaciej W. Rozycki * if reading the Count register might interfere with the timer
160afddce0cSMaciej W. Rozycki * interrupt, then we don't use the timer as a clock source.
161afddce0cSMaciej W. Rozycki * We may still use the timer as a clock source though if the
162afddce0cSMaciej W. Rozycki * timer interrupt isn't reliable; the interference doesn't
163afddce0cSMaciej W. Rozycki * matter then, because we don't use the interrupt.
164afddce0cSMaciej W. Rozycki */
165afddce0cSMaciej W. Rozycki if (mips_clockevent_init() != 0 || !cpu_has_mfc0_count_bug())
166d9eec1a5SAtsushi Nemoto init_mips_clocksource();
1671da177e4SLinus Torvalds }
168