Blame view

drivers/rtc/rtc-omap.c 13 KB
db68b189f   David Brownell   [PATCH] add rtc-o...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  /*
   * TI OMAP1 Real Time Clock interface for Linux
   *
   * Copyright (C) 2003 MontaVista Software, Inc.
   * Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
   *
   * Copyright (C) 2006 David Brownell (new RTC framework)
   *
   * 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.
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/ioport.h>
  #include <linux/delay.h>
  #include <linux/rtc.h>
  #include <linux/bcd.h>
  #include <linux/platform_device.h>
  
  #include <asm/io.h>
db68b189f   David Brownell   [PATCH] add rtc-o...
25
26
27
28
29
30
31
32
33
34
35
36
  
  
  /* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock
   * with century-range alarm matching, driven by the 32kHz clock.
   *
   * The main user-visible ways it differs from PC RTCs are by omitting
   * "don't care" alarm fields and sub-second periodic IRQs, and having
   * an autoadjust mechanism to calibrate to the true oscillator rate.
   *
   * Board-specific wiring options include using split power mode with
   * RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset),
   * and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from
fa5b07820   Sekhar Nori   rtc: omap: let de...
37
38
   * low power modes) for OMAP1 boards (OMAP-L138 has this built into
   * the SoC). See the BOARD-SPECIFIC CUSTOMIZATION comment.
db68b189f   David Brownell   [PATCH] add rtc-o...
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
   */
  
  #define OMAP_RTC_BASE			0xfffb4800
  
  /* RTC registers */
  #define OMAP_RTC_SECONDS_REG		0x00
  #define OMAP_RTC_MINUTES_REG		0x04
  #define OMAP_RTC_HOURS_REG		0x08
  #define OMAP_RTC_DAYS_REG		0x0C
  #define OMAP_RTC_MONTHS_REG		0x10
  #define OMAP_RTC_YEARS_REG		0x14
  #define OMAP_RTC_WEEKS_REG		0x18
  
  #define OMAP_RTC_ALARM_SECONDS_REG	0x20
  #define OMAP_RTC_ALARM_MINUTES_REG	0x24
  #define OMAP_RTC_ALARM_HOURS_REG	0x28
  #define OMAP_RTC_ALARM_DAYS_REG		0x2c
  #define OMAP_RTC_ALARM_MONTHS_REG	0x30
  #define OMAP_RTC_ALARM_YEARS_REG	0x34
  
  #define OMAP_RTC_CTRL_REG		0x40
  #define OMAP_RTC_STATUS_REG		0x44
  #define OMAP_RTC_INTERRUPTS_REG		0x48
  
  #define OMAP_RTC_COMP_LSB_REG		0x4c
  #define OMAP_RTC_COMP_MSB_REG		0x50
  #define OMAP_RTC_OSC_REG		0x54
  
  /* OMAP_RTC_CTRL_REG bit fields: */
  #define OMAP_RTC_CTRL_SPLIT		(1<<7)
  #define OMAP_RTC_CTRL_DISABLE		(1<<6)
  #define OMAP_RTC_CTRL_SET_32_COUNTER	(1<<5)
  #define OMAP_RTC_CTRL_TEST		(1<<4)
  #define OMAP_RTC_CTRL_MODE_12_24	(1<<3)
  #define OMAP_RTC_CTRL_AUTO_COMP		(1<<2)
  #define OMAP_RTC_CTRL_ROUND_30S		(1<<1)
  #define OMAP_RTC_CTRL_STOP		(1<<0)
  
  /* OMAP_RTC_STATUS_REG bit fields: */
  #define OMAP_RTC_STATUS_POWER_UP        (1<<7)
  #define OMAP_RTC_STATUS_ALARM           (1<<6)
  #define OMAP_RTC_STATUS_1D_EVENT        (1<<5)
  #define OMAP_RTC_STATUS_1H_EVENT        (1<<4)
  #define OMAP_RTC_STATUS_1M_EVENT        (1<<3)
  #define OMAP_RTC_STATUS_1S_EVENT        (1<<2)
  #define OMAP_RTC_STATUS_RUN             (1<<1)
  #define OMAP_RTC_STATUS_BUSY            (1<<0)
  
  /* OMAP_RTC_INTERRUPTS_REG bit fields: */
  #define OMAP_RTC_INTERRUPTS_IT_ALARM    (1<<3)
  #define OMAP_RTC_INTERRUPTS_IT_TIMER    (1<<2)
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
90
  static void __iomem	*rtc_base;
db68b189f   David Brownell   [PATCH] add rtc-o...
91

8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
92
93
  #define rtc_read(addr)		__raw_readb(rtc_base + (addr))
  #define rtc_write(val, addr)	__raw_writeb(val, rtc_base + (addr))
db68b189f   David Brownell   [PATCH] add rtc-o...
94

db68b189f   David Brownell   [PATCH] add rtc-o...
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
  /* we rely on the rtc framework to handle locking (rtc->ops_lock),
   * so the only other requirement is that register accesses which
   * require BUSY to be clear are made with IRQs locally disabled
   */
  static void rtc_wait_not_busy(void)
  {
  	int	count = 0;
  	u8	status;
  
  	/* BUSY may stay active for 1/32768 second (~30 usec) */
  	for (count = 0; count < 50; count++) {
  		status = rtc_read(OMAP_RTC_STATUS_REG);
  		if ((status & (u8)OMAP_RTC_STATUS_BUSY) == 0)
  			break;
  		udelay(1);
  	}
  	/* now we have ~15 usec to read/write various registers */
  }
ab6a2d70d   David Brownell   rtc: rtc interfac...
113
  static irqreturn_t rtc_irq(int irq, void *rtc)
db68b189f   David Brownell   [PATCH] add rtc-o...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  {
  	unsigned long		events = 0;
  	u8			irq_data;
  
  	irq_data = rtc_read(OMAP_RTC_STATUS_REG);
  
  	/* alarm irq? */
  	if (irq_data & OMAP_RTC_STATUS_ALARM) {
  		rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
  		events |= RTC_IRQF | RTC_AF;
  	}
  
  	/* 1/sec periodic/update irq? */
  	if (irq_data & OMAP_RTC_STATUS_1S_EVENT)
  		events |= RTC_IRQF | RTC_UF;
ab6a2d70d   David Brownell   rtc: rtc interfac...
129
  	rtc_update_irq(rtc, 1, events);
db68b189f   David Brownell   [PATCH] add rtc-o...
130
131
132
  
  	return IRQ_HANDLED;
  }
16380c153   John Stultz   RTC: Convert rtc ...
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
  static int omap_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
  {
  	u8 reg;
  
  	local_irq_disable();
  	rtc_wait_not_busy();
  	reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
  	if (enabled)
  		reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
  	else
  		reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
  	rtc_wait_not_busy();
  	rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
  	local_irq_enable();
  
  	return 0;
  }
db68b189f   David Brownell   [PATCH] add rtc-o...
150
151
152
153
154
  /* this hardware doesn't support "don't care" alarm fields */
  static int tm2bcd(struct rtc_time *tm)
  {
  	if (rtc_valid_tm(tm) != 0)
  		return -EINVAL;
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
155
156
157
158
  	tm->tm_sec = bin2bcd(tm->tm_sec);
  	tm->tm_min = bin2bcd(tm->tm_min);
  	tm->tm_hour = bin2bcd(tm->tm_hour);
  	tm->tm_mday = bin2bcd(tm->tm_mday);
db68b189f   David Brownell   [PATCH] add rtc-o...
159

fe20ba70a   Adrian Bunk   drivers/rtc/: use...
160
  	tm->tm_mon = bin2bcd(tm->tm_mon + 1);
db68b189f   David Brownell   [PATCH] add rtc-o...
161
162
163
164
  
  	/* epoch == 1900 */
  	if (tm->tm_year < 100 || tm->tm_year > 199)
  		return -EINVAL;
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
165
  	tm->tm_year = bin2bcd(tm->tm_year - 100);
db68b189f   David Brownell   [PATCH] add rtc-o...
166
167
168
169
170
171
  
  	return 0;
  }
  
  static void bcd2tm(struct rtc_time *tm)
  {
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
172
173
174
175
176
  	tm->tm_sec = bcd2bin(tm->tm_sec);
  	tm->tm_min = bcd2bin(tm->tm_min);
  	tm->tm_hour = bcd2bin(tm->tm_hour);
  	tm->tm_mday = bcd2bin(tm->tm_mday);
  	tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
db68b189f   David Brownell   [PATCH] add rtc-o...
177
  	/* epoch == 1900 */
fe20ba70a   Adrian Bunk   drivers/rtc/: use...
178
  	tm->tm_year = bcd2bin(tm->tm_year) + 100;
db68b189f   David Brownell   [PATCH] add rtc-o...
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
  }
  
  
  static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm)
  {
  	/* we don't report wday/yday/isdst ... */
  	local_irq_disable();
  	rtc_wait_not_busy();
  
  	tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG);
  	tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG);
  	tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG);
  	tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG);
  	tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG);
  	tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG);
  
  	local_irq_enable();
  
  	bcd2tm(tm);
  	return 0;
  }
  
  static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm)
  {
  	if (tm2bcd(tm) < 0)
  		return -EINVAL;
  	local_irq_disable();
  	rtc_wait_not_busy();
  
  	rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG);
  	rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG);
  	rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG);
  	rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG);
  	rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG);
  	rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG);
  
  	local_irq_enable();
  
  	return 0;
  }
  
  static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	local_irq_disable();
  	rtc_wait_not_busy();
  
  	alm->time.tm_sec = rtc_read(OMAP_RTC_ALARM_SECONDS_REG);
  	alm->time.tm_min = rtc_read(OMAP_RTC_ALARM_MINUTES_REG);
  	alm->time.tm_hour = rtc_read(OMAP_RTC_ALARM_HOURS_REG);
  	alm->time.tm_mday = rtc_read(OMAP_RTC_ALARM_DAYS_REG);
  	alm->time.tm_mon = rtc_read(OMAP_RTC_ALARM_MONTHS_REG);
  	alm->time.tm_year = rtc_read(OMAP_RTC_ALARM_YEARS_REG);
  
  	local_irq_enable();
  
  	bcd2tm(&alm->time);
a2db8dfce   David Brownell   [PATCH] rtc frame...
235
  	alm->enabled = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG)
db68b189f   David Brownell   [PATCH] add rtc-o...
236
  			& OMAP_RTC_INTERRUPTS_IT_ALARM);
db68b189f   David Brownell   [PATCH] add rtc-o...
237
238
239
240
241
242
243
  
  	return 0;
  }
  
  static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	u8 reg;
db68b189f   David Brownell   [PATCH] add rtc-o...
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
  	if (tm2bcd(&alm->time) < 0)
  		return -EINVAL;
  
  	local_irq_disable();
  	rtc_wait_not_busy();
  
  	rtc_write(alm->time.tm_year, OMAP_RTC_ALARM_YEARS_REG);
  	rtc_write(alm->time.tm_mon, OMAP_RTC_ALARM_MONTHS_REG);
  	rtc_write(alm->time.tm_mday, OMAP_RTC_ALARM_DAYS_REG);
  	rtc_write(alm->time.tm_hour, OMAP_RTC_ALARM_HOURS_REG);
  	rtc_write(alm->time.tm_min, OMAP_RTC_ALARM_MINUTES_REG);
  	rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG);
  
  	reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
  	if (alm->enabled)
  		reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
  	else
  		reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
  	rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
  
  	local_irq_enable();
  
  	return 0;
  }
  
  static struct rtc_class_ops omap_rtc_ops = {
db68b189f   David Brownell   [PATCH] add rtc-o...
270
271
272
273
  	.read_time	= omap_rtc_read_time,
  	.set_time	= omap_rtc_set_time,
  	.read_alarm	= omap_rtc_read_alarm,
  	.set_alarm	= omap_rtc_set_alarm,
16380c153   John Stultz   RTC: Convert rtc ...
274
  	.alarm_irq_enable = omap_rtc_alarm_irq_enable,
db68b189f   David Brownell   [PATCH] add rtc-o...
275
276
277
278
  };
  
  static int omap_rtc_alarm;
  static int omap_rtc_timer;
71fc82245   David Brownell   rtc: rtc-omap foo...
279
  static int __init omap_rtc_probe(struct platform_device *pdev)
db68b189f   David Brownell   [PATCH] add rtc-o...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
  {
  	struct resource		*res, *mem;
  	struct rtc_device	*rtc;
  	u8			reg, new_ctrl;
  
  	omap_rtc_timer = platform_get_irq(pdev, 0);
  	if (omap_rtc_timer <= 0) {
  		pr_debug("%s: no update irq?
  ", pdev->name);
  		return -ENOENT;
  	}
  
  	omap_rtc_alarm = platform_get_irq(pdev, 1);
  	if (omap_rtc_alarm <= 0) {
  		pr_debug("%s: no alarm irq?
  ", pdev->name);
  		return -ENOENT;
  	}
db68b189f   David Brownell   [PATCH] add rtc-o...
298
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
299
300
301
  	if (!res) {
  		pr_debug("%s: RTC resource data missing
  ", pdev->name);
db68b189f   David Brownell   [PATCH] add rtc-o...
302
303
  		return -ENOENT;
  	}
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
304
  	mem = request_mem_region(res->start, resource_size(res), pdev->name);
db68b189f   David Brownell   [PATCH] add rtc-o...
305
306
307
  	if (!mem) {
  		pr_debug("%s: RTC registers at %08x are not free
  ",
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
308
  			pdev->name, res->start);
db68b189f   David Brownell   [PATCH] add rtc-o...
309
310
  		return -EBUSY;
  	}
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
311
312
313
314
315
316
  	rtc_base = ioremap(res->start, resource_size(res));
  	if (!rtc_base) {
  		pr_debug("%s: RTC registers can't be mapped
  ", pdev->name);
  		goto fail;
  	}
db68b189f   David Brownell   [PATCH] add rtc-o...
317
318
319
320
321
322
  	rtc = rtc_device_register(pdev->name, &pdev->dev,
  			&omap_rtc_ops, THIS_MODULE);
  	if (IS_ERR(rtc)) {
  		pr_debug("%s: can't register RTC device, err %ld
  ",
  			pdev->name, PTR_ERR(rtc));
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
323
  		goto fail0;
db68b189f   David Brownell   [PATCH] add rtc-o...
324
325
  	}
  	platform_set_drvdata(pdev, rtc);
558a40f70   David Brownell   rtc-omap build fix
326
  	dev_set_drvdata(&rtc->dev, mem);
db68b189f   David Brownell   [PATCH] add rtc-o...
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
  
  	/* clear pending irqs, and set 1/second periodic,
  	 * which we'll use instead of update irqs
  	 */
  	rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
  
  	/* clear old status */
  	reg = rtc_read(OMAP_RTC_STATUS_REG);
  	if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {
  		pr_info("%s: RTC power up reset detected
  ",
  			pdev->name);
  		rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG);
  	}
  	if (reg & (u8) OMAP_RTC_STATUS_ALARM)
  		rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
  
  	/* handle periodic and alarm irqs */
38515e908   Thomas Gleixner   [PATCH] Scheduled...
345
  	if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,
744bcb13d   Kay Sievers   rtc: struct devic...
346
  			dev_name(&rtc->dev), rtc)) {
db68b189f   David Brownell   [PATCH] add rtc-o...
347
348
349
  		pr_debug("%s: RTC timer interrupt IRQ%d already claimed
  ",
  			pdev->name, omap_rtc_timer);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
350
  		goto fail1;
db68b189f   David Brownell   [PATCH] add rtc-o...
351
  	}
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
352
353
354
  	if ((omap_rtc_timer != omap_rtc_alarm) &&
  		(request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
  			dev_name(&rtc->dev), rtc))) {
db68b189f   David Brownell   [PATCH] add rtc-o...
355
356
357
  		pr_debug("%s: RTC alarm interrupt IRQ%d already claimed
  ",
  			pdev->name, omap_rtc_alarm);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
358
  		goto fail2;
db68b189f   David Brownell   [PATCH] add rtc-o...
359
360
361
362
363
364
365
366
367
  	}
  
  	/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
  	reg = rtc_read(OMAP_RTC_CTRL_REG);
  	if (reg & (u8) OMAP_RTC_CTRL_STOP)
  		pr_info("%s: already running
  ", pdev->name);
  
  	/* force to 24 hour mode */
12b3e038e   Daniel Glöckner   rtc-omap: fix ini...
368
  	new_ctrl = reg & (OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
db68b189f   David Brownell   [PATCH] add rtc-o...
369
370
371
372
  	new_ctrl |= OMAP_RTC_CTRL_STOP;
  
  	/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
  	 *
fa5b07820   Sekhar Nori   rtc: omap: let de...
373
374
375
376
377
  	 *  - Device wake-up capability setting should come through chip
  	 *    init logic. OMAP1 boards should initialize the "wakeup capable"
  	 *    flag in the platform device if the board is wired right for
  	 *    being woken up by RTC alarm. For OMAP-L138, this capability
  	 *    is built into the SoC by the "Deep Sleep" capability.
db68b189f   David Brownell   [PATCH] add rtc-o...
378
379
380
381
382
383
  	 *
  	 *  - Boards wired so RTC_ON_nOFF is used as the reset signal,
  	 *    rather than nPWRON_RESET, should forcibly enable split
  	 *    power mode.  (Some chip errata report that RTC_CTRL_SPLIT
  	 *    is write-only, and always reads as zero...)
  	 */
db68b189f   David Brownell   [PATCH] add rtc-o...
384
385
386
387
388
389
390
391
392
  
  	if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
  		pr_info("%s: split power mode
  ", pdev->name);
  
  	if (reg != new_ctrl)
  		rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);
  
  	return 0;
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
393
  fail2:
2dd93c4f4   Axel Lin   RTC: rtc-omap: Fi...
394
  	free_irq(omap_rtc_timer, rtc);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
395
  fail1:
db68b189f   David Brownell   [PATCH] add rtc-o...
396
  	rtc_device_unregister(rtc);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
397
398
  fail0:
  	iounmap(rtc_base);
db68b189f   David Brownell   [PATCH] add rtc-o...
399
  fail:
19412ce9f   Axel Lin   drivers/rtc/rtc-o...
400
  	release_mem_region(mem->start, resource_size(mem));
db68b189f   David Brownell   [PATCH] add rtc-o...
401
402
  	return -EIO;
  }
71fc82245   David Brownell   rtc: rtc-omap foo...
403
  static int __exit omap_rtc_remove(struct platform_device *pdev)
db68b189f   David Brownell   [PATCH] add rtc-o...
404
  {
a419aef8b   Joe Perches   trivial: remove u...
405
  	struct rtc_device	*rtc = platform_get_drvdata(pdev);
19412ce9f   Axel Lin   drivers/rtc/rtc-o...
406
  	struct resource		*mem = dev_get_drvdata(&rtc->dev);
db68b189f   David Brownell   [PATCH] add rtc-o...
407
408
409
410
411
412
413
  
  	device_init_wakeup(&pdev->dev, 0);
  
  	/* leave rtc running, but disable irqs */
  	rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
  
  	free_irq(omap_rtc_timer, rtc);
8cfde8c1d   Mark A. Greer   rtc: make rtc-oma...
414
415
416
  
  	if (omap_rtc_timer != omap_rtc_alarm)
  		free_irq(omap_rtc_alarm, rtc);
db68b189f   David Brownell   [PATCH] add rtc-o...
417

db68b189f   David Brownell   [PATCH] add rtc-o...
418
  	rtc_device_unregister(rtc);
19412ce9f   Axel Lin   drivers/rtc/rtc-o...
419
420
  	iounmap(rtc_base);
  	release_mem_region(mem->start, resource_size(mem));
db68b189f   David Brownell   [PATCH] add rtc-o...
421
422
423
424
  	return 0;
  }
  
  #ifdef CONFIG_PM
db68b189f   David Brownell   [PATCH] add rtc-o...
425
426
427
428
  static u8 irqstat;
  
  static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
  {
db68b189f   David Brownell   [PATCH] add rtc-o...
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
  	irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);
  
  	/* FIXME the RTC alarm is not currently acting as a wakeup event
  	 * source, and in fact this enable() call is just saving a flag
  	 * that's never used...
  	 */
  	if (device_may_wakeup(&pdev->dev))
  		enable_irq_wake(omap_rtc_alarm);
  	else
  		rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
  
  	return 0;
  }
  
  static int omap_rtc_resume(struct platform_device *pdev)
  {
db68b189f   David Brownell   [PATCH] add rtc-o...
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
  	if (device_may_wakeup(&pdev->dev))
  		disable_irq_wake(omap_rtc_alarm);
  	else
  		rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG);
  	return 0;
  }
  
  #else
  #define omap_rtc_suspend NULL
  #define omap_rtc_resume  NULL
  #endif
  
  static void omap_rtc_shutdown(struct platform_device *pdev)
  {
  	rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
  }
ad28a07bc   Kay Sievers   rtc: fix platform...
461
  MODULE_ALIAS("platform:omap_rtc");
db68b189f   David Brownell   [PATCH] add rtc-o...
462
  static struct platform_driver omap_rtc_driver = {
71fc82245   David Brownell   rtc: rtc-omap foo...
463
  	.remove		= __exit_p(omap_rtc_remove),
db68b189f   David Brownell   [PATCH] add rtc-o...
464
465
466
467
468
469
470
471
472
473
474
  	.suspend	= omap_rtc_suspend,
  	.resume		= omap_rtc_resume,
  	.shutdown	= omap_rtc_shutdown,
  	.driver		= {
  		.name	= "omap_rtc",
  		.owner	= THIS_MODULE,
  	},
  };
  
  static int __init rtc_init(void)
  {
71fc82245   David Brownell   rtc: rtc-omap foo...
475
  	return platform_driver_probe(&omap_rtc_driver, omap_rtc_probe);
db68b189f   David Brownell   [PATCH] add rtc-o...
476
477
478
479
480
481
482
483
484
485
486
  }
  module_init(rtc_init);
  
  static void __exit rtc_exit(void)
  {
  	platform_driver_unregister(&omap_rtc_driver);
  }
  module_exit(rtc_exit);
  
  MODULE_AUTHOR("George G. Davis (and others)");
  MODULE_LICENSE("GPL");