Blame view

drivers/rtc/rtc-stmp3xxx.c 9.76 KB
df17f6317   dmitry pervushin   rtc: add Freescal...
1
2
3
4
5
6
7
8
  /*
   * Freescale STMP37XX/STMP378X Real Time Clock driver
   *
   * Copyright (c) 2007 Sigmatel, Inc.
   * Peter Hartley, <peter.hartley@sigmatel.com>
   *
   * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
   * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
9
   * Copyright 2011 Wolfram Sang, Pengutronix e.K.
df17f6317   dmitry pervushin   rtc: add Freescal...
10
11
12
13
14
15
16
17
18
19
20
21
   */
  
  /*
   * The code contained herein is licensed under the GNU General Public
   * License. You may obtain a copy of the GNU General Public License
   * Version 2 or later at the following locations:
   *
   * http://www.opensource.org/licenses/gpl-license.html
   * http://www.gnu.org/copyleft/gpl.html
   */
  #include <linux/kernel.h>
  #include <linux/module.h>
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
22
  #include <linux/io.h>
df17f6317   dmitry pervushin   rtc: add Freescal...
23
24
25
26
  #include <linux/init.h>
  #include <linux/platform_device.h>
  #include <linux/interrupt.h>
  #include <linux/rtc.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
27
  #include <linux/slab.h>
dd8d20a3f   Marek Vasut   rtc: stmp3xxx: Ad...
28
  #include <linux/of_device.h>
c8a6046e1   Sachin Kamat   drivers/rtc: use ...
29
  #include <linux/of.h>
1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
30
31
  #include <linux/stmp_device.h>
  #include <linux/stmp3xxx_rtc_wdt.h>
df17f6317   dmitry pervushin   rtc: add Freescal...
32

46b212181   Wolfram Sang   rtc: stmp3xxx: Po...
33
  #include <mach/common.h>
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
34
35
  
  #define STMP3XXX_RTC_CTRL			0x0
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
36
37
  #define STMP3XXX_RTC_CTRL_SET			0x4
  #define STMP3XXX_RTC_CTRL_CLR			0x8
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
38
39
40
  #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN		0x00000001
  #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN	0x00000002
  #define STMP3XXX_RTC_CTRL_ALARM_IRQ		0x00000004
1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
41
  #define STMP3XXX_RTC_CTRL_WATCHDOGEN		0x00000010
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
42
43
44
45
46
47
48
49
  
  #define STMP3XXX_RTC_STAT			0x10
  #define STMP3XXX_RTC_STAT_STALE_SHIFT		16
  #define STMP3XXX_RTC_STAT_RTC_PRESENT		0x80000000
  
  #define STMP3XXX_RTC_SECONDS			0x30
  
  #define STMP3XXX_RTC_ALARM			0x40
1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
50
  #define STMP3XXX_RTC_WATCHDOG			0x50
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
51
  #define STMP3XXX_RTC_PERSISTENT0		0x60
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
52
53
  #define STMP3XXX_RTC_PERSISTENT0_SET		0x64
  #define STMP3XXX_RTC_PERSISTENT0_CLR		0x68
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
54
55
56
  #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN	0x00000002
  #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN	0x00000004
  #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE	0x00000080
df17f6317   dmitry pervushin   rtc: add Freescal...
57

1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
58
59
60
  #define STMP3XXX_RTC_PERSISTENT1		0x70
  /* missing bitmask in headers */
  #define STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER	0x80000000
df17f6317   dmitry pervushin   rtc: add Freescal...
61
62
  struct stmp3xxx_rtc_data {
  	struct rtc_device *rtc;
df17f6317   dmitry pervushin   rtc: add Freescal...
63
  	void __iomem *io;
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
64
  	int irq_alarm;
df17f6317   dmitry pervushin   rtc: add Freescal...
65
  };
1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
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
  #if IS_ENABLED(CONFIG_STMP3XXX_RTC_WATCHDOG)
  /**
   * stmp3xxx_wdt_set_timeout - configure the watchdog inside the STMP3xxx RTC
   * @dev: the parent device of the watchdog (= the RTC)
   * @timeout: the desired value for the timeout register of the watchdog.
   *           0 disables the watchdog
   *
   * The watchdog needs one register and two bits which are in the RTC domain.
   * To handle the resource conflict, the RTC driver will create another
   * platform_device for the watchdog driver as a child of the RTC device.
   * The watchdog driver is passed the below accessor function via platform_data
   * to configure the watchdog. Locking is not needed because accessing SET/CLR
   * registers is atomic.
   */
  
  static void stmp3xxx_wdt_set_timeout(struct device *dev, u32 timeout)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
  
  	if (timeout) {
  		writel(timeout, rtc_data->io + STMP3XXX_RTC_WATCHDOG);
  		writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
  		       rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET);
  		writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
  		       rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_SET);
  	} else {
  		writel(STMP3XXX_RTC_CTRL_WATCHDOGEN,
  		       rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR);
  		writel(STMP3XXX_RTC_PERSISTENT1_FORCE_UPDATER,
  		       rtc_data->io + STMP3XXX_RTC_PERSISTENT1 + STMP_OFFSET_REG_CLR);
  	}
  }
  
  static struct stmp3xxx_wdt_pdata wdt_pdata = {
  	.wdt_set_timeout = stmp3xxx_wdt_set_timeout,
  };
  
  static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
  {
  	struct platform_device *wdt_pdev =
  		platform_device_alloc("stmp3xxx_rtc_wdt", rtc_pdev->id);
  
  	if (wdt_pdev) {
  		wdt_pdev->dev.parent = &rtc_pdev->dev;
  		wdt_pdev->dev.platform_data = &wdt_pdata;
  		platform_device_add(wdt_pdev);
  	}
  }
  #else
  static void stmp3xxx_wdt_register(struct platform_device *rtc_pdev)
  {
  }
  #endif /* CONFIG_STMP3XXX_RTC_WATCHDOG */
df17f6317   dmitry pervushin   rtc: add Freescal...
119
120
121
122
123
124
125
  static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data)
  {
  	/*
  	 * The datasheet doesn't say which way round the
  	 * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0,
  	 * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS
  	 */
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
126
  	while (readl(rtc_data->io + STMP3XXX_RTC_STAT) &
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
127
  			(0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT))
df17f6317   dmitry pervushin   rtc: add Freescal...
128
129
130
131
132
133
134
135
136
  		cpu_relax();
  }
  
  /* Time read/write */
  static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
  
  	stmp3xxx_wait_time(rtc_data);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
137
  	rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm);
df17f6317   dmitry pervushin   rtc: add Freescal...
138
139
140
141
142
143
  	return 0;
  }
  
  static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
144
  	writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS);
df17f6317   dmitry pervushin   rtc: add Freescal...
145
146
147
148
149
150
151
152
  	stmp3xxx_wait_time(rtc_data);
  	return 0;
  }
  
  /* interrupt(s) handler */
  static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id);
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
153
  	u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL);
df17f6317   dmitry pervushin   rtc: add Freescal...
154

47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
155
  	if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
156
157
  		writel(STMP3XXX_RTC_CTRL_ALARM_IRQ,
  				rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
158
159
  		rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF);
  		return IRQ_HANDLED;
df17f6317   dmitry pervushin   rtc: add Freescal...
160
  	}
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
161
  	return IRQ_NONE;
df17f6317   dmitry pervushin   rtc: add Freescal...
162
163
164
165
166
  }
  
  static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
df17f6317   dmitry pervushin   rtc: add Freescal...
167
168
  
  	if (enabled) {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
169
170
171
172
173
  		writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
  				STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN,
  				rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET);
  		writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
  				rtc_data->io + STMP3XXX_RTC_CTRL_SET);
df17f6317   dmitry pervushin   rtc: add Freescal...
174
  	} else {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
175
176
177
178
179
  		writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
  				STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN,
  				rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
  		writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
  				rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
180
181
182
  	}
  	return 0;
  }
df17f6317   dmitry pervushin   rtc: add Freescal...
183
184
185
  static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
186
  	rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time);
df17f6317   dmitry pervushin   rtc: add Freescal...
187
188
189
190
191
192
193
194
195
  	return 0;
  }
  
  static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
  {
  	unsigned long t;
  	struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev);
  
  	rtc_tm_to_time(&alm->time, &t);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
196
  	writel(t, rtc_data->io + STMP3XXX_RTC_ALARM);
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
197
198
  
  	stmp3xxx_alarm_irq_enable(dev, alm->enabled);
df17f6317   dmitry pervushin   rtc: add Freescal...
199
200
201
202
203
204
  	return 0;
  }
  
  static struct rtc_class_ops stmp3xxx_rtc_ops = {
  	.alarm_irq_enable =
  			  stmp3xxx_alarm_irq_enable,
df17f6317   dmitry pervushin   rtc: add Freescal...
205
206
207
208
209
210
211
212
213
214
215
216
  	.read_time	= stmp3xxx_rtc_gettime,
  	.set_mmss	= stmp3xxx_rtc_set_mmss,
  	.read_alarm	= stmp3xxx_rtc_read_alarm,
  	.set_alarm	= stmp3xxx_rtc_set_alarm,
  };
  
  static int stmp3xxx_rtc_remove(struct platform_device *pdev)
  {
  	struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev);
  
  	if (!rtc_data)
  		return 0;
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
217
  	writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
218
  			rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
219
  	free_irq(rtc_data->irq_alarm, &pdev->dev);
df17f6317   dmitry pervushin   rtc: add Freescal...
220
  	rtc_device_unregister(rtc_data->rtc);
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
221
  	platform_set_drvdata(pdev, NULL);
df17f6317   dmitry pervushin   rtc: add Freescal...
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
  	iounmap(rtc_data->io);
  	kfree(rtc_data);
  
  	return 0;
  }
  
  static int stmp3xxx_rtc_probe(struct platform_device *pdev)
  {
  	struct stmp3xxx_rtc_data *rtc_data;
  	struct resource *r;
  	int err;
  
  	rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL);
  	if (!rtc_data)
  		return -ENOMEM;
  
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!r) {
  		dev_err(&pdev->dev, "failed to get resource
  ");
  		err = -ENXIO;
  		goto out_free;
  	}
  
  	rtc_data->io = ioremap(r->start, resource_size(r));
  	if (!rtc_data->io) {
  		dev_err(&pdev->dev, "ioremap failed
  ");
  		err = -EIO;
  		goto out_free;
  	}
  
  	rtc_data->irq_alarm = platform_get_irq(pdev, 0);
df17f6317   dmitry pervushin   rtc: add Freescal...
255

b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
256
  	if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) &
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
257
  			STMP3XXX_RTC_STAT_RTC_PRESENT)) {
df17f6317   dmitry pervushin   rtc: add Freescal...
258
259
260
261
262
  		dev_err(&pdev->dev, "no device onboard
  ");
  		err = -ENODEV;
  		goto out_remap;
  	}
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
263
  	platform_set_drvdata(pdev, rtc_data);
46b212181   Wolfram Sang   rtc: stmp3xxx: Po...
264
  	mxs_reset_block(rtc_data->io);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
265
  	writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
266
267
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
268
  			rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
269

7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
270
271
272
  	writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN |
  			STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
  			rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
273
274
275
276
277
278
  	rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev,
  				&stmp3xxx_rtc_ops, THIS_MODULE);
  	if (IS_ERR(rtc_data->rtc)) {
  		err = PTR_ERR(rtc_data->rtc);
  		goto out_remap;
  	}
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
279
280
  	err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 0,
  			"RTC alarm", &pdev->dev);
df17f6317   dmitry pervushin   rtc: add Freescal...
281
282
283
284
285
286
  	if (err) {
  		dev_err(&pdev->dev, "Cannot claim IRQ%d
  ",
  			rtc_data->irq_alarm);
  		goto out_irq_alarm;
  	}
df17f6317   dmitry pervushin   rtc: add Freescal...
287

1a71fb84f   Wolfram Sang   rtc: stmp3xxx: ad...
288
  	stmp3xxx_wdt_register(pdev);
df17f6317   dmitry pervushin   rtc: add Freescal...
289
  	return 0;
df17f6317   dmitry pervushin   rtc: add Freescal...
290
  out_irq_alarm:
df17f6317   dmitry pervushin   rtc: add Freescal...
291
292
  	rtc_device_unregister(rtc_data->rtc);
  out_remap:
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
293
  	platform_set_drvdata(pdev, NULL);
df17f6317   dmitry pervushin   rtc: add Freescal...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
  	iounmap(rtc_data->io);
  out_free:
  	kfree(rtc_data);
  	return err;
  }
  
  #ifdef CONFIG_PM
  static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state)
  {
  	return 0;
  }
  
  static int stmp3xxx_rtc_resume(struct platform_device *dev)
  {
  	struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev);
46b212181   Wolfram Sang   rtc: stmp3xxx: Po...
309
  	mxs_reset_block(rtc_data->io);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
310
  	writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
311
312
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
313
  			rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
314
315
316
317
318
319
  	return 0;
  }
  #else
  #define stmp3xxx_rtc_suspend	NULL
  #define stmp3xxx_rtc_resume	NULL
  #endif
dd8d20a3f   Marek Vasut   rtc: stmp3xxx: Ad...
320
321
322
323
324
  static const struct of_device_id rtc_dt_ids[] = {
  	{ .compatible = "fsl,stmp3xxx-rtc", },
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, rtc_dt_ids);
df17f6317   dmitry pervushin   rtc: add Freescal...
325
326
327
328
329
330
331
332
  static struct platform_driver stmp3xxx_rtcdrv = {
  	.probe		= stmp3xxx_rtc_probe,
  	.remove		= stmp3xxx_rtc_remove,
  	.suspend	= stmp3xxx_rtc_suspend,
  	.resume		= stmp3xxx_rtc_resume,
  	.driver		= {
  		.name	= "stmp3xxx-rtc",
  		.owner	= THIS_MODULE,
c8a6046e1   Sachin Kamat   drivers/rtc: use ...
333
  		.of_match_table = of_match_ptr(rtc_dt_ids),
df17f6317   dmitry pervushin   rtc: add Freescal...
334
335
  	},
  };
0c4eae665   Axel Lin   rtc: convert driv...
336
  module_platform_driver(stmp3xxx_rtcdrv);
df17f6317   dmitry pervushin   rtc: add Freescal...
337
338
  
  MODULE_DESCRIPTION("STMP3xxx RTC Driver");
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
339
340
  MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com> and "
  		"Wolfram Sang <w.sang@pengutronix.de>");
df17f6317   dmitry pervushin   rtc: add Freescal...
341
  MODULE_LICENSE("GPL");