1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <errno.h>
18 #include <isr.h>
19 #include <platform.h>
20 
21 #include <plat/cmsis.h>
22 #include <plat/exti.h>
23 #include <plat/pwr.h>
24 
25 struct StmExti
26 {
27     volatile uint32_t IMR;
28     volatile uint32_t EMR;
29     volatile uint32_t RTSR;
30     volatile uint32_t FTSR;
31     volatile uint32_t SWIER;
32     volatile uint32_t PR;
33 };
34 
35 #define EXTI ((struct StmExti*)EXTI_BASE)
36 
extiEnableIntLine(const enum ExtiLine line,enum ExtiTrigger trigger)37 void extiEnableIntLine(const enum ExtiLine line, enum ExtiTrigger trigger)
38 {
39     if (trigger == EXTI_TRIGGER_BOTH) {
40         EXTI->RTSR |= (1UL << line);
41         EXTI->FTSR |= (1UL << line);
42     } else if (trigger == EXTI_TRIGGER_RISING) {
43         EXTI->RTSR |= (1UL << line);
44         EXTI->FTSR &= ~(1UL << line);
45     } else if (trigger == EXTI_TRIGGER_FALLING) {
46         EXTI->RTSR &= ~(1UL << line);
47         EXTI->FTSR |= (1UL << line);
48     }
49 
50     /* Clear pending interrupt */
51     extiClearPendingLine(line);
52 
53     /* Enable hardware interrupt */
54     EXTI->IMR |= (1UL << line);
55 }
56 
extiDisableIntLine(const enum ExtiLine line)57 void extiDisableIntLine(const enum ExtiLine line)
58 {
59     EXTI->IMR &= ~(1UL << line);
60 }
61 
extiClearPendingLine(const enum ExtiLine line)62 void extiClearPendingLine(const enum ExtiLine line)
63 {
64     EXTI->PR = (1UL << line);
65 }
66 
extiIsPendingLine(const enum ExtiLine line)67 bool extiIsPendingLine(const enum ExtiLine line)
68 {
69     return (EXTI->PR & (1UL << line)) ? true : false;
70 }
71 
72 struct ExtiInterrupt
73 {
74     struct ChainedInterrupt base;
75     IRQn_Type irq;
76 };
77 
extiInterruptEnable(struct ChainedInterrupt * irq)78 static void extiInterruptEnable(struct ChainedInterrupt *irq)
79 {
80     struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base);
81     NVIC_EnableIRQ(exti->irq);
82 }
83 
extiInterruptDisable(struct ChainedInterrupt * irq)84 static void extiInterruptDisable(struct ChainedInterrupt *irq)
85 {
86     struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base);
87     NVIC_DisableIRQ(exti->irq);
88 }
89 
90 #define DECLARE_SHARED_EXTI(i) {            \
91     .base = {                               \
92         .enable = extiInterruptEnable,      \
93         .disable = extiInterruptDisable,    \
94     },                                      \
95     .irq = i,                               \
96 }
97 
98 uint32_t mMaxLatency = 0;
99 
100 static struct ExtiInterrupt mInterrupts[] = {
101     DECLARE_SHARED_EXTI(EXTI0_IRQn),
102     DECLARE_SHARED_EXTI(EXTI1_IRQn),
103     DECLARE_SHARED_EXTI(EXTI2_IRQn),
104     DECLARE_SHARED_EXTI(EXTI3_IRQn),
105     DECLARE_SHARED_EXTI(EXTI4_IRQn),
106     DECLARE_SHARED_EXTI(EXTI9_5_IRQn),
107     DECLARE_SHARED_EXTI(EXTI15_10_IRQn),
108 };
109 
extiUpdateMaxLatency(uint32_t maxLatencyNs)110 static void extiUpdateMaxLatency(uint32_t maxLatencyNs)
111 {
112     if (!maxLatencyNs && mMaxLatency)
113         platReleaseDevInSleepMode(Stm32sleepDevExti);
114     else if (maxLatencyNs && !mMaxLatency)
115         platRequestDevInSleepMode(Stm32sleepDevExti, maxLatencyNs);
116     else if (maxLatencyNs && mMaxLatency)
117         platAdjustDevInSleepMode(Stm32sleepDevExti, maxLatencyNs);
118     mMaxLatency = maxLatencyNs;
119 }
120 
extiCalcMaxLatency()121 static void extiCalcMaxLatency()
122 {
123     int i;
124     uint32_t maxLatency, newMaxLatency = 0;
125     struct ExtiInterrupt *exti = mInterrupts;
126 
127     for (i = 0; i < ARRAY_SIZE(mInterrupts); ++i, ++exti) {
128         maxLatency = maxLatencyIsr(&exti->base);
129         if (!newMaxLatency || (maxLatency && maxLatency < newMaxLatency))
130             newMaxLatency = maxLatency;
131     }
132     extiUpdateMaxLatency(newMaxLatency);
133 }
134 
extiForIrq(IRQn_Type n)135 static inline struct ExtiInterrupt *extiForIrq(IRQn_Type n)
136 {
137     if (n >= EXTI0_IRQn && n <= EXTI4_IRQn)
138         return &mInterrupts[n - EXTI0_IRQn];
139     if (n == EXTI9_5_IRQn)
140         return &mInterrupts[ARRAY_SIZE(mInterrupts) - 2];
141     if (n == EXTI15_10_IRQn)
142         return &mInterrupts[ARRAY_SIZE(mInterrupts) - 1];
143     return NULL;
144 }
145 
extiIrqHandler(IRQn_Type n)146 static void extiIrqHandler(IRQn_Type n)
147 {
148     struct ExtiInterrupt *exti = extiForIrq(n);
149     dispatchIsr(&exti->base);
150 }
151 
152 #define DEFINE_SHARED_EXTI_ISR(i)           \
153     void EXTI##i##_IRQHandler(void);        \
154     void EXTI##i##_IRQHandler(void) {       \
155         extiIrqHandler(EXTI##i##_IRQn);     \
156     }                                       \
157 
158 DEFINE_SHARED_EXTI_ISR(0)
159 DEFINE_SHARED_EXTI_ISR(1)
160 DEFINE_SHARED_EXTI_ISR(2)
161 DEFINE_SHARED_EXTI_ISR(3)
162 DEFINE_SHARED_EXTI_ISR(4)
163 DEFINE_SHARED_EXTI_ISR(9_5)
164 DEFINE_SHARED_EXTI_ISR(15_10)
165 
extiSetMaxLatency(struct ChainedIsr * isr,uint32_t maxLatencyNs)166 int extiSetMaxLatency(struct ChainedIsr *isr, uint32_t maxLatencyNs)
167 {
168     uint32_t latency;
169 
170     if (!isr)
171         return -EINVAL;
172 
173     if (maxLatencyNs != isr->maxLatencyNs) {
174         latency = isr->maxLatencyNs;
175         isr->maxLatencyNs = maxLatencyNs;
176         if (!mMaxLatency || latency == mMaxLatency || (maxLatencyNs && maxLatencyNs < mMaxLatency)) {
177             extiCalcMaxLatency();
178         }
179     }
180 
181     return 0;
182 }
183 
extiChainIsr(IRQn_Type n,struct ChainedIsr * isr)184 int extiChainIsr(IRQn_Type n, struct ChainedIsr *isr)
185 {
186     struct ExtiInterrupt *exti = extiForIrq(n);
187     if (!exti)
188         return -EINVAL;
189     else if (!list_is_empty(&isr->node))
190         return -EINVAL;
191 
192     chainIsr(&exti->base, isr);
193     if (!mMaxLatency || (isr->maxLatencyNs && isr->maxLatencyNs < mMaxLatency))
194         extiUpdateMaxLatency(isr->maxLatencyNs);
195 
196     return 0;
197 }
198 
extiUnchainIsr(IRQn_Type n,struct ChainedIsr * isr)199 int extiUnchainIsr(IRQn_Type n, struct ChainedIsr *isr)
200 {
201     struct ExtiInterrupt *exti = extiForIrq(n);
202     if (!exti)
203         return -EINVAL;
204     else if (list_is_empty(&isr->node))
205         return -EINVAL;
206 
207     unchainIsr(&exti->base, isr);
208     if (isr->maxLatencyNs && isr->maxLatencyNs == mMaxLatency)
209         extiCalcMaxLatency();
210     return 0;
211 }
212 
extiUnchainAll(uint32_t tid)213 int extiUnchainAll(uint32_t tid)
214 {
215     int i, count = 0;
216     struct ExtiInterrupt *exti = mInterrupts;
217 
218     for (i = 0; i < ARRAY_SIZE(mInterrupts); ++i, ++exti)
219         count += unchainIsrAll(&exti->base, tid);
220     extiCalcMaxLatency();
221 
222     return count;
223 }
224