/**************************************************************************** * arch/xtensa/src/esp32s3/esp32s3_gpio.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include "xtensa.h" #include "esp32s3_gpio.h" #include "esp32s3_irq.h" #include "hardware/esp32s3_gpio.h" #include "hardware/esp32s3_iomux.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define ESP32S3_NPINS 49 /**************************************************************************** * Private Data ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ static int g_gpio_cpuint; #endif /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: is_valid_gpio * * Description: * Check if the requested pin is a valid GPIO pin. * * Input Parameters: * pin - Pin to be checked for validity. * * Returned Value: * True if the requested pin is a valid GPIO pin, false otherwise. * ****************************************************************************/ static inline bool is_valid_gpio(uint32_t pin) { /* ESP32-S3 has 45 GPIO pins numbered from 0 to 21 and 26 to 48 */ return pin <= 21 || (pin >= 26 && pin < ESP32S3_NPINS); } /**************************************************************************** * Name: gpio_dispatch * * Description: * Second level dispatch for GPIO interrupt handling. * * Input Parameters: * irq - GPIO IRQ number. * status - Value from the GPIO interrupt status clear register. * regs - Saved CPU context. * * Returned Value: * None. * ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ static void gpio_dispatch(int irq, uint32_t status, uint32_t *regs) { uint32_t mask; int i; /* Check each bit in the status register */ for (i = 0; i < 32 && status != 0; i++) { /* Check if there is an interrupt pending for this pin */ mask = UINT32_C(1) << i; if ((status & mask) != 0) { /* Yes... perform the second level dispatch */ irq_dispatch(irq + i, regs); /* Clear the bit in the status so that we might execute this loop * sooner. */ status &= ~mask; } } } #endif /**************************************************************************** * Name: gpio_interrupt * * Description: * GPIO interrupt handler. * * Input Parameters: * irq - Identifier of the interrupt request. * context - Context data from the ISR. * arg - Opaque pointer to the internal driver state structure. * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned * on failure. * ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ static int gpio_interrupt(int irq, void *context, void *arg) { uint32_t status; /* Read and clear the lower GPIO interrupt status */ status = getreg32(GPIO_STATUS_REG); putreg32(status, GPIO_STATUS_W1TC_REG); /* Dispatch pending interrupts in the lower GPIO status register */ gpio_dispatch(ESP32S3_FIRST_GPIOIRQ, status, (uint32_t *)context); /* Read and clear the upper GPIO interrupt status */ status = getreg32(GPIO_STATUS1_REG); putreg32(status, GPIO_STATUS1_W1TC_REG); /* Dispatch pending interrupts in the lower GPIO status register */ gpio_dispatch(ESP32S3_FIRST_GPIOIRQ + 32, status, (uint32_t *)context); return OK; } #endif /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: esp32s3_configgpio * * Description: * Configure a GPIO pin based on encoded pin attributes. * * Input Parameters: * pin - GPIO pin to be configured. * attr - Attributes to be configured for the selected GPIO pin. * The following attributes are accepted: * - Direction (OUTPUT or INPUT) * - Pull (PULLUP, PULLDOWN or OPENDRAIN) * - Function (if not provided, assume function GPIO by * default) * - Drive strength (if not provided, assume DRIVE_2 by * default) * * Returned Value: * Zero (OK) on success, or -1 (ERROR) in case of failure. * ****************************************************************************/ int esp32s3_configgpio(uint32_t pin, gpio_pinattr_t attr) { uintptr_t regaddr; uint32_t func; uint32_t cntrl; uint32_t pin2func; DEBUGASSERT(is_valid_gpio(pin)); func = 0; cntrl = 0; /* Handle input pins */ if ((attr & INPUT) != 0) { if (pin < 32) { putreg32((UINT32_C(1) << pin), GPIO_ENABLE_W1TC_REG); } else { putreg32((UINT32_C(1) << (pin - 32)), GPIO_ENABLE1_W1TC_REG); } /* Input enable */ func |= FUN_IE; if ((attr & PULLUP) != 0) { func |= FUN_PU; } else if ((attr & PULLDOWN) != 0) { func |= FUN_PD; } } /* Handle output pins */ if ((attr & OUTPUT) != 0) { if (pin < 32) { putreg32((UINT32_C(1) << pin), GPIO_ENABLE_W1TS_REG); } else { putreg32((UINT32_C(1) << (pin - 32)), GPIO_ENABLE1_W1TS_REG); } } /* Configure the pad's function */ if ((attr & FUNCTION_MASK) != 0) { uint32_t val = ((attr & FUNCTION_MASK) >> FUNCTION_SHIFT) - 1; func |= val << MCU_SEL_S; } else { /* Function not provided, assuming function GPIO by default */ func |= (uint32_t)(PIN_FUNC_GPIO << MCU_SEL_S); } /* Configure the pad's drive strength */ if ((attr & DRIVE_MASK) != 0) { uint32_t val = ((attr & DRIVE_MASK) >> DRIVE_SHIFT) - 1; func |= val << FUN_DRV_S; } else { /* Drive strength not provided, assuming strength 2 by default */ func |= UINT32_C(2) << FUN_DRV_S; } if ((attr & OPEN_DRAIN) != 0) { cntrl |= (1 << GPIO_PIN_PAD_DRIVER_S); } /* Set the pin function to its register */ pin2func = (pin + 1) * 4; regaddr = REG_IO_MUX_BASE + pin2func; putreg32(func, regaddr); regaddr = GPIO_REG(pin); putreg32(cntrl, regaddr); return OK; } /**************************************************************************** * Name: esp32s3_gpiowrite * * Description: * Write one or zero to the selected GPIO pin. * * Input Parameters: * pin - GPIO pin to be written. * value - Value to be written to the GPIO pin. True will output * 1 (one) to the GPIO, while false will output 0 (zero). * * Returned Value: * None. * ****************************************************************************/ void esp32s3_gpiowrite(int pin, bool value) { DEBUGASSERT(is_valid_gpio(pin)); if (value) { if (pin < 32) { putreg32(UINT32_C(1) << pin, GPIO_OUT_W1TS_REG); } else { putreg32(UINT32_C(1) << (pin - 32), GPIO_OUT1_W1TS_REG); } } else { if (pin < 32) { putreg32(UINT32_C(1) << pin, GPIO_OUT_W1TC_REG); } else { putreg32(UINT32_C(1) << (pin - 32), GPIO_OUT1_W1TC_REG); } } } /**************************************************************************** * Name: esp32s3_gpioread * * Description: * Read one or zero from the selected GPIO pin. * * Input Parameters: * pin - GPIO pin to be read. * * Returned Value: * True in case the read value is 1 (one). If 0 (zero), then false will be * returned. * ****************************************************************************/ bool esp32s3_gpioread(int pin) { uint32_t regval; DEBUGASSERT(is_valid_gpio(pin)); if (pin < 32) { regval = getreg32(GPIO_IN_REG); return ((regval >> pin) & 1) != 0; } else { regval = getreg32(GPIO_IN1_REG); return ((regval >> (pin - 32)) & 1) != 0; } } /**************************************************************************** * Name: esp32s3_gpioirqinitialize * * Description: * Initialize logic to support a second level of interrupt decoding for * GPIO pins. * * Input Parameters: * None. * * Returned Value: * None. * ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ void esp32s3_gpioirqinitialize(void) { int cpu; /* Setup the GPIO interrupt. */ cpu = up_cpu_index(); g_gpio_cpuint = esp32s3_setup_irq(cpu, ESP32S3_PERIPH_GPIO_INT_CPU, 1, ESP32S3_CPUINT_LEVEL); DEBUGASSERT(g_gpio_cpuint >= 0); /* Attach and enable the interrupt handler */ DEBUGVERIFY(irq_attach(ESP32S3_IRQ_GPIO_INT_CPU, gpio_interrupt, NULL)); up_enable_irq(ESP32S3_IRQ_GPIO_INT_CPU); } #endif /**************************************************************************** * Name: esp32s3_gpioirqenable * * Description: * Enable the interrupt for the specified GPIO IRQ. * * Input Parameters: * irq - Identifier of the interrupt request. * intrtype - Interrupt type, select from gpio_intrtype_t. * * Returned Value: * None. * ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ void esp32s3_gpioirqenable(int irq, gpio_intrtype_t intrtype) { uintptr_t regaddr; uint32_t regval; int pin; DEBUGASSERT(irq >= ESP32S3_FIRST_GPIOIRQ && irq <= ESP32S3_LAST_GPIOIRQ); /* Convert the IRQ number to a pin number */ pin = ESP32S3_IRQ2PIN(irq); /* Disable the GPIO interrupt during the configuration. */ up_disable_irq(ESP32S3_IRQ_GPIO_INT_CPU); /* Get the address of the GPIO PIN register for this pin */ regaddr = GPIO_REG(pin); regval = getreg32(regaddr); regval &= ~(GPIO_PIN0_INT_ENA_M | GPIO_PIN0_INT_TYPE_M); /* Set the pin ENA field. * On ESP32-S3, CPU0 and CPU1 share the same interrupt enable bit. */ regval |= GPIO_PIN0_INT_ENA_M; regval |= (uint32_t)intrtype << GPIO_PIN0_INT_TYPE_S; putreg32(regval, regaddr); /* Configuration done. Re-enable the GPIO interrupt. */ up_enable_irq(ESP32S3_IRQ_GPIO_INT_CPU); } #endif /**************************************************************************** * Name: esp32s3_gpioirqdisable * * Description: * Disable the interrupt for the specified GPIO IRQ. * * Input Parameters: * irq - Identifier of the interrupt request. * * Returned Value: * None. * ****************************************************************************/ #ifdef CONFIG_ESP32S3_GPIO_IRQ void esp32s3_gpioirqdisable(int irq) { uintptr_t regaddr; uint32_t regval; int pin; DEBUGASSERT(irq >= ESP32S3_FIRST_GPIOIRQ && irq <= ESP32S3_LAST_GPIOIRQ); /* Convert the IRQ number to a pin number */ pin = ESP32S3_IRQ2PIN(irq); /* Disable the GPIO interrupt during the configuration. */ up_disable_irq(ESP32S3_IRQ_GPIO_INT_CPU); /* Reset the pin ENA and TYPE fields */ regaddr = GPIO_REG(pin); regval = getreg32(regaddr); regval &= ~(GPIO_PIN0_INT_ENA_M | GPIO_PIN0_INT_TYPE_M); putreg32(regval, regaddr); /* Configuration done. Re-enable the GPIO interrupt. */ up_enable_irq(ESP32S3_IRQ_GPIO_INT_CPU); } #endif /**************************************************************************** * Name: esp32s3_gpio_matrix_in * * Description: * Set GPIO input to a signal. * NOTE: one GPIO can receive inputs from several signals. * * Input Parameters: * pin - GPIO pin to be configured. * - If pin == 0x3c, cancel input to the signal, input 0 * to signal. * - If pin == 0x3a, input nothing to signal. * - If pin == 0x38, cancel input to the signal, input 1 * to signal. * signal_idx - Signal index. * inv - Flag indicating whether the signal is inverted. * * Returned Value: * None. * ****************************************************************************/ void esp32s3_gpio_matrix_in(uint32_t pin, uint32_t signal_idx, bool inv) { uint32_t regaddr = GPIO_FUNC0_IN_SEL_CFG_REG + (signal_idx * 4); uint32_t regval = pin << GPIO_FUNC0_IN_SEL_S; if (inv) { regval |= GPIO_FUNC0_IN_INV_SEL; } if (pin != 0x3a) { regval |= GPIO_SIG0_IN_SEL; } putreg32(regval, regaddr); } /**************************************************************************** * Name: esp32s3_gpio_matrix_out * * Description: * Set signal output to GPIO. * NOTE: one signal can output to several GPIOs. * * Input Parameters: * pin - GPIO pin to be configured. * signal_idx - Signal index. * - If signal_idx == 0x100, cancel output to the GPIO. * out_inv - Flag indicating whether the signal output is inverted. * oen_inv - Flag indicating whether the signal output enable is * inverted. * * Returned Value: * None. * ****************************************************************************/ void esp32s3_gpio_matrix_out(uint32_t pin, uint32_t signal_idx, bool out_inv, bool oen_inv) { uint32_t regaddr = GPIO_FUNC0_OUT_SEL_CFG_REG + (pin * 4); uint32_t regval = signal_idx << GPIO_FUNC0_OUT_SEL_S; DEBUGASSERT(is_valid_gpio(pin)); if (pin < 32) { putreg32((UINT32_C(1) << pin), GPIO_ENABLE_W1TS_REG); } else { putreg32((UINT32_C(1) << (pin - 32)), GPIO_ENABLE1_W1TS_REG); } if (out_inv) { regval |= GPIO_FUNC0_OUT_INV_SEL; } if (oen_inv) { regval |= GPIO_FUNC0_OEN_INV_SEL; } putreg32(regval, regaddr); }