Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / source / cyhal_gpio.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 14 KB Fix file modes.
/***************************************************************************//**
* \file cyhal_gpio.c
*
* Description:
* Provides a high level interface for interacting with the Cypress GPIO. This is
* a wrapper around the lower level PDL API.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 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 "cyhal_gpio_impl.h"
#include "cyhal_interconnect.h"
#include "cyhal_system.h"
#include "cyhal_hwmgr.h"

#if defined(CY_IP_MXS40IOSS) || defined(CY_IP_M0S8IOSS)

#if defined(__cplusplus)
extern "C" {
#endif /* __cplusplus */


/*******************************************************************************
*       Internal
*******************************************************************************/
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
#define _CYHAL_GPIO_DIRECTION_OUTPUT_MASK        (0x07UL)   /**< Mask to disable the input buffer */
#elif defined(COMPONENT_CAT2)
#define _CYHAL_GPIO_DIRECTION_OUTPUT_MASK        (0x08UL)   /**< Mask to disable the input buffer */
#endif

/* Callback array for GPIO interrupts */
static cyhal_gpio_event_callback_t _cyhal_gpio_callbacks[IOSS_GPIO_GPIO_PORT_NR][CY_GPIO_PINS_MAX];
static void *_cyhal_gpio_callback_args[IOSS_GPIO_GPIO_PORT_NR][CY_GPIO_PINS_MAX];
#if defined(CY_IP_MXS40IOSS)
static cyhal_source_t _cyhal_gpio_source_signals[sizeof(cyhal_pin_map_peri_tr_io_output)/sizeof(cyhal_resource_pin_mapping_t)] = { CYHAL_TRIGGER_CPUSS_ZERO };
#endif

#if defined(CY_DEVICE_PMG1S3)
/* PMG1-S3: Work-around as device does not support any GPIO port specific interrupts. */
#define ioss_interrupts_gpio_0_IRQn     (ioss_interrupt_gpio_IRQn)
#endif

/*******************************************************************************
*       Internal Interrupt Service Routine
*******************************************************************************/

static void _cyhal_gpio_irq_handler(void)
{
    IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
    uint32_t port = (uint32_t)(irqn - ioss_interrupts_gpio_0_IRQn);
    uint32_t intr_cause = 1 << port;
#if defined(COMPONENT_CAT2)
    if (irqn == ioss_interrupt_gpio_IRQn)
    {
        intr_cause = Cy_GPIO_GetInterruptCause();
    }
#endif

    while(intr_cause != 0)
    {
        uint32_t curr_port = 31U - __CLZ(intr_cause);
        GPIO_PRT_Type *portAddr = Cy_GPIO_PortToAddr(curr_port);
        for (uint8_t cnt = 0u; cnt < CY_GPIO_PINS_MAX; cnt++)
        {
            /* Each supported architecture much have a way to check the interrupt status of a pin on a port. */
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
            if (Cy_GPIO_GetInterruptStatusMasked(portAddr, cnt))
            {
#elif defined(COMPONENT_CAT2)
            if (Cy_GPIO_GetInterruptStatus(portAddr, cnt))
            {
#else
    #error "Unsupported architecture"
#endif
                if (_cyhal_gpio_callbacks[curr_port][cnt] != NULL)
                {
                    /* Call registered callbacks here */
                    cyhal_gpio_event_t event, edge = (cyhal_gpio_event_t)Cy_GPIO_GetInterruptEdge(portAddr, cnt);
                    switch (edge)
                    {
                        case CYHAL_GPIO_IRQ_RISE:
                        case CYHAL_GPIO_IRQ_FALL:
                            event = edge;
                            break;
                        default:
                            event = Cy_GPIO_Read(portAddr, cnt) != 0 ? CYHAL_GPIO_IRQ_RISE : CYHAL_GPIO_IRQ_FALL;
                            break;
                    }
                    (void)(_cyhal_gpio_callbacks[curr_port][cnt])(_cyhal_gpio_callback_args[curr_port][cnt], event);
                }
                Cy_GPIO_ClearInterrupt(portAddr, cnt);
            }
        }
        intr_cause &= ~(1 << curr_port);
    }
}

static uint32_t _cyhal_gpio_convert_drive_mode(cyhal_gpio_drive_mode_t drive_mode, cyhal_gpio_direction_t direction)
{
    uint32_t drvMode;
    switch (drive_mode)
    {
        /* For DRIVE_NONE and DRIVE_ANALOG: Return immediately so drvMode is
         * not modified after switch statement based on direction as direction
         * does not make sense for input only drive modes */
        case CYHAL_GPIO_DRIVE_NONE:
            drvMode = CY_GPIO_DM_HIGHZ;
            return drvMode;
        case CYHAL_GPIO_DRIVE_ANALOG:
            drvMode = CY_GPIO_DM_ANALOG;
            return drvMode;
        case CYHAL_GPIO_DRIVE_PULLUP:
            drvMode = CY_GPIO_DM_PULLUP;
            break;
        case CYHAL_GPIO_DRIVE_PULLDOWN:
            drvMode = CY_GPIO_DM_PULLDOWN;
            break;
        case CYHAL_GPIO_DRIVE_OPENDRAINDRIVESLOW:
            drvMode = CY_GPIO_DM_OD_DRIVESLOW;
            break;
        case CYHAL_GPIO_DRIVE_OPENDRAINDRIVESHIGH:
            drvMode = CY_GPIO_DM_OD_DRIVESHIGH;
            break;
        case CYHAL_GPIO_DRIVE_STRONG:
            drvMode = CY_GPIO_DM_STRONG;
            break;
        case CYHAL_GPIO_DRIVE_PULLUPDOWN:
            drvMode = CY_GPIO_DM_PULLUP_DOWN;
            break;
        case CYHAL_GPIO_DRIVE_PULL_NONE:
            if (direction == CYHAL_GPIO_DIR_OUTPUT || direction == CYHAL_GPIO_DIR_BIDIRECTIONAL)
            {
                drvMode = CY_GPIO_DM_STRONG;
            }
            else
            {
                drvMode = CY_GPIO_DM_HIGHZ;
            }
            break;
        default:
            CY_ASSERT(false);
            drvMode = CY_GPIO_DM_HIGHZ;
    }

    if (direction == CYHAL_GPIO_DIR_OUTPUT)
    {
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
        drvMode &= _CYHAL_GPIO_DIRECTION_OUTPUT_MASK;
#elif defined(COMPONENT_CAT2)
        drvMode |= _CYHAL_GPIO_DIRECTION_OUTPUT_MASK;
#endif
    }
    return drvMode;
}

/*******************************************************************************
*       HAL Implementation
*******************************************************************************/

cy_rslt_t cyhal_gpio_init(cyhal_gpio_t pin, cyhal_gpio_direction_t direction, cyhal_gpio_drive_mode_t drive_mode, bool init_val)
{
    /* Mbed creates GPIOs for pins that are dedicated to other peripherals in some cases. */
#ifndef __MBED__
    cyhal_resource_inst_t pinRsc = _cyhal_utils_get_gpio_resource(pin);
    cy_rslt_t status = cyhal_hwmgr_reserve(&pinRsc);
#else
    cy_rslt_t status = CY_RSLT_SUCCESS;
#endif

    if (status == CY_RSLT_SUCCESS)
    {
        uint32_t pdl_drive_mode = _cyhal_gpio_convert_drive_mode(drive_mode, direction);
        Cy_GPIO_Pin_FastInit(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), pdl_drive_mode, init_val, HSIOM_SEL_GPIO);
    }

    return status;
}

void cyhal_gpio_free(cyhal_gpio_t pin)
{
    if (pin != CYHAL_NC_PIN_VALUE)
    {
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
        Cy_GPIO_SetInterruptMask(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), 0);
#elif defined(COMPONENT_CAT2)
        Cy_GPIO_SetInterruptEdge(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), CY_GPIO_INTR_DISABLE);
#endif
        _cyhal_gpio_callbacks[CYHAL_GET_PORT(pin)][CYHAL_GET_PIN(pin)] = NULL;
        _cyhal_gpio_callback_args[CYHAL_GET_PORT(pin)][CYHAL_GET_PIN(pin)] = NULL;

        (void)cyhal_gpio_disable_output(pin);
        #if defined(CY_IP_MXS40IOSS)
        for(uint8_t i = 0; i < (uint8_t)(sizeof(cyhal_pin_map_peri_tr_io_output)/sizeof(cyhal_resource_pin_mapping_t)); i++)
        {
            cyhal_resource_pin_mapping_t mapping = cyhal_pin_map_peri_tr_io_output[i];
            if(mapping.pin == pin)
            {
                if (CYHAL_TRIGGER_CPUSS_ZERO != _cyhal_gpio_source_signals[i])
                {
                    (void)cyhal_gpio_disconnect_digital(pin, _cyhal_gpio_source_signals[i]);
                }
            }
        }
        #endif

        Cy_GPIO_Pin_FastInit(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), CY_GPIO_DM_ANALOG, 0UL, HSIOM_SEL_GPIO);
        /* Do not attempt to free the resource we don't reserve in mbed. */
#ifndef __MBED__
        cyhal_resource_inst_t pinRsc = _cyhal_utils_get_gpio_resource(pin);
        cyhal_hwmgr_free(&pinRsc);
#endif
    }
}

cy_rslt_t cyhal_gpio_configure(cyhal_gpio_t pin, cyhal_gpio_direction_t direction, cyhal_gpio_drive_mode_t drive_mode)
{
    uint32_t pdldrive_mode = _cyhal_gpio_convert_drive_mode(drive_mode, direction);
    Cy_GPIO_SetDrivemode(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), pdldrive_mode);

    return CY_RSLT_SUCCESS;
}

void cyhal_gpio_register_callback(cyhal_gpio_t pin, cyhal_gpio_event_callback_t callback, void *callback_arg)
{
    uint32_t savedIntrStatus = cyhal_system_critical_section_enter();
    _cyhal_gpio_callbacks[CYHAL_GET_PORT(pin)][CYHAL_GET_PIN(pin)] = callback;
    _cyhal_gpio_callback_args[CYHAL_GET_PORT(pin)][CYHAL_GET_PIN(pin)] = callback_arg;
    cyhal_system_critical_section_exit(savedIntrStatus);
}

void cyhal_gpio_enable_event(cyhal_gpio_t pin, cyhal_gpio_event_t event, uint8_t intr_priority, bool enable)
{
    Cy_GPIO_ClearInterrupt(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin));
#if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B)
    Cy_GPIO_SetInterruptEdge(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), (uint32_t)event);
    Cy_GPIO_SetInterruptMask(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), (uint32_t)enable);
    IRQn_Type irqn = (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + CYHAL_GET_PORT(pin));
#elif defined(COMPONENT_CAT2)
    uint32_t intr_val = enable ? (uint32_t)event : CY_GPIO_INTR_DISABLE;
    Cy_GPIO_SetInterruptEdge(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), intr_val);
    IRQn_Type irqn = (ioss_interrupts_gpio_0_IRQn + CYHAL_GET_PORT(pin) < ioss_interrupt_gpio_IRQn)
                    ? (IRQn_Type)(ioss_interrupts_gpio_0_IRQn + CYHAL_GET_PORT(pin))
                    : (IRQn_Type)(ioss_interrupt_gpio_IRQn);
#endif
    /* Only enable if it's not already enabled */
    if (NVIC_GetEnableIRQ(irqn) == 0)
    {
        cy_stc_sysint_t irqCfg = {irqn, intr_priority};

        Cy_SysInt_Init(&irqCfg, _cyhal_gpio_irq_handler);
        NVIC_EnableIRQ(irqn);
    }
    else
    {
        NVIC_SetPriority(irqn, intr_priority);
    }
}

#if defined(CY_IP_MXS40IOSS)
cy_rslt_t cyhal_gpio_connect_digital(cyhal_gpio_t pin, cyhal_source_t source, cyhal_signal_type_t type)
{
    // Search through cyhal_pin_map_peri_tr_io_output to determine if a trigger
    // can be routed to it. (Note: tr_io_output refers to trigger mux output,
    // not peripheral output. A trigger mux output is routed to a peripheral
    // input.)
    for(uint8_t i = 0; i < (uint8_t)(sizeof(cyhal_pin_map_peri_tr_io_output)/sizeof(cyhal_resource_pin_mapping_t)); i++)
    {
        cyhal_resource_pin_mapping_t mapping = cyhal_pin_map_peri_tr_io_output[i];
        if(mapping.pin == pin)
        {
            Cy_GPIO_SetHSIOM(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), mapping.hsiom);

            cyhal_dest_t dest = (cyhal_dest_t)(CYHAL_TRIGGER_PERI_TR_IO_OUTPUT0 + (uint32_t)(mapping.inst));

            cy_rslt_t rslt = _cyhal_connect_signal(source, dest, type);
            if (CY_RSLT_SUCCESS == rslt)
            {
                _cyhal_gpio_source_signals[i] = source;
            }
            return rslt;
        }
    }

    return CYHAL_GPIO_RSLT_ERR_NO_INPUT_SIGNAL;
}

cy_rslt_t cyhal_gpio_enable_output(cyhal_gpio_t pin, cyhal_source_t *source)
{
    // Search through cyhal_pin_map_peri_tr_io_input to determine if pin can be
    // used to drive a trigger line. (Note: tr_io_input refers to trigger mux
    // input, not peripheral input. A peripheral output is routed to a trigger
    // mux input.)
    for(uint8_t i = 0; i < (uint8_t)(sizeof(cyhal_pin_map_peri_tr_io_input)/sizeof(cyhal_resource_pin_mapping_t)); i++)
    {
        cyhal_resource_pin_mapping_t mapping = cyhal_pin_map_peri_tr_io_input[i];
        if(mapping.pin == pin)
        {
            Cy_GPIO_SetHSIOM(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), mapping.hsiom);

            *source = (cyhal_source_t)(CYHAL_TRIGGER_PERI_TR_IO_INPUT0 + (uint32_t)(mapping.inst));

            return CY_RSLT_SUCCESS;
        }
    }

    return CYHAL_GPIO_RSLT_ERR_NO_OUTPUT_SIGNAL;
}

cy_rslt_t cyhal_gpio_disconnect_digital(cyhal_gpio_t pin, cyhal_source_t source)
{
    for(uint8_t i = 0; i < (uint8_t)(sizeof(cyhal_pin_map_peri_tr_io_output)/sizeof(cyhal_resource_pin_mapping_t)); i++)
    {
        cyhal_resource_pin_mapping_t mapping = cyhal_pin_map_peri_tr_io_output[i];
        if(mapping.pin == pin)
        {
            Cy_GPIO_SetHSIOM(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), HSIOM_SEL_GPIO);

            cyhal_dest_t dest = (cyhal_dest_t)(CYHAL_TRIGGER_PERI_TR_IO_OUTPUT0 + (uint32_t)(mapping.inst));

            cy_rslt_t rslt = _cyhal_disconnect_signal(source, dest);
            if (CY_RSLT_SUCCESS == rslt)
            {
                _cyhal_gpio_source_signals[i] = CYHAL_TRIGGER_CPUSS_ZERO;
            }
            return rslt;
        }
    }

    return CYHAL_GPIO_RSLT_ERR_NO_INPUT_SIGNAL;
}

cy_rslt_t cyhal_gpio_disable_output(cyhal_gpio_t pin)
{
    for(uint8_t i = 0; i < (uint8_t)(sizeof(cyhal_pin_map_peri_tr_io_input)/sizeof(cyhal_resource_pin_mapping_t)); i++)
    {
        cyhal_resource_pin_mapping_t mapping = cyhal_pin_map_peri_tr_io_input[i];
        if(mapping.pin == pin)
        {
            Cy_GPIO_SetHSIOM(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), HSIOM_SEL_GPIO);

            return CY_RSLT_SUCCESS;
        }
    }

    return CYHAL_GPIO_RSLT_ERR_NO_OUTPUT_SIGNAL;
}
#elif defined(CY_IP_M0S8IOSS)
// M0S8 devices do not have gpio triggers
cy_rslt_t cyhal_gpio_connect_digital(cyhal_gpio_t pin, cyhal_source_t source, cyhal_signal_type_t type)
{
    CY_UNUSED_PARAMETER(pin);
    CY_UNUSED_PARAMETER(source);
    CY_UNUSED_PARAMETER(type);
    return CYHAL_INTERCONNECT_RSLT_INVALID_CONNECTION;
}

cy_rslt_t cyhal_gpio_enable_output(cyhal_gpio_t pin, cyhal_source_t *source)
{
    CY_UNUSED_PARAMETER(pin);
    CY_UNUSED_PARAMETER(source);
    return CYHAL_INTERCONNECT_RSLT_INVALID_CONNECTION;
}

cy_rslt_t cyhal_gpio_disconnect_digital(cyhal_gpio_t pin, cyhal_source_t source)
{
    CY_UNUSED_PARAMETER(pin);
    CY_UNUSED_PARAMETER(source);
    return CYHAL_INTERCONNECT_RSLT_INVALID_CONNECTION;
}

cy_rslt_t cyhal_gpio_disable_output(cyhal_gpio_t pin)
{
    CY_UNUSED_PARAMETER(pin);
    return CYHAL_INTERCONNECT_RSLT_INVALID_CONNECTION;
}
#endif

#if defined(__cplusplus)
}
#endif /* __cplusplus */

#endif /* defined(CY_IP_MXS40IOSS) || defined(CY_IP_M0S8IOSS) */