/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <errno.h> #include <isr.h> #include <platform.h> #include <plat/cmsis.h> #include <plat/exti.h> #include <plat/pwr.h> struct StmExti { volatile uint32_t IMR; volatile uint32_t EMR; volatile uint32_t RTSR; volatile uint32_t FTSR; volatile uint32_t SWIER; volatile uint32_t PR; }; #define EXTI ((struct StmExti*)EXTI_BASE) void extiEnableIntLine(const enum ExtiLine line, enum ExtiTrigger trigger) { if (trigger == EXTI_TRIGGER_BOTH) { EXTI->RTSR |= (1UL << line); EXTI->FTSR |= (1UL << line); } else if (trigger == EXTI_TRIGGER_RISING) { EXTI->RTSR |= (1UL << line); EXTI->FTSR &= ~(1UL << line); } else if (trigger == EXTI_TRIGGER_FALLING) { EXTI->RTSR &= ~(1UL << line); EXTI->FTSR |= (1UL << line); } /* Clear pending interrupt */ extiClearPendingLine(line); /* Enable hardware interrupt */ EXTI->IMR |= (1UL << line); } void extiDisableIntLine(const enum ExtiLine line) { EXTI->IMR &= ~(1UL << line); } void extiClearPendingLine(const enum ExtiLine line) { EXTI->PR = (1UL << line); } bool extiIsPendingLine(const enum ExtiLine line) { return (EXTI->PR & (1UL << line)) ? true : false; } struct ExtiInterrupt { struct ChainedInterrupt base; IRQn_Type irq; }; static void extiInterruptEnable(struct ChainedInterrupt *irq) { struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base); NVIC_EnableIRQ(exti->irq); } static void extiInterruptDisable(struct ChainedInterrupt *irq) { struct ExtiInterrupt *exti = container_of(irq, struct ExtiInterrupt, base); NVIC_DisableIRQ(exti->irq); } #define DECLARE_SHARED_EXTI(i) { \ .base = { \ .enable = extiInterruptEnable, \ .disable = extiInterruptDisable, \ }, \ .irq = i, \ } uint32_t mMaxLatency = 0; static struct ExtiInterrupt mInterrupts[] = { DECLARE_SHARED_EXTI(EXTI0_IRQn), DECLARE_SHARED_EXTI(EXTI1_IRQn), DECLARE_SHARED_EXTI(EXTI2_IRQn), DECLARE_SHARED_EXTI(EXTI3_IRQn), DECLARE_SHARED_EXTI(EXTI4_IRQn), DECLARE_SHARED_EXTI(EXTI9_5_IRQn), DECLARE_SHARED_EXTI(EXTI15_10_IRQn), }; static void extiUpdateMaxLatency(uint32_t maxLatencyNs) { if (!maxLatencyNs && mMaxLatency) platReleaseDevInSleepMode(Stm32sleepDevExti); else if (maxLatencyNs && !mMaxLatency) platRequestDevInSleepMode(Stm32sleepDevExti, maxLatencyNs); else if (maxLatencyNs && mMaxLatency) platAdjustDevInSleepMode(Stm32sleepDevExti, maxLatencyNs); mMaxLatency = maxLatencyNs; } static void extiCalcMaxLatency() { int i; uint32_t maxLatency, newMaxLatency = 0; struct ExtiInterrupt *exti = mInterrupts; for (i = 0; i < ARRAY_SIZE(mInterrupts); ++i, ++exti) { maxLatency = maxLatencyIsr(&exti->base); if (!newMaxLatency || (maxLatency && maxLatency < newMaxLatency)) newMaxLatency = maxLatency; } extiUpdateMaxLatency(newMaxLatency); } static inline struct ExtiInterrupt *extiForIrq(IRQn_Type n) { if (n >= EXTI0_IRQn && n <= EXTI4_IRQn) return &mInterrupts[n - EXTI0_IRQn]; if (n == EXTI9_5_IRQn) return &mInterrupts[ARRAY_SIZE(mInterrupts) - 2]; if (n == EXTI15_10_IRQn) return &mInterrupts[ARRAY_SIZE(mInterrupts) - 1]; return NULL; } static void extiIrqHandler(IRQn_Type n) { struct ExtiInterrupt *exti = extiForIrq(n); dispatchIsr(&exti->base); } #define DEFINE_SHARED_EXTI_ISR(i) \ void EXTI##i##_IRQHandler(void); \ void EXTI##i##_IRQHandler(void) { \ extiIrqHandler(EXTI##i##_IRQn); \ } \ DEFINE_SHARED_EXTI_ISR(0) DEFINE_SHARED_EXTI_ISR(1) DEFINE_SHARED_EXTI_ISR(2) DEFINE_SHARED_EXTI_ISR(3) DEFINE_SHARED_EXTI_ISR(4) DEFINE_SHARED_EXTI_ISR(9_5) DEFINE_SHARED_EXTI_ISR(15_10) int extiSetMaxLatency(struct ChainedIsr *isr, uint32_t maxLatencyNs) { uint32_t latency; if (!isr) return -EINVAL; if (maxLatencyNs != isr->maxLatencyNs) { latency = isr->maxLatencyNs; isr->maxLatencyNs = maxLatencyNs; if (!mMaxLatency || latency == mMaxLatency || (maxLatencyNs && maxLatencyNs < mMaxLatency)) { extiCalcMaxLatency(); } } return 0; } int extiChainIsr(IRQn_Type n, struct ChainedIsr *isr) { struct ExtiInterrupt *exti = extiForIrq(n); if (!exti) return -EINVAL; else if (!list_is_empty(&isr->node)) return -EINVAL; chainIsr(&exti->base, isr); if (!mMaxLatency || (isr->maxLatencyNs && isr->maxLatencyNs < mMaxLatency)) extiUpdateMaxLatency(isr->maxLatencyNs); return 0; } int extiUnchainIsr(IRQn_Type n, struct ChainedIsr *isr) { struct ExtiInterrupt *exti = extiForIrq(n); if (!exti) return -EINVAL; else if (list_is_empty(&isr->node)) return -EINVAL; unchainIsr(&exti->base, isr); if (isr->maxLatencyNs && isr->maxLatencyNs == mMaxLatency) extiCalcMaxLatency(); return 0; } int extiUnchainAll(uint32_t tid) { int i, count = 0; struct ExtiInterrupt *exti = mInterrupts; for (i = 0; i < ARRAY_SIZE(mInterrupts); ++i, ++exti) count += unchainIsrAll(&exti->base, tid); extiCalcMaxLatency(); return count; }