* [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 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 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
* 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 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 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
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).