Blame view

drivers/rtc/rtc-stmp3xxx.c 7.35 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>
df17f6317   dmitry pervushin   rtc: add Freescal...
28

46b212181   Wolfram Sang   rtc: stmp3xxx: Po...
29
  #include <mach/common.h>
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
30
31
  
  #define STMP3XXX_RTC_CTRL			0x0
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
32
33
  #define STMP3XXX_RTC_CTRL_SET			0x4
  #define STMP3XXX_RTC_CTRL_CLR			0x8
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
34
35
36
  #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN		0x00000001
  #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN	0x00000002
  #define STMP3XXX_RTC_CTRL_ALARM_IRQ		0x00000004
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
37
38
39
40
41
42
43
44
45
46
  
  #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
  
  #define STMP3XXX_RTC_PERSISTENT0		0x60
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
47
48
  #define STMP3XXX_RTC_PERSISTENT0_SET		0x64
  #define STMP3XXX_RTC_PERSISTENT0_CLR		0x68
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
49
50
51
  #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...
52
53
54
  
  struct stmp3xxx_rtc_data {
  	struct rtc_device *rtc;
df17f6317   dmitry pervushin   rtc: add Freescal...
55
  	void __iomem *io;
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
56
  	int irq_alarm;
df17f6317   dmitry pervushin   rtc: add Freescal...
57
58
59
60
61
62
63
64
65
  };
  
  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...
66
  	while (readl(rtc_data->io + STMP3XXX_RTC_STAT) &
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
67
  			(0x80 << STMP3XXX_RTC_STAT_STALE_SHIFT))
df17f6317   dmitry pervushin   rtc: add Freescal...
68
69
70
71
72
73
74
75
76
  		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...
77
  	rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_SECONDS), rtc_tm);
df17f6317   dmitry pervushin   rtc: add Freescal...
78
79
80
81
82
83
  	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...
84
  	writel(t, rtc_data->io + STMP3XXX_RTC_SECONDS);
df17f6317   dmitry pervushin   rtc: add Freescal...
85
86
87
88
89
90
91
92
  	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...
93
  	u32 status = readl(rtc_data->io + STMP3XXX_RTC_CTRL);
df17f6317   dmitry pervushin   rtc: add Freescal...
94

47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
95
  	if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
96
97
  		writel(STMP3XXX_RTC_CTRL_ALARM_IRQ,
  				rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
98
99
  		rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF);
  		return IRQ_HANDLED;
df17f6317   dmitry pervushin   rtc: add Freescal...
100
  	}
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
101
  	return IRQ_NONE;
df17f6317   dmitry pervushin   rtc: add Freescal...
102
103
104
105
106
  }
  
  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...
107
108
  
  	if (enabled) {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
109
110
111
112
113
  		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...
114
  	} else {
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
115
116
117
118
119
  		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...
120
121
122
  	}
  	return 0;
  }
df17f6317   dmitry pervushin   rtc: add Freescal...
123
124
125
  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...
126
  	rtc_time_to_tm(readl(rtc_data->io + STMP3XXX_RTC_ALARM), &alm->time);
df17f6317   dmitry pervushin   rtc: add Freescal...
127
128
129
130
131
132
133
134
135
  	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...
136
  	writel(t, rtc_data->io + STMP3XXX_RTC_ALARM);
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
137
138
  
  	stmp3xxx_alarm_irq_enable(dev, alm->enabled);
df17f6317   dmitry pervushin   rtc: add Freescal...
139
140
141
142
143
144
  	return 0;
  }
  
  static struct rtc_class_ops stmp3xxx_rtc_ops = {
  	.alarm_irq_enable =
  			  stmp3xxx_alarm_irq_enable,
df17f6317   dmitry pervushin   rtc: add Freescal...
145
146
147
148
149
150
151
152
153
154
155
156
  	.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...
157
  	writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
158
  			rtc_data->io + STMP3XXX_RTC_CTRL_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
159
  	free_irq(rtc_data->irq_alarm, &pdev->dev);
df17f6317   dmitry pervushin   rtc: add Freescal...
160
  	rtc_device_unregister(rtc_data->rtc);
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
161
  	platform_set_drvdata(pdev, NULL);
df17f6317   dmitry pervushin   rtc: add Freescal...
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
  	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...
195

b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
196
  	if (!(readl(STMP3XXX_RTC_STAT + rtc_data->io) &
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
197
  			STMP3XXX_RTC_STAT_RTC_PRESENT)) {
df17f6317   dmitry pervushin   rtc: add Freescal...
198
199
200
201
202
  		dev_err(&pdev->dev, "no device onboard
  ");
  		err = -ENODEV;
  		goto out_remap;
  	}
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
203
  	platform_set_drvdata(pdev, rtc_data);
46b212181   Wolfram Sang   rtc: stmp3xxx: Po...
204
  	mxs_reset_block(rtc_data->io);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
205
  	writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
206
207
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
208
  			rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
209

7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
210
211
212
  	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...
213
214
215
216
217
218
  	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...
219
220
  	err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, 0,
  			"RTC alarm", &pdev->dev);
df17f6317   dmitry pervushin   rtc: add Freescal...
221
222
223
224
225
226
  	if (err) {
  		dev_err(&pdev->dev, "Cannot claim IRQ%d
  ",
  			rtc_data->irq_alarm);
  		goto out_irq_alarm;
  	}
df17f6317   dmitry pervushin   rtc: add Freescal...
227

df17f6317   dmitry pervushin   rtc: add Freescal...
228
  	return 0;
df17f6317   dmitry pervushin   rtc: add Freescal...
229
  out_irq_alarm:
df17f6317   dmitry pervushin   rtc: add Freescal...
230
231
  	rtc_device_unregister(rtc_data->rtc);
  out_remap:
a91d2bab3   Wolfram Sang   rtc: stmp3xxx: In...
232
  	platform_set_drvdata(pdev, NULL);
df17f6317   dmitry pervushin   rtc: add Freescal...
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
  	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...
248
  	mxs_reset_block(rtc_data->io);
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
249
  	writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
47eac337c   Wolfram Sang   rtc: stmp3xxx: Re...
250
251
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
  			STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
b5167159d   Wolfram Sang   rtc: stmp3xxx: Ge...
252
  			rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR);
df17f6317   dmitry pervushin   rtc: add Freescal...
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  	return 0;
  }
  #else
  #define stmp3xxx_rtc_suspend	NULL
  #define stmp3xxx_rtc_resume	NULL
  #endif
  
  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,
  	},
  };
0c4eae665   Axel Lin   rtc: convert driv...
270
  module_platform_driver(stmp3xxx_rtcdrv);
df17f6317   dmitry pervushin   rtc: add Freescal...
271
272
  
  MODULE_DESCRIPTION("STMP3xxx RTC Driver");
7e794cb7e   Wolfram Sang   rtc: stmp3xxx: Re...
273
274
  MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com> and "
  		"Wolfram Sang <w.sang@pengutronix.de>");
df17f6317   dmitry pervushin   rtc: add Freescal...
275
  MODULE_LICENSE("GPL");