1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* drivers/rtc/rtc-s3c.c |
3 | * |
4 | * Copyright (c) 2010 Samsung Electronics Co., Ltd. |
5 | * http://www.samsung.com/ |
6 | * |
7 | * Copyright (c) 2004,2006 Simtec Electronics |
8 | * Ben Dooks, <ben@simtec.co.uk> |
9 | * http://armlinux.simtec.co.uk/ |
10 | * |
11 | * S3C2410/S3C2440/S3C24XX Internal RTC Driver |
12 | */ |
13 | |
14 | #include <linux/module.h> |
15 | #include <linux/fs.h> |
16 | #include <linux/string.h> |
17 | #include <linux/init.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/interrupt.h> |
20 | #include <linux/rtc.h> |
21 | #include <linux/bcd.h> |
22 | #include <linux/clk.h> |
23 | #include <linux/log2.h> |
24 | #include <linux/slab.h> |
25 | #include <linux/of.h> |
26 | #include <linux/uaccess.h> |
27 | #include <linux/io.h> |
28 | |
29 | #include <asm/irq.h> |
30 | #include "rtc-s3c.h" |
31 | |
32 | struct s3c_rtc { |
33 | struct device *dev; |
34 | struct rtc_device *rtc; |
35 | |
36 | void __iomem *base; |
37 | struct clk *rtc_clk; |
38 | struct clk *rtc_src_clk; |
39 | bool alarm_enabled; |
40 | |
41 | const struct s3c_rtc_data *data; |
42 | |
43 | int irq_alarm; |
44 | spinlock_t alarm_lock; |
45 | |
46 | bool wake_en; |
47 | }; |
48 | |
49 | struct s3c_rtc_data { |
50 | bool needs_src_clk; |
51 | |
52 | void (*irq_handler) (struct s3c_rtc *info, int mask); |
53 | void (*enable) (struct s3c_rtc *info); |
54 | void (*disable) (struct s3c_rtc *info); |
55 | }; |
56 | |
57 | static int s3c_rtc_enable_clk(struct s3c_rtc *info) |
58 | { |
59 | int ret; |
60 | |
61 | ret = clk_enable(clk: info->rtc_clk); |
62 | if (ret) |
63 | return ret; |
64 | |
65 | if (info->data->needs_src_clk) { |
66 | ret = clk_enable(clk: info->rtc_src_clk); |
67 | if (ret) { |
68 | clk_disable(clk: info->rtc_clk); |
69 | return ret; |
70 | } |
71 | } |
72 | return 0; |
73 | } |
74 | |
75 | static void s3c_rtc_disable_clk(struct s3c_rtc *info) |
76 | { |
77 | if (info->data->needs_src_clk) |
78 | clk_disable(clk: info->rtc_src_clk); |
79 | clk_disable(clk: info->rtc_clk); |
80 | } |
81 | |
82 | /* IRQ Handler */ |
83 | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) |
84 | { |
85 | struct s3c_rtc *info = (struct s3c_rtc *)id; |
86 | |
87 | if (info->data->irq_handler) |
88 | info->data->irq_handler(info, S3C2410_INTP_ALM); |
89 | |
90 | return IRQ_HANDLED; |
91 | } |
92 | |
93 | /* Update control registers */ |
94 | static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) |
95 | { |
96 | struct s3c_rtc *info = dev_get_drvdata(dev); |
97 | unsigned long flags; |
98 | unsigned int tmp; |
99 | int ret; |
100 | |
101 | dev_dbg(info->dev, "%s: aie=%d\n" , __func__, enabled); |
102 | |
103 | ret = s3c_rtc_enable_clk(info); |
104 | if (ret) |
105 | return ret; |
106 | |
107 | tmp = readb(addr: info->base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN; |
108 | |
109 | if (enabled) |
110 | tmp |= S3C2410_RTCALM_ALMEN; |
111 | |
112 | writeb(val: tmp, addr: info->base + S3C2410_RTCALM); |
113 | |
114 | spin_lock_irqsave(&info->alarm_lock, flags); |
115 | |
116 | if (info->alarm_enabled && !enabled) |
117 | s3c_rtc_disable_clk(info); |
118 | else if (!info->alarm_enabled && enabled) |
119 | ret = s3c_rtc_enable_clk(info); |
120 | |
121 | info->alarm_enabled = enabled; |
122 | spin_unlock_irqrestore(lock: &info->alarm_lock, flags); |
123 | |
124 | s3c_rtc_disable_clk(info); |
125 | |
126 | return ret; |
127 | } |
128 | |
129 | /* Read time from RTC and convert it from BCD */ |
130 | static int s3c_rtc_read_time(struct s3c_rtc *info, struct rtc_time *tm) |
131 | { |
132 | unsigned int have_retried = 0; |
133 | int ret; |
134 | |
135 | ret = s3c_rtc_enable_clk(info); |
136 | if (ret) |
137 | return ret; |
138 | |
139 | retry_get_time: |
140 | tm->tm_min = readb(addr: info->base + S3C2410_RTCMIN); |
141 | tm->tm_hour = readb(addr: info->base + S3C2410_RTCHOUR); |
142 | tm->tm_mday = readb(addr: info->base + S3C2410_RTCDATE); |
143 | tm->tm_mon = readb(addr: info->base + S3C2410_RTCMON); |
144 | tm->tm_year = readb(addr: info->base + S3C2410_RTCYEAR); |
145 | tm->tm_sec = readb(addr: info->base + S3C2410_RTCSEC); |
146 | |
147 | /* |
148 | * The only way to work out whether the system was mid-update |
149 | * when we read it is to check the second counter, and if it |
150 | * is zero, then we re-try the entire read |
151 | */ |
152 | if (tm->tm_sec == 0 && !have_retried) { |
153 | have_retried = 1; |
154 | goto retry_get_time; |
155 | } |
156 | |
157 | s3c_rtc_disable_clk(info); |
158 | |
159 | tm->tm_sec = bcd2bin(tm->tm_sec); |
160 | tm->tm_min = bcd2bin(tm->tm_min); |
161 | tm->tm_hour = bcd2bin(tm->tm_hour); |
162 | tm->tm_mday = bcd2bin(tm->tm_mday); |
163 | tm->tm_mon = bcd2bin(tm->tm_mon); |
164 | tm->tm_year = bcd2bin(tm->tm_year); |
165 | |
166 | return 0; |
167 | } |
168 | |
169 | /* Convert time to BCD and write it to RTC */ |
170 | static int s3c_rtc_write_time(struct s3c_rtc *info, const struct rtc_time *tm) |
171 | { |
172 | int ret; |
173 | |
174 | ret = s3c_rtc_enable_clk(info); |
175 | if (ret) |
176 | return ret; |
177 | |
178 | writeb(bin2bcd(tm->tm_sec), addr: info->base + S3C2410_RTCSEC); |
179 | writeb(bin2bcd(tm->tm_min), addr: info->base + S3C2410_RTCMIN); |
180 | writeb(bin2bcd(tm->tm_hour), addr: info->base + S3C2410_RTCHOUR); |
181 | writeb(bin2bcd(tm->tm_mday), addr: info->base + S3C2410_RTCDATE); |
182 | writeb(bin2bcd(tm->tm_mon), addr: info->base + S3C2410_RTCMON); |
183 | writeb(bin2bcd(tm->tm_year), addr: info->base + S3C2410_RTCYEAR); |
184 | |
185 | s3c_rtc_disable_clk(info); |
186 | |
187 | return 0; |
188 | } |
189 | |
190 | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *tm) |
191 | { |
192 | struct s3c_rtc *info = dev_get_drvdata(dev); |
193 | int ret; |
194 | |
195 | ret = s3c_rtc_read_time(info, tm); |
196 | if (ret) |
197 | return ret; |
198 | |
199 | /* Convert internal representation to actual date/time */ |
200 | tm->tm_year += 100; |
201 | tm->tm_mon -= 1; |
202 | |
203 | dev_dbg(dev, "read time %ptR\n" , tm); |
204 | return 0; |
205 | } |
206 | |
207 | static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) |
208 | { |
209 | struct s3c_rtc *info = dev_get_drvdata(dev); |
210 | struct rtc_time rtc_tm = *tm; |
211 | |
212 | dev_dbg(dev, "set time %ptR\n" , tm); |
213 | |
214 | /* |
215 | * Convert actual date/time to internal representation. |
216 | * We get around Y2K by simply not supporting it. |
217 | */ |
218 | rtc_tm.tm_year -= 100; |
219 | rtc_tm.tm_mon += 1; |
220 | |
221 | return s3c_rtc_write_time(info, tm: &rtc_tm); |
222 | } |
223 | |
224 | static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) |
225 | { |
226 | struct s3c_rtc *info = dev_get_drvdata(dev); |
227 | struct rtc_time *alm_tm = &alrm->time; |
228 | unsigned int alm_en; |
229 | int ret; |
230 | |
231 | ret = s3c_rtc_enable_clk(info); |
232 | if (ret) |
233 | return ret; |
234 | |
235 | alm_tm->tm_sec = readb(addr: info->base + S3C2410_ALMSEC); |
236 | alm_tm->tm_min = readb(addr: info->base + S3C2410_ALMMIN); |
237 | alm_tm->tm_hour = readb(addr: info->base + S3C2410_ALMHOUR); |
238 | alm_tm->tm_mon = readb(addr: info->base + S3C2410_ALMMON); |
239 | alm_tm->tm_mday = readb(addr: info->base + S3C2410_ALMDATE); |
240 | alm_tm->tm_year = readb(addr: info->base + S3C2410_ALMYEAR); |
241 | |
242 | alm_en = readb(addr: info->base + S3C2410_RTCALM); |
243 | |
244 | s3c_rtc_disable_clk(info); |
245 | |
246 | alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0; |
247 | |
248 | dev_dbg(dev, "read alarm %d, %ptR\n" , alm_en, alm_tm); |
249 | |
250 | /* decode the alarm enable field */ |
251 | if (alm_en & S3C2410_RTCALM_SECEN) |
252 | alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); |
253 | |
254 | if (alm_en & S3C2410_RTCALM_MINEN) |
255 | alm_tm->tm_min = bcd2bin(alm_tm->tm_min); |
256 | |
257 | if (alm_en & S3C2410_RTCALM_HOUREN) |
258 | alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); |
259 | |
260 | if (alm_en & S3C2410_RTCALM_DAYEN) |
261 | alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); |
262 | |
263 | if (alm_en & S3C2410_RTCALM_MONEN) { |
264 | alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); |
265 | alm_tm->tm_mon -= 1; |
266 | } |
267 | |
268 | if (alm_en & S3C2410_RTCALM_YEAREN) |
269 | alm_tm->tm_year = bcd2bin(alm_tm->tm_year); |
270 | |
271 | return 0; |
272 | } |
273 | |
274 | static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) |
275 | { |
276 | struct s3c_rtc *info = dev_get_drvdata(dev); |
277 | struct rtc_time *tm = &alrm->time; |
278 | unsigned int alrm_en; |
279 | int ret; |
280 | |
281 | dev_dbg(dev, "s3c_rtc_setalarm: %d, %ptR\n" , alrm->enabled, tm); |
282 | |
283 | ret = s3c_rtc_enable_clk(info); |
284 | if (ret) |
285 | return ret; |
286 | |
287 | alrm_en = readb(addr: info->base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN; |
288 | writeb(val: 0x00, addr: info->base + S3C2410_RTCALM); |
289 | |
290 | if (tm->tm_sec < 60 && tm->tm_sec >= 0) { |
291 | alrm_en |= S3C2410_RTCALM_SECEN; |
292 | writeb(bin2bcd(tm->tm_sec), addr: info->base + S3C2410_ALMSEC); |
293 | } |
294 | |
295 | if (tm->tm_min < 60 && tm->tm_min >= 0) { |
296 | alrm_en |= S3C2410_RTCALM_MINEN; |
297 | writeb(bin2bcd(tm->tm_min), addr: info->base + S3C2410_ALMMIN); |
298 | } |
299 | |
300 | if (tm->tm_hour < 24 && tm->tm_hour >= 0) { |
301 | alrm_en |= S3C2410_RTCALM_HOUREN; |
302 | writeb(bin2bcd(tm->tm_hour), addr: info->base + S3C2410_ALMHOUR); |
303 | } |
304 | |
305 | if (tm->tm_mon < 12 && tm->tm_mon >= 0) { |
306 | alrm_en |= S3C2410_RTCALM_MONEN; |
307 | writeb(bin2bcd(tm->tm_mon + 1), addr: info->base + S3C2410_ALMMON); |
308 | } |
309 | |
310 | if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { |
311 | alrm_en |= S3C2410_RTCALM_DAYEN; |
312 | writeb(bin2bcd(tm->tm_mday), addr: info->base + S3C2410_ALMDATE); |
313 | } |
314 | |
315 | dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n" , alrm_en); |
316 | |
317 | writeb(val: alrm_en, addr: info->base + S3C2410_RTCALM); |
318 | |
319 | s3c_rtc_setaie(dev, enabled: alrm->enabled); |
320 | |
321 | s3c_rtc_disable_clk(info); |
322 | |
323 | return 0; |
324 | } |
325 | |
326 | static const struct rtc_class_ops s3c_rtcops = { |
327 | .read_time = s3c_rtc_gettime, |
328 | .set_time = s3c_rtc_settime, |
329 | .read_alarm = s3c_rtc_getalarm, |
330 | .set_alarm = s3c_rtc_setalarm, |
331 | .alarm_irq_enable = s3c_rtc_setaie, |
332 | }; |
333 | |
334 | static void s3c24xx_rtc_enable(struct s3c_rtc *info) |
335 | { |
336 | unsigned int con, tmp; |
337 | |
338 | con = readw(addr: info->base + S3C2410_RTCCON); |
339 | /* re-enable the device, and check it is ok */ |
340 | if ((con & S3C2410_RTCCON_RTCEN) == 0) { |
341 | dev_info(info->dev, "rtc disabled, re-enabling\n" ); |
342 | |
343 | tmp = readw(addr: info->base + S3C2410_RTCCON); |
344 | writew(val: tmp | S3C2410_RTCCON_RTCEN, addr: info->base + S3C2410_RTCCON); |
345 | } |
346 | |
347 | if (con & S3C2410_RTCCON_CNTSEL) { |
348 | dev_info(info->dev, "removing RTCCON_CNTSEL\n" ); |
349 | |
350 | tmp = readw(addr: info->base + S3C2410_RTCCON); |
351 | writew(val: tmp & ~S3C2410_RTCCON_CNTSEL, |
352 | addr: info->base + S3C2410_RTCCON); |
353 | } |
354 | |
355 | if (con & S3C2410_RTCCON_CLKRST) { |
356 | dev_info(info->dev, "removing RTCCON_CLKRST\n" ); |
357 | |
358 | tmp = readw(addr: info->base + S3C2410_RTCCON); |
359 | writew(val: tmp & ~S3C2410_RTCCON_CLKRST, |
360 | addr: info->base + S3C2410_RTCCON); |
361 | } |
362 | } |
363 | |
364 | static void s3c24xx_rtc_disable(struct s3c_rtc *info) |
365 | { |
366 | unsigned int con; |
367 | |
368 | con = readw(addr: info->base + S3C2410_RTCCON); |
369 | con &= ~S3C2410_RTCCON_RTCEN; |
370 | writew(val: con, addr: info->base + S3C2410_RTCCON); |
371 | |
372 | con = readb(addr: info->base + S3C2410_TICNT); |
373 | con &= ~S3C2410_TICNT_ENABLE; |
374 | writeb(val: con, addr: info->base + S3C2410_TICNT); |
375 | } |
376 | |
377 | static void s3c6410_rtc_disable(struct s3c_rtc *info) |
378 | { |
379 | unsigned int con; |
380 | |
381 | con = readw(addr: info->base + S3C2410_RTCCON); |
382 | con &= ~S3C64XX_RTCCON_TICEN; |
383 | con &= ~S3C2410_RTCCON_RTCEN; |
384 | writew(val: con, addr: info->base + S3C2410_RTCCON); |
385 | } |
386 | |
387 | static void s3c_rtc_remove(struct platform_device *pdev) |
388 | { |
389 | struct s3c_rtc *info = platform_get_drvdata(pdev); |
390 | |
391 | s3c_rtc_setaie(dev: info->dev, enabled: 0); |
392 | |
393 | if (info->data->needs_src_clk) |
394 | clk_unprepare(clk: info->rtc_src_clk); |
395 | clk_unprepare(clk: info->rtc_clk); |
396 | } |
397 | |
398 | static int s3c_rtc_probe(struct platform_device *pdev) |
399 | { |
400 | struct s3c_rtc *info = NULL; |
401 | int ret; |
402 | |
403 | info = devm_kzalloc(dev: &pdev->dev, size: sizeof(*info), GFP_KERNEL); |
404 | if (!info) |
405 | return -ENOMEM; |
406 | |
407 | info->dev = &pdev->dev; |
408 | info->data = of_device_get_match_data(dev: &pdev->dev); |
409 | if (!info->data) { |
410 | dev_err(&pdev->dev, "failed getting s3c_rtc_data\n" ); |
411 | return -EINVAL; |
412 | } |
413 | spin_lock_init(&info->alarm_lock); |
414 | |
415 | platform_set_drvdata(pdev, data: info); |
416 | |
417 | info->irq_alarm = platform_get_irq(pdev, 0); |
418 | if (info->irq_alarm < 0) |
419 | return info->irq_alarm; |
420 | |
421 | dev_dbg(&pdev->dev, "s3c2410_rtc: alarm irq %d\n" , info->irq_alarm); |
422 | |
423 | /* get the memory region */ |
424 | info->base = devm_platform_ioremap_resource(pdev, index: 0); |
425 | if (IS_ERR(ptr: info->base)) |
426 | return PTR_ERR(ptr: info->base); |
427 | |
428 | info->rtc_clk = devm_clk_get(dev: &pdev->dev, id: "rtc" ); |
429 | if (IS_ERR(ptr: info->rtc_clk)) |
430 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: info->rtc_clk), |
431 | fmt: "failed to find rtc clock\n" ); |
432 | ret = clk_prepare_enable(clk: info->rtc_clk); |
433 | if (ret) |
434 | return ret; |
435 | |
436 | if (info->data->needs_src_clk) { |
437 | info->rtc_src_clk = devm_clk_get(dev: &pdev->dev, id: "rtc_src" ); |
438 | if (IS_ERR(ptr: info->rtc_src_clk)) { |
439 | ret = dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: info->rtc_src_clk), |
440 | fmt: "failed to find rtc source clock\n" ); |
441 | goto err_src_clk; |
442 | } |
443 | ret = clk_prepare_enable(clk: info->rtc_src_clk); |
444 | if (ret) |
445 | goto err_src_clk; |
446 | } |
447 | |
448 | /* disable RTC enable bits potentially set by the bootloader */ |
449 | if (info->data->disable) |
450 | info->data->disable(info); |
451 | |
452 | /* check to see if everything is setup correctly */ |
453 | if (info->data->enable) |
454 | info->data->enable(info); |
455 | |
456 | dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n" , |
457 | readw(info->base + S3C2410_RTCCON)); |
458 | |
459 | device_init_wakeup(dev: &pdev->dev, enable: 1); |
460 | |
461 | info->rtc = devm_rtc_allocate_device(dev: &pdev->dev); |
462 | if (IS_ERR(ptr: info->rtc)) { |
463 | ret = PTR_ERR(ptr: info->rtc); |
464 | goto err_nortc; |
465 | } |
466 | |
467 | info->rtc->ops = &s3c_rtcops; |
468 | info->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; |
469 | info->rtc->range_max = RTC_TIMESTAMP_END_2099; |
470 | |
471 | ret = devm_rtc_register_device(info->rtc); |
472 | if (ret) |
473 | goto err_nortc; |
474 | |
475 | ret = devm_request_irq(dev: &pdev->dev, irq: info->irq_alarm, handler: s3c_rtc_alarmirq, |
476 | irqflags: 0, devname: "s3c2410-rtc alarm" , dev_id: info); |
477 | if (ret) { |
478 | dev_err(&pdev->dev, "IRQ%d error %d\n" , info->irq_alarm, ret); |
479 | goto err_nortc; |
480 | } |
481 | |
482 | s3c_rtc_disable_clk(info); |
483 | |
484 | return 0; |
485 | |
486 | err_nortc: |
487 | if (info->data->disable) |
488 | info->data->disable(info); |
489 | |
490 | if (info->data->needs_src_clk) |
491 | clk_disable_unprepare(clk: info->rtc_src_clk); |
492 | err_src_clk: |
493 | clk_disable_unprepare(clk: info->rtc_clk); |
494 | |
495 | return ret; |
496 | } |
497 | |
498 | #ifdef CONFIG_PM_SLEEP |
499 | |
500 | static int s3c_rtc_suspend(struct device *dev) |
501 | { |
502 | struct s3c_rtc *info = dev_get_drvdata(dev); |
503 | int ret; |
504 | |
505 | ret = s3c_rtc_enable_clk(info); |
506 | if (ret) |
507 | return ret; |
508 | |
509 | if (info->data->disable) |
510 | info->data->disable(info); |
511 | |
512 | if (device_may_wakeup(dev) && !info->wake_en) { |
513 | if (enable_irq_wake(irq: info->irq_alarm) == 0) |
514 | info->wake_en = true; |
515 | else |
516 | dev_err(dev, "enable_irq_wake failed\n" ); |
517 | } |
518 | |
519 | return 0; |
520 | } |
521 | |
522 | static int s3c_rtc_resume(struct device *dev) |
523 | { |
524 | struct s3c_rtc *info = dev_get_drvdata(dev); |
525 | |
526 | if (info->data->enable) |
527 | info->data->enable(info); |
528 | |
529 | s3c_rtc_disable_clk(info); |
530 | |
531 | if (device_may_wakeup(dev) && info->wake_en) { |
532 | disable_irq_wake(irq: info->irq_alarm); |
533 | info->wake_en = false; |
534 | } |
535 | |
536 | return 0; |
537 | } |
538 | #endif |
539 | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); |
540 | |
541 | static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) |
542 | { |
543 | rtc_update_irq(rtc: info->rtc, num: 1, RTC_AF | RTC_IRQF); |
544 | } |
545 | |
546 | static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) |
547 | { |
548 | rtc_update_irq(rtc: info->rtc, num: 1, RTC_AF | RTC_IRQF); |
549 | writeb(val: mask, addr: info->base + S3C2410_INTP); |
550 | } |
551 | |
552 | static struct s3c_rtc_data const s3c2410_rtc_data = { |
553 | .irq_handler = s3c24xx_rtc_irq, |
554 | .enable = s3c24xx_rtc_enable, |
555 | .disable = s3c24xx_rtc_disable, |
556 | }; |
557 | |
558 | static struct s3c_rtc_data const s3c2416_rtc_data = { |
559 | .irq_handler = s3c24xx_rtc_irq, |
560 | .enable = s3c24xx_rtc_enable, |
561 | .disable = s3c24xx_rtc_disable, |
562 | }; |
563 | |
564 | static struct s3c_rtc_data const s3c2443_rtc_data = { |
565 | .irq_handler = s3c24xx_rtc_irq, |
566 | .enable = s3c24xx_rtc_enable, |
567 | .disable = s3c24xx_rtc_disable, |
568 | }; |
569 | |
570 | static struct s3c_rtc_data const s3c6410_rtc_data = { |
571 | .needs_src_clk = true, |
572 | .irq_handler = s3c6410_rtc_irq, |
573 | .enable = s3c24xx_rtc_enable, |
574 | .disable = s3c6410_rtc_disable, |
575 | }; |
576 | |
577 | static const __maybe_unused struct of_device_id s3c_rtc_dt_match[] = { |
578 | { |
579 | .compatible = "samsung,s3c2410-rtc" , |
580 | .data = &s3c2410_rtc_data, |
581 | }, { |
582 | .compatible = "samsung,s3c2416-rtc" , |
583 | .data = &s3c2416_rtc_data, |
584 | }, { |
585 | .compatible = "samsung,s3c2443-rtc" , |
586 | .data = &s3c2443_rtc_data, |
587 | }, { |
588 | .compatible = "samsung,s3c6410-rtc" , |
589 | .data = &s3c6410_rtc_data, |
590 | }, { |
591 | .compatible = "samsung,exynos3250-rtc" , |
592 | .data = &s3c6410_rtc_data, |
593 | }, |
594 | { /* sentinel */ }, |
595 | }; |
596 | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); |
597 | |
598 | static struct platform_driver s3c_rtc_driver = { |
599 | .probe = s3c_rtc_probe, |
600 | .remove_new = s3c_rtc_remove, |
601 | .driver = { |
602 | .name = "s3c-rtc" , |
603 | .pm = &s3c_rtc_pm_ops, |
604 | .of_match_table = of_match_ptr(s3c_rtc_dt_match), |
605 | }, |
606 | }; |
607 | module_platform_driver(s3c_rtc_driver); |
608 | |
609 | MODULE_DESCRIPTION("Samsung S3C RTC Driver" ); |
610 | MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>" ); |
611 | MODULE_LICENSE("GPL" ); |
612 | MODULE_ALIAS("platform:s3c2410-rtc" ); |
613 | |