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 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 57 void extiDisableIntLine(const enum ExtiLine line) 58 { 59 EXTI->IMR &= ~(1UL << line); 60 } 61 62 void extiClearPendingLine(const enum ExtiLine line) 63 { 64 EXTI->PR = (1UL << line); 65 } 66 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 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 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 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 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 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 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 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 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 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 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