/*
* 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;
}