Blame view

drivers/rtc/rtc-sirfsoc.c 11.6 KB
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * SiRFSoC Real Time Clock interface for Linux
   *
   * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
   *
   * Licensed under GPLv2 or later.
   */
  
  #include <linux/module.h>
  #include <linux/err.h>
  #include <linux/rtc.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  #include <linux/io.h>
  #include <linux/of.h>
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
16
  #include <linux/regmap.h>
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
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
  #include <linux/rtc/sirfsoc_rtciobrg.h>
  
  
  #define RTC_CN			0x00
  #define RTC_ALARM0		0x04
  #define RTC_ALARM1		0x18
  #define RTC_STATUS		0x08
  #define RTC_SW_VALUE            0x40
  #define SIRFSOC_RTC_AL1E	(1<<6)
  #define SIRFSOC_RTC_AL1		(1<<4)
  #define SIRFSOC_RTC_HZE		(1<<3)
  #define SIRFSOC_RTC_AL0E	(1<<2)
  #define SIRFSOC_RTC_HZ		(1<<1)
  #define SIRFSOC_RTC_AL0		(1<<0)
  #define RTC_DIV			0x0c
  #define RTC_DEEP_CTRL		0x14
  #define RTC_CLOCK_SWITCH	0x1c
  #define SIRFSOC_RTC_CLK		0x03	/* others are reserved */
  
  /* Refer to RTC DIV switch */
  #define RTC_HZ			16
  
  /* This macro is also defined in arch/arm/plat-sirfsoc/cpu.c */
  #define RTC_SHIFT		4
  
  #define INTR_SYSRTC_CN		0x48
  
  struct sirfsoc_rtc_drv {
  	struct rtc_device	*rtc;
  	u32			rtc_base;
  	u32			irq;
28984c7d9   Xianglong Du   drivers/rtc/rtc-s...
48
  	unsigned		irq_wake;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
49
50
  	/* Overflow for every 8 years extra time */
  	u32			overflow_rtc;
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
51
  	spinlock_t		lock;
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
52
  	struct regmap *regmap;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
53
54
55
56
57
  #ifdef CONFIG_PM
  	u32		saved_counter;
  	u32		saved_overflow_rtc;
  #endif
  };
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
58
59
60
61
62
63
64
65
66
67
68
69
70
  static u32 sirfsoc_rtc_readl(struct sirfsoc_rtc_drv *rtcdrv, u32 offset)
  {
  	u32 val;
  
  	regmap_read(rtcdrv->regmap, rtcdrv->rtc_base + offset, &val);
  	return val;
  }
  
  static void sirfsoc_rtc_writel(struct sirfsoc_rtc_drv *rtcdrv,
  			       u32 offset, u32 val)
  {
  	regmap_write(rtcdrv->regmap, rtcdrv->rtc_base + offset, val);
  }
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
71
72
73
74
75
  static int sirfsoc_rtc_read_alarm(struct device *dev,
  		struct rtc_wkalrm *alrm)
  {
  	unsigned long rtc_alarm, rtc_count;
  	struct sirfsoc_rtc_drv *rtcdrv;
b7efdf3b1   Jingoo Han   drivers/rtc/rtc-s...
76
  	rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
77

e9bc7363d   Barry Song   drivers/rtc/rtc-s...
78
  	spin_lock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
79

dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
80
  	rtc_count = sirfsoc_rtc_readl(rtcdrv, RTC_CN);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
81

dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
82
  	rtc_alarm = sirfsoc_rtc_readl(rtcdrv, RTC_ALARM0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  	memset(alrm, 0, sizeof(struct rtc_wkalrm));
  
  	/*
  	 * assume alarm interval not beyond one round counter overflow_rtc:
  	 * 0->0xffffffff
  	 */
  	/* if alarm is in next overflow cycle */
  	if (rtc_count > rtc_alarm)
  		rtc_time_to_tm((rtcdrv->overflow_rtc + 1)
  				<< (BITS_PER_LONG - RTC_SHIFT)
  				| rtc_alarm >> RTC_SHIFT, &(alrm->time));
  	else
  		rtc_time_to_tm(rtcdrv->overflow_rtc
  				<< (BITS_PER_LONG - RTC_SHIFT)
  				| rtc_alarm >> RTC_SHIFT, &(alrm->time));
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
98
  	if (sirfsoc_rtc_readl(rtcdrv, RTC_STATUS) & SIRFSOC_RTC_AL0E)
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
99
  		alrm->enabled = 1;
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
100
101
  
  	spin_unlock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
102
103
104
105
106
107
108
109
110
  
  	return 0;
  }
  
  static int sirfsoc_rtc_set_alarm(struct device *dev,
  		struct rtc_wkalrm *alrm)
  {
  	unsigned long rtc_status_reg, rtc_alarm;
  	struct sirfsoc_rtc_drv *rtcdrv;
b7efdf3b1   Jingoo Han   drivers/rtc/rtc-s...
111
  	rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
112
113
114
  
  	if (alrm->enabled) {
  		rtc_tm_to_time(&(alrm->time), &rtc_alarm);
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
115
  		spin_lock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
116

dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
117
  		rtc_status_reg = sirfsoc_rtc_readl(rtcdrv, RTC_STATUS);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
118
119
120
121
122
123
124
125
  		if (rtc_status_reg & SIRFSOC_RTC_AL0E) {
  			/*
  			 * An ongoing alarm in progress - ingore it and not
  			 * to return EBUSY
  			 */
  			dev_info(dev, "An old alarm was set, will be replaced by a new one
  ");
  		}
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
126
  		sirfsoc_rtc_writel(rtcdrv, RTC_ALARM0, rtc_alarm << RTC_SHIFT);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
127
128
129
130
131
132
133
134
  		rtc_status_reg &= ~0x07; /* mask out the lower status bits */
  		/*
  		 * This bit RTC_AL sets it as a wake-up source for Sleep Mode
  		 * Writing 1 into this bit will clear it
  		 */
  		rtc_status_reg |= SIRFSOC_RTC_AL0;
  		/* enable the RTC alarm interrupt */
  		rtc_status_reg |= SIRFSOC_RTC_AL0E;
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
135
  		sirfsoc_rtc_writel(rtcdrv, RTC_STATUS, rtc_status_reg);
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
136
137
  
  		spin_unlock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
138
139
140
141
142
143
  	} else {
  		/*
  		 * if this function was called with enabled=0
  		 * then it could mean that the application is
  		 * trying to cancel an ongoing alarm
  		 */
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
144
  		spin_lock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
145

dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
146
  		rtc_status_reg = sirfsoc_rtc_readl(rtcdrv, RTC_STATUS);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
147
148
149
150
151
152
153
  		if (rtc_status_reg & SIRFSOC_RTC_AL0E) {
  			/* clear the RTC status register's alarm bit */
  			rtc_status_reg &= ~0x07;
  			/* write 1 into SIRFSOC_RTC_AL0 to force a clear */
  			rtc_status_reg |= (SIRFSOC_RTC_AL0);
  			/* Clear the Alarm enable bit */
  			rtc_status_reg &= ~(SIRFSOC_RTC_AL0E);
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
154
155
  			sirfsoc_rtc_writel(rtcdrv, RTC_STATUS,
  					   rtc_status_reg);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
156
  		}
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
157
  		spin_unlock_irq(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
158
159
160
161
162
163
164
165
166
167
  	}
  
  	return 0;
  }
  
  static int sirfsoc_rtc_read_time(struct device *dev,
  		struct rtc_time *tm)
  {
  	unsigned long tmp_rtc = 0;
  	struct sirfsoc_rtc_drv *rtcdrv;
b7efdf3b1   Jingoo Han   drivers/rtc/rtc-s...
168
  	rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
169
170
171
172
173
174
  	/*
  	 * This patch is taken from WinCE - Need to validate this for
  	 * correctness. To work around sirfsoc RTC counter double sync logic
  	 * fail, read several times to make sure get stable value.
  	 */
  	do {
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
175
  		tmp_rtc = sirfsoc_rtc_readl(rtcdrv, RTC_CN);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
176
  		cpu_relax();
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
177
  	} while (tmp_rtc != sirfsoc_rtc_readl(rtcdrv, RTC_CN));
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
178
179
180
181
182
183
184
185
186
187
188
  
  	rtc_time_to_tm(rtcdrv->overflow_rtc << (BITS_PER_LONG - RTC_SHIFT) |
  					tmp_rtc >> RTC_SHIFT, tm);
  	return 0;
  }
  
  static int sirfsoc_rtc_set_time(struct device *dev,
  		struct rtc_time *tm)
  {
  	unsigned long rtc_time;
  	struct sirfsoc_rtc_drv *rtcdrv;
b7efdf3b1   Jingoo Han   drivers/rtc/rtc-s...
189
  	rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
190
191
192
193
  
  	rtc_tm_to_time(tm, &rtc_time);
  
  	rtcdrv->overflow_rtc = rtc_time >> (BITS_PER_LONG - RTC_SHIFT);
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
194
195
  	sirfsoc_rtc_writel(rtcdrv, RTC_SW_VALUE, rtcdrv->overflow_rtc);
  	sirfsoc_rtc_writel(rtcdrv, RTC_CN, rtc_time << RTC_SHIFT);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  
  	return 0;
  }
  
  static int sirfsoc_rtc_ioctl(struct device *dev, unsigned int cmd,
  		unsigned long arg)
  {
  	switch (cmd) {
  	case RTC_PIE_ON:
  	case RTC_PIE_OFF:
  	case RTC_UIE_ON:
  	case RTC_UIE_OFF:
  	case RTC_AIE_ON:
  	case RTC_AIE_OFF:
  		return 0;
  
  	default:
  		return -ENOIOCTLCMD;
  	}
  }
09e427f87   hao liu   drivers/rtc/rtc-s...
216
217
218
219
220
221
222
  static int sirfsoc_rtc_alarm_irq_enable(struct device *dev,
  		unsigned int enabled)
  {
  	unsigned long rtc_status_reg = 0x0;
  	struct sirfsoc_rtc_drv *rtcdrv;
  
  	rtcdrv = dev_get_drvdata(dev);
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
223
  	spin_lock_irq(&rtcdrv->lock);
09e427f87   hao liu   drivers/rtc/rtc-s...
224

dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
225
  	rtc_status_reg = sirfsoc_rtc_readl(rtcdrv, RTC_STATUS);
09e427f87   hao liu   drivers/rtc/rtc-s...
226
227
228
229
  	if (enabled)
  		rtc_status_reg |= SIRFSOC_RTC_AL0E;
  	else
  		rtc_status_reg &= ~SIRFSOC_RTC_AL0E;
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
230
  	sirfsoc_rtc_writel(rtcdrv, RTC_STATUS, rtc_status_reg);
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
231
232
  
  	spin_unlock_irq(&rtcdrv->lock);
09e427f87   hao liu   drivers/rtc/rtc-s...
233
234
235
236
  
  	return 0;
  
  }
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
237
238
239
240
241
  static const struct rtc_class_ops sirfsoc_rtc_ops = {
  	.read_time = sirfsoc_rtc_read_time,
  	.set_time = sirfsoc_rtc_set_time,
  	.read_alarm = sirfsoc_rtc_read_alarm,
  	.set_alarm = sirfsoc_rtc_set_alarm,
09e427f87   hao liu   drivers/rtc/rtc-s...
242
243
  	.ioctl = sirfsoc_rtc_ioctl,
  	.alarm_irq_enable = sirfsoc_rtc_alarm_irq_enable
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
244
245
246
247
248
249
250
  };
  
  static irqreturn_t sirfsoc_rtc_irq_handler(int irq, void *pdata)
  {
  	struct sirfsoc_rtc_drv *rtcdrv = pdata;
  	unsigned long rtc_status_reg = 0x0;
  	unsigned long events = 0x0;
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
251
  	spin_lock(&rtcdrv->lock);
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
252
  	rtc_status_reg = sirfsoc_rtc_readl(rtcdrv, RTC_STATUS);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
  	/* this bit will be set ONLY if an alarm was active
  	 * and it expired NOW
  	 * So this is being used as an ASSERT
  	 */
  	if (rtc_status_reg & SIRFSOC_RTC_AL0) {
  		/*
  		 * clear the RTC status register's alarm bit
  		 * mask out the lower status bits
  		 */
  		rtc_status_reg &= ~0x07;
  		/* write 1 into SIRFSOC_RTC_AL0 to ACK the alarm interrupt */
  		rtc_status_reg |= (SIRFSOC_RTC_AL0);
  		/* Clear the Alarm enable bit */
  		rtc_status_reg &= ~(SIRFSOC_RTC_AL0E);
  	}
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
268
269
  
  	sirfsoc_rtc_writel(rtcdrv, RTC_STATUS, rtc_status_reg);
e9bc7363d   Barry Song   drivers/rtc/rtc-s...
270
271
  
  	spin_unlock(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
272
273
274
275
276
277
278
279
280
281
282
283
284
  	/* this should wake up any apps polling/waiting on the read
  	 * after setting the alarm
  	 */
  	events |= RTC_IRQF | RTC_AF;
  	rtc_update_irq(rtcdrv->rtc, 1, events);
  
  	return IRQ_HANDLED;
  }
  
  static const struct of_device_id sirfsoc_rtc_of_match[] = {
  	{ .compatible = "sirf,prima2-sysrtc"},
  	{},
  };
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
285
286
287
288
289
290
  
  const struct regmap_config sysrtc_regmap_config = {
  	.reg_bits = 32,
  	.val_bits = 32,
  	.fast_io = true,
  };
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
291
292
293
294
295
296
297
298
299
300
301
  MODULE_DEVICE_TABLE(of, sirfsoc_rtc_of_match);
  
  static int sirfsoc_rtc_probe(struct platform_device *pdev)
  {
  	int err;
  	unsigned long rtc_div;
  	struct sirfsoc_rtc_drv *rtcdrv;
  	struct device_node *np = pdev->dev.of_node;
  
  	rtcdrv = devm_kzalloc(&pdev->dev,
  		sizeof(struct sirfsoc_rtc_drv), GFP_KERNEL);
98e2d21fa   Jingoo Han   rtc: rtc-sirfsoc:...
302
  	if (rtcdrv == NULL)
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
303
  		return -ENOMEM;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
304

e9bc7363d   Barry Song   drivers/rtc/rtc-s...
305
  	spin_lock_init(&rtcdrv->lock);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
306
307
308
309
  	err = of_property_read_u32(np, "reg", &rtcdrv->rtc_base);
  	if (err) {
  		dev_err(&pdev->dev, "unable to find base address of rtc node in dtb
  ");
3d09162e9   Sachin Kamat   drivers/rtc/rtc-s...
310
  		return err;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
311
312
313
314
315
316
  	}
  
  	platform_set_drvdata(pdev, rtcdrv);
  
  	/* Register rtc alarm as a wakeup source */
  	device_init_wakeup(&pdev->dev, 1);
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
317
318
319
320
321
322
323
324
325
  	rtcdrv->regmap = devm_regmap_init_iobg(&pdev->dev,
  			&sysrtc_regmap_config);
  	if (IS_ERR(rtcdrv->regmap)) {
  		err = PTR_ERR(rtcdrv->regmap);
  		dev_err(&pdev->dev, "Failed to allocate register map: %d
  ",
  			err);
  		return err;
  	}
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
326
327
328
329
330
331
  	/*
  	 * Set SYS_RTC counter in RTC_HZ HZ Units
  	 * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1
  	 * If 16HZ, therefore RTC_DIV = 1023;
  	 */
  	rtc_div = ((32768 / RTC_HZ) / 2) - 1;
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
332
  	sirfsoc_rtc_writel(rtcdrv, RTC_DIV, rtc_div);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
333

e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
334
  	/* 0x3 -> RTC_CLK */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
335
  	sirfsoc_rtc_writel(rtcdrv, RTC_CLOCK_SWITCH, SIRFSOC_RTC_CLK);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
336
337
  
  	/* reset SYS RTC ALARM0 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
338
  	sirfsoc_rtc_writel(rtcdrv, RTC_ALARM0, 0x0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
339
340
  
  	/* reset SYS RTC ALARM1 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
341
  	sirfsoc_rtc_writel(rtcdrv, RTC_ALARM1, 0x0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
342
343
344
  
  	/* Restore RTC Overflow From Register After Command Reboot */
  	rtcdrv->overflow_rtc =
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
345
  		sirfsoc_rtc_readl(rtcdrv, RTC_SW_VALUE);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
346

0e9532552   Guo Zeng   drivers/rtc/rtc-s...
347
348
349
350
351
352
353
354
  	rtcdrv->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
  			&sirfsoc_rtc_ops, THIS_MODULE);
  	if (IS_ERR(rtcdrv->rtc)) {
  		err = PTR_ERR(rtcdrv->rtc);
  		dev_err(&pdev->dev, "can't register RTC device
  ");
  		return err;
  	}
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
355
356
357
358
359
360
361
362
363
364
365
  	rtcdrv->irq = platform_get_irq(pdev, 0);
  	err = devm_request_irq(
  			&pdev->dev,
  			rtcdrv->irq,
  			sirfsoc_rtc_irq_handler,
  			IRQF_SHARED,
  			pdev->name,
  			rtcdrv);
  	if (err) {
  		dev_err(&pdev->dev, "Unable to register for the SiRF SOC RTC IRQ
  ");
3d09162e9   Sachin Kamat   drivers/rtc/rtc-s...
366
  		return err;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
367
368
369
  	}
  
  	return 0;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
370
371
372
373
  }
  
  static int sirfsoc_rtc_remove(struct platform_device *pdev)
  {
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
374
  	device_init_wakeup(&pdev->dev, 0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
375
376
377
  
  	return 0;
  }
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
378
  #ifdef CONFIG_PM_SLEEP
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
379
380
  static int sirfsoc_rtc_suspend(struct device *dev)
  {
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
381
  	struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
382
  	rtcdrv->overflow_rtc =
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
383
  		sirfsoc_rtc_readl(rtcdrv, RTC_SW_VALUE);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
384
385
  
  	rtcdrv->saved_counter =
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
386
  		sirfsoc_rtc_readl(rtcdrv, RTC_CN);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
387
  	rtcdrv->saved_overflow_rtc = rtcdrv->overflow_rtc;
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
388
  	if (device_may_wakeup(dev) && !enable_irq_wake(rtcdrv->irq))
28984c7d9   Xianglong Du   drivers/rtc/rtc-s...
389
  		rtcdrv->irq_wake = 1;
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
390
391
392
  
  	return 0;
  }
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
393
  static int sirfsoc_rtc_resume(struct device *dev)
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
394
395
  {
  	u32 tmp;
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
396
  	struct sirfsoc_rtc_drv *rtcdrv = dev_get_drvdata(dev);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
397
398
  
  	/*
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
399
  	 * if resume from snapshot and the rtc power is lost,
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
400
401
  	 * restroe the rtc settings
  	 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
402
  	if (SIRFSOC_RTC_CLK != sirfsoc_rtc_readl(rtcdrv, RTC_CLOCK_SWITCH)) {
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
403
404
  		u32 rtc_div;
  		/* 0x3 -> RTC_CLK */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
405
  		sirfsoc_rtc_writel(rtcdrv, RTC_CLOCK_SWITCH, SIRFSOC_RTC_CLK);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
406
407
408
409
410
411
  		/*
  		 * Set SYS_RTC counter in RTC_HZ HZ Units
  		 * We are using 32K RTC crystal (32768 / RTC_HZ / 2) -1
  		 * If 16HZ, therefore RTC_DIV = 1023;
  		 */
  		rtc_div = ((32768 / RTC_HZ) / 2) - 1;
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
412
  		sirfsoc_rtc_writel(rtcdrv, RTC_DIV, rtc_div);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
413
414
  
  		/* reset SYS RTC ALARM0 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
415
  		sirfsoc_rtc_writel(rtcdrv, RTC_ALARM0, 0x0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
416
417
  
  		/* reset SYS RTC ALARM1 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
418
  		sirfsoc_rtc_writel(rtcdrv, RTC_ALARM1, 0x0);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
419
420
421
422
423
424
425
  	}
  	rtcdrv->overflow_rtc = rtcdrv->saved_overflow_rtc;
  
  	/*
  	 * if current counter is small than previous,
  	 * it means overflow in sleep
  	 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
426
  	tmp = sirfsoc_rtc_readl(rtcdrv, RTC_CN);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
427
428
429
430
431
432
  	if (tmp <= rtcdrv->saved_counter)
  		rtcdrv->overflow_rtc++;
  	/*
  	 *PWRC Value Be Changed When Suspend, Restore Overflow
  	 * In Memory To Register
  	 */
dfe6c04aa   Guo Zeng   rtc: sirfsoc: mov...
433
  	sirfsoc_rtc_writel(rtcdrv, RTC_SW_VALUE, rtcdrv->overflow_rtc);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
434

3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
435
  	if (device_may_wakeup(dev) && rtcdrv->irq_wake) {
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
436
  		disable_irq_wake(rtcdrv->irq);
28984c7d9   Xianglong Du   drivers/rtc/rtc-s...
437
438
  		rtcdrv->irq_wake = 0;
  	}
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
439
440
441
  
  	return 0;
  }
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
442
  #endif
3916b09ee   Xianglong Du   drivers/rtc/rtc-s...
443
444
  static SIMPLE_DEV_PM_OPS(sirfsoc_rtc_pm_ops,
  		sirfsoc_rtc_suspend, sirfsoc_rtc_resume);
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
445
446
447
448
  
  static struct platform_driver sirfsoc_rtc_driver = {
  	.driver = {
  		.name = "sirfsoc-rtc",
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
449
  		.pm = &sirfsoc_rtc_pm_ops,
d149632e0   Sachin Kamat   drivers/rtc/rtc-s...
450
  		.of_match_table = sirfsoc_rtc_of_match,
e88b815e0   Xianglong Du   drivers/rtc/rtc-s...
451
452
453
454
455
456
457
458
459
460
  	},
  	.probe = sirfsoc_rtc_probe,
  	.remove = sirfsoc_rtc_remove,
  };
  module_platform_driver(sirfsoc_rtc_driver);
  
  MODULE_DESCRIPTION("SiRF SoC rtc driver");
  MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
  MODULE_LICENSE("GPL v2");
  MODULE_ALIAS("platform:sirfsoc-rtc");