drivers: gpio: Add NXP SC18IM704 GPIO support
Implement external GPIO controller driver with NXP's SC18IM704 device. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
This commit is contained in:
parent
9b36e723f4
commit
cd6fe580b0
5 changed files with 350 additions and 0 deletions
|
|
@ -74,3 +74,8 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s_port.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_RT1718S gpio_rt1718s_port.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_GPIO_HOGS gpio_hogs.c)
|
zephyr_library_sources_ifdef(CONFIG_GPIO_HOGS gpio_hogs.c)
|
||||||
|
|
||||||
|
if(CONFIG_GPIO_SC18IM704)
|
||||||
|
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)
|
||||||
|
zephyr_library_sources(gpio_sc18im704.c)
|
||||||
|
endif()
|
||||||
|
|
|
||||||
|
|
@ -181,4 +181,6 @@ source "drivers/gpio/Kconfig.numicro"
|
||||||
|
|
||||||
source "drivers/gpio/Kconfig.bd8lb600fs"
|
source "drivers/gpio/Kconfig.bd8lb600fs"
|
||||||
|
|
||||||
|
source "drivers/gpio/Kconfig.sc18im704"
|
||||||
|
|
||||||
endif # GPIO
|
endif # GPIO
|
||||||
|
|
|
||||||
19
drivers/gpio/Kconfig.sc18im704
Normal file
19
drivers/gpio/Kconfig.sc18im704
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Copyright (c) 2023 Basalte bv
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config GPIO_SC18IM704
|
||||||
|
bool "NXP SC18IM704 GPIO controller driver"
|
||||||
|
default y
|
||||||
|
depends on I2C_SC18IM704
|
||||||
|
depends on DT_HAS_NXP_SC18IM704_GPIO_ENABLED
|
||||||
|
help
|
||||||
|
Enables NXP SC18IM704 GPIO controller driver
|
||||||
|
|
||||||
|
config GPIO_SC18IM704_INIT_PRIORITY
|
||||||
|
int "SC18IM704 GPIO init priority"
|
||||||
|
default 52
|
||||||
|
depends on GPIO_SC18IM704
|
||||||
|
help
|
||||||
|
SC18IM704 GPIO controller initialization priority.
|
||||||
|
|
||||||
|
Note: Has to be greater than the parent SC18IM704 bridge initialization priority.
|
||||||
301
drivers/gpio/gpio_sc18im704.c
Normal file
301
drivers/gpio/gpio_sc18im704.c
Normal file
|
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c), 2023 Basalte bv
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT nxp_sc18im704_gpio
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/drivers/gpio/gpio_utils.h>
|
||||||
|
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
LOG_MODULE_REGISTER(gpio_sc18im, CONFIG_GPIO_LOG_LEVEL);
|
||||||
|
|
||||||
|
#include "i2c/i2c_sc18im704.h"
|
||||||
|
|
||||||
|
#define GPIO_SC18IM_MAX_PINS 8
|
||||||
|
|
||||||
|
/* After reset the GPIO config registers are 0x55 */
|
||||||
|
#define GPIO_SC18IM_DEFAULT_CONF 0x55
|
||||||
|
|
||||||
|
#define GPIO_SC18IM_CONF_INPUT 0x01
|
||||||
|
#define GPIO_SC18IM_CONF_PUSH_PULL 0x02
|
||||||
|
#define GPIO_SC18IM_CONF_OPEN_DRAIN 0x03
|
||||||
|
#define GPIO_SC18IM_CONF_MASK 0x03
|
||||||
|
|
||||||
|
struct gpio_sc18im_config {
|
||||||
|
/* gpio_driver_config needs to be first */
|
||||||
|
struct gpio_driver_config common;
|
||||||
|
|
||||||
|
const struct device *bridge;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gpio_sc18im_data {
|
||||||
|
/* gpio_driver_data needs to be first */
|
||||||
|
struct gpio_driver_data common;
|
||||||
|
|
||||||
|
uint8_t conf1;
|
||||||
|
uint8_t conf2;
|
||||||
|
uint8_t output_state;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_set_raw(const struct device *port,
|
||||||
|
uint8_t mask, uint8_t value, uint8_t toggle)
|
||||||
|
{
|
||||||
|
const struct gpio_sc18im_config *cfg = port->config;
|
||||||
|
struct gpio_sc18im_data *data = port->data;
|
||||||
|
uint8_t buf[] = {
|
||||||
|
SC18IM704_CMD_WRITE_GPIO,
|
||||||
|
data->output_state,
|
||||||
|
SC18IM704_CMD_STOP,
|
||||||
|
};
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (k_is_in_isr()) {
|
||||||
|
return -EWOULDBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[1] &= ~mask;
|
||||||
|
buf[1] |= (value & mask);
|
||||||
|
buf[1] ^= toggle;
|
||||||
|
|
||||||
|
ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to write GPIO state (%d)", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->output_state = buf[1];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_pin_configure(const struct device *port, gpio_pin_t pin,
|
||||||
|
gpio_flags_t flags)
|
||||||
|
{
|
||||||
|
const struct gpio_sc18im_config *cfg = port->config;
|
||||||
|
struct gpio_sc18im_data *data = port->data;
|
||||||
|
uint8_t pin_conf;
|
||||||
|
int ret;
|
||||||
|
uint8_t buf[] = {
|
||||||
|
SC18IM704_CMD_WRITE_REG,
|
||||||
|
0x00,
|
||||||
|
0x00,
|
||||||
|
SC18IM704_CMD_STOP,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (pin >= GPIO_SC18IM_MAX_PINS) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & GPIO_INPUT) {
|
||||||
|
pin_conf = GPIO_SC18IM_CONF_INPUT;
|
||||||
|
} else if (flags & GPIO_OUTPUT) {
|
||||||
|
if (flags & GPIO_SINGLE_ENDED) {
|
||||||
|
if (flags & GPIO_LINE_OPEN_DRAIN) {
|
||||||
|
pin_conf = GPIO_SC18IM_CONF_OPEN_DRAIN;
|
||||||
|
} else {
|
||||||
|
/* Open-drain is the only supported single-ended mode */
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Default to push/pull */
|
||||||
|
pin_conf = GPIO_SC18IM_CONF_PUSH_PULL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* Neither input nor output mode is selected */
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sc18im704_claim(cfg->bridge);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to claim bridge (%d)", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin < 4) {
|
||||||
|
/* Shift the config to the pin offset */
|
||||||
|
data->conf1 &= ~(GPIO_SC18IM_CONF_MASK << (pin * 2));
|
||||||
|
data->conf1 |= pin_conf << (pin * 2);
|
||||||
|
|
||||||
|
buf[1] = SC18IM704_REG_GPIO_CONF1;
|
||||||
|
buf[2] = data->conf1;
|
||||||
|
} else {
|
||||||
|
/* Shift the config to the pin offset */
|
||||||
|
data->conf2 &= ~(GPIO_SC18IM_CONF_MASK << ((pin - 4) * 2));
|
||||||
|
data->conf2 |= pin_conf << ((pin - 4) * 2);
|
||||||
|
|
||||||
|
buf[1] = SC18IM704_REG_GPIO_CONF2;
|
||||||
|
buf[2] = data->conf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to configure GPIO (%d)", ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0 && flags & GPIO_OUTPUT) {
|
||||||
|
if (flags & GPIO_OUTPUT_INIT_HIGH) {
|
||||||
|
gpio_sc18im_port_set_raw(port, BIT(pin), BIT(pin), 0);
|
||||||
|
}
|
||||||
|
if (flags & GPIO_OUTPUT_INIT_LOW) {
|
||||||
|
gpio_sc18im_port_set_raw(port, BIT(pin), 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sc18im704_release(cfg->bridge);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_GPIO_GET_CONFIG
|
||||||
|
static int gpio_sc18im_pin_get_config(const struct device *port, gpio_pin_t pin,
|
||||||
|
gpio_flags_t *flags)
|
||||||
|
{
|
||||||
|
struct gpio_sc18im_data *data = port->data;
|
||||||
|
uint8_t conf;
|
||||||
|
|
||||||
|
if (pin >= GPIO_SC18IM_MAX_PINS) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin < 4) {
|
||||||
|
conf = (data->conf1 >> (2 * pin)) & GPIO_SC18IM_CONF_MASK;
|
||||||
|
} else {
|
||||||
|
conf = (data->conf2 >> (2 * (pin - 4))) & GPIO_SC18IM_CONF_MASK;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (conf) {
|
||||||
|
case GPIO_SC18IM_CONF_PUSH_PULL:
|
||||||
|
*flags = GPIO_OUTPUT | GPIO_PUSH_PULL;
|
||||||
|
break;
|
||||||
|
case GPIO_SC18IM_CONF_OPEN_DRAIN:
|
||||||
|
*flags = GPIO_OUTPUT | GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN;
|
||||||
|
break;
|
||||||
|
case GPIO_SC18IM_CONF_INPUT:
|
||||||
|
default:
|
||||||
|
*flags = GPIO_INPUT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_get_raw(const struct device *port, gpio_port_value_t *value)
|
||||||
|
{
|
||||||
|
const struct gpio_sc18im_config *cfg = port->config;
|
||||||
|
|
||||||
|
uint8_t buf[] = {
|
||||||
|
SC18IM704_CMD_READ_GPIO,
|
||||||
|
SC18IM704_CMD_STOP,
|
||||||
|
};
|
||||||
|
uint8_t data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (k_is_in_isr()) {
|
||||||
|
return -EWOULDBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sc18im704_transfer(cfg->bridge, buf, sizeof(buf), &data, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to read GPIO state (%d)", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
*value = data;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_set_masked_raw(const struct device *port,
|
||||||
|
gpio_port_pins_t mask,
|
||||||
|
gpio_port_value_t value)
|
||||||
|
{
|
||||||
|
return gpio_sc18im_port_set_raw(port, (uint8_t)mask, (uint8_t)value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins)
|
||||||
|
{
|
||||||
|
return gpio_sc18im_port_set_raw(port, (uint8_t)pins, (uint8_t)pins, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins)
|
||||||
|
{
|
||||||
|
return gpio_sc18im_port_set_raw(port, (uint8_t)pins, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_port_toggle_bits(const struct device *port, gpio_port_pins_t pins)
|
||||||
|
{
|
||||||
|
return gpio_sc18im_port_set_raw(port, 0, 0, (uint8_t)pins);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_pin_interrupt_configure(const struct device *port, gpio_pin_t pin,
|
||||||
|
enum gpio_int_mode mode, enum gpio_int_trig trig)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(port);
|
||||||
|
ARG_UNUSED(pin);
|
||||||
|
ARG_UNUSED(mode);
|
||||||
|
ARG_UNUSED(trig);
|
||||||
|
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gpio_sc18im_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct gpio_sc18im_config *cfg = dev->config;
|
||||||
|
|
||||||
|
if (!device_is_ready(cfg->bridge)) {
|
||||||
|
LOG_ERR("Parent device not ready");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct gpio_driver_api gpio_sc18im_driver_api = {
|
||||||
|
.pin_configure = gpio_sc18im_pin_configure,
|
||||||
|
#ifdef CONFIG_GPIO_GET_CONFIG
|
||||||
|
.pin_get_config = gpio_sc18im_pin_get_config,
|
||||||
|
#endif
|
||||||
|
.port_get_raw = gpio_sc18im_port_get_raw,
|
||||||
|
.port_set_masked_raw = gpio_sc18im_port_set_masked_raw,
|
||||||
|
.port_set_bits_raw = gpio_sc18im_port_set_bits_raw,
|
||||||
|
.port_clear_bits_raw = gpio_sc18im_port_clear_bits_raw,
|
||||||
|
.port_toggle_bits = gpio_sc18im_port_toggle_bits,
|
||||||
|
.pin_interrupt_configure = gpio_sc18im_pin_interrupt_configure,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CHECK_COMPAT(node) \
|
||||||
|
COND_CODE_1(DT_NODE_HAS_COMPAT(node, nxp_sc18im704_i2c), (DEVICE_DT_GET(node)), ())
|
||||||
|
|
||||||
|
#define GPIO_SC18IM704_I2C_SIBLING(n) \
|
||||||
|
DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_PARENT(n), CHECK_COMPAT)
|
||||||
|
|
||||||
|
#define GPIO_SC18IM704_DEFINE(n) \
|
||||||
|
static const struct gpio_sc18im_config gpio_sc18im_config_##n = { \
|
||||||
|
.common = { \
|
||||||
|
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \
|
||||||
|
}, \
|
||||||
|
.bridge = GPIO_SC18IM704_I2C_SIBLING(n), \
|
||||||
|
}; \
|
||||||
|
static struct gpio_sc18im_data gpio_sc18im_data_##n = { \
|
||||||
|
.conf1 = GPIO_SC18IM_DEFAULT_CONF, \
|
||||||
|
.conf2 = GPIO_SC18IM_DEFAULT_CONF, \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, gpio_sc18im_init, NULL, \
|
||||||
|
&gpio_sc18im_data_##n, &gpio_sc18im_config_##n, \
|
||||||
|
POST_KERNEL, CONFIG_GPIO_SC18IM704_INIT_PRIORITY, \
|
||||||
|
&gpio_sc18im_driver_api);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(GPIO_SC18IM704_DEFINE);
|
||||||
23
dts/bindings/gpio/nxp,sc18im704-gpio.yaml
Normal file
23
dts/bindings/gpio/nxp,sc18im704-gpio.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# Copyright (c), 2023 Basalte bv
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: GPIO controller part for the SC18IM704 bridge
|
||||||
|
|
||||||
|
compatible: "nxp,sc18im704-gpio"
|
||||||
|
|
||||||
|
include: gpio-controller.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
"#gpio-cells":
|
||||||
|
required: true
|
||||||
|
const: 2
|
||||||
|
|
||||||
|
ngpios:
|
||||||
|
required: true
|
||||||
|
const: 8
|
||||||
|
|
||||||
|
gpio-cells:
|
||||||
|
- pin
|
||||||
|
- flags
|
||||||
|
|
||||||
|
on-bus: nxp,sc18im704
|
||||||
Loading…
Reference in a new issue