Newer
Older
mbed-os / targets / TARGET_Samsung / TARGET_SIDK_S1SBP6A / gpio_irq_api.c
/****************************************************************************
 *
 * Copyright 2020 Samsung Electronics All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 *
 * 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.
 *
 ****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <stddef.h>
#include "cmsis.h"
#include "gpio_irq_api.h"
#include "mbed_error.h"

/****************************************************************************
 * Pre-processor definitions
 ****************************************************************************/
#define CHANNEL_NUM                     48
#define MAX_PIN_PER_PORT                16
static gpio_irq_handler irq_handler;
static uintptr_t channel_contexts[CHANNEL_NUM];
static gpio_irq_event pins_event[CHANNEL_NUM];

static inline void handle_gpio_irq(uint32_t port)
{
    int i;
    uint32_t pin_name = port << 4;

    BP_GPIO_TypeDef *gpio_base = (BP_GPIO_TypeDef *)GPIO_PORT_BASE(port);

    for (i = 0; i < MAX_PIN_PER_PORT; i++) {
        if (gpio_base->INTSTAT & (1u << i)) {
            pin_name += i;
            NVIC_ClearPendingIRQ(PORT0_0_IRQn + port * 16 + i);
            gpio_base->INTCLR = 1u << i;
            irq_handler(channel_contexts[pin_name], pins_event[pin_name]);
            break;
        }
    }
}

void gpio0_irq(void)
{
    handle_gpio_irq(0);
    NVIC_ClearPendingIRQ(PORT0_ALL_IRQn);
}
void gpio1_irq(void)
{
    handle_gpio_irq(1);
    NVIC_ClearPendingIRQ(PORT1_ALL_IRQn);
}
void gpio2_irq(void)
{
    handle_gpio_irq(2);
    NVIC_ClearPendingIRQ(PORT2_ALL_IRQn);
}

int gpio_irq_init(gpio_irq_t *obj, PinName pin,
                  gpio_irq_handler handler, uintptr_t context)
{
    if (pin == (PinName)NC) {
        return -1;
    }

    obj->pin = pin;
    irq_handler = handler;
    channel_contexts[pin] = context;

    bp6a_gpio_set_dir(BP6A_PORT_IDX(obj->pin), BP6A_PIN_IDX(obj->pin), true);
    return 0;
}

void gpio_irq_free(gpio_irq_t *obj)
{
    channel_contexts[obj->pin] = 0;
    gpio_irq_disable(obj);
}

void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
    pins_event[obj->pin] = event;

    if (event == IRQ_NONE) {
        return;
    }

    if (event == IRQ_RISE) {
        bp6a_gpio_set_int_type(BP6A_PORT_IDX(obj->pin), BP6A_PIN_IDX(obj->pin),
                               true, true);
    } else {
        bp6a_gpio_set_int_type(BP6A_PORT_IDX(obj->pin), BP6A_PIN_IDX(obj->pin),
                               false, true);
    }
    if (enable) {
        gpio_irq_enable(obj);
    } else {
        gpio_irq_disable(obj);
    }
}

void gpio_irq_enable(gpio_irq_t *obj)
{
    uint32_t port = BP6A_PORT_IDX(obj->pin);

    bp6a_gpio_set_int(port, BP6A_PIN_IDX(obj->pin), true);

    void (*port_irq)(void) = NULL;

    switch (port) {
        case 0:
            port_irq = &gpio0_irq;
            break;
        case 1:
            port_irq = &gpio1_irq;
            break;
        case 2:
            port_irq = &gpio2_irq;
            break;
    }

    NVIC_SetVector((IRQn_Type)(PORT0_0_IRQn + port * 16 + BP6A_PIN_IDX(obj->pin)), (uint32_t)port_irq);

#if defined (__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U)
    SCB_InvalidateICache();
#endif

    NVIC_EnableIRQ((IRQn_Type)(PORT0_0_IRQn + port * 16 + BP6A_PIN_IDX(obj->pin)));
}

void gpio_irq_disable(gpio_irq_t *obj)
{
    uint32_t port_n =  BP6A_PORT_IDX(obj->pin);
    uint32_t pin_n = BP6A_PIN_IDX(obj->pin);

    NVIC_ClearPendingIRQ((IRQn_Type)(PORT0_ALL_IRQn + port_n));
    bp6a_gpio_clear_pend(port_n, pin_n);

    NVIC_DisableIRQ((IRQn_Type)(PORT0_ALL_IRQn + port_n));
    bp6a_gpio_set_int(port_n, pin_n, false);
}