df17f631 |
1 | /* |
2 | * Freescale STMP37XX/STMP378X Real Time Clock driver |
3 | * |
4 | * Copyright (c) 2007 Sigmatel, Inc. |
5 | * Peter Hartley, <peter.hartley@sigmatel.com> |
6 | * |
7 | * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. |
8 | * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. |
9 | */ |
10 | |
11 | /* |
12 | * The code contained herein is licensed under the GNU General Public |
13 | * License. You may obtain a copy of the GNU General Public License |
14 | * Version 2 or later at the following locations: |
15 | * |
16 | * http://www.opensource.org/licenses/gpl-license.html |
17 | * http://www.gnu.org/copyleft/gpl.html |
18 | */ |
19 | #include <linux/kernel.h> |
20 | #include <linux/module.h> |
21 | #include <linux/init.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/interrupt.h> |
24 | #include <linux/rtc.h> |
25 | |
26 | #include <mach/platform.h> |
27 | #include <mach/stmp3xxx.h> |
28 | #include <mach/regs-rtc.h> |
29 | |
30 | struct stmp3xxx_rtc_data { |
31 | struct rtc_device *rtc; |
32 | unsigned irq_count; |
33 | void __iomem *io; |
34 | int irq_alarm, irq_1msec; |
35 | }; |
36 | |
37 | static void stmp3xxx_wait_time(struct stmp3xxx_rtc_data *rtc_data) |
38 | { |
39 | /* |
40 | * The datasheet doesn't say which way round the |
41 | * NEW_REGS/STALE_REGS bitfields go. In fact it's 0x1=P0, |
42 | * 0x2=P1, .., 0x20=P5, 0x40=ALARM, 0x80=SECONDS |
43 | */ |
44 | while (__raw_readl(rtc_data->io + HW_RTC_STAT) & |
45 | BF(0x80, RTC_STAT_STALE_REGS)) |
46 | cpu_relax(); |
47 | } |
48 | |
49 | /* Time read/write */ |
50 | static int stmp3xxx_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) |
51 | { |
52 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
53 | |
54 | stmp3xxx_wait_time(rtc_data); |
55 | rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_SECONDS), rtc_tm); |
56 | return 0; |
57 | } |
58 | |
59 | static int stmp3xxx_rtc_set_mmss(struct device *dev, unsigned long t) |
60 | { |
61 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
62 | |
63 | __raw_writel(t, rtc_data->io + HW_RTC_SECONDS); |
64 | stmp3xxx_wait_time(rtc_data); |
65 | return 0; |
66 | } |
67 | |
68 | /* interrupt(s) handler */ |
69 | static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) |
70 | { |
71 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev_id); |
72 | u32 status; |
73 | u32 events = 0; |
74 | |
75 | status = __raw_readl(rtc_data->io + HW_RTC_CTRL) & |
76 | (BM_RTC_CTRL_ALARM_IRQ | BM_RTC_CTRL_ONEMSEC_IRQ); |
77 | |
78 | if (status & BM_RTC_CTRL_ALARM_IRQ) { |
79 | stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ, |
80 | rtc_data->io + HW_RTC_CTRL); |
81 | events |= RTC_AF | RTC_IRQF; |
82 | } |
83 | |
84 | if (status & BM_RTC_CTRL_ONEMSEC_IRQ) { |
85 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ, |
86 | rtc_data->io + HW_RTC_CTRL); |
87 | if (++rtc_data->irq_count % 1000 == 0) { |
88 | events |= RTC_UF | RTC_IRQF; |
89 | rtc_data->irq_count = 0; |
90 | } |
91 | } |
92 | |
93 | if (events) |
94 | rtc_update_irq(rtc_data->rtc, 1, events); |
95 | |
96 | return IRQ_HANDLED; |
97 | } |
98 | |
99 | static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) |
100 | { |
101 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
102 | void __iomem *p = rtc_data->io + HW_RTC_PERSISTENT0, |
103 | *ctl = rtc_data->io + HW_RTC_CTRL; |
104 | |
105 | if (enabled) { |
106 | stmp3xxx_setl(BM_RTC_PERSISTENT0_ALARM_EN | |
107 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); |
108 | stmp3xxx_setl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); |
109 | } else { |
110 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | |
111 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN, p); |
112 | stmp3xxx_clearl(BM_RTC_CTRL_ALARM_IRQ_EN, ctl); |
113 | } |
114 | return 0; |
115 | } |
116 | |
117 | static int stmp3xxx_update_irq_enable(struct device *dev, unsigned int enabled) |
118 | { |
119 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
120 | |
121 | if (enabled) |
122 | stmp3xxx_setl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, |
123 | rtc_data->io + HW_RTC_CTRL); |
124 | else |
125 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN, |
126 | rtc_data->io + HW_RTC_CTRL); |
127 | return 0; |
128 | } |
129 | |
130 | static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
131 | { |
132 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
133 | |
134 | rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); |
135 | return 0; |
136 | } |
137 | |
138 | static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
139 | { |
140 | unsigned long t; |
141 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
142 | |
143 | rtc_tm_to_time(&alm->time, &t); |
144 | __raw_writel(t, rtc_data->io + HW_RTC_ALARM); |
145 | return 0; |
146 | } |
147 | |
148 | static struct rtc_class_ops stmp3xxx_rtc_ops = { |
149 | .alarm_irq_enable = |
150 | stmp3xxx_alarm_irq_enable, |
151 | .update_irq_enable = |
152 | stmp3xxx_update_irq_enable, |
153 | .read_time = stmp3xxx_rtc_gettime, |
154 | .set_mmss = stmp3xxx_rtc_set_mmss, |
155 | .read_alarm = stmp3xxx_rtc_read_alarm, |
156 | .set_alarm = stmp3xxx_rtc_set_alarm, |
157 | }; |
158 | |
159 | static int stmp3xxx_rtc_remove(struct platform_device *pdev) |
160 | { |
161 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); |
162 | |
163 | if (!rtc_data) |
164 | return 0; |
165 | |
166 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, |
167 | rtc_data->io + HW_RTC_CTRL); |
168 | free_irq(rtc_data->irq_alarm, &pdev->dev); |
169 | free_irq(rtc_data->irq_1msec, &pdev->dev); |
170 | rtc_device_unregister(rtc_data->rtc); |
171 | iounmap(rtc_data->io); |
172 | kfree(rtc_data); |
173 | |
174 | return 0; |
175 | } |
176 | |
177 | static int stmp3xxx_rtc_probe(struct platform_device *pdev) |
178 | { |
179 | struct stmp3xxx_rtc_data *rtc_data; |
180 | struct resource *r; |
181 | int err; |
182 | |
183 | rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); |
184 | if (!rtc_data) |
185 | return -ENOMEM; |
186 | |
187 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
188 | if (!r) { |
189 | dev_err(&pdev->dev, "failed to get resource\n"); |
190 | err = -ENXIO; |
191 | goto out_free; |
192 | } |
193 | |
194 | rtc_data->io = ioremap(r->start, resource_size(r)); |
195 | if (!rtc_data->io) { |
196 | dev_err(&pdev->dev, "ioremap failed\n"); |
197 | err = -EIO; |
198 | goto out_free; |
199 | } |
200 | |
201 | rtc_data->irq_alarm = platform_get_irq(pdev, 0); |
202 | rtc_data->irq_1msec = platform_get_irq(pdev, 1); |
203 | |
204 | if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & |
205 | BM_RTC_STAT_RTC_PRESENT)) { |
206 | dev_err(&pdev->dev, "no device onboard\n"); |
207 | err = -ENODEV; |
208 | goto out_remap; |
209 | } |
210 | |
211 | stmp3xxx_reset_block(rtc_data->io, true); |
212 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | |
213 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | |
214 | BM_RTC_PERSISTENT0_ALARM_WAKE, |
215 | rtc_data->io + HW_RTC_PERSISTENT0); |
216 | rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, |
217 | &stmp3xxx_rtc_ops, THIS_MODULE); |
218 | if (IS_ERR(rtc_data->rtc)) { |
219 | err = PTR_ERR(rtc_data->rtc); |
220 | goto out_remap; |
221 | } |
222 | |
223 | rtc_data->irq_count = 0; |
224 | err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, |
225 | IRQF_DISABLED, "RTC alarm", &pdev->dev); |
226 | if (err) { |
227 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", |
228 | rtc_data->irq_alarm); |
229 | goto out_irq_alarm; |
230 | } |
231 | err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, |
232 | IRQF_DISABLED, "RTC tick", &pdev->dev); |
233 | if (err) { |
234 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", |
235 | rtc_data->irq_1msec); |
236 | goto out_irq1; |
237 | } |
238 | |
239 | platform_set_drvdata(pdev, rtc_data); |
240 | |
241 | return 0; |
242 | |
243 | out_irq1: |
244 | free_irq(rtc_data->irq_alarm, &pdev->dev); |
245 | out_irq_alarm: |
246 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, |
247 | rtc_data->io + HW_RTC_CTRL); |
248 | rtc_device_unregister(rtc_data->rtc); |
249 | out_remap: |
250 | iounmap(rtc_data->io); |
251 | out_free: |
252 | kfree(rtc_data); |
253 | return err; |
254 | } |
255 | |
256 | #ifdef CONFIG_PM |
257 | static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) |
258 | { |
259 | return 0; |
260 | } |
261 | |
262 | static int stmp3xxx_rtc_resume(struct platform_device *dev) |
263 | { |
264 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); |
265 | |
266 | stmp3xxx_reset_block(rtc_data->io, true); |
267 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | |
268 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | |
269 | BM_RTC_PERSISTENT0_ALARM_WAKE, |
270 | rtc_data->io + HW_RTC_PERSISTENT0); |
271 | return 0; |
272 | } |
273 | #else |
274 | #define stmp3xxx_rtc_suspend NULL |
275 | #define stmp3xxx_rtc_resume NULL |
276 | #endif |
277 | |
278 | static struct platform_driver stmp3xxx_rtcdrv = { |
279 | .probe = stmp3xxx_rtc_probe, |
280 | .remove = stmp3xxx_rtc_remove, |
281 | .suspend = stmp3xxx_rtc_suspend, |
282 | .resume = stmp3xxx_rtc_resume, |
283 | .driver = { |
284 | .name = "stmp3xxx-rtc", |
285 | .owner = THIS_MODULE, |
286 | }, |
287 | }; |
288 | |
289 | static int __init stmp3xxx_rtc_init(void) |
290 | { |
291 | return platform_driver_register(&stmp3xxx_rtcdrv); |
292 | } |
293 | |
294 | static void __exit stmp3xxx_rtc_exit(void) |
295 | { |
296 | platform_driver_unregister(&stmp3xxx_rtcdrv); |
297 | } |
298 | |
299 | module_init(stmp3xxx_rtc_init); |
300 | module_exit(stmp3xxx_rtc_exit); |
301 | |
302 | MODULE_DESCRIPTION("STMP3xxx RTC Driver"); |
303 | MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); |
304 | MODULE_LICENSE("GPL"); |