mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

targets/TARGET_Cypress/TARGET_PSOC6/gpio_irq_api.c

Committer:
AnnaBridge
Date:
2019-02-20
Revision:
189:f392fc9709a3
Parent:
188:bcfe06ba3d64

File content as of revision 189:f392fc9709a3:

/*
 * mbed Microcontroller Library
 * Copyright (c) 2017-2018 Future Electronics
 * Copyright (c) 2018-2019 Cypress Semiconductor Corporation
 * 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.
 */

#include "cmsis.h"
#include "device.h"
#include "mbed_assert.h"
#include "gpio_object.h"
#include "gpio_irq_api.h"
#include "psoc6_utils.h"
#include "cy_sysint.h"


#define NUM_GPIO_PORTS              IOSS_GPIO_GPIO_PORT_NR
#define NUM_GPIO_PORT_PINS          8
#define GPIO_DEFAULT_IRQ_PRIORITY   5

static gpio_irq_t *irq_objects[NUM_GPIO_PORTS][NUM_GPIO_PORT_PINS] = {NULL};

typedef struct irq_port_info_s {
    IRQn_Type   irqn;
    uint32_t    pin_mask;
} irq_port_info_t;

static irq_port_info_t irq_port_usage[NUM_GPIO_PORTS] = {{0, 0},};

static void gpio_irq_dispatcher(uint32_t port_id)
{
    uint32_t pin;
    gpio_irq_event event;
    GPIO_PRT_Type *port = Cy_GPIO_PortToAddr(port_id);

    for (pin = 0; pin < NUM_GPIO_PORT_PINS; ++pin) {
        if (Cy_GPIO_GetInterruptStatusMasked(port, pin)) {
            gpio_irq_t *obj = irq_objects[port_id][pin];
            MBED_ASSERT(obj);
            Cy_GPIO_ClearInterrupt(port, pin);
            /*    event = (obj->mode == IRQ_FALL)? IRQ_FALL : IRQ_RISE; */

            /* Read pin to determine the edge to support "both" mode */
            event = (0UL != Cy_GPIO_Read(port, pin)) ? IRQ_RISE : IRQ_FALL;
            if (0UL == (obj->mode & event)) {
                /* In case of very short pulse, actually both edges are occurred, so indicating only the supported one */
                event = obj->mode;
            } /* Otherwise the determined edge is supported (0UL != (obj->mode & event)), so indicating it as is */

            obj->handler(obj->id_arg, event);
        }
    }
}

static void gpio_irq_dispatcher_port0(void)
{
    gpio_irq_dispatcher(0);
}

static void gpio_irq_dispatcher_port1(void)
{
    gpio_irq_dispatcher(1);
}

static void gpio_irq_dispatcher_port2(void)
{
    gpio_irq_dispatcher(2);
}

static void gpio_irq_dispatcher_port3(void)
{
    gpio_irq_dispatcher(3);
}

static void gpio_irq_dispatcher_port4(void)
{
    gpio_irq_dispatcher(4);
}

static void gpio_irq_dispatcher_port5(void)
{
    gpio_irq_dispatcher(5);
}

static void gpio_irq_dispatcher_port6(void)
{
    gpio_irq_dispatcher(6);
}

static void gpio_irq_dispatcher_port7(void)
{
    gpio_irq_dispatcher(7);
}

static void gpio_irq_dispatcher_port8(void)
{
    gpio_irq_dispatcher(8);
}

static void gpio_irq_dispatcher_port9(void)
{
    gpio_irq_dispatcher(9);
}

static void gpio_irq_dispatcher_port10(void)
{
    gpio_irq_dispatcher(10);
}

static void gpio_irq_dispatcher_port11(void)
{
    gpio_irq_dispatcher(11);
}

static void gpio_irq_dispatcher_port12(void)
{
    gpio_irq_dispatcher(12);
}

static void gpio_irq_dispatcher_port13(void)
{
    gpio_irq_dispatcher(13);
}

static void gpio_irq_dispatcher_port14(void)
{
    gpio_irq_dispatcher(14);
}

static void (*irq_dispatcher_table[])(void) = {
    gpio_irq_dispatcher_port0,
    gpio_irq_dispatcher_port1,
    gpio_irq_dispatcher_port2,
    gpio_irq_dispatcher_port3,
    gpio_irq_dispatcher_port4,
    gpio_irq_dispatcher_port5,
    gpio_irq_dispatcher_port6,
    gpio_irq_dispatcher_port7,
    gpio_irq_dispatcher_port8,
    gpio_irq_dispatcher_port9,
    gpio_irq_dispatcher_port10,
    gpio_irq_dispatcher_port11,
    gpio_irq_dispatcher_port12,
    gpio_irq_dispatcher_port13,
    gpio_irq_dispatcher_port14
};


static IRQn_Type gpio_irq_allocate_channel(gpio_irq_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
    obj->cm0p_irq_src = ioss_interrupts_gpio_0_IRQn + obj->port_id;
    return cy_m0_nvic_allocate_channel(CY_GPIO_IRQN_ID + obj->port_id);
#else
    return (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + obj->port_id);
#endif // M0
}

static void gpio_irq_release_channel(IRQn_Type channel, uint32_t port_id)
{
#if defined (TARGET_MCU_PSOC6_M0)
    cy_m0_nvic_release_channel(channel, CY_GPIO_IRQN_ID + port_id);
#endif //M0
}

static int gpio_irq_setup_channel(gpio_irq_t *obj)
{
    cy_stc_sysint_t irq_config;

    if (irq_port_usage[obj->port_id].pin_mask == 0) {
        IRQn_Type irqn = gpio_irq_allocate_channel(obj);
        if (irqn < 0) {
            return (-1);
        }
        irq_port_usage[obj->port_id].irqn = irqn;
        // Configure NVIC
        irq_config.intrPriority = GPIO_DEFAULT_IRQ_PRIORITY;
        irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
        irq_config.cm0pSrc = obj->cm0p_irq_src;
#endif
        if (Cy_SysInt_Init(&irq_config, irq_dispatcher_table[obj->port_id]) != CY_SYSINT_SUCCESS) {
            return (-1);
        }

        irq_port_usage[obj->port_id].pin_mask |= (1 << obj->pin);
        gpio_irq_enable(obj);
        NVIC_EnableIRQ(irqn);
    }

    return 0;
}

int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler, uint32_t id)
{
    if (pin != NC) {
        obj->port_id = CY_PORT(pin);
        obj->port = Cy_GPIO_PortToAddr(obj->port_id);
        obj->pin = CY_PIN(pin);
        if (obj->pin > NUM_GPIO_PORT_PINS) {
            MBED_ASSERT("Invalid pin ID!");
            return (-1);
        }
        obj->handler = handler;
        obj->id_arg = id;
        /* Save reference to current object */
        irq_objects[obj->port_id][obj->pin] = obj;

        return gpio_irq_setup_channel(obj);
    } else {
        return (-1);
    }
}

void gpio_irq_free(gpio_irq_t *obj)
{
    gpio_irq_disable(obj);
    // TODO: Need atomicity for the following operations.
    NVIC_DisableIRQ(irq_port_usage[obj->port_id].irqn);
    irq_port_usage[obj->port_id].pin_mask &= ~(1 << obj->pin);
    if (irq_port_usage[obj->port_id].pin_mask == 0) {
        gpio_irq_release_channel(irq_port_usage[obj->port_id].irqn, obj->port_id);
        return;
    }
    NVIC_EnableIRQ(irq_port_usage[obj->port_id].irqn);
}

void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
    if (enable) {
        if (event == IRQ_RISE) {
            if (obj->mode == IRQ_FALL) {
                obj->mode += IRQ_RISE;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_BOTH);
            } else {
                obj->mode = IRQ_RISE;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_RISING);
            }
        } else if (event == IRQ_FALL) {
            if (obj->mode == IRQ_RISE) {
                obj->mode += IRQ_FALL;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_BOTH);
            } else {
                obj->mode = IRQ_FALL;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_FALLING);
            }
        } else {
            obj->mode = IRQ_NONE;
            Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
        }
    } else if (obj->mode != IRQ_NONE) {
        if (event == IRQ_RISE) {
            if (obj->mode == IRQ_RISE) {
                obj->mode = IRQ_NONE;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
            } else {
                obj->mode = IRQ_FALL;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_FALLING);
            }
        } else if (event == IRQ_FALL) {
            if (obj->mode == IRQ_FALL) {
                obj->mode = IRQ_NONE;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
            } else {
                obj->mode = IRQ_RISE;
                Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_RISING);
            }
        } else {
            obj->mode = IRQ_NONE;
            Cy_GPIO_SetInterruptEdge(obj->port, obj->pin, CY_GPIO_INTR_DISABLE);
        }
    }
}

void gpio_irq_enable(gpio_irq_t *obj)
{
    Cy_GPIO_SetInterruptMask(obj->port, obj->pin, 1);
}

void gpio_irq_disable(gpio_irq_t *obj)
{
    Cy_GPIO_SetInterruptMask(obj->port, obj->pin, 0);
}