Blame view

drivers/rtc/rtc-at91sam9.c 14.6 KB
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * "RTT as Real Time Clock" driver for AT91SAM9 SoC family
   *
   * (C) 2007 Michel Benoit
   *
   * Based on rtc-at91rm9200.c by Rick Bronson
   *
   * 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.
   */
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
13
  #include <linux/clk.h>
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
14
15
  #include <linux/interrupt.h>
  #include <linux/ioctl.h>
9d42e4651   Jingoo Han   rtc: rtc-at91sam9...
16
  #include <linux/io.h>
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
17
  #include <linux/kernel.h>
43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
18
  #include <linux/mfd/syscon.h>
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
19
  #include <linux/module.h>
1955f213a   Alexandre Belloni   rtc: at91sam9: in...
20
  #include <linux/of.h>
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
21
  #include <linux/platform_device.h>
43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
22
  #include <linux/regmap.h>
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
23
24
  #include <linux/rtc.h>
  #include <linux/slab.h>
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
25
  #include <linux/suspend.h>
6932ff539   Alexandre Belloni   rtc: at91sam9: so...
26
  #include <linux/time.h>
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
27

4cdf854f7   David Brownell   rtc: at91sam9 RTC...
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  /*
   * This driver uses two configurable hardware resources that live in the
   * AT91SAM9 backup power domain (intended to be powered at all times)
   * to implement the Real Time Clock interfaces
   *
   *  - A "Real-time Timer" (RTT) counts up in seconds from a base time.
   *    We can't assign the counter value (CRTV) ... but we can reset it.
   *
   *  - One of the "General Purpose Backup Registers" (GPBRs) holds the
   *    base time, normally an offset from the beginning of the POSIX
   *    epoch (1970-Jan-1 00:00:00 UTC).  Some systems also include the
   *    local timezone's offset.
   *
   * The RTC's value is the RTT counter plus that offset.  The RTC's alarm
   * is likewise a base (ALMV) plus that offset.
   *
   * Not all RTTs will be used as RTCs; some systems have multiple RTTs to
   * choose from, or a "real" RTC module.  All systems have multiple GPBR
   * registers available, likewise usable for more than "RTC" support.
   */
6575bd7cb   Boris BREZILLON   rtc: at91sam9: re...
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  #define AT91_RTT_MR		0x00			/* Real-time Mode Register */
  #define AT91_RTT_RTPRES		(0xffff << 0)		/* Real-time Timer Prescaler Value */
  #define AT91_RTT_ALMIEN		(1 << 16)		/* Alarm Interrupt Enable */
  #define AT91_RTT_RTTINCIEN	(1 << 17)		/* Real Time Timer Increment Interrupt Enable */
  #define AT91_RTT_RTTRST		(1 << 18)		/* Real Time Timer Restart */
  
  #define AT91_RTT_AR		0x04			/* Real-time Alarm Register */
  #define AT91_RTT_ALMV		(0xffffffff)		/* Alarm Value */
  
  #define AT91_RTT_VR		0x08			/* Real-time Value Register */
  #define AT91_RTT_CRTV		(0xffffffff)		/* Current Real-time Value */
  
  #define AT91_RTT_SR		0x0c			/* Real-time Status Register */
  #define AT91_RTT_ALMS		(1 << 0)		/* Real-time Alarm Status */
  #define AT91_RTT_RTTINC		(1 << 1)		/* Real-time Timer Increment */
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
63
64
65
66
67
68
69
70
71
72
73
  /*
   * We store ALARM_DISABLED in ALMV to record that no alarm is set.
   * It's also the reset value for that field.
   */
  #define ALARM_DISABLED	((u32)~0)
  
  
  struct sam9_rtc {
  	void __iomem		*rtt;
  	struct rtc_device	*rtcdev;
  	u32			imr;
43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
74
75
  	struct regmap		*gpbr;
  	unsigned int		gpbr_offset;
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
76
  	int 			irq;
a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
77
  	struct clk		*sclk;
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
78
79
80
  	bool			suspended;
  	unsigned long		events;
  	spinlock_t		lock;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
81
82
83
  };
  
  #define rtt_readl(rtc, field) \
272f1dfa6   Boris BREZILLON   rtc: at91sam9: us...
84
  	readl((rtc)->rtt + AT91_RTT_ ## field)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
85
  #define rtt_writel(rtc, field, val) \
272f1dfa6   Boris BREZILLON   rtc: at91sam9: us...
86
  	writel((val), (rtc)->rtt + AT91_RTT_ ## field)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
87

43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
88
89
90
91
92
93
94
95
96
97
98
99
100
  static inline unsigned int gpbr_readl(struct sam9_rtc *rtc)
  {
  	unsigned int val;
  
  	regmap_read(rtc->gpbr, rtc->gpbr_offset, &val);
  
  	return val;
  }
  
  static inline void gpbr_writel(struct sam9_rtc *rtc, unsigned int val)
  {
  	regmap_write(rtc->gpbr, rtc->gpbr_offset, val);
  }
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
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
  
  /*
   * Read current time and date in RTC
   */
  static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
  	u32 secs, secs2;
  	u32 offset;
  
  	/* read current time offset */
  	offset = gpbr_readl(rtc);
  	if (offset == 0)
  		return -EILSEQ;
  
  	/* reread the counter to help sync the two clock domains */
  	secs = rtt_readl(rtc, VR);
  	secs2 = rtt_readl(rtc, VR);
  	if (secs != secs2)
  		secs = rtt_readl(rtc, VR);
  
  	rtc_time_to_tm(offset + secs, tm);
  
  	dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d
  ", "readtime",
  		1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
  		tm->tm_hour, tm->tm_min, tm->tm_sec);
  
  	return 0;
  }
  
  /*
   * Set current time and date in RTC
   */
  static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
  	int err;
  	u32 offset, alarm, mr;
  	unsigned long secs;
  
  	dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d
  ", "settime",
  		1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
  		tm->tm_hour, tm->tm_min, tm->tm_sec);
  
  	err = rtc_tm_to_time(tm, &secs);
  	if (err != 0)
  		return err;
  
  	mr = rtt_readl(rtc, MR);
  
  	/* disable interrupts */
  	rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
  
  	/* read current time offset */
  	offset = gpbr_readl(rtc);
  
  	/* store the new base time in a battery backup register */
  	secs += 1;
  	gpbr_writel(rtc, secs);
  
  	/* adjust the alarm time for the new base */
  	alarm = rtt_readl(rtc, AR);
  	if (alarm != ALARM_DISABLED) {
  		if (offset > secs) {
  			/* time jumped backwards, increase time until alarm */
  			alarm += (offset - secs);
  		} else if ((alarm + offset) > secs) {
  			/* time jumped forwards, decrease time until alarm */
  			alarm -= (secs - offset);
  		} else {
  			/* time jumped past the alarm, disable alarm */
  			alarm = ALARM_DISABLED;
  			mr &= ~AT91_RTT_ALMIEN;
  		}
  		rtt_writel(rtc, AR, alarm);
  	}
  
  	/* reset the timer, and re-enable interrupts */
  	rtt_writel(rtc, MR, mr | AT91_RTT_RTTRST);
  
  	return 0;
  }
  
  static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
  	struct rtc_time *tm = &alrm->time;
  	u32 alarm = rtt_readl(rtc, AR);
  	u32 offset;
  
  	offset = gpbr_readl(rtc);
  	if (offset == 0)
  		return -EILSEQ;
870a2761a   Julia Lawall   rtc-at91sam9: Cor...
196
  	memset(alrm, 0, sizeof(*alrm));
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
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
  	if (alarm != ALARM_DISABLED && offset != 0) {
  		rtc_time_to_tm(offset + alarm, tm);
  
  		dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d
  ", "readalarm",
  			1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
  			tm->tm_hour, tm->tm_min, tm->tm_sec);
  
  		if (rtt_readl(rtc, MR) & AT91_RTT_ALMIEN)
  			alrm->enabled = 1;
  	}
  
  	return 0;
  }
  
  static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
  	struct rtc_time *tm = &alrm->time;
  	unsigned long secs;
  	u32 offset;
  	u32 mr;
  	int err;
  
  	err = rtc_tm_to_time(tm, &secs);
  	if (err != 0)
  		return err;
  
  	offset = gpbr_readl(rtc);
  	if (offset == 0) {
  		/* time is not set */
  		return -EILSEQ;
  	}
  	mr = rtt_readl(rtc, MR);
  	rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
  
  	/* alarm in the past? finish and leave disabled */
  	if (secs <= offset) {
  		rtt_writel(rtc, AR, ALARM_DISABLED);
  		return 0;
  	}
  
  	/* else set alarm and maybe enable it */
  	rtt_writel(rtc, AR, secs - offset);
  	if (alrm->enabled)
  		rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
  
  	dev_dbg(dev, "%s: %4d-%02d-%02d %02d:%02d:%02d
  ", "setalarm",
  		tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour,
  		tm->tm_min, tm->tm_sec);
  
  	return 0;
  }
16380c153   John Stultz   RTC: Convert rtc ...
251
252
253
254
255
256
257
258
259
260
261
262
263
  static int at91_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
  	u32 mr = rtt_readl(rtc, MR);
  
  	dev_dbg(dev, "alarm_irq_enable: enabled=%08x, mr %08x
  ", enabled, mr);
  	if (enabled)
  		rtt_writel(rtc, MR, mr | AT91_RTT_ALMIEN);
  	else
  		rtt_writel(rtc, MR, mr & ~AT91_RTT_ALMIEN);
  	return 0;
  }
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
264
265
266
267
268
269
  /*
   * Provide additional RTC information in /proc/driver/rtc
   */
  static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
  {
  	struct sam9_rtc *rtc = dev_get_drvdata(dev);
df2d741f0   Colin Ian King   rtc: at91sam9: re...
270
  	u32 mr = rtt_readl(rtc, MR);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
271
272
273
274
275
276
  
  	seq_printf(seq, "update_IRQ\t: %s
  ",
  			(mr & AT91_RTT_RTTINCIEN) ? "yes" : "no");
  	return 0;
  }
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
277
  static irqreturn_t at91_rtc_cache_events(struct sam9_rtc *rtc)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
278
  {
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
279
  	u32 sr, mr;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
280
281
282
283
284
  
  	/* Shared interrupt may be for another device.  Note: reading
  	 * SR clears it, so we must only read it in this irq handler!
  	 */
  	mr = rtt_readl(rtc, MR) & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
9fedc9f1b   David Brownell   rtc-at91sam9 fixes
285
  	sr = rtt_readl(rtc, SR) & (mr >> 16);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
286
287
288
289
290
  	if (!sr)
  		return IRQ_NONE;
  
  	/* alarm status */
  	if (sr & AT91_RTT_ALMS)
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
291
  		rtc->events |= (RTC_AF | RTC_IRQF);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
292
293
294
  
  	/* timer update/increment */
  	if (sr & AT91_RTT_RTTINC)
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
295
296
297
298
299
300
301
302
303
  		rtc->events |= (RTC_UF | RTC_IRQF);
  
  	return IRQ_HANDLED;
  }
  
  static void at91_rtc_flush_events(struct sam9_rtc *rtc)
  {
  	if (!rtc->events)
  		return;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
304

603b1a232   Boris BREZILLON   rtc: at91sam9: re...
305
306
  	rtc_update_irq(rtc->rtcdev, 1, rtc->events);
  	rtc->events = 0;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
307

2a4e2b878   Harvey Harrison   rtc: replace rema...
308
309
  	pr_debug("%s: num=%ld, events=0x%02lx
  ", __func__,
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
310
311
  		rtc->events >> 8, rtc->events & 0x000000FF);
  }
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
312

603b1a232   Boris BREZILLON   rtc: at91sam9: re...
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  /*
   * IRQ handler for the RTC
   */
  static irqreturn_t at91_rtc_interrupt(int irq, void *_rtc)
  {
  	struct sam9_rtc *rtc = _rtc;
  	int ret;
  
  	spin_lock(&rtc->lock);
  
  	ret = at91_rtc_cache_events(rtc);
  
  	/* We're called in suspended state */
  	if (rtc->suspended) {
  		/* Mask irqs coming from this peripheral */
  		rtt_writel(rtc, MR,
  			   rtt_readl(rtc, MR) &
  			   ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
  		/* Trigger a system wakeup */
  		pm_system_wakeup();
  	} else {
  		at91_rtc_flush_events(rtc);
  	}
  
  	spin_unlock(&rtc->lock);
  
  	return ret;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
340
341
342
  }
  
  static const struct rtc_class_ops at91_rtc_ops = {
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
343
344
345
346
347
  	.read_time	= at91_rtc_readtime,
  	.set_time	= at91_rtc_settime,
  	.read_alarm	= at91_rtc_readalarm,
  	.set_alarm	= at91_rtc_setalarm,
  	.proc		= at91_rtc_proc,
d40358509   Jelle Martijn Kok   RTC: fix typo in ...
348
  	.alarm_irq_enable = at91_rtc_alarm_irq_enable,
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
349
  };
bddd8ddd9   Krzysztof Kozlowski   drivers/rtc/rtc-a...
350
  static const struct regmap_config gpbr_regmap_config = {
43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
351
352
353
354
  	.reg_bits = 32,
  	.val_bits = 32,
  	.reg_stride = 4,
  };
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
355
356
357
  /*
   * Initialize and install RTC driver
   */
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
358
  static int at91_rtc_probe(struct platform_device *pdev)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
359
  {
d41da3ee1   Boris BREZILLON   rtc: at91sam9: re...
360
  	struct resource	*r;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
361
  	struct sam9_rtc	*rtc;
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
362
  	int		ret, irq;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
363
  	u32		mr;
a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
364
  	unsigned int	sclk_rate;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
365

e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
366
367
368
369
370
371
  	irq = platform_get_irq(pdev, 0);
  	if (irq < 0) {
  		dev_err(&pdev->dev, "failed to get interrupt resource
  ");
  		return irq;
  	}
9d42e4651   Jingoo Han   rtc: rtc-at91sam9...
372
  	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
373
374
  	if (!rtc)
  		return -ENOMEM;
b7b17633d   Wei Yongjun   rtc: at91sam9: Fi...
375
  	spin_lock_init(&rtc->lock);
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
376
  	rtc->irq = irq;
9fedc9f1b   David Brownell   rtc-at91sam9 fixes
377
378
379
  	/* platform setup code should have handled this; sigh */
  	if (!device_can_wakeup(&pdev->dev))
  		device_init_wakeup(&pdev->dev, 1);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
380
  	platform_set_drvdata(pdev, rtc);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
381

d41da3ee1   Boris BREZILLON   rtc: at91sam9: re...
382
383
384
385
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	rtc->rtt = devm_ioremap_resource(&pdev->dev, r);
  	if (IS_ERR(rtc->rtt))
  		return PTR_ERR(rtc->rtt);
43e112bb3   Boris BREZILLON   rtc: at91sam9: ma...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
  	if (!pdev->dev.of_node) {
  		/*
  		 * TODO: Remove this code chunk when removing non DT board
  		 * support. Remember to remove the gpbr_regmap_config
  		 * variable too.
  		 */
  		void __iomem *gpbr;
  
  		r = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  		gpbr = devm_ioremap_resource(&pdev->dev, r);
  		if (IS_ERR(gpbr))
  			return PTR_ERR(gpbr);
  
  		rtc->gpbr = regmap_init_mmio(NULL, gpbr,
  					     &gpbr_regmap_config);
  	} else {
  		struct of_phandle_args args;
  
  		ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
  						"atmel,rtt-rtc-time-reg", 1, 0,
  						&args);
  		if (ret)
  			return ret;
  
  		rtc->gpbr = syscon_node_to_regmap(args.np);
  		rtc->gpbr_offset = args.args[0];
  	}
  
  	if (IS_ERR(rtc->gpbr)) {
  		dev_err(&pdev->dev, "failed to retrieve gpbr regmap, aborting.
  ");
  		return -ENOMEM;
  	}
b3af8b49b   Jean-Christophe PLAGNIOL-VILLARD   ARM: at91/rtc-at9...
419

a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
420
421
422
  	rtc->sclk = devm_clk_get(&pdev->dev, NULL);
  	if (IS_ERR(rtc->sclk))
  		return PTR_ERR(rtc->sclk);
a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
423
424
425
426
427
428
  	ret = clk_prepare_enable(rtc->sclk);
  	if (ret) {
  		dev_err(&pdev->dev, "Could not enable slow clock
  ");
  		return ret;
  	}
8918bd8a5   Alexandre Belloni   rtc: at91sam9: ge...
429
430
431
432
433
434
435
  	sclk_rate = clk_get_rate(rtc->sclk);
  	if (!sclk_rate || sclk_rate > AT91_RTT_RTPRES) {
  		dev_err(&pdev->dev, "Invalid slow clock rate
  ");
  		ret = -EINVAL;
  		goto err_clk;
  	}
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
436
437
438
  	mr = rtt_readl(rtc, MR);
  
  	/* unless RTT is counting at 1 Hz, re-initialize it */
a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
439
440
  	if ((mr & AT91_RTT_RTPRES) != sclk_rate) {
  		mr = AT91_RTT_RTTRST | (sclk_rate & AT91_RTT_RTPRES);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
441
442
443
444
445
446
  		gpbr_writel(rtc, 0);
  	}
  
  	/* disable all interrupts (same as on shutdown path) */
  	mr &= ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
  	rtt_writel(rtc, MR, mr);
9d42e4651   Jingoo Han   rtc: rtc-at91sam9...
447
448
  	rtc->rtcdev = devm_rtc_device_register(&pdev->dev, pdev->name,
  					&at91_rtc_ops, THIS_MODULE);
ffe60fcfd   Alexandre Belloni   rtc: at91sam9: pr...
449
450
451
452
  	if (IS_ERR(rtc->rtcdev)) {
  		ret = PTR_ERR(rtc->rtcdev);
  		goto err_clk;
  	}
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
453
454
  
  	/* register irq handler after we know what name we'll use */
9d42e4651   Jingoo Han   rtc: rtc-at91sam9...
455
  	ret = devm_request_irq(&pdev->dev, rtc->irq, at91_rtc_interrupt,
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
456
457
  			       IRQF_SHARED | IRQF_COND_SUSPEND,
  			       dev_name(&rtc->rtcdev->dev), rtc);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
458
  	if (ret) {
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
459
460
  		dev_dbg(&pdev->dev, "can't share IRQ %d?
  ", rtc->irq);
ffe60fcfd   Alexandre Belloni   rtc: at91sam9: pr...
461
  		goto err_clk;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
462
463
464
465
466
467
468
469
470
471
472
  	}
  
  	/* NOTE:  sam9260 rev A silicon has a ROM bug which resets the
  	 * RTT on at least some reboots.  If you have that chip, you must
  	 * initialize the time from some external source like a GPS, wall
  	 * clock, discrete RTC, etc
  	 */
  
  	if (gpbr_readl(rtc) == 0)
  		dev_warn(&pdev->dev, "%s: SET TIME!
  ",
744bcb13d   Kay Sievers   rtc: struct devic...
473
  				dev_name(&rtc->rtcdev->dev));
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
474
475
  
  	return 0;
ffe60fcfd   Alexandre Belloni   rtc: at91sam9: pr...
476
477
478
479
480
  
  err_clk:
  	clk_disable_unprepare(rtc->sclk);
  
  	return ret;
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
481
482
483
484
485
  }
  
  /*
   * Disable and remove the RTC driver
   */
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
486
  static int at91_rtc_remove(struct platform_device *pdev)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
487
488
489
490
491
492
  {
  	struct sam9_rtc	*rtc = platform_get_drvdata(pdev);
  	u32		mr = rtt_readl(rtc, MR);
  
  	/* disable all interrupts */
  	rtt_writel(rtc, MR, mr & ~(AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN));
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
493

73ab31ce1   Alexandre Belloni   rtc: at91sam9: re...
494
  	clk_disable_unprepare(rtc->sclk);
a975f47f6   Boris BREZILLON   rtc: at91sam9: us...
495

4cdf854f7   David Brownell   rtc: at91sam9 RTC...
496
497
498
499
500
501
502
503
504
505
506
  	return 0;
  }
  
  static void at91_rtc_shutdown(struct platform_device *pdev)
  {
  	struct sam9_rtc	*rtc = platform_get_drvdata(pdev);
  	u32		mr = rtt_readl(rtc, MR);
  
  	rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
  	rtt_writel(rtc, MR, mr & ~rtc->imr);
  }
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
507
  #ifdef CONFIG_PM_SLEEP
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
508
509
  
  /* AT91SAM9 RTC Power management control */
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
510
  static int at91_rtc_suspend(struct device *dev)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
511
  {
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
512
  	struct sam9_rtc	*rtc = dev_get_drvdata(dev);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
513
514
515
516
517
518
519
520
  	u32		mr = rtt_readl(rtc, MR);
  
  	/*
  	 * This IRQ is shared with DBGU and other hardware which isn't
  	 * necessarily a wakeup event source.
  	 */
  	rtc->imr = mr & (AT91_RTT_ALMIEN | AT91_RTT_RTTINCIEN);
  	if (rtc->imr) {
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
521
  		if (device_may_wakeup(dev) && (mr & AT91_RTT_ALMIEN)) {
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
522
  			unsigned long flags;
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
523
  			enable_irq_wake(rtc->irq);
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
524
525
526
  			spin_lock_irqsave(&rtc->lock, flags);
  			rtc->suspended = true;
  			spin_unlock_irqrestore(&rtc->lock, flags);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
527
528
529
530
531
532
533
534
535
  			/* don't let RTTINC cause wakeups */
  			if (mr & AT91_RTT_RTTINCIEN)
  				rtt_writel(rtc, MR, mr & ~AT91_RTT_RTTINCIEN);
  		} else
  			rtt_writel(rtc, MR, mr & ~rtc->imr);
  	}
  
  	return 0;
  }
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
536
  static int at91_rtc_resume(struct device *dev)
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
537
  {
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
538
  	struct sam9_rtc	*rtc = dev_get_drvdata(dev);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
539
540
541
  	u32		mr;
  
  	if (rtc->imr) {
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
542
  		unsigned long flags;
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
543
  		if (device_may_wakeup(dev))
e402af6ca   Ludovic Desroches   ARM: at91: fix rt...
544
  			disable_irq_wake(rtc->irq);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
545
546
  		mr = rtt_readl(rtc, MR);
  		rtt_writel(rtc, MR, mr | rtc->imr);
603b1a232   Boris BREZILLON   rtc: at91sam9: re...
547
548
549
550
551
552
  
  		spin_lock_irqsave(&rtc->lock, flags);
  		rtc->suspended = false;
  		at91_rtc_cache_events(rtc);
  		at91_rtc_flush_events(rtc);
  		spin_unlock_irqrestore(&rtc->lock, flags);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
553
554
555
556
  	}
  
  	return 0;
  }
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
557
  #endif
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
558
  static SIMPLE_DEV_PM_OPS(at91_rtc_pm_ops, at91_rtc_suspend, at91_rtc_resume);
07d4d7245   Boris BREZILLON   rtc: at91sam9: ad...
559
560
561
562
563
564
565
  #ifdef CONFIG_OF
  static const struct of_device_id at91_rtc_dt_ids[] = {
  	{ .compatible = "atmel,at91sam9260-rtt" },
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, at91_rtc_dt_ids);
  #endif
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
566
  static struct platform_driver at91_rtc_driver = {
205056a3e   Jean-Christophe PLAGNIOL-VILLARD   ARM: at91/rtc-at9...
567
  	.probe		= at91_rtc_probe,
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
568
  	.remove		= at91_rtc_remove,
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
569
  	.shutdown	= at91_rtc_shutdown,
205056a3e   Jean-Christophe PLAGNIOL-VILLARD   ARM: at91/rtc-at9...
570
571
  	.driver		= {
  		.name	= "rtc-at91sam9",
4dc8eb13c   Jingoo Han   rtc: rtc-at91sam9...
572
  		.pm	= &at91_rtc_pm_ops,
07d4d7245   Boris BREZILLON   rtc: at91sam9: ad...
573
  		.of_match_table = of_match_ptr(at91_rtc_dt_ids),
205056a3e   Jean-Christophe PLAGNIOL-VILLARD   ARM: at91/rtc-at9...
574
  	},
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
575
  };
477d30d78   Devendra Naga   drivers/rtc/rtc-a...
576
  module_platform_driver(at91_rtc_driver);
4cdf854f7   David Brownell   rtc: at91sam9 RTC...
577
578
579
580
  
  MODULE_AUTHOR("Michel Benoit");
  MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x");
  MODULE_LICENSE("GPL");