59bbcfdb创建于 4月21日历史提交
/****************************************************************************
 * arch/arm/src/imxrt/imxrt_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 <stdbool.h>
#include <assert.h>
#include <errno.h>

#include <nuttx/spinlock.h>

#include "chip.h"
#include "arm_internal.h"
#include "imxrt_iomuxc.h"
#include "imxrt_gpio.h"
#include "hardware/imxrt_daisy.h"

#if defined(CONFIG_ARCH_FAMILY_IMXRT102x)
#  include "imxrt102x_gpio.c"
#elif defined(CONFIG_ARCH_FAMILY_IMXRT105x)
#  include "imxrt105x_gpio.c"
#elif defined(CONFIG_ARCH_FAMILY_IMXRT106x)
#  include "imxrt106x_gpio.c"
#elif defined(CONFIG_ARCH_FAMILY_IMXRT117x)
#  include "imxrt117x_gpio.c"
#else
#  error Unrecognized i.MX RT architecture
#endif

/****************************************************************************
 * Public Data
 ****************************************************************************/

/* Look-up table that maps GPIO1..GPIOn indexes into GPIO register base
 * addresses
 */

const uintptr_t g_gpio_base[IMXRT_GPIO_NPORTS] =
{
  IMXRT_GPIO1_BASE
#if IMXRT_GPIO_NPORTS > 1
  , IMXRT_GPIO2_BASE
#endif
#if IMXRT_GPIO_NPORTS > 2
  , IMXRT_GPIO3_BASE
#endif
#if IMXRT_GPIO_NPORTS > 3
#if defined(IMXRT_GPIO4_BASE)
  , IMXRT_GPIO4_BASE
#else
  , 0
#endif
#endif
#if IMXRT_GPIO_NPORTS > 4
  , IMXRT_GPIO5_BASE
#endif
#if IMXRT_GPIO_NPORTS > 5
  , IMXRT_GPIO6_BASE
#endif
#if IMXRT_GPIO_NPORTS > 6
  , IMXRT_GPIO7_BASE
#endif
#if IMXRT_GPIO_NPORTS > 7
  , IMXRT_GPIO8_BASE
#endif
#if IMXRT_GPIO_NPORTS > 8
  , IMXRT_GPIO9_BASE
#endif
#if IMXRT_GPIO_NPORTS > 9
  , IMXRT_GPIO10_BASE
#endif
#if IMXRT_GPIO_NPORTS > 10
  , IMXRT_GPIO11_BASE
#endif
#if IMXRT_GPIO_NPORTS > 11
  , IMXRT_GPIO12_BASE
#endif
#if IMXRT_GPIO_NPORTS > 12
  , IMXRT_GPIO13_BASE
#endif
};

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Name: imxrt_padmux_address
 ****************************************************************************/

static uintptr_t imxrt_padmux_address(unsigned int index)
{
#if defined(IMXRT_PAD1MUX_OFFSET)
  if (index >= IMXRT_PADMUX_GPIO_SPI_B0_00_INDEX)
    {
      return (IMXRT_PAD1MUX_OFFSET(index -
                                   IMXRT_PADMUX_GPIO_SPI_B0_00_INDEX));
    }

#elif defined(IMXRT_PADMUX_OFFSET_LPSR)
  if (index >= IMXRT_PADMUX_GPIO_LPSR_00_INDEX)
    {
      return (IMXRT_PADMUX_ADDRESS_LPSR(index -
                                        IMXRT_PADMUX_GPIO_LPSR_00_INDEX));
    }

#endif
  if (index >= IMXRT_PADMUX_WAKEUP_INDEX)
    {
      return (IMXRT_PADMUX_ADDRESS_SNVS(index -
                                        IMXRT_PADMUX_WAKEUP_INDEX));
    }

  return (IMXRT_PADMUX_ADDRESS(index));
}

/****************************************************************************
 * Name: imxrt_padctl_address
 ****************************************************************************/

static uintptr_t imxrt_padctl_address(unsigned int index)
{
#if defined(IMXRT_PAD1CTL_OFFSET)
  if (index >= IMXRT_PADCTL_GPIO_SPI_B0_00_INDEX)
    {
      return (IMXRT_PAD1CTL_OFFSET(index -
                                   IMXRT_PADCTL_GPIO_SPI_B0_00_INDEX));
    }

#elif defined(IMXRT_PADCTL_OFFSET_LPSR)
  if (index >= IMXRT_PADCTL_GPIO_LPSR_00_INDEX)
    {
      return (IMXRT_PADCTL_ADDRESS_LPSR(index -
                                        IMXRT_PADCTL_GPIO_LPSR_00_INDEX));
    }

#endif
#if defined(IMXRT_PADCTL_TEST_MODE_INDEX)
  if (index >= IMXRT_PADCTL_TEST_MODE_INDEX)
    {
      return (IMXRT_PADCTL_ADDRESS_SNVS(index -
                                        IMXRT_PADCTL_TEST_MODE_INDEX));
    }
#else
  if (index >= IMXRT_PADCTL_WAKEUP_INDEX)
    {
      return (IMXRT_PADCTL_ADDRESS_SNVS(index -
                                        IMXRT_PADCTL_WAKEUP_INDEX));
    }
#endif

  return (IMXRT_PADCTL_ADDRESS(index));
}

/****************************************************************************
 * Name: imxrt_gpio_dirout
 ****************************************************************************/

static inline void imxrt_gpio_dirout(int port, int pin)
{
  uint32_t regval = getreg32(IMXRT_GPIO_GDIR(port));
  regval |= GPIO_PIN(pin);
  putreg32(regval, IMXRT_GPIO_GDIR(port));
}

/****************************************************************************
 * Name: imxrt_gpio_dirin
 ****************************************************************************/

static inline void imxrt_gpio_dirin(int port, int pin)
{
  uint32_t regval = getreg32(IMXRT_GPIO_GDIR(port));
  regval &= ~GPIO_PIN(pin);
  putreg32(regval, IMXRT_GPIO_GDIR(port));
}

/****************************************************************************
 * Name: imxrt_gpio_setoutput
 ****************************************************************************/

static void imxrt_gpio_setoutput(int port, int pin, bool value)
{
  uintptr_t regaddr = IMXRT_GPIO_DR(port);
  uint32_t regval;

  regval = getreg32(regaddr);
  if (value)
    {
      regval |= GPIO_PIN(pin);
    }
  else
    {
      regval &= ~GPIO_PIN(pin);
    }

  putreg32(regval, regaddr);
}

/****************************************************************************
 * Name: imxrt_gpio_getpin_status
 ****************************************************************************/

static inline bool imxrt_gpio_get_pinstatus(int port, int pin)
{
  uintptr_t regaddr = IMXRT_GPIO_PSR(port);
  uint32_t regval;

  regval = getreg32(regaddr);
  return ((regval & GPIO_PIN(pin)) != 0);
}

/****************************************************************************
 * Name: imxrt_gpio_getinput
 ****************************************************************************/

static inline bool imxrt_gpio_getinput(int port, int pin)
{
  uintptr_t regaddr = IMXRT_GPIO_DR(port);
  uint32_t regval;

  regval = getreg32(regaddr);
  return ((regval & GPIO_PIN(pin)) != 0);
}

/****************************************************************************
 * Name: imxrt_gpio_select
 * GPIO{1234}(l) and GPIO{6789}(h) share same IO MUX function, GPIO_MUXn
 * selects one GPIO function.
 * 0: GPIOl[n] is selected
 * 1: GPIOh[n] is selected
 ****************************************************************************/

static inline int imxrt_gpio_select(int port, int pin)
{
#if IMXRT_GPIO_NPORTS > 5 && defined(CONFIG_IMXRT_IOMUX_VER1)
  uint32_t gpr = port;
  uint32_t setbits = 1 << pin;
  uint32_t clearbits = 1 << pin;
  uintptr_t regaddr = (uintptr_t) IMXRT_IOMUXC_GPR_GPR26;

  if (port != GPIO5)
    {
      /* Uses GPR26 as the base */

      if (port >= GPIO6)
        {
          /* Map port to correct gpr index and set the GPIO_MUX3_GPIO[b]_SEL
           * bit
           */

          gpr = port - GPIO6;
          clearbits = 0;
        }
      else
        {
          /* The port is correct gpr index, so just clear the
           * GPIO_MUX3_GPIO[b]_SEL bit.
           */

          setbits = 0;
        }

      regaddr |= gpr * sizeof(uint32_t);
      modifyreg32(regaddr, clearbits, setbits);
    }

#endif
  return OK;
}

/****************************************************************************
 * Name: imxrt_gpio_configinput
 ****************************************************************************/

static int imxrt_gpio_configinput(gpio_pinset_t pinset)
{
  int port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
  int pin  = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
  const uint8_t *table;
  iomux_pinset_t ioset;
  uintptr_t regaddr;
  unsigned int index;
  uint32_t sion = 0;

  DEBUGASSERT((unsigned int)port < IMXRT_GPIO_NPORTS);

  /* Configure pin as in input */

  imxrt_gpio_dirin(port, pin);

  /* Configure pin as a GPIO */

  table = g_gpio_padmux[port];
  if (table == NULL)
    {
      return -EINVAL;
    }

  index = (unsigned int)table[pin];
  if (index >= IMXRT_PADMUX_NREGISTERS)
    {
      return -EINVAL;
    }

  regaddr = imxrt_padmux_address(index);

  if ((pinset & GPIO_OUTPUT) == GPIO_OUTPUT)
    {
      sion |= (pinset & GPIO_SION_MASK) ? PADMUX_SION : 0;
    }

  putreg32(PADMUX_MUXMODE_ALT5 | sion, regaddr);

  imxrt_gpio_select(port, pin);

  /* Configure pin pad settings */

  index = imxrt_padmux_map(index);
  if (index >= IMXRT_PADCTL_NREGISTERS)
    {
      return -EINVAL;
    }

  regaddr = imxrt_padctl_address(index);
  ioset   = (iomux_pinset_t)((pinset & GPIO_IOMUX_MASK) >> GPIO_IOMUX_SHIFT);
  return imxrt_iomux_configure(regaddr, ioset);
}

/****************************************************************************
 * Name: imxrt_gpio_configoutput
 ****************************************************************************/

static inline int imxrt_gpio_configoutput(gpio_pinset_t pinset)
{
  int port   = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
  int pin    = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
  bool value = ((pinset & GPIO_OUTPUT_ONE) != 0);

  DEBUGASSERT((unsigned int)port < IMXRT_GPIO_NPORTS);

  /* Set the output value */

  imxrt_gpio_setoutput(port, pin, value);

  /* Convert the configured input GPIO to an output */

  imxrt_gpio_dirout(port, pin);
  return OK;
}

/****************************************************************************
 * Name: imxrt_gpio_configperiph
 ****************************************************************************/

static inline int imxrt_gpio_configperiph(gpio_pinset_t pinset)
{
  iomux_pinset_t ioset;
  uintptr_t regaddr;
  uint32_t regval;
  uint32_t alt;
  unsigned int index;

  /* Configure pin as a peripheral via SW MUX Control Register */

  index   = ((pinset & GPIO_PADMUX_MASK) >> GPIO_PADMUX_SHIFT);
  regaddr = imxrt_padmux_address(index);

  alt     = (pinset & GPIO_ALT_MASK) >> GPIO_ALT_SHIFT;
  regval  = alt << PADMUX_MUXMODE_SHIFT;
  regval |= (pinset & GPIO_SION_MASK) ? PADMUX_SION : 0;

  putreg32(regval, regaddr);

  /* Configure pin Daisy Select Input Daisy Register */

  imxrt_daisy_select(index, alt);

  /* Configure pin pad settings SW PAD Control Register */

  index = imxrt_padmux_map(index);
  if (index >= IMXRT_PADCTL_NREGISTERS)
    {
      return -EINVAL;
    }

  regaddr = imxrt_padctl_address(index);
  ioset   = (iomux_pinset_t)((pinset & GPIO_IOMUX_MASK) >> GPIO_IOMUX_SHIFT);
  return imxrt_iomux_configure(regaddr, ioset);
}

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: imxrt_config_gpio
 *
 * Description:
 *   Configure a GPIO pin based on pin-encoded description of the pin.
 *
 ****************************************************************************/

int imxrt_config_gpio(gpio_pinset_t pinset)
{
  irqstate_t flags;
  int ret;

  /* Configure the pin as an input initially to avoid any spurious outputs */

  flags = enter_critical_section();

  /* Configure based upon the pin mode */

  switch (pinset & GPIO_MODE_MASK)
    {
      case GPIO_INPUT:
        {
          /* Configure the pin as a GPIO input */

          ret = imxrt_gpio_configinput(pinset);
        }
        break;

      case GPIO_OUTPUT:
        {
          /* First configure the pin as a GPIO input to avoid output
           * glitches.
           */

          ret = imxrt_gpio_configinput(pinset);
          if (ret >= 0)
            {
              /* Convert the input to an output */

              ret = imxrt_gpio_configoutput(pinset);
            }
        }
        break;

      case GPIO_PERIPH:
        {
          /* Configure the pin as a peripheral */

          ret = imxrt_gpio_configperiph(pinset);
        }
        break;

#ifdef CONFIG_IMXRT_GPIO_IRQ
      case GPIO_INTERRUPT:
        {
          /* Configure the pin as a GPIO input */

          ret = imxrt_gpio_configinput(pinset);
          if (ret == OK)
            {
              ret = imxrt_gpioirq_configure(pinset);
            }
        }
        break;
#endif

      default:
        ret = -EINVAL;
        break;
    }

  leave_critical_section(flags);
  return ret;
}

/****************************************************************************
 * Name: imxrt_gpio_write
 *
 * Description:
 *   Write one or zero to the selected GPIO pin
 *
 ****************************************************************************/

void imxrt_gpio_write(gpio_pinset_t pinset, bool value)
{
  irqstate_t flags;
  int port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
  int pin  = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;

  DEBUGASSERT((unsigned int)port < IMXRT_GPIO_NPORTS);

  flags = enter_critical_section();
  imxrt_gpio_setoutput(port, pin, value);
  leave_critical_section(flags);
}

/****************************************************************************
 * Name: imxrt_gpio_read
 *
 * Description:
 *   Read one or zero from the selected GPIO pin
 *
 ****************************************************************************/

bool imxrt_gpio_read(gpio_pinset_t pinset)
{
  irqstate_t flags;
  int port = (pinset & GPIO_PORT_MASK) >> GPIO_PORT_SHIFT;
  int pin  = (pinset & GPIO_PIN_MASK) >> GPIO_PIN_SHIFT;
  bool value;

  DEBUGASSERT((unsigned int)port < IMXRT_GPIO_NPORTS);

  flags = enter_critical_section();
  if ((pinset & (GPIO_OUTPUT | GPIO_SION_ENABLE)) ==
                (GPIO_OUTPUT | GPIO_SION_ENABLE))
    {
      value = imxrt_gpio_get_pinstatus(port, pin);
    }
  else
    {
      value = imxrt_gpio_getinput(port, pin);
    }

    leave_critical_section(flags);
  return value;
}