zephyr/drivers/gpio/gpio_mcux_igpio.c
Daniel DeGrasse f7986835e8 drivers: gpio: gpio_mcux_igpio: add workaround for non contiguous pins
some iMX RT SOCs have non contiguous sets of gpio pins available, which
caused issues when selecting appropriate pinmux for these parts. Add
workaround code to adjust offset of pinmux settings when configuring
these pins.

Fixes #44391

Signed-off-by: Daniel DeGrasse <daniel.degrasse@nxp.com>
2022-05-18 10:55:22 +02:00

424 lines
11 KiB
C

/*
* Copyright (c) 2017, NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_imx_gpio
#include <errno.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#include <soc.h>
#include <fsl_common.h>
#include <fsl_gpio.h>
#ifdef CONFIG_PINCTRL
#include <zephyr/drivers/pinctrl.h>
#endif
#include "gpio_utils.h"
struct mcux_igpio_config {
/* gpio_driver_config needs to be first */
struct gpio_driver_config common;
GPIO_Type *base;
#ifdef CONFIG_PINCTRL
uint8_t port_num;
const struct pinctrl_soc_pinmux *pin_muxes;
uint8_t mux_count;
#endif
};
struct mcux_igpio_data {
/* gpio_driver_data needs to be first */
struct gpio_driver_data general;
/* port ISR callback routine address */
sys_slist_t callbacks;
};
static int mcux_igpio_configure(const struct device *dev,
gpio_pin_t pin, gpio_flags_t flags)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
#ifdef CONFIG_PINCTRL
struct pinctrl_soc_pin pin_cfg;
int cfg_idx = pin;
/* Some SOCs have non-contiguous gpio pin layouts, account for this */
if (IS_ENABLED(CONFIG_SOC_MIMXRT1015) &&
(config->port_num == 3) && (pin >= 20)) {
/* RT1015 does not have GPIO3 pins 4-19 */
cfg_idx -= 16;
} else if (IS_ENABLED(CONFIG_SOC_MIMXRT1015) &&
(config->port_num == 5)) {
/* RT1015 only has one GPIO5 pin */
cfg_idx = 0;
} else if ((IS_ENABLED(CONFIG_SOC_MIMXRT1024) ||
IS_ENABLED(CONFIG_SOC_MIMXRT1021)) &&
(config->port_num == 3) && (pin >= 13)) {
/* RT102x does not have GPIO3 pins 10-12 */
cfg_idx -= 3;
}
/* Init pin configuration struct, and use pinctrl api to apply settings */
assert(cfg_idx < config->mux_count);
/* Set appropriate bits in pin configuration register */
volatile uint32_t *gpio_cfg_reg =
(volatile uint32_t *)config->pin_muxes[cfg_idx].config_register;
uint32_t reg = *gpio_cfg_reg;
#ifdef CONFIG_SOC_SERIES_IMX_RT10XX
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
} else {
reg &= ~IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
}
if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
reg |= IOMUXC_SW_PAD_CTL_PAD_PUE_MASK;
if (((flags & GPIO_PULL_UP) != 0)) {
/* Use 100K pullup */
reg |= IOMUXC_SW_PAD_CTL_PAD_PUS(2);
} else {
/* 100K pulldown */
reg &= ~IOMUXC_SW_PAD_CTL_PAD_PUS_MASK;
}
} else {
/* Set pin to keeper */
reg &= ~IOMUXC_SW_PAD_CTL_PAD_PUE_MASK;
}
#elif defined(CONFIG_SOC_SERIES_IMX_RT11XX)
if (config->pin_muxes[pin].pue_mux) {
/* PUE type register layout (GPIO_AD pins) */
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
} else {
reg &= ~IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
}
if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
reg |= IOMUXC_SW_PAD_CTL_PAD_PUE_MASK;
if (((flags & GPIO_PULL_UP) != 0)) {
reg |= IOMUXC_SW_PAD_CTL_PAD_PUS_MASK;
} else {
reg &= ~IOMUXC_SW_PAD_CTL_PAD_PUS_MASK;
}
} else {
/* Set pin to highz */
reg &= ~IOMUXC_SW_PAD_CTL_PAD_PUE_MASK;
}
} else {
/* PDRV/SNVS/LPSR type register layout */
if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
reg &= ~IOMUXC_SW_PAD_CTL_PAD_PULL_MASK;
if (((flags & GPIO_PULL_UP) != 0)) {
reg |= IOMUXC_SW_PAD_CTL_PAD_PULL(0x1U);
} else {
reg |= IOMUXC_SW_PAD_CTL_PAD_PULL(0x2U);
}
} else {
/* Set pin to no pull */
reg |= IOMUXC_SW_PAD_CTL_PAD_PUS_MASK;
}
/* PDRV/SNVS/LPSR reg have different ODE bits */
if (config->pin_muxes[cfg_idx].pdrv_mux) {
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
} else {
reg &= ~IOMUXC_SW_PAD_CTL_PAD_ODE_MASK;
}
} else if (config->pin_muxes[cfg_idx].lpsr_mux) {
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= (IOMUXC_SW_PAD_CTL_PAD_ODE_MASK << 1);
} else {
reg &= ~(IOMUXC_SW_PAD_CTL_PAD_ODE_MASK << 1);
}
} else if (config->pin_muxes[cfg_idx].snvs_mux) {
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= (IOMUXC_SW_PAD_CTL_PAD_ODE_MASK << 2);
} else {
reg &= ~(IOMUXC_SW_PAD_CTL_PAD_ODE_MASK << 2);
}
}
}
#elif defined(CONFIG_SOC_SERIES_IMX8MQ_M4)
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= (0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT);
} else {
reg &= ~(0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT);
}
if ((flags & GPIO_PULL_UP) != 0) {
reg |= (0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT);
}
if ((flag & GPIO_PULL_DOWN) != 0) {
return -ENOTSUP;
}
#else
/* Default flags, should work for most SOCs */
if ((flags & GPIO_SINGLE_ENDED) != 0) {
/* Set ODE bit */
reg |= (0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT);
} else {
reg &= ~(0x1 << MCUX_IMX_DRIVE_OPEN_DRAIN_SHIFT);
}
if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
reg |= (0x1 << MCUX_IMX_BIAS_PULL_ENABLE_SHIFT);
if (((flags & GPIO_PULL_UP) != 0)) {
reg |= (0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT);
} else {
reg &= ~(0x1 << MCUX_IMX_BIAS_PULL_UP_SHIFT);
}
} else {
/* Set pin to highz */
reg &= ~(0x1 << MCUX_IMX_BIAS_PULL_ENABLE_SHIFT);
}
#endif /* CONFIG_SOC_SERIES_IMX_RT10XX */
memcpy(&pin_cfg.pinmux, &config->pin_muxes[cfg_idx], sizeof(pin_cfg));
/* cfg register will be set by pinctrl_configure_pins */
pin_cfg.pin_ctrl_flags = reg;
pinctrl_configure_pins(&pin_cfg, 1, PINCTRL_REG_NONE);
#else
/* Without pinctrl, no support for GPIO flags */
if ((flags & GPIO_SINGLE_ENDED) != 0) {
return -ENOTSUP;
}
if (((flags & GPIO_PULL_UP) != 0) || ((flags & GPIO_PULL_DOWN) != 0)) {
return -ENOTSUP;
}
#endif /* CONFIG_PINCTRL */
if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
return -ENOTSUP;
}
if (flags & GPIO_OUTPUT_INIT_HIGH) {
GPIO_WritePinOutput(base, pin, 1);
}
if (flags & GPIO_OUTPUT_INIT_LOW) {
GPIO_WritePinOutput(base, pin, 0);
}
WRITE_BIT(base->GDIR, pin, flags & GPIO_OUTPUT);
return 0;
}
static int mcux_igpio_port_get_raw(const struct device *dev, uint32_t *value)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
*value = base->DR;
return 0;
}
static int mcux_igpio_port_set_masked_raw(const struct device *dev,
uint32_t mask,
uint32_t value)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
base->DR = (base->DR & ~mask) | (mask & value);
return 0;
}
static int mcux_igpio_port_set_bits_raw(const struct device *dev,
uint32_t mask)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
GPIO_PortSet(base, mask);
return 0;
}
static int mcux_igpio_port_clear_bits_raw(const struct device *dev,
uint32_t mask)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
GPIO_PortClear(base, mask);
return 0;
}
static int mcux_igpio_port_toggle_bits(const struct device *dev,
uint32_t mask)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
GPIO_PortToggle(base, mask);
return 0;
}
static int mcux_igpio_pin_interrupt_configure(const struct device *dev,
gpio_pin_t pin,
enum gpio_int_mode mode,
enum gpio_int_trig trig)
{
const struct mcux_igpio_config *config = dev->config;
GPIO_Type *base = config->base;
unsigned int key;
uint8_t icr;
int shift;
if (mode == GPIO_INT_MODE_DISABLED) {
key = irq_lock();
WRITE_BIT(base->IMR, pin, 0);
irq_unlock(key);
return 0;
}
if ((mode == GPIO_INT_MODE_EDGE) && (trig == GPIO_INT_TRIG_LOW)) {
icr = 3;
} else if ((mode == GPIO_INT_MODE_EDGE) &&
(trig == GPIO_INT_TRIG_HIGH)) {
icr = 2;
} else if ((mode == GPIO_INT_MODE_LEVEL) &&
(trig == GPIO_INT_TRIG_HIGH)) {
icr = 1;
} else {
icr = 0;
}
if (pin < 16) {
shift = 2 * pin;
base->ICR1 = (base->ICR1 & ~(3 << shift)) | (icr << shift);
} else if (pin < 32) {
shift = 2 * (pin - 16);
base->ICR2 = (base->ICR2 & ~(3 << shift)) | (icr << shift);
} else {
return -EINVAL;
}
key = irq_lock();
WRITE_BIT(base->EDGE_SEL, pin, trig == GPIO_INT_TRIG_BOTH);
WRITE_BIT(base->ISR, pin, 1);
WRITE_BIT(base->IMR, pin, 1);
irq_unlock(key);
return 0;
}
static int mcux_igpio_manage_callback(const struct device *dev,
struct gpio_callback *callback,
bool set)
{
struct mcux_igpio_data *data = dev->data;
return gpio_manage_callback(&data->callbacks, callback, set);
}
static void mcux_igpio_port_isr(const struct device *dev)
{
const struct mcux_igpio_config *config = dev->config;
struct mcux_igpio_data *data = dev->data;
GPIO_Type *base = config->base;
uint32_t int_flags;
int_flags = base->ISR;
base->ISR = int_flags;
gpio_fire_callbacks(&data->callbacks, dev, int_flags);
}
static const struct gpio_driver_api mcux_igpio_driver_api = {
.pin_configure = mcux_igpio_configure,
.port_get_raw = mcux_igpio_port_get_raw,
.port_set_masked_raw = mcux_igpio_port_set_masked_raw,
.port_set_bits_raw = mcux_igpio_port_set_bits_raw,
.port_clear_bits_raw = mcux_igpio_port_clear_bits_raw,
.port_toggle_bits = mcux_igpio_port_toggle_bits,
.pin_interrupt_configure = mcux_igpio_pin_interrupt_configure,
.manage_callback = mcux_igpio_manage_callback,
};
#ifdef CONFIG_PINCTRL
/* These macros will declare an array of pinctrl_soc_pinmux types */
#define PINMUX_INIT(node, prop, idx) MCUX_IMX_PINMUX(DT_PROP_BY_IDX(node, prop, idx)),
#define MCUX_IGPIO_PIN_DECLARE(n) \
const struct pinctrl_soc_pinmux mcux_igpio_pinmux_##n[] = { \
DT_FOREACH_PROP_ELEM(DT_DRV_INST(n), pinmux, PINMUX_INIT) \
};
#define MCUX_IGPIO_PIN_INIT(n) \
.port_num = (n + 1), \
.pin_muxes = mcux_igpio_pinmux_##n, \
.mux_count = DT_PROP_LEN(DT_DRV_INST(n), pinmux),
#else
#define MCUX_IGPIO_PIN_DECLARE(n)
#define MCUX_IGPIO_PIN_INIT(n)
#endif /* CONFIG_PINCTRL */
#define MCUX_IGPIO_IRQ_INIT(n, i) \
do { \
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, i, irq), \
DT_INST_IRQ_BY_IDX(n, i, priority), \
mcux_igpio_port_isr, \
DEVICE_DT_INST_GET(n), 0); \
\
irq_enable(DT_INST_IRQ_BY_IDX(n, i, irq)); \
} while (0)
#define MCUX_IGPIO_INIT(n) \
MCUX_IGPIO_PIN_DECLARE(n) \
static int mcux_igpio_##n##_init(const struct device *dev); \
\
static const struct mcux_igpio_config mcux_igpio_##n##_config = {\
.common = { \
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\
}, \
.base = (GPIO_Type *)DT_INST_REG_ADDR(n), \
MCUX_IGPIO_PIN_INIT(n) \
}; \
\
static struct mcux_igpio_data mcux_igpio_##n##_data; \
\
DEVICE_DT_INST_DEFINE(n, \
mcux_igpio_##n##_init, \
NULL, \
&mcux_igpio_##n##_data, \
&mcux_igpio_##n##_config, \
POST_KERNEL, \
CONFIG_GPIO_INIT_PRIORITY, \
&mcux_igpio_driver_api); \
\
static int mcux_igpio_##n##_init(const struct device *dev) \
{ \
IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), \
(MCUX_IGPIO_IRQ_INIT(n, 0);)) \
\
IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 1), \
(MCUX_IGPIO_IRQ_INIT(n, 1);)) \
\
return 0; \
}
DT_INST_FOREACH_STATUS_OKAY(MCUX_IGPIO_INIT)