linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6 v6] SGI RTC: add clocksource/clockevent driver and generic vector
@ 2009-03-03 15:10 Dimitri Sivanich
  2009-03-03 15:13 ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Dimitri Sivanich
  0 siblings, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:10 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

The following patches provide a driver for synchronized RTC clocksource and
clockevents for SGI systems, as well as a generic system interrupt.

With these patches, a module can be installed that registers the system-wide
synchronized RTC clocksource and timers as both a clocksource and clockevents
device running in high resolution mode.


Subject: [PATCH 1/6 v6] SGI RTC: add generic system interrupt
Subject: [PATCH 2/6 v6] SGI RTC: export clocksource_unregister
Subject: [PATCH 3/6 v6] SGI RTC: export clockevents_register_device
Subject: [PATCH 4/6 v6] SGI RTC: export schedule_on_each_cpu
Subject: [PATCH 5/6 v6] SGI RTC: loop through installed UV blades
Subject: [PATCH 6/6 v6] SGI RTC: add clocksource driver


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 1/6 v6] SGI RTC: add generic system interrupt
  2009-03-03 15:10 [PATCH 0/6 v6] SGI RTC: add clocksource/clockevent driver and generic vector Dimitri Sivanich
@ 2009-03-03 15:13 ` Dimitri Sivanich
  2009-03-03 15:15   ` [PATCH 2/6 v6] SGI RTC: export clocksource_unregister Dimitri Sivanich
  2009-03-03 15:34   ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Ingo Molnar
  0 siblings, 2 replies; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:13 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

This patch allocates a system interrupt vector for various platform
specific uses.

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

Made changes suggested by tglx and refreshed for latest -tip.

 arch/x86/include/asm/hardirq.h     |    1 
 arch/x86/include/asm/hw_irq.h      |    3 +
 arch/x86/include/asm/irq.h         |    9 ++++
 arch/x86/include/asm/irq_vectors.h |    5 ++
 arch/x86/kernel/entry_64.S         |    2 
 arch/x86/kernel/irq.c              |   12 +++++
 arch/x86/kernel/irqinit_64.c       |   74 +++++++++++++++++++++++++++++++++
 7 files changed, 106 insertions(+)

Index: linux/arch/x86/kernel/entry_64.S
===================================================================
--- linux.orig/arch/x86/kernel/entry_64.S	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/kernel/entry_64.S	2009-03-02 08:20:52.000000000 -0600
@@ -984,6 +984,8 @@ apicinterrupt UV_BAU_MESSAGE \
 #endif
 apicinterrupt LOCAL_TIMER_VECTOR \
 	apic_timer_interrupt smp_apic_timer_interrupt
+apicinterrupt GENERIC_INTERRUPT_VECTOR \
+	generic_interrupt smp_generic_interrupt
 
 #ifdef CONFIG_SMP
 apicinterrupt INVALIDATE_TLB_VECTOR_START+0 \
Index: linux/arch/x86/kernel/irqinit_64.c
===================================================================
--- linux.orig/arch/x86/kernel/irqinit_64.c	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/kernel/irqinit_64.c	2009-03-02 08:20:52.000000000 -0600
@@ -22,6 +22,7 @@
 #include <asm/desc.h>
 #include <asm/apic.h>
 #include <asm/i8259.h>
+#include <asm/idle.h>
 
 /*
  * ISA PIC or low IO-APIC triggered (INTA-cycle or APIC) interrupts:
@@ -105,6 +106,76 @@ static void __init init_ISA_irqs(void)
 
 void init_IRQ(void) __attribute__((weak, alias("native_init_IRQ")));
 
+
+/* Function pointer for generic interrupt vector handling */
+static void (*generic_interrupt_extension)(void);
+static char generic_show_string[28];
+static char generic_show_prefix[6];
+
+int is_generic_interrupt_registered()
+{
+	if (generic_interrupt_extension)
+		return 1;
+	else
+		return 0;
+}
+
+char *generic_interrupt_string(void)
+{
+	return generic_show_string;
+}
+
+char *generic_interrupt_prefix(void)
+{
+	return generic_show_prefix;
+}
+
+int register_generic_interrupt_extension(void (*fn)(void),
+					 const char *show_prefix,
+					 const char *show_string)
+{
+	if (generic_interrupt_extension)
+		return -EBUSY;
+
+	snprintf(generic_show_prefix, sizeof(generic_show_prefix),
+		"%3.3s: ", show_prefix ? show_prefix : "GEN");
+
+	snprintf(generic_show_string, sizeof(generic_show_string),
+		"  %s", show_string ?  show_string : "Generic interrupts");
+
+	generic_interrupt_extension = fn;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(register_generic_interrupt_extension);
+
+void unregister_generic_interrupt_extension(void)
+{
+	if (generic_interrupt_extension)
+		generic_interrupt_extension = NULL;
+}
+EXPORT_SYMBOL_GPL(unregister_generic_interrupt_extension);
+
+void smp_generic_interrupt(struct pt_regs *regs)
+{
+	struct pt_regs *old_regs = set_irq_regs(regs);
+
+	ack_APIC_irq();
+
+	exit_idle();
+
+	irq_enter();
+
+	inc_irq_stat(generic_irqs);
+
+	if (generic_interrupt_extension)
+		generic_interrupt_extension();
+
+	irq_exit();
+
+	set_irq_regs(old_regs);
+}
+
 static void __init smp_intr_init(void)
 {
 #ifdef CONFIG_SMP
@@ -147,6 +218,9 @@ static void __init apic_intr_init(void)
 	/* self generated IPI for local APIC timer */
 	alloc_intr_gate(LOCAL_TIMER_VECTOR, apic_timer_interrupt);
 
+	/* generic IPI for platform specific use */
+	alloc_intr_gate(GENERIC_INTERRUPT_VECTOR, generic_interrupt);
+
 	/* IPI vectors for APIC spurious and error interrupts */
 	alloc_intr_gate(SPURIOUS_APIC_VECTOR, spurious_interrupt);
 	alloc_intr_gate(ERROR_APIC_VECTOR, error_interrupt);
Index: linux/arch/x86/include/asm/irq_vectors.h
===================================================================
--- linux.orig/arch/x86/include/asm/irq_vectors.h	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/include/asm/irq_vectors.h	2009-03-02 08:20:52.000000000 -0600
@@ -112,6 +112,11 @@
 #define LOCAL_PERF_VECTOR		0xee
 
 /*
+ * Generic system vector for platform specific use
+ */
+#define GENERIC_INTERRUPT_VECTOR	0xed
+
+/*
  * First APIC vector available to drivers: (vectors 0x30-0xee) we
  * start at 0x31(0x41) to spread out vectors evenly between priority
  * levels. (0x80 is the syscall vector)
Index: linux/arch/x86/include/asm/hw_irq.h
===================================================================
--- linux.orig/arch/x86/include/asm/hw_irq.h	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/include/asm/hw_irq.h	2009-03-02 08:20:52.000000000 -0600
@@ -27,6 +27,9 @@
 
 /* Interrupt handlers registered during init_IRQ */
 extern void apic_timer_interrupt(void);
+#ifdef CONFIG_X86_64
+extern void generic_interrupt(void);
+#endif
 extern void error_interrupt(void);
 extern void perf_counter_interrupt(void);
 
Index: linux/arch/x86/include/asm/irq.h
===================================================================
--- linux.orig/arch/x86/include/asm/irq.h	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/include/asm/irq.h	2009-03-02 08:20:52.000000000 -0600
@@ -42,6 +42,15 @@ extern bool handle_irq(unsigned irq, str
 
 extern unsigned int do_IRQ(struct pt_regs *regs);
 
+#ifdef CONFIG_X86_64
+extern int is_generic_interrupt_registered(void);
+extern char *generic_interrupt_string(void);
+extern char *generic_interrupt_prefix(void);
+extern int register_generic_interrupt_extension(void (*fn)(void), const char *,
+							const char *);
+extern void unregister_generic_interrupt_extension(void);
+#endif
+
 /* Interrupt vector management */
 extern DECLARE_BITMAP(used_vectors, NR_VECTORS);
 extern int vector_used_by_percpu_irq(unsigned int vector);
Index: linux/arch/x86/kernel/irq.c
===================================================================
--- linux.orig/arch/x86/kernel/irq.c	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/kernel/irq.c	2009-03-02 08:20:52.000000000 -0600
@@ -60,6 +60,14 @@ static int show_other_interrupts(struct 
 		seq_printf(p, "%10u ", irq_stats(j)->apic_perf_irqs);
 	seq_printf(p, "  Performance counter interrupts\n");
 #endif
+#ifdef CONFIG_X86_64
+	if (is_generic_interrupt_registered()) {
+		seq_printf(p, "%s", generic_interrupt_prefix());
+		for_each_online_cpu(j)
+			seq_printf(p, "%10u ", irq_stats(j)->generic_irqs);
+		seq_printf(p, "%s\n", generic_interrupt_string());
+	}
+#endif
 #ifdef CONFIG_SMP
 	seq_printf(p, "RES: ");
 	for_each_online_cpu(j)
@@ -168,6 +176,10 @@ u64 arch_irq_stat_cpu(unsigned int cpu)
 	sum += irq_stats(cpu)->apic_timer_irqs;
 	sum += irq_stats(cpu)->apic_perf_irqs;
 #endif
+#ifdef CONFIG_X86_64
+	if (is_generic_interrupt_registered())
+		sum += irq_stats(cpu)->generic_irqs;
+#endif
 #ifdef CONFIG_SMP
 	sum += irq_stats(cpu)->irq_resched_count;
 	sum += irq_stats(cpu)->irq_call_count;
Index: linux/arch/x86/include/asm/hardirq.h
===================================================================
--- linux.orig/arch/x86/include/asm/hardirq.h	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/include/asm/hardirq.h	2009-03-02 08:20:52.000000000 -0600
@@ -22,6 +22,7 @@ typedef struct {
 	unsigned int irq_thermal_count;
 # ifdef CONFIG_X86_64
 	unsigned int irq_threshold_count;
+	unsigned int generic_irqs;
 # endif
 #endif
 } ____cacheline_aligned irq_cpustat_t;

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 2/6 v6] SGI RTC: export clocksource_unregister
  2009-03-03 15:13 ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Dimitri Sivanich
@ 2009-03-03 15:15   ` Dimitri Sivanich
  2009-03-03 15:16     ` [PATCH 3/6 v6] SGI RTC: export clockevents_register_device Dimitri Sivanich
  2009-03-03 15:34   ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Ingo Molnar
  1 sibling, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:15 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

Export clocksource_unregister().

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

 kernel/time/clocksource.c |    1 +
 1 file changed, 1 insertion(+)

Index: linux/kernel/time/clocksource.c
===================================================================
--- linux.orig/kernel/time/clocksource.c	2009-03-02 08:20:32.000000000 -0600
+++ linux/kernel/time/clocksource.c	2009-03-02 08:20:53.000000000 -0600
@@ -370,6 +370,7 @@ void clocksource_unregister(struct clock
 	next_clocksource = select_clocksource();
 	spin_unlock_irqrestore(&clocksource_lock, flags);
 }
+EXPORT_SYMBOL_GPL(clocksource_unregister);
 
 #ifdef CONFIG_SYSFS
 /**

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 3/6 v6] SGI RTC: export clockevents_register_device
  2009-03-03 15:15   ` [PATCH 2/6 v6] SGI RTC: export clocksource_unregister Dimitri Sivanich
@ 2009-03-03 15:16     ` Dimitri Sivanich
  2009-03-03 15:18       ` [PATCH 4/6 v6] SGI RTC: export schedule_on_each_cpu Dimitri Sivanich
  0 siblings, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:16 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

Export clockevents_register_device().

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

 kernel/time/clockevents.c |    1 +
 1 file changed, 1 insertion(+)

Index: linux/kernel/time/clockevents.c
===================================================================
--- linux.orig/kernel/time/clockevents.c	2009-03-02 08:20:32.000000000 -0600
+++ linux/kernel/time/clockevents.c	2009-03-02 08:20:56.000000000 -0600
@@ -187,6 +187,7 @@ void clockevents_register_device(struct 
 
 	spin_unlock(&clockevents_lock);
 }
+EXPORT_SYMBOL_GPL(clockevents_register_device);
 
 /*
  * Noop handler when we shut down an event device

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 4/6 v6] SGI RTC: export schedule_on_each_cpu
  2009-03-03 15:16     ` [PATCH 3/6 v6] SGI RTC: export clockevents_register_device Dimitri Sivanich
@ 2009-03-03 15:18       ` Dimitri Sivanich
  2009-03-03 15:20         ` [PATCH 5/6 v6] SGI RTC: loop through installed UV blades Dimitri Sivanich
  0 siblings, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:18 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

Export schedule_on_each_cpu().

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

 kernel/workqueue.c |    1 +
 1 file changed, 1 insertion(+)

Index: linux/kernel/workqueue.c
===================================================================
--- linux.orig/kernel/workqueue.c	2009-03-02 08:20:32.000000000 -0600
+++ linux/kernel/workqueue.c	2009-03-02 08:20:57.000000000 -0600
@@ -705,6 +705,7 @@ int schedule_on_each_cpu(work_func_t fun
 	free_percpu(works);
 	return 0;
 }
+EXPORT_SYMBOL_GPL(schedule_on_each_cpu);
 
 void flush_scheduled_work(void)
 {

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 5/6 v6] SGI RTC: loop through installed UV blades
  2009-03-03 15:18       ` [PATCH 4/6 v6] SGI RTC: export schedule_on_each_cpu Dimitri Sivanich
@ 2009-03-03 15:20         ` Dimitri Sivanich
  2009-03-03 15:22           ` [PATCH 6/6 v6] SGI RTC: add clocksource driver Dimitri Sivanich
  0 siblings, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:20 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

Add macro to loop through each possible blade.

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

 arch/x86/include/asm/uv/uv_hub.h |    4 ++++
 1 file changed, 4 insertions(+)

Index: linux/arch/x86/include/asm/uv/uv_hub.h
===================================================================
--- linux.orig/arch/x86/include/asm/uv/uv_hub.h	2009-03-02 08:20:20.000000000 -0600
+++ linux/arch/x86/include/asm/uv/uv_hub.h	2009-03-02 08:20:57.000000000 -0600
@@ -179,6 +179,10 @@ DECLARE_PER_CPU(struct uv_hub_info_s, __
 #define LOCAL_BUS_BASE		0x1c00000
 #define LOCAL_BUS_SIZE		(4 * 1024 * 1024)
 
+/* Loop through all installed blades */
+#define for_each_possible_blade(bid)                    \
+	for ((bid) = 0; (bid) < uv_num_possible_blades(); (bid)++)
+
 /*
  * System Controller Interface Reg
  *

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH 6/6 v6] SGI RTC: add clocksource driver
  2009-03-03 15:20         ` [PATCH 5/6 v6] SGI RTC: loop through installed UV blades Dimitri Sivanich
@ 2009-03-03 15:22           ` Dimitri Sivanich
  2009-03-03 15:35             ` Ingo Molnar
  2009-03-03 20:01             ` john stultz
  0 siblings, 2 replies; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 15:22 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

This patch provides a driver for SGI RTC clocks and timers.

This provides a high resolution clock and timer source using the SGI
system-wide synchronized RTC clock/timer hardware.

Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>

---

Implemented suggested changes and refreshed for latest -tip.

 drivers/clocksource/Makefile |    1 
 drivers/clocksource/rtc_uv.c |  381 +++++++++++++++++++++++++++++++++++++++
 drivers/misc/Kconfig         |    9 
 3 files changed, 391 insertions(+)

Index: linux/drivers/clocksource/Makefile
===================================================================
--- linux.orig/drivers/clocksource/Makefile	2009-03-02 15:02:55.000000000 -0600
+++ linux/drivers/clocksource/Makefile	2009-03-02 15:03:11.000000000 -0600
@@ -2,3 +2,4 @@ obj-$(CONFIG_ATMEL_TCB_CLKSRC)	+= tcb_cl
 obj-$(CONFIG_X86_CYCLONE_TIMER)	+= cyclone.o
 obj-$(CONFIG_X86_PM_TIMER)	+= acpi_pm.o
 obj-$(CONFIG_SCx200HR_TIMER)	+= scx200_hrt.o
+obj-$(CONFIG_SGI_RTC_TIMER)	+= rtc_uv.o
Index: linux/drivers/misc/Kconfig
===================================================================
--- linux.orig/drivers/misc/Kconfig	2009-03-02 15:02:55.000000000 -0600
+++ linux/drivers/misc/Kconfig	2009-03-02 15:03:11.000000000 -0600
@@ -225,6 +225,15 @@ config DELL_LAPTOP
 	This driver adds support for rfkill and backlight control to Dell
 	laptops.
 
+config SGI_RTC_TIMER
+	tristate "SGI RTC driver"
+	depends on GENERIC_CLOCKEVENTS_BUILD
+	depends on X86_UV && SMP
+	default m
+	---help---
+	This option enables RTC event handling and adds the systemwide RTC
+	as a high resolution clock source for SGI systems.
+
 source "drivers/misc/c2port/Kconfig"
 source "drivers/misc/eeprom/Kconfig"
 
Index: linux/drivers/clocksource/rtc_uv.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux/drivers/clocksource/rtc_uv.c	2009-03-02 15:16:17.000000000 -0600
@@ -0,0 +1,381 @@
+/*
+ * SGI RTC clock/timer routines.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ *  Copyright (c) 2009 Silicon Graphics, Inc.  All Rights Reserved.
+ *  Copyright (c) Dimitri Sivanich
+ */
+#include <linux/module.h>
+#include <linux/clocksource.h>
+#include <linux/clockchips.h>
+#include <asm/genapic.h>
+#include <asm/uv/uv.h>
+#include <asm/uv/uv_mmrs.h>
+#include <asm/uv/uv_hub.h>
+#include <asm/uv/bios.h>
+
+MODULE_LICENSE("GPL");
+
+#define RTC_NAME		"sgi_rtc"
+
+static cycle_t uv_read_rtc(void);
+static int uv_rtc_next_event(unsigned long, struct clock_event_device *);
+static void uv_rtc_timer_setup(enum clock_event_mode,
+				struct clock_event_device *);
+
+static struct clocksource clocksource_uv = {
+	.name		= RTC_NAME,
+	.rating		= 400,
+	.read		= uv_read_rtc,
+	.mask		= (cycle_t)UVH_RTC_REAL_TIME_CLOCK_MASK,
+	.shift		= 10,
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static struct clock_event_device clock_event_device_uv = {
+	.name		= RTC_NAME,
+	.features	= CLOCK_EVT_FEAT_ONESHOT,
+	.shift		= 20,
+	.rating		= 400,
+	.irq		= -1,
+	.set_next_event	= uv_rtc_next_event,
+	.set_mode	= uv_rtc_timer_setup,
+	.event_handler	= NULL,
+};
+
+static DEFINE_PER_CPU(struct clock_event_device, cpu_ced);
+
+/* There is one of these allocated per node */
+struct uv_rtc_timer_head {
+	spinlock_t lock;
+	int next_cpu;	/* next cpu waiting for timer, local node relative */
+	int ncpus;	/* number of cpus on this node */
+	struct {
+		int lcpu;	/* systemwide logical cpu number */
+		u64 expires;	/* next timer expiration for this cpu */
+	} cpu[1];
+};
+
+/*
+ * Access to uv_rtc_timer_head via blade id.
+ */
+static struct uv_rtc_timer_head **blade_info __read_mostly;
+
+/*
+ * Hardware interface routines
+ */
+
+/* Send IPIs to another node */
+static void uv_rtc_send_IPI(int cpu)
+{
+	unsigned long apicid, val;
+	int pnode;
+
+	apicid = per_cpu(x86_cpu_to_apicid, cpu);
+	pnode = uv_apicid_to_pnode(apicid);
+	val = (1UL << UVH_IPI_INT_SEND_SHFT) |
+	      (apicid << UVH_IPI_INT_APIC_ID_SHFT) |
+	      (GENERIC_INTERRUPT_VECTOR << UVH_IPI_INT_VECTOR_SHFT);
+
+	uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
+}
+
+/* Check for an RTC interrupt pending */
+static int uv_intr_pending(int pnode)
+{
+	return uv_read_global_mmr64(pnode, UVH_EVENT_OCCURRED0) &
+		UVH_EVENT_OCCURRED0_RTC1_MASK;
+}
+
+/* Setup interrupt and return non-zero if early expiration occurred. */
+static int uv_setup_intr(int cpu, u64 expires)
+{
+	u64 val;
+	int pnode = uv_cpu_to_pnode(cpu);
+
+	uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG,
+		UVH_RTC1_INT_CONFIG_M_MASK);
+	uv_write_global_mmr64(pnode, UVH_INT_CMPB, -1L);
+
+	uv_write_global_mmr64(pnode, UVH_EVENT_OCCURRED0_ALIAS,
+		UVH_EVENT_OCCURRED0_RTC1_MASK);
+
+	val = (GENERIC_INTERRUPT_VECTOR << UVH_RTC1_INT_CONFIG_VECTOR_SHFT) |
+		((u64)cpu_physical_id(cpu) << UVH_RTC1_INT_CONFIG_APIC_ID_SHFT);
+
+	/* Set configuration */
+	uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG, val);
+	/* Initialize comparator value */
+	uv_write_global_mmr64(pnode, UVH_INT_CMPB, expires);
+
+	return (expires < uv_read_rtc() && !uv_intr_pending(pnode));
+}
+
+/*
+ * Per-cpu timer tracking routines
+ */
+
+static __init void uv_rtc_deallocate_timers(void)
+{
+	int bid;
+
+	for_each_possible_blade(bid) {
+		kfree(blade_info[bid]);
+	}
+	kfree(blade_info);
+}
+
+/* Allocate per-node list of cpu timer expiration times. */
+static __init int uv_rtc_allocate_timers(void)
+{
+	int cpu;
+
+	blade_info = kmalloc(uv_possible_blades * sizeof(void *), GFP_KERNEL);
+	if (!blade_info)
+		return -ENOMEM;
+	memset(blade_info, 0, uv_possible_blades * sizeof(void *));
+
+	for_each_present_cpu(cpu) {
+		int nid = cpu_to_node(cpu);
+		int bid = uv_cpu_to_blade_id(cpu);
+		int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
+		struct uv_rtc_timer_head *head = blade_info[bid];
+
+		if (!head) {
+			head = kmalloc_node(sizeof(struct uv_rtc_timer_head) +
+				(uv_blade_nr_possible_cpus(bid) *
+					2 * sizeof(u64)),
+				GFP_KERNEL, nid);
+			if (!head) {
+				uv_rtc_deallocate_timers();
+				return -ENOMEM;
+			}
+			spin_lock_init(&head->lock);
+			head->ncpus = uv_blade_nr_possible_cpus(bid);
+			head->next_cpu = -1;
+			blade_info[bid] = head;
+		}
+
+		head->cpu[bcpu].lcpu = cpu;
+		head->cpu[bcpu].expires = ULLONG_MAX;
+	}
+
+	return 0;
+}
+
+/* Find and set the next expiring timer.  */
+static void uv_rtc_find_next_timer(struct uv_rtc_timer_head *head, int pnode)
+{
+	u64 lowest = ULLONG_MAX;
+	int c, bcpu = -1;
+
+	head->next_cpu = -1;
+	for (c = 0; c < head->ncpus; c++) {
+		u64 exp = head->cpu[c].expires;
+		if (exp < lowest) {
+			bcpu = c;
+			lowest = exp;
+		}
+	}
+	if (bcpu >= 0) {
+		head->next_cpu = bcpu;
+		c = head->cpu[bcpu].lcpu;
+		if (uv_setup_intr(c, lowest))
+			/* If we didn't set it up in time, trigger */
+			uv_rtc_send_IPI(c);
+	} else
+		uv_write_global_mmr64(pnode, UVH_RTC1_INT_CONFIG,
+			UVH_RTC1_INT_CONFIG_M_MASK);
+}
+
+/*
+ * Set expiration time for current cpu.
+ *
+ * Returns 1 if we missed the expiration time.
+ */
+static int uv_rtc_set_timer(int cpu, u64 expires)
+{
+	int pnode = uv_cpu_to_pnode(cpu);
+	int bid = uv_cpu_to_blade_id(cpu);
+	struct uv_rtc_timer_head *head = blade_info[bid];
+	int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
+	u64 *t = &head->cpu[bcpu].expires;
+	unsigned long flags;
+	int next_cpu;
+
+	spin_lock_irqsave(&head->lock, flags);
+
+	next_cpu = head->next_cpu;
+	*t = expires;
+	/* Will this one be next to go off? */
+	if (next_cpu < 0 || bcpu == next_cpu ||
+			expires < head->cpu[next_cpu].expires) {
+		head->next_cpu = bcpu;
+		if (uv_setup_intr(cpu, expires)) {
+			*t = ULLONG_MAX;
+			uv_rtc_find_next_timer(head, pnode);
+			spin_unlock_irqrestore(&head->lock, flags);
+			return 1;
+		}
+	}
+
+	spin_unlock_irqrestore(&head->lock, flags);
+	return 0;
+}
+
+/*
+ * Unset expiration time for current cpu.
+ *
+ * Returns 1 if this timer was pending.
+ */
+static int uv_rtc_unset_timer(int cpu)
+{
+	int pnode = uv_cpu_to_pnode(cpu);
+	int bid = uv_cpu_to_blade_id(cpu);
+	struct uv_rtc_timer_head *head = blade_info[bid];
+	int bcpu = uv_cpu_hub_info(cpu)->blade_processor_id;
+	u64 *t = &head->cpu[bcpu].expires;
+	unsigned long flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&head->lock, flags);
+
+	if (head->next_cpu == bcpu && uv_read_rtc() >= *t)
+		rc = 1;
+
+	*t = ULLONG_MAX;
+
+	/* Was the hardware setup for this timer? */
+	if (head->next_cpu == bcpu)
+		uv_rtc_find_next_timer(head, pnode);
+
+	spin_unlock_irqrestore(&head->lock, flags);
+	return rc;
+}
+
+
+/*
+ * Kernel interface routines.
+ */
+
+/*
+ * Read the RTC.
+ */
+static cycle_t uv_read_rtc(void)
+{
+	return (cycle_t)uv_read_local_mmr(UVH_RTC);
+}
+
+/*
+ * Program the next event, relative to now
+ */
+static int uv_rtc_next_event(unsigned long delta,
+			     struct clock_event_device *ced)
+{
+	int ced_cpu = cpumask_first(ced->cpumask);
+
+	return uv_rtc_set_timer(ced_cpu, delta + uv_read_rtc());
+}
+
+/*
+ * Setup the RTC timer in oneshot mode
+ */
+static void uv_rtc_timer_setup(enum clock_event_mode mode,
+			       struct clock_event_device *evt)
+{
+	int ced_cpu = cpumask_first(evt->cpumask);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_RESUME:
+		/* Nothing to do here yet */
+		break;
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+		uv_rtc_unset_timer(ced_cpu);
+		break;
+	}
+}
+
+static void uv_rtc_interrupt(void)
+{
+	struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
+	int cpu = smp_processor_id();
+
+	if (!ced || !ced->event_handler)
+		return;
+
+	if (uv_rtc_unset_timer(cpu) != 1)
+		return;
+
+	ced->event_handler(ced);
+}
+
+static __init void uv_rtc_register_clockevents(struct work_struct *dummy)
+{
+	struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
+
+	*ced = clock_event_device_uv;
+	ced->cpumask = cpumask_of(smp_processor_id());
+	clockevents_register_device(ced);
+}
+
+static __init int uv_rtc_setup_clock(void)
+{
+	int rc;
+
+	if (!is_uv_system() ||
+	    register_generic_interrupt_extension(uv_rtc_interrupt, "UVT",
+	    "UV Timer interrupts"))
+		return -ENODEV;
+
+	clocksource_uv.mult = clocksource_hz2mult(sn_rtc_cycles_per_second,
+				clocksource_uv.shift);
+
+	rc = clocksource_register(&clocksource_uv);
+	if (rc) {
+		unregister_generic_interrupt_extension();
+		return rc;
+	}
+
+	/* Setup and register clockevents */
+	rc = uv_rtc_allocate_timers();
+	if (rc) {
+		clocksource_unregister(&clocksource_uv);
+		unregister_generic_interrupt_extension();
+		return rc;
+	}
+
+	clock_event_device_uv.mult = div_sc(sn_rtc_cycles_per_second,
+				NSEC_PER_SEC, clock_event_device_uv.shift);
+
+	clock_event_device_uv.min_delta_ns = NSEC_PER_SEC /
+						sn_rtc_cycles_per_second;
+
+	clock_event_device_uv.max_delta_ns = clocksource_uv.mask *
+				(NSEC_PER_SEC / sn_rtc_cycles_per_second);
+
+	rc = schedule_on_each_cpu(uv_rtc_register_clockevents);
+	if (rc) {
+		clocksource_unregister(&clocksource_uv);
+		unregister_generic_interrupt_extension();
+		uv_rtc_deallocate_timers();
+	}
+
+	return rc;
+}
+module_init(uv_rtc_setup_clock);

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/6 v6] SGI RTC: add generic system interrupt
  2009-03-03 15:13 ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Dimitri Sivanich
  2009-03-03 15:15   ` [PATCH 2/6 v6] SGI RTC: export clocksource_unregister Dimitri Sivanich
@ 2009-03-03 15:34   ` Ingo Molnar
  2009-03-03 17:39     ` Dimitri Sivanich
  1 sibling, 1 reply; 14+ messages in thread
From: Ingo Molnar @ 2009-03-03 15:34 UTC (permalink / raw)
  To: Dimitri Sivanich
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel


* Dimitri Sivanich <sivanich@sgi.com> wrote:

> This patch allocates a system interrupt vector for various 
> platform specific uses.

this is really ugly.

Also, why are all these symbols exported? There's no need to 
build the UV RTC driver as a module. It's either built-in or not 
built-in - it's small enough.

this stuff:

> +/* Function pointer for generic interrupt vector handling */
> +static void (*generic_interrupt_extension)(void);
> +static char generic_show_string[28];
> +static char generic_show_prefix[6];
> +
> +int is_generic_interrupt_registered()
> +{
> +	if (generic_interrupt_extension)
> +		return 1;
> +	else
> +		return 0;
> +}
> +
> +char *generic_interrupt_string(void)
> +{
> +	return generic_show_string;
> +}
> +
> +char *generic_interrupt_prefix(void)
> +{
> +	return generic_show_prefix;
> +}

is SMP unsafe, etc. etc. - not something we should ever call 
from a module. We just shouldnt do it in this form. What 
necessiates it?

All we need is:

> +	/* generic IPI for platform specific use */
> +	alloc_intr_gate(GENERIC_INTERRUPT_VECTOR, generic_interrupt);

plus one trivial callback function - and then the UV platform 
uses it for its own purpose. It's not like two platforms will be 
running at once so there's no locking needed, etc.

> +#ifdef CONFIG_X86_64
> +extern void generic_interrupt(void);
> +#endif

in any case please make it symmetric across 32-bit and 64-bit - 
even though UV is 64-bit only.

	Ingo

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/6 v6] SGI RTC: add clocksource driver
  2009-03-03 15:22           ` [PATCH 6/6 v6] SGI RTC: add clocksource driver Dimitri Sivanich
@ 2009-03-03 15:35             ` Ingo Molnar
  2009-03-03 21:59               ` H. Peter Anvin
  2009-03-03 20:01             ` john stultz
  1 sibling, 1 reply; 14+ messages in thread
From: Ingo Molnar @ 2009-03-03 15:35 UTC (permalink / raw)
  To: Dimitri Sivanich
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel


* Dimitri Sivanich <sivanich@sgi.com> wrote:

>  drivers/clocksource/Makefile |    1 
>  drivers/clocksource/rtc_uv.c |  381 +++++++++++++++++++++++++++++++++++++++
>  drivers/misc/Kconfig         |    9 

btw., since this is a platform driver, i'd suggest to move it to 
arch/x86/kernel/uv_time.c or so.

	Ingo

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/6 v6] SGI RTC: add generic system interrupt
  2009-03-03 15:34   ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Ingo Molnar
@ 2009-03-03 17:39     ` Dimitri Sivanich
  2009-03-03 18:52       ` Ingo Molnar
  0 siblings, 1 reply; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 17:39 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel

On Tue, Mar 03, 2009 at 04:34:33PM +0100, Ingo Molnar wrote:
> 
> * Dimitri Sivanich <sivanich@sgi.com> wrote:
> 
> > This patch allocates a system interrupt vector for various 
> > platform specific uses.
> 
> this is really ugly.
> 
> Also, why are all these symbols exported? There's no need to 
> build the UV RTC driver as a module. It's either built-in or not 
> built-in - it's small enough.

OK.

> 
> this stuff:
> 
> > +/* Function pointer for generic interrupt vector handling */
> > +static void (*generic_interrupt_extension)(void);
> > +static char generic_show_string[28];
> > +static char generic_show_prefix[6];
> > +
> > +int is_generic_interrupt_registered()
> > +{
> > +	if (generic_interrupt_extension)
> > +		return 1;
> > +	else
> > +		return 0;
> > +}
> > +
> > +char *generic_interrupt_string(void)
> > +{
> > +	return generic_show_string;
> > +}
> > +
> > +char *generic_interrupt_prefix(void)
> > +{
> > +	return generic_show_prefix;
> > +}
> 
> is SMP unsafe, etc. etc. - not something we should ever call 
> from a module. We just shouldnt do it in this form. What 
> necessiates it?

I wanted a way to show specific strings when displaying irq statistics.  We could show these in a more generic way I suppose.

Any given platform should just be changing these once, hence the lack of need for locking.

> 
> All we need is:
> 
> > +	/* generic IPI for platform specific use */
> > +	alloc_intr_gate(GENERIC_INTERRUPT_VECTOR, generic_interrupt);
> 
> plus one trivial callback function - and then the UV platform 
> uses it for its own purpose. It's not like two platforms will be 
> running at once so there's no locking needed, etc.

Right.  But still register the callback function as I have it now?

> 
> > +#ifdef CONFIG_X86_64
> > +extern void generic_interrupt(void);
> > +#endif
> 
> in any case please make it symmetric across 32-bit and 64-bit - 
> even though UV is 64-bit only.

OK.

> 
> 	Ingo

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 1/6 v6] SGI RTC: add generic system interrupt
  2009-03-03 17:39     ` Dimitri Sivanich
@ 2009-03-03 18:52       ` Ingo Molnar
  0 siblings, 0 replies; 14+ messages in thread
From: Ingo Molnar @ 2009-03-03 18:52 UTC (permalink / raw)
  To: Dimitri Sivanich
  Cc: Thomas Gleixner, H. Peter Anvin, Andrew Morton, john stultz,
	linux-kernel


* Dimitri Sivanich <sivanich@sgi.com> wrote:

> On Tue, Mar 03, 2009 at 04:34:33PM +0100, Ingo Molnar wrote:
> > 
> > * Dimitri Sivanich <sivanich@sgi.com> wrote:
> > 
> > > This patch allocates a system interrupt vector for various 
> > > platform specific uses.
> > 
> > this is really ugly.
> > 
> > Also, why are all these symbols exported? There's no need to 
> > build the UV RTC driver as a module. It's either built-in or not 
> > built-in - it's small enough.
> 
> OK.
> 
> > 
> > this stuff:
> > 
> > > +/* Function pointer for generic interrupt vector handling */
> > > +static void (*generic_interrupt_extension)(void);
> > > +static char generic_show_string[28];
> > > +static char generic_show_prefix[6];
> > > +
> > > +int is_generic_interrupt_registered()
> > > +{
> > > +	if (generic_interrupt_extension)
> > > +		return 1;
> > > +	else
> > > +		return 0;
> > > +}
> > > +
> > > +char *generic_interrupt_string(void)
> > > +{
> > > +	return generic_show_string;
> > > +}
> > > +
> > > +char *generic_interrupt_prefix(void)
> > > +{
> > > +	return generic_show_prefix;
> > > +}
> > 
> > is SMP unsafe, etc. etc. - not something we should ever call 
> > from a module. We just shouldnt do it in this form. What 
> > necessiates it?
> 
> I wanted a way to show specific strings when displaying irq statistics.  We could show these in a more generic way I suppose.
> 
> Any given platform should just be changing these once, hence the lack of need for locking.
> 
> > 
> > All we need is:
> > 
> > > +	/* generic IPI for platform specific use */
> > > +	alloc_intr_gate(GENERIC_INTERRUPT_VECTOR, generic_interrupt);
> > 
> > plus one trivial callback function - and then the UV platform 
> > uses it for its own purpose. It's not like two platforms will be 
> > running at once so there's no locking needed, etc.
> 
> Right.  But still register the callback function as I have it 
> now?

i'd suggest to just override that global function pointer from 
the UV detection routines.

this way we have it in a minimalistically generic fashion, but 
with a minimum amount of fuss around it.

	Ingo

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/6 v6] SGI RTC: add clocksource driver
  2009-03-03 15:22           ` [PATCH 6/6 v6] SGI RTC: add clocksource driver Dimitri Sivanich
  2009-03-03 15:35             ` Ingo Molnar
@ 2009-03-03 20:01             ` john stultz
  2009-03-03 20:25               ` Dimitri Sivanich
  1 sibling, 1 reply; 14+ messages in thread
From: john stultz @ 2009-03-03 20:01 UTC (permalink / raw)
  To: Dimitri Sivanich
  Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
	linux-kernel

On Tue, 2009-03-03 at 09:22 -0600, Dimitri Sivanich wrote:
> This patch provides a driver for SGI RTC clocks and timers.
> 
> This provides a high resolution clock and timer source using the SGI
> system-wide synchronized RTC clock/timer hardware.
> 
> Signed-off-by: Dimitri Sivanich <sivanich@sgi.com>
> 
> ---
> 
> Implemented suggested changes and refreshed for latest -tip.
> 
>  drivers/clocksource/Makefile |    1 
>  drivers/clocksource/rtc_uv.c |  381 +++++++++++++++++++++++++++++++++++++++
>  drivers/misc/Kconfig         |    9 
>  3 files changed, 391 insertions(+)
[snip]
> +
> +static struct clocksource clocksource_uv = {
> +	.name		= RTC_NAME,
> +	.rating		= 400,
> +	.read		= uv_read_rtc,
> +	.mask		= (cycle_t)UVH_RTC_REAL_TIME_CLOCK_MASK,
> +	.shift		= 10,


What is the expected value for sn_rtc_cycles_per_second ? 

I ask because a shift value of 10 seems a bit low. It could very well be
appropriate, but I just wanted to check.

thanks
-john



^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/6 v6] SGI RTC: add clocksource driver
  2009-03-03 20:01             ` john stultz
@ 2009-03-03 20:25               ` Dimitri Sivanich
  0 siblings, 0 replies; 14+ messages in thread
From: Dimitri Sivanich @ 2009-03-03 20:25 UTC (permalink / raw)
  To: john stultz
  Cc: Ingo Molnar, Thomas Gleixner, H. Peter Anvin, Andrew Morton,
	linux-kernel

On Tue, Mar 03, 2009 at 12:01:32PM -0800, john stultz wrote:
> On Tue, 2009-03-03 at 09:22 -0600, Dimitri Sivanich wrote:
> > +
> > +static struct clocksource clocksource_uv = {
> > +	.name		= RTC_NAME,
> > +	.rating		= 400,
> > +	.read		= uv_read_rtc,
> > +	.mask		= (cycle_t)UVH_RTC_REAL_TIME_CLOCK_MASK,
> > +	.shift		= 10,
> 
> 
> What is the expected value for sn_rtc_cycles_per_second ? 
> 
> I ask because a shift value of 10 seems a bit low. It could very well be
> appropriate, but I just wanted to check.
> 

Likely in the range of 20,000,000 - 40,000,000.

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH 6/6 v6] SGI RTC: add clocksource driver
  2009-03-03 15:35             ` Ingo Molnar
@ 2009-03-03 21:59               ` H. Peter Anvin
  0 siblings, 0 replies; 14+ messages in thread
From: H. Peter Anvin @ 2009-03-03 21:59 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Dimitri Sivanich, Thomas Gleixner, Andrew Morton, john stultz,
	linux-kernel

Ingo Molnar wrote:
> * Dimitri Sivanich <sivanich@sgi.com> wrote:
> 
>>  drivers/clocksource/Makefile |    1 
>>  drivers/clocksource/rtc_uv.c |  381 +++++++++++++++++++++++++++++++++++++++
>>  drivers/misc/Kconfig         |    9 
> 
> btw., since this is a platform driver, i'd suggest to move it to 
> arch/x86/kernel/uv_time.c or so.
> 

I thought we agreed platforms drivers go in drivers/x86?

	-hpa

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2009-03-03 22:08 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-03 15:10 [PATCH 0/6 v6] SGI RTC: add clocksource/clockevent driver and generic vector Dimitri Sivanich
2009-03-03 15:13 ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Dimitri Sivanich
2009-03-03 15:15   ` [PATCH 2/6 v6] SGI RTC: export clocksource_unregister Dimitri Sivanich
2009-03-03 15:16     ` [PATCH 3/6 v6] SGI RTC: export clockevents_register_device Dimitri Sivanich
2009-03-03 15:18       ` [PATCH 4/6 v6] SGI RTC: export schedule_on_each_cpu Dimitri Sivanich
2009-03-03 15:20         ` [PATCH 5/6 v6] SGI RTC: loop through installed UV blades Dimitri Sivanich
2009-03-03 15:22           ` [PATCH 6/6 v6] SGI RTC: add clocksource driver Dimitri Sivanich
2009-03-03 15:35             ` Ingo Molnar
2009-03-03 21:59               ` H. Peter Anvin
2009-03-03 20:01             ` john stultz
2009-03-03 20:25               ` Dimitri Sivanich
2009-03-03 15:34   ` [PATCH 1/6 v6] SGI RTC: add generic system interrupt Ingo Molnar
2009-03-03 17:39     ` Dimitri Sivanich
2009-03-03 18:52       ` Ingo Molnar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).