You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
LiteOS-Reading/src/kernel/base/los_hwi.c

511 lines
14 KiB

/* ----------------------------------------------------------------------------
* Copyright (c) Huawei Technologies Co., Ltd. 2013-2021. All rights reserved.
* Description: Interrupt Abstraction Layer And API Implementation
* Author: Huawei LiteOS Team
* Create: 2013-01-01
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* --------------------------------------------------------------------------- */
#include "los_hwi_pri.h"
#include "los_hwi.h"
#include "los_memory.h"
#include "los_spinlock.h"
#include "los_trace.h"
#ifdef LOSCFG_KERNEL_CPUP
#include "los_cpup_pri.h"
#endif
#ifdef LOSCFG_DEBUG_SCHED_STATISTICS
#include "los_sched_debug_pri.h"
#endif
#include "los_err_pri.h"
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
/* spinlock for hwi module, only available on SMP mode */
LITE_OS_SEC_BSS SPIN_LOCK_INIT(g_hwiSpin);
#define HWI_LOCK(state) LOS_SpinLockSave(&g_hwiSpin, &(state))
#define HWI_UNLOCK(state) LOS_SpinUnlockRestore(&g_hwiSpin, (state))
size_t g_intCount[LOSCFG_KERNEL_CORE_NUM] = {0};
#ifdef LOSCFG_KERNEL_LOWPOWER
STATIC WAKEUPFROMINTHOOK g_intWakeupHook = NULL;
#endif
const HwiControllerOps *g_hwiOps = NULL;
typedef VOID (*HWI_PROC_FUNC0)(VOID);
typedef VOID (*HWI_PROC_FUNC2)(INT32, VOID *);
STATIC INLINE VOID OsIrqNestingActive(UINT32 hwiNum)
{
#ifdef LOSCFG_ARCH_INTERRUPT_PREEMPTION
/* preemption not allowed when handling tick interrupt */
if (hwiNum != OS_TICK_INT_NUM) {
(VOID)LOS_IntUnLock();
}
#endif
}
STATIC INLINE VOID OsIrqNestingInactive(UINT32 hwiNum)
{
#ifdef LOSCFG_ARCH_INTERRUPT_PREEMPTION
if (hwiNum != OS_TICK_INT_NUM) {
(VOID)LOS_IntLock();
}
#endif
}
size_t OsIrqNestingCntGet(VOID)
{
return g_intCount[ArchCurrCpuid()];
}
VOID OsIrqNestingCntSet(size_t val)
{
g_intCount[ArchCurrCpuid()] = val;
}
STATIC INLINE VOID InterruptHandle(HwiHandleInfo *hwiForm)
{
hwiForm->respCount++;
#ifdef LOSCFG_SHARED_IRQ
while (hwiForm->next != NULL) {
hwiForm = hwiForm->next;
#endif
if (hwiForm->registerInfo) {
HWI_PROC_FUNC2 func = (HWI_PROC_FUNC2)hwiForm->hook;
if (func != NULL) {
UINTPTR *param = (UINTPTR *)(hwiForm->registerInfo);
func((INT32)(*param), (VOID *)(*(param + 1)));
}
} else {
HWI_PROC_FUNC0 func = (HWI_PROC_FUNC0)hwiForm->hook;
if (func != NULL) {
func();
}
}
#ifdef LOSCFG_SHARED_IRQ
}
#endif
}
VOID OsIntHandle(UINT32 hwiNum, HwiHandleInfo *hwiForm)
{
size_t *intCnt = NULL;
#ifdef LOSCFG_CPUP_INCLUDE_IRQ
OsCpupIrqStart();
#endif
intCnt = &g_intCount[ArchCurrCpuid()];
*intCnt = *intCnt + 1;
#ifdef LOSCFG_DEBUG_SCHED_STATISTICS
OsHwiStatistics(hwiNum);
#endif
#ifdef LOSCFG_KERNEL_LOWPOWER
if (g_intWakeupHook != NULL) {
g_intWakeupHook(hwiNum);
}
#endif
LOS_TRACE(HWI_RESPONSE_IN, hwiNum);
OsIrqNestingActive(hwiNum);
InterruptHandle(hwiForm);
OsIrqNestingInactive(hwiNum);
LOS_TRACE(HWI_RESPONSE_OUT, hwiNum);
*intCnt = *intCnt - 1;
#ifdef LOSCFG_CPUP_INCLUDE_IRQ
OsCpupIrqEnd(hwiNum);
#endif
}
VOID OsIntEntry(VOID)
{
if ((g_hwiOps != NULL) && (g_hwiOps->handleIrq != NULL)) {
g_hwiOps->handleIrq();
}
return;
}
STATIC HWI_ARG_T OsHwiCpIrqParam(const HWI_IRQ_PARAM_S *irqParam)
{
HWI_IRQ_PARAM_S *paramByAlloc = NULL;
paramByAlloc = (HWI_IRQ_PARAM_S *)LOS_MemAlloc(m_aucSysMem0, sizeof(HWI_IRQ_PARAM_S));
if (paramByAlloc != NULL) {
(VOID)memcpy_s(paramByAlloc, sizeof(HWI_IRQ_PARAM_S), irqParam, sizeof(HWI_IRQ_PARAM_S));
}
return (HWI_ARG_T)paramByAlloc;
}
#ifndef LOSCFG_SHARED_IRQ
STATIC UINT32 OsHwiDel(HwiHandleInfo *hwiForm, const HWI_IRQ_PARAM_S *irqParam, UINT32 irqId)
{
UINT32 intSave;
(VOID)irqParam;
HWI_LOCK(intSave);
hwiForm->hook = NULL;
if (hwiForm->registerInfo) {
(VOID)LOS_MemFree(m_aucSysMem0, (VOID *)hwiForm->registerInfo);
}
hwiForm->registerInfo = 0;
hwiForm->respCount = 0;
if (g_hwiOps->disableIrq == NULL) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
g_hwiOps->disableIrq(irqId);
HWI_UNLOCK(intSave);
return LOS_OK;
}
STATIC UINT32 OsHwiCreate(HwiHandleInfo *hwiForm, HWI_MODE_T hwiMode, HWI_PROC_FUNC hwiHandler,
const HWI_IRQ_PARAM_S *irqParam)
{
UINT32 intSave;
if (hwiMode & IRQF_SHARED) {
return LOS_ERRNO_HWI_SHARED_ERROR;
}
HWI_LOCK(intSave);
if (hwiForm->hook == NULL) {
hwiForm->hook = hwiHandler;
if (irqParam != NULL) {
hwiForm->registerInfo = OsHwiCpIrqParam(irqParam);
if (hwiForm->registerInfo == (HWI_ARG_T)NULL) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_NO_MEMORY;
}
}
} else {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_ALREADY_CREATED;
}
HWI_UNLOCK(intSave);
return LOS_OK;
}
#else /* LOSCFG_SHARED_IRQ */
STATIC INLINE UINT32 OsFreeHwiNode(HwiHandleInfo *head, HwiHandleInfo *hwiForm, UINT32 irqId)
{
UINT32 ret = LOS_OK;
if (hwiForm->registerInfo != (HWI_ARG_T)NULL) {
(VOID)LOS_MemFree(m_aucSysMem0, (VOID *)hwiForm->registerInfo);
}
(VOID)LOS_MemFree(m_aucSysMem0, hwiForm);
if (head->next == NULL) {
head->shareMode = 0;
head->respCount = 0;
if (g_hwiOps->disableIrq == NULL) {
ret = LOS_ERRNO_HWI_PROC_FUNC_NULL;
return ret;
}
g_hwiOps->disableIrq(irqId);
}
return ret;
}
STATIC UINT32 OsHwiDel(HwiHandleInfo *head, const HWI_IRQ_PARAM_S *irqParam, UINT32 irqId)
{
HwiHandleInfo *hwiFormPrev = NULL;
HwiHandleInfo *hwiForm = NULL;
UINT32 intSave;
UINT32 ret;
HWI_LOCK(intSave);
if ((head->shareMode & IRQF_SHARED) && ((irqParam == NULL) || (irqParam->pDevId == NULL))) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_SHARED_ERROR;
}
/* Non-shared interrupt. */
if (!(head->shareMode & IRQF_SHARED)) {
if (head->next == NULL) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_HWINUM_UNCREATE;
}
hwiForm = head->next;
head->next = NULL;
ret = OsFreeHwiNode(head, hwiForm, irqId);
HWI_UNLOCK(intSave);
return ret;
}
/* Shared interrupt. */
hwiFormPrev = head;
hwiForm = head->next;
while (hwiForm != NULL) {
if (((HWI_IRQ_PARAM_S *)(hwiForm->registerInfo))->pDevId == irqParam->pDevId) {
break;
}
hwiFormPrev = hwiForm;
hwiForm = hwiForm->next;
}
if (hwiForm == NULL) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_HWINUM_UNCREATE;
}
hwiFormPrev->next = hwiForm->next;
ret = OsFreeHwiNode(head, hwiForm, irqId);
HWI_UNLOCK(intSave);
return ret;
}
STATIC UINT32 OsHwiCreate(HwiHandleInfo *head, HWI_MODE_T hwiMode, HWI_PROC_FUNC hwiHandler,
const HWI_IRQ_PARAM_S *irqParam)
{
UINT32 intSave;
HwiHandleInfo *hwiFormNode = NULL;
HWI_IRQ_PARAM_S *hwiParam = NULL;
HWI_MODE_T modeResult = hwiMode & IRQF_SHARED;
HwiHandleInfo *hwiForm = NULL;
if (modeResult && ((irqParam == NULL) || (irqParam->pDevId == NULL))) {
return LOS_ERRNO_HWI_SHARED_ERROR;
}
HWI_LOCK(intSave);
if ((head->next != NULL) && ((modeResult == 0) || (!(head->shareMode & IRQF_SHARED)))) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_SHARED_ERROR;
}
hwiForm = head;
while (hwiForm->next != NULL) {
hwiForm = hwiForm->next;
hwiParam = (HWI_IRQ_PARAM_S *)(hwiForm->registerInfo);
if (hwiParam->pDevId == irqParam->pDevId) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_ALREADY_CREATED;
}
}
hwiFormNode = (HwiHandleInfo *)LOS_MemAlloc(m_aucSysMem0, sizeof(HwiHandleInfo));
if (hwiFormNode == NULL) {
HWI_UNLOCK(intSave);
return LOS_ERRNO_HWI_NO_MEMORY;
}
hwiForm->respCount = 0;
if (irqParam != NULL) {
hwiFormNode->registerInfo = OsHwiCpIrqParam(irqParam);
if (hwiFormNode->registerInfo == (HWI_ARG_T)NULL) {
HWI_UNLOCK(intSave);
(VOID) LOS_MemFree(m_aucSysMem0, hwiFormNode);
return LOS_ERRNO_HWI_NO_MEMORY;
}
} else {
hwiFormNode->registerInfo = 0;
}
hwiFormNode->hook = hwiHandler;
hwiFormNode->next = (struct tagHwiHandleForm *)NULL;
hwiForm->next = hwiFormNode;
head->shareMode = modeResult;
HWI_UNLOCK(intSave);
return LOS_OK;
}
#endif
size_t IntActive()
{
size_t intCount;
UINT32 intSave = LOS_IntLock();
intCount = g_intCount[ArchCurrCpuid()];
LOS_IntRestore(intSave);
return intCount;
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiCreate(HWI_HANDLE_T hwiNum,
HWI_PRIOR_T hwiPrio,
HWI_MODE_T hwiMode,
HWI_PROC_FUNC hwiHandler,
HWI_IRQ_PARAM_S *irqParam)
{
UINT32 ret;
HwiHandleInfo *hwiForm = NULL;
if (hwiHandler == NULL) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
if ((g_hwiOps == NULL) || (g_hwiOps->getHandleForm == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
hwiForm = g_hwiOps->getHandleForm(hwiNum);
if (hwiForm == NULL) {
return LOS_ERRNO_HWI_NUM_INVALID;
}
LOS_TRACE(HWI_CREATE, hwiNum, hwiPrio, hwiMode, (UINTPTR)hwiHandler);
ret = OsHwiCreate(hwiForm, hwiMode, hwiHandler, irqParam);
LOS_TRACE(HWI_CREATE_SHARE, hwiNum, (UINTPTR)(irqParam != NULL ? irqParam->pDevId : NULL), ret);
/* priority will be changed if setIrqPriority implemented,
* but interrupt preemption only allowed when LOSCFG_ARCH_INTERRUPT_PREEMPTION enable */
if ((ret == LOS_OK) && (g_hwiOps->setIrqPriority != NULL)) {
ret = g_hwiOps->setIrqPriority(hwiNum, hwiPrio);
if (ret != LOS_OK) {
(VOID)OsHwiDel(hwiForm, irqParam, hwiNum);
}
}
return ret;
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiDelete(HWI_HANDLE_T hwiNum, HWI_IRQ_PARAM_S *irqParam)
{
UINT32 ret;
HwiHandleInfo *hwiForm = NULL;
if ((g_hwiOps == NULL) || (g_hwiOps->getHandleForm == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
hwiForm = g_hwiOps->getHandleForm(hwiNum);
if (hwiForm == NULL) {
return LOS_ERRNO_HWI_NUM_INVALID;
}
LOS_TRACE(HWI_DELETE, hwiNum);
ret = OsHwiDel(hwiForm, irqParam, hwiNum);
LOS_TRACE(HWI_DELETE_SHARE, hwiNum, (UINTPTR)(irqParam != NULL ? irqParam->pDevId : NULL), ret);
return ret;
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiTrigger(HWI_HANDLE_T hwiNum)
{
if ((g_hwiOps == NULL) || (g_hwiOps->triggerIrq == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_TRIGGER, hwiNum);
return g_hwiOps->triggerIrq(hwiNum);
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiEnable(HWI_HANDLE_T hwiNum)
{
if ((g_hwiOps == NULL) || (g_hwiOps->enableIrq == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_ENABLE, hwiNum);
return g_hwiOps->enableIrq(hwiNum);
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiDisable(HWI_HANDLE_T hwiNum)
{
if ((g_hwiOps == NULL) || (g_hwiOps->disableIrq == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_DISABLE, hwiNum);
return g_hwiOps->disableIrq(hwiNum);
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiClear(HWI_HANDLE_T hwiNum)
{
if ((g_hwiOps == NULL) || (g_hwiOps->clearIrq == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_CLEAR, hwiNum);
return g_hwiOps->clearIrq(hwiNum);
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiSetPriority(HWI_HANDLE_T hwiNum, HWI_PRIOR_T priority)
{
if ((g_hwiOps == NULL) || (g_hwiOps->setIrqPriority == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_SETPRI, hwiNum, priority);
return g_hwiOps->setIrqPriority(hwiNum, priority);
}
#ifdef LOSCFG_KERNEL_SMP
LITE_OS_SEC_TEXT UINT32 LOS_HwiSetAffinity(HWI_HANDLE_T hwiNum, UINT32 cpuMask)
{
if ((g_hwiOps == NULL) || (g_hwiOps->setIrqCpuAffinity == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_SETAFFINITY, hwiNum, cpuMask);
return g_hwiOps->setIrqCpuAffinity(hwiNum, cpuMask);
}
LITE_OS_SEC_TEXT UINT32 LOS_HwiSendIpi(HWI_HANDLE_T hwiNum, UINT32 cpuMask)
{
if ((g_hwiOps == NULL) || (g_hwiOps->sendIpi == NULL)) {
return LOS_ERRNO_HWI_PROC_FUNC_NULL;
}
LOS_TRACE(HWI_SENDIPI, hwiNum, cpuMask);
return g_hwiOps->sendIpi(cpuMask, hwiNum);
}
#endif
#ifdef LOSCFG_KERNEL_LOWPOWER
LITE_OS_SEC_TEXT_MINOR VOID LOS_IntWakeupHookReg(WAKEUPFROMINTHOOK hook)
{
g_intWakeupHook = hook;
}
#endif
/* Initialization of the hardware interrupt */
LITE_OS_SEC_TEXT_INIT VOID OsHwiInit(VOID)
{
ArchIrqInit();
return;
}
#ifdef __cplusplus
#if __cplusplus
}
#endif /* __cplusplus */
#endif /* __cplusplus */