Commit ecac4411 authored by Benedikt Schmidt's avatar Benedikt Schmidt Committed by Christopher Friedt
Browse files

drivers: gpio: implement GPIOs in ADS114S08

Implement GPIO exander within the ADC ADS114S08.

Signed-off-by: default avatarBenedikt Schmidt <benedikt.schmidt@embedded-solutions.at>
parent 53683f61
Loading
Loading
Loading
Loading
+10 −0
Changes for drivers/adc/Kconfig.ads114s0x: 10 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -23,3 +23,13 @@ config ADC_ADS114S0X_ACQUISITION_THREAD_STACK_SIZE
	help
	  Size of the stack used for the internal data acquisition
	  thread.

config ADC_ADS114S0X_GPIO
	bool "GPIO support"
	default n
	depends on GPIO && ADC_ADS114S0X
	help
	  Enable GPIO child device support in the ADS114S0x ADC driver.

	  The GPIO functionality is handled by the ADS114S0x GPIO
	  driver.
+311 −1
Changes for drivers/adc/adc_ads114s0x.c: 311 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/adc/ads114s0x.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
@@ -26,6 +27,7 @@ LOG_MODULE_REGISTER(ads114s0x, CONFIG_ADC_LOG_LEVEL);
#define ADS114S0X_INPUT_SELECTION_AINCOM		    12
#define ADS114S0X_RESOLUTION				    16
#define ADS114S0X_REF_INTERNAL				    2500
#define ADS114S0X_GPIO_MAX				    3
#define ADS114S0X_POWER_ON_RESET_TIME_IN_US		    2200

/* Not mentioned in the datasheet, but instead determined experimentally. */
@@ -272,6 +274,70 @@ enum ads114s0x_register {
#define ADS114S0X_REGISTER_REF_REFCON_SET(target, value)                                           \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_REF_REFCON_POS,             \
				     ADS114S0X_REGISTER_REF_REFCON_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_LENGTH 1
#define ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_POS    7
#define ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_GET(value)                                           \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_POS,             \
				     ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_SET(target, value)                                   \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_POS,     \
				     ADS114S0X_REGISTER_IDACMAG_FL_RAIL_EN_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_PSW_LENGTH 1
#define ADS114S0X_REGISTER_IDACMAG_PSW_POS    6
#define ADS114S0X_REGISTER_IDACMAG_PSW_GET(value)                                                  \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_IDACMAG_PSW_POS,                    \
				     ADS114S0X_REGISTER_IDACMAG_PSW_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_PSW_SET(target, value)                                          \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_IDACMAG_PSW_POS,            \
				     ADS114S0X_REGISTER_IDACMAG_PSW_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_IMAG_LENGTH 4
#define ADS114S0X_REGISTER_IDACMAG_IMAG_POS    0
#define ADS114S0X_REGISTER_IDACMAG_IMAG_GET(value)                                                 \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_IDACMAG_IMAG_POS,                   \
				     ADS114S0X_REGISTER_IDACMAG_IMAG_LENGTH)
#define ADS114S0X_REGISTER_IDACMAG_IMAG_SET(target, value)                                         \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_IDACMAG_IMAG_POS,           \
				     ADS114S0X_REGISTER_IDACMAG_IMAG_LENGTH)
#define ADS114S0X_REGISTER_IDACMUX_I2MUX_LENGTH 4
#define ADS114S0X_REGISTER_IDACMUX_I2MUX_POS	4
#define ADS114S0X_REGISTER_IDACMUX_I2MUX_GET(value)                                                \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_IDACMUX_I2MUX_POS,                  \
				     ADS114S0X_REGISTER_IDACMUX_I2MUX_LENGTH)
#define ADS114S0X_REGISTER_IDACMUX_I2MUX_SET(target, value)                                        \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_IDACMUX_I2MUX_POS,          \
				     ADS114S0X_REGISTER_IDACMUX_I2MUX_LENGTH)
#define ADS114S0X_REGISTER_IDACMUX_I1MUX_LENGTH 4
#define ADS114S0X_REGISTER_IDACMUX_I1MUX_POS	0
#define ADS114S0X_REGISTER_IDACMUX_I1MUX_GET(value)                                                \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_IDACMUX_I1MUX_POS,                  \
				     ADS114S0X_REGISTER_IDACMUX_I1MUX_LENGTH)
#define ADS114S0X_REGISTER_IDACMUX_I1MUX_SET(target, value)                                        \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_IDACMUX_I1MUX_POS,          \
				     ADS114S0X_REGISTER_IDACMUX_I1MUX_LENGTH)
#define ADS114S0X_REGISTER_GPIODAT_DIR_LENGTH 4
#define ADS114S0X_REGISTER_GPIODAT_DIR_POS    4
#define ADS114S0X_REGISTER_GPIODAT_DIR_GET(value)                                                  \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_GPIODAT_DIR_POS,                    \
				     ADS114S0X_REGISTER_GPIODAT_DIR_LENGTH)
#define ADS114S0X_REGISTER_GPIODAT_DIR_SET(target, value)                                          \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_GPIODAT_DIR_POS,            \
				     ADS114S0X_REGISTER_GPIODAT_DIR_LENGTH)
#define ADS114S0X_REGISTER_GPIODAT_DAT_LENGTH 4
#define ADS114S0X_REGISTER_GPIODAT_DAT_POS    0
#define ADS114S0X_REGISTER_GPIODAT_DAT_GET(value)                                                  \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_GPIODAT_DAT_POS,                    \
				     ADS114S0X_REGISTER_GPIODAT_DAT_LENGTH)
#define ADS114S0X_REGISTER_GPIODAT_DAT_SET(target, value)                                          \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_GPIODAT_DAT_POS,            \
				     ADS114S0X_REGISTER_GPIODAT_DAT_LENGTH)
#define ADS114S0X_REGISTER_GPIOCON_CON_LENGTH 4
#define ADS114S0X_REGISTER_GPIOCON_CON_POS    0
#define ADS114S0X_REGISTER_GPIOCON_CON_GET(value)                                                  \
	ADS114S0X_REGISTER_GET_VALUE(value, ADS114S0X_REGISTER_GPIOCON_CON_POS,                    \
				     ADS114S0X_REGISTER_GPIOCON_CON_LENGTH)
#define ADS114S0X_REGISTER_GPIOCON_CON_SET(target, value)                                          \
	ADS114S0X_REGISTER_SET_VALUE(target, value, ADS114S0X_REGISTER_GPIOCON_CON_POS,            \
				     ADS114S0X_REGISTER_GPIOCON_CON_LENGTH)

/*
 * - AIN0 as positive input
@@ -330,12 +396,18 @@ struct ads114s0x_data {
	struct adc_context ctx;
#if CONFIG_ADC_ASYNC
	struct k_thread thread;
#endif
#endif /* CONFIG_ADC_ASYNC */
	struct gpio_callback callback_data_ready;
	struct k_sem data_ready_signal;
	struct k_sem acquire_signal;
	int16_t *buffer;
	int16_t *buffer_ptr;
#if CONFIG_ADC_ADS114S0X_GPIO
	struct k_mutex gpio_lock;
	uint8_t gpio_enabled;	/* one bit per GPIO, 1 = enabled */
	uint8_t gpio_direction; /* one bit per GPIO, 1 = input */
	uint8_t gpio_value;	/* one bit per GPIO, 1 = high */
#endif				/* CONFIG_ADC_ADS114S0X_GPIO */
};

static void ads114s0x_data_ready_handler(const struct device *dev, struct gpio_callback *gpio_cb,
@@ -870,6 +942,227 @@ static void ads114s0x_acquisition_thread(struct device *dev)
}
#endif

#ifdef CONFIG_ADC_ADS114S0X_GPIO
static int ads114s0x_gpio_write_config(const struct device *dev)
{
	struct ads114s0x_data *data = dev->data;
	const struct ads114s0x_config *config = dev->config;
	uint8_t register_addresses[2];
	uint8_t register_values[ARRAY_SIZE(register_addresses)];
	uint8_t gpio_dat = 0;
	uint8_t gpio_con = 0;

	ADS114S0X_REGISTER_GPIOCON_CON_SET(gpio_con, data->gpio_enabled);
	ADS114S0X_REGISTER_GPIODAT_DAT_SET(gpio_dat, data->gpio_value);
	ADS114S0X_REGISTER_GPIODAT_DIR_SET(gpio_dat, data->gpio_direction);

	register_values[0] = gpio_dat;
	register_values[1] = gpio_con;
	register_addresses[0] = ADS114S0X_REGISTER_GPIODAT;
	register_addresses[1] = ADS114S0X_REGISTER_GPIOCON;
	return ads114s0x_write_multiple_registers(&config->bus, register_addresses, register_values,
						  ARRAY_SIZE(register_values));
}

static int ads114s0x_gpio_write_value(const struct device *dev)
{
	struct ads114s0x_data *data = dev->data;
	const struct ads114s0x_config *config = dev->config;
	uint8_t gpio_dat = 0;

	ADS114S0X_REGISTER_GPIODAT_DAT_SET(gpio_dat, data->gpio_value);
	ADS114S0X_REGISTER_GPIODAT_DIR_SET(gpio_dat, data->gpio_direction);

	return ads114s0x_write_register(&config->bus, ADS114S0X_REGISTER_GPIODAT, gpio_dat);
}

int ads114s0x_gpio_set_output(const struct device *dev, uint8_t pin, bool initial_value)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	if (pin > ADS114S0X_GPIO_MAX) {
		LOG_ERR("invalid pin %i", pin);
		return -EINVAL;
	}

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	data->gpio_enabled |= BIT(pin);
	data->gpio_direction &= ~BIT(pin);

	if (initial_value) {
		data->gpio_value |= BIT(pin);
	} else {
		data->gpio_value &= ~BIT(pin);
	}

	result = ads114s0x_gpio_write_config(dev);

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_set_input(const struct device *dev, uint8_t pin)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	if (pin > ADS114S0X_GPIO_MAX) {
		LOG_ERR("invalid pin %i", pin);
		return -EINVAL;
	}

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	data->gpio_enabled |= BIT(pin);
	data->gpio_direction |= BIT(pin);
	data->gpio_value &= ~BIT(pin);

	result = ads114s0x_gpio_write_config(dev);

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_deconfigure(const struct device *dev, uint8_t pin)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	if (pin > ADS114S0X_GPIO_MAX) {
		LOG_ERR("invalid pin %i", pin);
		return -EINVAL;
	}

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	data->gpio_enabled &= ~BIT(pin);
	data->gpio_direction |= BIT(pin);
	data->gpio_value &= ~BIT(pin);

	result = ads114s0x_gpio_write_config(dev);

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_set_pin_value(const struct device *dev, uint8_t pin, bool value)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	if (pin > ADS114S0X_GPIO_MAX) {
		LOG_ERR("invalid pin %i", pin);
		return -EINVAL;
	}

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	if ((BIT(pin) & data->gpio_enabled) == 0) {
		LOG_ERR("gpio pin %i not configured", pin);
		result = -EINVAL;
	} else if ((BIT(pin) & data->gpio_direction) != 0) {
		LOG_ERR("gpio pin %i not configured as output", pin);
		result = -EINVAL;
	} else {
		data->gpio_value |= BIT(pin);

		result = ads114s0x_gpio_write_value(dev);
	}

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_get_pin_value(const struct device *dev, uint8_t pin, bool *value)
{
	struct ads114s0x_data *data = dev->data;
	const struct ads114s0x_config *config = dev->config;
	int result = 0;
	uint8_t gpio_dat;

	if (pin > ADS114S0X_GPIO_MAX) {
		LOG_ERR("invalid pin %i", pin);
		return -EINVAL;
	}

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	if ((BIT(pin) & data->gpio_enabled) == 0) {
		LOG_ERR("gpio pin %i not configured", pin);
		result = -EINVAL;
	} else if ((BIT(pin) & data->gpio_direction) == 0) {
		LOG_ERR("gpio pin %i not configured as input", pin);
		result = -EINVAL;
	} else {
		result = ads114s0x_read_register(&config->bus, ADS114S0X_REGISTER_GPIODAT,
						 &gpio_dat);
		data->gpio_value = ADS114S0X_REGISTER_GPIODAT_DAT_GET(gpio_dat);
		*value = (BIT(pin) & data->gpio_value) != 0;
	}

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_port_get_raw(const struct device *dev, gpio_port_value_t *value)
{
	struct ads114s0x_data *data = dev->data;
	const struct ads114s0x_config *config = dev->config;
	int result = 0;
	uint8_t gpio_dat;

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	result = ads114s0x_read_register(&config->bus, ADS114S0X_REGISTER_GPIODAT, &gpio_dat);
	data->gpio_value = ADS114S0X_REGISTER_GPIODAT_DAT_GET(gpio_dat);
	*value = data->gpio_value;

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
				       gpio_port_value_t value)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	data->gpio_value = ((data->gpio_value & ~mask) | (mask & value)) & data->gpio_enabled &
			   ~data->gpio_direction;
	result = ads114s0x_gpio_write_value(dev);

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

int ads114s0x_gpio_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
{
	struct ads114s0x_data *data = dev->data;
	int result = 0;

	k_mutex_lock(&data->gpio_lock, K_FOREVER);

	data->gpio_value = (data->gpio_value ^ pins) & data->gpio_enabled & ~data->gpio_direction;
	result = ads114s0x_gpio_write_value(dev);

	k_mutex_unlock(&data->gpio_lock);

	return result;
}

#endif /* CONFIG_ADC_ADS114S0X_GPIO */

static int ads114s0x_init(const struct device *dev)
{
	uint8_t status = 0;
@@ -884,6 +1177,10 @@ static int ads114s0x_init(const struct device *dev)
	k_sem_init(&data->data_ready_signal, 0, 1);
	k_sem_init(&data->acquire_signal, 0, 1);

#ifdef CONFIG_ADC_ADS114S0X_GPIO
	k_mutex_init(&data->gpio_lock);
#endif /* CONFIG_ADC_ADS114S0X_GPIO */

	if (!spi_is_ready_dt(&config->bus)) {
		LOG_ERR("SPI device is not ready");
		return -ENODEV;
@@ -986,6 +1283,19 @@ static int ads114s0x_init(const struct device *dev)
		return -EIO;
	}

#ifdef CONFIG_ADC_ADS114S0X_GPIO
	data->gpio_enabled = 0x00;
	data->gpio_direction = 0x0F;
	data->gpio_value = 0x00;

	result = ads114s0x_gpio_write_config(dev);

	if (result != 0) {
		LOG_ERR("unable to configure defaults for GPIOs");
		return result;
	}
#endif

	adc_context_unlock_unconditionally(&data->ctx);

	return result;
+1 −0
Changes for drivers/gpio/CMakeLists.txt: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_STELLARIS gpio_stellaris.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_RPI_PICO   gpio_rpi_pico.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_RV32M1     gpio_rv32m1.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LMP90XXX   gpio_lmp90xxx.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_ADS114S0X  gpio_ads114s0x.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LITEX      gpio_litex.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_LPC11U6X   gpio_lpc11u6x.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_XLNX_AXI   gpio_xlnx_axi.c)
+2 −0
Changes for drivers/gpio/Kconfig: 2 added lines, 0 removed lines.
Original line number Diff line number Diff line
@@ -127,6 +127,8 @@ source "drivers/gpio/Kconfig.rv32m1"

source "drivers/gpio/Kconfig.lmp90xxx"

source "drivers/gpio/Kconfig.ads114s0x"

source "drivers/gpio/Kconfig.litex"

source "drivers/gpio/Kconfig.lpc11u6x"
+25 −0
Changes for drivers/gpio/Kconfig.ads114s0x: 25 added lines, 0 removed lines.
Original line number Diff line number Diff line
# ADS114S0x GPIO configuration options

# Copyright (c) 2023 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0

menuconfig GPIO_ADS114S0X
	bool "ADS114S0x GPIO driver"
	default y
	depends on DT_HAS_TI_ADS114S0X_GPIO_ENABLED
	depends on ADC_ADS114S0X_GPIO
	help
	  Enable GPIO driver for ADS114S0x.

	  The ADS114S0x is a multi-channel analog frontend (AFE).

	  The GPIO port of the ADS114S0x (GPIO0 to GPIO3) is exposed as a
	  GPIO controller driver with read/write support.

config GPIO_ADS114S0X_INIT_PRIORITY
	int "Driver init priority"
	default 99
	depends on GPIO_ADS114S0X
	help
	  Device driver initialization priority. This driver must be
	  initialized after the ADS114S0x ADC driver.
Loading