Blame view

drivers/rtc/rtc-lpc32xx.c 8.57 KB
7342e2a73   Alexandre Belloni   rtc: lpc32xx: con...
1
  // SPDX-License-Identifier: GPL-2.0+
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
2
3
  /*
   * Copyright (C) 2010 NXP Semiconductors
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
4
5
6
7
8
9
10
11
12
13
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/platform_device.h>
  #include <linux/spinlock.h>
  #include <linux/rtc.h>
  #include <linux/slab.h>
  #include <linux/io.h>
e862e7c4e   Roland Stigge   drivers/rtc/rtc-l...
14
  #include <linux/of.h>
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
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
  
  /*
   * Clock and Power control register offsets
   */
  #define LPC32XX_RTC_UCOUNT		0x00
  #define LPC32XX_RTC_DCOUNT		0x04
  #define LPC32XX_RTC_MATCH0		0x08
  #define LPC32XX_RTC_MATCH1		0x0C
  #define LPC32XX_RTC_CTRL		0x10
  #define LPC32XX_RTC_INTSTAT		0x14
  #define LPC32XX_RTC_KEY			0x18
  #define LPC32XX_RTC_SRAM		0x80
  
  #define LPC32XX_RTC_CTRL_MATCH0		(1 << 0)
  #define LPC32XX_RTC_CTRL_MATCH1		(1 << 1)
  #define LPC32XX_RTC_CTRL_ONSW_MATCH0	(1 << 2)
  #define LPC32XX_RTC_CTRL_ONSW_MATCH1	(1 << 3)
  #define LPC32XX_RTC_CTRL_SW_RESET	(1 << 4)
  #define LPC32XX_RTC_CTRL_CNTR_DIS	(1 << 6)
  #define LPC32XX_RTC_CTRL_ONSW_FORCE_HI	(1 << 7)
  
  #define LPC32XX_RTC_INTSTAT_MATCH0	(1 << 0)
  #define LPC32XX_RTC_INTSTAT_MATCH1	(1 << 1)
  #define LPC32XX_RTC_INTSTAT_ONSW	(1 << 2)
  
  #define LPC32XX_RTC_KEY_ONSW_LOADVAL	0xB5C13F27
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  #define rtc_readl(dev, reg) \
  	__raw_readl((dev)->rtc_base + (reg))
  #define rtc_writel(dev, reg, val) \
  	__raw_writel((val), (dev)->rtc_base + (reg))
  
  struct lpc32xx_rtc {
  	void __iomem *rtc_base;
  	int irq;
  	unsigned char alarm_enabled;
  	struct rtc_device *rtc;
  	spinlock_t lock;
  };
  
  static int lpc32xx_rtc_read_time(struct device *dev, struct rtc_time *time)
  {
  	unsigned long elapsed_sec;
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  
  	elapsed_sec = rtc_readl(rtc, LPC32XX_RTC_UCOUNT);
f04dd349a   Alexandre Belloni   rtc: lpc32xx: swi...
60
  	rtc_time64_to_tm(elapsed_sec, time);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
61

ab62670e5   Alexandre Belloni   rtc: stop validat...
62
  	return 0;
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
63
  }
34b21c9ee   Alexandre Belloni   rtc: lpc32xx: use...
64
  static int lpc32xx_rtc_set_time(struct device *dev, struct rtc_time *time)
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
65
66
  {
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
34b21c9ee   Alexandre Belloni   rtc: lpc32xx: use...
67
  	u32 secs = rtc_tm_to_time64(time);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  	u32 tmp;
  
  	spin_lock_irq(&rtc->lock);
  
  	/* RTC must be disabled during count update */
  	tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
  	rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp | LPC32XX_RTC_CTRL_CNTR_DIS);
  	rtc_writel(rtc, LPC32XX_RTC_UCOUNT, secs);
  	rtc_writel(rtc, LPC32XX_RTC_DCOUNT, 0xFFFFFFFF - secs);
  	rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp &= ~LPC32XX_RTC_CTRL_CNTR_DIS);
  
  	spin_unlock_irq(&rtc->lock);
  
  	return 0;
  }
  
  static int lpc32xx_rtc_read_alarm(struct device *dev,
  	struct rtc_wkalrm *wkalrm)
  {
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
f04dd349a   Alexandre Belloni   rtc: lpc32xx: swi...
88
  	rtc_time64_to_tm(rtc_readl(rtc, LPC32XX_RTC_MATCH0), &wkalrm->time);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
89
90
91
92
93
94
95
96
97
98
99
100
101
  	wkalrm->enabled = rtc->alarm_enabled;
  	wkalrm->pending = !!(rtc_readl(rtc, LPC32XX_RTC_INTSTAT) &
  		LPC32XX_RTC_INTSTAT_MATCH0);
  
  	return rtc_valid_tm(&wkalrm->time);
  }
  
  static int lpc32xx_rtc_set_alarm(struct device *dev,
  	struct rtc_wkalrm *wkalrm)
  {
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  	unsigned long alarmsecs;
  	u32 tmp;
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
102

f04dd349a   Alexandre Belloni   rtc: lpc32xx: swi...
103
  	alarmsecs = rtc_tm_to_time64(&wkalrm->time);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
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
  
  	spin_lock_irq(&rtc->lock);
  
  	/* Disable alarm during update */
  	tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
  	rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp & ~LPC32XX_RTC_CTRL_MATCH0);
  
  	rtc_writel(rtc, LPC32XX_RTC_MATCH0, alarmsecs);
  
  	rtc->alarm_enabled = wkalrm->enabled;
  	if (wkalrm->enabled) {
  		rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
  			   LPC32XX_RTC_INTSTAT_MATCH0);
  		rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp |
  			   LPC32XX_RTC_CTRL_MATCH0);
  	}
  
  	spin_unlock_irq(&rtc->lock);
  
  	return 0;
  }
  
  static int lpc32xx_rtc_alarm_irq_enable(struct device *dev,
  	unsigned int enabled)
  {
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
  	u32 tmp;
  
  	spin_lock_irq(&rtc->lock);
  	tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
  
  	if (enabled) {
  		rtc->alarm_enabled = 1;
  		tmp |= LPC32XX_RTC_CTRL_MATCH0;
  	} else {
  		rtc->alarm_enabled = 0;
  		tmp &= ~LPC32XX_RTC_CTRL_MATCH0;
  	}
  
  	rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
  	spin_unlock_irq(&rtc->lock);
  
  	return 0;
  }
  
  static irqreturn_t lpc32xx_rtc_alarm_interrupt(int irq, void *dev)
  {
  	struct lpc32xx_rtc *rtc = dev;
  
  	spin_lock(&rtc->lock);
  
  	/* Disable alarm interrupt */
  	rtc_writel(rtc, LPC32XX_RTC_CTRL,
  		rtc_readl(rtc, LPC32XX_RTC_CTRL) &
  			  ~LPC32XX_RTC_CTRL_MATCH0);
  	rtc->alarm_enabled = 0;
  
  	/*
  	 * Write a large value to the match value so the RTC won't
  	 * keep firing the match status
  	 */
  	rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
  	rtc_writel(rtc, LPC32XX_RTC_INTSTAT, LPC32XX_RTC_INTSTAT_MATCH0);
  
  	spin_unlock(&rtc->lock);
  
  	rtc_update_irq(rtc->rtc, 1, RTC_IRQF | RTC_AF);
  
  	return IRQ_HANDLED;
  }
  
  static const struct rtc_class_ops lpc32xx_rtc_ops = {
  	.read_time		= lpc32xx_rtc_read_time,
34b21c9ee   Alexandre Belloni   rtc: lpc32xx: use...
177
  	.set_time		= lpc32xx_rtc_set_time,
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
178
179
180
181
  	.read_alarm		= lpc32xx_rtc_read_alarm,
  	.set_alarm		= lpc32xx_rtc_set_alarm,
  	.alarm_irq_enable	= lpc32xx_rtc_alarm_irq_enable,
  };
5a167f454   Greg Kroah-Hartman   Drivers: rtc: rem...
182
  static int lpc32xx_rtc_probe(struct platform_device *pdev)
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
183
  {
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
184
  	struct lpc32xx_rtc *rtc;
ba4a84f5a   Alexandre Belloni   rtc: lpc32xx: sim...
185
  	int err;
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
186
  	u32 tmp;
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
187
  	rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
5c336b0a7   Jingoo Han   rtc: rtc-lpc32xx:...
188
  	if (unlikely(!rtc))
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
189
  		return -ENOMEM;
5c336b0a7   Jingoo Han   rtc: rtc-lpc32xx:...
190

09ef18bcd   YueHaibing   rtc: use devm_pla...
191
  	rtc->rtc_base = devm_platform_ioremap_resource(pdev, 0);
7c1d69ee1   Julia Lawall   rtc: simplify dev...
192
193
  	if (IS_ERR(rtc->rtc_base))
  		return PTR_ERR(rtc->rtc_base);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
194
195
196
197
  
  	spin_lock_init(&rtc->lock);
  
  	/*
25985edce   Lucas De Marchi   Fix common misspe...
198
  	 * The RTC is on a separate power domain and can keep it's state
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
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
  	 * across a chip power cycle. If the RTC has never been previously
  	 * setup, then set it up now for the first time.
  	 */
  	tmp = rtc_readl(rtc, LPC32XX_RTC_CTRL);
  	if (rtc_readl(rtc, LPC32XX_RTC_KEY) != LPC32XX_RTC_KEY_ONSW_LOADVAL) {
  		tmp &= ~(LPC32XX_RTC_CTRL_SW_RESET |
  			LPC32XX_RTC_CTRL_CNTR_DIS |
  			LPC32XX_RTC_CTRL_MATCH0 |
  			LPC32XX_RTC_CTRL_MATCH1 |
  			LPC32XX_RTC_CTRL_ONSW_MATCH0 |
  			LPC32XX_RTC_CTRL_ONSW_MATCH1 |
  			LPC32XX_RTC_CTRL_ONSW_FORCE_HI);
  		rtc_writel(rtc, LPC32XX_RTC_CTRL, tmp);
  
  		/* Clear latched interrupt states */
  		rtc_writel(rtc, LPC32XX_RTC_MATCH0, 0xFFFFFFFF);
  		rtc_writel(rtc, LPC32XX_RTC_INTSTAT,
  			   LPC32XX_RTC_INTSTAT_MATCH0 |
  			   LPC32XX_RTC_INTSTAT_MATCH1 |
  			   LPC32XX_RTC_INTSTAT_ONSW);
  
  		/* Write key value to RTC so it won't reload on reset */
  		rtc_writel(rtc, LPC32XX_RTC_KEY,
  			   LPC32XX_RTC_KEY_ONSW_LOADVAL);
  	} else {
  		rtc_writel(rtc, LPC32XX_RTC_CTRL,
  			   tmp & ~LPC32XX_RTC_CTRL_MATCH0);
  	}
  
  	platform_set_drvdata(pdev, rtc);
6bbad585f   Alexandre Belloni   rtc: lpc32xx: con...
229
230
  	rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
  	if (IS_ERR(rtc->rtc))
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
231
  		return PTR_ERR(rtc->rtc);
6bbad585f   Alexandre Belloni   rtc: lpc32xx: con...
232
233
  
  	rtc->rtc->ops = &lpc32xx_rtc_ops;
3a1342690   Alexandre Belloni   rtc: lpc32xx: set...
234
  	rtc->rtc->range_max = U32_MAX;
6bbad585f   Alexandre Belloni   rtc: lpc32xx: con...
235
236
237
238
  
  	err = rtc_register_device(rtc->rtc);
  	if (err)
  		return err;
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
239
240
241
242
243
  
  	/*
  	 * IRQ is enabled after device registration in case alarm IRQ
  	 * is pending upon suspend exit.
  	 */
ba4a84f5a   Alexandre Belloni   rtc: lpc32xx: sim...
244
245
246
247
248
  	rtc->irq = platform_get_irq(pdev, 0);
  	if (rtc->irq < 0) {
  		dev_warn(&pdev->dev, "Can't get interrupt resource
  ");
  	} else {
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
249
250
  		if (devm_request_irq(&pdev->dev, rtc->irq,
  				     lpc32xx_rtc_alarm_interrupt,
2f6e5f945   Yong Zhang   drivers/rtc: remo...
251
  				     0, pdev->name, rtc) < 0) {
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
252
253
254
255
256
257
258
259
260
261
  			dev_warn(&pdev->dev, "Can't request interrupt.
  ");
  			rtc->irq = -1;
  		} else {
  			device_init_wakeup(&pdev->dev, 1);
  		}
  	}
  
  	return 0;
  }
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
262
263
264
  #ifdef CONFIG_PM
  static int lpc32xx_rtc_suspend(struct device *dev)
  {
85368bb9d   Wolfram Sang   rtc: simplify get...
265
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
266
267
  
  	if (rtc->irq >= 0) {
85368bb9d   Wolfram Sang   rtc: simplify get...
268
  		if (device_may_wakeup(dev))
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
269
270
271
272
273
274
275
276
277
278
  			enable_irq_wake(rtc->irq);
  		else
  			disable_irq_wake(rtc->irq);
  	}
  
  	return 0;
  }
  
  static int lpc32xx_rtc_resume(struct device *dev)
  {
85368bb9d   Wolfram Sang   rtc: simplify get...
279
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
280

85368bb9d   Wolfram Sang   rtc: simplify get...
281
  	if (rtc->irq >= 0 && device_may_wakeup(dev))
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
282
283
284
285
286
287
288
289
  		disable_irq_wake(rtc->irq);
  
  	return 0;
  }
  
  /* Unconditionally disable the alarm */
  static int lpc32xx_rtc_freeze(struct device *dev)
  {
85368bb9d   Wolfram Sang   rtc: simplify get...
290
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
  
  	spin_lock_irq(&rtc->lock);
  
  	rtc_writel(rtc, LPC32XX_RTC_CTRL,
  		rtc_readl(rtc, LPC32XX_RTC_CTRL) &
  			  ~LPC32XX_RTC_CTRL_MATCH0);
  
  	spin_unlock_irq(&rtc->lock);
  
  	return 0;
  }
  
  static int lpc32xx_rtc_thaw(struct device *dev)
  {
85368bb9d   Wolfram Sang   rtc: simplify get...
305
  	struct lpc32xx_rtc *rtc = dev_get_drvdata(dev);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
  
  	if (rtc->alarm_enabled) {
  		spin_lock_irq(&rtc->lock);
  
  		rtc_writel(rtc, LPC32XX_RTC_CTRL,
  			   rtc_readl(rtc, LPC32XX_RTC_CTRL) |
  			   LPC32XX_RTC_CTRL_MATCH0);
  
  		spin_unlock_irq(&rtc->lock);
  	}
  
  	return 0;
  }
  
  static const struct dev_pm_ops lpc32xx_rtc_pm_ops = {
  	.suspend = lpc32xx_rtc_suspend,
  	.resume = lpc32xx_rtc_resume,
  	.freeze = lpc32xx_rtc_freeze,
  	.thaw = lpc32xx_rtc_thaw,
  	.restore = lpc32xx_rtc_resume
  };
  
  #define LPC32XX_RTC_PM_OPS (&lpc32xx_rtc_pm_ops)
  #else
  #define LPC32XX_RTC_PM_OPS NULL
  #endif
e862e7c4e   Roland Stigge   drivers/rtc/rtc-l...
332
333
334
335
336
337
338
  #ifdef CONFIG_OF
  static const struct of_device_id lpc32xx_rtc_match[] = {
  	{ .compatible = "nxp,lpc3220-rtc" },
  	{ }
  };
  MODULE_DEVICE_TABLE(of, lpc32xx_rtc_match);
  #endif
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
339
340
  static struct platform_driver lpc32xx_rtc_driver = {
  	.probe		= lpc32xx_rtc_probe,
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
341
  	.driver = {
6bbad585f   Alexandre Belloni   rtc: lpc32xx: con...
342
  		.name	= "rtc-lpc32xx",
e862e7c4e   Roland Stigge   drivers/rtc/rtc-l...
343
344
  		.pm	= LPC32XX_RTC_PM_OPS,
  		.of_match_table = of_match_ptr(lpc32xx_rtc_match),
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
345
346
  	},
  };
0c4eae665   Axel Lin   rtc: convert driv...
347
  module_platform_driver(lpc32xx_rtc_driver);
9aa449bed   Kevin Wells   rtc: rtc-lpc32xx:...
348
349
350
351
352
  
  MODULE_AUTHOR("Kevin Wells <wellsk40@gmail.com");
  MODULE_DESCRIPTION("RTC driver for the LPC32xx SoC");
  MODULE_LICENSE("GPL");
  MODULE_ALIAS("platform:rtc-lpc32xx");