Newer
Older
mbed-os / targets / TARGET_ARM_SSG / TARGET_CM3DS_MPS2 / gpio_irq_api.c
/* mbed Microcontroller Library
 * Copyright (c) 2006-2018 ARM Limited
 * 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 <stddef.h>
#include "objects.h"
#include "gpio_irq_api.h"
#include "mbed_error.h"

#define ERROR_BIT_NUMBER 0xFF

struct gpio_irq_handler_t {
    gpio_irq_handler handler;
    gpio_irq_event event;
    uintptr_t context;
};

/* Handlers registered */
static struct gpio_irq_handler_t gpio_irq[PINS_NUMBER];

/*
 * Return the bit number of the lowest significant bit set to 1 or
 * ERROR_BIT_NUMBER if there is no bit set.
 */
static uint8_t find_first_set_bit(uint32_t word)
{
    uint8_t bit_number = 0;

    if (word == 0) {
        return ERROR_BIT_NUMBER;
    }

    while (((word >> bit_number++) & 1UL) == 0);

    return (bit_number - 1);
}

static void handler(struct arm_gpio_dev_t* dev, uint32_t gpio_number,
                    uint32_t exp_pin_base)
{
    uint32_t irq_status = 0;
    /* Pin that triggered the IRQ in this GPIO */
    uint8_t pin_number;
    /* Pin number in the expension port */
    uint8_t exp_pin_number;

    (void)arm_gpio_get_irq_status(dev, ARM_GPIO_ACCESS_PORT, ARG_NOT_USED,
                                  &irq_status);

    pin_number = find_first_set_bit(irq_status);
    if (pin_number == ERROR_BIT_NUMBER) {
        /* There was no IRQ */
        return;
    }

    (void)arm_gpio_clear_interrupt(dev, pin_number);

    exp_pin_number = exp_pin_base + pin_number;

    gpio_irq[exp_pin_number].handler(gpio_irq[exp_pin_number].context,
                                     gpio_irq[exp_pin_number].event);
}

#ifdef ARM_GPIO0
void PORT0_IRQHandler(void)
{
    handler(&ARM_GPIO0_DEV, GPIO0_NUMBER, EXP_PIN_BASE0);
}
#endif /* ARM_GPIO0 */

#ifdef ARM_GPIO1
void PORT1_ALL_IRQHandler(void)
{
    handler(&ARM_GPIO1_DEV, GPIO1_NUMBER, EXP_PIN_BASE1);
}
#endif /* ARM_GPIO1 */

#ifdef ARM_GPIO2
void PORT2_ALL_IRQHandler(void)
{
    handler(&ARM_GPIO2_DEV, GPIO2_NUMBER, EXP_PIN_BASE2);
}
#endif /* ARM_GPIO2 */

#ifdef ARM_GPIO3
void PORT3_ALL_IRQHandler(void)
{
    handler(&ARM_GPIO3_DEV, GPIO3_NUMBER, EXP_PIN_BASE3);
}
#endif /* ARM_GPIO3 */

int gpio_irq_init(gpio_irq_t *obj, PinName pin, gpio_irq_handler handler,
                  uintptr_t context)
{
    struct arm_gpio_dev_t *gpio_dev;

    if (pin >= EXP0 && pin <= EXP51) {
        /* GPIO pins */
        switch (GPIO_DEV_NUMBER(pin)) {
#ifdef ARM_GPIO0
            case 0:
                gpio_dev = &ARM_GPIO0_DEV;
                obj->irq_number = PORT0_ALL_IRQn;
                break;
#endif /* ARM_GPIO0 */
#ifdef ARM_GPIO1
            case 1:
                gpio_dev = &ARM_GPIO1_DEV;
                obj->irq_number = PORT1_ALL_IRQn;
                break;
#endif /* ARM_GPIO1 */
#ifdef ARM_GPIO2
            case 2:
                gpio_dev = &ARM_GPIO2_DEV;
                obj->irq_number = PORT2_ALL_IRQn;
                break;
#endif /* ARM_GPIO2 */
#ifdef ARM_GPIO3
            case 3:
                gpio_dev = &ARM_GPIO3_DEV;
                obj->irq_number = PORT3_ALL_IRQn;
                break;
#endif /* ARM_GPIO3 */
            default:
                error("GPIO %d is not enabled", GPIO_DEV_NUMBER(pin));
                return -1;
        }

        obj->gpio_dev = gpio_dev;
        obj->pin_number = GPIO_PIN_NUMBER(pin);
        obj->exp_pin_number = pin;

        arm_gpio_init(gpio_dev);

        /* Save the handler and id into the global structure */
        gpio_irq[pin].handler = handler;
        gpio_irq[pin].context = context;

        return 0;
    } else {
        /* The pin is not concerned with GPIO IRQ */
        error("Pin %d is not a GPIO", pin);
        return -1;
    }
}

void gpio_irq_free(gpio_irq_t *obj)
{
    /* Not implemented because the device can not be uninitialized. */
}

void gpio_irq_set(gpio_irq_t *obj, gpio_irq_event event, uint32_t enable)
{
    /* Interrupt is set on an input pin on rising or falling edge */
    uint32_t flags = ARM_GPIO_PIN_ENABLE | ARM_GPIO_INPUT | ARM_GPIO_IRQ |
                     ARM_GPIO_IRQ_EDGE;

    switch (event) {
        case IRQ_RISE:
            flags |= ARM_GPIO_IRQ_ACTIVE_HIGH;
            break;
        case IRQ_FALL:
            flags |= ARM_GPIO_IRQ_ACTIVE_LOW;
            break;
        case IRQ_NONE:
            return;
    }

    (void)arm_gpio_config(obj->gpio_dev, ARM_GPIO_ACCESS_PIN, obj->pin_number,
                          flags);

    /* Record the event type of this pin */
    gpio_irq[obj->exp_pin_number].event = event;

    NVIC_EnableIRQ(obj->irq_number);

    if (enable) {
        gpio_irq_enable(obj);
    } else {
        gpio_irq_disable(obj);
    }
}

void gpio_irq_enable(gpio_irq_t *obj)
{
    (void)arm_gpio_set_interrupt(obj->gpio_dev, ARM_GPIO_ACCESS_PIN,
                                 obj->pin_number, ARM_GPIO_IRQ_ENABLE);
}

void gpio_irq_disable(gpio_irq_t *obj)
{
    (void)arm_gpio_set_interrupt(obj->gpio_dev, ARM_GPIO_ACCESS_PIN,
                                 obj->pin_number, ARM_GPIO_IRQ_DISABLE);
}