Index: linux-2.6.15/drivers/char/Kconfig
===================================================================
--- linux-2.6.15.orig/drivers/char/Kconfig	2006-01-10 01:50:47.000000000 +0000
+++ linux-2.6.15/drivers/char/Kconfig	2006-01-10 01:51:43.000000000 +0000
@@ -695,7 +695,7 @@
 
 config RTC
 	tristate "Enhanced Real Time Clock Support"
-	depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV
+	depends on !PPC32 && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARCH_SA1100 && !ARCH_PXA
 	---help---
 	  If you say Y here and create a character special file /dev/rtc with
 	  major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -741,6 +741,19 @@
 	  via the file /proc/rtc and its behaviour is set by various ioctls on
 	  /dev/rtc.
 
+config SA1100_RTC
+	bool "SA11x0/PXA2xx RTC support"
+	depends on ARCH_SA1100 || ARCH_PXA
+	help
+	  If you say Y here and create a character special file /dev/rtc with
+	  major number 10 and minor number 135 using mknod ("man mknod"), you
+	  will get access to the real time clock built into your SA11x0 or
+	  PXA2xx CPU. It reports status information via the file /proc/rtc
+	  and its behaviour is set by various ioctls on /dev/rtc.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called sa1100-rtc.
+
 config GEN_RTC
 	tristate "Generic /dev/rtc emulation"
 	depends on RTC!=y && !IA64 && !ARM && !M32R && !SPARC && !FRV
Index: linux-2.6.15/drivers/char/Makefile
===================================================================
--- linux-2.6.15.orig/drivers/char/Makefile	2006-01-10 01:50:47.000000000 +0000
+++ linux-2.6.15/drivers/char/Makefile	2006-01-10 01:51:04.000000000 +0000
@@ -61,6 +61,7 @@
 obj-$(CONFIG_HPET) += hpet.o
 obj-$(CONFIG_GEN_RTC) += genrtc.o
 obj-$(CONFIG_EFI_RTC) += efirtc.o
+obj-$(CONFIG_SA1100_RTC) += sa1100-rtc.o
 obj-$(CONFIG_SGI_DS1286) += ds1286.o
 obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
 obj-$(CONFIG_DS1302) += ds1302.o
Index: linux-2.6.15/drivers/char/sa1100-rtc.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.15/drivers/char/sa1100-rtc.c	2006-01-10 01:51:04.000000000 +0000
@@ -0,0 +1,432 @@
+/*
+ * linux/drivers/char/sa1100-rtc.c
+ *
+ * Real Time Clock interface for Linux on StrongARM SA1x00
+ * (and XScale PXA2xx which shares the same RTC register definitions)
+ *
+ * Copyright (c) 2000 Nils Faerber
+ *
+ * Based on rtc.c by Paul Gortmaker
+ *
+ * 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.
+ *
+ * 1.02 2002-07-15 Andrew Christian <andrew.christian@hp.com>
+ *      - added pm_ routines to shut off extraneous interrupts while asleep
+ *
+ * 1.01	2002-07-09 Nils Faerber <nils@kernelconcepts.de>
+ *	- fixed rtc_poll() so that select() now works
+ *
+ * 1.00	2001-06-08 Nicolas Pitre <nico@cam.org>
+ *	- added periodic timer capability using OSMR1
+ *	- flag compatibility with other RTC chips
+ *	- permission checks for ioctls
+ *	- major cleanup, partial rewrite
+ *
+ * 0.03	2001-03-07 CIH <cih@coventive.com>
+ *	- Modify the bug setups RTC clock.
+ *
+ * 0.02	2001-02-27 Nils Faerber <nils@kernelconcepts.de>
+ *	- removed mktime(), added alarm irq clear
+ *
+ * 0.01	2000-10-01 Nils Faerber <nils@kernelconcepts.de>
+ *	- initial release
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/interrupt.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/rtc.h>
+#include <linux/pm.h>
+
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+#ifdef CONFIG_ARCH_PXA
+#include <asm/arch/pxa-regs.h>
+#endif
+
+#define	DRIVER_VERSION		"1.02"
+
+#define TIMER_FREQ		CLOCK_TICK_RATE
+
+#define RTC_DEF_DIVIDER		32768 - 1
+#define RTC_DEF_TRIM		0
+
+/* Those are the bits from a classic RTC we want to mimic */
+#define RTC_IRQF		0x80	/* any of the following 3 is active */
+#define RTC_PF			0x40
+#define RTC_AF			0x20
+#define RTC_UF			0x10
+
+//static unsigned long rtc_status;
+//static unsigned long rtc_irq_data;
+static unsigned long rtc_freq = 1024;
+static struct rtc_time rtc_alarm;
+
+//static struct fasync_struct *rtc_async_queue;
+static DECLARE_WAIT_QUEUE_HEAD(rtc_wait);
+
+extern spinlock_t rtc_lock;
+
+static int rtc_update_alarm(struct rtc_time *alrm)
+{
+	struct rtc_time alarm_tm, now_tm;
+	unsigned long now, time;
+	int ret;
+
+	do {
+		 now = RCNR;
+		 rtc_time_to_tm(now, &now_tm);
+		 rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
+		 ret = rtc_tm_to_time(&alarm_tm, &time);
+		 if (ret != 0)
+			  break;
+
+		 RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
+		 RTAR = time;
+	} while (now != RCNR);
+
+	return ret;
+}
+
+static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int rtsr;
+	unsigned long events = 0;
+
+	spin_lock(&rtc_lock);
+
+	rtsr = RTSR;
+	/* clear interrupt sources */
+	RTSR = 0;
+	RTSR = (RTSR_AL|RTSR_HZ) & (rtsr >> 2);
+
+	/* clear alarm interrupt if it has occurred */
+	if (rtsr & RTSR_AL)
+		 rtsr &= ~RTSR_ALE;
+	RTSR = rtsr & (RTSR_ALE|RTSR_HZE);
+
+	/* update irq data & counter */
+	if (rtsr & RTSR_AL)
+		 events |= RTC_AF|RTC_IRQF;
+	if (rtsr & RTSR_HZ)
+		 events |= RTC_UF|RTC_IRQF;
+
+	rtc_update(1, events);
+
+	if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
+		 rtc_update_alarm(&rtc_alarm);
+
+	spin_unlock(&rtc_lock);
+
+	return IRQ_HANDLED;
+}
+
+#if 0
+static irqreturn_t timer1_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/*
+	 * If we match for the first time, the periodic interrupt flag won't
+	 * be set.  If it is, then we did wrap around (very unlikely but
+	 * still possible) and compute the amount of missed periods.
+	 * The match reg is updated only when the data is actually retrieved
+	 * to avoid unnecessary interrupts.
+	 */
+	OSSR = OSSR_M1;	/* clear match on timer1 */
+	if (rtc_irq_data & RTC_PF) {
+		rtc_irq_data += (rtc_freq * ((1<<30)/(TIMER_FREQ>>2))) << 8;
+	} else {
+		rtc_irq_data += (0x100|RTC_PF|RTC_IRQF);
+	}
+
+	wake_up_interruptible(&rtc_wait);
+	kill_fasync (&rtc_async_queue, SIGIO, POLL_IN);
+
+	return IRQ_HANDLED;
+}
+#endif
+
+static int sa1100_rtc_open(void)
+{
+	int ret;
+
+	ret = request_irq(IRQ_RTC1Hz, rtc_interrupt, SA_INTERRUPT, "rtc 1Hz", NULL);
+	if (ret) {
+		 printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTC1Hz);
+		 goto fail_ui;
+	}
+	ret = request_irq(IRQ_RTCAlrm, rtc_interrupt, SA_INTERRUPT, "rtc Alrm", NULL);
+	if (ret) {
+		 printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_RTCAlrm);
+		 goto fail_ai;
+	}
+#if 0
+	ret = request_irq(IRQ_OST1, timer1_interrupt, SA_INTERRUPT, "rtc timer", NULL);
+	if (ret) {
+		 printk(KERN_ERR "rtc: IRQ%d already in use.\n", IRQ_OST1);
+		 goto fail_pi;
+	}
+	rtc_irq_data = 0;
+#endif
+	return 0;
+
+ fail_pi:
+	free_irq(IRQ_RTCAlrm, NULL);
+ fail_ai:
+	free_irq(IRQ_RTC1Hz, NULL);
+ fail_ui:
+	return ret;
+}
+
+static void sa1100_rtc_release(void)
+{
+	spin_lock_irq (&rtc_lock);
+	RTSR = 0;
+	OIER &= ~OIER_E1;
+	OSSR = OSSR_M1;
+	spin_unlock_irq (&rtc_lock);
+
+//     free_irq(IRQ_OST1, NULL);
+	free_irq(IRQ_RTCAlrm, NULL);
+	free_irq(IRQ_RTC1Hz, NULL);
+}
+
+#if 0
+ssize_t rtc_read(struct file *file, char *buf, size_t count, loff_t *ppos)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long data;
+	ssize_t retval;
+
+	if (count < sizeof(unsigned long))
+		return -EINVAL;
+
+	add_wait_queue(&rtc_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	for (;;) {
+		spin_lock_irq (&rtc_lock);
+		data = rtc_irq_data;
+		if (data != 0) {
+			rtc_irq_data = 0;
+			break;
+		}
+		spin_unlock_irq (&rtc_lock);
+
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			goto out;
+		}
+
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			goto out;
+		}
+
+		schedule();
+	}
+
+	if (data & RTC_PF) {
+		/* interpolate missed periods and set match for the next one */
+		unsigned long period = TIMER_FREQ/rtc_freq;
+		unsigned long oscr = OSCR;
+		unsigned long osmr1 = OSMR1;
+		unsigned long missed = (oscr - osmr1)/period;
+		data += missed << 8;
+		OSSR = OSSR_M1;	/* clear match on timer 1 */
+		OSMR1 = osmr1 + (missed + 1)*period;
+		/* Ensure we didn't miss another match in the mean time.
+		   Here we compare (match - OSCR)  against 8 instead of 0 --
+		   see comment in pxa_timer_interrupt() for explanation. */
+		while( (signed long)((osmr1 = OSMR1) - OSCR) <= 8 ) {
+			data += 0x100;
+			OSSR = OSSR_M1;	/* clear match on timer 1 */
+			OSMR1 = osmr1 + period;
+		}
+	}
+	spin_unlock_irq (&rtc_lock);
+
+	data -= 0x100;	/* the first IRQ wasn't actually missed */
+
+	retval = put_user(data, (unsigned long *)buf);
+	if (!retval)
+		retval = sizeof(unsigned long);
+
+out:
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rtc_wait, &wait);
+	return retval;
+}
+#endif
+
+static int sa1100_rtc_ioctl(unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case RTC_AIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		RTSR &= ~RTSR_ALE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_AIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_ALE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		RTSR &= ~RTSR_HZE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_HZE;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_PIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		OIER &= ~OIER_E1;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+#if 0
+	case RTC_PIE_ON:
+		if ((rtc_freq > 64) && !capable(CAP_SYS_RESOURCE))
+			return -EACCES;
+		spin_lock_irq(&rtc_lock);
+		OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
+		OIER |= OIER_E1;
+//		rtc_irq_data = 0;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_IRQP_READ:
+		return put_user(rtc_freq, (unsigned long *)arg);
+	case RTC_IRQP_SET:
+		if (arg < 1 || arg > TIMER_FREQ)
+				 return -EINVAL;
+		if ((arg > 64) && (!capable(CAP_SYS_RESOURCE)))
+				 return -EACCES;
+		rtc_freq = arg;
+		return 0;
+#endif
+	}
+	return -EINVAL;
+}
+
+static int sa1100_rtc_read_time(struct rtc_time *tm)
+{
+	rtc_time_to_tm(RCNR, tm);
+	return 0;
+}
+
+static int sa1100_rtc_set_time(struct rtc_time *tm)
+{
+	unsigned long time;
+	int ret;
+
+	ret = rtc_tm_to_time(tm, &time);
+	if (ret == 0)
+		 RCNR = time;
+	return ret;
+}
+
+static int sa1100_rtc_read_alarm(struct rtc_wkalrm *alrm)
+{
+	memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
+	alrm->pending = RTSR & RTSR_AL ? 1 : 0;
+	return 0;
+}
+
+static int sa1100_rtc_set_alarm(struct rtc_wkalrm *alrm)
+{
+	int ret;
+
+	spin_lock_irq(&rtc_lock);
+	ret = rtc_update_alarm(&alrm->time);
+	if (ret == 0) {
+		 memcpy(&rtc_alarm, &alrm->time, sizeof(struct rtc_time));
+
+		 if (alrm->enabled)
+			  enable_irq_wake(IRQ_RTCAlrm);
+		 else
+			  disable_irq_wake(IRQ_RTCAlrm);
+	}
+	spin_unlock_irq(&rtc_lock);
+
+	return ret;
+}
+
+static int sa1100_rtc_proc(char *buf)
+{
+	char *p = buf;
+
+	p += sprintf(p, "trim/divider\t: 0x%08x\n", RTTR);
+	p += sprintf(p, "alarm_IRQ\t: %s\n", (RTSR & RTSR_ALE) ? "yes" : "no" );
+	p += sprintf(p, "update_IRQ\t: %s\n", (RTSR & RTSR_HZE) ? "yes" : "no");
+	p += sprintf(p, "periodic_IRQ\t: %s\n", (OIER & OIER_E1) ? "yes" : "no");
+	p += sprintf(p, "periodic_freq\t: %ld\n", rtc_freq);
+
+	return p - buf;
+}
+
+static struct rtc_ops sa1100_rtc_ops = {
+	.owner	   = THIS_MODULE,
+	.open	    = sa1100_rtc_open,
+	.release	 = sa1100_rtc_release,
+	.ioctl	   = sa1100_rtc_ioctl,
+
+	.read_time      = sa1100_rtc_read_time,
+	.set_time	= sa1100_rtc_set_time,
+	.read_alarm     = sa1100_rtc_read_alarm,
+	.set_alarm      = sa1100_rtc_set_alarm,
+	.proc	    = sa1100_rtc_proc,
+};
+
+static int __init rtc_init(void)
+{
+	printk (KERN_INFO "SA1100 Real Time Clock driver v" DRIVER_VERSION "\n");
+
+	/*
+	 * According to the manual we should be able to let RTTR be zero
+	 * and then a default diviser for a 32.768KHz clock is used.
+	 * Apparently this doesn't work, at least for my SA1110 rev 5.
+	 * If the clock divider is uninitialized then reset it to the
+	 * default value to get the 1Hz clock.
+	 */
+	if (RTTR == 0) {
+		RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
+		printk (KERN_WARNING "rtc: warning: initializing default clock divider/trim value\n");
+		/*  The current RTC value probably doesn't make sense either */
+		RCNR = 0;
+	}
+
+	register_rtc(&sa1100_rtc_ops);
+
+	return 0;
+}
+
+static void __exit rtc_exit(void)
+{
+	unregister_rtc(&sa1100_rtc_ops);
+}
+
+module_init(rtc_init);
+module_exit(rtc_exit);
+
+MODULE_AUTHOR("Nils Faerber <nils@@kernelconcepts.de>");
+MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(RTC_MINOR);

