diff options
-rw-r--r-- | drivers/rtc/rtc-sh.c | 190 |
1 files changed, 117 insertions, 73 deletions
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index 1c3fc6b428e9..b37f44b0406e 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c | |||
@@ -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) |
@@ -585,26 +629,12 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
585 | ret = platform_get_irq(pdev, 0); | 629 | ret = platform_get_irq(pdev, 0); |
586 | if (unlikely(ret <= 0)) { | 630 | if (unlikely(ret <= 0)) { |
587 | ret = -ENOENT; | 631 | ret = -ENOENT; |
588 | dev_err(&pdev->dev, "No IRQ for period\n"); | 632 | dev_err(&pdev->dev, "No IRQ resource\n"); |
589 | goto err_badres; | 633 | goto err_badres; |
590 | } | 634 | } |
591 | rtc->periodic_irq = ret; | 635 | rtc->periodic_irq = ret; |
592 | 636 | rtc->carry_irq = platform_get_irq(pdev, 1); | |
593 | ret = platform_get_irq(pdev, 1); | 637 | 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 | 638 | ||
609 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); | 639 | res = platform_get_resource(pdev, IORESOURCE_IO, 0); |
610 | if (unlikely(res == NULL)) { | 640 | if (unlikely(res == NULL)) { |
@@ -651,35 +681,47 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) | |||
651 | 681 | ||
652 | platform_set_drvdata(pdev, rtc); | 682 | platform_set_drvdata(pdev, rtc); |
653 | 683 | ||
654 | /* register periodic/carry/alarm irqs */ | 684 | if (rtc->carry_irq <= 0) { |
655 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED, | 685 | /* register shared periodic/carry/alarm irq */ |
656 | "sh-rtc period", rtc); | 686 | ret = request_irq(rtc->periodic_irq, sh_rtc_shared, |
657 | if (unlikely(ret)) { | 687 | IRQF_DISABLED, "sh-rtc", rtc); |
658 | dev_err(&pdev->dev, | 688 | if (unlikely(ret)) { |
659 | "request period IRQ failed with %d, IRQ %d\n", ret, | 689 | dev_err(&pdev->dev, |
660 | rtc->periodic_irq); | 690 | "request IRQ failed with %d, IRQ %d\n", ret, |
661 | goto err_unmap; | 691 | rtc->periodic_irq); |
662 | } | 692 | goto err_unmap; |
693 | } | ||
694 | } else { | ||
695 | /* register periodic/carry/alarm irqs */ | ||
696 | ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, | ||
697 | IRQF_DISABLED, "sh-rtc period", rtc); | ||
698 | if (unlikely(ret)) { | ||
699 | dev_err(&pdev->dev, | ||
700 | "request period IRQ failed with %d, IRQ %d\n", | ||
701 | ret, rtc->periodic_irq); | ||
702 | goto err_unmap; | ||
703 | } | ||
663 | 704 | ||
664 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED, | 705 | ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, |
665 | "sh-rtc carry", rtc); | 706 | IRQF_DISABLED, "sh-rtc carry", rtc); |
666 | if (unlikely(ret)) { | 707 | if (unlikely(ret)) { |
667 | dev_err(&pdev->dev, | 708 | dev_err(&pdev->dev, |
668 | "request carry IRQ failed with %d, IRQ %d\n", ret, | 709 | "request carry IRQ failed with %d, IRQ %d\n", |
669 | rtc->carry_irq); | 710 | ret, rtc->carry_irq); |
670 | free_irq(rtc->periodic_irq, rtc); | 711 | free_irq(rtc->periodic_irq, rtc); |
671 | goto err_unmap; | 712 | goto err_unmap; |
672 | } | 713 | } |
673 | 714 | ||
674 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED, | 715 | ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, |
675 | "sh-rtc alarm", rtc); | 716 | IRQF_DISABLED, "sh-rtc alarm", rtc); |
676 | if (unlikely(ret)) { | 717 | if (unlikely(ret)) { |
677 | dev_err(&pdev->dev, | 718 | dev_err(&pdev->dev, |
678 | "request alarm IRQ failed with %d, IRQ %d\n", ret, | 719 | "request alarm IRQ failed with %d, IRQ %d\n", |
679 | rtc->alarm_irq); | 720 | ret, rtc->alarm_irq); |
680 | free_irq(rtc->carry_irq, rtc); | 721 | free_irq(rtc->carry_irq, rtc); |
681 | free_irq(rtc->periodic_irq, rtc); | 722 | free_irq(rtc->periodic_irq, rtc); |
682 | goto err_unmap; | 723 | goto err_unmap; |
724 | } | ||
683 | } | 725 | } |
684 | 726 | ||
685 | tmp = readb(rtc->regbase + RCR1); | 727 | tmp = readb(rtc->regbase + RCR1); |
@@ -709,9 +751,11 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) | |||
709 | sh_rtc_setpie(&pdev->dev, 0); | 751 | sh_rtc_setpie(&pdev->dev, 0); |
710 | sh_rtc_setaie(&pdev->dev, 0); | 752 | sh_rtc_setaie(&pdev->dev, 0); |
711 | 753 | ||
712 | free_irq(rtc->carry_irq, rtc); | ||
713 | free_irq(rtc->periodic_irq, rtc); | 754 | free_irq(rtc->periodic_irq, rtc); |
714 | free_irq(rtc->alarm_irq, rtc); | 755 | if (rtc->carry_irq > 0) { |
756 | free_irq(rtc->carry_irq, rtc); | ||
757 | free_irq(rtc->alarm_irq, rtc); | ||
758 | } | ||
715 | 759 | ||
716 | release_resource(rtc->res); | 760 | release_resource(rtc->res); |
717 | 761 | ||