Root/
Source at commit 0e502b79ca6ecdfe4fb4082f3ba4ccf98a48d6e1 created 10 years 11 months ago. By Lars-Peter Clausen, drivers/rtc/rtc-jz4740.c: Use managed resources | |
---|---|
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/io.h> |
18 | #include <linux/clk.h> |
19 | #include <linux/kernel.h> |
20 | #include <linux/module.h> |
21 | #include <linux/platform_device.h> |
22 | #include <linux/rtc.h> |
23 | #include <linux/slab.h> |
24 | #include <linux/spinlock.h> |
25 | |
26 | #define JZ_REG_RTC_CTRL 0x00 |
27 | #define JZ_REG_RTC_SEC 0x04 |
28 | #define JZ_REG_RTC_SEC_ALARM 0x08 |
29 | #define JZ_REG_RTC_REGULATOR 0x0C |
30 | #define JZ_REG_RTC_HIBERNATE 0x20 |
31 | #define JZ_REG_RTC_SCRATCHPAD 0x34 |
32 | |
33 | #define JZ_RTC_CTRL_WRDY BIT(7) |
34 | #define JZ_RTC_CTRL_1HZ BIT(6) |
35 | #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) |
36 | #define JZ_RTC_CTRL_AF BIT(4) |
37 | #define JZ_RTC_CTRL_AF_IRQ BIT(3) |
38 | #define JZ_RTC_CTRL_AE BIT(2) |
39 | #define JZ_RTC_CTRL_ENABLE BIT(0) |
40 | |
41 | struct jz4740_rtc { |
42 | void __iomem *base; |
43 | |
44 | struct rtc_device *rtc; |
45 | |
46 | 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 jz4740_rtc_probe(struct platform_device *pdev) |
215 | { |
216 | int ret; |
217 | struct jz4740_rtc *rtc; |
218 | struct resource *mem; |
219 | uint32_t scratchpad; |
220 | struct clk *rtc_clk; |
221 | |
222 | rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL); |
223 | if (!rtc) |
224 | return -ENOMEM; |
225 | |
226 | rtc->irq = platform_get_irq(pdev, 0); |
227 | if (rtc->irq < 0) { |
228 | dev_err(&pdev->dev, "Failed to get platform irq\n"); |
229 | return -ENOENT; |
230 | } |
231 | |
232 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
233 | rtc->base = devm_ioremap_resource(&pdev->dev, mem); |
234 | if (!rtc->base) |
235 | return PTR_ERR(rtc->base); |
236 | |
237 | spin_lock_init(&rtc->lock); |
238 | |
239 | platform_set_drvdata(pdev, rtc); |
240 | |
241 | device_init_wakeup(&pdev->dev, 1); |
242 | |
243 | rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, |
244 | &jz4740_rtc_ops, THIS_MODULE); |
245 | if (IS_ERR(rtc->rtc)) { |
246 | ret = PTR_ERR(rtc->rtc); |
247 | dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); |
248 | return ret; |
249 | } |
250 | |
251 | ret = devm_request_irq(&pdev->dev, rtc->irq, jz4740_rtc_irq, 0, |
252 | pdev->name, rtc); |
253 | if (ret) { |
254 | dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); |
255 | return ret; |
256 | } |
257 | |
258 | scratchpad = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD); |
259 | if (scratchpad != 0x12345678) { |
260 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678); |
261 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0); |
262 | if (ret) { |
263 | dev_err(&pdev->dev, "Could not write write to RTC registers\n"); |
264 | return ret; |
265 | } |
266 | } |
267 | |
268 | rtc_clk = clk_get(&pdev->dev, "rtc"); |
269 | if (IS_ERR(rtc_clk)) { |
270 | dev_err(&pdev->dev, "Failed to get RTC clock\n"); |
271 | return PTR_ERR(rtc_clk); |
272 | } |
273 | |
274 | /* TODO: initialize the ADJC bits (25:16) to fine-tune |
275 | * the accuracy of the RTC */ |
276 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, |
277 | (clk_get_rate(rtc_clk) - 1) & 0xffff); |
278 | clk_put(rtc_clk); |
279 | |
280 | if (ret) |
281 | dev_warn(&pdev->dev, "Could not update RTC regulator register\n"); |
282 | |
283 | return 0; |
284 | } |
285 | |
286 | #ifdef CONFIG_PM |
287 | static int jz4740_rtc_suspend(struct device *dev) |
288 | { |
289 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
290 | |
291 | if (device_may_wakeup(dev)) |
292 | enable_irq_wake(rtc->irq); |
293 | return 0; |
294 | } |
295 | |
296 | static int jz4740_rtc_resume(struct device *dev) |
297 | { |
298 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
299 | |
300 | if (device_may_wakeup(dev)) |
301 | disable_irq_wake(rtc->irq); |
302 | return 0; |
303 | } |
304 | |
305 | static const struct dev_pm_ops jz4740_pm_ops = { |
306 | .suspend = jz4740_rtc_suspend, |
307 | .resume = jz4740_rtc_resume, |
308 | }; |
309 | #define JZ4740_RTC_PM_OPS (&jz4740_pm_ops) |
310 | |
311 | #else |
312 | #define JZ4740_RTC_PM_OPS NULL |
313 | #endif /* CONFIG_PM */ |
314 | |
315 | static struct platform_driver jz4740_rtc_driver = { |
316 | .probe = jz4740_rtc_probe, |
317 | .driver = { |
318 | .name = "jz4740-rtc", |
319 | .owner = THIS_MODULE, |
320 | .pm = JZ4740_RTC_PM_OPS, |
321 | }, |
322 | }; |
323 | |
324 | module_platform_driver(jz4740_rtc_driver); |
325 | |
326 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); |
327 | MODULE_LICENSE("GPL"); |
328 | MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n"); |
329 | MODULE_ALIAS("platform:jz4740-rtc"); |
330 |
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