diff options
author | Chanwoo Choi <cw00.choi@samsung.com> | 2014-10-13 18:52:33 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-13 20:18:17 -0400 |
commit | ae05c95074e0ead8a8fda4aca066e10270086e3f (patch) | |
tree | f6596bb217bd96544c67108122c6b9b495d774b0 /drivers | |
parent | d67288da51b782f54dd3ae1455b997131160fd41 (diff) |
rtc: s3c: add s3c_rtc_data structure to use variant data instead of s3c_cpu_type
Add s3c_rtc_data structure to variant data according to SoC type. The
s3c_rtc_data structure includes some functions to control RTC operation
and specific data dependent on SoC type.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Cc: Alessandro Zummo <a.zummo@towertech.it>
Cc: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 461 |
1 files changed, 289 insertions, 172 deletions
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 4e95cca7615c..0d9089228bb0 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c | |||
@@ -32,17 +32,6 @@ | |||
32 | #include <asm/irq.h> | 32 | #include <asm/irq.h> |
33 | #include "rtc-s3c.h" | 33 | #include "rtc-s3c.h" |
34 | 34 | ||
35 | enum s3c_cpu_type { | ||
36 | TYPE_S3C2410, | ||
37 | TYPE_S3C2416, | ||
38 | TYPE_S3C2443, | ||
39 | TYPE_S3C64XX, | ||
40 | }; | ||
41 | |||
42 | struct s3c_rtc_drv_data { | ||
43 | int cpu_type; | ||
44 | }; | ||
45 | |||
46 | struct s3c_rtc { | 35 | struct s3c_rtc { |
47 | struct device *dev; | 36 | struct device *dev; |
48 | struct rtc_device *rtc; | 37 | struct rtc_device *rtc; |
@@ -51,7 +40,7 @@ struct s3c_rtc { | |||
51 | struct clk *rtc_clk; | 40 | struct clk *rtc_clk; |
52 | bool enabled; | 41 | bool enabled; |
53 | 42 | ||
54 | enum s3c_cpu_type cpu_type; | 43 | struct s3c_rtc_data *data; |
55 | 44 | ||
56 | int irq_alarm; | 45 | int irq_alarm; |
57 | int irq_tick; | 46 | int irq_tick; |
@@ -63,6 +52,19 @@ struct s3c_rtc { | |||
63 | bool wake_en; | 52 | bool wake_en; |
64 | }; | 53 | }; |
65 | 54 | ||
55 | struct s3c_rtc_data { | ||
56 | int max_user_freq; | ||
57 | |||
58 | void (*irq_handler) (struct s3c_rtc *info, int mask); | ||
59 | void (*set_freq) (struct s3c_rtc *info, int freq); | ||
60 | void (*enable_tick) (struct s3c_rtc *info, struct seq_file *seq); | ||
61 | void (*select_tick_clk) (struct s3c_rtc *info); | ||
62 | void (*save_tick_cnt) (struct s3c_rtc *info); | ||
63 | void (*restore_tick_cnt) (struct s3c_rtc *info); | ||
64 | void (*enable) (struct s3c_rtc *info); | ||
65 | void (*disable) (struct s3c_rtc *info); | ||
66 | }; | ||
67 | |||
66 | static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) | 68 | static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) |
67 | { | 69 | { |
68 | unsigned long irq_flags; | 70 | unsigned long irq_flags; |
@@ -83,34 +85,22 @@ static void s3c_rtc_alarm_clk_enable(struct s3c_rtc *info, bool enable) | |||
83 | } | 85 | } |
84 | 86 | ||
85 | /* IRQ Handlers */ | 87 | /* IRQ Handlers */ |
86 | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) | 88 | static irqreturn_t s3c_rtc_tickirq(int irq, void *id) |
87 | { | 89 | { |
88 | struct s3c_rtc *info = (struct s3c_rtc *)id; | 90 | struct s3c_rtc *info = (struct s3c_rtc *)id; |
89 | 91 | ||
90 | clk_enable(info->rtc_clk); | 92 | if (info->data->irq_handler) |
91 | rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); | 93 | info->data->irq_handler(info, S3C2410_INTP_TIC); |
92 | |||
93 | if (info->cpu_type == TYPE_S3C64XX) | ||
94 | writeb(S3C2410_INTP_ALM, info->base + S3C2410_INTP); | ||
95 | |||
96 | clk_disable(info->rtc_clk); | ||
97 | |||
98 | s3c_rtc_alarm_clk_enable(info, false); | ||
99 | 94 | ||
100 | return IRQ_HANDLED; | 95 | return IRQ_HANDLED; |
101 | } | 96 | } |
102 | 97 | ||
103 | static irqreturn_t s3c_rtc_tickirq(int irq, void *id) | 98 | static irqreturn_t s3c_rtc_alarmirq(int irq, void *id) |
104 | { | 99 | { |
105 | struct s3c_rtc *info = (struct s3c_rtc *)id; | 100 | struct s3c_rtc *info = (struct s3c_rtc *)id; |
106 | 101 | ||
107 | clk_enable(info->rtc_clk); | 102 | if (info->data->irq_handler) |
108 | rtc_update_irq(info->rtc, 1, RTC_PF | RTC_IRQF); | 103 | info->data->irq_handler(info, S3C2410_INTP_ALM); |
109 | |||
110 | if (info->cpu_type == TYPE_S3C64XX) | ||
111 | writeb(S3C2410_INTP_TIC, info->base + S3C2410_INTP); | ||
112 | |||
113 | clk_disable(info->rtc_clk); | ||
114 | 104 | ||
115 | return IRQ_HANDLED; | 105 | return IRQ_HANDLED; |
116 | } | 106 | } |
@@ -137,36 +127,18 @@ static int s3c_rtc_setaie(struct device *dev, unsigned int enabled) | |||
137 | return 0; | 127 | return 0; |
138 | } | 128 | } |
139 | 129 | ||
130 | /* Set RTC frequency */ | ||
140 | static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | 131 | static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) |
141 | { | 132 | { |
142 | unsigned int tmp = 0; | ||
143 | int val; | ||
144 | |||
145 | if (!is_power_of_2(freq)) | 133 | if (!is_power_of_2(freq)) |
146 | return -EINVAL; | 134 | return -EINVAL; |
147 | 135 | ||
148 | clk_enable(info->rtc_clk); | 136 | clk_enable(info->rtc_clk); |
149 | spin_lock_irq(&info->pie_lock); | 137 | spin_lock_irq(&info->pie_lock); |
150 | 138 | ||
151 | if (info->cpu_type != TYPE_S3C64XX) { | 139 | if (info->data->set_freq) |
152 | tmp = readb(info->base + S3C2410_TICNT); | 140 | info->data->set_freq(info, freq); |
153 | tmp &= S3C2410_TICNT_ENABLE; | ||
154 | } | ||
155 | |||
156 | val = (info->rtc->max_user_freq / freq) - 1; | ||
157 | |||
158 | if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { | ||
159 | tmp |= S3C2443_TICNT_PART(val); | ||
160 | writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||
161 | 141 | ||
162 | if (info->cpu_type == TYPE_S3C2416) | ||
163 | writel(S3C2416_TICNT2_PART(val), | ||
164 | info->base + S3C2416_TICNT2); | ||
165 | } else { | ||
166 | tmp |= val; | ||
167 | } | ||
168 | |||
169 | writel(tmp, info->base + S3C2410_TICNT); | ||
170 | spin_unlock_irq(&info->pie_lock); | 142 | spin_unlock_irq(&info->pie_lock); |
171 | clk_disable(info->rtc_clk); | 143 | clk_disable(info->rtc_clk); |
172 | 144 | ||
@@ -174,7 +146,6 @@ static int s3c_rtc_setfreq(struct s3c_rtc *info, int freq) | |||
174 | } | 146 | } |
175 | 147 | ||
176 | /* Time read/write */ | 148 | /* Time read/write */ |
177 | |||
178 | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) | 149 | static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) |
179 | { | 150 | { |
180 | struct s3c_rtc *info = dev_get_drvdata(dev); | 151 | struct s3c_rtc *info = dev_get_drvdata(dev); |
@@ -355,19 +326,14 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | |||
355 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) | 326 | static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) |
356 | { | 327 | { |
357 | struct s3c_rtc *info = dev_get_drvdata(dev); | 328 | struct s3c_rtc *info = dev_get_drvdata(dev); |
358 | unsigned int ticnt; | ||
359 | 329 | ||
360 | clk_enable(info->rtc_clk); | 330 | clk_enable(info->rtc_clk); |
361 | if (info->cpu_type == TYPE_S3C64XX) { | ||
362 | ticnt = readw(info->base + S3C2410_RTCCON); | ||
363 | ticnt &= S3C64XX_RTCCON_TICEN; | ||
364 | } else { | ||
365 | ticnt = readb(info->base + S3C2410_TICNT); | ||
366 | ticnt &= S3C2410_TICNT_ENABLE; | ||
367 | } | ||
368 | 331 | ||
369 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | 332 | if (info->data->enable_tick) |
333 | info->data->enable_tick(info, seq); | ||
334 | |||
370 | clk_disable(info->rtc_clk); | 335 | clk_disable(info->rtc_clk); |
336 | |||
371 | return 0; | 337 | return 0; |
372 | } | 338 | } |
373 | 339 | ||
@@ -380,50 +346,69 @@ static const struct rtc_class_ops s3c_rtcops = { | |||
380 | .alarm_irq_enable = s3c_rtc_setaie, | 346 | .alarm_irq_enable = s3c_rtc_setaie, |
381 | }; | 347 | }; |
382 | 348 | ||
383 | static void s3c_rtc_enable(struct s3c_rtc *info, int en) | 349 | static void s3c24xx_rtc_enable(struct s3c_rtc *info) |
384 | { | 350 | { |
385 | unsigned int con, tmp; | 351 | unsigned int con, tmp; |
386 | 352 | ||
387 | clk_enable(info->rtc_clk); | 353 | clk_enable(info->rtc_clk); |
388 | 354 | ||
389 | con = readw(info->base + S3C2410_RTCCON); | 355 | con = readw(info->base + S3C2410_RTCCON); |
390 | if (!en) { | 356 | /* re-enable the device, and check it is ok */ |
391 | if (info->cpu_type == TYPE_S3C64XX) | 357 | if ((con & S3C2410_RTCCON_RTCEN) == 0) { |
392 | con &= ~S3C64XX_RTCCON_TICEN; | 358 | dev_info(info->dev, "rtc disabled, re-enabling\n"); |
393 | con &= ~S3C2410_RTCCON_RTCEN; | ||
394 | writew(con, info->base + S3C2410_RTCCON); | ||
395 | |||
396 | if (info->cpu_type != TYPE_S3C64XX) { | ||
397 | con = readb(info->base + S3C2410_TICNT); | ||
398 | con &= ~S3C2410_TICNT_ENABLE; | ||
399 | writeb(con, info->base + S3C2410_TICNT); | ||
400 | } | ||
401 | } else { | ||
402 | /* re-enable the device, and check it is ok */ | ||
403 | if ((con & S3C2410_RTCCON_RTCEN) == 0) { | ||
404 | dev_info(info->dev, "rtc disabled, re-enabling\n"); | ||
405 | 359 | ||
406 | tmp = readw(info->base + S3C2410_RTCCON); | 360 | tmp = readw(info->base + S3C2410_RTCCON); |
407 | writew(tmp | S3C2410_RTCCON_RTCEN, | 361 | writew(tmp | S3C2410_RTCCON_RTCEN, |
408 | info->base + S3C2410_RTCCON); | 362 | info->base + S3C2410_RTCCON); |
409 | } | 363 | } |
410 | 364 | ||
411 | if (con & S3C2410_RTCCON_CNTSEL) { | 365 | if (con & S3C2410_RTCCON_CNTSEL) { |
412 | dev_info(info->dev, "removing RTCCON_CNTSEL\n"); | 366 | dev_info(info->dev, "removing RTCCON_CNTSEL\n"); |
413 | 367 | ||
414 | tmp = readw(info->base + S3C2410_RTCCON); | 368 | tmp = readw(info->base + S3C2410_RTCCON); |
415 | writew(tmp & ~S3C2410_RTCCON_CNTSEL, | 369 | writew(tmp & ~S3C2410_RTCCON_CNTSEL, |
416 | info->base + S3C2410_RTCCON); | 370 | info->base + S3C2410_RTCCON); |
417 | } | 371 | } |
418 | 372 | ||
419 | if (con & S3C2410_RTCCON_CLKRST) { | 373 | if (con & S3C2410_RTCCON_CLKRST) { |
420 | dev_info(info->dev, "removing RTCCON_CLKRST\n"); | 374 | dev_info(info->dev, "removing RTCCON_CLKRST\n"); |
421 | 375 | ||
422 | tmp = readw(info->base + S3C2410_RTCCON); | 376 | tmp = readw(info->base + S3C2410_RTCCON); |
423 | writew(tmp & ~S3C2410_RTCCON_CLKRST, | 377 | writew(tmp & ~S3C2410_RTCCON_CLKRST, |
424 | info->base + S3C2410_RTCCON); | 378 | info->base + S3C2410_RTCCON); |
425 | } | ||
426 | } | 379 | } |
380 | |||
381 | clk_disable(info->rtc_clk); | ||
382 | } | ||
383 | |||
384 | static void s3c24xx_rtc_disable(struct s3c_rtc *info) | ||
385 | { | ||
386 | unsigned int con; | ||
387 | |||
388 | clk_enable(info->rtc_clk); | ||
389 | |||
390 | con = readw(info->base + S3C2410_RTCCON); | ||
391 | con &= ~S3C2410_RTCCON_RTCEN; | ||
392 | writew(con, info->base + S3C2410_RTCCON); | ||
393 | |||
394 | con = readb(info->base + S3C2410_TICNT); | ||
395 | con &= ~S3C2410_TICNT_ENABLE; | ||
396 | writeb(con, info->base + S3C2410_TICNT); | ||
397 | |||
398 | clk_disable(info->rtc_clk); | ||
399 | } | ||
400 | |||
401 | static void s3c6410_rtc_disable(struct s3c_rtc *info) | ||
402 | { | ||
403 | unsigned int con; | ||
404 | |||
405 | clk_enable(info->rtc_clk); | ||
406 | |||
407 | con = readw(info->base + S3C2410_RTCCON); | ||
408 | con &= ~S3C64XX_RTCCON_TICEN; | ||
409 | con &= ~S3C2410_RTCCON_RTCEN; | ||
410 | writew(con, info->base + S3C2410_RTCCON); | ||
411 | |||
427 | clk_disable(info->rtc_clk); | 412 | clk_disable(info->rtc_clk); |
428 | } | 413 | } |
429 | 414 | ||
@@ -441,20 +426,12 @@ static int s3c_rtc_remove(struct platform_device *pdev) | |||
441 | 426 | ||
442 | static const struct of_device_id s3c_rtc_dt_match[]; | 427 | static const struct of_device_id s3c_rtc_dt_match[]; |
443 | 428 | ||
444 | static inline int s3c_rtc_get_driver_data(struct platform_device *pdev) | 429 | static struct s3c_rtc_data *s3c_rtc_get_data(struct platform_device *pdev) |
445 | { | 430 | { |
446 | #ifdef CONFIG_OF | 431 | const struct of_device_id *match; |
447 | struct s3c_rtc_drv_data *data; | ||
448 | 432 | ||
449 | if (pdev->dev.of_node) { | 433 | match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); |
450 | const struct of_device_id *match; | 434 | return (struct s3c_rtc_data *)match->data; |
451 | |||
452 | match = of_match_node(s3c_rtc_dt_match, pdev->dev.of_node); | ||
453 | data = (struct s3c_rtc_drv_data *) match->data; | ||
454 | return data->cpu_type; | ||
455 | } | ||
456 | #endif | ||
457 | return platform_get_device_id(pdev)->driver_data; | ||
458 | } | 435 | } |
459 | 436 | ||
460 | static int s3c_rtc_probe(struct platform_device *pdev) | 437 | static int s3c_rtc_probe(struct platform_device *pdev) |
@@ -463,7 +440,6 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
463 | struct rtc_time rtc_tm; | 440 | struct rtc_time rtc_tm; |
464 | struct resource *res; | 441 | struct resource *res; |
465 | int ret; | 442 | int ret; |
466 | int tmp; | ||
467 | 443 | ||
468 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); | 444 | info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); |
469 | if (!info) | 445 | if (!info) |
@@ -477,7 +453,11 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
477 | } | 453 | } |
478 | 454 | ||
479 | info->dev = &pdev->dev; | 455 | info->dev = &pdev->dev; |
480 | info->cpu_type = s3c_rtc_get_driver_data(pdev); | 456 | info->data = s3c_rtc_get_data(pdev); |
457 | if (!info->data) { | ||
458 | dev_err(&pdev->dev, "failed getting s3c_rtc_data\n"); | ||
459 | return -EINVAL; | ||
460 | } | ||
481 | spin_lock_init(&info->pie_lock); | 461 | spin_lock_init(&info->pie_lock); |
482 | spin_lock_init(&info->alarm_clk_lock); | 462 | spin_lock_init(&info->alarm_clk_lock); |
483 | 463 | ||
@@ -506,7 +486,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
506 | clk_prepare_enable(info->rtc_clk); | 486 | clk_prepare_enable(info->rtc_clk); |
507 | 487 | ||
508 | /* check to see if everything is setup correctly */ | 488 | /* check to see if everything is setup correctly */ |
509 | s3c_rtc_enable(info, 1); | 489 | if (info->data->enable) |
490 | info->data->enable(info); | ||
510 | 491 | ||
511 | dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", | 492 | dev_dbg(&pdev->dev, "s3c2410_rtc: RTCCON=%02x\n", |
512 | readw(info->base + S3C2410_RTCCON)); | 493 | readw(info->base + S3C2410_RTCCON)); |
@@ -552,16 +533,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
552 | dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); | 533 | dev_warn(&pdev->dev, "warning: invalid RTC value so initializing it\n"); |
553 | } | 534 | } |
554 | 535 | ||
555 | if (info->cpu_type != TYPE_S3C2410) | 536 | if (info->data->select_tick_clk) |
556 | info->rtc->max_user_freq = 32768; | 537 | info->data->select_tick_clk(info); |
557 | else | ||
558 | info->rtc->max_user_freq = 128; | ||
559 | |||
560 | if (info->cpu_type == TYPE_S3C2416 || info->cpu_type == TYPE_S3C2443) { | ||
561 | tmp = readw(info->base + S3C2410_RTCCON); | ||
562 | tmp |= S3C2443_RTCCON_TICSEL; | ||
563 | writew(tmp, info->base + S3C2410_RTCCON); | ||
564 | } | ||
565 | 538 | ||
566 | s3c_rtc_setfreq(info, 1); | 539 | s3c_rtc_setfreq(info, 1); |
567 | 540 | ||
@@ -570,7 +543,8 @@ static int s3c_rtc_probe(struct platform_device *pdev) | |||
570 | return 0; | 543 | return 0; |
571 | 544 | ||
572 | err_nortc: | 545 | err_nortc: |
573 | s3c_rtc_enable(info, 0); | 546 | if (info->data->disable) |
547 | info->data->disable(info); | ||
574 | clk_disable_unprepare(info->rtc_clk); | 548 | clk_disable_unprepare(info->rtc_clk); |
575 | 549 | ||
576 | return ret; | 550 | return ret; |
@@ -583,15 +557,13 @@ static int s3c_rtc_suspend(struct device *dev) | |||
583 | struct s3c_rtc *info = dev_get_drvdata(dev); | 557 | struct s3c_rtc *info = dev_get_drvdata(dev); |
584 | 558 | ||
585 | clk_enable(info->rtc_clk); | 559 | clk_enable(info->rtc_clk); |
560 | |||
586 | /* save TICNT for anyone using periodic interrupts */ | 561 | /* save TICNT for anyone using periodic interrupts */ |
587 | if (info->cpu_type == TYPE_S3C64XX) { | 562 | if (info->data->save_tick_cnt) |
588 | info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); | 563 | info->data->save_tick_cnt(info); |
589 | info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; | 564 | |
590 | info->ticnt_save = readl(info->base + S3C2410_TICNT); | 565 | if (info->data->disable) |
591 | } else { | 566 | info->data->disable(info); |
592 | info->ticnt_save = readb(info->base + S3C2410_TICNT); | ||
593 | } | ||
594 | s3c_rtc_enable(info, 0); | ||
595 | 567 | ||
596 | if (device_may_wakeup(dev) && !info->wake_en) { | 568 | if (device_may_wakeup(dev) && !info->wake_en) { |
597 | if (enable_irq_wake(info->irq_alarm) == 0) | 569 | if (enable_irq_wake(info->irq_alarm) == 0) |
@@ -599,6 +571,7 @@ static int s3c_rtc_suspend(struct device *dev) | |||
599 | else | 571 | else |
600 | dev_err(dev, "enable_irq_wake failed\n"); | 572 | dev_err(dev, "enable_irq_wake failed\n"); |
601 | } | 573 | } |
574 | |||
602 | clk_disable(info->rtc_clk); | 575 | clk_disable(info->rtc_clk); |
603 | 576 | ||
604 | return 0; | 577 | return 0; |
@@ -607,25 +580,20 @@ static int s3c_rtc_suspend(struct device *dev) | |||
607 | static int s3c_rtc_resume(struct device *dev) | 580 | static int s3c_rtc_resume(struct device *dev) |
608 | { | 581 | { |
609 | struct s3c_rtc *info = dev_get_drvdata(dev); | 582 | struct s3c_rtc *info = dev_get_drvdata(dev); |
610 | unsigned int tmp; | ||
611 | 583 | ||
612 | clk_enable(info->rtc_clk); | 584 | clk_enable(info->rtc_clk); |
613 | s3c_rtc_enable(info, 1); | 585 | |
614 | if (info->cpu_type == TYPE_S3C64XX) { | 586 | if (info->data->enable) |
615 | writel(info->ticnt_save, info->base + S3C2410_TICNT); | 587 | info->data->enable(info); |
616 | if (info->ticnt_en_save) { | 588 | |
617 | tmp = readw(info->base + S3C2410_RTCCON); | 589 | if (info->data->restore_tick_cnt) |
618 | writew(tmp | info->ticnt_en_save, | 590 | info->data->restore_tick_cnt(info); |
619 | info->base + S3C2410_RTCCON); | ||
620 | } | ||
621 | } else { | ||
622 | writeb(info->ticnt_save, info->base + S3C2410_TICNT); | ||
623 | } | ||
624 | 591 | ||
625 | if (device_may_wakeup(dev) && info->wake_en) { | 592 | if (device_may_wakeup(dev) && info->wake_en) { |
626 | disable_irq_wake(info->irq_alarm); | 593 | disable_irq_wake(info->irq_alarm); |
627 | info->wake_en = false; | 594 | info->wake_en = false; |
628 | } | 595 | } |
596 | |||
629 | clk_disable(info->rtc_clk); | 597 | clk_disable(info->rtc_clk); |
630 | 598 | ||
631 | return 0; | 599 | return 0; |
@@ -633,56 +601,206 @@ static int s3c_rtc_resume(struct device *dev) | |||
633 | #endif | 601 | #endif |
634 | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); | 602 | static SIMPLE_DEV_PM_OPS(s3c_rtc_pm_ops, s3c_rtc_suspend, s3c_rtc_resume); |
635 | 603 | ||
636 | #ifdef CONFIG_OF | 604 | static void s3c24xx_rtc_irq(struct s3c_rtc *info, int mask) |
637 | static struct s3c_rtc_drv_data s3c_rtc_drv_data_array[] = { | 605 | { |
638 | [TYPE_S3C2410] = { TYPE_S3C2410 }, | 606 | clk_enable(info->rtc_clk); |
639 | [TYPE_S3C2416] = { TYPE_S3C2416 }, | 607 | rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); |
640 | [TYPE_S3C2443] = { TYPE_S3C2443 }, | 608 | clk_disable(info->rtc_clk); |
641 | [TYPE_S3C64XX] = { TYPE_S3C64XX }, | 609 | |
610 | s3c_rtc_alarm_clk_enable(info, false); | ||
611 | } | ||
612 | |||
613 | static void s3c6410_rtc_irq(struct s3c_rtc *info, int mask) | ||
614 | { | ||
615 | clk_enable(info->rtc_clk); | ||
616 | rtc_update_irq(info->rtc, 1, RTC_AF | RTC_IRQF); | ||
617 | writeb(mask, info->base + S3C2410_INTP); | ||
618 | clk_disable(info->rtc_clk); | ||
619 | |||
620 | s3c_rtc_alarm_clk_enable(info, false); | ||
621 | } | ||
622 | |||
623 | static void s3c2410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
624 | { | ||
625 | unsigned int tmp = 0; | ||
626 | int val; | ||
627 | |||
628 | tmp = readb(info->base + S3C2410_TICNT); | ||
629 | tmp &= S3C2410_TICNT_ENABLE; | ||
630 | |||
631 | val = (info->rtc->max_user_freq / freq) - 1; | ||
632 | tmp |= val; | ||
633 | |||
634 | writel(tmp, info->base + S3C2410_TICNT); | ||
635 | } | ||
636 | |||
637 | static void s3c2416_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
638 | { | ||
639 | unsigned int tmp = 0; | ||
640 | int val; | ||
641 | |||
642 | tmp = readb(info->base + S3C2410_TICNT); | ||
643 | tmp &= S3C2410_TICNT_ENABLE; | ||
644 | |||
645 | val = (info->rtc->max_user_freq / freq) - 1; | ||
646 | |||
647 | tmp |= S3C2443_TICNT_PART(val); | ||
648 | writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||
649 | |||
650 | writel(S3C2416_TICNT2_PART(val), info->base + S3C2416_TICNT2); | ||
651 | |||
652 | writel(tmp, info->base + S3C2410_TICNT); | ||
653 | } | ||
654 | |||
655 | static void s3c2443_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
656 | { | ||
657 | unsigned int tmp = 0; | ||
658 | int val; | ||
659 | |||
660 | tmp = readb(info->base + S3C2410_TICNT); | ||
661 | tmp &= S3C2410_TICNT_ENABLE; | ||
662 | |||
663 | val = (info->rtc->max_user_freq / freq) - 1; | ||
664 | |||
665 | tmp |= S3C2443_TICNT_PART(val); | ||
666 | writel(S3C2443_TICNT1_PART(val), info->base + S3C2443_TICNT1); | ||
667 | |||
668 | writel(tmp, info->base + S3C2410_TICNT); | ||
669 | } | ||
670 | |||
671 | static void s3c6410_rtc_setfreq(struct s3c_rtc *info, int freq) | ||
672 | { | ||
673 | int val; | ||
674 | |||
675 | val = (info->rtc->max_user_freq / freq) - 1; | ||
676 | writel(val, info->base + S3C2410_TICNT); | ||
677 | } | ||
678 | |||
679 | static void s3c24xx_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||
680 | { | ||
681 | unsigned int ticnt; | ||
682 | |||
683 | ticnt = readb(info->base + S3C2410_TICNT); | ||
684 | ticnt &= S3C2410_TICNT_ENABLE; | ||
685 | |||
686 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
687 | } | ||
688 | |||
689 | static void s3c2416_rtc_select_tick_clk(struct s3c_rtc *info) | ||
690 | { | ||
691 | unsigned int con; | ||
692 | |||
693 | con = readw(info->base + S3C2410_RTCCON); | ||
694 | con |= S3C2443_RTCCON_TICSEL; | ||
695 | writew(con, info->base + S3C2410_RTCCON); | ||
696 | } | ||
697 | |||
698 | static void s3c6410_rtc_enable_tick(struct s3c_rtc *info, struct seq_file *seq) | ||
699 | { | ||
700 | unsigned int ticnt; | ||
701 | |||
702 | ticnt = readw(info->base + S3C2410_RTCCON); | ||
703 | ticnt &= S3C64XX_RTCCON_TICEN; | ||
704 | |||
705 | seq_printf(seq, "periodic_IRQ\t: %s\n", ticnt ? "yes" : "no"); | ||
706 | } | ||
707 | |||
708 | static void s3c24xx_rtc_save_tick_cnt(struct s3c_rtc *info) | ||
709 | { | ||
710 | info->ticnt_save = readb(info->base + S3C2410_TICNT); | ||
711 | } | ||
712 | |||
713 | static void s3c24xx_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||
714 | { | ||
715 | writeb(info->ticnt_save, info->base + S3C2410_TICNT); | ||
716 | } | ||
717 | |||
718 | static void s3c6410_rtc_save_tick_cnt(struct s3c_rtc *info) | ||
719 | { | ||
720 | info->ticnt_en_save = readw(info->base + S3C2410_RTCCON); | ||
721 | info->ticnt_en_save &= S3C64XX_RTCCON_TICEN; | ||
722 | info->ticnt_save = readl(info->base + S3C2410_TICNT); | ||
723 | } | ||
724 | |||
725 | static void s3c6410_rtc_restore_tick_cnt(struct s3c_rtc *info) | ||
726 | { | ||
727 | unsigned int con; | ||
728 | |||
729 | writel(info->ticnt_save, info->base + S3C2410_TICNT); | ||
730 | if (info->ticnt_en_save) { | ||
731 | con = readw(info->base + S3C2410_RTCCON); | ||
732 | writew(con | info->ticnt_en_save, | ||
733 | info->base + S3C2410_RTCCON); | ||
734 | } | ||
735 | } | ||
736 | |||
737 | static struct s3c_rtc_data const s3c2410_rtc_data = { | ||
738 | .max_user_freq = 128, | ||
739 | .irq_handler = s3c24xx_rtc_irq, | ||
740 | .set_freq = s3c2410_rtc_setfreq, | ||
741 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
742 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
743 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
744 | .enable = s3c24xx_rtc_enable, | ||
745 | .disable = s3c24xx_rtc_disable, | ||
746 | }; | ||
747 | |||
748 | static struct s3c_rtc_data const s3c2416_rtc_data = { | ||
749 | .max_user_freq = 32768, | ||
750 | .irq_handler = s3c24xx_rtc_irq, | ||
751 | .set_freq = s3c2416_rtc_setfreq, | ||
752 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
753 | .select_tick_clk = s3c2416_rtc_select_tick_clk, | ||
754 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
755 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
756 | .enable = s3c24xx_rtc_enable, | ||
757 | .disable = s3c24xx_rtc_disable, | ||
758 | }; | ||
759 | |||
760 | static struct s3c_rtc_data const s3c2443_rtc_data = { | ||
761 | .max_user_freq = 32768, | ||
762 | .irq_handler = s3c24xx_rtc_irq, | ||
763 | .set_freq = s3c2443_rtc_setfreq, | ||
764 | .enable_tick = s3c24xx_rtc_enable_tick, | ||
765 | .select_tick_clk = s3c2416_rtc_select_tick_clk, | ||
766 | .save_tick_cnt = s3c24xx_rtc_save_tick_cnt, | ||
767 | .restore_tick_cnt = s3c24xx_rtc_restore_tick_cnt, | ||
768 | .enable = s3c24xx_rtc_enable, | ||
769 | .disable = s3c24xx_rtc_disable, | ||
770 | }; | ||
771 | |||
772 | static struct s3c_rtc_data const s3c6410_rtc_data = { | ||
773 | .max_user_freq = 32768, | ||
774 | .irq_handler = s3c6410_rtc_irq, | ||
775 | .set_freq = s3c6410_rtc_setfreq, | ||
776 | .enable_tick = s3c6410_rtc_enable_tick, | ||
777 | .save_tick_cnt = s3c6410_rtc_save_tick_cnt, | ||
778 | .restore_tick_cnt = s3c6410_rtc_restore_tick_cnt, | ||
779 | .enable = s3c24xx_rtc_enable, | ||
780 | .disable = s3c6410_rtc_disable, | ||
642 | }; | 781 | }; |
643 | 782 | ||
644 | static const struct of_device_id s3c_rtc_dt_match[] = { | 783 | static const struct of_device_id s3c_rtc_dt_match[] = { |
645 | { | 784 | { |
646 | .compatible = "samsung,s3c2410-rtc", | 785 | .compatible = "samsung,s3c2410-rtc", |
647 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2410], | 786 | .data = (void *)&s3c2410_rtc_data, |
648 | }, { | 787 | }, { |
649 | .compatible = "samsung,s3c2416-rtc", | 788 | .compatible = "samsung,s3c2416-rtc", |
650 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2416], | 789 | .data = (void *)&s3c2416_rtc_data, |
651 | }, { | 790 | }, { |
652 | .compatible = "samsung,s3c2443-rtc", | 791 | .compatible = "samsung,s3c2443-rtc", |
653 | .data = &s3c_rtc_drv_data_array[TYPE_S3C2443], | 792 | .data = (void *)&s3c2443_rtc_data, |
654 | }, { | 793 | }, { |
655 | .compatible = "samsung,s3c6410-rtc", | 794 | .compatible = "samsung,s3c6410-rtc", |
656 | .data = &s3c_rtc_drv_data_array[TYPE_S3C64XX], | 795 | .data = (void *)&s3c6410_rtc_data, |
657 | }, | 796 | }, |
658 | {}, | 797 | { /* sentinel */ }, |
659 | }; | 798 | }; |
660 | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); | 799 | MODULE_DEVICE_TABLE(of, s3c_rtc_dt_match); |
661 | #endif | ||
662 | |||
663 | static struct platform_device_id s3c_rtc_driver_ids[] = { | ||
664 | { | ||
665 | .name = "s3c2410-rtc", | ||
666 | .driver_data = TYPE_S3C2410, | ||
667 | }, { | ||
668 | .name = "s3c2416-rtc", | ||
669 | .driver_data = TYPE_S3C2416, | ||
670 | }, { | ||
671 | .name = "s3c2443-rtc", | ||
672 | .driver_data = TYPE_S3C2443, | ||
673 | }, { | ||
674 | .name = "s3c64xx-rtc", | ||
675 | .driver_data = TYPE_S3C64XX, | ||
676 | }, | ||
677 | { } | ||
678 | }; | ||
679 | |||
680 | MODULE_DEVICE_TABLE(platform, s3c_rtc_driver_ids); | ||
681 | 800 | ||
682 | static struct platform_driver s3c_rtc_driver = { | 801 | static struct platform_driver s3c_rtc_driver = { |
683 | .probe = s3c_rtc_probe, | 802 | .probe = s3c_rtc_probe, |
684 | .remove = s3c_rtc_remove, | 803 | .remove = s3c_rtc_remove, |
685 | .id_table = s3c_rtc_driver_ids, | ||
686 | .driver = { | 804 | .driver = { |
687 | .name = "s3c-rtc", | 805 | .name = "s3c-rtc", |
688 | .owner = THIS_MODULE, | 806 | .owner = THIS_MODULE, |
@@ -690,7 +808,6 @@ static struct platform_driver s3c_rtc_driver = { | |||
690 | .of_match_table = of_match_ptr(s3c_rtc_dt_match), | 808 | .of_match_table = of_match_ptr(s3c_rtc_dt_match), |
691 | }, | 809 | }, |
692 | }; | 810 | }; |
693 | |||
694 | module_platform_driver(s3c_rtc_driver); | 811 | module_platform_driver(s3c_rtc_driver); |
695 | 812 | ||
696 | MODULE_DESCRIPTION("Samsung S3C RTC Driver"); | 813 | MODULE_DESCRIPTION("Samsung S3C RTC Driver"); |