aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-gumstix-2.6.15/rmk-2022-2-rtctime-sa110-pxa255-driver.patch
blob: 87f4ebdc38031dec87130056096136ea0629039e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
Index: linux-2.6.15gum/drivers/char/sa1100-rtc.c
===================================================================
--- /dev/null
+++ linux-2.6.15gum/drivers/char/sa1100-rtc.c
@@ -0,0 +1,297 @@
+/*
+ *	Real Time Clock interface for Linux on StrongARM SA1100
+ *
+ *	Copyright (c) 2000 Nils Faerber
+ *
+ *	Based on rtc.c by Paul Gortmaker
+ *	Date/time conversion routines taken from arch/arm/kernel/time.c
+ *			by Linus Torvalds and Russel King
+ *		and the GNU C Library
+ *	( ... I love the GPL ... just take what you need! ;)
+ *
+ *	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.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/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/poll.h>
+#include <linux/proc_fs.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+
+#ifdef CONFIG_ARCH_PXA
+#include <asm/arch/pxa-regs.h>
+#endif
+
+#include <asm/bitops.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/rtc.h>
+
+#define TIMER_FREQ		3686400
+
+#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_freq = 1024;
+static struct rtc_time rtc_alarm = {
+	.tm_year	= 0,
+	.tm_mon		= 0,
+	.tm_mday	= 0,
+	.tm_hour	= 0,
+	.tm_mon		= 0,
+	.tm_sec		= 0,
+};
+
+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;
+
+	printk("Updating alarm\n");
+	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);
+	printk("set RTAR to %lx, now is %lx\n", time, now);
+
+	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);
+
+	printk(KERN_CRIT "rtc_interrupt: rtsr = %x\n", rtsr);
+
+	/* clear alarm interrupt if it has occurred */
+	if (rtsr & RTSR_AL) {
+		printk(KERN_CRIT "ALARM INTRRUPT\n");
+		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;
+}
+
+
+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;
+	}
+	return 0;
+
+ 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_RTCAlrm, NULL);
+	free_irq(IRQ_RTC1Hz, NULL);
+}
+
+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;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_AIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_ALE;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_OFF:
+		spin_lock_irq(&rtc_lock);
+		RTSR &= ~RTSR_HZE;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	case RTC_UIE_ON:
+		spin_lock_irq(&rtc_lock);
+		RTSR |= RTSR_HZE;
+		spin_unlock_irq(&rtc_lock);
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static void sa1100_rtc_read_time(struct rtc_time *tm)
+{
+	rtc_time_to_tm(RCNR, tm);
+}
+
+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 void sa1100_rtc_read_alarm(struct rtc_wkalrm *alrm)
+{
+	memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
+	alrm->pending = RTSR & RTSR_AL ? 1 : 0;
+}
+
+static int sa1100_rtc_set_alarm(struct rtc_wkalrm *alrm)
+{
+	int ret;
+
+	printk("sa1100_rtc_set_alarm\n");
+
+	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)
+{
+	/*
+	 * 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("SA1100 Realtime Clock Driver (RTC)");
+MODULE_LICENSE("GPL");		/* so says the header */
Index: linux-2.6.15gum/drivers/char/Makefile
===================================================================
--- linux-2.6.15gum.orig/drivers/char/Makefile
+++ linux-2.6.15gum/drivers/char/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_RTC) += rtc.o
 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.15gum/drivers/char/Kconfig
===================================================================
--- linux-2.6.15gum.orig/drivers/char/Kconfig
+++ linux-2.6.15gum/drivers/char/Kconfig
@@ -790,6 +790,10 @@ config COBALT_LCD
 	  This option enables support for the LCD display and buttons found
 	  on Cobalt systems through a misc device.
 
+config SA1100_RTC
+	tristate "SA1100/PXA2xx Real Time Clock"
+	depends on ARCH_SA1100 || ARCH_PXA
+
 config DTLK
 	tristate "Double Talk PC internal speech card support"
 	help