From bc638f38db46c00997431db7140e8fd32386509a Mon Sep 17 00:00:00 2001 From: Nazar Palamar Date: Fri, 25 Mar 2022 12:25:28 +0200 Subject: [PATCH] drivers: gpio: Add Infineon CAT1 GPIO driver Added initial version of Infineon CAT1 GPIO driver. Added initial version of binding file for Infineon CAT1 GPIO driver. Signed-off-by: Nazar Palamar --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.ifx_cat1 | 12 + drivers/gpio/gpio_ifx_cat1.c | 315 ++++++++++++++++++++++ dts/bindings/gpio/infineon,cat1-gpio.yaml | 25 ++ 5 files changed, 355 insertions(+) create mode 100644 drivers/gpio/Kconfig.ifx_cat1 create mode 100644 drivers/gpio/gpio_ifx_cat1.c create mode 100644 dts/bindings/gpio/infineon,cat1-gpio.yaml diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index e3ed0efa0a9..ec6761667c9 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_GPIO_TELINK_B91 gpio_b91.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_INFINEON_CAT1 gpio_ifx_cat1.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CC13XX_CC26XX gpio_cc13xx_cc26xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CC32XX gpio_cc32xx.c) zephyr_library_sources_ifdef(CONFIG_GPIO_CMSDK_AHB gpio_cmsdk_ahb.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 49253a572de..68256a7e9c9 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -103,6 +103,8 @@ source "drivers/gpio/Kconfig.imx" source "drivers/gpio/Kconfig.it8xxx2" +source "drivers/gpio/Kconfig.ifx_cat1" + source "drivers/gpio/Kconfig.intel" source "drivers/gpio/Kconfig.xec" diff --git a/drivers/gpio/Kconfig.ifx_cat1 b/drivers/gpio/Kconfig.ifx_cat1 new file mode 100644 index 00000000000..6491c754525 --- /dev/null +++ b/drivers/gpio/Kconfig.ifx_cat1 @@ -0,0 +1,12 @@ +# Infineon CAT1 GPIO configuration options + +# Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_INFINEON_CAT1 + bool "Infineon CAT1 GPIO driver" + default y + depends on DT_HAS_INFINEON_CAT1_GPIO_ENABLED + help + Enable support for Infineon CAT1 GPIO controllers. diff --git a/drivers/gpio/gpio_ifx_cat1.c b/drivers/gpio/gpio_ifx_cat1.c new file mode 100644 index 00000000000..82bf2e94152 --- /dev/null +++ b/drivers/gpio/gpio_ifx_cat1.c @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) or + * an affiliate of Cypress Semiconductor Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @brief GPIO driver for Infineon CAT1 MCU family. + * + * Note: + * - Trigger detection on pin rising or falling edge (GPIO_INT_TRIG_BOTH) + * is not supported in current version of GPIO CAT1 driver. + */ + +#define DT_DRV_COMPAT infineon_cat1_gpio + +#include +#include + +#include +#include +#include + +#include +LOG_MODULE_REGISTER(gpio_cat1, CONFIG_GPIO_LOG_LEVEL); + +/* Device config structure */ +struct gpio_cat1_config { + /* gpio_driver_config needs to be first */ + struct gpio_driver_config common; + cyhal_gpio_callback_data_t *cb_data_ptr; + GPIO_PRT_Type *regs; + uint8_t ngpios; + uint8_t intr_priority; +}; + +/* Data structure */ +struct gpio_cat1_data { + /* gpio_driver_data needs to be first */ + struct gpio_driver_data common; + + /* device's owner of this data */ + const struct device *dev; + + /* callbacks list */ + sys_slist_t callbacks; +}; + +/* Get port number by calculation difference from current port address minus + * GPIO base address divided by GPIO structure size. + */ +#define GET_PORT_NUM(dev) \ + (((uint32_t) ((const struct gpio_cat1_config *const) \ + (dev)->config)->regs - CY_GPIO_BASE) / GPIO_PRT_SECTION_SIZE) + +#define GET_DEV_OBJ_FROM_LIST(i, _) \ + DEVICE_DT_GET_OR_NULL(DT_NODELABEL(gpio_prt##i)) + +/* Map port number to device object */ +static const struct device *const port_dev_obj[IOSS_GPIO_GPIO_PORT_NR] = { + LISTIFY(15, GET_DEV_OBJ_FROM_LIST, (,)) +}; + +static int gpio_cat1_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + cy_rslt_t status; + cyhal_gpio_t gpio_pin = CYHAL_GET_GPIO(GET_PORT_NUM(dev), pin); + cyhal_gpio_drive_mode_t gpio_mode = CYHAL_GPIO_DRIVE_NONE; + cyhal_gpio_direction_t gpio_dir = CYHAL_GPIO_DIR_INPUT; + bool pin_val = false; + + switch (flags & (GPIO_INPUT | GPIO_OUTPUT)) { + case GPIO_INPUT: + gpio_dir = CYHAL_GPIO_DIR_INPUT; + + if ((flags & GPIO_PULL_UP) && (flags & GPIO_PULL_DOWN)) { + gpio_mode = CYHAL_GPIO_DRIVE_PULLUPDOWN; + } else if (flags & GPIO_PULL_UP) { + gpio_mode = CYHAL_GPIO_DRIVE_PULLUP; + pin_val = true; + } else if (flags & GPIO_PULL_DOWN) { + gpio_mode = CYHAL_GPIO_DRIVE_PULLDOWN; + } else { + gpio_mode = CYHAL_GPIO_DRIVE_NONE; + } + break; + + case GPIO_OUTPUT: + gpio_dir = CYHAL_GPIO_DIR_OUTPUT; + if (flags & GPIO_SINGLE_ENDED) { + if (flags & GPIO_LINE_OPEN_DRAIN) { + gpio_mode = CYHAL_GPIO_DRIVE_OPENDRAINDRIVESLOW; + pin_val = true; + } else { + gpio_mode = CYHAL_GPIO_DRIVE_OPENDRAINDRIVESHIGH; + pin_val = false; + } + } else { + gpio_mode = CYHAL_GPIO_DRIVE_STRONG; + pin_val = (flags & GPIO_OUTPUT_INIT_HIGH) ? true : false; + } + break; + + case GPIO_DISCONNECTED: + cyhal_gpio_free(gpio_pin); + return 0; + + default: + return -ENOTSUP; + } + + status = cyhal_gpio_init(gpio_pin, gpio_dir, gpio_mode, pin_val); + + /* If the gpio requested resource is already in use, try to free and + * initialize again + */ + if (status == CYHAL_HWMGR_RSLT_ERR_INUSE) { + cyhal_gpio_free(gpio_pin); + status = cyhal_gpio_init(gpio_pin, gpio_dir, gpio_mode, pin_val); + } + + return (status == CY_RSLT_SUCCESS) ? 0 : -EIO; +} + +static int gpio_cat1_port_get_raw(const struct device *dev, + uint32_t *value) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + *value = GPIO_PRT_IN(base); + + return 0; +} + +static int gpio_cat1_port_set_masked_raw(const struct device *dev, + uint32_t mask, uint32_t value) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + GPIO_PRT_OUT(base) = (GPIO_PRT_OUT(base) & ~mask) | (mask & value); + + return 0; +} + +static int gpio_cat1_port_set_bits_raw(const struct device *dev, + uint32_t mask) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + GPIO_PRT_OUT_SET(base) = mask; + + return 0; +} + +static int gpio_cat1_port_clear_bits_raw(const struct device *dev, + uint32_t mask) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + GPIO_PRT_OUT_CLR(base) = mask; + + return 0; +} + +static int gpio_cat1_port_toggle_bits(const struct device *dev, + uint32_t mask) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + GPIO_PRT_OUT_INV(base) = mask; + + return 0; +} + +static uint32_t gpio_cat1_get_pending_int(const struct device *dev) +{ + const struct gpio_cat1_config *const cfg = dev->config; + GPIO_PRT_Type *const base = cfg->regs; + + return GPIO_PRT_INTR_MASKED(base); +} + +static void gpio_event_callback(void *callback_arg, cyhal_gpio_event_t event) +{ + ARG_UNUSED(event); + uint32_t port_num = CYHAL_GET_PORT((uint32_t) callback_arg); + uint32_t pin_num = CYHAL_GET_PIN((uint32_t) callback_arg); + const struct device *dev = port_dev_obj[port_num]; + + /* Goes through and fires callback from a callback list */ + if (dev) { + gpio_fire_callbacks(&((struct gpio_cat1_data *const)(dev)->data)->callbacks, + dev, 1 << pin_num); + } + /* NOTE: cyhal gpio handles cleaning of interrupts */ +} + +static int gpio_cat1_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, + enum gpio_int_mode mode, enum gpio_int_trig trig) +{ + const struct gpio_cat1_config *const cfg = dev->config; + cyhal_gpio_callback_data_t *cb_data_ptr = cfg->cb_data_ptr; + cyhal_gpio_event_t event = CYHAL_GPIO_IRQ_NONE; + + /* Level interrupts (GPIO_INT_MODE_LEVEL) is not supported */ + if (mode == GPIO_INT_MODE_LEVEL) { + return -ENOTSUP; + } + + switch (trig) { + case GPIO_INT_TRIG_LOW: + event = CYHAL_GPIO_IRQ_FALL; + break; + + case GPIO_INT_TRIG_HIGH: + event = CYHAL_GPIO_IRQ_RISE; + break; + + case GPIO_INT_TRIG_BOTH: + /* Trigger detection on pin rising or falling edge (GPIO_INT_TRIG_BOTH) + * is not supported. Refer to SWINTEGRATION-696 + */ + /* event = CYHAL_GPIO_IRQ_BOTH; */ + return -ENOTSUP; + + default: + return -ENOTSUP; + } + + cyhal_gpio_t gpio_pin = CYHAL_GET_GPIO(GET_PORT_NUM(dev), pin); + + /* Find index of free callback data structure */ + uint32_t index; + + for (index = 0u; index < cfg->ngpios; index++) { + if ((cb_data_ptr[index].callback == NULL) || (cb_data_ptr[index].pin == gpio_pin)) { + break; + } + } + + if (index != cfg->ngpios) { + /* Store callback data: gpio callback and gpio device driver handle */ + cb_data_ptr[index].callback = &gpio_event_callback; + cb_data_ptr[index].callback_arg = (void *)(gpio_pin); + + /* Register/clear a callback handler for pin events */ + cyhal_gpio_register_callback(gpio_pin, (mode == GPIO_INT_MODE_DISABLED) ? + NULL : &cb_data_ptr[index]); + + /* Enable/disable the specified GPIO event */ + cyhal_gpio_enable_event(gpio_pin, event, cfg->intr_priority, + (mode == GPIO_INT_MODE_DISABLED) ? false : true); + return 0; + } else { + return -EINVAL; + } +} + +static int gpio_cat1_manage_callback(const struct device *port, + struct gpio_callback *callback, + bool set) +{ + return gpio_manage_callback(&((struct gpio_cat1_data *const)(port)->data)->callbacks, + callback, set); +} + +static const struct gpio_driver_api gpio_cat1_api = { + .pin_configure = gpio_cat1_configure, + .port_get_raw = gpio_cat1_port_get_raw, + .port_set_masked_raw = gpio_cat1_port_set_masked_raw, + .port_set_bits_raw = gpio_cat1_port_set_bits_raw, + .port_clear_bits_raw = gpio_cat1_port_clear_bits_raw, + .port_toggle_bits = gpio_cat1_port_toggle_bits, + .pin_interrupt_configure = gpio_cat1_pin_interrupt_configure, + .manage_callback = gpio_cat1_manage_callback, + .get_pending_int = gpio_cat1_get_pending_int, +}; + +static int gpio_cat1_init(const struct device *dev) +{ + ARG_UNUSED(dev); + return 0; +} + +#define GPIO_CAT1_INIT(n) \ + \ + cyhal_gpio_callback_data_t \ + _cat1_gpio##n##_cb_data[DT_INST_PROP(n, ngpios)]; \ + \ + static const struct gpio_cat1_config _cat1_gpio##n##_config = { \ + .common = { \ + .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ + }, \ + .intr_priority = DT_INST_IRQ_BY_IDX(n, 0, priority), \ + .cb_data_ptr = _cat1_gpio##n##_cb_data, \ + .ngpios = DT_INST_PROP(n, ngpios), \ + .regs = (GPIO_PRT_Type *)DT_INST_REG_ADDR(n), \ + }; \ + \ + static struct gpio_cat1_data _cat1_gpio##n##_data; \ + \ + DEVICE_DT_INST_DEFINE(n, gpio_cat1_init, NULL, \ + &_cat1_gpio##n##_data, \ + &_cat1_gpio##n##_config, POST_KERNEL, \ + CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ + &gpio_cat1_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_CAT1_INIT) diff --git a/dts/bindings/gpio/infineon,cat1-gpio.yaml b/dts/bindings/gpio/infineon,cat1-gpio.yaml new file mode 100644 index 00000000000..3bcfe65473d --- /dev/null +++ b/dts/bindings/gpio/infineon,cat1-gpio.yaml @@ -0,0 +1,25 @@ +# Copyright (c) 2020-2021 ATL Electronics +# Copyright (c) 2022 Cypress Semiconductor Corporation (an Infineon company) or +# an affiliate of Cypress Semiconductor Corporation +# +# SPDX-License-Identifier: Apache-2.0 + +description: Infineon CAT1 GPIO PORT node + +compatible: "infineon,cat1-gpio" + +include: ["gpio-controller.yaml", "base.yaml"] + +properties: + reg: + required: true + + interrupts: + required: true + + "#gpio-cells": + const: 2 + +gpio-cells: + - pin + - flags