/* * 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 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, }; 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)