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