From: Dimitri Sivanich <sivanich@sgi.com>
To: Ingo Molnar <mingo@elte.hu>, Thomas Gleixner <tglx@linutronix.de>,
linux-kernel@vger.kernel.org
Cc: Dimitri Sivanich <sivanich@sgi.com>
Subject: [PATCH 1/3] SGI RTC: add clocksource driver
Date: Tue, 21 Oct 2008 16:19:21 -0500 [thread overview]
Message-ID: <20081021211921.GA4037@sgi.com> (raw)
In-Reply-To: <20081021211740.GA3936@sgi.com>
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>
Index: linux/drivers/clocksource/Makefile
===================================================================
--- linux.orig/drivers/clocksource/Makefile 2008-10-21 12:02:41.000000000 -0500
+++ linux/drivers/clocksource/Makefile 2008-10-21 12:02:44.000000000 -0500
@@ -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 2008-10-21 12:02:41.000000000 -0500
+++ linux/drivers/misc/Kconfig 2008-10-21 12:02:44.000000000 -0500
@@ -488,4 +488,12 @@ config SGI_GRU_DEBUG
This option enables addition debugging code for the SGI GRU driver. If
you are unsure, say N.
+config SGI_RTC_TIMER
+ tristate "SGI RTC driver"
+ depends on X86_64 || IA64_SGI_UV
+ default m
+ ---help---
+ This option enables RTC event handling and adds the systemwide RTC
+ as a high resolution clock source for SGI systems.
+
endif # MISC_DEVICES
Index: linux/drivers/clocksource/rtc_uv.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ linux/drivers/clocksource/rtc_uv.c 2008-10-21 12:29:00.000000000 -0500
@@ -0,0 +1,213 @@
+/*
+ * 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) 2008 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/bios.h>
+#include <asm/uv/uv_mmrs.h>
+#include <asm/uv/uv_hub.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 void uv_rtc_timer_broadcast(cpumask_t);
+
+
+static struct clocksource clocksource_uv = {
+ .name = RTC_NAME,
+ .rating = 400,
+ .read = uv_read_rtc,
+ .shift = 0,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static struct clock_event_device clock_event_device_uv = {
+ .name = RTC_NAME,
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .shift = 10,
+ .rating = 400,
+ .irq = -1,
+ .set_next_event = uv_rtc_next_event,
+ .set_mode = uv_rtc_timer_setup,
+ .event_handler = NULL,
+ .broadcast = uv_rtc_timer_broadcast
+};
+
+static DEFINE_PER_CPU(struct clock_event_device, cpu_ced);
+
+/* Send IPIs to another node */
+static void
+uv_rtc_send_IPI(int cpu, int vector)
+{
+ unsigned long val, apicid, lapicid;
+ int pnode;
+
+ apicid = per_cpu(x86_cpu_to_apicid, cpu);
+ lapicid = apicid & 0x3f;
+ pnode = uv_apicid_to_pnode(apicid);
+
+ val = (1UL << UVH_IPI_INT_SEND_SHFT) | (lapicid <<
+ UVH_IPI_INT_APIC_ID_SHFT) |
+ (vector << UVH_IPI_INT_VECTOR_SHFT);
+ uv_write_global_mmr64(pnode, UVH_IPI_INT, val);
+}
+
+/*
+ * Read the RTC.
+ */
+static cycle_t
+uv_read_rtc(void)
+{
+ cycle_t ticks;
+ uv_bios_rtct(UV_RTC_READ, &ticks, 0, 0, 0, 0);
+ return ticks;
+}
+
+/*
+ * Program the next event, relative to now
+ */
+static int
+uv_rtc_next_event(unsigned long delta, struct clock_event_device *ced)
+{
+ int ced_cpu = first_cpu(ced->cpumask);
+
+ return uv_bios_rtct(UV_RTC_EVT_SET, &ced_cpu, &delta, 0, 0, 0);
+}
+
+/*
+ * Setup the RTC timer in oneshot mode
+ */
+static void
+uv_rtc_timer_setup(enum clock_event_mode mode, struct clock_event_device *evt)
+{
+ unsigned long flags;
+ int ced_cpu = first_cpu(evt->cpumask);
+
+ local_irq_save(flags);
+
+ switch (mode) {
+ case CLOCK_EVT_MODE_PERIODIC:
+ case CLOCK_EVT_MODE_ONESHOT:
+ uv_bios_rtct(UV_RTC_EVT_START, &ced_cpu, 0, 0, 0, 0);
+ break;
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ uv_bios_rtct(UV_RTC_EVT_STOP, &ced_cpu, 0, 0, 0, 0);
+ break;
+ case CLOCK_EVT_MODE_RESUME:
+ uv_bios_rtct(UV_RTC_EVT_RESUME, &ced_cpu, 0, 0, 0, 0);
+ break;
+ }
+
+ local_irq_restore(flags);
+}
+
+/*
+ * Local APIC timer broadcast function
+ */
+static void
+uv_rtc_timer_broadcast(cpumask_t mask)
+{
+ int cpu;
+ for_each_cpu_mask(cpu, mask)
+ uv_rtc_send_IPI(cpu, RTC_TIMER_VECTOR);
+}
+
+static void
+uv_rtc_interrupt(void)
+{
+ struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
+ unsigned long flags;
+ int cpu = smp_processor_id();
+
+ local_irq_save(flags);
+ if (!ced || !ced->event_handler ||
+ uv_bios_rtct(UV_RTC_EVT_ACK, &cpu, 0, 0, 0, 0)) {
+ local_irq_restore(flags);
+ printk(KERN_WARNING
+ "Spurious uv_rtc timer interrupt on cpu %d\n",
+ smp_processor_id());
+ return;
+ }
+ local_irq_restore(flags);
+ ced->event_handler(ced);
+}
+
+static __init void
+uv_rtc_register_clockevents(void *data)
+{
+ struct clock_event_device *ced = &__get_cpu_var(cpu_ced);
+
+ memcpy(ced, &clock_event_device_uv, sizeof(clock_event_device_uv));
+ cpus_clear(ced->cpumask);
+ cpu_set(smp_processor_id(), ced->cpumask);
+ clockevents_register_device(ced);
+}
+
+static __init int
+uv_rtc_setup_clock(void)
+{
+ u64 vector = RTC_TIMER_VECTOR;
+ int rc;
+
+ if (!is_uv_system() || uv_rtc_reg_extension(uv_rtc_interrupt))
+ return -1;
+
+ /* Setup and register the clocksource */
+ if (uv_bios_rtct(UV_RTC_MASK, &clocksource_uv.mask, 0, 0, 0, 0) ==
+ BIOS_STATUS_UNIMPLEMENTED) {
+ uv_rtc_unreg_extension();
+ printk("BIOS RTC unimplemented\n");
+ return -1;
+ }
+
+ clocksource_uv.mult = clocksource_hz2mult(sn_rtc_cycles_per_second,
+ clocksource_uv.shift);
+
+ rc = clocksource_register(&clocksource_uv);
+ if (rc) {
+ uv_rtc_unreg_extension();
+ return -1;
+ }
+
+ /* Setup and register clockevents */
+ uv_bios_rtct(UV_RTC_EVT_INIT, &vector, 0, 0, 0, 0);
+
+ 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);
+
+ smp_call_function(uv_rtc_register_clockevents, NULL, 0);
+ uv_rtc_register_clockevents(NULL);
+
+ return 0;
+}
+module_init(uv_rtc_setup_clock);
Index: linux/kernel/time/clockevents.c
===================================================================
--- linux.orig/kernel/time/clockevents.c 2008-10-21 12:02:41.000000000 -0500
+++ linux/kernel/time/clockevents.c 2008-10-21 12:02:44.000000000 -0500
@@ -183,6 +183,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
Index: linux/kernel/time/clocksource.c
===================================================================
--- linux.orig/kernel/time/clocksource.c 2008-10-21 12:02:41.000000000 -0500
+++ linux/kernel/time/clocksource.c 2008-10-21 12:02:44.000000000 -0500
@@ -369,6 +369,7 @@ void clocksource_unregister(struct clock
next_clocksource = select_clocksource();
spin_unlock_irqrestore(&clocksource_lock, flags);
}
+EXPORT_SYMBOL(clocksource_unregister);
#ifdef CONFIG_SYSFS
/**
next prev parent reply other threads:[~2008-10-21 21:19 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-10-21 21:17 [PATCH 0/3] SGI RTC: add clocksource/clockevent driver and timer bios calls Dimitri Sivanich
2008-10-21 21:19 ` Dimitri Sivanich [this message]
2008-10-21 21:20 ` [PATCH 2/3] SGI RTC: add bios framework for RTC timer operations Dimitri Sivanich
2008-10-21 21:21 ` [PATCH 3/3] SGI RTC: add RTC system interrupt Dimitri Sivanich
2008-10-22 6:36 ` [PATCH 0/3] SGI RTC: add clocksource/clockevent driver and timer bios calls Ingo Molnar
2008-10-22 16:07 ` Dimitri Sivanich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20081021211921.GA4037@sgi.com \
--to=sivanich@sgi.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@elte.hu \
--cc=tglx@linutronix.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.