diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 1ad85f63d02..360a3f7d859 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -66,3 +66,4 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_TCA6424A gpio_tca6424a.c) zephyr_library_sources_ifdef(CONFIG_GPIO_SHELL gpio_shell.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE gpio_handlers.c) zephyr_library_sources_ifdef(CONFIG_GPIO_XMC4XXX gpio_xmc4xxx.c) +zephyr_library_sources_ifdef(CONFIG_GPIO_NPM6001 gpio_npm6001.c) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d239e1b69ad..cc520118242 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -156,4 +156,6 @@ source "drivers/gpio/Kconfig.s32" source "drivers/gpio/Kconfig.tca6424a" +source "drivers/gpio/Kconfig.npm6001" + endif # GPIO diff --git a/drivers/gpio/Kconfig.npm6001 b/drivers/gpio/Kconfig.npm6001 new file mode 100644 index 00000000000..e837c37a980 --- /dev/null +++ b/drivers/gpio/Kconfig.npm6001 @@ -0,0 +1,18 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config GPIO_NPM6001 + bool "nPM6001 GPIO driver" + default y + depends on DT_HAS_NORDIC_NPM6001_GPIO_ENABLED + select I2C + help + Enable the nPM6001 GPIO driver. + +config GPIO_NPM6001_INIT_PRIORITY + int "nPM6001 GPIO driver initialization priority" + depends on GPIO_NPM6001 + default 60 + help + Initialization priority for the nPM6001 GPIO driver. It must be + greater than the I2C controller init priority. diff --git a/drivers/gpio/gpio_npm6001.c b/drivers/gpio/gpio_npm6001.c new file mode 100644 index 00000000000..98bd4db49e0 --- /dev/null +++ b/drivers/gpio/gpio_npm6001.c @@ -0,0 +1,245 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nordic_npm6001_gpio + +#include + +#include +#include +#include +#include +#include +#include +#include + +/* nPM6001 GPIO related registers */ +#define NPM6001_GPIOOUTSET 0x69U +#define NPM6001_GPIOOUTCLR 0x6AU +#define NPM6001_GPIOIN 0x6BU +#define NPM6001_GPIO0CONF 0x6CU +#define NPM6001_GPIO1CONF 0x6DU +#define NPM6001_GPIO2CONF 0x6EU + +/* GPIO(0|1|2)CONF fields */ +#define NPM6001_CONF_DIRECTION_OUTPUT BIT(0) +#define NPM6001_CONF_INPUT_ENABLED BIT(1) +#define NPM6001_CONF_PULLDOWN_ENABLED BIT(2) +#define NPM6001_CONF_DRIVE_HIGH BIT(5) +#define NPM6001_CONF_SENSE_CMOS BIT(6) + +#define NPM6001_PIN_MAX 2U +#define NPM6001_PIN_MSK 0x7U + +struct gpio_npm6001_config { + struct gpio_driver_config common; + struct i2c_dt_spec bus; +}; + +struct gpio_npm6001_data { + struct gpio_driver_data common; +}; + +static int gpio_npm6001_port_get_raw(const struct device *dev, uint32_t *value) +{ + const struct gpio_npm6001_config *config = dev->config; + uint8_t reg = NPM6001_GPIOIN; + uint8_t val; + int ret; + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + ret = i2c_write_read_dt(&config->bus, ®, sizeof(reg), &val, + sizeof(val)); + if (ret < 0) { + return ret; + } + + *value = val; + + return 0; +} + +static int gpio_npm6001_port_set_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_npm6001_config *config = dev->config; + uint8_t buf[2] = {NPM6001_GPIOOUTSET, (uint8_t)pins & NPM6001_PIN_MSK}; + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + return i2c_write_dt(&config->bus, buf, sizeof(buf)); +} + +static int gpio_npm6001_port_clear_bits_raw(const struct device *dev, + gpio_port_pins_t pins) +{ + const struct gpio_npm6001_config *config = dev->config; + uint8_t buf[2] = {NPM6001_GPIOOUTCLR, (uint8_t)pins & NPM6001_PIN_MSK}; + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + return i2c_write_dt(&config->bus, buf, sizeof(buf)); +} + +static inline int gpio_npm6001_configure(const struct device *dev, + gpio_pin_t pin, gpio_flags_t flags) +{ + const struct gpio_npm6001_config *config = dev->config; + uint8_t buf[2] = {NPM6001_GPIO0CONF, 0U}; + + if (k_is_in_isr()) { + return -EWOULDBLOCK; + } + + if (pin > NPM6001_PIN_MAX) { + return -EINVAL; + } + + /* select GPIO0CONF/GPIO1CONF/GPIO2CONF */ + buf[0] += pin; + + if ((flags & GPIO_OUTPUT) != 0U) { + /* input buffer enabled to allow reading output level */ + buf[1] |= NPM6001_CONF_DIRECTION_OUTPUT | + NPM6001_CONF_INPUT_ENABLED; + + /* open-drain/open-source not supported */ + if ((flags & GPIO_SINGLE_ENDED) != 0U) { + return -ENOTSUP; + } + + /* drive strength (defaults to normal) */ + if ((flags & NPM6001_GPIO_DRIVE_MSK) == + NPM6001_GPIO_DRIVE_HIGH) { + buf[1] |= NPM6001_CONF_DRIVE_HIGH; + } + + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0U) { + int ret; + + ret = gpio_npm6001_port_set_bits_raw( + dev, (gpio_port_pins_t)BIT(pin)); + if (ret < 0) { + return ret; + } + } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0U) { + int ret; + + ret = gpio_npm6001_port_clear_bits_raw( + dev, (gpio_port_pins_t)BIT(pin)); + if (ret < 0) { + return ret; + } + } + } else if ((flags & GPIO_INPUT) != 0U) { + buf[1] |= NPM6001_CONF_INPUT_ENABLED; + + /* pull resistor */ + if ((flags & GPIO_PULL_DOWN) != 0U) { + buf[1] |= NPM6001_CONF_PULLDOWN_ENABLED; + } else if ((flags & GPIO_PULL_UP) != 0U) { + return -ENOTSUP; + } + + /* input type (defaults to schmitt trigger) */ + if ((flags & NPM6001_GPIO_SENSE_MSK) == + NPM6001_GPIO_SENSE_CMOS) { + buf[1] |= NPM6001_CONF_SENSE_CMOS; + } + } else { + return -ENOTSUP; + } + + return i2c_write_dt(&config->bus, buf, sizeof(buf)); +} + +static int gpio_npm6001_port_set_masked_raw(const struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + int ret; + + ret = gpio_npm6001_port_set_bits_raw(dev, mask & value); + if (ret < 0) { + return ret; + } + + return gpio_npm6001_port_clear_bits_raw( + dev, mask & (~value & NPM6001_PIN_MSK)); +} + +static int gpio_npm6001_port_toggle_bits(const struct device *dev, + gpio_port_pins_t pins) +{ + uint32_t val; + int ret; + + ret = gpio_npm6001_port_get_raw(dev, &val); + if (ret < 0) { + return ret; + } + + return gpio_npm6001_port_set_masked_raw(dev, pins, + ~val & NPM6001_PIN_MSK); +} + +static int gpio_npm6001_pin_interrupt_configure(const struct device *dev, + gpio_pin_t pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + ARG_UNUSED(dev); + ARG_UNUSED(pin); + ARG_UNUSED(mode); + ARG_UNUSED(trig); + + return -ENOTSUP; +} + +static const struct gpio_driver_api gpio_npm6001_api = { + .pin_configure = gpio_npm6001_configure, + .port_get_raw = gpio_npm6001_port_get_raw, + .port_set_masked_raw = gpio_npm6001_port_set_masked_raw, + .port_set_bits_raw = gpio_npm6001_port_set_bits_raw, + .port_clear_bits_raw = gpio_npm6001_port_clear_bits_raw, + .port_toggle_bits = gpio_npm6001_port_toggle_bits, + .pin_interrupt_configure = gpio_npm6001_pin_interrupt_configure, +}; + +static int gpio_npm6001_init(const struct device *dev) +{ + const struct gpio_npm6001_config *config = dev->config; + + if (!device_is_ready(config->bus.bus)) { + return -ENODEV; + } + + return 0; +} + +#define GPIO_NPM6001_DEFINE(n) \ + static const struct gpio_npm6001_config gpio_npm6001_config##n = { \ + .common = \ + { \ + .port_pin_mask = \ + GPIO_PORT_PIN_MASK_FROM_DT_INST(n), \ + }, \ + .bus = I2C_DT_SPEC_GET(DT_INST_PARENT(n))}; \ + \ + static struct gpio_npm6001_data gpio_npm6001_data##n; \ + \ + DEVICE_DT_INST_DEFINE(n, &gpio_npm6001_init, NULL, \ + &gpio_npm6001_data##n, &gpio_npm6001_config##n, \ + POST_KERNEL, CONFIG_GPIO_NPM6001_INIT_PRIORITY, \ + &gpio_npm6001_api); + +DT_INST_FOREACH_STATUS_OKAY(GPIO_NPM6001_DEFINE) diff --git a/dts/bindings/gpio/nordic,npm6001-gpio.yaml b/dts/bindings/gpio/nordic,npm6001-gpio.yaml new file mode 100644 index 00000000000..a6ab15449ac --- /dev/null +++ b/dts/bindings/gpio/nordic,npm6001-gpio.yaml @@ -0,0 +1,12 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +description: nPM6001 GPIO Controller + +compatible: "nordic,npm6001-gpio" + +include: gpio-controller.yaml + +gpio-cells: + - pin + - flags diff --git a/include/zephyr/dt-bindings/gpio/nordic-npm6001-gpio.h b/include/zephyr/dt-bindings/gpio/nordic-npm6001-gpio.h new file mode 100644 index 00000000000..72eebd5d264 --- /dev/null +++ b/include/zephyr/dt-bindings/gpio/nordic-npm6001-gpio.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2022 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NORDIC_NPM6001_GPIO_H_ +#define ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NORDIC_NPM6001_GPIO_H_ + +/** + * @brief nPM6001-specific GPIO Flags + * @defgroup gpio_interface_npm6001 nPM6001-specific GPIO Flags + * + * The drive flags are encoded in the 8 upper bits of @ref gpio_dt_flags_t as + * follows: + * + * - Bit 8: Drive strength (0=NORMAL, 1=HIGH) + * - Bit 9: Input type (0=SCHMITT, 1=CMOS) + * + * @ingroup gpio_interface + * @{ + */ + +/** + * @name nPM6001 GPIO drive strength flags + * @brief nPM6001 GPIO drive strength flags + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ +/** Drive mode field mask */ +#define NPM6001_GPIO_DRIVE_MSK 0x0100U +/** @endcond */ + +/** Normal drive */ +#define NPM6001_GPIO_DRIVE_NORMAL (0U << 8U) +/** High drive */ +#define NPM6001_GPIO_DRIVE_HIGH (1U << 8U) + +/** @} */ + +/** + * @name nPM6001 GPIO drive strength flags + * @brief nPM6001 GPIO drive strength flags + * @{ + */ + +/** @cond INTERNAL_HIDDEN */ +/** Input type field mask */ +#define NPM6001_GPIO_SENSE_MSK 0x0200U +/** @endcond */ + +/** Schmitt trigger input type */ +#define NPM6001_GPIO_SENSE_SCHMITT (0U << 9U) +/** CMOS input type */ +#define NPM6001_GPIO_SENSE_CMOS (1U << 9U) + +/** @} */ + +/** @} */ + +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_GPIO_NORDIC_NRF_GPIO_H_ */