Root/
1 | /* |
2 | * Copyright (C) 2009, Lars-Peter Clausen <lars@metafoo.de> |
3 | * JZ4720/JZ4740 SoC RTC driver |
4 | * |
5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the |
7 | * Free Software Foundation; either version 2 of the License, or (at your |
8 | * option) any later version. |
9 | * |
10 | * You should have received a copy of the GNU General Public License along |
11 | * with this program; if not, write to the Free Software Foundation, Inc., |
12 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
13 | * |
14 | */ |
15 | |
16 | #include <linux/kernel.h> |
17 | #include <linux/module.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/spinlock.h> |
20 | #include <linux/rtc.h> |
21 | |
22 | #define JZ_REG_RTC_CTRL 0x00 |
23 | #define JZ_REG_RTC_SEC 0x04 |
24 | #define JZ_REG_RTC_SEC_ALARM 0x08 |
25 | #define JZ_REG_RTC_REGULATOR 0x0C |
26 | #define JZ_REG_RTC_SCRATCHPAD 0x34 |
27 | |
28 | #define JZ_RTC_CTRL_WRDY BIT(7) |
29 | #define JZ_RTC_CTRL_1HZ BIT(6) |
30 | #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) |
31 | #define JZ_RTC_CTRL_AF BIT(4) |
32 | #define JZ_RTC_CTRL_AF_IRQ BIT(3) |
33 | #define JZ_RTC_CTRL_AE BIT(2) |
34 | #define JZ_RTC_CTRL_ENABLE BIT(0) |
35 | |
36 | struct jz4740_rtc { |
37 | struct resource *mem; |
38 | void __iomem *base; |
39 | |
40 | struct rtc_device *rtc; |
41 | |
42 | unsigned int irq; |
43 | |
44 | spinlock_t lock; |
45 | }; |
46 | |
47 | static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) |
48 | { |
49 | return readl(rtc->base + reg); |
50 | } |
51 | |
52 | static inline void jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) |
53 | { |
54 | uint32_t ctrl; |
55 | do { |
56 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
57 | } while (!(ctrl & JZ_RTC_CTRL_WRDY)); |
58 | } |
59 | |
60 | |
61 | static inline void jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, |
62 | uint32_t val) |
63 | { |
64 | jz4740_rtc_wait_write_ready(rtc); |
65 | writel(val, rtc->base + reg); |
66 | } |
67 | |
68 | static void jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, |
69 | uint32_t val) |
70 | { |
71 | unsigned long flags; |
72 | uint32_t ctrl; |
73 | |
74 | spin_lock_irqsave(&rtc->lock, flags); |
75 | |
76 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
77 | |
78 | /* Don't clear interrupt flags by accident */ |
79 | ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; |
80 | |
81 | ctrl &= ~mask; |
82 | ctrl |= val; |
83 | |
84 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); |
85 | |
86 | spin_unlock_irqrestore(&rtc->lock, flags); |
87 | } |
88 | |
89 | static inline struct jz4740_rtc *dev_to_rtc(struct device *dev) |
90 | { |
91 | return dev_get_drvdata(dev); |
92 | } |
93 | |
94 | static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) |
95 | { |
96 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
97 | uint32_t secs, secs2; |
98 | |
99 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
100 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
101 | |
102 | while (secs != secs2) { |
103 | secs = secs2; |
104 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
105 | } |
106 | |
107 | rtc_time_to_tm(secs, time); |
108 | |
109 | return rtc_valid_tm(time); |
110 | } |
111 | |
112 | static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs) |
113 | { |
114 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
115 | |
116 | if ((uint32_t)secs != secs) |
117 | return -EINVAL; |
118 | |
119 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs); |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
125 | { |
126 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
127 | uint32_t secs, secs2; |
128 | uint32_t ctrl; |
129 | |
130 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); |
131 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); |
132 | |
133 | while (secs != secs2){ |
134 | secs = secs2; |
135 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); |
136 | } |
137 | |
138 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
139 | |
140 | alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); |
141 | alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); |
142 | |
143 | rtc_time_to_tm(secs, &alrm->time); |
144 | |
145 | return rtc_valid_tm(&alrm->time); |
146 | } |
147 | |
148 | static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
149 | { |
150 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
151 | unsigned long secs; |
152 | |
153 | rtc_tm_to_time(&alrm->time, &secs); |
154 | |
155 | if ((uint32_t)secs != secs) |
156 | return -EINVAL; |
157 | |
158 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, (uint32_t)secs); |
159 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE, |
160 | alrm->enabled ? JZ_RTC_CTRL_AE : 0); |
161 | |
162 | return 0; |
163 | } |
164 | |
165 | static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enabled) |
166 | { |
167 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
168 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, |
169 | enabled ? JZ_RTC_CTRL_1HZ_IRQ : 0); |
170 | return 0; |
171 | } |
172 | |
173 | |
174 | static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) |
175 | { |
176 | struct jz4740_rtc *rtc = dev_to_rtc(dev); |
177 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, |
178 | enabled ? JZ_RTC_CTRL_AF_IRQ : 0); |
179 | return 0; |
180 | } |
181 | |
182 | static struct rtc_class_ops jz4740_rtc_ops = { |
183 | .read_time = jz4740_rtc_read_time, |
184 | .set_mmss = jz4740_rtc_set_mmss, |
185 | .read_alarm = jz4740_rtc_read_alarm, |
186 | .set_alarm = jz4740_rtc_set_alarm, |
187 | .update_irq_enable = jz4740_rtc_update_irq_enable, |
188 | .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, |
189 | }; |
190 | |
191 | static irqreturn_t jz4740_rtc_irq(int irq, void *data) |
192 | { |
193 | struct jz4740_rtc *rtc = data; |
194 | uint32_t ctrl; |
195 | unsigned long events = 0; |
196 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
197 | |
198 | if (ctrl & JZ_RTC_CTRL_1HZ) |
199 | events |= (RTC_UF | RTC_IRQF); |
200 | |
201 | if (ctrl & JZ_RTC_CTRL_AF) |
202 | events |= (RTC_AF | RTC_IRQF); |
203 | |
204 | rtc_update_irq(rtc->rtc, 1, events); |
205 | |
206 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, 0); |
207 | |
208 | return IRQ_HANDLED; |
209 | } |
210 | |
211 | static int __devinit jz4740_rtc_probe(struct platform_device *pdev) |
212 | { |
213 | int ret; |
214 | struct jz4740_rtc *rtc; |
215 | uint32_t scratchpad; |
216 | |
217 | rtc = kmalloc(sizeof(*rtc), GFP_KERNEL); |
218 | |
219 | rtc->irq = platform_get_irq(pdev, 0); |
220 | |
221 | if (rtc->irq < 0) { |
222 | ret = -ENOENT; |
223 | dev_err(&pdev->dev, "Failed to get platform irq\n"); |
224 | goto err_free; |
225 | } |
226 | |
227 | rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
228 | if (!rtc->mem) { |
229 | ret = -ENOENT; |
230 | dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); |
231 | goto err_free; |
232 | } |
233 | |
234 | rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), |
235 | pdev->name); |
236 | |
237 | if (!rtc->mem) { |
238 | ret = -EBUSY; |
239 | dev_err(&pdev->dev, "Failed to request mmio memory region\n"); |
240 | goto err_free; |
241 | } |
242 | |
243 | rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); |
244 | |
245 | if (!rtc->base) { |
246 | ret = -EBUSY; |
247 | dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); |
248 | goto err_release_mem_region; |
249 | } |
250 | |
251 | platform_set_drvdata(pdev, rtc); |
252 | |
253 | rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, |
254 | THIS_MODULE); |
255 | |
256 | if (IS_ERR(rtc->rtc)) { |
257 | ret = PTR_ERR(rtc->rtc); |
258 | dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); |
259 | goto err_iounmap; |
260 | } |
261 | |
262 | ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, |
263 | pdev->name, rtc); |
264 | |
265 | if (ret) { |
266 | dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); |
267 | goto err_unregister_rtc; |
268 | } |
269 | |
270 | scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); |
271 | if (scratchpad != 0x12345678) { |
272 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); |
273 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); |
274 | } |
275 | |
276 | return 0; |
277 | |
278 | err_unregister_rtc: |
279 | rtc_device_unregister(rtc->rtc); |
280 | err_iounmap: |
281 | platform_set_drvdata(pdev, NULL); |
282 | iounmap(rtc->base); |
283 | err_release_mem_region: |
284 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); |
285 | err_free: |
286 | kfree(rtc); |
287 | |
288 | return ret; |
289 | } |
290 | |
291 | static int __devexit jz4740_rtc_remove(struct platform_device *pdev) |
292 | { |
293 | struct jz4740_rtc *rtc = platform_get_drvdata(pdev); |
294 | |
295 | free_irq(rtc->irq, rtc); |
296 | |
297 | rtc_device_unregister(rtc->rtc); |
298 | |
299 | iounmap(rtc->base); |
300 | release_mem_region(rtc->mem->start, resource_size(rtc->mem)); |
301 | |
302 | kfree(rtc); |
303 | |
304 | platform_set_drvdata(pdev, NULL); |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | struct platform_driver jz4740_rtc_driver = { |
310 | .probe = jz4740_rtc_probe, |
311 | .remove = __devexit_p(jz4740_rtc_remove), |
312 | .driver = { |
313 | .name = "jz4740-rtc", |
314 | .owner = THIS_MODULE, |
315 | }, |
316 | }; |
317 | |
318 | static int __init jz4740_rtc_init(void) |
319 | { |
320 | return platform_driver_register(&jz4740_rtc_driver); |
321 | } |
322 | module_init(jz4740_rtc_init); |
323 | |
324 | static void __exit jz4740_rtc_exit(void) |
325 | { |
326 | platform_driver_unregister(&jz4740_rtc_driver); |
327 | } |
328 | module_exit(jz4740_rtc_exit); |
329 | |
330 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
331 | MODULE_LICENSE("GPL"); |
332 | MODULE_DESCRIPTION("RTC driver for the JZ4720/JZ4740 SoC\n"); |
333 | MODULE_ALIAS("platform:jz4740-rtc"); |
334 |
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