1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> |
4 | * Copyright (C) 2010, Paul Cercueil <paul@crapouillou.net> |
5 | * JZ4740 SoC RTC driver |
6 | */ |
7 | |
8 | #include <linux/clk.h> |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/io.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/platform_device.h> |
16 | #include <linux/pm_wakeirq.h> |
17 | #include <linux/property.h> |
18 | #include <linux/reboot.h> |
19 | #include <linux/rtc.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/spinlock.h> |
22 | |
23 | #define JZ_REG_RTC_CTRL 0x00 |
24 | #define JZ_REG_RTC_SEC 0x04 |
25 | #define JZ_REG_RTC_SEC_ALARM 0x08 |
26 | #define JZ_REG_RTC_REGULATOR 0x0C |
27 | #define JZ_REG_RTC_HIBERNATE 0x20 |
28 | #define JZ_REG_RTC_WAKEUP_FILTER 0x24 |
29 | #define JZ_REG_RTC_RESET_COUNTER 0x28 |
30 | #define JZ_REG_RTC_SCRATCHPAD 0x34 |
31 | #define JZ_REG_RTC_CKPCR 0x40 |
32 | |
33 | /* The following are present on the jz4780 */ |
34 | #define JZ_REG_RTC_WENR 0x3C |
35 | #define JZ_RTC_WENR_WEN BIT(31) |
36 | |
37 | #define JZ_RTC_CTRL_WRDY BIT(7) |
38 | #define JZ_RTC_CTRL_1HZ BIT(6) |
39 | #define JZ_RTC_CTRL_1HZ_IRQ BIT(5) |
40 | #define JZ_RTC_CTRL_AF BIT(4) |
41 | #define JZ_RTC_CTRL_AF_IRQ BIT(3) |
42 | #define JZ_RTC_CTRL_AE BIT(2) |
43 | #define JZ_RTC_CTRL_ENABLE BIT(0) |
44 | |
45 | /* Magic value to enable writes on jz4780 */ |
46 | #define JZ_RTC_WENR_MAGIC 0xA55A |
47 | |
48 | #define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0 |
49 | #define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0 |
50 | |
51 | #define JZ_RTC_CKPCR_CK32PULL_DIS BIT(4) |
52 | #define JZ_RTC_CKPCR_CK32CTL_EN (BIT(2) | BIT(1)) |
53 | |
54 | enum jz4740_rtc_type { |
55 | ID_JZ4740, |
56 | ID_JZ4760, |
57 | ID_JZ4780, |
58 | }; |
59 | |
60 | struct jz4740_rtc { |
61 | void __iomem *base; |
62 | enum jz4740_rtc_type type; |
63 | |
64 | struct rtc_device *rtc; |
65 | |
66 | struct clk_hw clk32k; |
67 | |
68 | spinlock_t lock; |
69 | }; |
70 | |
71 | static struct device *dev_for_power_off; |
72 | |
73 | static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) |
74 | { |
75 | return readl(addr: rtc->base + reg); |
76 | } |
77 | |
78 | static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) |
79 | { |
80 | uint32_t ctrl; |
81 | |
82 | return readl_poll_timeout(rtc->base + JZ_REG_RTC_CTRL, ctrl, |
83 | ctrl & JZ_RTC_CTRL_WRDY, 0, 1000); |
84 | } |
85 | |
86 | static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc) |
87 | { |
88 | uint32_t ctrl; |
89 | int ret; |
90 | |
91 | ret = jz4740_rtc_wait_write_ready(rtc); |
92 | if (ret != 0) |
93 | return ret; |
94 | |
95 | writel(JZ_RTC_WENR_MAGIC, addr: rtc->base + JZ_REG_RTC_WENR); |
96 | |
97 | return readl_poll_timeout(rtc->base + JZ_REG_RTC_WENR, ctrl, |
98 | ctrl & JZ_RTC_WENR_WEN, 0, 1000); |
99 | } |
100 | |
101 | static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, |
102 | uint32_t val) |
103 | { |
104 | int ret = 0; |
105 | |
106 | if (rtc->type >= ID_JZ4760) |
107 | ret = jz4780_rtc_enable_write(rtc); |
108 | if (ret == 0) |
109 | ret = jz4740_rtc_wait_write_ready(rtc); |
110 | if (ret == 0) |
111 | writel(val, addr: rtc->base + reg); |
112 | |
113 | return ret; |
114 | } |
115 | |
116 | static int jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, |
117 | bool set) |
118 | { |
119 | int ret; |
120 | unsigned long flags; |
121 | uint32_t ctrl; |
122 | |
123 | spin_lock_irqsave(&rtc->lock, flags); |
124 | |
125 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
126 | |
127 | /* Don't clear interrupt flags by accident */ |
128 | ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; |
129 | |
130 | if (set) |
131 | ctrl |= mask; |
132 | else |
133 | ctrl &= ~mask; |
134 | |
135 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, val: ctrl); |
136 | |
137 | spin_unlock_irqrestore(lock: &rtc->lock, flags); |
138 | |
139 | return ret; |
140 | } |
141 | |
142 | static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) |
143 | { |
144 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
145 | uint32_t secs, secs2; |
146 | int timeout = 5; |
147 | |
148 | if (jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SCRATCHPAD) != 0x12345678) |
149 | return -EINVAL; |
150 | |
151 | /* If the seconds register is read while it is updated, it can contain a |
152 | * bogus value. This can be avoided by making sure that two consecutive |
153 | * reads have the same value. |
154 | */ |
155 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
156 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
157 | |
158 | while (secs != secs2 && --timeout) { |
159 | secs = secs2; |
160 | secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); |
161 | } |
162 | |
163 | if (timeout == 0) |
164 | return -EIO; |
165 | |
166 | rtc_time64_to_tm(time: secs, tm: time); |
167 | |
168 | return 0; |
169 | } |
170 | |
171 | static int jz4740_rtc_set_time(struct device *dev, struct rtc_time *time) |
172 | { |
173 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
174 | int ret; |
175 | |
176 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, val: rtc_tm_to_time64(tm: time)); |
177 | if (ret) |
178 | return ret; |
179 | |
180 | return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, val: 0x12345678); |
181 | } |
182 | |
183 | static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
184 | { |
185 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
186 | uint32_t secs; |
187 | uint32_t ctrl; |
188 | |
189 | secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); |
190 | |
191 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
192 | |
193 | alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); |
194 | alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); |
195 | |
196 | rtc_time64_to_tm(time: secs, tm: &alrm->time); |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) |
202 | { |
203 | int ret; |
204 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
205 | uint32_t secs = lower_32_bits(rtc_tm_to_time64(&alrm->time)); |
206 | |
207 | ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, val: secs); |
208 | if (!ret) |
209 | ret = jz4740_rtc_ctrl_set_bits(rtc, |
210 | JZ_RTC_CTRL_AE | JZ_RTC_CTRL_AF_IRQ, set: alrm->enabled); |
211 | |
212 | return ret; |
213 | } |
214 | |
215 | static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enable) |
216 | { |
217 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
218 | return jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, set: enable); |
219 | } |
220 | |
221 | static const struct rtc_class_ops jz4740_rtc_ops = { |
222 | .read_time = jz4740_rtc_read_time, |
223 | .set_time = jz4740_rtc_set_time, |
224 | .read_alarm = jz4740_rtc_read_alarm, |
225 | .set_alarm = jz4740_rtc_set_alarm, |
226 | .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, |
227 | }; |
228 | |
229 | static irqreturn_t jz4740_rtc_irq(int irq, void *data) |
230 | { |
231 | struct jz4740_rtc *rtc = data; |
232 | uint32_t ctrl; |
233 | unsigned long events = 0; |
234 | |
235 | ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); |
236 | |
237 | if (ctrl & JZ_RTC_CTRL_1HZ) |
238 | events |= (RTC_UF | RTC_IRQF); |
239 | |
240 | if (ctrl & JZ_RTC_CTRL_AF) |
241 | events |= (RTC_AF | RTC_IRQF); |
242 | |
243 | rtc_update_irq(rtc: rtc->rtc, num: 1, events); |
244 | |
245 | jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, set: false); |
246 | |
247 | return IRQ_HANDLED; |
248 | } |
249 | |
250 | static void jz4740_rtc_poweroff(struct device *dev) |
251 | { |
252 | struct jz4740_rtc *rtc = dev_get_drvdata(dev); |
253 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_HIBERNATE, val: 1); |
254 | } |
255 | |
256 | static void jz4740_rtc_power_off(void) |
257 | { |
258 | jz4740_rtc_poweroff(dev: dev_for_power_off); |
259 | kernel_halt(); |
260 | } |
261 | |
262 | static const struct of_device_id jz4740_rtc_of_match[] = { |
263 | { .compatible = "ingenic,jz4740-rtc" , .data = (void *)ID_JZ4740 }, |
264 | { .compatible = "ingenic,jz4760-rtc" , .data = (void *)ID_JZ4760 }, |
265 | { .compatible = "ingenic,jz4770-rtc" , .data = (void *)ID_JZ4780 }, |
266 | { .compatible = "ingenic,jz4780-rtc" , .data = (void *)ID_JZ4780 }, |
267 | {}, |
268 | }; |
269 | MODULE_DEVICE_TABLE(of, jz4740_rtc_of_match); |
270 | |
271 | static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc, |
272 | struct device_node *np, |
273 | unsigned long rate) |
274 | { |
275 | unsigned long wakeup_ticks, reset_ticks; |
276 | unsigned int min_wakeup_pin_assert_time = 60; /* Default: 60ms */ |
277 | unsigned int reset_pin_assert_time = 100; /* Default: 100ms */ |
278 | |
279 | of_property_read_u32(np, propname: "ingenic,reset-pin-assert-time-ms" , |
280 | out_value: &reset_pin_assert_time); |
281 | of_property_read_u32(np, propname: "ingenic,min-wakeup-pin-assert-time-ms" , |
282 | out_value: &min_wakeup_pin_assert_time); |
283 | |
284 | /* |
285 | * Set minimum wakeup pin assertion time: 100 ms. |
286 | * Range is 0 to 2 sec if RTC is clocked at 32 kHz. |
287 | */ |
288 | wakeup_ticks = (min_wakeup_pin_assert_time * rate) / 1000; |
289 | if (wakeup_ticks < JZ_RTC_WAKEUP_FILTER_MASK) |
290 | wakeup_ticks &= JZ_RTC_WAKEUP_FILTER_MASK; |
291 | else |
292 | wakeup_ticks = JZ_RTC_WAKEUP_FILTER_MASK; |
293 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_WAKEUP_FILTER, val: wakeup_ticks); |
294 | |
295 | /* |
296 | * Set reset pin low-level assertion time after wakeup: 60 ms. |
297 | * Range is 0 to 125 ms if RTC is clocked at 32 kHz. |
298 | */ |
299 | reset_ticks = (reset_pin_assert_time * rate) / 1000; |
300 | if (reset_ticks < JZ_RTC_RESET_COUNTER_MASK) |
301 | reset_ticks &= JZ_RTC_RESET_COUNTER_MASK; |
302 | else |
303 | reset_ticks = JZ_RTC_RESET_COUNTER_MASK; |
304 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, val: reset_ticks); |
305 | } |
306 | |
307 | static int jz4740_rtc_clk32k_enable(struct clk_hw *hw) |
308 | { |
309 | struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); |
310 | |
311 | return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, |
312 | JZ_RTC_CKPCR_CK32PULL_DIS | |
313 | JZ_RTC_CKPCR_CK32CTL_EN); |
314 | } |
315 | |
316 | static void jz4740_rtc_clk32k_disable(struct clk_hw *hw) |
317 | { |
318 | struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); |
319 | |
320 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, val: 0); |
321 | } |
322 | |
323 | static int jz4740_rtc_clk32k_is_enabled(struct clk_hw *hw) |
324 | { |
325 | struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k); |
326 | u32 ckpcr; |
327 | |
328 | ckpcr = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CKPCR); |
329 | |
330 | return !!(ckpcr & JZ_RTC_CKPCR_CK32CTL_EN); |
331 | } |
332 | |
333 | static const struct clk_ops jz4740_rtc_clk32k_ops = { |
334 | .enable = jz4740_rtc_clk32k_enable, |
335 | .disable = jz4740_rtc_clk32k_disable, |
336 | .is_enabled = jz4740_rtc_clk32k_is_enabled, |
337 | }; |
338 | |
339 | static int jz4740_rtc_probe(struct platform_device *pdev) |
340 | { |
341 | struct device *dev = &pdev->dev; |
342 | struct device_node *np = dev->of_node; |
343 | struct jz4740_rtc *rtc; |
344 | unsigned long rate; |
345 | struct clk *clk; |
346 | int ret, irq; |
347 | |
348 | rtc = devm_kzalloc(dev, size: sizeof(*rtc), GFP_KERNEL); |
349 | if (!rtc) |
350 | return -ENOMEM; |
351 | |
352 | rtc->type = (uintptr_t)device_get_match_data(dev); |
353 | |
354 | irq = platform_get_irq(pdev, 0); |
355 | if (irq < 0) |
356 | return irq; |
357 | |
358 | rtc->base = devm_platform_ioremap_resource(pdev, index: 0); |
359 | if (IS_ERR(ptr: rtc->base)) |
360 | return PTR_ERR(ptr: rtc->base); |
361 | |
362 | clk = devm_clk_get_enabled(dev, id: "rtc" ); |
363 | if (IS_ERR(ptr: clk)) |
364 | return dev_err_probe(dev, err: PTR_ERR(ptr: clk), fmt: "Failed to get RTC clock\n" ); |
365 | |
366 | spin_lock_init(&rtc->lock); |
367 | |
368 | platform_set_drvdata(pdev, data: rtc); |
369 | |
370 | device_init_wakeup(dev, enable: 1); |
371 | |
372 | ret = dev_pm_set_wake_irq(dev, irq); |
373 | if (ret) |
374 | return dev_err_probe(dev, err: ret, fmt: "Failed to set wake irq\n" ); |
375 | |
376 | rtc->rtc = devm_rtc_allocate_device(dev); |
377 | if (IS_ERR(ptr: rtc->rtc)) |
378 | return dev_err_probe(dev, err: PTR_ERR(ptr: rtc->rtc), |
379 | fmt: "Failed to allocate rtc device\n" ); |
380 | |
381 | rtc->rtc->ops = &jz4740_rtc_ops; |
382 | rtc->rtc->range_max = U32_MAX; |
383 | |
384 | rate = clk_get_rate(clk); |
385 | jz4740_rtc_set_wakeup_params(rtc, np, rate); |
386 | |
387 | /* Each 1 Hz pulse should happen after (rate) ticks */ |
388 | jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, val: rate - 1); |
389 | |
390 | ret = devm_rtc_register_device(rtc->rtc); |
391 | if (ret) |
392 | return ret; |
393 | |
394 | ret = devm_request_irq(dev, irq, handler: jz4740_rtc_irq, irqflags: 0, |
395 | devname: pdev->name, dev_id: rtc); |
396 | if (ret) |
397 | return dev_err_probe(dev, err: ret, fmt: "Failed to request rtc irq\n" ); |
398 | |
399 | if (of_device_is_system_power_controller(np)) { |
400 | dev_for_power_off = dev; |
401 | |
402 | if (!pm_power_off) |
403 | pm_power_off = jz4740_rtc_power_off; |
404 | else |
405 | dev_warn(dev, "Poweroff handler already present!\n" ); |
406 | } |
407 | |
408 | if (device_property_present(dev, propname: "#clock-cells" )) { |
409 | rtc->clk32k.init = CLK_HW_INIT_HW("clk32k" , __clk_get_hw(clk), |
410 | &jz4740_rtc_clk32k_ops, 0); |
411 | |
412 | ret = devm_clk_hw_register(dev, hw: &rtc->clk32k); |
413 | if (ret) |
414 | return dev_err_probe(dev, err: ret, |
415 | fmt: "Unable to register clk32k clock\n" ); |
416 | |
417 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_simple_get, |
418 | data: &rtc->clk32k); |
419 | if (ret) |
420 | return dev_err_probe(dev, err: ret, |
421 | fmt: "Unable to register clk32k clock provider\n" ); |
422 | } |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | static struct platform_driver jz4740_rtc_driver = { |
428 | .probe = jz4740_rtc_probe, |
429 | .driver = { |
430 | .name = "jz4740-rtc" , |
431 | .of_match_table = jz4740_rtc_of_match, |
432 | }, |
433 | }; |
434 | |
435 | module_platform_driver(jz4740_rtc_driver); |
436 | |
437 | MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>" ); |
438 | MODULE_LICENSE("GPL" ); |
439 | MODULE_DESCRIPTION("RTC driver for the JZ4740 SoC\n" ); |
440 | MODULE_ALIAS("platform:jz4740-rtc" ); |
441 | |