* [PATCH]sparc32: GENERIC_CLOCKEVENTS support
@ 2012-02-07 22:09 Kirill Tkhai
2012-03-16 22:55 ` Kirill Tkhai
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Kirill Tkhai @ 2012-02-07 22:09 UTC (permalink / raw)
To: sparclinux
This patch implements GENERIC_CLOCKEVENTS support on sparc32.
The kernel uses l14 timers as clockevents. l10 timer is used
as clocksource if platform master_l10_counter isn't constantly
zero. The clocksource is continuous, so it's possible to use
high resolution timers. l10 timer is also used as clockevent
on UP configurations.
This realization is for sun4m, sun4d, sun4c and microsparc-IIep
platforms. It's necessary to make appropriate changes for LEON
part (it should not have l10 based cs and ce in case of SMP
because of LEON's master_l10_counter is zero).
Thanks to Sam Ravnborg for the review and comments.
Signed-off-by: Tkhai Kirill <tkhai@yandex.ru>
---
arch/sparc/Kconfig | 6 +-
arch/sparc/include/asm/cpudata_32.h | 1 -
arch/sparc/include/asm/timer_32.h | 14 +++
arch/sparc/include/asm/timex_32.h | 1 -
arch/sparc/kernel/irq.h | 8 +-
arch/sparc/kernel/kernel.h | 2 -
arch/sparc/kernel/pcic.c | 50 +++++----
arch/sparc/kernel/smp_32.c | 21 +---
arch/sparc/kernel/sun4c_irq.c | 8 +-
arch/sparc/kernel/sun4d_irq.c | 16 ++-
arch/sparc/kernel/sun4d_smp.c | 28 ++---
arch/sparc/kernel/sun4m_irq.c | 20 ++-
arch/sparc/kernel/sun4m_smp.c | 38 ++----
arch/sparc/kernel/time_32.c | 219 ++++++++++++++++++++++++++++------
14 files changed, 281 insertions(+), 151 deletions(-)
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 868ea08..4225559 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -70,17 +70,13 @@ config BITS
default 32 if SPARC32
default 64 if SPARC64
-config ARCH_USES_GETTIMEOFFSET
- bool
- default y if SPARC32
-
config GENERIC_CMOS_UPDATE
bool
default y
config GENERIC_CLOCKEVENTS
bool
- default y if SPARC64
+ default y
config IOMMU_HELPER
bool
diff --git a/arch/sparc/include/asm/cpudata_32.h b/arch/sparc/include/asm/cpudata_32.h
index a4c5a93..0300d94 100644
--- a/arch/sparc/include/asm/cpudata_32.h
+++ b/arch/sparc/include/asm/cpudata_32.h
@@ -14,7 +14,6 @@
typedef struct {
unsigned long udelay_val;
unsigned long clock_tick;
- unsigned int multiplier;
unsigned int counter;
#ifdef CONFIG_SMP
unsigned int irq_resched_count;
diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h
index 2ec030e..3a5c24b 100644
--- a/arch/sparc/include/asm/timer_32.h
+++ b/arch/sparc/include/asm/timer_32.h
@@ -8,10 +8,24 @@
#ifndef _SPARC_TIMER_H
#define _SPARC_TIMER_H
+#include <linux/irqreturn.h>
+#include <linux/clocksource.h>
+#include <asm-generic/percpu.h>
#include <asm/system.h> /* For SUN4M_NCPUS */
#include <asm/btfixup.h>
+#define SBUS_CLOCK_RATE 2000000 /* 2MHz */
+
extern __volatile__ unsigned int *master_l10_counter;
+extern unsigned int (*get_cycles_offset)(void);
+extern unsigned int timer_cs_period;
+
+extern irqreturn_t notrace timer_interrupt(int dummy, void *dev_id);
+
+#ifdef CONFIG_SMP
+DECLARE_PER_CPU(struct clock_event_device, sparc32_clockevent);
+extern void register_percpu_ce(int cpu);
+#endif
/* FIXME: Make do_[gs]ettimeofday btfixup calls */
BTFIXUPDEF_CALL(int, bus_do_settimeofday, struct timespec *tv)
diff --git a/arch/sparc/include/asm/timex_32.h b/arch/sparc/include/asm/timex_32.h
index a254750..b6ccdb0 100644
--- a/arch/sparc/include/asm/timex_32.h
+++ b/arch/sparc/include/asm/timex_32.h
@@ -12,5 +12,4 @@
typedef unsigned long cycles_t;
#define get_cycles() (0)
-extern u32 (*do_arch_gettimeoffset)(void);
#endif
diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h
index 4285112..cbfba9f 100644
--- a/arch/sparc/kernel/irq.h
+++ b/arch/sparc/kernel/irq.h
@@ -40,15 +40,21 @@ struct sun4m_irq_global {
extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS];
extern struct sun4m_irq_global __iomem *sun4m_irq_global;
+/* The following definitions describe the individual platform features: */
+#define FEAT_L10_CS (1 << 0) /* L10 timer is used as a clocksource */
+#define FEAT_L10_CE (1 << 1) /* L10 timer is used as a clockevent */
+#define FEAT_L14_OS (1 << 2) /* L14 timer clockevent can oneshot mode */
+
/*
* Platform specific irq configuration
* The individual platforms assign their platform
* specifics in their init functions.
*/
struct sparc_irq_config {
- void (*init_timers)(irq_handler_t);
+ void (*init_timers)(void);
unsigned int (*build_device_irq)(struct platform_device *op,
unsigned int real_irq);
+ unsigned int features;
};
extern struct sparc_irq_config sparc_irq_config;
diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h
index fd6c36b..8abbad3 100644
--- a/arch/sparc/kernel/kernel.h
+++ b/arch/sparc/kernel/kernel.h
@@ -47,8 +47,6 @@ extern void init_IRQ(void);
extern void sun4c_init_IRQ(void);
/* sun4m_irq.c */
-extern unsigned int lvl14_resolution;
-
extern void sun4m_init_IRQ(void);
extern void sun4m_unmask_profile_irq(void);
extern void sun4m_clear_profile_irq(int cpu);
diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c
index fcc148e..090f6af 100644
--- a/arch/sparc/kernel/pcic.c
+++ b/arch/sparc/kernel/pcic.c
@@ -703,31 +703,28 @@ static void pcic_clear_clock_irq(void)
pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT);
}
-static irqreturn_t pcic_timer_handler (int irq, void *h)
-{
- pcic_clear_clock_irq();
- xtime_update(1);
-#ifndef CONFIG_SMP
- update_process_times(user_mode(get_irq_regs()));
-#endif
- return IRQ_HANDLED;
-}
+/* CPU frequency is 100 MHz, timer increments every 4 CPU clocks */
+#define USECS_PER_JIFFY (1000000 / HZ)
+#define TICK_TIMER_LIMIT ((100 * 1000000 / 4) / HZ)
-#define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */
-#define TICK_TIMER_LIMIT ((100*1000000/4)/100)
-
-u32 pci_gettimeoffset(void)
+static u32 pcic_cycles_offset(void)
{
+ u32 value, count;
+
+ value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER);
+ count = value & ~PCI_SYS_COUNTER_OVERFLOW;
+
+ if (value & PCI_SYS_COUNTER_OVERFLOW)
+ count += TICK_TIMER_LIMIT;
/*
- * We divide all by 100
+ * We divide all by HZ
* to have microsecond resolution and to avoid overflow
*/
- unsigned long count - readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW;
- count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100);
- return count * 1000;
-}
+ count = ((count / HZ) * USECS_PER_JIFFY) / (TICK_TIMER_LIMIT / HZ);
+ /* Coordinate with the fact that timer_cs rate is 2MHz */
+ return count * 2;
+}
void __init pci_time_init(void)
{
@@ -736,9 +733,16 @@ void __init pci_time_init(void)
int timer_irq, irq;
int err;
- do_arch_gettimeoffset = pci_gettimeoffset;
-
- btfixup();
+#ifndef CONFIG_SMP
+ /*
+ * It's in SBUS dimension, because timer_cs is in this dimension.
+ * We take into account this in pcic_cycles_offset()
+ */
+ timer_cs_period = SBUS_CLOCK_RATE / HZ;
+ sparc_irq_config.features |= FEAT_L10_CE;
+#endif
+ sparc_irq_config.features |= FEAT_L10_CS;
+ get_cycles_offset = pcic_cycles_offset;
writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT);
/* PROM should set appropriate irq */
@@ -747,7 +751,7 @@ void __init pci_time_init(void)
writel (PCI_COUNTER_IRQ_SET(timer_irq, 0),
pcic->pcic_regs+PCI_COUNTER_IRQ);
irq = pcic_build_device_irq(NULL, timer_irq);
- err = request_irq(irq, pcic_timer_handler,
+ err = request_irq(irq, timer_interrupt,
IRQF_TIMER, "timer", NULL);
if (err) {
prom_printf("time_init: unable to attach IRQ%d\n", timer_irq);
diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c
index f671e7f..569a8a9 100644
--- a/arch/sparc/kernel/smp_32.c
+++ b/arch/sparc/kernel/smp_32.c
@@ -301,28 +301,9 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr)
local_flush_sig_insns(mm, insn_addr);
}
-extern unsigned int lvl14_resolution;
-
-/* /proc/profile writes can call this, don't __init it please. */
-static DEFINE_SPINLOCK(prof_setup_lock);
-
int setup_profiling_timer(unsigned int multiplier)
{
- int i;
- unsigned long flags;
-
- /* Prevent level14 ticker IRQ flooding. */
- if((!multiplier) || (lvl14_resolution / multiplier) < 500)
- return -EINVAL;
-
- spin_lock_irqsave(&prof_setup_lock, flags);
- for_each_possible_cpu(i) {
- load_profile_irq(i, lvl14_resolution / multiplier);
- prof_multiplier(i) = multiplier;
- }
- spin_unlock_irqrestore(&prof_setup_lock, flags);
-
- return 0;
+ return -EINVAL;
}
void __init smp_prepare_cpus(unsigned int max_cpus)
diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c
index f6bf25a..caaa7b9 100644
--- a/arch/sparc/kernel/sun4c_irq.c
+++ b/arch/sparc/kernel/sun4c_irq.c
@@ -174,7 +174,7 @@ static void sun4c_load_profile_irq(int cpu, unsigned int limit)
/* Errm.. not sure how to do this.. */
}
-static void __init sun4c_init_timers(irq_handler_t counter_fn)
+static void __init sun4c_init_timers(void)
{
const struct linux_prom_irqs *prom_irqs;
struct device_node *dp;
@@ -207,12 +207,14 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn)
* level 14 timer limit since we are letting the prom handle
* them until we have a real console driver so L1-A works.
*/
- sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit);
+ timer_cs_period = SBUS_CLOCK_RATE / HZ;
+ sparc_irq_config.features |= (FEAT_L10_CS | FEAT_L10_CE);
+ sbus_writel(((timer_cs_period + 1) << 9), &sun4c_timers->l10_limit);
master_l10_counter = &sun4c_timers->l10_count;
irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri);
- err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL);
+ err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
if (err) {
prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err);
prom_halt();
diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
index 1d13c5b..7fb9437 100644
--- a/arch/sparc/kernel/sun4d_irq.c
+++ b/arch/sparc/kernel/sun4d_irq.c
@@ -282,7 +282,8 @@ static void sun4d_clear_clock_irq(void)
static void sun4d_load_profile_irq(int cpu, unsigned int limit)
{
- bw_set_prof_limit(cpu, limit);
+ unsigned int value = limit ? (limit + 1) << 9 : 0;
+ bw_set_prof_limit(cpu, value);
}
static void __init sun4d_load_profile_irqs(void)
@@ -423,7 +424,7 @@ static void __init sun4d_fixup_trap_table(void)
#endif
}
-static void __init sun4d_init_timers(irq_handler_t counter_fn)
+static void __init sun4d_init_timers(void)
{
struct device_node *dp;
struct resource res;
@@ -466,12 +467,19 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn)
prom_halt();
}
- sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit);
+#ifdef CONFIG_SMP
+ timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */
+#else
+ timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */
+ sparc_irq_config.features |= FEAT_L10_CE;
+#endif
+ sparc_irq_config.features |= FEAT_L10_CS;
+ sbus_writel(((timer_cs_period + 1) << 9), &sun4d_timers->l10_timer_limit);
master_l10_counter = &sun4d_timers->l10_cur_count;
irq = sun4d_build_timer_irq(board, SUN4D_TIMER_IRQ);
- err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL);
+ err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
if (err) {
prom_printf("sun4d_init_timers: request_irq() failed with %d\n",
err);
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c
index 1333879..b0b92e7 100644
--- a/arch/sparc/kernel/sun4d_smp.c
+++ b/arch/sparc/kernel/sun4d_smp.c
@@ -10,11 +10,13 @@
#include <linux/profile.h>
#include <linux/delay.h>
#include <linux/cpu.h>
+#include <linux/clockchips.h>
#include <asm/sbi.h>
#include <asm/mmu.h>
#include <asm/tlbflush.h>
#include <asm/cacheflush.h>
+#include <asm/timer.h>
#include "kernel.h"
#include "irq.h"
@@ -33,7 +35,6 @@ static inline unsigned long sun4d_swap(volatile unsigned long *ptr, unsigned lon
}
static void smp4d_ipi_init(void);
-static void smp_setup_percpu_timer(void);
static unsigned char cpu_leds[32];
@@ -69,7 +70,7 @@ void __cpuinit smp4d_callin(void)
* to call the scheduler code.
*/
/* Get our local ticker going. */
- smp_setup_percpu_timer();
+ register_percpu_ce(cpuid);
calibrate_delay();
smp_store_cpu_info(cpuid);
@@ -122,7 +123,6 @@ void __init smp4d_boot_cpus(void)
smp4d_ipi_init();
if (boot_cpu_id)
current_set[0] = NULL;
- smp_setup_percpu_timer();
local_flush_cache_all();
}
@@ -363,6 +363,7 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs;
int cpu = hard_smp4d_processor_id();
+ struct clock_event_device *ce;
static int cpu_tick[NR_CPUS];
static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd };
@@ -378,28 +379,15 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs)
show_leds(cpu);
}
- profile_tick(CPU_PROFILING);
+ ce = &per_cpu(sparc32_clockevent, cpu);
- if (!--prof_counter(cpu)) {
- int user = user_mode(regs);
+ irq_enter();
+ ce->event_handler(ce);
+ irq_exit();
- irq_enter();
- update_process_times(user);
- irq_exit();
-
- prof_counter(cpu) = prof_multiplier(cpu);
- }
set_irq_regs(old_regs);
}
-static void __cpuinit smp_setup_percpu_timer(void)
-{
- int cpu = hard_smp4d_processor_id();
-
- prof_counter(cpu) = prof_multiplier(cpu) = 1;
- load_profile_irq(cpu, lvl14_resolution);
-}
-
void __init smp4d_blackbox_id(unsigned *addr)
{
int rd = *addr & 0x3e000000;
diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c
index e611651..0f8f3b6 100644
--- a/arch/sparc/kernel/sun4m_irq.c
+++ b/arch/sparc/kernel/sun4m_irq.c
@@ -318,9 +318,6 @@ struct sun4m_timer_global {
static struct sun4m_timer_global __iomem *timers_global;
-
-unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10);
-
static void sun4m_clear_clock_irq(void)
{
sbus_readl(&timers_global->l10_limit);
@@ -369,10 +366,11 @@ void sun4m_clear_profile_irq(int cpu)
static void sun4m_load_profile_irq(int cpu, unsigned int limit)
{
- sbus_writel(limit, &timers_percpu[cpu]->l14_limit);
+ unsigned int value = limit ? (limit + 1) << 9 : 0;
+ sbus_writel(value, &timers_percpu[cpu]->l14_limit);
}
-static void __init sun4m_init_timers(irq_handler_t counter_fn)
+static void __init sun4m_init_timers(void)
{
struct device_node *dp = of_find_node_by_name(NULL, "counter");
int i, err, len, num_cpu_timers;
@@ -402,13 +400,21 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn)
/* Every per-cpu timer works in timer mode */
sbus_writel(0x00000000, &timers_global->timer_config);
- sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit);
+#ifdef CONFIG_SMP
+ timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */
+ sparc_irq_config.features |= FEAT_L14_OS;
+#else
+ timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */
+ sparc_irq_config.features |= FEAT_L10_CE;
+#endif
+ sparc_irq_config.features |= FEAT_L10_CS;
+ sbus_writel(((timer_cs_period + 1) << 9), &timers_global->l10_limit);
master_l10_counter = &timers_global->l10_count;
irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ);
- err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL);
+ err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL);
if (err) {
printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n",
err);
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c
index 5947686..7447729 100644
--- a/arch/sparc/kernel/sun4m_smp.c
+++ b/arch/sparc/kernel/sun4m_smp.c
@@ -8,9 +8,11 @@
#include <linux/profile.h>
#include <linux/delay.h>
#include <linux/cpu.h>
+#include <linux/clockchips.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
+#include <asm/timer.h>
#include "irq.h"
#include "kernel.h"
@@ -30,7 +32,6 @@ swap_ulong(volatile unsigned long *ptr, unsigned long val)
}
static void smp4m_ipi_init(void);
-static void smp_setup_percpu_timer(void);
void __cpuinit smp4m_callin(void)
{
@@ -41,8 +42,7 @@ void __cpuinit smp4m_callin(void)
notify_cpu_starting(cpuid);
- /* Get our local ticker going. */
- smp_setup_percpu_timer();
+ register_percpu_ce(cpuid);
calibrate_delay();
smp_store_cpu_info(cpuid);
@@ -86,7 +86,7 @@ void __cpuinit smp4m_callin(void)
void __init smp4m_boot_cpus(void)
{
smp4m_ipi_init();
- smp_setup_percpu_timer();
+ sun4m_unmask_profile_irq();
local_flush_cache_all();
}
@@ -259,37 +259,25 @@ void smp4m_cross_call_irq(void)
void smp4m_percpu_timer_interrupt(struct pt_regs *regs)
{
struct pt_regs *old_regs;
+ struct clock_event_device *ce;
int cpu = smp_processor_id();
old_regs = set_irq_regs(regs);
- sun4m_clear_profile_irq(cpu);
+ ce = &per_cpu(sparc32_clockevent, cpu);
- profile_tick(CPU_PROFILING);
+ if (ce->mode & CLOCK_EVT_MODE_PERIODIC)
+ sun4m_clear_profile_irq(cpu);
+ else
+ load_profile_irq(cpu, 0); /* Is this needless? */
- if (!--prof_counter(cpu)) {
- int user = user_mode(regs);
+ irq_enter();
+ ce->event_handler(ce);
+ irq_exit();
- irq_enter();
- update_process_times(user);
- irq_exit();
-
- prof_counter(cpu) = prof_multiplier(cpu);
- }
set_irq_regs(old_regs);
}
-static void __cpuinit smp_setup_percpu_timer(void)
-{
- int cpu = smp_processor_id();
-
- prof_counter(cpu) = prof_multiplier(cpu) = 1;
- load_profile_irq(cpu, lvl14_resolution);
-
- if (cpu = boot_cpu_id)
- sun4m_unmask_profile_irq();
-}
-
static void __init smp4m_blackbox_id(unsigned *addr)
{
int rd = *addr & 0x3e000000;
diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c
index 1060e06..fc66bff 100644
--- a/arch/sparc/kernel/time_32.c
+++ b/arch/sparc/kernel/time_32.c
@@ -26,6 +26,8 @@
#include <linux/rtc.h>
#include <linux/rtc/m48t59.h>
#include <linux/timex.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioport.h>
@@ -45,9 +47,24 @@
#include <asm/page.h>
#include <asm/pcic.h>
#include <asm/irq_regs.h>
+#include <asm/setup.h>
#include "irq.h"
+static __cacheline_aligned_in_smp DEFINE_SEQLOCK(timer_cs_lock);
+static __volatile__ u64 timer_cs_internal_counter = 0;
+/* Tick period in cycles */
+unsigned int timer_cs_period;
+static char timer_cs_enabled = 0;
+u32 (*get_cycles_offset)(void);
+
+static struct clock_event_device timer_ce;
+static char timer_ce_enabled = 0;
+
+#ifdef CONFIG_SMP
+DEFINE_PER_CPU(struct clock_event_device, sparc32_clockevent);
+#endif
+
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
@@ -76,36 +93,166 @@ EXPORT_SYMBOL(profile_pc);
__volatile__ unsigned int *master_l10_counter;
-u32 (*do_arch_gettimeoffset)(void);
-
int update_persistent_clock(struct timespec now)
{
return set_rtc_mmss(now.tv_sec);
}
-/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "xtime_update()" routine every clocktick
- */
+irqreturn_t notrace timer_interrupt(int dummy, void *dev_id)
+{
+ if (timer_cs_enabled) {
+ write_seqlock(&timer_cs_lock);
+ timer_cs_internal_counter++;
+ clear_clock_irq();
+ write_sequnlock(&timer_cs_lock);
+ } else
+ clear_clock_irq();
-#define TICK_SIZE (tick_nsec / 1000)
+ if (timer_ce_enabled)
+ timer_ce.event_handler(&timer_ce);
-static irqreturn_t timer_interrupt(int dummy, void *dev_id)
+ return IRQ_HANDLED;
+}
+
+static void timer_ce_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *evt)
{
-#ifndef CONFIG_SMP
- profile_tick(CPU_PROFILING);
-#endif
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ case CLOCK_EVT_MODE_RESUME:
+ timer_ce_enabled = 1;
+ break;
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ timer_ce_enabled = 0;
+ break;
+ default:
+ break;
+ }
+ smp_mb();
+}
+
+static __init void setup_timer_ce(void)
+{
+ struct clock_event_device *ce = &timer_ce;
+
+ BUG_ON(smp_processor_id() != boot_cpu_id);
+
+ ce->name = "l10_ce";
+ ce->rating = 100;
+ ce->features = CLOCK_EVT_FEAT_PERIODIC;
+ ce->set_mode = timer_ce_set_mode;
+ ce->cpumask = cpu_possible_mask;
+ ce->shift = 32;
+ ce->mult = div_sc(SBUS_CLOCK_RATE, NSEC_PER_SEC,
+ ce->shift);
+ clockevents_register_device(ce);
+}
- clear_clock_irq();
+static u32 sbus_cycles_offset(void)
+{
+ unsigned int val, offset;
- xtime_update(1);
+ val = *master_l10_counter;
+ offset = (val >> 9) & 0x3fffff;
-#ifndef CONFIG_SMP
- update_process_times(user_mode(get_irq_regs()));
-#endif
- return IRQ_HANDLED;
+ /* Limit hit? */
+ if (val & 0x80000000)
+ offset += timer_cs_period;
+
+ return offset;
+}
+
+static cycle_t timer_cs_read(struct clocksource *cs)
+{
+ unsigned int seq, offset;
+ u64 cycles;
+
+ do {
+ seq = read_seqbegin(&timer_cs_lock);
+
+ cycles = timer_cs_internal_counter;
+ offset = get_cycles_offset();
+ } while (read_seqretry(&timer_cs_lock, seq));
+
+ /* Count absolute cycles */
+ cycles *= timer_cs_period;
+ cycles += offset;
+
+ return cycles;
}
+static struct clocksource timer_cs = {
+ .name = "l10_cs",
+ .rating = 100,
+ .read = timer_cs_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .shift = 2,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static __init int setup_timer_cs(void)
+{
+ timer_cs_enabled = 1;
+ /* Clock rate is 2MHz */
+ timer_cs.mult = clocksource_hz2mult(SBUS_CLOCK_RATE, timer_cs.shift);
+
+ return clocksource_register(&timer_cs);
+}
+
+#ifdef CONFIG_SMP
+static void percpu_ce_setup(enum clock_event_mode mode,
+ struct clock_event_device *evt)
+{
+ int cpu = __first_cpu(evt->cpumask);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ load_profile_irq(cpu, SBUS_CLOCK_RATE/HZ);
+ break;
+ case CLOCK_EVT_MODE_ONESHOT:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ case CLOCK_EVT_MODE_UNUSED:
+ load_profile_irq(cpu, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+static int percpu_ce_set_next_event(unsigned long delta,
+ struct clock_event_device *evt)
+{
+ int cpu = __first_cpu(evt->cpumask);
+ unsigned int next = (unsigned int)delta;
+
+ load_profile_irq(cpu, next);
+ return 0;
+}
+
+void register_percpu_ce(int cpu)
+{
+ struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu);
+ unsigned int features = CLOCK_EVT_FEAT_PERIODIC;
+
+ if (sparc_irq_config.features & FEAT_L14_OS)
+ features |= CLOCK_EVT_FEAT_ONESHOT;
+
+ ce->name = "l14_ce";
+ ce->rating = 200;
+ ce->features = features;
+ ce->set_mode = percpu_ce_setup;
+ ce->set_next_event = percpu_ce_set_next_event;
+ ce->cpumask = cpumask_of(cpu);
+ ce->shift = 32;
+ ce->mult = div_sc(SBUS_CLOCK_RATE, NSEC_PER_SEC,
+ ce->shift);
+ ce->max_delta_ns = clockevent_delta2ns(SBUS_CLOCK_RATE, ce);
+ ce->min_delta_ns = clockevent_delta2ns(100, ce);
+
+ clockevents_register_device(ce);
+}
+#endif
+
static unsigned char mostek_read_byte(struct device *dev, u32 ofs)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -196,42 +343,36 @@ static int __init clock_init(void)
*/
fs_initcall(clock_init);
-
-u32 sbus_do_gettimeoffset(void)
-{
- unsigned long val = *master_l10_counter;
- unsigned long usec = (val >> 10) & 0x1fffff;
-
- /* Limit hit? */
- if (val & 0x80000000)
- usec += 1000000 / HZ;
-
- return usec * 1000;
-}
-
-
-u32 arch_gettimeoffset(void)
+static void __init sparc32_late_time_init(void)
{
- if (unlikely(!do_arch_gettimeoffset))
- return 0;
- return do_arch_gettimeoffset();
+ if (sparc_irq_config.features & FEAT_L10_CE)
+ setup_timer_ce();
+ if (sparc_irq_config.features & FEAT_L10_CS)
+ setup_timer_cs();
+#ifdef CONFIG_SMP
+ register_percpu_ce(smp_processor_id());
+#endif
}
static void __init sbus_time_init(void)
{
- do_arch_gettimeoffset = sbus_do_gettimeoffset;
-
- btfixup();
+ get_cycles_offset = sbus_cycles_offset;
- sparc_irq_config.init_timers(timer_interrupt);
+ sparc_irq_config.init_timers();
}
void __init time_init(void)
{
+ btfixup();
+
+ sparc_irq_config.features = 0;
+
if (pcic_present())
pci_time_init();
else
sbus_time_init();
+
+ late_time_init = sparc32_late_time_init;
}
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH]sparc32: GENERIC_CLOCKEVENTS support 2012-02-07 22:09 [PATCH]sparc32: GENERIC_CLOCKEVENTS support Kirill Tkhai @ 2012-03-16 22:55 ` Kirill Tkhai 2012-04-04 9:18 ` Sam Ravnborg 2012-04-04 9:35 ` Konrad Eisele 2 siblings, 0 replies; 6+ messages in thread From: Kirill Tkhai @ 2012-03-16 22:55 UTC (permalink / raw) To: sparclinux This patch implements GENERIC_CLOCKEVENTS support on sparc32. The kernel uses l14 timers as clockevents. l10 timer is used as clocksource if platform master_l10_counter isn't constantly zero. The clocksource is continuous, so it's possible to use high resolution timers. l10 timer is also used as clockevent on UP configurations. This realization is for sun4m, sun4d, sun4c, microsparc-IIep and LEON platforms. The appropriate LEON changes was made by Konrad Eisele. Thanks! Thanks to Sam Ravnborg for the deep review, additions and comments. Signed-off-by: Tkhai Kirill <tkhai@yandex.ru> Signed-off-by: Konrad Eisele <konrad@gaisler.com> Reviewed-by: Sam Ravnborg <sam@ravnborg.org> --- arch/sparc/Kconfig | 7 +- arch/sparc/include/asm/cpudata_32.h | 1 - arch/sparc/include/asm/leon.h | 3 +- arch/sparc/include/asm/timer_32.h | 29 +++++ arch/sparc/include/asm/timex_32.h | 1 - arch/sparc/kernel/irq.h | 8 +- arch/sparc/kernel/kernel.h | 2 - arch/sparc/kernel/leon_kernel.c | 189 +++++++++++++++++++++++++++++- arch/sparc/kernel/leon_smp.c | 34 +----- arch/sparc/kernel/pcic.c | 48 ++++---- arch/sparc/kernel/smp_32.c | 21 +--- arch/sparc/kernel/sun4c_irq.c | 9 +- arch/sparc/kernel/sun4d_irq.c | 17 ++- arch/sparc/kernel/sun4d_smp.c | 28 ++--- arch/sparc/kernel/sun4m_irq.c | 20 ++- arch/sparc/kernel/sun4m_smp.c | 38 ++---- arch/sparc/kernel/time_32.c | 219 ++++++++++++++++++++++++++++------ 17 files changed, 488 insertions(+), 186 deletions(-) diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index ca5580e..71794fc 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -72,17 +72,12 @@ config BITS default 32 if SPARC32 default 64 if SPARC64 -config ARCH_USES_GETTIMEOFFSET - bool - default y if SPARC32 - config GENERIC_CMOS_UPDATE bool default y config GENERIC_CLOCKEVENTS - bool - default y if SPARC64 + def_bool y config IOMMU_HELPER bool diff --git a/arch/sparc/include/asm/cpudata_32.h b/arch/sparc/include/asm/cpudata_32.h index a4c5a93..0300d94 100644 --- a/arch/sparc/include/asm/cpudata_32.h +++ b/arch/sparc/include/asm/cpudata_32.h @@ -14,7 +14,6 @@ typedef struct { unsigned long udelay_val; unsigned long clock_tick; - unsigned int multiplier; unsigned int counter; #ifdef CONFIG_SMP unsigned int irq_resched_count; diff --git a/arch/sparc/include/asm/leon.h b/arch/sparc/include/asm/leon.h index a4e457f..9b9b24f 100644 --- a/arch/sparc/include/asm/leon.h +++ b/arch/sparc/include/asm/leon.h @@ -323,7 +323,7 @@ extern void leon_update_virq_handling(unsigned int virq, const char *name, int do_ack); extern void leon_clear_clock_irq(void); extern void leon_load_profile_irq(int cpu, unsigned int limit); -extern void leon_init_timers(irq_handler_t counter_fn); +extern void leon_init_timers(void); extern void leon_clear_clock_irq(void); extern void leon_load_profile_irq(int cpu, unsigned int limit); extern void leon_trans_init(struct device_node *dp); @@ -351,6 +351,7 @@ extern void init_IRQ(void); extern void cpu_panic(void); extern int __leon_processor_id(void); void leon_enable_irq_cpu(unsigned int irq_nr, unsigned int cpu); +extern void leon_register_percpu_ce(int cpu); extern irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused); extern unsigned int real_irq_entry[]; diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h index 2ec030e..492190d 100644 --- a/arch/sparc/include/asm/timer_32.h +++ b/arch/sparc/include/asm/timer_32.h @@ -8,10 +8,39 @@ #ifndef _SPARC_TIMER_H #define _SPARC_TIMER_H +#include <linux/irqreturn.h> +#include <linux/clocksource.h> +#include <asm-generic/percpu.h> #include <asm/system.h> /* For SUN4M_NCPUS */ #include <asm/btfixup.h> +#define SBUS_CLOCK_RATE 2000000 /* 2MHz */ +#define TIMER_VALUE_SHIFT 9 +#define TIMER_VALUE_MASK 0x3fffff +#define TIMER_LIMIT_BIT (1 << 31) /* Bit 31 in Counter-Timer register */ + +/* The counter timer register has the value offset by 9 bits. + * From sun4m manual: + * When a counter reaches the value in the corresponding limit register, + * the Limit bit is set and the counter is set to 500 nS (i.e. 0x00000200). + * + * To compensate for this add one to the value. + */ +static inline unsigned int timer_value(unsigned int value) +{ + return (value + 1) << TIMER_VALUE_SHIFT; +} + extern __volatile__ unsigned int *master_l10_counter; +extern unsigned int (*get_cycles_offset)(void); +extern unsigned int timer_cs_period; + +extern irqreturn_t notrace timer_interrupt(int dummy, void *dev_id); + +#ifdef CONFIG_SMP +DECLARE_PER_CPU(struct clock_event_device, sparc32_clockevent); +extern void register_percpu_ce(int cpu); +#endif /* FIXME: Make do_[gs]ettimeofday btfixup calls */ BTFIXUPDEF_CALL(int, bus_do_settimeofday, struct timespec *tv) diff --git a/arch/sparc/include/asm/timex_32.h b/arch/sparc/include/asm/timex_32.h index a254750..b6ccdb0 100644 --- a/arch/sparc/include/asm/timex_32.h +++ b/arch/sparc/include/asm/timex_32.h @@ -12,5 +12,4 @@ typedef unsigned long cycles_t; #define get_cycles() (0) -extern u32 (*do_arch_gettimeoffset)(void); #endif diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index 4285112..fc59aab 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h @@ -40,15 +40,21 @@ struct sun4m_irq_global { extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; extern struct sun4m_irq_global __iomem *sun4m_irq_global; +/* The following definitions describe the individual platform features: */ +#define FEAT_L10_CLOCKSOURCE (1 << 0) /* L10 timer is used as a clocksource */ +#define FEAT_L10_CLOCKEVENT (1 << 1) /* L10 timer is used as a clockevent */ +#define FEAT_L14_ONESHOT (1 << 2) /* L14 timer clockevent can oneshot */ + /* * Platform specific irq configuration * The individual platforms assign their platform * specifics in their init functions. */ struct sparc_irq_config { - void (*init_timers)(irq_handler_t); + void (*init_timers)(void); unsigned int (*build_device_irq)(struct platform_device *op, unsigned int real_irq); + unsigned int features; }; extern struct sparc_irq_config sparc_irq_config; diff --git a/arch/sparc/kernel/kernel.h b/arch/sparc/kernel/kernel.h index fd6c36b..8abbad3 100644 --- a/arch/sparc/kernel/kernel.h +++ b/arch/sparc/kernel/kernel.h @@ -47,8 +47,6 @@ extern void init_IRQ(void); extern void sun4c_init_IRQ(void); /* sun4m_irq.c */ -extern unsigned int lvl14_resolution; - extern void sun4m_init_IRQ(void); extern void sun4m_unmask_profile_irq(void); extern void sun4m_clear_profile_irq(int cpu); diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index a19c8a0..62afe3f 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c @@ -10,6 +10,8 @@ #include <linux/of_platform.h> #include <linux/interrupt.h> #include <linux/of_device.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> #include <asm/oplib.h> #include <asm/timer.h> @@ -27,12 +29,18 @@ struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */ struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */ +static __cacheline_aligned_in_smp DEFINE_SEQLOCK(leon_timer_cs_lock); +static __volatile__ u64 leon_timer_cs_internal_counter = 0; int leondebug_irq_disable; int leon_debug_irqout; static int dummy_master_l10_counter; unsigned long amba_system_id; static DEFINE_SPINLOCK(leon_irq_lock); +static char leon_timer_cs_enabled = 0; +#ifndef CONFIG_SMP +static char leon_timer_ce_enabled = 0; +#endif unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ int leon3_ticker_irq; /* Timer ticker IRQ */ @@ -250,7 +258,177 @@ void leon_update_virq_handling(unsigned int virq, irq_set_chip_data(virq, (void *)mask); } -void __init leon_init_timers(irq_handler_t counter_fn) +static u32 leon_cycles_offset(void) +{ + u32 rld, val, off; + rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld); + val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val); + off = rld - val; + return rld - val; +} + +static cycle_t leon_timer_cs_read(struct clocksource *cs) +{ + unsigned int seq, offset; + u64 cycles; + + do { + seq = read_seqbegin(&leon_timer_cs_lock); + cycles = leon_timer_cs_internal_counter; + offset = leon_cycles_offset(); + } while (read_seqretry(&leon_timer_cs_lock, seq)); + + cycles *= timer_cs_period; + cycles += offset; + return cycles; +} + +static struct clocksource leon_timer_cs = { + .name = "grtimer-cs", + .rating = 200, + .read = leon_timer_cs_read, + .mask = CLOCKSOURCE_MASK(32), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +#ifndef CONFIG_SMP + +static void leon_timer_ce_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_RESUME: + leon_timer_ce_enabled = 1; + break; + case CLOCK_EVT_MODE_SHUTDOWN: + leon_timer_ce_enabled = 0; + break; + default: + break; + } + smp_mb(); +} + +static struct clock_event_device leon_timer_ce = { + .name = "grtimer-ce", + .rating = 100, + .features = CLOCK_EVT_FEAT_PERIODIC, + .set_mode = leon_timer_ce_set_mode, + .shift = 32 +}; + +#endif /* !CONFIG_SMP */ + +static void __init leon_late_time_init(void) +{ + leon_timer_cs_enabled = 1; + + clocksource_register_hz(&leon_timer_cs, 1000000); + +#ifdef CONFIG_SMP + leon_register_percpu_ce(smp_processor_id()); +#else + BUG_ON(smp_processor_id() != boot_cpu_id); + leon_timer_ce.cpumask = cpu_possible_mask; + leon_timer_ce.mult = div_sc(1000000, NSEC_PER_SEC, + leon_timer_ce.shift); + clockevents_register_device(&leon_timer_ce); +#endif /* CONFIG_SMP */ +} + +/* clocksource irq, non-smp clockevent */ +irqreturn_t notrace leon_timer_interrupt(int dummy, void *dev_id) +{ + if (leon_timer_cs_enabled) { + write_seqlock(&leon_timer_cs_lock); + leon_timer_cs_internal_counter++; + write_sequnlock(&leon_timer_cs_lock); + } +#ifndef CONFIG_SMP + if (leon_timer_ce_enabled) { + if (leon_timer_ce.event_handler) + leon_timer_ce.event_handler(&leon_timer_ce); + } + +#endif + return IRQ_HANDLED; +} + +#ifdef CONFIG_SMP + +/* smp clockevent irq */ +irqreturn_t leon_percpu_timer_ce_interrupt(int irq, void *unused) +{ + struct clock_event_device *ce; + int cpu = smp_processor_id(); + + leon_clear_profile_irq(cpu); + + ce = &per_cpu(sparc32_clockevent, cpu); + + irq_enter(); + if (ce->event_handler) + ce->event_handler(ce); + irq_exit(); + + return IRQ_HANDLED; +} + +static void leon_percpu_ce_setup(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + int cpu = __first_cpu(evt->cpumask); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + leon_load_profile_irq(cpu, (1000000 / HZ)); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + leon_load_profile_irq(cpu, 0); + break; + default: + break; + } +} + +static int leon_percpu_ce_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + int cpu = __first_cpu(evt->cpumask); + unsigned int next = (unsigned int)delta; + leon_load_profile_irq(cpu, next); + return 0; +} + +void leon_register_percpu_ce(int cpu) +{ + struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); + unsigned int features = CLOCK_EVT_FEAT_PERIODIC; + + if (sparc_irq_config.features & FEAT_L14_OS) + features |= CLOCK_EVT_FEAT_ONESHOT; + + ce->name = "grtimer-ce"; + ce->rating = 200; + ce->features = features; + ce->set_mode = leon_percpu_ce_setup; + ce->set_next_event = leon_percpu_ce_set_next_event; + ce->cpumask = cpumask_of(cpu); + ce->shift = 32; + ce->mult = div_sc(1000000, NSEC_PER_SEC, + ce->shift); + ce->max_delta_ns = clockevent_delta2ns(1000000, ce); + ce->min_delta_ns = clockevent_delta2ns(100, ce); + + clockevents_register_device(ce); +} + +#endif /* CONFIG_SMP */ + +void __init leon_init_timers(void) { int irq, eirq; struct device_node *rootnp, *np, *nnp; @@ -260,6 +438,10 @@ void __init leon_init_timers(irq_handler_t counter_fn) int ampopts; int err; + late_time_init = leon_late_time_init; + get_cycles_offset = leon_cycles_offset; + timer_cs_period = 1000000 / HZ; + leondebug_irq_disable = 0; leon_debug_irqout = 0; master_l10_counter = (unsigned int *)&dummy_master_l10_counter; @@ -369,7 +551,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) leon_eirq_setup(eirq); irq = _leon_build_device_irq(NULL, leon3_gptimer_irq+leon3_gptimer_idx); - err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); + err = request_irq(irq, leon_timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { printk(KERN_ERR "unable to attach timer IRQ%d\n", irq); prom_halt(); @@ -401,7 +583,7 @@ void __init leon_init_timers(irq_handler_t counter_fn) /* Install per-cpu IRQ handler for broadcasted ticker */ irq = leon_build_device_irq(leon3_ticker_irq, handle_percpu_irq, "per-cpu", 0); - err = request_irq(irq, leon_percpu_timer_interrupt, + err = request_irq(irq, leon_percpu_timer_ce_interrupt, IRQF_PERCPU | IRQF_TIMER, "ticker", NULL); if (err) { @@ -428,7 +610,6 @@ void leon_clear_clock_irq(void) void leon_load_profile_irq(int cpu, unsigned int limit) { - BUG(); } void __init leon_trans_init(struct device_node *dp) diff --git a/arch/sparc/kernel/leon_smp.c b/arch/sparc/kernel/leon_smp.c index 1210fde..0bda9b1 100644 --- a/arch/sparc/kernel/leon_smp.c +++ b/arch/sparc/kernel/leon_smp.c @@ -23,6 +23,8 @@ #include <linux/pm.h> #include <linux/delay.h> #include <linux/gfp.h> +#include <linux/cpu.h> +#include <linux/clockchips.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> @@ -42,6 +44,7 @@ #include <asm/asi.h> #include <asm/leon.h> #include <asm/leon_amba.h> +#include <asm/timer.h> #include "kernel.h" @@ -68,8 +71,6 @@ static inline unsigned long do_swap(volatile unsigned long *ptr, return val; } -static void smp_setup_percpu_timer(void); - void __cpuinit leon_callin(void) { int cpuid = hard_smpleon_processor_id(); @@ -79,7 +80,7 @@ void __cpuinit leon_callin(void) leon_configure_cache_smp(); /* Get our local ticker going. */ - smp_setup_percpu_timer(); + leon_register_percpu_ce(cpuid); calibrate_delay(); smp_store_cpu_info(cpuid); @@ -196,7 +197,6 @@ void __init leon_boot_cpus(void) leon_smp_setbroadcast(1 << LEON3_IRQ_TICKER); leon_configure_cache_smp(); - smp_setup_percpu_timer(); local_flush_cache_all(); } @@ -489,32 +489,6 @@ void leon_cross_call_irq(void) ccall_info.processors_out[i] = 1; } -irqreturn_t leon_percpu_timer_interrupt(int irq, void *unused) -{ - int cpu = smp_processor_id(); - - leon_clear_profile_irq(cpu); - - profile_tick(CPU_PROFILING); - - if (!--prof_counter(cpu)) { - int user = user_mode(get_irq_regs()); - - update_process_times(user); - - prof_counter(cpu) = prof_multiplier(cpu); - } - - return IRQ_HANDLED; -} - -static void __init smp_setup_percpu_timer(void) -{ - int cpu = smp_processor_id(); - - prof_counter(cpu) = prof_multiplier(cpu) = 1; -} - void __init leon_blackbox_id(unsigned *addr) { int rd = *addr & 0x3e000000; diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index fcc148e..5dce17a 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -703,31 +703,28 @@ static void pcic_clear_clock_irq(void) pcic_timer_dummy = readl(pcic0.pcic_regs+PCI_SYS_LIMIT); } -static irqreturn_t pcic_timer_handler (int irq, void *h) +/* CPU frequency is 100 MHz, timer increments every 4 CPU clocks */ +#define USECS_PER_JIFFY (1000000 / HZ) +#define TICK_TIMER_LIMIT ((100 * 1000000 / 4) / HZ) + +static u32 pcic_cycles_offset(void) { - pcic_clear_clock_irq(); - xtime_update(1); -#ifndef CONFIG_SMP - update_process_times(user_mode(get_irq_regs())); -#endif - return IRQ_HANDLED; -} + u32 value, count; -#define USECS_PER_JIFFY 10000 /* We have 100HZ "standard" timer for sparc */ -#define TICK_TIMER_LIMIT ((100*1000000/4)/100) + value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER); + count = value & ~PCI_SYS_COUNTER_OVERFLOW; -u32 pci_gettimeoffset(void) -{ + if (value & PCI_SYS_COUNTER_OVERFLOW) + count += TICK_TIMER_LIMIT; /* - * We divide all by 100 + * We divide all by HZ * to have microsecond resolution and to avoid overflow */ - unsigned long count - readl(pcic0.pcic_regs+PCI_SYS_COUNTER) & ~PCI_SYS_COUNTER_OVERFLOW; - count = ((count/100)*USECS_PER_JIFFY) / (TICK_TIMER_LIMIT/100); - return count * 1000; -} + count = ((count / HZ) * USECS_PER_JIFFY) / (TICK_TIMER_LIMIT / HZ); + /* Coordinate with the fact that timer_cs rate is 2MHz */ + return count * 2; +} void __init pci_time_init(void) { @@ -736,9 +733,16 @@ void __init pci_time_init(void) int timer_irq, irq; int err; - do_arch_gettimeoffset = pci_gettimeoffset; - - btfixup(); +#ifndef CONFIG_SMP + /* + * It's in SBUS dimension, because timer_cs is in this dimension. + * We take into account this in pcic_cycles_offset() + */ + timer_cs_period = SBUS_CLOCK_RATE / HZ; + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; +#endif + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; + get_cycles_offset = pcic_cycles_offset; writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); /* PROM should set appropriate irq */ @@ -747,7 +751,7 @@ void __init pci_time_init(void) writel (PCI_COUNTER_IRQ_SET(timer_irq, 0), pcic->pcic_regs+PCI_COUNTER_IRQ); irq = pcic_build_device_irq(NULL, timer_irq); - err = request_irq(irq, pcic_timer_handler, + err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { prom_printf("time_init: unable to attach IRQ%d\n", timer_irq); diff --git a/arch/sparc/kernel/smp_32.c b/arch/sparc/kernel/smp_32.c index f671e7f..569a8a9 100644 --- a/arch/sparc/kernel/smp_32.c +++ b/arch/sparc/kernel/smp_32.c @@ -301,28 +301,9 @@ void smp_flush_sig_insns(struct mm_struct *mm, unsigned long insn_addr) local_flush_sig_insns(mm, insn_addr); } -extern unsigned int lvl14_resolution; - -/* /proc/profile writes can call this, don't __init it please. */ -static DEFINE_SPINLOCK(prof_setup_lock); - int setup_profiling_timer(unsigned int multiplier) { - int i; - unsigned long flags; - - /* Prevent level14 ticker IRQ flooding. */ - if((!multiplier) || (lvl14_resolution / multiplier) < 500) - return -EINVAL; - - spin_lock_irqsave(&prof_setup_lock, flags); - for_each_possible_cpu(i) { - load_profile_irq(i, lvl14_resolution / multiplier); - prof_multiplier(i) = multiplier; - } - spin_unlock_irqrestore(&prof_setup_lock, flags); - - return 0; + return -EINVAL; } void __init smp_prepare_cpus(unsigned int max_cpus) diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index f6bf25a..bbd1954 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -174,7 +174,7 @@ static void sun4c_load_profile_irq(int cpu, unsigned int limit) /* Errm.. not sure how to do this.. */ } -static void __init sun4c_init_timers(irq_handler_t counter_fn) +static void __init sun4c_init_timers(void) { const struct linux_prom_irqs *prom_irqs; struct device_node *dp; @@ -207,12 +207,15 @@ static void __init sun4c_init_timers(irq_handler_t counter_fn) * level 14 timer limit since we are letting the prom handle * them until we have a real console driver so L1-A works. */ - sbus_writel((((1000000/HZ) + 1) << 10), &sun4c_timers->l10_limit); + timer_cs_period = SBUS_CLOCK_RATE / HZ; + sparc_irq_config.features |+ FEAT_L10_CLOCKSOURCE | FEAT_L10_CLOCKEVENT; + sbus_writel(timer_value(timer_cs_period), &sun4c_timers->l10_limit); master_l10_counter = &sun4c_timers->l10_count; irq = sun4c_build_device_irq(NULL, prom_irqs[0].pri); - err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); + err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { prom_printf("sun4c_init_timers: request_irq() fails with %d\n", err); prom_halt(); diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 1d13c5b..5f8068a 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -282,7 +282,8 @@ static void sun4d_clear_clock_irq(void) static void sun4d_load_profile_irq(int cpu, unsigned int limit) { - bw_set_prof_limit(cpu, limit); + unsigned int value = limit ? timer_value(limit) : 0; + bw_set_prof_limit(cpu, value); } static void __init sun4d_load_profile_irqs(void) @@ -423,7 +424,7 @@ static void __init sun4d_fixup_trap_table(void) #endif } -static void __init sun4d_init_timers(irq_handler_t counter_fn) +static void __init sun4d_init_timers(void) { struct device_node *dp; struct resource res; @@ -466,12 +467,20 @@ static void __init sun4d_init_timers(irq_handler_t counter_fn) prom_halt(); } - sbus_writel((((1000000/HZ) + 1) << 10), &sun4d_timers->l10_timer_limit); +#ifdef CONFIG_SMP + timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ +#else + timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; +#endif + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; + sbus_writel(timer_value(timer_cs_period), + &sun4d_timers->l10_timer_limit); master_l10_counter = &sun4d_timers->l10_cur_count; irq = sun4d_build_timer_irq(board, SUN4D_TIMER_IRQ); - err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); + err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { prom_printf("sun4d_init_timers: request_irq() failed with %d\n", err); diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 1333879..b0b92e7 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -10,11 +10,13 @@ #include <linux/profile.h> #include <linux/delay.h> #include <linux/cpu.h> +#include <linux/clockchips.h> #include <asm/sbi.h> #include <asm/mmu.h> #include <asm/tlbflush.h> #include <asm/cacheflush.h> +#include <asm/timer.h> #include "kernel.h" #include "irq.h" @@ -33,7 +35,6 @@ static inline unsigned long sun4d_swap(volatile unsigned long *ptr, unsigned lon } static void smp4d_ipi_init(void); -static void smp_setup_percpu_timer(void); static unsigned char cpu_leds[32]; @@ -69,7 +70,7 @@ void __cpuinit smp4d_callin(void) * to call the scheduler code. */ /* Get our local ticker going. */ - smp_setup_percpu_timer(); + register_percpu_ce(cpuid); calibrate_delay(); smp_store_cpu_info(cpuid); @@ -122,7 +123,6 @@ void __init smp4d_boot_cpus(void) smp4d_ipi_init(); if (boot_cpu_id) current_set[0] = NULL; - smp_setup_percpu_timer(); local_flush_cache_all(); } @@ -363,6 +363,7 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs; int cpu = hard_smp4d_processor_id(); + struct clock_event_device *ce; static int cpu_tick[NR_CPUS]; static char led_mask[] = { 0xe, 0xd, 0xb, 0x7, 0xb, 0xd }; @@ -378,28 +379,15 @@ void smp4d_percpu_timer_interrupt(struct pt_regs *regs) show_leds(cpu); } - profile_tick(CPU_PROFILING); + ce = &per_cpu(sparc32_clockevent, cpu); - if (!--prof_counter(cpu)) { - int user = user_mode(regs); + irq_enter(); + ce->event_handler(ce); + irq_exit(); - irq_enter(); - update_process_times(user); - irq_exit(); - - prof_counter(cpu) = prof_multiplier(cpu); - } set_irq_regs(old_regs); } -static void __cpuinit smp_setup_percpu_timer(void) -{ - int cpu = hard_smp4d_processor_id(); - - prof_counter(cpu) = prof_multiplier(cpu) = 1; - load_profile_irq(cpu, lvl14_resolution); -} - void __init smp4d_blackbox_id(unsigned *addr) { int rd = *addr & 0x3e000000; diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index e611651..a81553e 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -318,9 +318,6 @@ struct sun4m_timer_global { static struct sun4m_timer_global __iomem *timers_global; - -unsigned int lvl14_resolution = (((1000000/HZ) + 1) << 10); - static void sun4m_clear_clock_irq(void) { sbus_readl(&timers_global->l10_limit); @@ -369,10 +366,11 @@ void sun4m_clear_profile_irq(int cpu) static void sun4m_load_profile_irq(int cpu, unsigned int limit) { - sbus_writel(limit, &timers_percpu[cpu]->l14_limit); + unsigned int value = limit ? timer_value(limit) : 0; + sbus_writel(value, &timers_percpu[cpu]->l14_limit); } -static void __init sun4m_init_timers(irq_handler_t counter_fn) +static void __init sun4m_init_timers(void) { struct device_node *dp = of_find_node_by_name(NULL, "counter"); int i, err, len, num_cpu_timers; @@ -402,13 +400,21 @@ static void __init sun4m_init_timers(irq_handler_t counter_fn) /* Every per-cpu timer works in timer mode */ sbus_writel(0x00000000, &timers_global->timer_config); - sbus_writel((((1000000/HZ) + 1) << 10), &timers_global->l10_limit); +#ifdef CONFIG_SMP + timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ + sparc_irq_config.features |= FEAT_L14_ONESHOT; +#else + timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; +#endif + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; + sbus_writel(timer_value(timer_cs_period), &timers_global->l10_limit); master_l10_counter = &timers_global->l10_count; irq = sun4m_build_device_irq(NULL, SUN4M_TIMER_IRQ); - err = request_irq(irq, counter_fn, IRQF_TIMER, "timer", NULL); + err = request_irq(irq, timer_interrupt, IRQF_TIMER, "timer", NULL); if (err) { printk(KERN_ERR "sun4m_init_timers: Register IRQ error %d.\n", err); diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 5947686..7447729 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -8,9 +8,11 @@ #include <linux/profile.h> #include <linux/delay.h> #include <linux/cpu.h> +#include <linux/clockchips.h> #include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <asm/timer.h> #include "irq.h" #include "kernel.h" @@ -30,7 +32,6 @@ swap_ulong(volatile unsigned long *ptr, unsigned long val) } static void smp4m_ipi_init(void); -static void smp_setup_percpu_timer(void); void __cpuinit smp4m_callin(void) { @@ -41,8 +42,7 @@ void __cpuinit smp4m_callin(void) notify_cpu_starting(cpuid); - /* Get our local ticker going. */ - smp_setup_percpu_timer(); + register_percpu_ce(cpuid); calibrate_delay(); smp_store_cpu_info(cpuid); @@ -86,7 +86,7 @@ void __cpuinit smp4m_callin(void) void __init smp4m_boot_cpus(void) { smp4m_ipi_init(); - smp_setup_percpu_timer(); + sun4m_unmask_profile_irq(); local_flush_cache_all(); } @@ -259,37 +259,25 @@ void smp4m_cross_call_irq(void) void smp4m_percpu_timer_interrupt(struct pt_regs *regs) { struct pt_regs *old_regs; + struct clock_event_device *ce; int cpu = smp_processor_id(); old_regs = set_irq_regs(regs); - sun4m_clear_profile_irq(cpu); + ce = &per_cpu(sparc32_clockevent, cpu); - profile_tick(CPU_PROFILING); + if (ce->mode & CLOCK_EVT_MODE_PERIODIC) + sun4m_clear_profile_irq(cpu); + else + load_profile_irq(cpu, 0); /* Is this needless? */ - if (!--prof_counter(cpu)) { - int user = user_mode(regs); + irq_enter(); + ce->event_handler(ce); + irq_exit(); - irq_enter(); - update_process_times(user); - irq_exit(); - - prof_counter(cpu) = prof_multiplier(cpu); - } set_irq_regs(old_regs); } -static void __cpuinit smp_setup_percpu_timer(void) -{ - int cpu = smp_processor_id(); - - prof_counter(cpu) = prof_multiplier(cpu) = 1; - load_profile_irq(cpu, lvl14_resolution); - - if (cpu = boot_cpu_id) - sun4m_unmask_profile_irq(); -} - static void __init smp4m_blackbox_id(unsigned *addr) { int rd = *addr & 0x3e000000; diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index 1060e06..ce95587 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -26,6 +26,8 @@ #include <linux/rtc.h> #include <linux/rtc/m48t59.h> #include <linux/timex.h> +#include <linux/clocksource.h> +#include <linux/clockchips.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/ioport.h> @@ -45,9 +47,24 @@ #include <asm/page.h> #include <asm/pcic.h> #include <asm/irq_regs.h> +#include <asm/setup.h> #include "irq.h" +static __cacheline_aligned_in_smp DEFINE_SEQLOCK(timer_cs_lock); +static __volatile__ u64 timer_cs_internal_counter = 0; +/* Tick period in cycles */ +unsigned int timer_cs_period; +static char timer_cs_enabled = 0; +u32 (*get_cycles_offset)(void); + +static struct clock_event_device timer_ce; +static char timer_ce_enabled = 0; + +#ifdef CONFIG_SMP +DEFINE_PER_CPU(struct clock_event_device, sparc32_clockevent); +#endif + DEFINE_SPINLOCK(rtc_lock); EXPORT_SYMBOL(rtc_lock); @@ -76,36 +93,166 @@ EXPORT_SYMBOL(profile_pc); __volatile__ unsigned int *master_l10_counter; -u32 (*do_arch_gettimeoffset)(void); - int update_persistent_clock(struct timespec now) { return set_rtc_mmss(now.tv_sec); } -/* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "xtime_update()" routine every clocktick - */ +irqreturn_t notrace timer_interrupt(int dummy, void *dev_id) +{ + if (timer_cs_enabled) { + write_seqlock(&timer_cs_lock); + timer_cs_internal_counter++; + clear_clock_irq(); + write_sequnlock(&timer_cs_lock); + } else + clear_clock_irq(); -#define TICK_SIZE (tick_nsec / 1000) + if (timer_ce_enabled) + timer_ce.event_handler(&timer_ce); -static irqreturn_t timer_interrupt(int dummy, void *dev_id) + return IRQ_HANDLED; +} + +static void timer_ce_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) { -#ifndef CONFIG_SMP - profile_tick(CPU_PROFILING); -#endif + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + case CLOCK_EVT_MODE_RESUME: + timer_ce_enabled = 1; + break; + case CLOCK_EVT_MODE_SHUTDOWN: + timer_ce_enabled = 0; + break; + default: + break; + } + smp_mb(); +} - clear_clock_irq(); +static __init void setup_timer_ce(void) +{ + struct clock_event_device *ce = &timer_ce; + + BUG_ON(smp_processor_id() != boot_cpu_id); + + ce->name = "l10_ce"; + ce->rating = 100; + ce->features = CLOCK_EVT_FEAT_PERIODIC; + ce->set_mode = timer_ce_set_mode; + ce->cpumask = cpu_possible_mask; + ce->shift = 32; + ce->mult = div_sc(SBUS_CLOCK_RATE, NSEC_PER_SEC, + ce->shift); + clockevents_register_device(ce); +} - xtime_update(1); +static u32 sbus_cycles_offset(void) +{ + unsigned int val, offset; -#ifndef CONFIG_SMP - update_process_times(user_mode(get_irq_regs())); -#endif - return IRQ_HANDLED; + val = *master_l10_counter; + offset = (val >> TIMER_VALUE_SHIFT) & TIMER_VALUE_MASK; + + /* Limit hit? */ + if (val & TIMER_LIMIT_BIT) + offset += timer_cs_period; + + return offset; } +static cycle_t timer_cs_read(struct clocksource *cs) +{ + unsigned int seq, offset; + u64 cycles; + + do { + seq = read_seqbegin(&timer_cs_lock); + + cycles = timer_cs_internal_counter; + offset = get_cycles_offset(); + } while (read_seqretry(&timer_cs_lock, seq)); + + /* Count absolute cycles */ + cycles *= timer_cs_period; + cycles += offset; + + return cycles; +} + +static struct clocksource timer_cs = { + .name = "l10_cs", + .rating = 100, + .read = timer_cs_read, + .mask = CLOCKSOURCE_MASK(64), + .shift = 2, + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static __init int setup_timer_cs(void) +{ + timer_cs_enabled = 1; + /* Clock rate is 2MHz */ + timer_cs.mult = clocksource_hz2mult(SBUS_CLOCK_RATE, timer_cs.shift); + + return clocksource_register(&timer_cs); +} + +#ifdef CONFIG_SMP +static void percpu_ce_setup(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + int cpu = __first_cpu(evt->cpumask); + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + load_profile_irq(cpu, SBUS_CLOCK_RATE / HZ); + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + load_profile_irq(cpu, 0); + break; + default: + break; + } +} + +static int percpu_ce_set_next_event(unsigned long delta, + struct clock_event_device *evt) +{ + int cpu = __first_cpu(evt->cpumask); + unsigned int next = (unsigned int)delta; + + load_profile_irq(cpu, next); + return 0; +} + +void register_percpu_ce(int cpu) +{ + struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); + unsigned int features = CLOCK_EVT_FEAT_PERIODIC; + + if (sparc_irq_config.features & FEAT_L14_ONESHOT) + features |= CLOCK_EVT_FEAT_ONESHOT; + + ce->name = "l14_ce"; + ce->rating = 200; + ce->features = features; + ce->set_mode = percpu_ce_setup; + ce->set_next_event = percpu_ce_set_next_event; + ce->cpumask = cpumask_of(cpu); + ce->shift = 32; + ce->mult = div_sc(SBUS_CLOCK_RATE, NSEC_PER_SEC, + ce->shift); + ce->max_delta_ns = clockevent_delta2ns(SBUS_CLOCK_RATE, ce); + ce->min_delta_ns = clockevent_delta2ns(100, ce); + + clockevents_register_device(ce); +} +#endif + static unsigned char mostek_read_byte(struct device *dev, u32 ofs) { struct platform_device *pdev = to_platform_device(dev); @@ -196,38 +343,32 @@ static int __init clock_init(void) */ fs_initcall(clock_init); - -u32 sbus_do_gettimeoffset(void) -{ - unsigned long val = *master_l10_counter; - unsigned long usec = (val >> 10) & 0x1fffff; - - /* Limit hit? */ - if (val & 0x80000000) - usec += 1000000 / HZ; - - return usec * 1000; -} - - -u32 arch_gettimeoffset(void) +static void __init sparc32_late_time_init(void) { - if (unlikely(!do_arch_gettimeoffset)) - return 0; - return do_arch_gettimeoffset(); + if (sparc_irq_config.features & FEAT_L10_CLOCKEVENT) + setup_timer_ce(); + if (sparc_irq_config.features & FEAT_L10_CLOCKSOURCE) + setup_timer_cs(); +#ifdef CONFIG_SMP + register_percpu_ce(smp_processor_id()); +#endif } static void __init sbus_time_init(void) { - do_arch_gettimeoffset = sbus_do_gettimeoffset; - - btfixup(); + get_cycles_offset = sbus_cycles_offset; - sparc_irq_config.init_timers(timer_interrupt); + sparc_irq_config.init_timers(); } void __init time_init(void) { + btfixup(); + + sparc_irq_config.features = 0; + + late_time_init = sparc32_late_time_init; + if (pcic_present()) pci_time_init(); else ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH]sparc32: GENERIC_CLOCKEVENTS support 2012-02-07 22:09 [PATCH]sparc32: GENERIC_CLOCKEVENTS support Kirill Tkhai 2012-03-16 22:55 ` Kirill Tkhai @ 2012-04-04 9:18 ` Sam Ravnborg 2012-04-04 9:35 ` Konrad Eisele 2 siblings, 0 replies; 6+ messages in thread From: Sam Ravnborg @ 2012-04-04 9:18 UTC (permalink / raw) To: sparclinux Hi all. I have started to look into this combined patch-set. The leon part puzzeles me a bit... Ir looks like the leon parts duplicate in several cases what we already have in time_32.c. But we prefer to use the same base functions for all platforms when it is possible. > diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c > index a19c8a0..62afe3f 100644 > --- a/arch/sparc/kernel/leon_kernel.c > +++ b/arch/sparc/kernel/leon_kernel.c > @@ -10,6 +10,8 @@ > #include <linux/of_platform.h> > #include <linux/interrupt.h> > #include <linux/of_device.h> > +#include <linux/clocksource.h> > +#include <linux/clockchips.h> > > #include <asm/oplib.h> > #include <asm/timer.h> > @@ -27,12 +29,18 @@ > struct leon3_irqctrl_regs_map *leon3_irqctrl_regs; /* interrupt controller base address */ > struct leon3_gptimer_regs_map *leon3_gptimer_regs; /* timer controller base address */ > > +static __cacheline_aligned_in_smp DEFINE_SEQLOCK(leon_timer_cs_lock); > +static __volatile__ u64 leon_timer_cs_internal_counter = 0; time_32.c already define these.. > int leondebug_irq_disable; > int leon_debug_irqout; > static int dummy_master_l10_counter; > unsigned long amba_system_id; > static DEFINE_SPINLOCK(leon_irq_lock); > > +static char leon_timer_cs_enabled = 0; > +#ifndef CONFIG_SMP > +static char leon_timer_ce_enabled = 0; > +#endif Likewise. > unsigned long leon3_gptimer_irq; /* interrupt controller irq number */ > unsigned long leon3_gptimer_idx; /* Timer Index (0..6) within Timer Core */ > int leon3_ticker_irq; /* Timer ticker IRQ */ > @@ -250,7 +258,177 @@ void leon_update_virq_handling(unsigned int virq, > irq_set_chip_data(virq, (void *)mask); > } > > -void __init leon_init_timers(irq_handler_t counter_fn) > +static u32 leon_cycles_offset(void) > +{ > + u32 rld, val, off; > + rld = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].rld); > + val = LEON3_BYPASS_LOAD_PA(&leon3_gptimer_regs->e[leon3_gptimer_idx].val); > + off = rld - val; > + return rld - val; > +} OK > + > +static cycle_t leon_timer_cs_read(struct clocksource *cs) > +{ > + unsigned int seq, offset; > + u64 cycles; > + > + do { > + seq = read_seqbegin(&leon_timer_cs_lock); > + cycles = leon_timer_cs_internal_counter; > + offset = leon_cycles_offset(); > + } while (read_seqretry(&leon_timer_cs_lock, seq)); > + > + cycles *= timer_cs_period; > + cycles += offset; > + return cycles; > +} This is an exact copy of time_32.c:timer_cs_read - except that is uses the leon specific variables. > + > +static struct clocksource leon_timer_cs = { > + .name = "grtimer-cs", > + .rating = 200, > + .read = leon_timer_cs_read, > + .mask = CLOCKSOURCE_MASK(32), > + .flags = CLOCK_SOURCE_IS_CONTINUOUS, > +}; > + > +#ifndef CONFIG_SMP > + > +static void leon_timer_ce_set_mode(enum clock_event_mode mode, > + struct clock_event_device *evt) > +{ > + switch (mode) { > + case CLOCK_EVT_MODE_PERIODIC: > + case CLOCK_EVT_MODE_RESUME: > + leon_timer_ce_enabled = 1; > + break; > + case CLOCK_EVT_MODE_SHUTDOWN: > + leon_timer_ce_enabled = 0; > + break; > + default: > + break; > + } > + smp_mb(); > +} Copy of time_32.c:timer_ce_set_mode > + > +static struct clock_event_device leon_timer_ce = { > + .name = "grtimer-ce", > + .rating = 100, > + .features = CLOCK_EVT_FEAT_PERIODIC, > + .set_mode = leon_timer_ce_set_mode, > + .shift = 32 > +}; OK > + > +#endif /* !CONFIG_SMP */ > + > +static void __init leon_late_time_init(void) > +{ > + leon_timer_cs_enabled = 1; > + > + clocksource_register_hz(&leon_timer_cs, 1000000); > + > +#ifdef CONFIG_SMP > + leon_register_percpu_ce(smp_processor_id()); > +#else > + BUG_ON(smp_processor_id() != boot_cpu_id); > + leon_timer_ce.cpumask = cpu_possible_mask; > + leon_timer_ce.mult = div_sc(1000000, NSEC_PER_SEC, > + leon_timer_ce.shift); > + clockevents_register_device(&leon_timer_ce); > +#endif /* CONFIG_SMP */ > +} > + > +/* clocksource irq, non-smp clockevent */ > +irqreturn_t notrace leon_timer_interrupt(int dummy, void *dev_id) > +{ > + if (leon_timer_cs_enabled) { > + write_seqlock(&leon_timer_cs_lock); > + leon_timer_cs_internal_counter++; > + write_sequnlock(&leon_timer_cs_lock); > + } > +#ifndef CONFIG_SMP > + if (leon_timer_ce_enabled) { > + if (leon_timer_ce.event_handler) > + leon_timer_ce.event_handler(&leon_timer_ce); > + } > + > +#endif > + return IRQ_HANDLED; > +} Copy of time_32.c:timer_interrupt Are there any special reason for this duplication that I have missed? I will look a bit deeper into the patch later today or one of the following days. Sam ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH]sparc32: GENERIC_CLOCKEVENTS support 2012-02-07 22:09 [PATCH]sparc32: GENERIC_CLOCKEVENTS support Kirill Tkhai 2012-03-16 22:55 ` Kirill Tkhai 2012-04-04 9:18 ` Sam Ravnborg @ 2012-04-04 9:35 ` Konrad Eisele 2 siblings, 0 replies; 6+ messages in thread From: Konrad Eisele @ 2012-04-04 9:35 UTC (permalink / raw) To: sparclinux > Copy of time_32.c:timer_interrupt > > Are there any special reason for this duplication that I have missed? The names are maybe similar but the parameters to time_ce and time_cs are different, the frequency cannot be patched for the Leon case with a lot of modifications in the generic sparc code. -- Konrad ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] sparc32: GENERIC_CLOCKEVENTS support @ 2012-02-15 17:02 Sam Ravnborg 2012-02-16 7:47 ` Daniel Hellstrom 0 siblings, 1 reply; 6+ messages in thread From: Sam Ravnborg @ 2012-02-15 17:02 UTC (permalink / raw) To: sparclinux Hi Kirill. On Wed, Feb 08, 2012 at 02:09:36AM +0400, Kirill Tkhai wrote: > This patch implements GENERIC_CLOCKEVENTS support on sparc32. > > The kernel uses l14 timers as clockevents. l10 timer is used > as clocksource if platform master_l10_counter isn't constantly > zero. The clocksource is continuous, so it's possible to use > high resolution timers. l10 timer is also used as clockevent > on UP configurations. > > This realization is for sun4m, sun4d, sun4c and microsparc-IIep > platforms. I have looked through the code and I had a couple of minor issues with it. See attached patch which go on top of yours. From a functionality point of view I saw no problems, but I am not intiminate with neither sparc timers nor clockevents. I have tried out the patch on my sun4m box, and /proc/timer_list showed the new timers. I did not try your test-program - but I can do so if you like. > It's necessary to make appropriate changes for LEON > part (it should not have l10 based cs and ce in case of SMP > because of LEON's master_l10_counter is zero). How do we go forward with Leon now? Daniel - can you help here? > Thanks to Sam Ravnborg for the review and comments. You can add my: Reviewed-by: Sam Ravnborg <sam@ravnborg.org> Sam diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 3ee5d1f..71794fc 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -77,8 +77,7 @@ config GENERIC_CMOS_UPDATE default y config GENERIC_CLOCKEVENTS - bool - default y + def_bool y config IOMMU_HELPER bool diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h index 3a5c24b..492190d 100644 --- a/arch/sparc/include/asm/timer_32.h +++ b/arch/sparc/include/asm/timer_32.h @@ -15,6 +15,21 @@ #include <asm/btfixup.h> #define SBUS_CLOCK_RATE 2000000 /* 2MHz */ +#define TIMER_VALUE_SHIFT 9 +#define TIMER_VALUE_MASK 0x3fffff +#define TIMER_LIMIT_BIT (1 << 31) /* Bit 31 in Counter-Timer register */ + +/* The counter timer register has the value offset by 9 bits. + * From sun4m manual: + * When a counter reaches the value in the corresponding limit register, + * the Limit bit is set and the counter is set to 500 nS (i.e. 0x00000200). + * + * To compensate for this add one to the value. + */ +static inline unsigned int timer_value(unsigned int value) +{ + return (value + 1) << TIMER_VALUE_SHIFT; +} extern __volatile__ unsigned int *master_l10_counter; extern unsigned int (*get_cycles_offset)(void); diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h index cbfba9f..fc59aab 100644 --- a/arch/sparc/kernel/irq.h +++ b/arch/sparc/kernel/irq.h @@ -40,10 +40,10 @@ struct sun4m_irq_global { extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; extern struct sun4m_irq_global __iomem *sun4m_irq_global; -/* The following definitions describe the individual platform features: */ -#define FEAT_L10_CS (1 << 0) /* L10 timer is used as a clocksource */ -#define FEAT_L10_CE (1 << 1) /* L10 timer is used as a clockevent */ -#define FEAT_L14_OS (1 << 2) /* L14 timer clockevent can oneshot mode */ +/* The following definitions describe the individual platform features: */ +#define FEAT_L10_CLOCKSOURCE (1 << 0) /* L10 timer is used as a clocksource */ +#define FEAT_L10_CLOCKEVENT (1 << 1) /* L10 timer is used as a clockevent */ +#define FEAT_L14_ONESHOT (1 << 2) /* L14 timer clockevent can oneshot */ /* * Platform specific irq configuration diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 090f6af..5dce17a 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -710,7 +710,7 @@ static void pcic_clear_clock_irq(void) static u32 pcic_cycles_offset(void) { u32 value, count; - + value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER); count = value & ~PCI_SYS_COUNTER_OVERFLOW; @@ -739,9 +739,9 @@ void __init pci_time_init(void) * We take into account this in pcic_cycles_offset() */ timer_cs_period = SBUS_CLOCK_RATE / HZ; - sparc_irq_config.features |= FEAT_L10_CE; + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; #endif - sparc_irq_config.features |= FEAT_L10_CS; + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; get_cycles_offset = pcic_cycles_offset; writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index caaa7b9..bbd1954 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -208,8 +208,9 @@ static void __init sun4c_init_timers(void) * them until we have a real console driver so L1-A works. */ timer_cs_period = SBUS_CLOCK_RATE / HZ; - sparc_irq_config.features |= (FEAT_L10_CS | FEAT_L10_CE); - sbus_writel(((timer_cs_period + 1) << 9), &sun4c_timers->l10_limit); + sparc_irq_config.features |+ FEAT_L10_CLOCKSOURCE | FEAT_L10_CLOCKEVENT; + sbus_writel(timer_value(timer_cs_period), &sun4c_timers->l10_limit); master_l10_counter = &sun4c_timers->l10_count; diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 7fb9437..5f8068a 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -282,7 +282,7 @@ static void sun4d_clear_clock_irq(void) static void sun4d_load_profile_irq(int cpu, unsigned int limit) { - unsigned int value = limit ? (limit + 1) << 9 : 0; + unsigned int value = limit ? timer_value(limit) : 0; bw_set_prof_limit(cpu, value); } @@ -471,10 +471,11 @@ static void __init sun4d_init_timers(void) timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ #else timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ - sparc_irq_config.features |= FEAT_L10_CE; + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; #endif - sparc_irq_config.features |= FEAT_L10_CS; - sbus_writel(((timer_cs_period + 1) << 9), &sun4d_timers->l10_timer_limit); + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; + sbus_writel(timer_value(timer_cs_period), + &sun4d_timers->l10_timer_limit); master_l10_counter = &sun4d_timers->l10_cur_count; diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 0f8f3b6..a81553e 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -366,7 +366,7 @@ void sun4m_clear_profile_irq(int cpu) static void sun4m_load_profile_irq(int cpu, unsigned int limit) { - unsigned int value = limit ? (limit + 1) << 9 : 0; + unsigned int value = limit ? timer_value(limit) : 0; sbus_writel(value, &timers_percpu[cpu]->l14_limit); } @@ -402,13 +402,13 @@ static void __init sun4m_init_timers(void) #ifdef CONFIG_SMP timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ - sparc_irq_config.features |= FEAT_L14_OS; + sparc_irq_config.features |= FEAT_L14_ONESHOT; #else timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ - sparc_irq_config.features |= FEAT_L10_CE; + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; #endif - sparc_irq_config.features |= FEAT_L10_CS; - sbus_writel(((timer_cs_period + 1) << 9), &timers_global->l10_limit); + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; + sbus_writel(timer_value(timer_cs_period), &timers_global->l10_limit); master_l10_counter = &timers_global->l10_count; diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c index fc66bff..bc27bb5 100644 --- a/arch/sparc/kernel/time_32.c +++ b/arch/sparc/kernel/time_32.c @@ -153,10 +153,10 @@ static u32 sbus_cycles_offset(void) unsigned int val, offset; val = *master_l10_counter; - offset = (val >> 9) & 0x3fffff; + offset = (val >> TIMER_VALUE_SHIFT) & TIMER_VALUE_MASK; /* Limit hit? */ - if (val & 0x80000000) + if (val & TIMER_LIMIT_BIT) offset += timer_cs_period; return offset; @@ -207,7 +207,7 @@ static void percpu_ce_setup(enum clock_event_mode mode, switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - load_profile_irq(cpu, SBUS_CLOCK_RATE/HZ); + load_profile_irq(cpu, SBUS_CLOCK_RATE / HZ); break; case CLOCK_EVT_MODE_ONESHOT: case CLOCK_EVT_MODE_SHUTDOWN: @@ -234,7 +234,7 @@ void register_percpu_ce(int cpu) struct clock_event_device *ce = &per_cpu(sparc32_clockevent, cpu); unsigned int features = CLOCK_EVT_FEAT_PERIODIC; - if (sparc_irq_config.features & FEAT_L14_OS) + if (sparc_irq_config.features & FEAT_L14_ONESHOT) features |= CLOCK_EVT_FEAT_ONESHOT; ce->name = "l14_ce"; @@ -345,9 +345,9 @@ fs_initcall(clock_init); static void __init sparc32_late_time_init(void) { - if (sparc_irq_config.features & FEAT_L10_CE) + if (sparc_irq_config.features & FEAT_L10_CLOCKEVENT) setup_timer_ce(); - if (sparc_irq_config.features & FEAT_L10_CS) + if (sparc_irq_config.features & FEAT_L10_CLOCKSOURCE) setup_timer_cs(); #ifdef CONFIG_SMP register_percpu_ce(smp_processor_id()); ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] sparc32: GENERIC_CLOCKEVENTS support 2012-02-15 17:02 [PATCH] sparc32: " Sam Ravnborg @ 2012-02-16 7:47 ` Daniel Hellstrom 0 siblings, 0 replies; 6+ messages in thread From: Daniel Hellstrom @ 2012-02-16 7:47 UTC (permalink / raw) To: sparclinux On 02/15/2012 06:02 PM, Sam Ravnborg wrote: > Hi Kirill. > > On Wed, Feb 08, 2012 at 02:09:36AM +0400, Kirill Tkhai wrote: >> This patch implements GENERIC_CLOCKEVENTS support on sparc32. >> >> The kernel uses l14 timers as clockevents. l10 timer is used >> as clocksource if platform master_l10_counter isn't constantly >> zero. The clocksource is continuous, so it's possible to use >> high resolution timers. l10 timer is also used as clockevent >> on UP configurations. >> >> This realization is for sun4m, sun4d, sun4c and microsparc-IIep >> platforms. > I have looked through the code and I had a couple of minor > issues with it. See attached patch which go on top of yours. > > > From a functionality point of view I saw no problems, > but I am not intiminate with neither sparc timers > nor clockevents. > > I have tried out the patch on my sun4m box, > and /proc/timer_list showed the new timers. > I did not try your test-program - but I can do so if you like. > > >> It's necessary to make appropriate changes for LEON >> part (it should not have l10 based cs and ce in case of SMP >> because of LEON's master_l10_counter is zero). > How do we go forward with Leon now? > Daniel - can you help here? I have started looking at the code, but I haven't got into the clocksource/event interface yet. As I understand the master_l10_counter points to a timer counter register (similar on all SUN SPARCs) which can be access to get the current time in nanoseconds between ticks. However on LEON I think it is faulty declared to always point to zero, on LEON one can get an exact usec value from the LEON timer (GPTIMER), however the calculation to get usec is different than from pcic/sbus. I try to understand what impact that has, probably that gettimeofday() only has "tick" accuracy. It look as a LEON bug to me. Do you know if there are documentation available on the clocksource/event interface? Daniel >> Thanks to Sam Ravnborg for the review and comments. > You can add my: > Reviewed-by: Sam Ravnborg<sam@ravnborg.org> > > Sam > > diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig > index 3ee5d1f..71794fc 100644 > --- a/arch/sparc/Kconfig > +++ b/arch/sparc/Kconfig > @@ -77,8 +77,7 @@ config GENERIC_CMOS_UPDATE > default y > > config GENERIC_CLOCKEVENTS > - bool > - default y > + def_bool y > > config IOMMU_HELPER > bool > diff --git a/arch/sparc/include/asm/timer_32.h b/arch/sparc/include/asm/timer_32.h > index 3a5c24b..492190d 100644 > --- a/arch/sparc/include/asm/timer_32.h > +++ b/arch/sparc/include/asm/timer_32.h > @@ -15,6 +15,21 @@ > #include<asm/btfixup.h> > > #define SBUS_CLOCK_RATE 2000000 /* 2MHz */ > +#define TIMER_VALUE_SHIFT 9 > +#define TIMER_VALUE_MASK 0x3fffff > +#define TIMER_LIMIT_BIT (1<< 31) /* Bit 31 in Counter-Timer register */ > + > +/* The counter timer register has the value offset by 9 bits. > + * From sun4m manual: > + * When a counter reaches the value in the corresponding limit register, > + * the Limit bit is set and the counter is set to 500 nS (i.e. 0x00000200). > + * > + * To compensate for this add one to the value. > + */ > +static inline unsigned int timer_value(unsigned int value) > +{ > + return (value + 1)<< TIMER_VALUE_SHIFT; > +} > > extern __volatile__ unsigned int *master_l10_counter; > extern unsigned int (*get_cycles_offset)(void); > diff --git a/arch/sparc/kernel/irq.h b/arch/sparc/kernel/irq.h > index cbfba9f..fc59aab 100644 > --- a/arch/sparc/kernel/irq.h > +++ b/arch/sparc/kernel/irq.h > @@ -40,10 +40,10 @@ struct sun4m_irq_global { > extern struct sun4m_irq_percpu __iomem *sun4m_irq_percpu[SUN4M_NCPUS]; > extern struct sun4m_irq_global __iomem *sun4m_irq_global; > > -/* The following definitions describe the individual platform features: */ > -#define FEAT_L10_CS (1<< 0) /* L10 timer is used as a clocksource */ > -#define FEAT_L10_CE (1<< 1) /* L10 timer is used as a clockevent */ > -#define FEAT_L14_OS (1<< 2) /* L14 timer clockevent can oneshot mode */ > +/* The following definitions describe the individual platform features: */ > +#define FEAT_L10_CLOCKSOURCE (1<< 0) /* L10 timer is used as a clocksource */ > +#define FEAT_L10_CLOCKEVENT (1<< 1) /* L10 timer is used as a clockevent */ > +#define FEAT_L14_ONESHOT (1<< 2) /* L14 timer clockevent can oneshot */ > > /* > * Platform specific irq configuration > diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c > index 090f6af..5dce17a 100644 > --- a/arch/sparc/kernel/pcic.c > +++ b/arch/sparc/kernel/pcic.c > @@ -710,7 +710,7 @@ static void pcic_clear_clock_irq(void) > static u32 pcic_cycles_offset(void) > { > u32 value, count; > - > + > value = readl(pcic0.pcic_regs + PCI_SYS_COUNTER); > count = value& ~PCI_SYS_COUNTER_OVERFLOW; > > @@ -739,9 +739,9 @@ void __init pci_time_init(void) > * We take into account this in pcic_cycles_offset() > */ > timer_cs_period = SBUS_CLOCK_RATE / HZ; > - sparc_irq_config.features |= FEAT_L10_CE; > + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; > #endif > - sparc_irq_config.features |= FEAT_L10_CS; > + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; > get_cycles_offset = pcic_cycles_offset; > > writel (TICK_TIMER_LIMIT, pcic->pcic_regs+PCI_SYS_LIMIT); > diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c > index caaa7b9..bbd1954 100644 > --- a/arch/sparc/kernel/sun4c_irq.c > +++ b/arch/sparc/kernel/sun4c_irq.c > @@ -208,8 +208,9 @@ static void __init sun4c_init_timers(void) > * them until we have a real console driver so L1-A works. > */ > timer_cs_period = SBUS_CLOCK_RATE / HZ; > - sparc_irq_config.features |= (FEAT_L10_CS | FEAT_L10_CE); > - sbus_writel(((timer_cs_period + 1)<< 9),&sun4c_timers->l10_limit); > + sparc_irq_config.features |> + FEAT_L10_CLOCKSOURCE | FEAT_L10_CLOCKEVENT; > + sbus_writel(timer_value(timer_cs_period),&sun4c_timers->l10_limit); > > master_l10_counter =&sun4c_timers->l10_count; > > diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c > index 7fb9437..5f8068a 100644 > --- a/arch/sparc/kernel/sun4d_irq.c > +++ b/arch/sparc/kernel/sun4d_irq.c > @@ -282,7 +282,7 @@ static void sun4d_clear_clock_irq(void) > > static void sun4d_load_profile_irq(int cpu, unsigned int limit) > { > - unsigned int value = limit ? (limit + 1)<< 9 : 0; > + unsigned int value = limit ? timer_value(limit) : 0; > bw_set_prof_limit(cpu, value); > } > > @@ -471,10 +471,11 @@ static void __init sun4d_init_timers(void) > timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ > #else > timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ > - sparc_irq_config.features |= FEAT_L10_CE; > + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; > #endif > - sparc_irq_config.features |= FEAT_L10_CS; > - sbus_writel(((timer_cs_period + 1)<< 9),&sun4d_timers->l10_timer_limit); > + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; > + sbus_writel(timer_value(timer_cs_period), > + &sun4d_timers->l10_timer_limit); > > master_l10_counter =&sun4d_timers->l10_cur_count; > > diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c > index 0f8f3b6..a81553e 100644 > --- a/arch/sparc/kernel/sun4m_irq.c > +++ b/arch/sparc/kernel/sun4m_irq.c > @@ -366,7 +366,7 @@ void sun4m_clear_profile_irq(int cpu) > > static void sun4m_load_profile_irq(int cpu, unsigned int limit) > { > - unsigned int value = limit ? (limit + 1)<< 9 : 0; > + unsigned int value = limit ? timer_value(limit) : 0; > sbus_writel(value,&timers_percpu[cpu]->l14_limit); > } > > @@ -402,13 +402,13 @@ static void __init sun4m_init_timers(void) > > #ifdef CONFIG_SMP > timer_cs_period = SBUS_CLOCK_RATE * 2; /* 2 seconds */ > - sparc_irq_config.features |= FEAT_L14_OS; > + sparc_irq_config.features |= FEAT_L14_ONESHOT; > #else > timer_cs_period = SBUS_CLOCK_RATE / HZ; /* 1/HZ sec */ > - sparc_irq_config.features |= FEAT_L10_CE; > + sparc_irq_config.features |= FEAT_L10_CLOCKEVENT; > #endif > - sparc_irq_config.features |= FEAT_L10_CS; > - sbus_writel(((timer_cs_period + 1)<< 9),&timers_global->l10_limit); > + sparc_irq_config.features |= FEAT_L10_CLOCKSOURCE; > + sbus_writel(timer_value(timer_cs_period),&timers_global->l10_limit); > > master_l10_counter =&timers_global->l10_count; > > diff --git a/arch/sparc/kernel/time_32.c b/arch/sparc/kernel/time_32.c > index fc66bff..bc27bb5 100644 > --- a/arch/sparc/kernel/time_32.c > +++ b/arch/sparc/kernel/time_32.c > @@ -153,10 +153,10 @@ static u32 sbus_cycles_offset(void) > unsigned int val, offset; > > val = *master_l10_counter; > - offset = (val>> 9)& 0x3fffff; > + offset = (val>> TIMER_VALUE_SHIFT)& TIMER_VALUE_MASK; > > /* Limit hit? */ > - if (val& 0x80000000) > + if (val& TIMER_LIMIT_BIT) > offset += timer_cs_period; > > return offset; > @@ -207,7 +207,7 @@ static void percpu_ce_setup(enum clock_event_mode mode, > > switch (mode) { > case CLOCK_EVT_MODE_PERIODIC: > - load_profile_irq(cpu, SBUS_CLOCK_RATE/HZ); > + load_profile_irq(cpu, SBUS_CLOCK_RATE / HZ); > break; > case CLOCK_EVT_MODE_ONESHOT: > case CLOCK_EVT_MODE_SHUTDOWN: > @@ -234,7 +234,7 @@ void register_percpu_ce(int cpu) > struct clock_event_device *ce =&per_cpu(sparc32_clockevent, cpu); > unsigned int features = CLOCK_EVT_FEAT_PERIODIC; > > - if (sparc_irq_config.features& FEAT_L14_OS) > + if (sparc_irq_config.features& FEAT_L14_ONESHOT) > features |= CLOCK_EVT_FEAT_ONESHOT; > > ce->name = "l14_ce"; > @@ -345,9 +345,9 @@ fs_initcall(clock_init); > > static void __init sparc32_late_time_init(void) > { > - if (sparc_irq_config.features& FEAT_L10_CE) > + if (sparc_irq_config.features& FEAT_L10_CLOCKEVENT) > setup_timer_ce(); > - if (sparc_irq_config.features& FEAT_L10_CS) > + if (sparc_irq_config.features& FEAT_L10_CLOCKSOURCE) > setup_timer_cs(); > #ifdef CONFIG_SMP > register_percpu_ce(smp_processor_id()); > > ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2012-04-04 9:35 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2012-02-07 22:09 [PATCH]sparc32: GENERIC_CLOCKEVENTS support Kirill Tkhai 2012-03-16 22:55 ` Kirill Tkhai 2012-04-04 9:18 ` Sam Ravnborg 2012-04-04 9:35 ` Konrad Eisele -- strict thread matches above, loose matches on Subject: below -- 2012-02-15 17:02 [PATCH] sparc32: " Sam Ravnborg 2012-02-16 7:47 ` Daniel Hellstrom
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.