diff options
Diffstat (limited to 'drivers/rtc/rtc-sh.c')
-rw-r--r-- | drivers/rtc/rtc-sh.c | 246 |
1 files changed, 159 insertions, 87 deletions
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 1c3fc6b428e9..4898f7fe8518 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c | |||
@@ -28,7 +28,7 @@ | |||
28 | #include <asm/rtc.h> | 28 | #include <asm/rtc.h> |
29 | 29 | ||
30 | #define DRV_NAME "sh-rtc" | 30 | #define DRV_NAME "sh-rtc" |
31 | #define DRV_VERSION "0.2.0" | 31 | #define DRV_VERSION "0.2.1" |
32 | 32 | ||
33 | #define RTC_REG(r) ((r) * rtc_reg_size) | 33 | #define RTC_REG(r) ((r) * rtc_reg_size) |
34 | 34 | ||
@@ -99,56 +99,51 @@ struct sh_rtc { | |||
99 | unsigned short periodic_freq; | 99 | unsigned short periodic_freq; |
100 | }; | 100 | }; |
101 | 101 | ||
102 | static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) | 102 | static int __sh_rtc_interrupt(struct sh_rtc *rtc) |
103 | { | 103 | { |
104 | struct sh_rtc *rtc = dev_id; | 104 | unsigned int tmp, pending; |
105 | unsigned int tmp; | ||
106 | |||
107 | spin_lock(&rtc->lock); | ||
108 | 105 | ||
109 | tmp = readb(rtc->regbase + RCR1); | 106 | tmp = readb(rtc->regbase + RCR1); |
107 | pending = tmp & RCR1_CF; | ||
110 | tmp &= ~RCR1_CF; | 108 | tmp &= ~RCR1_CF; |
111 | writeb(tmp, rtc->regbase + RCR1); | 109 | writeb(tmp, rtc->regbase + RCR1); |
112 | 110 | ||
113 | /* Users have requested One x Second IRQ */ | 111 | /* Users have requested One x Second IRQ */ |
114 | if (rtc->periodic_freq & PF_OXS) | 112 | if (pending && rtc->periodic_freq & PF_OXS) |
115 | rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); | 113 | rtc_update_irq(rtc->rtc_dev, 1, RTC_UF | RTC_IRQF); |
116 | 114 | ||
117 | spin_unlock(&rtc->lock); | 115 | return pending; |
118 | |||
119 | return IRQ_HANDLED; | ||
120 | } | 116 | } |
121 | 117 | ||
122 | static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) | 118 | static int __sh_rtc_alarm(struct sh_rtc *rtc) |
123 | { | 119 | { |
124 | struct sh_rtc *rtc = dev_id; | 120 | unsigned int tmp, pending; |
125 | unsigned int tmp; | ||
126 | |||
127 | spin_lock(&rtc->lock); | ||
128 | 121 | ||
129 | tmp = readb(rtc->regbase + RCR1); | 122 | tmp = readb(rtc->regbase + RCR1); |
123 | pending = tmp & RCR1_AF; | ||
130 | tmp &= ~(RCR1_AF | RCR1_AIE); | 124 | tmp &= ~(RCR1_AF | RCR1_AIE); |
131 | writeb(tmp, rtc->regbase + RCR1); | 125 | writeb(tmp, rtc->regbase + RCR1); |
132 | |||
133 | rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); | ||
134 | 126 | ||
135 | spin_unlock(&rtc->lock); | 127 | if (pending) |
128 | rtc_update_irq(rtc->rtc_dev, 1, RTC_AF | RTC_IRQF); | ||
136 | 129 | ||
137 | return IRQ_HANDLED; | 130 | return pending; |
138 | } | 131 | } |
139 | 132 | ||
140 | static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) | 133 | static int __sh_rtc_periodic(struct sh_rtc *rtc) |
141 | { | 134 | { |
142 | struct sh_rtc *rtc = dev_id; | ||
143 | struct rtc_device *rtc_dev = rtc->rtc_dev; | 135 | struct rtc_device *rtc_dev = rtc->rtc_dev; |
144 | unsigned int tmp; | 136 | struct rtc_task *irq_task; |
145 | 137 | unsigned int tmp, pending; | |
146 | spin_lock(&rtc->lock); | ||
147 | 138 | ||
148 | tmp = readb(rtc->regbase + RCR2); | 139 | tmp = readb(rtc->regbase + RCR2); |
140 | pending = tmp & RCR2_PEF; | ||
149 | tmp &= ~RCR2_PEF; | 141 | tmp &= ~RCR2_PEF; |
150 | writeb(tmp, rtc->regbase + RCR2); | 142 | writeb(tmp, rtc->regbase + RCR2); |
151 | 143 | ||
144 | if (!pending) | ||
145 | return 0; | ||
146 | |||
152 | /* Half period enabled than one skipped and the next notified */ | 147 | /* Half period enabled than one skipped and the next notified */ |
153 | if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) | 148 | if ((rtc->periodic_freq & PF_HP) && (rtc->periodic_freq & PF_COUNT)) |
154 | rtc->periodic_freq &= ~PF_COUNT; | 149 | rtc->periodic_freq &= ~PF_COUNT; |
@@ -157,16 +152,65 @@ static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) | |||
157 | rtc->periodic_freq |= PF_COUNT; | 152 | rtc->periodic_freq |= PF_COUNT; |
158 | if (rtc->periodic_freq & PF_KOU) { | 153 | if (rtc->periodic_freq & PF_KOU) { |
159 | spin_lock(&rtc_dev->irq_task_lock); | 154 | spin_lock(&rtc_dev->irq_task_lock); |
160 | if (rtc_dev->irq_task) | 155 | irq_task = rtc_dev->irq_task; |
161 | rtc_dev->irq_task->func(rtc_dev->irq_task->private_data); | 156 | if (irq_task) |
157 | irq_task->func(irq_task->private_data); | ||
162 | spin_unlock(&rtc_dev->irq_task_lock); | 158 | spin_unlock(&rtc_dev->irq_task_lock); |
163 | } else | 159 | } else |
164 | rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); | 160 | rtc_update_irq(rtc->rtc_dev, 1, RTC_PF | RTC_IRQF); |
165 | } | 161 | } |
166 | 162 | ||
163 | return pending; | ||
164 | } | ||
165 | |||
166 | static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) | ||
167 | { | ||
168 | struct sh_rtc *rtc = dev_id; | ||
169 | int ret; | ||
170 | |||
171 | spin_lock(&rtc->lock); | ||
172 | ret = __sh_rtc_interrupt(rtc); | ||
173 | spin_unlock(&rtc->lock); | ||
174 | |||
175 | return IRQ_RETVAL(ret); | ||
176 | } | ||
177 | |||
178 | static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) | ||
179 | { | ||
180 | struct sh_rtc *rtc = dev_id; | ||
181 | int ret; | ||
182 | |||
183 | spin_lock(&rtc->lock); | ||
184 | ret = __sh_rtc_alarm(rtc); | ||
185 | spin_unlock(&rtc->lock); | ||
186 | |||
187 | return IRQ_RETVAL(ret); | ||
188 | } | ||
189 | |||
190 | static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) | ||
191 | { | ||
192 | struct sh_rtc *rtc = dev_id; | ||
193 | int ret; | ||
194 | |||
195 | spin_lock(&rtc->lock); | ||
196 | ret = __sh_rtc_periodic(rtc); | ||
167 | spin_unlock(&rtc->lock); | 197 | spin_unlock(&rtc->lock); |
168 | 198 | ||
169 | return IRQ_HANDLED; | 199 | return IRQ_RETVAL(ret); |
200 | } | ||
201 | |||
202 | static irqreturn_t sh_rtc_shared(int irq, void *dev_id) | ||
203 | { | ||
204 | struct sh_rtc *rtc = dev_id; | ||
205 | int ret; | ||
206 | |||
207 | spin_lock(&rtc->lock); | ||
208 | ret = __sh_rtc_interrupt(rtc); | ||
209 | ret |= __sh_rtc_alarm(rtc); | ||
210 | ret |= __sh_rtc_periodic(rtc); | ||
211 | spin_unlock(&rtc->lock); | ||
212 | |||
213 | return IRQ_RETVAL(ret); | ||
170 | } | 214 | } |
171 | 215 | ||
172 | static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) | 216 | static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) |
@@ -275,6 +319,25 @@ static int sh_rtc_proc(struct device *dev, struct seq_file *seq) | |||
275 | return 0; | 319 | return 0; |
276 | } | 320 | } |
277 | 321 | ||
322 | static inline void sh_rtc_setcie(struct device *dev, unsigned int enable) | ||
323 | { | ||
324 | struct sh_rtc *rtc = dev_get_drvdata(dev); | ||
325 | unsigned int tmp; | ||
326 | |||
327 | spin_lock_irq(&rtc->lock); | ||
328 | |||
329 | tmp = readb(rtc->regbase + RCR1); | ||
330 | |||
331 | if (!enable) | ||
332 | tmp &= ~RCR1_CIE; | ||
333 | else | ||
334 | tmp |= RCR1_CIE; | ||
335 | |||
336 | writeb(tmp, rtc->regbase + RCR1); | ||
337 | |||
338 | spin_unlock_irq(&rtc->lock); | ||
339 | } | ||
340 | |||
278 | static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | 341 | static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) |
279 | { | 342 | { |
280 | struct sh_rtc *rtc = dev_get_drvdata(dev); | 343 | struct sh_rtc *rtc = dev_get_drvdata(dev); |
@@ -291,9 +354,11 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) | |||
291 | break; | 354 | break; |
292 | case RTC_UIE_OFF: | 355 | case RTC_UIE_OFF: |
293 | rtc->periodic_freq &= ~PF_OXS; | 356 | rtc->periodic_freq &= ~PF_OXS; |
357 | sh_rtc_setcie(dev, 0); | ||
294 | break; | 358 | break; |
295 | case RTC_UIE_ON: | 359 | case RTC_UIE_ON: |
296 | rtc->periodic_freq |= PF_OXS; | 360 | rtc->periodic_freq |= PF_OXS; |
361 | sh_rtc_setcie(dev, 1); | ||
297 | break; | 362 | break; |
298 | case RTC_IRQP_READ: | 363 | case RTC_IRQP_READ: |
299 | ret = put_user(rtc->rtc_dev->irq_freq, | 364 | ret = put_user(rtc->rtc_dev->irq_freq, |
@@ -356,18 +421,17 @@ static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm) | |||
356 | tm->tm_sec--; | 421 | tm->tm_sec--; |
357 | #endif | 422 | #endif |
358 | 423 | ||
424 | /* only keep the carry interrupt enabled if UIE is on */ | ||
425 | if (!(rtc->periodic_freq & PF_OXS)) | ||
426 | sh_rtc_setcie(dev, 0); | ||
427 | |||
359 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " | 428 | dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " |
360 | "mday=%d, mon=%d, year=%d, wday=%d\n", | 429 | "mday=%d, mon=%d, year=%d, wday=%d\n", |
361 | __func__, | 430 | __func__, |
362 | tm->tm_sec, tm->tm_min, tm->tm_hour, | 431 | tm->tm_sec, tm->tm_min, tm->tm_hour, |
363 | tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); | 432 | tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); |
364 | 433 | ||
365 | if (rtc_valid_tm(tm) < 0) { | 434 | return rtc_valid_tm(tm); |
366 | dev_err(dev, "invalid date\n"); | ||
367 | rtc_time_to_tm(0, tm); | ||
368 | } | ||
369 | |||
370 | return 0; | ||
371 | } | 435 | } |
372 | 436 | ||
373 | static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) | 437 | static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm) |
@@ -572,7 +636,7 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
572 | { | 636 | { |
573 | struct sh_rtc *rtc; | 637 | struct sh_rtc *rtc; |
574 | struct resource *res; | 638 | struct resource *res; |
575 | unsigned int tmp; | 639 | struct rtc_time r; |
576 | int ret; | 640 | int ret; |
577 | 641 | ||
578 | rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); | 642 | rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL); |
@@ -585,26 +649,12 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
585 | ret = platform_get_irq(pdev, 0); | 649 | ret = platform_get_irq(pdev, 0); |
586 | if (unlikely(ret <= 0)) { | 650 | if (unlikely(ret <= 0)) { |
587 | ret = -ENOENT; | 651 | ret = -ENOENT; |
588 | dev_err(&pdev->dev, "No IRQ for period\n"); | 652 | dev_err(&pdev->dev, "No IRQ resource\n"); |
589 | goto err_badres; | 653 | goto err_badres; |
590 | } | 654 | } |
591 | rtc->periodic_irq = ret; | 655 | rtc->periodic_irq = ret; |
592 | 656 | rtc->carry_irq = platform_get_irq(pdev, 1); | |
593 | ret = platform_get_irq(pdev, 1); | 657 | rtc->alarm_irq = platform_get_irq(pdev, 2); |
594 | if (unlikely(ret <= 0)) { | ||
595 | ret = -ENOENT; | ||
596 | dev_err(&pdev->dev, "No IRQ for carry\n"); | ||
597 | goto err_badres; | ||
598 | } | ||
599 | rtc->carry_irq = ret; | ||
600 | |||
601 | ret = platform_get_irq(pdev, 2); | ||
602 | if (unlikely(ret <= 0)) { | ||
603 | ret = -ENOENT; | ||
604 | dev_err(&pdev->dev, "No IRQ for alarm\n"); | ||
605 | goto err_badres; | ||
606 | } | ||
607 | rtc->alarm_irq = ret; | ||
608 | 658 | ||
609 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 659 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
610 | if (unlikely(res == NULL)) { | 660 | if (unlikely(res == NULL)) { |
@@ -646,47 +696,66 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
646 | } | 696 | } |
647 | 697 | ||
648 | rtc->rtc_dev->max_user_freq = 256; | 698 | rtc->rtc_dev->max_user_freq = 256; |
649 | rtc->rtc_dev->irq_freq = 1; | ||
650 | rtc->periodic_freq = 0x60; | ||
651 | 699 | ||
652 | platform_set_drvdata(pdev, rtc); | 700 | platform_set_drvdata(pdev, rtc); |
653 | 701 | ||
654 | /* register periodic/carry/alarm irqs */ | 702 | if (rtc->carry_irq <= 0) { |
655 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, | 703 | /* register shared periodic/carry/alarm irq */ |
656 | "sh-rtc period", rtc); | 704 | ret = request_irq(rtc->periodic_irq, sh_rtc_shared, |
657 | if (unlikely(ret)) { | 705 | IRQF_DISABLED, "sh-rtc", rtc); |
658 | dev_err(&pdev->dev, | 706 | if (unlikely(ret)) { |
659 | "request period IRQ failed with %d, IRQ %d\n", ret, | 707 | dev_err(&pdev->dev, |
660 | rtc->periodic_irq); | 708 | "request IRQ failed with %d, IRQ %d\n", ret, |
661 | goto err_unmap; | 709 | rtc->periodic_irq); |
662 | } | 710 | goto err_unmap; |
711 | } | ||
712 | } else { | ||
713 | /* register periodic/carry/alarm irqs */ | ||
714 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, | ||
715 | IRQF_DISABLED, "sh-rtc period", rtc); | ||
716 | if (unlikely(ret)) { | ||
717 | dev_err(&pdev->dev, | ||
718 | "request period IRQ failed with %d, IRQ %d\n", | ||
719 | ret, rtc->periodic_irq); | ||
720 | goto err_unmap; | ||
721 | } | ||
663 | 722 | ||
664 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, | 723 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, |
665 | "sh-rtc carry", rtc); | 724 | IRQF_DISABLED, "sh-rtc carry", rtc); |
666 | if (unlikely(ret)) { | 725 | if (unlikely(ret)) { |
667 | dev_err(&pdev->dev, | 726 | dev_err(&pdev->dev, |
668 | "request carry IRQ failed with %d, IRQ %d\n", ret, | 727 | "request carry IRQ failed with %d, IRQ %d\n", |
669 | rtc->carry_irq); | 728 | ret, rtc->carry_irq); |
670 | free_irq(rtc->periodic_irq, rtc); | 729 | free_irq(rtc->periodic_irq, rtc); |
671 | goto err_unmap; | 730 | goto err_unmap; |
672 | } | 731 | } |
673 | 732 | ||
674 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, | 733 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, |
675 | "sh-rtc alarm", rtc); | 734 | IRQF_DISABLED, "sh-rtc alarm", rtc); |
676 | if (unlikely(ret)) { | 735 | if (unlikely(ret)) { |
677 | dev_err(&pdev->dev, | 736 | dev_err(&pdev->dev, |
678 | "request alarm IRQ failed with %d, IRQ %d\n", ret, | 737 | "request alarm IRQ failed with %d, IRQ %d\n", |
679 | rtc->alarm_irq); | 738 | ret, rtc->alarm_irq); |
680 | free_irq(rtc->carry_irq, rtc); | 739 | free_irq(rtc->carry_irq, rtc); |
681 | free_irq(rtc->periodic_irq, rtc); | 740 | free_irq(rtc->periodic_irq, rtc); |
682 | goto err_unmap; | 741 | goto err_unmap; |
742 | } | ||
683 | } | 743 | } |
684 | 744 | ||
685 | tmp = readb(rtc->regbase + RCR1); | 745 | /* everything disabled by default */ |
686 | tmp &= ~RCR1_CF; | 746 | rtc->periodic_freq = 0; |
687 | tmp |= RCR1_CIE; | 747 | rtc->rtc_dev->irq_freq = 0; |
688 | writeb(tmp, rtc->regbase + RCR1); | 748 | sh_rtc_setpie(&pdev->dev, 0); |
749 | sh_rtc_setaie(&pdev->dev, 0); | ||
750 | sh_rtc_setcie(&pdev->dev, 0); | ||
751 | |||
752 | /* reset rtc to epoch 0 if time is invalid */ | ||
753 | if (rtc_read_time(rtc->rtc_dev, &r) < 0) { | ||
754 | rtc_time_to_tm(0, &r); | ||
755 | rtc_set_time(rtc->rtc_dev, &r); | ||
756 | } | ||
689 | 757 | ||
758 | device_init_wakeup(&pdev->dev, 1); | ||
690 | return 0; | 759 | return 0; |
691 | 760 | ||
692 | err_unmap: | 761 | err_unmap: |
@@ -708,10 +777,13 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) | |||
708 | 777 | ||
709 | sh_rtc_setpie(&pdev->dev, 0); | 778 | sh_rtc_setpie(&pdev->dev, 0); |
710 | sh_rtc_setaie(&pdev->dev, 0); | 779 | sh_rtc_setaie(&pdev->dev, 0); |
780 | sh_rtc_setcie(&pdev->dev, 0); | ||
711 | 781 | ||
712 | free_irq(rtc->carry_irq, rtc); | ||
713 | free_irq(rtc->periodic_irq, rtc); | 782 | free_irq(rtc->periodic_irq, rtc); |
714 | free_irq(rtc->alarm_irq, rtc); | 783 | if (rtc->carry_irq > 0) { |
784 | free_irq(rtc->carry_irq, rtc); | ||
785 | free_irq(rtc->alarm_irq, rtc); | ||
786 | } | ||
715 | 787 | ||
716 | release_resource(rtc->res); | 788 | release_resource(rtc->res); |
717 | 789 | ||