diff mbox series

[v2,19/20] mips: cevt-r4k: Update the r4k-clockevent frequency in sync with CPU

Message ID 20200506174238.15385-20-Sergey.Semin@baikalelectronics.ru (mailing list archive)
State Not Applicable, archived
Headers show
Series mips: Prepare MIPS-arch code for Baikal-T1 SoC support | expand

Commit Message

Serge Semin May 6, 2020, 5:42 p.m. UTC
From: Serge Semin <Sergey.Semin@baikalelectronics.ru>

Due to being embedded into the CPU cores MIPS count/compare timer
frequency is changed together with the CPU clocks alteration.
In case if frequency really changes the kernel clockevent framework
must be notified, otherwise the kernel timers won't work correctly.
Fix this by calling clockevents_update_freq() for each r4k clockevent
handlers registered per available CPUs.

Traditionally MIPS r4k-clock are clocked with CPU frequency divided by 2.
But this isn't true for some of the platforms. Due to this we have to save
the basic CPU frequency, so then use it to scale the initial timer
frequency (mips_hpt_frequency) and pass the updated value further to the
clockevent framework.

Signed-off-by: Serge Semin <Sergey.Semin@baikalelectronics.ru>
Cc: Alexey Malahov <Alexey.Malahov@baikalelectronics.ru>
Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Cc: Paul Burton <paulburton@kernel.org>
Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: linux-pm@vger.kernel.org
Cc: devicetree@vger.kernel.org
---
 arch/mips/kernel/cevt-r4k.c | 44 +++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

Comments

Thomas Bogendoerfer May 8, 2020, 3:40 p.m. UTC | #1
On Wed, May 06, 2020 at 08:42:37PM +0300, Sergey.Semin@baikalelectronics.ru wrote:
> diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
> index 17a9cbb8b3df..f5b72fb7d5ee 100644
> --- a/arch/mips/kernel/cevt-r4k.c
> +++ b/arch/mips/kernel/cevt-r4k.c
> @@ -8,6 +8,7 @@
>   */
>  #include <linux/clockchips.h>
>  #include <linux/interrupt.h>
> +#include <linux/cpufreq.h>
>  #include <linux/percpu.h>
>  #include <linux/smp.h>
>  #include <linux/irq.h>
> @@ -250,6 +251,49 @@ unsigned int __weak get_c0_compare_int(void)
>  	return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
>  }
>  
> +#ifdef CONFIG_CPU_FREQ
> +
> +static unsigned long mips_ref_freq;
> +
> +static int cpufreq_callback(struct notifier_block *nb,
> +			    unsigned long val, void *data)

please prefix function names with r4k_ to make them different from
the other ones you implemented in kernel/time.c. I know they are
static, but keeping different names makes looking at crashes easier.

> +	struct cpufreq_freqs *freq = data;
> +	struct clock_event_device *cd;
> +	unsigned long rate;
> +	int cpu;
> +
> +	if (!mips_ref_freq)
> +		mips_ref_freq = freq->old;

isn't this the same as mips_hpt_frequency ?

Thomas.
Serge Semin May 11, 2020, 12:34 a.m. UTC | #2
On Fri, May 08, 2020 at 05:40:46PM +0200, Thomas Bogendoerfer wrote:
> On Wed, May 06, 2020 at 08:42:37PM +0300, Sergey.Semin@baikalelectronics.ru wrote:
> > diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
> > index 17a9cbb8b3df..f5b72fb7d5ee 100644
> > --- a/arch/mips/kernel/cevt-r4k.c
> > +++ b/arch/mips/kernel/cevt-r4k.c
> > @@ -8,6 +8,7 @@
> >   */
> >  #include <linux/clockchips.h>
> >  #include <linux/interrupt.h>
> > +#include <linux/cpufreq.h>
> >  #include <linux/percpu.h>
> >  #include <linux/smp.h>
> >  #include <linux/irq.h>
> > @@ -250,6 +251,49 @@ unsigned int __weak get_c0_compare_int(void)
> >  	return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
> >  }
> >  
> > +#ifdef CONFIG_CPU_FREQ
> > +
> > +static unsigned long mips_ref_freq;
> > +
> > +static int cpufreq_callback(struct notifier_block *nb,
> > +			    unsigned long val, void *data)
> 
> please prefix function names with r4k_ to make them different from
> the other ones you implemented in kernel/time.c. I know they are
> static, but keeping different names makes looking at crashes easier.

Agreed. I'll fix it in v3.

> 
> > +	struct cpufreq_freqs *freq = data;
> > +	struct clock_event_device *cd;
> > +	unsigned long rate;
> > +	int cpu;
> > +
> > +	if (!mips_ref_freq)
> > +		mips_ref_freq = freq->old;
> 
> isn't this the same as mips_hpt_frequency ?

No. Here I save the initial CPU frequency so use one then to scale the
mips_hpt_frequency value in accordance with the CPU clock rate change. Yes,
mips_hpt_frequency value may initially match the CPU frequency on some platforms
but normally the r4k timer is clocked with half of it while some systems may have
a complicated algorithm of the timer ref clock rate calculation.

-Sergey

> 
> Thomas.
> 
> -- 
> Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
> good idea.                                                [ RFC1925, 2.3 ]
diff mbox series

Patch

diff --git a/arch/mips/kernel/cevt-r4k.c b/arch/mips/kernel/cevt-r4k.c
index 17a9cbb8b3df..f5b72fb7d5ee 100644
--- a/arch/mips/kernel/cevt-r4k.c
+++ b/arch/mips/kernel/cevt-r4k.c
@@ -8,6 +8,7 @@ 
  */
 #include <linux/clockchips.h>
 #include <linux/interrupt.h>
+#include <linux/cpufreq.h>
 #include <linux/percpu.h>
 #include <linux/smp.h>
 #include <linux/irq.h>
@@ -250,6 +251,49 @@  unsigned int __weak get_c0_compare_int(void)
 	return MIPS_CPU_IRQ_BASE + cp0_compare_irq;
 }
 
+#ifdef CONFIG_CPU_FREQ
+
+static unsigned long mips_ref_freq;
+
+static int cpufreq_callback(struct notifier_block *nb,
+			    unsigned long val, void *data)
+{
+	struct cpufreq_freqs *freq = data;
+	struct clock_event_device *cd;
+	unsigned long rate;
+	int cpu;
+
+	if (!mips_ref_freq)
+		mips_ref_freq = freq->old;
+
+	if (val == CPUFREQ_POSTCHANGE) {
+		rate = cpufreq_scale(mips_hpt_frequency, mips_ref_freq,
+				     freq->new);
+
+		for_each_cpu(cpu, freq->policy->cpus) {
+			cd = &per_cpu(mips_clockevent_device, cpu);
+
+			clockevents_update_freq(cd, rate);
+		}
+	}
+
+	return 0;
+}
+
+static struct notifier_block cpufreq_notifier = {
+	.notifier_call  = cpufreq_callback,
+};
+
+static int __init register_cpufreq_notifier(void)
+{
+	return cpufreq_register_notifier(&cpufreq_notifier,
+					 CPUFREQ_TRANSITION_NOTIFIER);
+
+}
+core_initcall(register_cpufreq_notifier);
+
+#endif /* !CONFIG_CPU_FREQ */
+
 int r4k_clockevent_init(void)
 {
 	unsigned long flags = IRQF_PERCPU | IRQF_TIMER | IRQF_SHARED;