aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/rtc/rtc-sh.c
diff options
context:
space:
mode:
authorMagnus Damm <damm@igel.co.jp>2009-02-24 08:11:03 -0500
committerPaul Mundt <lethal@linux-sh.org>2009-02-27 02:50:00 -0500
commit5e084a1586a864d4e9b3f2edbb1bd3429909d652 (patch)
tree19a9f70ccc0fe1c746fbeccd108b6366f5021ea7 /drivers/rtc/rtc-sh.c
parent973e5d525d39be6f9f6c38d37aacf03efda02e60 (diff)
rtc: sh-rtc: Add Single IRQ Support
Add support for single IRQ hardware to the sh-rtc driver. This is useful for processors with limited interrupt masking support such as sh7750 and sh7780. With this patch in place we can add logic to the intc code that merges all RTC vectors into a single linux interrupt with proper masking/unmasking support. Specify a single IRQ in the platform data to use this new shared IRQ feature. Separate Periodic/Carry/Alarm IRQs are still supported. Signed-off-by: Magnus Damm <damm@igel.co.jp> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/rtc/rtc-sh.c')
-rw-r--r--drivers/rtc/rtc-sh.c190
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
102static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id) 102static 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
122static irqreturn_t sh_rtc_alarm(int irq, void *dev_id) 118static 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
140static irqreturn_t sh_rtc_periodic(int irq, void *dev_id) 133static 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
166static 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
178static 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
190static 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
202static 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
172static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) 216static 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