While there are 3 patches in this series that are specifically related
to QEMU, there are 3 more that significantly re-architect generic parts
of arch/alpha that hopefully bring them more into line with current
linux-kernel design, and 2 more that seem like they ought to work
generically, but ought to be tested on real hardware. In particular:
1/10: Ought to have no effect on real hw, since the "interrupt handling"
was a printk.
5/10: Ought to work, and result in cooler cpus, on real hardware. That
said, excersising parts of random PALcodes on misc machines is
surely doomed to failure. I can well imagine that we'll need
some sort of black/white-list for the various machines.
6/10: Ought to work better for Marvel, since more accesses to the RTC
are now being vectored across the smp-call. OTOH, I could have
introduced a silly typo somewhere.
9/10: Probably the most significant cleanup, and the one most likely to
have something subtle go wrong on the various machines.
The series seems pretty stable under QEMU, but I have no real hardware
on which to test -- the whole reason I'm interested in QEMU of course.
So I'm hoping that someone will notice this and help me out with testing.
In addition, if folks more familiar with the various kernel/time or
driver/rtc interfaces spot anything I'm doing wrong in the rewrite,
please let me know.
r~
Richard Henderson (10):
alpha: Don't if-out dp264_device_interrupt.
alpha: Notice if we're being run under QEMU
alpha: Force the user-visible HZ to a constant 1024.
alpha: Allow HZ to be configured
alpha: Primitive support for CPU power down.
alpha: Reorganize rtc handling
alpha: Add an rtc driver for the qemu wallclock PALcall
alpha: Always enable the rpcc clocksource for single processor
alpha: Switch to GENERIC_CLOCKEVENTS
alpha: Use qemu+cserve provided high-res clock and alarm.
arch/alpha/Kconfig | 52 ++++-
arch/alpha/include/asm/machvec.h | 22 +-
arch/alpha/include/asm/pal.h | 71 +++++++
arch/alpha/include/asm/param.h | 8 +-
arch/alpha/include/asm/rtc.h | 11 -
arch/alpha/include/uapi/asm/pal.h | 1 +
arch/alpha/include/uapi/asm/param.h | 7 -
arch/alpha/kernel/Makefile | 2 +-
arch/alpha/kernel/irq_alpha.c | 16 +-
arch/alpha/kernel/machvec_impl.h | 5 +-
arch/alpha/kernel/process.c | 15 ++
arch/alpha/kernel/proto.h | 13 +-
arch/alpha/kernel/rtc.c | 374 +++++++++++++++++++++++++++++++++
arch/alpha/kernel/setup.c | 23 +-
arch/alpha/kernel/smp.c | 33 +--
arch/alpha/kernel/sys_dp264.c | 8 -
arch/alpha/kernel/sys_jensen.c | 2 -
arch/alpha/kernel/sys_marvel.c | 55 +----
arch/alpha/kernel/time.c | 403 ++++++++++++++++--------------------
arch/alpha/kernel/traps.c | 12 ++
drivers/rtc/Kconfig | 17 +-
21 files changed, 767 insertions(+), 383 deletions(-)
create mode 100644 arch/alpha/kernel/rtc.c
--
1.8.1.4
With the 1024Hz default, we spend 50% of QEMU emulation
processing timer interrupts.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/Kconfig | 36 ++++++++++++++++++++++++++++++++++--
arch/alpha/kernel/setup.c | 10 ++++++++--
arch/alpha/kernel/time.c | 24 +++++++++++++++++-------
3 files changed, 59 insertions(+), 11 deletions(-)
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 2053f215..5991a99 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -628,9 +628,41 @@ config VERBOSE_MCHECK_ON
Take the default (1) unless you want more control or more info.
+choice
+ prompt "Timer interrupt frequency (HZ)?"
+ default HZ_128 if ALPHA_QEMU
+ default HZ_1200 if ALPHA_RAWHIDE
+ default HZ_1024
+ ---help---
+ The frequency at which timer interrupts occur. A high frequency
+ minimizes latency, whereas a low frequency minimizes overhead of
+ process accounting. The later effect is especially significant
+ when being run under QEMU.
+
+ Note that some Alpha hardware cannot change the interrupt frequency
+ of the timer. If unsure, say 1024 (or 1200 for Rawhide).
+
+ config HZ_32
+ bool "32 Hz"
+ config HZ_64
+ bool "64 Hz"
+ config HZ_128
+ bool "128 Hz"
+ config HZ_256
+ bool "256 Hz"
+ config HZ_1024
+ bool "1024 Hz"
+ config HZ_1200
+ bool "1200 Hz"
+endchoice
+
config HZ
- int
- default 1200 if ALPHA_RAWHIDE
+ int
+ default 32 if HZ_32
+ default 64 if HZ_64
+ default 128 if HZ_128
+ default 256 if HZ_256
+ default 1200 if HZ_1200
default 1024
source "drivers/pci/Kconfig"
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index c38d6a1..b20af76 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -1218,6 +1218,7 @@ show_cpuinfo(struct seq_file *f, void *slot)
char *systype_name;
char *sysvariation_name;
int nr_processors;
+ unsigned long timer_freq;
cpu_index = (unsigned) (cpu->type - 1);
cpu_name = "Unknown";
@@ -1229,6 +1230,12 @@ show_cpuinfo(struct seq_file *f, void *slot)
nr_processors = get_nr_processors(cpu, hwrpb->nr_processors);
+#if CONFIG_HZ == 1024 || CONFIG_HZ == 1200
+ timer_freq = (100UL * hwrpb->intr_freq) / 4096;
+#else
+ timer_freq = 100UL * CONFIG_HZ;
+#endif
+
seq_printf(f, "cpu\t\t\t: Alpha\n"
"cpu model\t\t: %s\n"
"cpu variation\t\t: %ld\n"
@@ -1254,8 +1261,7 @@ show_cpuinfo(struct seq_file *f, void *slot)
(char*)hwrpb->ssn,
est_cycle_freq ? : hwrpb->cycle_freq,
est_cycle_freq ? "est." : "",
- hwrpb->intr_freq / 4096,
- (100 * hwrpb->intr_freq / 4096) % 100,
+ timer_freq / 100, timer_freq % 100,
hwrpb->pagesize,
hwrpb->pa_bits,
hwrpb->max_asn,
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index e336694..7938bf9 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -203,16 +203,26 @@ irqreturn_t timer_interrupt(int irq, void *dev)
void __init
common_init_rtc(void)
{
- unsigned char x;
+ unsigned char x, sel = 0;
/* Reset periodic interrupt frequency. */
- x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f;
- /* Test includes known working values on various platforms
- where 0x26 is wrong; we refuse to change those. */
- if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) {
- printk("Setting RTC_FREQ to 1024 Hz (%x)\n", x);
- CMOS_WRITE(0x26, RTC_FREQ_SELECT);
+#if CONFIG_HZ == 1024 || CONFIG_HZ == 1200
+ x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f;
+ /* Test includes known working values on various platforms
+ where 0x26 is wrong; we refuse to change those. */
+ if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) {
+ sel = RTC_REF_CLCK_32KHZ + 6;
}
+#elif CONFIG_HZ == 256 || CONFIG_HZ == 128 || CONFIG_HZ == 64 || CONFIG_HZ == 32
+ sel = RTC_REF_CLCK_32KHZ + __builtin_ffs(32768 / CONFIG_HZ);
+#else
+# error "Unknown HZ from arch/alpha/Kconfig"
+#endif
+ if (sel) {
+ printk(KERN_INFO "Setting RTC_FREQ to %d Hz (%x)\n",
+ CONFIG_HZ, sel);
+ CMOS_WRITE(sel, RTC_FREQ_SELECT);
+ }
/* Turn on periodic interrupts. */
x = CMOS_READ(RTC_CONTROL);
--
1.8.1.4
Discontinue use of GENERIC_CMOS_UPDATE; rely on the RTC subsystem.
The marvel platform requires that the rtc only be touched from the
boot cpu. This had been partially implemented with hooks for
get/set_rtc_time, but read/update_persistent_clock were not handled.
Fixed by adding an rtc_set_mmss hook along the way.
We had read_persistent_clock managing the epoch against which the
rtc hw is based, but this didn't apply to get_rtc_time or set_rtc_time.
This resulted in incorrect values when hwclock(8) gets involved.
Allow the epoch to be set from the kernel command-line, overriding
the autodetection, which is doomed to fail in 2028. Further, by
implementing the rtc ioctl function, we can expose this epoch to
userland.
Elide the alarm functions that RTC_DRV_CMOS implements. This was
highly questionable on Alpha, since the interrupt is used by the
system timer.
Cc: [email protected]
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/Kconfig | 1 -
arch/alpha/include/asm/machvec.h | 4 +-
arch/alpha/include/asm/rtc.h | 11 --
arch/alpha/kernel/Makefile | 1 +
arch/alpha/kernel/machvec_impl.h | 5 +-
arch/alpha/kernel/proto.h | 9 ++
arch/alpha/kernel/rtc.c | 323 +++++++++++++++++++++++++++++++++++++++
arch/alpha/kernel/sys_jensen.c | 2 -
arch/alpha/kernel/sys_marvel.c | 55 +------
arch/alpha/kernel/time.c | 146 +-----------------
drivers/rtc/Kconfig | 10 +-
11 files changed, 346 insertions(+), 221 deletions(-)
create mode 100644 arch/alpha/kernel/rtc.c
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 5991a99..7e268cf 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -17,7 +17,6 @@ config ALPHA
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select GENERIC_SMP_IDLE_THREAD
- select GENERIC_CMOS_UPDATE
select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER
select HAVE_MOD_ARCH_SPECIFIC
diff --git a/arch/alpha/include/asm/machvec.h b/arch/alpha/include/asm/machvec.h
index 4ac9016..75cb364 100644
--- a/arch/alpha/include/asm/machvec.h
+++ b/arch/alpha/include/asm/machvec.h
@@ -33,6 +33,7 @@ struct alpha_machine_vector
int nr_irqs;
int rtc_port;
+ int rtc_boot_cpu_only;
unsigned int max_asn;
unsigned long max_isa_dma_address;
unsigned long irq_probe_mask;
@@ -95,9 +96,6 @@ struct alpha_machine_vector
struct _alpha_agp_info *(*agp_info)(void);
- unsigned int (*rtc_get_time)(struct rtc_time *);
- int (*rtc_set_time)(struct rtc_time *);
-
const char *vector_name;
/* NUMA information */
diff --git a/arch/alpha/include/asm/rtc.h b/arch/alpha/include/asm/rtc.h
index d70408d..f71c3b0 100644
--- a/arch/alpha/include/asm/rtc.h
+++ b/arch/alpha/include/asm/rtc.h
@@ -1,12 +1 @@
-#ifndef _ALPHA_RTC_H
-#define _ALPHA_RTC_H
-
-#if defined(CONFIG_ALPHA_MARVEL) && defined(CONFIG_SMP) \
- || defined(CONFIG_ALPHA_GENERIC)
-# define get_rtc_time alpha_mv.rtc_get_time
-# define set_rtc_time alpha_mv.rtc_set_time
-#endif
-
#include <asm-generic/rtc.h>
-
-#endif
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 84ec46b..0d54650 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -16,6 +16,7 @@ obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
obj-$(CONFIG_SRM_ENV) += srm_env.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
+obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o
ifdef CONFIG_ALPHA_GENERIC
diff --git a/arch/alpha/kernel/machvec_impl.h b/arch/alpha/kernel/machvec_impl.h
index 7fa6248..f54bdf6 100644
--- a/arch/alpha/kernel/machvec_impl.h
+++ b/arch/alpha/kernel/machvec_impl.h
@@ -43,10 +43,7 @@
#define CAT1(x,y) x##y
#define CAT(x,y) CAT1(x,y)
-#define DO_DEFAULT_RTC \
- .rtc_port = 0x70, \
- .rtc_get_time = common_get_rtc_time, \
- .rtc_set_time = common_set_rtc_time
+#define DO_DEFAULT_RTC .rtc_port = 0x70
#define DO_EV4_MMU \
.max_asn = EV4_MAX_ASN, \
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index d3e52d3..b0c8498 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -144,8 +144,17 @@ extern void smp_percpu_timer_interrupt(struct pt_regs *);
extern irqreturn_t timer_interrupt(int irq, void *dev);
extern void common_init_rtc(void);
extern unsigned long est_cycle_freq;
+
+/* rtc.c */
+#ifdef CONFIG_RTC_DRV_ALPHA
extern unsigned int common_get_rtc_time(struct rtc_time *time);
extern int common_set_rtc_time(struct rtc_time *time);
+extern int common_set_rtc_mmss(unsigned long);
+#else
+# define common_get_rtc_time NULL
+# define common_set_rtc_time NULL
+# define common_set_rtc_mmss NULL
+#endif
/* smc37c93x.c */
extern void SMC93x_Init(void);
diff --git a/arch/alpha/kernel/rtc.c b/arch/alpha/kernel/rtc.c
new file mode 100644
index 0000000..c8d284d
--- /dev/null
+++ b/arch/alpha/kernel/rtc.c
@@ -0,0 +1,323 @@
+/*
+ * linux/arch/alpha/kernel/rtc.c
+ *
+ * Copyright (C) 1991, 1992, 1995, 1999, 2000 Linus Torvalds
+ *
+ * This file contains date handling.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mc146818rtc.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/platform_device.h>
+
+#include <asm/rtc.h>
+
+#include "proto.h"
+
+
+/*
+ * Support for the RTC device.
+ *
+ * We don't want to use the rtc-cmos driver, because we don't want to support
+ * alarms, as that would be indistinguishable from timer interrupts.
+ *
+ * Further, generic code is really, really tied to a 1900 epoch. This is
+ * true in __get_rtc_time as well as the users of struct rtc_time e.g.
+ * rtc_tm_to_time. Thankfully all of the other epochs in use are later
+ * than 1900, and so it's easy to adjust.
+ */
+
+static unsigned long rtc_epoch;
+
+static int __init
+specifiy_epoch(char *str)
+{
+ unsigned long epoch = simple_strtoul(str, NULL, 0);
+ if (epoch < 1900)
+ printk("Ignoring invalid user specified epoch %lu\n", epoch);
+ else
+ rtc_epoch = epoch;
+ return 1;
+}
+__setup("epoch=", specifiy_epoch);
+
+static void __init
+init_rtc_epoch(void)
+{
+ int epoch, year, ctrl;
+
+ if (rtc_epoch != 0) {
+ /* The epoch was specified on the command-line. */
+ return;
+ }
+
+ /* Detect the epoch in use on this computer. */
+ ctrl = CMOS_READ(RTC_CONTROL);
+ year = CMOS_READ(RTC_YEAR);
+ if (!(ctrl & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ year = bcd2bin(year);
+
+ /* PC-like is standard; used for year >= 70 */
+ epoch = 1900;
+ if (year < 20) {
+ epoch = 2000;
+ } else if (year >= 20 && year < 48) {
+ /* NT epoch */
+ epoch = 1980;
+ } else if (year >= 48 && year < 70) {
+ /* Digital UNIX epoch */
+ epoch = 1952;
+ }
+ rtc_epoch = epoch;
+
+ printk(KERN_INFO "Using epoch %d for rtc year %d\n", epoch, year);
+}
+
+static int
+alpha_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ __get_rtc_time(tm);
+
+ /* Adjust for non-default epochs. It's easier to depend on the
+ generic __get_rtc_time and adjust the epoch here than create
+ a copy of __get_rtc_time with the edits we need. */
+ if (rtc_epoch != 1900) {
+ int year = tm->tm_year;
+ /* Undo the century adjustment made in __get_rtc_time. */
+ if (year >= 100)
+ year -= 100;
+ year += rtc_epoch - 1900;
+ /* Redo the century adjustment with the epoch in place. */
+ if (year <= 69)
+ year += 100;
+ tm->tm_year = year;
+ }
+
+ return rtc_valid_tm(tm);
+}
+
+static int
+alpha_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct rtc_time xtm;
+
+ if (rtc_epoch != 1900) {
+ xtm = *tm;
+ xtm.tm_year -= rtc_epoch - 1900;
+ tm = &xtm;
+ }
+
+ return __set_rtc_time(tm);
+}
+
+static int
+alpha_rtc_set_mmss(struct device *dev, unsigned long nowtime)
+{
+ int retval = 0;
+ int real_seconds, real_minutes, cmos_minutes;
+ unsigned char save_control, save_freq_select;
+
+ /* Note: This code only updates minutes and seconds. Comments
+ indicate this was to avoid messing with unknown time zones,
+ and with the epoch nonsense described above. In order for
+ this to work, the existing clock cannot be off by more than
+ 15 minutes.
+
+ ??? This choice is may be out of date. The x86 port does
+ not have problems with timezones, and the epoch processing has
+ now been fixed in alpha_set_rtc_time.
+
+ In either case, one can always force a full rtc update with
+ the userland hwclock program, so surely 15 minute accuracy
+ is no real burden. */
+
+ /* In order to set the CMOS clock precisely, we have to be called
+ 500 ms after the second nowtime has started, because when
+ nowtime is written into the registers of the CMOS clock, it will
+ jump to the next second precisely 500 ms later. Check the Motorola
+ MC146818A or Dallas DS12887 data sheet for details. */
+
+ /* irq are locally disabled here */
+ spin_lock(&rtc_lock);
+ /* Tell the clock it's being set */
+ save_control = CMOS_READ(RTC_CONTROL);
+ CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
+
+ /* Stop and reset prescaler */
+ save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
+ CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+ cmos_minutes = CMOS_READ(RTC_MINUTES);
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+ cmos_minutes = bcd2bin(cmos_minutes);
+
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+ if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) {
+ /* correct for half hour time zone */
+ real_minutes += 30;
+ }
+ real_minutes %= 60;
+
+ if (abs(real_minutes - cmos_minutes) < 30) {
+ if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+ real_seconds = bin2bcd(real_seconds);
+ real_minutes = bin2bcd(real_minutes);
+ }
+ CMOS_WRITE(real_seconds,RTC_SECONDS);
+ CMOS_WRITE(real_minutes,RTC_MINUTES);
+ } else {
+ printk_once(KERN_NOTICE
+ "set_rtc_mmss: can't update from %d to %d\n",
+ cmos_minutes, real_minutes);
+ retval = -1;
+ }
+
+ /* The following flags have to be released exactly in this order,
+ * otherwise the DS12887 (popular MC146818A clone with integrated
+ * battery and quartz) will not reset the oscillator and will not
+ * update precisely 500 ms later. You won't find this mentioned in
+ * the Dallas Semiconductor data sheets, but who believes data
+ * sheets anyway ... -- Markus Kuhn
+ */
+ CMOS_WRITE(save_control, RTC_CONTROL);
+ CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+ spin_unlock(&rtc_lock);
+
+ return retval;
+}
+
+static int
+alpha_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case RTC_EPOCH_READ:
+ return put_user(rtc_epoch, (unsigned long __user *)arg);
+ case RTC_EPOCH_SET:
+ if (arg < 1900)
+ return -EINVAL;
+ rtc_epoch = arg;
+ return 0;
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+static const struct rtc_class_ops alpha_rtc_ops = {
+ .read_time = alpha_rtc_read_time,
+ .set_time = alpha_rtc_set_time,
+ .set_mmss = alpha_rtc_set_mmss,
+ .ioctl = alpha_rtc_ioctl,
+};
+
+/*
+ * Similarly, except do the actual CMOS access on the boot cpu only.
+ * This requires marshalling the data across an interprocessor call.
+ */
+
+#if defined(CONFIG_SMP) && \
+ (defined(CONFIG_ALPHA_GENERIC) || defined(CONFIG_ALPHA_MARVEL))
+# define HAVE_REMOTE_RTC 1
+
+union remote_data {
+ struct rtc_time *tm;
+ unsigned long now;
+ long retval;
+};
+
+static void
+do_remote_read(void *data)
+{
+ union remote_data *x = data;
+ x->retval = alpha_rtc_read_time(NULL, x->tm);
+}
+
+static int
+remote_read_time(struct device *dev, struct rtc_time *tm)
+{
+ union remote_data x;
+ if (smp_processor_id() != boot_cpuid) {
+ x.tm = tm;
+ smp_call_function_single(boot_cpuid, do_remote_read, &x, 1);
+ return x.retval;
+ }
+ return alpha_rtc_read_time(NULL, tm);
+}
+
+static void
+do_remote_set(void *data)
+{
+ union remote_data *x = data;
+ x->retval = alpha_rtc_set_time(NULL, x->tm);
+}
+
+static int
+remote_set_time(struct device *dev, struct rtc_time *tm)
+{
+ union remote_data x;
+ if (smp_processor_id() != boot_cpuid) {
+ x.tm = tm;
+ smp_call_function_single(boot_cpuid, do_remote_set, &x, 1);
+ return x.retval;
+ }
+ return alpha_rtc_set_time(NULL, tm);
+}
+
+static void
+do_remote_mmss(void *data)
+{
+ union remote_data *x = data;
+ x->retval = alpha_rtc_set_mmss(NULL, x->now);
+}
+
+static int
+remote_set_mmss(struct device *dev, unsigned long now)
+{
+ union remote_data x;
+ if (smp_processor_id() != boot_cpuid) {
+ x.now = now;
+ smp_call_function_single(boot_cpuid, do_remote_mmss, &x, 1);
+ return x.retval;
+ }
+ return alpha_rtc_set_mmss(NULL, now);
+}
+
+static const struct rtc_class_ops remote_rtc_ops = {
+ .read_time = remote_read_time,
+ .set_time = remote_set_time,
+ .set_mmss = remote_set_mmss,
+ .ioctl = alpha_rtc_ioctl,
+};
+#endif
+
+static int __init
+alpha_rtc_init(void)
+{
+ const struct rtc_class_ops *ops;
+ struct platform_device *pdev;
+ struct rtc_device *rtc;
+ const char *name;
+
+ init_rtc_epoch();
+ name = "rtc-alpha";
+ ops = &alpha_rtc_ops;
+
+#ifdef HAVE_REMOTE_RTC
+ if (alpha_mv.rtc_boot_cpu_only)
+ ops = &remote_rtc_ops;
+#endif
+
+ pdev = platform_device_register_simple(name, -1, NULL, 0);
+ rtc = devm_rtc_device_register(&pdev->dev, name, ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
+
+ platform_set_drvdata(pdev, rtc);
+ return 0;
+}
+device_initcall(alpha_rtc_init);
diff --git a/arch/alpha/kernel/sys_jensen.c b/arch/alpha/kernel/sys_jensen.c
index 5a0af11..608f2a7 100644
--- a/arch/alpha/kernel/sys_jensen.c
+++ b/arch/alpha/kernel/sys_jensen.c
@@ -224,8 +224,6 @@ struct alpha_machine_vector jensen_mv __initmv = {
.machine_check = jensen_machine_check,
.max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS,
.rtc_port = 0x170,
- .rtc_get_time = common_get_rtc_time,
- .rtc_set_time = common_set_rtc_time,
.nr_irqs = 16,
.device_interrupt = jensen_device_interrupt,
diff --git a/arch/alpha/kernel/sys_marvel.c b/arch/alpha/kernel/sys_marvel.c
index 53d6e4a..03d63c6 100644
--- a/arch/alpha/kernel/sys_marvel.c
+++ b/arch/alpha/kernel/sys_marvel.c
@@ -22,7 +22,6 @@
#include <asm/hwrpb.h>
#include <asm/tlbflush.h>
#include <asm/vga.h>
-#include <asm/rtc.h>
#include "proto.h"
#include "err_impl.h"
@@ -400,57 +399,6 @@ marvel_init_rtc(void)
init_rtc_irq();
}
-struct marvel_rtc_time {
- struct rtc_time *time;
- int retval;
-};
-
-#ifdef CONFIG_SMP
-static void
-smp_get_rtc_time(void *data)
-{
- struct marvel_rtc_time *mrt = data;
- mrt->retval = __get_rtc_time(mrt->time);
-}
-
-static void
-smp_set_rtc_time(void *data)
-{
- struct marvel_rtc_time *mrt = data;
- mrt->retval = __set_rtc_time(mrt->time);
-}
-#endif
-
-static unsigned int
-marvel_get_rtc_time(struct rtc_time *time)
-{
-#ifdef CONFIG_SMP
- struct marvel_rtc_time mrt;
-
- if (smp_processor_id() != boot_cpuid) {
- mrt.time = time;
- smp_call_function_single(boot_cpuid, smp_get_rtc_time, &mrt, 1);
- return mrt.retval;
- }
-#endif
- return __get_rtc_time(time);
-}
-
-static int
-marvel_set_rtc_time(struct rtc_time *time)
-{
-#ifdef CONFIG_SMP
- struct marvel_rtc_time mrt;
-
- if (smp_processor_id() != boot_cpuid) {
- mrt.time = time;
- smp_call_function_single(boot_cpuid, smp_set_rtc_time, &mrt, 1);
- return mrt.retval;
- }
-#endif
- return __set_rtc_time(time);
-}
-
static void
marvel_smp_callin(void)
{
@@ -492,8 +440,7 @@ struct alpha_machine_vector marvel_ev7_mv __initmv = {
.vector_name = "MARVEL/EV7",
DO_EV7_MMU,
.rtc_port = 0x70,
- .rtc_get_time = marvel_get_rtc_time,
- .rtc_set_time = marvel_set_rtc_time,
+ .rtc_boot_cpu_only = 1,
DO_MARVEL_IO,
.machine_check = marvel_machine_check,
.max_isa_dma_address = ALPHA_MAX_ISA_DMA_ADDRESS,
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index 7938bf9..dbd56ec 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -3,13 +3,7 @@
*
* Copyright (C) 1991, 1992, 1995, 1999, 2000 Linus Torvalds
*
- * This file contains the PC-specific time handling details:
- * reading the RTC at bootup, etc..
- * 1994-07-02 Alan Modra
- * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
- * 1995-03-26 Markus Kuhn
- * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
- * precision CMOS clock update
+ * This file contains the clocksource time handling.
* 1997-09-10 Updated NTP code according to technical memorandum Jan '96
* "A Kernel Model for Precision Timekeeping" by Dave Mills
* 1997-01-09 Adrian Sun
@@ -21,9 +15,6 @@
* 1999-04-16 Thorsten Kranzkowski ([email protected])
* fixed algorithm in do_gettimeofday() for calculating the precise time
* from processor cycle counter (now taking lost_ticks into account)
- * 2000-08-13 Jan-Benedict Glaw <[email protected]>
- * Fixed time_init to be aware of epoches != 1900. This prevents
- * booting up in 2048 for me;) Code is stolen from rtc.c.
* 2003-06-03 R. Scott Bailey <[email protected]>
* Tighten sanity in time_init from 1% (10,000 PPM) to 250 PPM
*/
@@ -46,7 +37,6 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/hwrpb.h>
-#include <asm/rtc.h>
#include <linux/mc146818rtc.h>
#include <linux/time.h>
@@ -56,8 +46,6 @@
#include "proto.h"
#include "irq_impl.h"
-static int set_rtc_mmss(unsigned long);
-
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
@@ -110,53 +98,6 @@ static inline __u32 rpcc(void)
return result;
}
-int update_persistent_clock(struct timespec now)
-{
- return set_rtc_mmss(now.tv_sec);
-}
-
-void read_persistent_clock(struct timespec *ts)
-{
- unsigned int year, mon, day, hour, min, sec, epoch;
-
- sec = CMOS_READ(RTC_SECONDS);
- min = CMOS_READ(RTC_MINUTES);
- hour = CMOS_READ(RTC_HOURS);
- day = CMOS_READ(RTC_DAY_OF_MONTH);
- mon = CMOS_READ(RTC_MONTH);
- year = CMOS_READ(RTC_YEAR);
-
- if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- sec = bcd2bin(sec);
- min = bcd2bin(min);
- hour = bcd2bin(hour);
- day = bcd2bin(day);
- mon = bcd2bin(mon);
- year = bcd2bin(year);
- }
-
- /* PC-like is standard; used for year >= 70 */
- epoch = 1900;
- if (year < 20)
- epoch = 2000;
- else if (year >= 20 && year < 48)
- /* NT epoch */
- epoch = 1980;
- else if (year >= 48 && year < 70)
- /* Digital UNIX epoch */
- epoch = 1952;
-
- printk(KERN_INFO "Using epoch = %d\n", epoch);
-
- if ((year += epoch) < 1970)
- year += 100;
-
- ts->tv_sec = mktime(year, mon, day, hour, min, sec);
- ts->tv_nsec = 0;
-}
-
-
-
/*
* timer_interrupt() needs to keep up the real-time clock,
* as well as call the "xtime_update()" routine every clocktick
@@ -245,16 +186,6 @@ common_init_rtc(void)
init_rtc_irq();
}
-unsigned int common_get_rtc_time(struct rtc_time *time)
-{
- return __get_rtc_time(time);
-}
-
-int common_set_rtc_time(struct rtc_time *time)
-{
- return __set_rtc_time(time);
-}
-
/* Validate a computed cycle counter result against the known bounds for
the given processor core. There's too much brokenness in the way of
timing hardware for any one method to work everywhere. :-(
@@ -455,78 +386,3 @@ time_init(void)
/* Startup the timer source. */
alpha_mv.init_rtc();
}
-
-/*
- * In order to set the CMOS clock precisely, set_rtc_mmss has to be
- * called 500 ms after the second nowtime has started, because when
- * nowtime is written into the registers of the CMOS clock, it will
- * jump to the next second precisely 500 ms later. Check the Motorola
- * MC146818A or Dallas DS12887 data sheet for details.
- *
- * BUG: This routine does not handle hour overflow properly; it just
- * sets the minutes. Usually you won't notice until after reboot!
- */
-
-
-static int
-set_rtc_mmss(unsigned long nowtime)
-{
- int retval = 0;
- int real_seconds, real_minutes, cmos_minutes;
- unsigned char save_control, save_freq_select;
-
- /* irq are locally disabled here */
- spin_lock(&rtc_lock);
- /* Tell the clock it's being set */
- save_control = CMOS_READ(RTC_CONTROL);
- CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL);
-
- /* Stop and reset prescaler */
- save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
- CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT);
-
- cmos_minutes = CMOS_READ(RTC_MINUTES);
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
- cmos_minutes = bcd2bin(cmos_minutes);
-
- /*
- * since we're only adjusting minutes and seconds,
- * don't interfere with hour overflow. This avoids
- * messing with unknown time zones but requires your
- * RTC not to be off by more than 15 minutes
- */
- real_seconds = nowtime % 60;
- real_minutes = nowtime / 60;
- if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) {
- /* correct for half hour time zone */
- real_minutes += 30;
- }
- real_minutes %= 60;
-
- if (abs(real_minutes - cmos_minutes) < 30) {
- if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
- real_seconds = bin2bcd(real_seconds);
- real_minutes = bin2bcd(real_minutes);
- }
- CMOS_WRITE(real_seconds,RTC_SECONDS);
- CMOS_WRITE(real_minutes,RTC_MINUTES);
- } else {
- printk_once(KERN_NOTICE
- "set_rtc_mmss: can't update from %d to %d\n",
- cmos_minutes, real_minutes);
- retval = -1;
- }
-
- /* The following flags have to be released exactly in this order,
- * otherwise the DS12887 (popular MC146818A clone with integrated
- * battery and quartz) will not reset the oscillator and will not
- * update precisely 500 ms later. You won't find this mentioned in
- * the Dallas Semiconductor data sheets, but who believes data
- * sheets anyway ... -- Markus Kuhn
- */
- CMOS_WRITE(save_control, RTC_CONTROL);
- CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
- spin_unlock(&rtc_lock);
-
- return retval;
-}
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 9e3498b..a4d3f9a 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -606,7 +606,7 @@ comment "Platform RTC drivers"
config RTC_DRV_CMOS
tristate "PC-style 'CMOS'"
- depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64
+ depends on X86 || ARM || M32R || ATARI || PPC || MIPS || SPARC64
default y if X86
help
Say "yes" here to get direct support for the real time clock
@@ -623,6 +623,14 @@ config RTC_DRV_CMOS
This driver can also be built as a module. If so, the module
will be called rtc-cmos.
+config RTC_DRV_ALPHA
+ bool "Alpha PC-style CMOS"
+ depends on ALPHA
+ default y
+ help
+ Direct support for the real-time clock found on every Alpha
+ system, specifically MC146818 compatibles. If in doubt, say Y.
+
config RTC_DRV_VRTC
tristate "Virtual RTC for Intel MID platforms"
depends on X86_INTEL_MID
--
1.8.1.4
When building a generic kernel, do a run-time check on the serial
number, like we do for MILO. When building a custom kernel, make
this a configure-time check.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/Kconfig | 14 ++++++++++++++
arch/alpha/include/asm/machvec.h | 18 ++++++++++++------
arch/alpha/kernel/setup.c | 13 ++++++++++++-
3 files changed, 38 insertions(+), 7 deletions(-)
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 082d9b4..2053f215 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -488,6 +488,20 @@ config VGA_HOSE
which always have multiple hoses, and whose consoles support it.
+config ALPHA_QEMU
+ bool "Run under QEMU emulation"
+ depends on !ALPHA_GENERIC
+ ---help---
+ Assume the presence of special features supported by QEMU PALcode
+ that reduce the overhead of system emulation.
+
+ Generic kernels will auto-detect QEMU. But when building a
+ system-specific kernel, the assumption is that we want to
+ elimiate as many runtime tests as possible.
+
+ If unsure, say N.
+
+
config ALPHA_SRM
bool "Use SRM as bootloader" if ALPHA_CABRIOLET || ALPHA_AVANTI_CH || ALPHA_EB64P || ALPHA_PC164 || ALPHA_TAKARA || ALPHA_EB164 || ALPHA_ALCOR || ALPHA_MIATA || ALPHA_LX164 || ALPHA_SX164 || ALPHA_NAUTILUS || ALPHA_NONAME
depends on TTY
diff --git a/arch/alpha/include/asm/machvec.h b/arch/alpha/include/asm/machvec.h
index 72dbf23..4ac9016 100644
--- a/arch/alpha/include/asm/machvec.h
+++ b/arch/alpha/include/asm/machvec.h
@@ -126,13 +126,19 @@ extern struct alpha_machine_vector alpha_mv;
#ifdef CONFIG_ALPHA_GENERIC
extern int alpha_using_srm;
+extern int alpha_using_qemu;
#else
-#ifdef CONFIG_ALPHA_SRM
-#define alpha_using_srm 1
-#else
-#define alpha_using_srm 0
-#endif
+# ifdef CONFIG_ALPHA_SRM
+# define alpha_using_srm 1
+# else
+# define alpha_using_srm 0
+# endif
+# ifdef CONFIG_ALPHA_QEMU
+# define alpha_using_qemu 1
+# else
+# define alpha_using_qemu 0
+# endif
#endif /* GENERIC */
-#endif
+#endif /* __KERNEL__ */
#endif /* __ALPHA_MACHVEC_H */
diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c
index 9e3107cc5..c38d6a1 100644
--- a/arch/alpha/kernel/setup.c
+++ b/arch/alpha/kernel/setup.c
@@ -115,10 +115,17 @@ unsigned long alpha_agpgart_size = DEFAULT_AGP_APER_SIZE;
#ifdef CONFIG_ALPHA_GENERIC
struct alpha_machine_vector alpha_mv;
+#endif
+
+#ifndef alpha_using_srm
int alpha_using_srm;
EXPORT_SYMBOL(alpha_using_srm);
#endif
+#ifndef alpha_using_qemu
+int alpha_using_qemu;
+#endif
+
static struct alpha_machine_vector *get_sysvec(unsigned long, unsigned long,
unsigned long);
static struct alpha_machine_vector *get_sysvec_byname(const char *);
@@ -529,11 +536,15 @@ setup_arch(char **cmdline_p)
atomic_notifier_chain_register(&panic_notifier_list,
&alpha_panic_block);
-#ifdef CONFIG_ALPHA_GENERIC
+#ifndef alpha_using_srm
/* Assume that we've booted from SRM if we haven't booted from MILO.
Detect the later by looking for "MILO" in the system serial nr. */
alpha_using_srm = strncmp((const char *)hwrpb->ssn, "MILO", 4) != 0;
#endif
+#ifndef alpha_using_qemu
+ /* Similarly, look for QEMU. */
+ alpha_using_qemu = strstr((const char *)hwrpb->ssn, "QEMU") != 0;
+#endif
/* If we are using SRM, we want to allow callbacks
as early as possible, so do this NOW, and then
--
1.8.1.4
QEMU provides a high-resolution timer and alarm; use this for
a clock source and clock event source when available.
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/include/asm/pal.h | 14 ++++++
arch/alpha/kernel/irq_alpha.c | 2 +-
arch/alpha/kernel/proto.h | 2 +-
arch/alpha/kernel/time.c | 106 ++++++++++++++++++++++++++++++++++++++++--
4 files changed, 117 insertions(+), 7 deletions(-)
diff --git a/arch/alpha/include/asm/pal.h b/arch/alpha/include/asm/pal.h
index ccaa49a..5422a47 100644
--- a/arch/alpha/include/asm/pal.h
+++ b/arch/alpha/include/asm/pal.h
@@ -168,5 +168,19 @@ qemu_set_alarm_abs(unsigned long expire)
: "$0", "$18", "$19", "$20", "$21");
}
+static inline unsigned long
+qemu_get_vmtime(void)
+{
+ register unsigned long v0 __asm__("$0");
+ register unsigned long a0 __asm__("$16") = 7;
+
+ asm("call_pal %2 # cserve get_time"
+ : "=r"(v0), "+r"(a0)
+ : "i"(PAL_cserve)
+ : "$17", "$18", "$19", "$20", "$21");
+
+ return v0;
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ALPHA_PAL_H */
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index 49d50e2..00add72 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -214,7 +214,7 @@ process_mcheck_info(unsigned long vector, unsigned long la_ptr,
*/
struct irqaction timer_irqaction = {
- .handler = timer_interrupt,
+ .handler = rtc_timer_interrupt,
.name = "timer",
};
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index 175f7a4..db31ffb 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -140,7 +140,7 @@ extern void handle_ipi(struct pt_regs *);
/* extern void reset_for_srm(void); */
/* time.c */
-extern irqreturn_t timer_interrupt(int irq, void *dev);
+extern irqreturn_t rtc_timer_interrupt(int irq, void *dev);
extern void init_clockevent(void);
extern void common_init_rtc(void);
extern unsigned long est_cycle_freq;
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index ec45001..3b7601e 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -89,7 +89,7 @@ static inline __u32 rpcc(void)
static DEFINE_PER_CPU(struct clock_event_device, cpu_ce);
irqreturn_t
-timer_interrupt(int irq, void *dev)
+rtc_timer_interrupt(int irq, void *dev)
{
int cpu = smp_processor_id();
struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
@@ -119,8 +119,8 @@ rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
return -EINVAL;
}
-void __init
-init_clockevent(void)
+static void __init
+init_rtc_clockevent(void)
{
int cpu = smp_processor_id();
struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
@@ -137,6 +137,83 @@ init_clockevent(void)
clockevents_config_and_register(ce, CONFIG_HZ, 0, 0);
}
+
+/*
+ * The QEMU clock as a clocksource primitive.
+ */
+
+static cycle_t
+qemu_cs_read(struct clocksource *cs)
+{
+ return qemu_get_vmtime();
+}
+
+static struct clocksource qemu_cs = {
+ .name = "qemu",
+ .rating = 400,
+ .read = qemu_cs_read,
+ .mask = CLOCKSOURCE_MASK(64),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .mult = 1,
+ .shift = 0,
+ .max_idle_ns = LONG_MAX
+};
+
+/*
+ * The QEMU clock and alarm as a clock_event_device primitive.
+ */
+
+
+static void
+qemu_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce)
+{
+ /* The mode member of CE is updated for us in generic code.
+ Just make sure that the event is disabled. */
+ qemu_set_alarm_abs(0);
+}
+
+static int
+qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
+{
+ qemu_set_alarm_rel(evt);
+ return 0;
+}
+
+static irqreturn_t
+qemu_timer_interrupt(int irq, void *dev)
+{
+ int cpu = smp_processor_id();
+ struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+ ce->event_handler(ce);
+ return IRQ_HANDLED;
+}
+
+static void __init
+init_qemu_clockevent(void)
+{
+ int cpu = smp_processor_id();
+ struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+ *ce = (struct clock_event_device){
+ .name = "qemu",
+ .features = CLOCK_EVT_FEAT_ONESHOT,
+ .min_delta_ns = 1000,
+ .min_delta_ticks = 1000,
+ .max_delta_ns = LONG_MAX,
+ .max_delta_ticks = LONG_MAX,
+ .mult = 1,
+ .shift = 0,
+ .rating = 400,
+ .cpumask = cpumask_of(cpu),
+ .set_mode = qemu_ce_set_mode,
+ .set_next_event = qemu_ce_set_next_event,
+ };
+
+ clockevents_register_device(ce);
+}
+
+
void __init
common_init_rtc(void)
{
@@ -324,6 +401,15 @@ time_init(void)
unsigned long cycle_freq, tolerance;
long diff;
+ if (alpha_using_qemu) {
+ clocksource_register(&qemu_cs);
+ init_qemu_clockevent();
+
+ timer_irqaction.handler = qemu_timer_interrupt;
+ init_rtc_irq();
+ return;
+ }
+
/* Calibrate CPU clock -- attempt #1. */
if (!est_cycle_freq)
est_cycle_freq = validate_cc_value(calibrate_cc_with_pit());
@@ -363,7 +449,17 @@ time_init(void)
/* Startup the timer source. */
alpha_mv.init_rtc();
+ init_rtc_clockevent();
+}
- /* Start up the clock event device. */
- init_clockevent();
+/* Initialize the clock_event_device for secondary cpus. */
+#ifdef CONFIG_SMP
+void __init
+init_clockevent(void)
+{
+ if (alpha_using_qemu)
+ init_qemu_clockevent();
+ else
+ init_rtc_clockevent();
}
+#endif
--
1.8.1.4
Don't depend on SMP, just check the number of processors online.
This allows a single distribution kernel to use the clocksource
when run on a single processor machine.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/kernel/time.c | 58 +++++++++++++++++++++++-------------------------
1 file changed, 28 insertions(+), 30 deletions(-)
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index dbd56ec..cadb82b 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -186,6 +186,31 @@ common_init_rtc(void)
init_rtc_irq();
}
+
+/*
+ * The RPCC as a clocksource primitive.
+ *
+ * While we have free-running timecounters running on all CPUs, and we make
+ * a half-hearted attempt in init_rtc_rpcc_info to sync the timecounter
+ * with the wall clock, that initialization isn't kept up-to-date across
+ * different time counters in SMP mode. Therefore we can only use this
+ * method when there's only one CPU enabled.
+ */
+
+static cycle_t read_rpcc(struct clocksource *cs)
+{
+ return rpcc();
+}
+
+static struct clocksource clocksource_rpcc = {
+ .name = "rpcc",
+ .rating = 300,
+ .read = read_rpcc,
+ .mask = CLOCKSOURCE_MASK(32),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS
+};
+
+
/* Validate a computed cycle counter result against the known bounds for
the given processor core. There's too much brokenness in the way of
timing hardware for any one method to work everywhere. :-(
@@ -296,33 +321,6 @@ rpcc_after_update_in_progress(void)
return rpcc();
}
-#ifndef CONFIG_SMP
-/* Until and unless we figure out how to get cpu cycle counters
- in sync and keep them there, we can't use the rpcc. */
-static cycle_t read_rpcc(struct clocksource *cs)
-{
- cycle_t ret = (cycle_t)rpcc();
- return ret;
-}
-
-static struct clocksource clocksource_rpcc = {
- .name = "rpcc",
- .rating = 300,
- .read = read_rpcc,
- .mask = CLOCKSOURCE_MASK(32),
- .flags = CLOCK_SOURCE_IS_CONTINUOUS
-};
-
-static inline void register_rpcc_clocksource(long cycle_freq)
-{
- clocksource_register_hz(&clocksource_rpcc, cycle_freq);
-}
-#else /* !CONFIG_SMP */
-static inline void register_rpcc_clocksource(long cycle_freq)
-{
-}
-#endif /* !CONFIG_SMP */
-
void __init
time_init(void)
{
@@ -364,20 +362,20 @@ time_init(void)
"and unable to estimate a proper value!\n");
}
+ if (hwrpb->nr_processors == 1)
+ clocksource_register_hz(&clocksource_rpcc, cycle_freq);
+
/* From John Bowman <[email protected]>: allow the values
to settle, as the Update-In-Progress bit going low isn't good
enough on some hardware. 2ms is our guess; we haven't found
bogomips yet, but this is close on a 500Mhz box. */
__delay(1000000);
-
if (HZ > (1<<16)) {
extern void __you_loose (void);
__you_loose();
}
- register_rpcc_clocksource(cycle_freq);
-
state.last_time = cc1;
state.scaled_ticks_per_cycle
= ((unsigned long) HZ << FIX_SHIFT) / cycle_freq;
--
1.8.1.4
This allows us to get rid of some hacky code for SMP. Get rid of
some cycle counter hackery that's now handled by generic code via
clocksource + clock_event_device objects.
Cc: Thomas Gleixner <[email protected]>
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/Kconfig | 1 +
arch/alpha/kernel/irq_alpha.c | 14 ------
arch/alpha/kernel/proto.h | 2 +-
arch/alpha/kernel/smp.c | 33 ++-----------
arch/alpha/kernel/time.c | 111 ++++++++++++++++++------------------------
5 files changed, 52 insertions(+), 109 deletions(-)
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index 7e268cf..5b57618 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -16,6 +16,7 @@ config ALPHA
select ARCH_WANT_IPC_PARSE_VERSION
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+ select GENERIC_CLOCKEVENTS
select GENERIC_SMP_IDLE_THREAD
select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER
diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c
index f433fc1..49d50e2 100644
--- a/arch/alpha/kernel/irq_alpha.c
+++ b/arch/alpha/kernel/irq_alpha.c
@@ -66,21 +66,7 @@ do_entInt(unsigned long type, unsigned long vector,
break;
case 1:
old_regs = set_irq_regs(regs);
-#ifdef CONFIG_SMP
- {
- long cpu;
-
- smp_percpu_timer_interrupt(regs);
- cpu = smp_processor_id();
- if (cpu != boot_cpuid) {
- kstat_incr_irqs_this_cpu(RTC_IRQ, irq_to_desc(RTC_IRQ));
- } else {
- handle_irq(RTC_IRQ);
- }
- }
-#else
handle_irq(RTC_IRQ);
-#endif
set_irq_regs(old_regs);
return;
case 2:
diff --git a/arch/alpha/kernel/proto.h b/arch/alpha/kernel/proto.h
index b0c8498..175f7a4 100644
--- a/arch/alpha/kernel/proto.h
+++ b/arch/alpha/kernel/proto.h
@@ -135,13 +135,13 @@ extern void unregister_srm_console(void);
/* smp.c */
extern void setup_smp(void);
extern void handle_ipi(struct pt_regs *);
-extern void smp_percpu_timer_interrupt(struct pt_regs *);
/* bios32.c */
/* extern void reset_for_srm(void); */
/* time.c */
extern irqreturn_t timer_interrupt(int irq, void *dev);
+extern void init_clockevent(void);
extern void common_init_rtc(void);
extern unsigned long est_cycle_freq;
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index 7b60834..1a8a10e 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -138,9 +138,11 @@ smp_callin(void)
/* Get our local ticker going. */
smp_setup_percpu_timer(cpuid);
+ init_clockevent();
/* Call platform-specific callin, if specified */
- if (alpha_mv.smp_callin) alpha_mv.smp_callin();
+ if (alpha_mv.smp_callin)
+ alpha_mv.smp_callin();
/* All kernel threads share the same mm context. */
atomic_inc(&init_mm.mm_count);
@@ -497,35 +499,6 @@ smp_cpus_done(unsigned int max_cpus)
((bogosum + 2500) / (5000/HZ)) % 100);
}
-
-void
-smp_percpu_timer_interrupt(struct pt_regs *regs)
-{
- struct pt_regs *old_regs;
- int cpu = smp_processor_id();
- unsigned long user = user_mode(regs);
- struct cpuinfo_alpha *data = &cpu_data[cpu];
-
- old_regs = set_irq_regs(regs);
-
- /* Record kernel PC. */
- profile_tick(CPU_PROFILING);
-
- if (!--data->prof_counter) {
- /* We need to make like a normal interrupt -- otherwise
- timer interrupts ignore the global interrupt lock,
- which would be a Bad Thing. */
- irq_enter();
-
- update_process_times(user);
-
- data->prof_counter = data->prof_multiplier;
-
- irq_exit();
- }
- set_irq_regs(old_regs);
-}
-
int
setup_profiling_timer(unsigned int multiplier)
{
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c
index cadb82b..ec45001 100644
--- a/arch/alpha/kernel/time.c
+++ b/arch/alpha/kernel/time.c
@@ -42,6 +42,7 @@
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/clocksource.h>
+#include <linux/clockchips.h>
#include "proto.h"
#include "irq_impl.h"
@@ -49,25 +50,6 @@
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);
-#define TICK_SIZE (tick_nsec / 1000)
-
-/*
- * Shift amount by which scaled_ticks_per_cycle is scaled. Shifting
- * by 48 gives us 16 bits for HZ while keeping the accuracy good even
- * for large CPU clock rates.
- */
-#define FIX_SHIFT 48
-
-/* lump static variables together for more efficient access: */
-static struct {
- /* cycle counter last time it got invoked */
- __u32 last_time;
- /* ticks/cycle * 2^48 */
- unsigned long scaled_ticks_per_cycle;
- /* partial unused tick */
- unsigned long partial_tick;
-} state;
-
unsigned long est_cycle_freq;
#ifdef CONFIG_IRQ_WORK
@@ -98,49 +80,63 @@ static inline __u32 rpcc(void)
return result;
}
+
+
/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "xtime_update()" routine every clocktick
+ * The RTC as a clock_event_device primitive.
*/
-irqreturn_t timer_interrupt(int irq, void *dev)
-{
- unsigned long delta;
- __u32 now;
- long nticks;
-#ifndef CONFIG_SMP
- /* Not SMP, do kernel PC profiling here. */
- profile_tick(CPU_PROFILING);
-#endif
+static DEFINE_PER_CPU(struct clock_event_device, cpu_ce);
- /*
- * Calculate how many ticks have passed since the last update,
- * including any previous partial leftover. Save any resulting
- * fraction for the next pass.
- */
- now = rpcc();
- delta = now - state.last_time;
- state.last_time = now;
- delta = delta * state.scaled_ticks_per_cycle + state.partial_tick;
- state.partial_tick = delta & ((1UL << FIX_SHIFT) - 1);
- nticks = delta >> FIX_SHIFT;
+irqreturn_t
+timer_interrupt(int irq, void *dev)
+{
+ int cpu = smp_processor_id();
+ struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
- if (nticks)
- xtime_update(nticks);
+ /* Don't run the hook for UNUSED or SHUTDOWN. */
+ if (likely(ce->mode == CLOCK_EVT_MODE_PERIODIC))
+ ce->event_handler(ce);
if (test_irq_work_pending()) {
clear_irq_work_pending();
irq_work_run();
}
-#ifndef CONFIG_SMP
- while (nticks--)
- update_process_times(user_mode(get_irq_regs()));
-#endif
-
return IRQ_HANDLED;
}
+static void
+rtc_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce)
+{
+ /* The mode member of CE is updated in generic code.
+ Since we only support periodic events, nothing to do. */
+}
+static int
+rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
+{
+ /* This hook is for oneshot mode, which we don't support. */
+ return -EINVAL;
+}
+
+void __init
+init_clockevent(void)
+{
+ int cpu = smp_processor_id();
+ struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
+
+ *ce = (struct clock_event_device){
+ .name = "rtc",
+ .features = CLOCK_EVT_FEAT_PERIODIC,
+ .rating = 100,
+ .cpumask = cpumask_of(cpu),
+ .set_mode = rtc_ce_set_mode,
+ .set_next_event = rtc_ce_set_next_event,
+ };
+
+ clockevents_config_and_register(ce, CONFIG_HZ, 0, 0);
+}
+
void __init
common_init_rtc(void)
{
@@ -365,22 +361,9 @@ time_init(void)
if (hwrpb->nr_processors == 1)
clocksource_register_hz(&clocksource_rpcc, cycle_freq);
- /* From John Bowman <[email protected]>: allow the values
- to settle, as the Update-In-Progress bit going low isn't good
- enough on some hardware. 2ms is our guess; we haven't found
- bogomips yet, but this is close on a 500Mhz box. */
- __delay(1000000);
-
- if (HZ > (1<<16)) {
- extern void __you_loose (void);
- __you_loose();
- }
-
- state.last_time = cc1;
- state.scaled_ticks_per_cycle
- = ((unsigned long) HZ << FIX_SHIFT) / cycle_freq;
- state.partial_tick = 0L;
-
/* Startup the timer source. */
alpha_mv.init_rtc();
+
+ /* Start up the clock event device. */
+ init_clockevent();
}
--
1.8.1.4
In the normal case, one wants the guest to follow the host time.
QEMU has a cserve call that retrieves the wall clock.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/include/asm/pal.h | 56 ++++++++++++++++++++++++++++++++++++++
arch/alpha/kernel/Makefile | 3 +-
arch/alpha/kernel/rtc.c | 65 +++++++++++++++++++++++++++++++++++++++-----
drivers/rtc/Kconfig | 7 +++++
4 files changed, 122 insertions(+), 9 deletions(-)
diff --git a/arch/alpha/include/asm/pal.h b/arch/alpha/include/asm/pal.h
index e78ec9b..ccaa49a 100644
--- a/arch/alpha/include/asm/pal.h
+++ b/arch/alpha/include/asm/pal.h
@@ -112,5 +112,61 @@ __CALL_PAL_RW1(wtint, unsigned long, unsigned long);
#define tbiap() __tbi(-1, /* no second argument */)
#define tbia() __tbi(-2, /* no second argument */)
+/*
+ * QEMU Cserv routines..
+ */
+
+static inline unsigned long
+qemu_get_walltime(void)
+{
+ register unsigned long v0 __asm__("$0");
+ register unsigned long a0 __asm__("$16") = 3;
+
+ asm("call_pal %2 # cserve get_time"
+ : "=r"(v0), "+r"(a0)
+ : "i"(PAL_cserve)
+ : "$17", "$18", "$19", "$20", "$21");
+
+ return v0;
+}
+
+static inline unsigned long
+qemu_get_alarm(void)
+{
+ register unsigned long v0 __asm__("$0");
+ register unsigned long a0 __asm__("$16") = 4;
+
+ asm("call_pal %2 # cserve get_alarm"
+ : "=r"(v0), "+r"(a0)
+ : "i"(PAL_cserve)
+ : "$17", "$18", "$19", "$20", "$21");
+
+ return v0;
+}
+
+static inline void
+qemu_set_alarm_rel(unsigned long expire)
+{
+ register unsigned long a0 __asm__("$16") = 5;
+ register unsigned long a1 __asm__("$17") = expire;
+
+ asm volatile("call_pal %2 # cserve set_alarm_rel"
+ : "+r"(a0), "+r"(a1)
+ : "i"(PAL_cserve)
+ : "$0", "$18", "$19", "$20", "$21");
+}
+
+static inline void
+qemu_set_alarm_abs(unsigned long expire)
+{
+ register unsigned long a0 __asm__("$16") = 6;
+ register unsigned long a1 __asm__("$17") = expire;
+
+ asm volatile("call_pal %2 # cserve set_alarm_abs"
+ : "+r"(a0), "+r"(a1)
+ : "i"(PAL_cserve)
+ : "$0", "$18", "$19", "$20", "$21");
+}
+
#endif /* !__ASSEMBLY__ */
#endif /* __ALPHA_PAL_H */
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile
index 0d54650..bf04ee8 100644
--- a/arch/alpha/kernel/Makefile
+++ b/arch/alpha/kernel/Makefile
@@ -8,7 +8,7 @@ ccflags-y := -Wno-sign-compare
obj-y := entry.o traps.o process.o osf_sys.o irq.o \
irq_alpha.o signal.o setup.o ptrace.o time.o \
- alpha_ksyms.o systbls.o err_common.o io.o
+ alpha_ksyms.o systbls.o err_common.o io.o rtc.o
obj-$(CONFIG_VGA_HOSE) += console.o
obj-$(CONFIG_SMP) += smp.o
@@ -16,7 +16,6 @@ obj-$(CONFIG_PCI) += pci.o pci_iommu.o pci-sysfs.o
obj-$(CONFIG_SRM_ENV) += srm_env.o
obj-$(CONFIG_MODULES) += module.o
obj-$(CONFIG_PERF_EVENTS) += perf_event.o
-obj-$(CONFIG_RTC_DRV_ALPHA) += rtc.o
ifdef CONFIG_ALPHA_GENERIC
diff --git a/arch/alpha/kernel/rtc.c b/arch/alpha/kernel/rtc.c
index c8d284d..fd910ff 100644
--- a/arch/alpha/kernel/rtc.c
+++ b/arch/alpha/kernel/rtc.c
@@ -16,10 +16,12 @@
#include <linux/platform_device.h>
#include <asm/rtc.h>
+#include <asm/pal.h>
#include "proto.h"
+#ifdef CONFIG_RTC_DRV_ALPHA
/*
* Support for the RTC device.
*
@@ -293,20 +295,68 @@ static const struct rtc_class_ops remote_rtc_ops = {
.set_mmss = remote_set_mmss,
.ioctl = alpha_rtc_ioctl,
};
-#endif
+#endif /* HAVE_REMOTE_RTC */
+#endif /* CONFIG_RTC_DRV_ALPHA */
+
+#ifdef CONFIG_RTC_DRV_ALPHA_QEMU
+/*
+ * Support for the QEMU wall clock as an RTC device.
+ */
+
+static int
+qemu_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ unsigned long nsec = qemu_get_walltime();
+ unsigned long sec = nsec / NSEC_PER_SEC;
+ rtc_time_to_tm(sec, tm);
+ return 0;
+}
+
+static int
+qemu_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ return -EOPNOTSUPP;
+}
+
+static int
+qemu_rtc_set_mmss(struct device *dev, unsigned long sec)
+{
+ return -EOPNOTSUPP;
+}
+
+static const struct rtc_class_ops qemu_rtc_ops = {
+ .read_time = qemu_rtc_read_time,
+ .set_time = qemu_rtc_set_time,
+ .set_mmss = qemu_rtc_set_mmss,
+};
+#endif /* CONFIG_RTC_DRV_ALPHA_QEMU */
+
+#if defined(CONFIG_RTC_DRV_ALPHA) || defined(CONFIG_RTC_DRV_ALPHA_QEMU)
static int __init
-alpha_rtc_init(void)
+rtc_init(void)
{
const struct rtc_class_ops *ops;
struct platform_device *pdev;
struct rtc_device *rtc;
const char *name;
+#ifdef CONFIG_RTC_DRV_ALPHA_QEMU
+ if (alpha_using_qemu) {
+ name = "rtc-qemu";
+ ops = &qemu_rtc_ops;
+
+ pdev = platform_device_register_simple(name, -1, NULL, 0);
+ rtc = devm_rtc_device_register(&pdev->dev, name, ops, NULL);
+ if (!IS_ERR(rtc))
+ platform_set_drvdata(pdev, rtc);
+ }
+#endif /* ALPHA_QEMU */
+
+#ifdef CONFIG_RTC_DRV_ALPHA
init_rtc_epoch();
name = "rtc-alpha";
ops = &alpha_rtc_ops;
-
#ifdef HAVE_REMOTE_RTC
if (alpha_mv.rtc_boot_cpu_only)
ops = &remote_rtc_ops;
@@ -314,10 +364,11 @@ alpha_rtc_init(void)
pdev = platform_device_register_simple(name, -1, NULL, 0);
rtc = devm_rtc_device_register(&pdev->dev, name, ops, THIS_MODULE);
- if (IS_ERR(rtc))
- return PTR_ERR(rtc);
+ if (!IS_ERR(rtc))
+ platform_set_drvdata(pdev, rtc);
+#endif /* ALPHA */
- platform_set_drvdata(pdev, rtc);
return 0;
}
-device_initcall(alpha_rtc_init);
+device_initcall(rtc_init);
+#endif
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index a4d3f9a..1884ca4 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -631,6 +631,13 @@ config RTC_DRV_ALPHA
Direct support for the real-time clock found on every Alpha
system, specifically MC146818 compatibles. If in doubt, say Y.
+config RTC_DRV_ALPHA_QEMU
+ bool "Alpha QEMU paravirtual RTC"
+ depends on ALPHA
+ default y if ALPHA_QEMU
+ help
+ Support for the paravirtual real-time clock found in QEMU.
+
config RTC_DRV_VRTC
tristate "Virtual RTC for Intel MID platforms"
depends on X86_INTEL_MID
--
1.8.1.4
Use WTINT to wait for the next interrupt. Squash the WTINT call
if the PALcode doesn't support it (e.g. MILO). No attempt is yet
made to skip clock ticks during normal scheduling in order to stay
in power down mode longer.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/include/asm/pal.h | 1 +
arch/alpha/include/uapi/asm/pal.h | 1 +
arch/alpha/kernel/process.c | 15 +++++++++++++++
arch/alpha/kernel/traps.c | 12 ++++++++++++
4 files changed, 29 insertions(+)
diff --git a/arch/alpha/include/asm/pal.h b/arch/alpha/include/asm/pal.h
index 6fcd2b5..e78ec9b 100644
--- a/arch/alpha/include/asm/pal.h
+++ b/arch/alpha/include/asm/pal.h
@@ -89,6 +89,7 @@ __CALL_PAL_W1(wrmces, unsigned long);
__CALL_PAL_RW2(wrperfmon, unsigned long, unsigned long, unsigned long);
__CALL_PAL_W1(wrusp, unsigned long);
__CALL_PAL_W1(wrvptptr, unsigned long);
+__CALL_PAL_RW1(wtint, unsigned long, unsigned long);
/*
* TB routines..
diff --git a/arch/alpha/include/uapi/asm/pal.h b/arch/alpha/include/uapi/asm/pal.h
index 3c0ce08..dfc8140 100644
--- a/arch/alpha/include/uapi/asm/pal.h
+++ b/arch/alpha/include/uapi/asm/pal.h
@@ -46,6 +46,7 @@
#define PAL_rdusp 58
#define PAL_whami 60
#define PAL_retsys 61
+#define PAL_wtint 62
#define PAL_rti 63
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index f2360a7..3130f13 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -46,6 +46,21 @@
void (*pm_power_off)(void) = machine_power_off;
EXPORT_SYMBOL(pm_power_off);
+/*
+ * Sleep the CPU.
+ * EV6, LCA45 and QEMU know how to power down, skipping N timer interrupts.
+ */
+void arch_cpu_idle(void)
+{
+ wtint(0);
+ local_irq_enable();
+}
+
+void arch_cpu_idle_dead(void)
+{
+ wtint(INT_MAX);
+}
+
struct halt_info {
int mode;
char *restart_cmd;
diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
index affccb9..991f6c3 100644
--- a/arch/alpha/kernel/traps.c
+++ b/arch/alpha/kernel/traps.c
@@ -243,6 +243,18 @@ do_entIF(unsigned long type, struct pt_regs *regs)
(const char *)(data[1] | (long)data[2] << 32),
data[0]);
}
+ if (type == 4) {
+ /* If CALL_PAL WTINT is not supported by the PALcode,
+ "emulate" it by overwriting the insn. */
+ unsigned int *pinsn
+ = (unsigned int *) regs->pc - 1;
+ if (*pinsn == PAL_wtint) {
+ *pinsn = 0x47e01400; /* mov 0,$0 */
+ imb();
+ regs->r0 = 0;
+ return;
+ }
+ }
die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"),
regs, type, NULL);
}
--
1.8.1.4
This kernel/user split was done long ago for other architectures.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/include/asm/param.h | 8 +++++---
arch/alpha/include/uapi/asm/param.h | 7 -------
2 files changed, 5 insertions(+), 10 deletions(-)
diff --git a/arch/alpha/include/asm/param.h b/arch/alpha/include/asm/param.h
index bf46af5..a5b68b2 100644
--- a/arch/alpha/include/asm/param.h
+++ b/arch/alpha/include/asm/param.h
@@ -3,7 +3,9 @@
#include <uapi/asm/param.h>
-#define HZ CONFIG_HZ
-#define USER_HZ HZ
-# define CLOCKS_PER_SEC HZ /* frequency at which times() counts */
+# undef HZ
+# define HZ CONFIG_HZ
+# define USER_HZ 1024
+# define CLOCKS_PER_SEC USER_HZ /* frequency at which times() counts */
+
#endif /* _ASM_ALPHA_PARAM_H */
diff --git a/arch/alpha/include/uapi/asm/param.h b/arch/alpha/include/uapi/asm/param.h
index 29daed8..dbcd983 100644
--- a/arch/alpha/include/uapi/asm/param.h
+++ b/arch/alpha/include/uapi/asm/param.h
@@ -1,13 +1,7 @@
#ifndef _UAPI_ASM_ALPHA_PARAM_H
#define _UAPI_ASM_ALPHA_PARAM_H
-/* ??? Gross. I don't want to parameterize this, and supposedly the
- hardware ignores reprogramming. We also need userland buy-in to the
- change in HZ, since this is visible in the wait4 resources etc. */
-
-#ifndef __KERNEL__
#define HZ 1024
-#endif
#define EXEC_PAGESIZE 8192
@@ -17,5 +11,4 @@
#define MAXHOSTNAMELEN 64 /* max length of hostname */
-
#endif /* _UAPI_ASM_ALPHA_PARAM_H */
--
1.8.1.4
The code as written is correct, and will be used by QEMU emulation.
Signed-off-by: Richard Henderson <[email protected]>
---
arch/alpha/kernel/sys_dp264.c | 8 --------
1 file changed, 8 deletions(-)
diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c
index 5bf401f..6c35159 100644
--- a/arch/alpha/kernel/sys_dp264.c
+++ b/arch/alpha/kernel/sys_dp264.c
@@ -190,9 +190,6 @@ static struct irq_chip clipper_irq_type = {
static void
dp264_device_interrupt(unsigned long vector)
{
-#if 1
- printk("dp264_device_interrupt: NOT IMPLEMENTED YET!!\n");
-#else
unsigned long pld;
unsigned int i;
@@ -210,12 +207,7 @@ dp264_device_interrupt(unsigned long vector)
isa_device_interrupt(vector);
else
handle_irq(16 + i);
-#if 0
- TSUNAMI_cchip->dir0.csr = 1UL << i; mb();
- tmp = TSUNAMI_cchip->dir0.csr;
-#endif
}
-#endif
}
static void
--
1.8.1.4
On Tue, Jul 16, 2013 at 10:34 AM, Richard Henderson <[email protected]> wrote:
> Use WTINT to wait for the next interrupt. Squash the WTINT call
> if the PALcode doesn't support it (e.g. MILO). No attempt is yet
> made to skip clock ticks during normal scheduling in order to stay
> in power down mode longer.
The architecture reference manual says
> The counter, PCC, may increment at a lower rate or may stop entirely
> during wtint execution. This side effect is implementation dependent.
Is that anything to worry about?
>
> Signed-off-by: Richard Henderson <[email protected]>
> ---
> arch/alpha/include/asm/pal.h | 1 +
> arch/alpha/include/uapi/asm/pal.h | 1 +
> arch/alpha/kernel/process.c | 15 +++++++++++++++
> arch/alpha/kernel/traps.c | 12 ++++++++++++
> 4 files changed, 29 insertions(+)
>
> diff --git a/arch/alpha/include/asm/pal.h b/arch/alpha/include/asm/pal.h
> index 6fcd2b5..e78ec9b 100644
> --- a/arch/alpha/include/asm/pal.h
> +++ b/arch/alpha/include/asm/pal.h
> @@ -89,6 +89,7 @@ __CALL_PAL_W1(wrmces, unsigned long);
> __CALL_PAL_RW2(wrperfmon, unsigned long, unsigned long, unsigned long);
> __CALL_PAL_W1(wrusp, unsigned long);
> __CALL_PAL_W1(wrvptptr, unsigned long);
> +__CALL_PAL_RW1(wtint, unsigned long, unsigned long);
>
> /*
> * TB routines..
> diff --git a/arch/alpha/include/uapi/asm/pal.h b/arch/alpha/include/uapi/asm/pal.h
> index 3c0ce08..dfc8140 100644
> --- a/arch/alpha/include/uapi/asm/pal.h
> +++ b/arch/alpha/include/uapi/asm/pal.h
> @@ -46,6 +46,7 @@
> #define PAL_rdusp 58
> #define PAL_whami 60
> #define PAL_retsys 61
> +#define PAL_wtint 62
> #define PAL_rti 63
>
>
> diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
> index f2360a7..3130f13 100644
> --- a/arch/alpha/kernel/process.c
> +++ b/arch/alpha/kernel/process.c
> @@ -46,6 +46,21 @@
> void (*pm_power_off)(void) = machine_power_off;
> EXPORT_SYMBOL(pm_power_off);
>
> +/*
> + * Sleep the CPU.
> + * EV6, LCA45 and QEMU know how to power down, skipping N timer interrupts.
> + */
> +void arch_cpu_idle(void)
> +{
> + wtint(0);
> + local_irq_enable();
> +}
> +
> +void arch_cpu_idle_dead(void)
> +{
> + wtint(INT_MAX);
> +}
> +
> struct halt_info {
> int mode;
> char *restart_cmd;
> diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c
> index affccb9..991f6c3 100644
> --- a/arch/alpha/kernel/traps.c
> +++ b/arch/alpha/kernel/traps.c
> @@ -243,6 +243,18 @@ do_entIF(unsigned long type, struct pt_regs *regs)
> (const char *)(data[1] | (long)data[2] << 32),
> data[0]);
> }
> + if (type == 4) {
> + /* If CALL_PAL WTINT is not supported by the PALcode,
> + "emulate" it by overwriting the insn. */
The pseudo-code for WTINT contains an IF(implemented) check, where the
ELSE case just does v0 <- 0. So is overwriting with nop just an
optimization to avoid the (expensive) PAL call? If it is, could we
clarify the comment?
> + unsigned int *pinsn
> + = (unsigned int *) regs->pc - 1;
> + if (*pinsn == PAL_wtint) {
> + *pinsn = 0x47e01400; /* mov 0,$0 */
> + imb();
> + regs->r0 = 0;
> + return;
> + }
> + }
> die_if_kernel((type == 1 ? "Kernel Bug" : "Instruction fault"),
> regs, type, NULL);
> }
> --
> 1.8.1.4
>
On 07/16/2013 10:17 PM, Matt Turner wrote:
> On Tue, Jul 16, 2013 at 10:34 AM, Richard Henderson <[email protected]> wrote:
>> Use WTINT to wait for the next interrupt. Squash the WTINT call
>> if the PALcode doesn't support it (e.g. MILO). No attempt is yet
>> made to skip clock ticks during normal scheduling in order to stay
>> in power down mode longer.
>
> The architecture reference manual says
>
>> The counter, PCC, may increment at a lower rate or may stop entirely
>> during wtint execution. This side effect is implementation dependent.
>
> Is that anything to worry about?
Hmm, yes. It means that we can't use both this and rpcc as a clocksource.
Which we can't do while SMP either, so perhaps that's not so bad. So, right
now, with an SMP EV6 system we ought not worry. Care to report whether that
hypothesis is true?
It may well be a tradeoff we want to CONFIG though, since nothing in the EV5
thru PCA56 can make use of the power down state, and were often uniprocessor
systems...
>> @@ -243,6 +243,18 @@ do_entIF(unsigned long type, struct pt_regs *regs)
>> (const char *)(data[1] | (long)data[2] << 32),
>> data[0]);
>> }
>> + if (type == 4) {
>> + /* If CALL_PAL WTINT is not supported by the PALcode,
>> + "emulate" it by overwriting the insn. */
>
> The pseudo-code for WTINT contains an IF(implemented) check, where the
> ELSE case just does v0 <- 0. So is overwriting with nop just an
> optimization to avoid the (expensive) PAL call? If it is, could we
> clarify the comment?
No, notice where this code is: entIF, aka SIGILL. Looking at MILO sources
I can tell you that WTINT isn't implemented at all. Overwriting with the
mov insn is what I call "emulating" the unimplemented PALcall.
r~
On Tue, Jul 16, 2013 at 10:34:08AM -0700, Richard Henderson wrote:
> The series seems pretty stable under QEMU, but I have no real hardware
> on which to test -- the whole reason I'm interested in QEMU of course.
> So I'm hoping that someone will notice this and help me out with testing.
Tested the patch series applied against 3.10.1 on a 3-CPU ES45. System came
up fine but I noticed date was wrong and had been reset back to the start
of the epoch (Jan 1 1970). Appears it is not reading hardware clock
correctly on boot.
I manually set the clock to the correct time but noticed it is running slow,
indeed in a period of 60s the system clock incremented by 21s (+/-1s) so
would appear clock is running slow by a factor given by the number of CPUs.
Cheers
Michael
On 07/17/2013 06:14 PM, Michael Cree wrote:
> Tested the patch series applied against 3.10.1 on a 3-CPU ES45. System came
> up fine but I noticed date was wrong and had been reset back to the start
> of the epoch (Jan 1 1970). Appears it is not reading hardware clock
> correctly on boot.
Please scan the dmesg for the "Using epoch 2000 for rtc year 13" line,
and compare vs a similar message on the previous kernel.
> I manually set the clock to the correct time but noticed it is running slow,
> indeed in a period of 60s the system clock incremented by 21s (+/-1s) so
> would appear clock is running slow by a factor given by the number of CPUs.
That's curious. I wonder if somehow I botched the smp changes, and I'm
not getting the timer interrupt either (1) enabled or (2) registered on
the secondary cpus...
Perhaps /proc/interrupts will confirm or deny this?
r~
On Thu, Jul 18, 2013 at 06:38:14AM -0700, Richard Henderson wrote:
> On 07/17/2013 06:14 PM, Michael Cree wrote:
> > Tested the patch series applied against 3.10.1 on a 3-CPU ES45. System came
> > up fine but I noticed date was wrong and had been reset back to the start
> > of the epoch (Jan 1 1970). Appears it is not reading hardware clock
> > correctly on boot.
>
> Please scan the dmesg for the "Using epoch 2000 for rtc year 13" line,
> and compare vs a similar message on the previous kernel.
The kernel without the patch set has the "Using epoch 2000" line and
the kernel with the patch set is missing the "Using epoch 2000" line.
The differences in config between the two kernels are:
diff /boot/config-3.10.1-titan-p1+ /boot/config-3.10.1-titan-rth2+
25c25
< CONFIG_LOCALVERSION="-titan-p1"
---
> CONFIG_LOCALVERSION="-titan-rth2"
44c44,53
< CONFIG_GENERIC_CMOS_UPDATE=y
---
> CONFIG_GENERIC_CLOCKEVENTS=y
> CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
>
> #
> # Timers subsystem
> #
> CONFIG_HZ_PERIODIC=y
> # CONFIG_NO_HZ_IDLE is not set
> # CONFIG_NO_HZ is not set
> # CONFIG_HIGH_RES_TIMERS is not set
253a263
> # CONFIG_ALPHA_QEMU is not set
277a288,293
> # CONFIG_HZ_32 is not set
> # CONFIG_HZ_64 is not set
> # CONFIG_HZ_128 is not set
> # CONFIG_HZ_256 is not set
> CONFIG_HZ_1024=y
> # CONFIG_HZ_1200 is not set
> > I manually set the clock to the correct time but noticed it is running slow,
> > indeed in a period of 60s the system clock incremented by 21s (+/-1s) so
> > would appear clock is running slow by a factor given by the number of CPUs.
>
> That's curious. I wonder if somehow I botched the smp changes, and I'm
> not getting the timer interrupt either (1) enabled or (2) registered on
> the secondary cpus...
>
> Perhaps /proc/interrupts will confirm or deny this?
On the kernel without the patch set the dummy-RTC timer interrupt received
30876 interrupts in a 30s period which is within measurement uncertainty of
the expected 30*1024 = 30720 interrupts.
On the kernel with the patch set the dummy-RTC timer interrupt received 14419
interrupts on CPU-1, 13935 interrupts on CPU-2 and 2619 interrupts on CPU-3
over a 30s period.
Cheers
Michael.
On 07/18/2013 02:28 PM, Michael Cree wrote:
> The kernel without the patch set has the "Using epoch 2000" line and
> the kernel with the patch set is missing the "Using epoch 2000" line.
Ah hah. You appear to be missing CONFIG_RTC_DRV_ALPHA in the new kernel, so
you have no support for the RTC at all. Please re-try.
> On the kernel without the patch set the dummy-RTC timer interrupt received
> 30876 interrupts in a 30s period which is within measurement uncertainty of
> the expected 30*1024 = 30720 interrupts.
>
> On the kernel with the patch set the dummy-RTC timer interrupt received 14419
> interrupts on CPU-1, 13935 interrupts on CPU-2 and 2619 interrupts on CPU-3
> over a 30s period.
Grr. Naturally, the old kernel obfuscates which timers are being delivered
where. So a direct comparison doesn't appear possible. But those separated
numbers seem uncomfortably low.
I've just pushed a new branch to
git://github.com/rth7680/linux.git axp-qemu-7
Please try that with CONFIG_ALPHA_WTINT disabled. I'd like to eliminate that
as a source of uncertainty for your system.
r~
On Thu, Jul 18, 2013 at 03:04:25PM -0700, Richard Henderson wrote:
> On 07/18/2013 02:28 PM, Michael Cree wrote:
> > On the kernel without the patch set the dummy-RTC timer interrupt received
> > 30876 interrupts in a 30s period which is within measurement uncertainty of
> > the expected 30*1024 = 30720 interrupts.
> >
> > On the kernel with the patch set the dummy-RTC timer interrupt received 14419
> > interrupts on CPU-1, 13935 interrupts on CPU-2 and 2619 interrupts on CPU-3
> > over a 30s period.
>
> Grr. Naturally, the old kernel obfuscates which timers are being delivered
> where. So a direct comparison doesn't appear possible. But those separated
> numbers seem uncomfortably low.
>
> I've just pushed a new branch to
>
> git://github.com/rth7680/linux.git axp-qemu-7
>
> Please try that with CONFIG_ALPHA_WTINT disabled. I'd like to eliminate that
> as a source of uncertainty for your system.
Running that axp-qemu-7 branch with RTC_DRV_ALPHA on and ALPHA_WTINT off
still results in a rather slow clock. (Does start up with correct time
now that I have RTC_DRV_ALPHA selected.)
Over a 30s period still get too few timer interrupts: 843, 28805 and 1434
interrupts on the CPUs.
Cheers
Michael