Root/
1 | /* |
2 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
3 | * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> |
4 | * JZ4740 SoC RTC driver |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it |
7 | * under the terms of the GNU General Public License as published by the |
8 | * Free Software Foundation; either version 2 of the License, or (at your |
9 | * option) any later version. |
10 | * |
11 | * You should have received a copy of the GNU General Public License along |
12 | * with this program; if not, write to the Free Software Foundation, Inc., |
13 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
14 | * |
15 | */ |
16 | |
17 | #include <linux/clk.h> |
18 | #include <linux/kernel.h> |
19 | #include <linux/module.h> |
20 | #include <linux/platform_device.h> |
21 | #include <linux/rtc.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/spinlock.h> |
24 | |
25 | #define JZ_REG_RTC_CTRL 0x00 |
26 | #define JZ_REG_RTC_SEC 0x04 |
27 | #define JZ_REG_RTC_SEC_ALARM 0x08 |
28 | #define JZ_REG_RTC_REGULATOR 0x0C |
29 | #define JZ_REG_RTC_HIBERNATE 0x20 |
30 | #define JZ_REG_RTC_SCRATCHPAD 0x34 |
31 | |
32 | #define JZ_RTC_CTRL_WRDY BIT(7) |
33 | #define JZ_RTC_CTRL_1HZ BIT(6) |
34 | #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) |
35 | #define JZ_RTC_CTRL_AF BIT(4) |
36 | #define JZ_RTC_CTRL_AF_IRQ BIT(3) |
37 | #define JZ_RTC_CTRL_AE BIT(2) |
38 | #define JZ_RTC_CTRL_ENABLE BIT(0) |
39 | |
40 | struct jz4740_rtc { |
41 | struct resource *mem; |
42 | void __iomem *base; |
43 | |
44 | struct rtc_device *rtc; |
45 | |
46 | unsigned int irq; |
47 | |
48 | spinlock_t lock; |
49 | }; |
50 | |
51 | static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) |
52 | { |
53 | return readl(rtc->base + reg); |
54 | } |
55 | |
56 | static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) |
57 | { |
58 | uint32_t ctrl; |
59 | int timeout = 1000; |
60 | |
61 | do { |
62 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
63 | } while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout); |
64 | |
65 | return timeout ? 0 : -EIO; |
66 | } |
67 | |
68 | static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, |
69 | uint32_t val) |
70 | { |
71 | int ret; |
72 | ret = jz4740_rtc_wait_write_ready(rtc); |
73 | if (ret == 0) |
74 | writel(val, rtc->base + reg); |
75 | |
76 | return ret; |
77 | } |
78 | |
79 | static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, |
80 | bool set) |
81 | { |
82 | int ret; |
83 | unsigned long flags; |
84 | uint32_t ctrl; |
85 | |
86 | spin_lock_irqsave(&rtc->lock, flags); |
87 | |
88 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
89 | |
90 | /* Don't clear interrupt flags by accident */ |
91 | ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; |
92 | |
93 | if (set) |
94 | ctrl |= mask; |
95 | else |
96 | ctrl &= ~mask; |
97 | |
98 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); |
99 | |
100 | spin_unlock_irqrestore(&rtc->lock, flags); |
101 | |
102 | return ret; |
103 | } |
104 | |
105 | static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) |
106 | { |
107 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
108 | uint32_t secs, secs2; |
109 | int timeout = 5; |
110 | |
111 | /* If the seconds register is read while it is updated, it can contain a |
112 | * bogus value. This can be avoided by making sure that two consecutive |
113 | * reads have the same value. |
114 | */ |
115 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
116 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
117 | |
118 | while (secs != secs2 && --timeout) { |
119 | secs = secs2; |
120 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
121 | } |
122 | |
123 | if (timeout == 0) |
124 | return -EIO; |
125 | |
126 | rtc_time_to_tm(secs, time); |
127 | |
128 | return rtc_valid_tm(time); |
129 | } |
130 | |
131 | static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs) |
132 | { |
133 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
134 | |
135 | return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs); |
136 | } |
137 | |
138 | static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
139 | { |
140 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
141 | uint32_t secs; |
142 | uint32_t ctrl; |
143 | |
144 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); |
145 | |
146 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
147 | |
148 | alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); |
149 | alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); |
150 | |
151 | rtc_time_to_tm(secs, &alrm->time); |
152 | |
153 | return rtc_valid_tm(&alrm->time); |
154 | } |
155 | |
156 | static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
157 | { |
158 | int ret; |
159 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
160 | unsigned long secs; |
161 | |
162 | rtc_tm_to_time(&alrm->time, &secs); |
163 | |
164 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, secs); |
165 | if (!ret) |
166 | ret = jz4740_rtc_ctrl_set_bits(rtc, |
167 | JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, alrm->enabled); |
168 | |
169 | return ret; |
170 | } |
171 | |
172 | static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
173 | { |
174 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
175 | return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, enable); |
176 | } |
177 | |
178 | static struct rtc_class_ops jz4740_rtc_ops = { |
179 | .read_time = jz4740_rtc_read_time, |
180 | .set_mmss = jz4740_rtc_set_mmss, |
181 | .read_alarm = jz4740_rtc_read_alarm, |
182 | .set_alarm = jz4740_rtc_set_alarm, |
183 | .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, |
184 | }; |
185 | |
186 | static irqreturn_t jz4740_rtc_irq(int irq, void *data) |
187 | { |
188 | struct jz4740_rtc *rtc = data; |
189 | uint32_t ctrl; |
190 | unsigned long events = 0; |
191 | |
192 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
193 | |
194 | if (ctrl & JZ_RTC_CTRL_1HZ) |
195 | events |= (RTC_UF | RTC_IRQF); |
196 | |
197 | if (ctrl & JZ_RTC_CTRL_AF) |
198 | events |= (RTC_AF | RTC_IRQF); |
199 | |
200 | rtc_update_irq(rtc->rtc, 1, events); |
201 | |
202 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, false); |
203 | |
204 | return IRQ_HANDLED; |
205 | } |
206 | |
207 | void jz4740_rtc_poweroff(struct device *dev) |
208 | { |
209 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
210 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, 1); |
211 | } |
212 | EXPORT_SYMBOL_GPL(jz4740_rtc_poweroff); |
213 | |
214 | static int __devinit jz4740_rtc_probe(struct platform_device *pdev) |
215 | { |
216 | int ret; |
217 | struct jz4740_rtc *rtc; |
218 | uint32_t scratchpad; |
219 | struct clk *rtc_clk; |
220 | |
221 | rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); |
222 | if (!rtc) |
223 | return -ENOMEM; |
224 | |
225 | rtc->irq = platform_get_irq(pdev, 0); |
226 | if (rtc->irq < 0) { |
227 | ret = -ENOENT; |
228 | dev_err(&pdev->dev, "Failed to get platform irq\n"); |
229 | goto err_free; |
230 | } |
231 | |
232 | rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
233 | if (!rtc->mem) { |
234 | ret = -ENOENT; |
235 | dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); |
236 | goto err_free; |
237 | } |
238 | |
239 | rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), |
240 | pdev->name); |
241 | if (!rtc->mem) { |
242 | ret = -EBUSY; |
243 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); |
244 | goto err_free; |
245 | } |
246 | |
247 | rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); |
248 | if (!rtc->base) { |
249 | ret = -EBUSY; |
250 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); |
251 | goto err_release_mem_region; |
252 | } |
253 | |
254 | spin_lock_init(&rtc->lock); |
255 | |
256 | platform_set_drvdata(pdev, rtc); |
257 | |
258 | device_init_wakeup(&pdev->dev, 1); |
259 | |
260 | rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, |
261 | THIS_MODULE); |
262 | if (IS_ERR(rtc->rtc)) { |
263 | ret = PTR_ERR(rtc->rtc); |
264 | dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); |
265 | goto err_iounmap; |
266 | } |
267 | |
268 | ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, |
269 | pdev->name, rtc); |
270 | if (ret) { |
271 | dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); |
272 | goto err_unregister_rtc; |
273 | } |
274 | |
275 | scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); |
276 | if (scratchpad != 0x12345678) { |
277 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); |
278 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); |
279 | if (ret) { |
280 | dev_err(&pdev->dev, "Could not write write to RTC registers\n"); |
281 | goto err_free_irq; |
282 | } |
283 | } |
284 | |
285 | rtc_clk = clk_get(&pdev->dev, "rtc"); |
286 | if (IS_ERR(rtc_clk)) { |
287 | dev_err(&pdev->dev, "Failed to get RTC clock\n"); |
288 | goto err_free_irq; |
289 | } |
290 | |
291 | /* TODO: initialize the ADJC bits (25:16) to fine-tune |
292 | * the accuracy of the RTC */ |
293 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, |
294 | (clk_get_rate(rtc_clk) - 1) & 0xffff); |
295 | clk_put(rtc_clk); |
296 | |
297 | if (ret) |
298 | dev_warn(&pdev->dev, "Could not update RTC regulator register\n"); |
299 | |
300 | return 0; |
301 | |
302 | err_free_irq: |
303 | free_irq(rtc->irq, rtc); |
304 | err_unregister_rtc: |
305 | rtc_device_unregister(rtc->rtc); |
306 | err_iounmap: |
307 | platform_set_drvdata(pdev, NULL); |
308 | iounmap(rtc->base); |
309 | err_release_mem_region: |
310 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); |
311 | err_free: |
312 | kfree(rtc); |
313 | |
314 | return ret; |
315 | } |
316 | |
317 | static int __devexit jz4740_rtc_remove(struct platform_device *pdev) |
318 | { |
319 | struct jz4740_rtc *rtc = platform_get_drvdata(pdev); |
320 | |
321 | free_irq(rtc->irq, rtc); |
322 | |
323 | rtc_device_unregister(rtc->rtc); |
324 | |
325 | iounmap(rtc->base); |
326 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); |
327 | |
328 | kfree(rtc); |
329 | |
330 | platform_set_drvdata(pdev, NULL); |
331 | |
332 | return 0; |
333 | } |
334 | |
335 | |
336 | #ifdef CONFIG_PM |
337 | static int jz4740_rtc_suspend(struct device *dev) |
338 | { |
339 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
340 | |
341 | if (device_may_wakeup(dev)) |
342 | enable_irq_wake(rtc->irq); |
343 | return 0; |
344 | } |
345 | |
346 | static int jz4740_rtc_resume(struct device *dev) |
347 | { |
348 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
349 | |
350 | if (device_may_wakeup(dev)) |
351 | disable_irq_wake(rtc->irq); |
352 | return 0; |
353 | } |
354 | |
355 | static const struct dev_pm_ops jz4740_pm_ops = { |
356 | .suspend = jz4740_rtc_suspend, |
357 | .resume = jz4740_rtc_resume, |
358 | }; |
359 | #define JZ4740_RTC_PM_OPS (&jz4740_pm_ops) |
360 | |
361 | #else |
362 | #define JZ4740_RTC_PM_OPS NULL |
363 | #endif /* CONFIG_PM */ |
364 | |
365 | static struct platform_driver jz4740_rtc_driver = { |
366 | .probe = jz4740_rtc_probe, |
367 | .remove = __devexit_p(jz4740_rtc_remove), |
368 | .driver = { |
369 | .name = "jz4740-rtc", |
370 | .owner = THIS_MODULE, |
371 | .pm = JZ4740_RTC_PM_OPS, |
372 | }, |
373 | }; |
374 | |
375 | module_platform_driver(jz4740_rtc_driver); |
376 | |
377 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
378 | MODULE_LICENSE("GPL"); |
379 | MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); |
380 | MODULE_ALIAS("platform:jz4740-rtc"); |
381 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9