| /* | 
 |  * Copyright (c) 2023 SILA Embedded Solutions GmbH | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 | #include <zephyr/device.h> | 
 | #include <zephyr/devicetree.h> | 
 | #include <zephyr/drivers/adc.h> | 
 | #include <zephyr/drivers/spi.h> | 
 | #include <zephyr/drivers/gpio.h> | 
 | #include <zephyr/logging/log.h> | 
 | #include <zephyr/kernel.h> | 
 | #include <zephyr/sys/byteorder.h> | 
 | #include <zephyr/sys/util.h> | 
 |  | 
 | #define ADC_CONTEXT_USES_KERNEL_TIMER 1 | 
 | #include "adc_context.h" | 
 |  | 
 | LOG_MODULE_REGISTER(max11102_17, CONFIG_ADC_LOG_LEVEL); | 
 |  | 
 | struct max11102_17_config { | 
 | 	struct spi_dt_spec bus; | 
 | 	const struct gpio_dt_spec gpio_chsel; | 
 | 	uint8_t resolution; | 
 | 	uint8_t channel_count; | 
 | }; | 
 |  | 
 | struct max11102_17_data { | 
 | 	struct adc_context ctx; | 
 | 	struct k_sem acquire_signal; | 
 | 	int16_t *buffer; | 
 | 	int16_t *buffer_ptr; | 
 | 	uint8_t current_channel_id; | 
 | 	uint8_t sequence_channel_id; | 
 | #if CONFIG_ADC_ASYNC | 
 | 	struct k_thread thread; | 
 |  | 
 | 	K_KERNEL_STACK_MEMBER(stack, CONFIG_ADC_MAX11102_17_ACQUISITION_THREAD_STACK_SIZE); | 
 | #endif /* CONFIG_ADC_ASYNC */ | 
 | }; | 
 |  | 
 | static int max11102_17_switch_channel(const struct device *dev) | 
 | { | 
 | 	const struct max11102_17_config *config = dev->config; | 
 | 	struct max11102_17_data *data = dev->data; | 
 | 	int result; | 
 | 	uint8_t buffer_rx[1]; | 
 | 	const struct spi_buf rx_buf[] = {{ | 
 | 		.buf = buffer_rx, | 
 | 		.len = ARRAY_SIZE(buffer_rx), | 
 | 	}}; | 
 | 	const struct spi_buf_set rx = { | 
 | 		.buffers = rx_buf, | 
 | 		.count = ARRAY_SIZE(rx_buf), | 
 | 	}; | 
 | 	struct spi_dt_spec bus; | 
 |  | 
 | 	memcpy(&bus, &config->bus, sizeof(bus)); | 
 | 	bus.config.operation |= SPI_HOLD_ON_CS; | 
 |  | 
 | 	result = spi_read_dt(&bus, &rx); | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("read failed with error %i", result); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	gpio_pin_set_dt(&config->gpio_chsel, data->current_channel_id); | 
 |  | 
 | 	result = spi_read_dt(&config->bus, &rx); | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("read failed with error %i", result); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int max11102_17_channel_setup(const struct device *dev, | 
 | 				     const struct adc_channel_cfg *channel_cfg) | 
 | { | 
 | 	const struct max11102_17_config *config = dev->config; | 
 |  | 
 | 	LOG_DBG("read from ADC channel %i", channel_cfg->channel_id); | 
 |  | 
 | 	if (channel_cfg->reference != ADC_REF_EXTERNAL0) { | 
 | 		LOG_ERR("invalid reference %i", channel_cfg->reference); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (channel_cfg->gain != ADC_GAIN_1) { | 
 | 		LOG_ERR("invalid gain %i", channel_cfg->gain); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (channel_cfg->acquisition_time != ADC_ACQ_TIME_DEFAULT) { | 
 | 		LOG_ERR("invalid acquisition time %i", channel_cfg->acquisition_time); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (channel_cfg->differential != 0) { | 
 | 		LOG_ERR("differential inputs are not supported"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (channel_cfg->channel_id > config->channel_count) { | 
 | 		LOG_ERR("invalid channel selection %i", channel_cfg->channel_id); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int max11102_17_validate_buffer_size(const struct adc_sequence *sequence) | 
 | { | 
 | 	size_t necessary = sizeof(int16_t); | 
 |  | 
 | 	if (sequence->options) { | 
 | 		necessary *= (1 + sequence->options->extra_samplings); | 
 | 	} | 
 |  | 
 | 	if (sequence->buffer_size < necessary) { | 
 | 		return -ENOMEM; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int max11102_17_validate_sequence(const struct device *dev, | 
 | 					 const struct adc_sequence *sequence) | 
 | { | 
 | 	const struct max11102_17_config *config = dev->config; | 
 | 	struct max11102_17_data *data = dev->data; | 
 | 	size_t sequence_channel_count = 0; | 
 | 	const size_t channel_maximum = 8*sizeof(sequence->channels); | 
 |  | 
 | 	if (sequence->resolution != config->resolution) { | 
 | 		LOG_ERR("invalid resolution"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	for (size_t i = 0; i < channel_maximum; ++i) { | 
 | 		if ((BIT(i) & sequence->channels) == 0) { | 
 | 			continue; | 
 | 		} | 
 |  | 
 | 		if (i > config->channel_count) { | 
 | 			LOG_ERR("invalid channel selection"); | 
 | 			return -EINVAL; | 
 | 		} | 
 |  | 
 | 		sequence_channel_count++; | 
 | 		data->sequence_channel_id = i; | 
 | 	} | 
 |  | 
 | 	if (sequence_channel_count == 0) { | 
 | 		LOG_ERR("no channel selected"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (sequence_channel_count > 1) { | 
 | 		LOG_ERR("multiple channels selected"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	if (sequence->oversampling) { | 
 | 		LOG_ERR("oversampling is not supported"); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	return max11102_17_validate_buffer_size(sequence); | 
 | } | 
 |  | 
 | static void adc_context_update_buffer_pointer(struct adc_context *ctx, bool repeat_sampling) | 
 | { | 
 | 	struct max11102_17_data *data = CONTAINER_OF(ctx, struct max11102_17_data, ctx); | 
 |  | 
 | 	if (repeat_sampling) { | 
 | 		data->buffer = data->buffer_ptr; | 
 | 	} | 
 | } | 
 |  | 
 | static void adc_context_start_sampling(struct adc_context *ctx) | 
 | { | 
 | 	struct max11102_17_data *data = CONTAINER_OF(ctx, struct max11102_17_data, ctx); | 
 |  | 
 | 	data->buffer_ptr = data->buffer; | 
 | 	k_sem_give(&data->acquire_signal); | 
 | } | 
 |  | 
 | static int max11102_17_adc_start_read(const struct device *dev, const struct adc_sequence *sequence, | 
 | 				      bool wait) | 
 | { | 
 | 	int result; | 
 | 	struct max11102_17_data *data = dev->data; | 
 |  | 
 | 	result = max11102_17_validate_sequence(dev, sequence); | 
 |  | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("sequence validation failed"); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	data->buffer = sequence->buffer; | 
 |  | 
 | 	adc_context_start_read(&data->ctx, sequence); | 
 |  | 
 | 	if (wait) { | 
 | 		result = adc_context_wait_for_completion(&data->ctx); | 
 | 	} | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static int max11102_17_read_sample(const struct device *dev, int16_t *sample) | 
 | { | 
 | 	const struct max11102_17_config *config = dev->config; | 
 | 	int result; | 
 | 	size_t trailing_bits = 15 - config->resolution; | 
 | 	uint8_t buffer_rx[2]; | 
 | 	const struct spi_buf rx_buf[] = {{ | 
 | 		.buf = buffer_rx, | 
 | 		.len = ARRAY_SIZE(buffer_rx), | 
 | 	}}; | 
 | 	const struct spi_buf_set rx = { | 
 | 		.buffers = rx_buf, | 
 | 		.count = ARRAY_SIZE(rx_buf), | 
 | 	}; | 
 |  | 
 | 	result = spi_read_dt(&config->bus, &rx); | 
 |  | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("read failed with error %i", result); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	*sample = sys_get_be16(buffer_rx); | 
 | 	LOG_DBG("raw sample: 0x%04X", *sample); | 
 |  | 
 | 	*sample = *sample >> trailing_bits; | 
 | 	*sample = *sample & GENMASK(config->resolution, 0); | 
 | 	LOG_DBG("sample: 0x%04X", *sample); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int max11102_17_adc_perform_read(const struct device *dev) | 
 | { | 
 | 	int result; | 
 | 	struct max11102_17_data *data = dev->data; | 
 |  | 
 | 	k_sem_take(&data->acquire_signal, K_FOREVER); | 
 |  | 
 | 	if (data->sequence_channel_id != data->current_channel_id) { | 
 | 		LOG_DBG("switch channel selection"); | 
 | 		data->current_channel_id = data->sequence_channel_id; | 
 | 		max11102_17_switch_channel(dev); | 
 | 	} | 
 |  | 
 | 	result = max11102_17_read_sample(dev, data->buffer); | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("reading sample failed"); | 
 | 		adc_context_complete(&data->ctx, result); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	data->buffer++; | 
 |  | 
 | 	adc_context_on_sampling_done(&data->ctx, dev); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | #if CONFIG_ADC_ASYNC | 
 | static int max11102_17_adc_read_async(const struct device *dev, const struct adc_sequence *sequence, | 
 | 				      struct k_poll_signal *async) | 
 | { | 
 | 	int result; | 
 | 	struct max11102_17_data *data = dev->data; | 
 |  | 
 | 	adc_context_lock(&data->ctx, true, async); | 
 | 	result = max11102_17_adc_start_read(dev, sequence, true); | 
 | 	adc_context_release(&data->ctx, result); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static int max11102_17_read(const struct device *dev, const struct adc_sequence *sequence) | 
 | { | 
 | 	int result; | 
 | 	struct max11102_17_data *data = dev->data; | 
 |  | 
 | 	adc_context_lock(&data->ctx, false, NULL); | 
 | 	result = max11102_17_adc_start_read(dev, sequence, true); | 
 | 	adc_context_release(&data->ctx, result); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | #else | 
 | static int max11102_17_read(const struct device *dev, const struct adc_sequence *sequence) | 
 | { | 
 | 	int result; | 
 | 	struct max11102_17_data *data = dev->data; | 
 |  | 
 | 	adc_context_lock(&data->ctx, false, NULL); | 
 | 	result = max11102_17_adc_start_read(dev, sequence, false); | 
 |  | 
 | 	while (result == 0 && k_sem_take(&data->ctx.sync, K_NO_WAIT) != 0) { | 
 | 		result = max11102_17_adc_perform_read(dev); | 
 | 	} | 
 |  | 
 | 	adc_context_release(&data->ctx, result); | 
 | 	return result; | 
 | } | 
 | #endif | 
 |  | 
 | #if CONFIG_ADC_ASYNC | 
 | static void max11102_17_acquisition_thread(void *p1, void *p2, void *p3) | 
 | { | 
 | 	ARG_UNUSED(p2); | 
 | 	ARG_UNUSED(p3); | 
 |  | 
 | 	const struct device *dev = p1; | 
 | 	while (true) { | 
 | 		max11102_17_adc_perform_read(dev); | 
 | 	} | 
 | } | 
 | #endif | 
 |  | 
 | static int max11102_17_init(const struct device *dev) | 
 | { | 
 | 	int result; | 
 | 	const struct max11102_17_config *config = dev->config; | 
 | 	struct max11102_17_data *data = dev->data; | 
 | 	int16_t sample; | 
 |  | 
 | 	adc_context_init(&data->ctx); | 
 |  | 
 | 	k_sem_init(&data->acquire_signal, 0, 1); | 
 |  | 
 | 	if (!spi_is_ready_dt(&config->bus)) { | 
 | 		LOG_ERR("SPI device is not ready"); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	switch (config->channel_count) { | 
 | 	case 1: | 
 | 		if (config->gpio_chsel.port != NULL) { | 
 | 			LOG_ERR("GPIO for chsel set with only one channel"); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		break; | 
 | 	case 2: | 
 | 		if (config->gpio_chsel.port == NULL) { | 
 | 			LOG_ERR("no GPIO for chsel set with two channels"); | 
 | 			return -EINVAL; | 
 | 		} | 
 |  | 
 | 		result = gpio_pin_configure_dt(&config->gpio_chsel, GPIO_OUTPUT_INACTIVE); | 
 | 		if (result != 0) { | 
 | 			LOG_ERR("failed to initialize GPIO for chsel"); | 
 | 			return result; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 		LOG_ERR("invalid number of channels (%i)", config->channel_count); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	data->current_channel_id = 0; | 
 |  | 
 | #if CONFIG_ADC_ASYNC | 
 | 	k_tid_t tid = k_thread_create( | 
 | 		&data->thread, data->stack, CONFIG_ADC_MAX11102_17_ACQUISITION_THREAD_STACK_SIZE, | 
 | 		max11102_17_acquisition_thread, (void *)dev, NULL, NULL, | 
 | 		CONFIG_ADC_MAX11102_17_ACQUISITION_THREAD_INIT_PRIO, 0, K_NO_WAIT); | 
 | 	k_thread_name_set(tid, "adc_max11102_17"); | 
 | #endif | 
 |  | 
 | 	/* power up time is one conversion cycle */ | 
 | 	result = max11102_17_read_sample(dev, &sample); | 
 | 	if (result != 0) { | 
 | 		LOG_ERR("unable to read dummy sample for power up timing"); | 
 | 		return result; | 
 | 	} | 
 |  | 
 | 	adc_context_unlock_unconditionally(&data->ctx); | 
 |  | 
 | 	return result; | 
 | } | 
 |  | 
 | static const struct adc_driver_api api = { | 
 | 	.channel_setup = max11102_17_channel_setup, | 
 | 	.read = max11102_17_read, | 
 | 	.ref_internal = 0, | 
 | #ifdef CONFIG_ADC_ASYNC | 
 | 	.read_async = max11102_17_adc_read_async, | 
 | #endif | 
 | }; | 
 |  | 
 | BUILD_ASSERT(CONFIG_ADC_INIT_PRIORITY > CONFIG_SPI_INIT_PRIORITY, | 
 | 	     "CONFIG_ADC_INIT_PRIORITY must be higher than CONFIG_SPI_INIT_PRIORITY"); | 
 |  | 
 | #define ADC_MAX11102_17_INST_DEFINE(index, name, res, channels)                                    \ | 
 | 	static const struct max11102_17_config config_##name##_##index = {                         \ | 
 | 		.bus = SPI_DT_SPEC_INST_GET(                                                       \ | 
 | 			index,                                                                     \ | 
 | 			SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_WORD_SET(8), 0),  \ | 
 | 		.gpio_chsel = GPIO_DT_SPEC_INST_GET_OR(index, chsel_gpios, {0}),                   \ | 
 | 		.resolution = res,                                                                 \ | 
 | 		.channel_count = channels,                                                         \ | 
 | 	};                                                                                         \ | 
 | 	static struct max11102_17_data data_##name##_##index;                                      \ | 
 | 	DEVICE_DT_INST_DEFINE(index, max11102_17_init, NULL, &data_##name##_##index,               \ | 
 | 			      &config_##name##_##index, POST_KERNEL, CONFIG_ADC_INIT_PRIORITY,     \ | 
 | 			      &api); | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11102 | 
 | #define ADC_MAX11102_RESOLUTION 12 | 
 | #define ADC_MAX11102_CHANNELS   2 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11102_RESOLUTION, ADC_MAX11102_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11103 | 
 | #define ADC_MAX11103_RESOLUTION 12 | 
 | #define ADC_MAX11103_CHANNELS   2 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11103_RESOLUTION, ADC_MAX11103_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11105 | 
 | #define ADC_MAX11105_RESOLUTION 12 | 
 | #define ADC_MAX11105_CHANNELS   1 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11105_RESOLUTION, ADC_MAX11105_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11106 | 
 | #define ADC_MAX11106_RESOLUTION 10 | 
 | #define ADC_MAX11106_CHANNELS   2 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11106_RESOLUTION, ADC_MAX11106_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11110 | 
 | #define ADC_MAX11110_RESOLUTION 10 | 
 | #define ADC_MAX11110_CHANNELS   1 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11110_RESOLUTION, ADC_MAX11110_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11111 | 
 | #define ADC_MAX11111_RESOLUTION 8 | 
 | #define ADC_MAX11111_CHANNELS   2 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11111_RESOLUTION, ADC_MAX11111_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11115 | 
 | #define ADC_MAX11115_RESOLUTION 8 | 
 | #define ADC_MAX11115_CHANNELS   1 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11115_RESOLUTION, ADC_MAX11115_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11116 | 
 | #define ADC_MAX11116_RESOLUTION 8 | 
 | #define ADC_MAX11116_CHANNELS   1 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11116_RESOLUTION, ADC_MAX11116_CHANNELS) | 
 | #undef DT_DRV_COMPAT | 
 |  | 
 | #define DT_DRV_COMPAT           maxim_max11117 | 
 | #define ADC_MAX11117_RESOLUTION 10 | 
 | #define ADC_MAX11117_CHANNELS   1 | 
 | DT_INST_FOREACH_STATUS_OKAY_VARGS(ADC_MAX11102_17_INST_DEFINE, DT_DRV_COMPAT, | 
 | 				  ADC_MAX11117_RESOLUTION, ADC_MAX11117_CHANNELS) | 
 | #undef DT_DRV_COMPAT |