text: /****************************************************************************
   * arch/xtensa/src/esp32/esp32_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 <nuttx/config.h>
  
  #include <sys/types.h>
  #include <stdint.h>
  #include <assert.h>
  #include <debug.h>
  
  #include <nuttx/arch.h>
  #include <nuttx/irq.h>
  #include <arch/irq.h>
  
  #include "xtensa.h"
  
  #include "hardware/esp32_iomux.h"
  #include "hardware/esp32_gpio.h"
  
  #include "esp32_irq.h"
  #include "esp32_rtc_gpio.h"
  
  #include "esp32_gpio.h"
  
  /****************************************************************************
   * Pre-processor Definitions
   ****************************************************************************/
  
  #define NGPIO_HPINS  (ESP32_NIRQ_GPIO - 32)
  #define NGPIO_HMASK  ((1ul << NGPIO_HPINS) - 1)
  #define _NA_         0xff
  
  /****************************************************************************
   * Private Data
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_GPIO_IRQ
  #ifdef CONFIG_SMP
  static int g_gpio_cpuint[CONFIG_SMP_NCPUS];
  #else
  static int g_gpio_cpuint[1];
  #endif
  #endif
  
  static const uint8_t g_pin2func[40] =
  {
    0x44, 0x88, 0x40, 0x84, 0x48, 0x6c, 0x60, 0x64,  /* 0-7 */
    0x68, 0x54, 0x58, 0x5c, 0x34, 0x38, 0x30, 0x3c,  /* 8-15 */
    0x4c, 0x50, 0x70, 0x74, _NA_, 0x7c, 0x80, 0x8c,  /* 16-19, N/A, 21-23 */
    _NA_, 0x24, 0x28, 0x2c, _NA_, _NA_, _NA_, _NA_,  /* N/A, 25-27, N/A, N/A, N/A, N/A */
    0x1c, 0x20, 0x14, 0x18, 0x04, 0x08, 0x0c, 0x10   /* 32-39 */
  };
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: rtc_gpio_is_valid_gpio
   *
   * Description:
   *   Determine if the specified GPIO is a valid RTC GPIO.
   *
   ****************************************************************************/
  
  static inline bool rtc_gpio_is_valid_gpio(uint32_t gpio_num)
  {
    return (gpio_num < GPIO_PIN_COUNT && g_rtc_io_num_map[gpio_num] >= 0);
  }
  
  /****************************************************************************
   * Name: gpio_dispatch
   *
   * Description:
   *   Second level dispatch for GPIO interrupt handling.
   *
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_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 = (1ul << 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.
   *
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_GPIO_IRQ
  static int gpio_interrupt(int irq, FAR void *context, FAR 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(ESP32_FIRST_GPIOIRQ, status, (uint32_t *)context);
  
    /* Read and clear the upper GPIO interrupt status */
  
    status = getreg32(GPIO_STATUS1_REG) & NGPIO_HMASK;
    putreg32(status, GPIO_STATUS1_W1TC_REG);
  
    /* Dispatch pending interrupts in the lower GPIO status register */
  
    gpio_dispatch(ESP32_FIRST_GPIOIRQ + 32, status, (uint32_t *)context);
    return OK;
  }
  #endif
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: esp32_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 esp32_configgpio(int pin, gpio_pinattr_t attr)
  {
    uintptr_t regaddr;
    uint32_t func;
    uint32_t cntrl;
  
    DEBUGASSERT(pin >= 0 && pin <= ESP32_NGPIOS);
  
    /* Handle input pins */
  
    func  = 0;
    cntrl = 0;
  
    if ((attr & INPUT) != 0)
      {
        if (pin < 32)
          {
            putreg32((1ul << pin), GPIO_ENABLE_W1TC_REG);
          }
        else
          {
            putreg32((1ul << (pin - 32)), GPIO_ENABLE1_W1TC_REG);
          }
  
        /* Input enable */
  
        func |= FUN_IE;
  
        /* Some pins only support Pull-Up and Pull-Down resistor on RTC GPIO */
  
        if (rtc_gpio_is_valid_gpio(pin))
          {
            uint32_t rtc_gpio_idx = g_rtc_io_num_map[pin];
  
            if (rtc_gpio_idx >= RTCIO_GPIO34_IDX)
              {
                gpioerr("Pins 34-39 don't support PullUp/PullDown\n");
              }
            else
              {
                uint32_t regval;
                uint32_t rtc_gpio_pin;
                bool en_pu = false;
                bool en_pd = false;
  
                if ((attr & PULLUP) != 0)
                  {
                    en_pu = true;
                  }
                else if ((attr & PULLDOWN) != 0)
                  {
                    en_pd = true;
                  }
  
                /* Get the pin register */
  
                rtc_gpio_pin = g_rtc_io_desc[rtc_gpio_idx];
  
                /* Read the current value from RTC GPIO pin */
  
                regval = getreg32(rtc_gpio_pin);
  
                /* RTC_IO_X32P (GPIO32) uses different PU/PD bits */
  
                if (rtc_gpio_idx == RTCIO_GPIO32_IDX)
                  {
                    /* First, disable PU/PD */
  
                    regval &= ~SPECIAL_RTC_PU_BIT;
                    regval &= ~SPECIAL_RTC_PD_BIT;
  
                    /* Enable PU/PD, if needed */
  
                    regval |= en_pu ? SPECIAL_RTC_PU_BIT : 0;
                    regval |= en_pd ? SPECIAL_RTC_PD_BIT : 0;
                  }
                else
                  {
                    /* First, disable PU/PD */
  
                    regval &= ~DEFAULT_RTC_PU_BIT;
                    regval &= ~DEFAULT_RTC_PD_BIT;
  
                    /* Enable PU/PD, if needed */
  
                    regval |= en_pu ? DEFAULT_RTC_PU_BIT : 0;
                    regval |= en_pd ? DEFAULT_RTC_PD_BIT : 0;
                  }
  
                putreg32(regval, rtc_gpio_pin);
              }
          }
        else if ((attr & PULLUP) != 0)
          {
            func |= FUN_PU;
          }
        else if (attr & PULLDOWN)
          {
            func |= FUN_PD;
          }
      }
  
    /* Handle output pins */
  
    if ((attr & OUTPUT) != 0)
      {
        if (pin < 32)
          {
            putreg32((1ul << pin), GPIO_ENABLE_W1TS_REG);
          }
        else
          {
            putreg32((1ul << (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);
      }
  
    regaddr = DR_REG_IO_MUX_BASE + g_pin2func[pin];
    putreg32(func, regaddr);
  
    regaddr = GPIO_REG(pin);
    putreg32(cntrl, regaddr);
    return OK;
  }
  
  /****************************************************************************
   * Name: esp32_gpiowrite
   *
   * Description:
   *   Write one or zero to the selected GPIO pin
   *
   ****************************************************************************/
  
  void esp32_gpiowrite(int pin, bool value)
  {
    DEBUGASSERT(pin >= 0 && pin <= ESP32_NGPIOS);
  
    if (value)
      {
        if (pin < 32)
          {
            putreg32((uint32_t)(1ul << pin), GPIO_OUT_W1TS_REG);
          }
        else
          {
            putreg32((uint32_t)(1ul << (pin - 32)), GPIO_OUT1_W1TS_REG);
          }
      }
    else
      {
        if (pin < 32)
          {
            putreg32((uint32_t)(1ul << pin), GPIO_OUT_W1TC_REG);
          }
        else
          {
            putreg32((uint32_t)(1ul << (pin - 32)), GPIO_OUT1_W1TC_REG);
          }
      }
  }
  
  /****************************************************************************
   * Name: esp32_gpioread
   *
   * Description:
   *   Read one or zero from the selected GPIO pin
   *
   ****************************************************************************/
  
  bool esp32_gpioread(int pin)
  {
    uint32_t regval;
  
    DEBUGASSERT(pin >= 0 && pin <= ESP32_NGPIOS);
  
    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: esp32_gpioirqinitialize
   *
   * Description:
   *   Initialize logic to support a second level of interrupt decoding for
   *   GPIO pins.
   *
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_GPIO_IRQ
  void esp32_gpioirqinitialize(int cpu)
  {
  #ifdef CONFIG_SMP
    DEBUGASSERT(cpu >= 0 && cpu <= CONFIG_SMP_NCPUS);
  #else
    DEBUGASSERT(cpu == 0);
  #endif
  
    /* Setup the GPIO interrupt. */
  
    g_gpio_cpuint[cpu] = esp32_setup_irq(cpu, ESP32_PERIPH_CPU_GPIO,
                                         1, ESP32_CPUINT_LEVEL);
    DEBUGASSERT(g_gpio_cpuint[cpu] >= 0);
  
    /* Attach and enable the interrupt handler */
  
    DEBUGVERIFY(irq_attach(esp32_irq_gpio(cpu), gpio_interrupt, NULL));
    up_enable_irq(esp32_irq_gpio(cpu));
  }
  #endif
  
  /****************************************************************************
   * Name: esp32_gpioirqenable
   *
   * Description:
   *   Enable the COPY interrupt for specified GPIO IRQ
   *
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_GPIO_IRQ
  void esp32_gpioirqenable(int irq, gpio_intrtype_t intrtype)
  {
    uintptr_t regaddr;
    uint32_t regval;
    int pin;
    int cpu = up_cpu_index();
  
    DEBUGASSERT(irq >= ESP32_FIRST_GPIOIRQ && irq <= ESP32_LAST_GPIOIRQ);
  
    /* Convert the IRQ number to a pin number */
  
    pin = ESP32_IRQ2PIN(irq);
  
    /* Get the address of the GPIO PIN register for this pin */
  
    up_disable_irq(esp32_irq_gpio(cpu));
  
    regaddr = GPIO_REG(pin);
    regval  = getreg32(regaddr);
    regval &= ~(GPIO_PIN_INT_ENA_M | GPIO_PIN_INT_TYPE_M);
  
    /* Set the pin ENA field:
     *
     *   Bit 0: APP CPU interrupt enable
     *   Bit 1: APP CPU non-maskable interrupt enable
     *   Bit 3: PRO CPU interrupt enable
     *   Bit 4: PRO CPU non-maskable interrupt enable
     *   Bit 5: SDIO's extent interrupt enable.
     */
  
  #ifdef CONFIG_SMP
    if (cpu != 0)
      {
        /* APP_CPU */
  
        regval |= ((1 << 0) << GPIO_PIN_INT_ENA_S);
      }
    else
  #endif
      {
        /* PRO_CPU */
  
        regval |= ((1 << 2) << GPIO_PIN_INT_ENA_S);
      }
  
    regval |= (intrtype << GPIO_PIN_INT_TYPE_S);
    putreg32(regval, regaddr);
  
    up_enable_irq(esp32_irq_gpio(cpu));
  }
  #endif
  
  /****************************************************************************
   * Name: esp32_gpioirqdisable
   *
   * Description:
   *   Disable the interrupt for specified GPIO IRQ
   *
   ****************************************************************************/
  
  #ifdef CONFIG_ESP32_GPIO_IRQ
  void esp32_gpioirqdisable(int irq)
  {
    uintptr_t regaddr;
    uint32_t regval;
    int pin;
    int cpu = up_cpu_index();
  
    DEBUGASSERT(irq >= ESP32_FIRST_GPIOIRQ && irq <= ESP32_LAST_GPIOIRQ);
  
    /* Convert the IRQ number to a pin number */
  
    pin = ESP32_IRQ2PIN(irq);
  
    /* Get the address of the GPIO PIN register for this pin */
  
    up_disable_irq(esp32_irq_gpio(cpu));
  
    regaddr = GPIO_REG(pin);
    regval  = getreg32(regaddr);
    regval &= ~(GPIO_PIN_INT_ENA_M | GPIO_PIN_INT_TYPE_M);
    putreg32(regval, regaddr);
  
    up_enable_irq(esp32_irq_gpio(cpu));
  }
  #endif
  
  /****************************************************************************
   * Name: esp32_gpio_matrix_in
   *
   * Description:
   *   Set gpio input to a signal
   *   NOTE: one gpio can input to several signals
   *   If gpio == 0x30, cancel input to the signal, input 0 to signal
   *   If gpio == 0x38, cancel input to the signal, input 1 to signal,
   *   for I2C pad
   *
   ****************************************************************************/
  
  void esp32_gpio_matrix_in(uint32_t gpio, uint32_t signal_idx, bool inv)
  {
    uint32_t regaddr = GPIO_FUNC0_IN_SEL_CFG_REG + (signal_idx * 4);
    uint32_t regval = (gpio << GPIO_FUNC0_IN_SEL_S);
  
    if (inv)
      {
        regval |= GPIO_FUNC0_IN_INV_SEL;
      }
  
    if (gpio != 0x34)
      {
        regval |= GPIO_SIG0_IN_SEL;
      }
  
    putreg32(regval, regaddr);
  }
  
  /****************************************************************************
   * Name: esp32_gpio_matrix_out
   *
   * Description:
   *   Set signal output to gpio
   *   NOTE: one signal can output to several gpios
   *   If signal_idx == 0x100, cancel output put to the gpio
   *
   ****************************************************************************/
  
  void esp32_gpio_matrix_out(uint32_t gpio, uint32_t signal_idx, bool out_inv,
                             bool oen_inv)
  {
    uint32_t regaddr = GPIO_FUNC0_OUT_SEL_CFG_REG + (gpio * 4);
    uint32_t regval = signal_idx << GPIO_FUNC0_OUT_SEL_S;
  
    if (gpio >= GPIO_PIN_COUNT)
      {
        return;
      }
  
    if (gpio < 32)
      {
        putreg32((1ul << gpio), GPIO_ENABLE_W1TS_REG);
      }
    else
      {
        putreg32((1ul << (gpio - 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);
  }
  