-
Notifications
You must be signed in to change notification settings - Fork 8.4k
drivers: gpio: Introduce SC18IS606 GPIO Extension drivers #100743
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| # Copyright (c) 2025 tinyvision.ai | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| config GPIO_SC18IS606 | ||
| bool "NXP SC18IS606 GPIO Controller driver" | ||
| default y | ||
| depends on SPI_SC18IS606 | ||
| depends on DT_HAS_NXP_SC18IS606_GPIO_ENABLED | ||
| help | ||
| Enables NXP SC18IS606 gpio controller driver | ||
|
|
||
| config GPIO_SC18IS606_INIT_PRIORITY | ||
| int "SC18IS606 GPIO Init Priority" | ||
| default 66 | ||
| depends on GPIO_SC18IS606 | ||
| help | ||
| SC18IS606 GPIO Controller Init priority | ||
|
|
||
| Note: Has to be greater than the parent SC18IS606 bridge init priority |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,252 @@ | ||
| /* | ||
| * Copyright (c), 2025 tinyvision.ai | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #define DT_DRV_COMPAT nxp_sc18is606_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/sys/util.h> | ||
|
|
||
| #include <zephyr/logging/log.h> | ||
| LOG_MODULE_REGISTER(nxp_sc18is606_gpio, CONFIG_GPIO_LOG_LEVEL); | ||
|
|
||
| #include "spi/spi_sc18is606.h" | ||
|
|
||
| #define SC18IS606_GPIO_MAX_PINS 3 | ||
|
|
||
| #define SC18IS606_GPIO_WRITE 0xF4 | ||
| #define SC18IS606_GPIO_READ 0xF5 | ||
| #define SC18IS606_GPIO_ENABLE 0xF6 | ||
| #define SC18IS606_GPIO_CONF 0xF7 | ||
|
|
||
| #define SC18IS606_GPIO_CONF_INPUT 0x00 | ||
| #define SC18IS606_GPIO_CONF_PUSH_PULL 0x01 | ||
| #define SC18IS606_GPIO_CONF_OPEN_DRAIN 0x03 | ||
| #define SC18IS606_GPIO_CONF_MASK 0x03 | ||
|
|
||
| #define SC18IS606_GPIO_ENABLE_MASK GENMASK(2, 0) | ||
|
|
||
| struct gpio_sc18is606_config { | ||
| struct gpio_driver_config common; | ||
|
|
||
| const struct device *bridge; | ||
| }; | ||
|
|
||
| struct gpio_sc18is606_data { | ||
| struct gpio_driver_data sc18is606_data; | ||
|
|
||
| /* current port state */ | ||
| uint8_t output_state; | ||
|
|
||
| /* current port pin config */ | ||
| uint8_t conf; | ||
| }; | ||
|
|
||
| static int gpio_sc18is606_port_set_raw(const struct device *port, uint8_t mask, uint8_t value, | ||
| uint8_t toggle) | ||
| { | ||
| const struct gpio_sc18is606_config *cfg = port->config; | ||
| struct gpio_sc18is606_data *data = port->data; | ||
|
|
||
| uint8_t buf[] = { | ||
| SC18IS606_GPIO_WRITE, | ||
| data->output_state, | ||
| }; | ||
| int ret; | ||
|
|
||
| if (k_is_in_isr()) { | ||
| return -EWOULDBLOCK; | ||
| } | ||
|
|
||
| buf[1] &= ~mask; | ||
| buf[1] |= (value & mask); | ||
| buf[1] ^= toggle; | ||
|
|
||
| ret = nxp_sc18is606_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0, NULL); | ||
|
|
||
| if (ret < 0) { | ||
| LOG_ERR("Failed to write to GPIO (%d)", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| data->output_state = buf[1]; | ||
|
Comment on lines
+68
to
+79
|
||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int gpio_sc18is606_pin_configure(const struct device *port, gpio_pin_t pin, | ||
| gpio_flags_t flags) | ||
| { | ||
| const struct gpio_sc18is606_config *cfg = port->config; | ||
| struct gpio_sc18is606_data *data = port->data; | ||
| uint8_t pin_conf; | ||
| uint8_t pin_enable; | ||
| int ret; | ||
|
|
||
| uint8_t buf[] = { | ||
| SC18IS606_GPIO_CONF, | ||
| 0x00, | ||
| }; | ||
|
|
||
| uint8_t enable_buf[] = { | ||
| SC18IS606_GPIO_ENABLE, | ||
| 0x00, | ||
| }; | ||
|
|
||
| if (pin >= SC18IS606_GPIO_MAX_PINS) { | ||
| return -EINVAL; | ||
| } | ||
|
|
||
| if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) { | ||
| return -ENOTSUP; | ||
| } | ||
|
|
||
| if (flags & GPIO_INPUT) { | ||
| pin_conf = SC18IS606_GPIO_CONF_INPUT; | ||
| } else if (flags & GPIO_OUTPUT) { | ||
| if (flags & GPIO_SINGLE_ENDED) { | ||
| if (flags & GPIO_LINE_OPEN_DRAIN) { | ||
| pin_conf = SC18IS606_GPIO_CONF_OPEN_DRAIN; | ||
| } else { | ||
| return -ENOTSUP; | ||
| } | ||
| } else { | ||
| pin_conf = SC18IS606_GPIO_CONF_PUSH_PULL; | ||
| } | ||
| } else { | ||
| /* Impossible option */ | ||
| return -ENOTSUP; | ||
| } | ||
|
|
||
| ret = nxp_sc18is606_claim(cfg->bridge); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to claim the bridge (%d)", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| pin_enable = FIELD_PREP(SC18IS606_GPIO_ENABLE_MASK, (1 << pin)); | ||
|
|
||
| enable_buf[1] = pin_enable; | ||
|
|
||
| ret = nxp_sc18is606_transfer(cfg->bridge, enable_buf, sizeof(enable_buf), NULL, 0, NULL); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to enable GPIO (%d)", ret); | ||
| } | ||
|
Comment on lines
+138
to
+141
|
||
|
|
||
| data->conf &= ~(SC18IS606_GPIO_CONF_MASK << (pin * 2)); | ||
| data->conf |= (pin_conf & SC18IS606_GPIO_CONF_MASK) << (pin * 2); | ||
| buf[1] = data->conf; | ||
|
|
||
| ret = nxp_sc18is606_transfer(cfg->bridge, buf, sizeof(buf), NULL, 0, NULL); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to configure GPIO (%d)", ret); | ||
| } | ||
|
Comment on lines
+147
to
+150
|
||
|
|
||
| if (ret == 0 && flags & GPIO_OUTPUT) { | ||
| if (flags & GPIO_OUTPUT_INIT_HIGH) { | ||
| gpio_sc18is606_port_set_raw(port, BIT(pin), BIT(pin), 0); | ||
| } else if (flags & GPIO_OUTPUT_INIT_LOW) { | ||
| gpio_sc18is606_port_set_raw(port, BIT(pin), 0, 0); | ||
| } | ||
| } | ||
|
|
||
| nxp_sc18is606_release(cfg->bridge); | ||
|
|
||
| return ret; | ||
| } | ||
|
|
||
| static int gpio_sc18is606_port_get_raw(const struct device *port, gpio_port_value_t *value) | ||
| { | ||
| const struct gpio_sc18is606_config *cfg = port->config; | ||
|
|
||
| uint8_t buf[] = { | ||
| SC18IS606_GPIO_READ, | ||
| }; | ||
| uint8_t data; | ||
| int ret; | ||
|
|
||
| if (k_is_in_isr()) { | ||
| return -EWOULDBLOCK; | ||
| } | ||
|
|
||
| ret = nxp_sc18is606_transfer(cfg->bridge, buf, sizeof(buf), &data, 1, NULL); | ||
| if (ret < 0) { | ||
| LOG_ERR("Failed to read GPIO state (%d)", ret); | ||
| return ret; | ||
| } | ||
|
|
||
| *value = data; | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static int gpio_sc18is606_port_set_masked_raw(const struct device *port, gpio_port_pins_t mask, | ||
| gpio_port_value_t value) | ||
| { | ||
| return gpio_sc18is606_port_set_raw(port, (uint8_t)mask, (uint8_t)value, 0); | ||
| } | ||
|
|
||
| static int gpio_sc18is606_port_set_bits_raw(const struct device *port, gpio_port_pins_t pins) | ||
| { | ||
| return gpio_sc18is606_port_set_raw(port, (uint8_t)pins, (uint8_t)pins, 0); | ||
| } | ||
|
|
||
| static int gpio_sc18is606_port_clear_bits_raw(const struct device *port, gpio_port_pins_t pins) | ||
| { | ||
| return gpio_sc18is606_port_set_raw(port, (uint8_t)pins, 0, 0); | ||
| } | ||
| static int gpio_sc18is606_port_toggle_bits(const struct device *port, gpio_port_pins_t pins) | ||
| { | ||
| return gpio_sc18is606_port_set_raw(port, 0, 0, (uint8_t)pins); | ||
| } | ||
|
|
||
| static int gpio_sc18is606_init(const struct device *dev) | ||
| { | ||
| const struct gpio_sc18is606_config *cfg = dev->config; | ||
|
|
||
| if (!device_is_ready(cfg->bridge)) { | ||
| LOG_ERR("Parent device not ready"); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| return 0; | ||
| } | ||
|
|
||
| static DEVICE_API(gpio, gpio_sc18is606_driver_api) = { | ||
| .pin_configure = gpio_sc18is606_pin_configure, | ||
| .port_get_raw = gpio_sc18is606_port_get_raw, | ||
| .port_set_masked_raw = gpio_sc18is606_port_set_masked_raw, | ||
| .port_set_bits_raw = gpio_sc18is606_port_set_bits_raw, | ||
| .port_clear_bits_raw = gpio_sc18is606_port_clear_bits_raw, | ||
| .port_toggle_bits = gpio_sc18is606_port_toggle_bits, | ||
| }; | ||
|
|
||
| #define CHECK_COMPAT(node) \ | ||
| COND_CODE_1(DT_NODE_HAS_COMPAT(node, nxp_sc18is606_spi), (DEVICE_DT_GET(node)), ()) | ||
|
|
||
| #define GPIO_SC18IS606_SPI_SIBLING(n) DT_FOREACH_CHILD_STATUS_OKAY(DT_INST_PARENT(n), CHECK_COMPAT) | ||
|
|
||
| #define GPIO_SC18IS606_DEFINE(inst) \ | ||
| static const struct gpio_sc18is606_config gpio_sc18is606_config##inst = { \ | ||
| .common = \ | ||
| { \ | ||
| .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \ | ||
| }, \ | ||
| .bridge = GPIO_SC18IS606_SPI_SIBLING(inst), \ | ||
| }; \ | ||
| static struct gpio_sc18is606_data gpio_sc18is606_data##inst = { \ | ||
| .conf = 0x00, \ | ||
| }; \ | ||
| \ | ||
| DEVICE_DT_INST_DEFINE(inst, gpio_sc18is606_init, NULL, &gpio_sc18is606_data##inst, \ | ||
| &gpio_sc18is606_config##inst, POST_KERNEL, \ | ||
| CONFIG_GPIO_SC18IS606_INIT_PRIORITY, &gpio_sc18is606_driver_api); | ||
|
|
||
| DT_INST_FOREACH_STATUS_OKAY(GPIO_SC18IS606_DEFINE); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| # Copyright (c), 2025 tinyvision.ai | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
|
|
||
| description: GPIO Controller part for the SC18IS606 bridge. | ||
|
|
||
| compatible: "nxp,sc18is606-gpio" | ||
|
|
||
| include: gpio-controller.yaml | ||
|
|
||
| properties: | ||
| "#gpio-cells": | ||
| required: true | ||
| const: 2 | ||
|
|
||
| ngpios: | ||
| required: true | ||
| const: 3 | ||
|
|
||
| gpio-cells: | ||
| - pin | ||
| - flags | ||
|
|
||
| on-bus: nxp,sc18is606 |
Uh oh!
There was an error while loading. Please reload this page.