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 | |
df17f631 |
118 | static int stmp3xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
119 | { |
120 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
121 | |
122 | rtc_time_to_tm(__raw_readl(rtc_data->io + HW_RTC_ALARM), &alm->time); |
123 | return 0; |
124 | } |
125 | |
126 | static int stmp3xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
127 | { |
128 | unsigned long t; |
129 | struct stmp3xxx_rtc_data *rtc_data = dev_get_drvdata(dev); |
130 | |
131 | rtc_tm_to_time(&alm->time, &t); |
132 | __raw_writel(t, rtc_data->io + HW_RTC_ALARM); |
133 | return 0; |
134 | } |
135 | |
136 | static struct rtc_class_ops stmp3xxx_rtc_ops = { |
137 | .alarm_irq_enable = |
138 | stmp3xxx_alarm_irq_enable, |
df17f631 |
139 | .read_time = stmp3xxx_rtc_gettime, |
140 | .set_mmss = stmp3xxx_rtc_set_mmss, |
141 | .read_alarm = stmp3xxx_rtc_read_alarm, |
142 | .set_alarm = stmp3xxx_rtc_set_alarm, |
143 | }; |
144 | |
145 | static int stmp3xxx_rtc_remove(struct platform_device *pdev) |
146 | { |
147 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(pdev); |
148 | |
149 | if (!rtc_data) |
150 | return 0; |
151 | |
152 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, |
153 | rtc_data->io + HW_RTC_CTRL); |
154 | free_irq(rtc_data->irq_alarm, &pdev->dev); |
155 | free_irq(rtc_data->irq_1msec, &pdev->dev); |
156 | rtc_device_unregister(rtc_data->rtc); |
157 | iounmap(rtc_data->io); |
158 | kfree(rtc_data); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | static int stmp3xxx_rtc_probe(struct platform_device *pdev) |
164 | { |
165 | struct stmp3xxx_rtc_data *rtc_data; |
166 | struct resource *r; |
167 | int err; |
168 | |
169 | rtc_data = kzalloc(sizeof *rtc_data, GFP_KERNEL); |
170 | if (!rtc_data) |
171 | return -ENOMEM; |
172 | |
173 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
174 | if (!r) { |
175 | dev_err(&pdev->dev, "failed to get resource\n"); |
176 | err = -ENXIO; |
177 | goto out_free; |
178 | } |
179 | |
180 | rtc_data->io = ioremap(r->start, resource_size(r)); |
181 | if (!rtc_data->io) { |
182 | dev_err(&pdev->dev, "ioremap failed\n"); |
183 | err = -EIO; |
184 | goto out_free; |
185 | } |
186 | |
187 | rtc_data->irq_alarm = platform_get_irq(pdev, 0); |
188 | rtc_data->irq_1msec = platform_get_irq(pdev, 1); |
189 | |
190 | if (!(__raw_readl(HW_RTC_STAT + rtc_data->io) & |
191 | BM_RTC_STAT_RTC_PRESENT)) { |
192 | dev_err(&pdev->dev, "no device onboard\n"); |
193 | err = -ENODEV; |
194 | goto out_remap; |
195 | } |
196 | |
197 | stmp3xxx_reset_block(rtc_data->io, true); |
198 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | |
199 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | |
200 | BM_RTC_PERSISTENT0_ALARM_WAKE, |
201 | rtc_data->io + HW_RTC_PERSISTENT0); |
202 | rtc_data->rtc = rtc_device_register(pdev->name, &pdev->dev, |
203 | &stmp3xxx_rtc_ops, THIS_MODULE); |
204 | if (IS_ERR(rtc_data->rtc)) { |
205 | err = PTR_ERR(rtc_data->rtc); |
206 | goto out_remap; |
207 | } |
208 | |
209 | rtc_data->irq_count = 0; |
210 | err = request_irq(rtc_data->irq_alarm, stmp3xxx_rtc_interrupt, |
211 | IRQF_DISABLED, "RTC alarm", &pdev->dev); |
212 | if (err) { |
213 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", |
214 | rtc_data->irq_alarm); |
215 | goto out_irq_alarm; |
216 | } |
217 | err = request_irq(rtc_data->irq_1msec, stmp3xxx_rtc_interrupt, |
218 | IRQF_DISABLED, "RTC tick", &pdev->dev); |
219 | if (err) { |
220 | dev_err(&pdev->dev, "Cannot claim IRQ%d\n", |
221 | rtc_data->irq_1msec); |
222 | goto out_irq1; |
223 | } |
224 | |
225 | platform_set_drvdata(pdev, rtc_data); |
226 | |
227 | return 0; |
228 | |
229 | out_irq1: |
230 | free_irq(rtc_data->irq_alarm, &pdev->dev); |
231 | out_irq_alarm: |
232 | stmp3xxx_clearl(BM_RTC_CTRL_ONEMSEC_IRQ_EN | BM_RTC_CTRL_ALARM_IRQ_EN, |
233 | rtc_data->io + HW_RTC_CTRL); |
234 | rtc_device_unregister(rtc_data->rtc); |
235 | out_remap: |
236 | iounmap(rtc_data->io); |
237 | out_free: |
238 | kfree(rtc_data); |
239 | return err; |
240 | } |
241 | |
242 | #ifdef CONFIG_PM |
243 | static int stmp3xxx_rtc_suspend(struct platform_device *dev, pm_message_t state) |
244 | { |
245 | return 0; |
246 | } |
247 | |
248 | static int stmp3xxx_rtc_resume(struct platform_device *dev) |
249 | { |
250 | struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); |
251 | |
252 | stmp3xxx_reset_block(rtc_data->io, true); |
253 | stmp3xxx_clearl(BM_RTC_PERSISTENT0_ALARM_EN | |
254 | BM_RTC_PERSISTENT0_ALARM_WAKE_EN | |
255 | BM_RTC_PERSISTENT0_ALARM_WAKE, |
256 | rtc_data->io + HW_RTC_PERSISTENT0); |
257 | return 0; |
258 | } |
259 | #else |
260 | #define stmp3xxx_rtc_suspend NULL |
261 | #define stmp3xxx_rtc_resume NULL |
262 | #endif |
263 | |
264 | static struct platform_driver stmp3xxx_rtcdrv = { |
265 | .probe = stmp3xxx_rtc_probe, |
266 | .remove = stmp3xxx_rtc_remove, |
267 | .suspend = stmp3xxx_rtc_suspend, |
268 | .resume = stmp3xxx_rtc_resume, |
269 | .driver = { |
270 | .name = "stmp3xxx-rtc", |
271 | .owner = THIS_MODULE, |
272 | }, |
273 | }; |
274 | |
275 | static int __init stmp3xxx_rtc_init(void) |
276 | { |
277 | return platform_driver_register(&stmp3xxx_rtcdrv); |
278 | } |
279 | |
280 | static void __exit stmp3xxx_rtc_exit(void) |
281 | { |
282 | platform_driver_unregister(&stmp3xxx_rtcdrv); |
283 | } |
284 | |
285 | module_init(stmp3xxx_rtc_init); |
286 | module_exit(stmp3xxx_rtc_exit); |
287 | |
288 | MODULE_DESCRIPTION("STMP3xxx RTC Driver"); |
289 | MODULE_AUTHOR("dmitry pervushin <dpervushin@embeddedalley.com>"); |
290 | MODULE_LICENSE("GPL"); |