summaryrefslogtreecommitdiff
path: root/firmware/os/platform/stm32/exti.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/os/platform/stm32/exti.c')
-rw-r--r--firmware/os/platform/stm32/exti.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/firmware/os/platform/stm32/exti.c b/firmware/os/platform/stm32/exti.c
new file mode 100644
index 00000000..ababe3c1
--- /dev/null
+++ b/firmware/os/platform/stm32/exti.c
@@ -0,0 +1,167 @@
+/*
+ * 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 <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, \
+}
+
+static struct ExtiInterrupt gInterrupts[] = {
+ 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 inline struct ExtiInterrupt *extiForIrq(IRQn_Type n)
+{
+ if (n >= EXTI0_IRQn && n <= EXTI4_IRQn)
+ return &gInterrupts[n - EXTI0_IRQn];
+ if (n == EXTI9_5_IRQn)
+ return &gInterrupts[ARRAY_SIZE(gInterrupts) - 2];
+ if (n == EXTI15_10_IRQn)
+ return &gInterrupts[ARRAY_SIZE(gInterrupts) - 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 extiChainIsr(IRQn_Type n, struct ChainedIsr *isr)
+{
+ struct ExtiInterrupt *exti = extiForIrq(n);
+ if (!exti)
+ return -EINVAL;
+
+ chainIsr(&exti->base, isr);
+ return 0;
+}
+
+int extiUnchainIsr(IRQn_Type n, struct ChainedIsr *isr)
+{
+ struct ExtiInterrupt *exti = extiForIrq(n);
+ if (!exti)
+ return -EINVAL;
+
+ unchainIsr(&exti->base, isr);
+ return 0;
+}
+
+int extiUnchainAll(uint32_t tid)
+{
+ int i, count = 0;
+ struct ExtiInterrupt *exti = gInterrupts;
+
+ for (i = 0; i < ARRAY_SIZE(gInterrupts); ++i, ++exti)
+ count += unchainIsrAll(&exti->base, tid);
+
+ return count;
+}