Some checks failed
Basic Checks / license-check (push) Has been cancelled
Basic Checks / include-check (push) Has been cancelled
Basic Checks / style-check (push) Has been cancelled
Basic Checks / docs-check (push) Has been cancelled
Basic Checks / python-tests (push) Has been cancelled
Basic Checks / pin-validation (push) Has been cancelled
Basic Checks / cmake-checks (push) Has been cancelled
Basic Checks / frozen-tools-check (push) Has been cancelled
Publish or Update docker image for head of branch / prepare-tags (push) Has been cancelled
Release or update docker image for a released mbed-os version / prepare-tags (push) Has been cancelled
Publish or Update docker image for head of branch / build-container (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/amd64) (push) Has been cancelled
Publish or Update docker image for head of branch / test-container (linux/arm64) (push) Has been cancelled
Publish or Update docker image for head of branch / deploy-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / build-container (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/amd64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / test-container (linux/arm64) (push) Has been cancelled
Release or update docker image for a released mbed-os version / deploy-container (push) Has been cancelled
Prune temporary docker images / prune-images (push) Has been cancelled
399 lines
15 KiB
C
399 lines
15 KiB
C
/***************************************************************************//**
|
|
* \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) */
|