Linux Audio

Check our new training course

Embedded Linux Audio

Check our new training course
with Creative Commons CC-BY-SA
lecture materials

Bootlin logo

Elixir Cross Referencer

Loading...
/*
 * Copyright (c) 2021 Eug Krashtan
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <device.h>
#include <drivers/sensor.h>
#include <drivers/adc.h>
#include <logging/log.h>

LOG_MODULE_REGISTER(stm32_temp, CONFIG_SENSOR_LOG_LEVEL);

#define DT_DRV_COMPAT st_stm32_temp

struct stm32_temp_data {
	const struct device *adc;
	uint8_t channel;
	struct adc_channel_cfg adc_cfg;
	struct adc_sequence adc_seq;
	struct k_mutex mutex;
	int16_t sample_buffer;
	int32_t mv; /* Sensor value in millivolts */
};

struct stm32_temp_config {
	int avgslope;
	int v25_mv;
	int tsv_mv;
	bool is_ntc;
};

static int stm32_temp_sample_fetch(const struct device *dev,
				  enum sensor_channel chan)
{
	const struct stm32_temp_config *cfg = dev->config;
	struct stm32_temp_data *data = dev->data;
	struct adc_sequence *sp = &data->adc_seq;
	int rc;

	if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
		return -ENOTSUP;
	}

	k_mutex_lock(&data->mutex, K_FOREVER);

	rc = adc_read(data->adc, sp);
	sp->calibrate = false;
	if (rc == 0) {
		data->mv = data->sample_buffer * cfg->tsv_mv /	0x0FFF;
	}

	k_mutex_unlock(&data->mutex);

	return 0;
}

static int stm32_temp_channel_get(const struct device *dev,
				 enum sensor_channel chan,
				 struct sensor_value *val)
{
	struct stm32_temp_data *data = dev->data;
	const struct stm32_temp_config *cfg = dev->config;
	float temp;

	if (chan != SENSOR_CHAN_DIE_TEMP) {
		return -ENOTSUP;
	}

	if (cfg->is_ntc) {
		temp = (float)(cfg->v25_mv - data->mv);
	} else {
		temp = (float)(data->mv - cfg->v25_mv);
	}
	temp = (temp/cfg->avgslope)*10;
	temp += 25;
	sensor_value_from_double(val, temp);

	return 0;
}

static const struct sensor_driver_api stm32_temp_driver_api = {
	.sample_fetch = stm32_temp_sample_fetch,
	.channel_get = stm32_temp_channel_get,
};

static int stm32_temp_init(const struct device *dev)
{
	struct stm32_temp_data *data = dev->data;
	struct adc_channel_cfg *accp = &data->adc_cfg;
	struct adc_sequence *asp = &data->adc_seq;
	int rc;

	k_mutex_init(&data->mutex);

	if (!device_is_ready(data->adc)) {
		LOG_ERR("Device %s is not ready", data->adc->name);
		return -ENODEV;
	}

	*accp = (struct adc_channel_cfg){
		.gain = ADC_GAIN_1,
		.reference = ADC_REF_INTERNAL,
		.acquisition_time = ADC_ACQ_TIME_MAX,
		.channel_id = data->channel,
		.differential = 0
	};
	rc = adc_channel_setup(data->adc, accp);
	LOG_DBG("Setup AIN%u got %d", data->channel, rc);

	*asp = (struct adc_sequence){
		.channels = BIT(data->channel),
		.buffer = &data->sample_buffer,
		.buffer_size = sizeof(data->sample_buffer),
		.resolution = 12,
		.calibrate = true,
	};

	return 0;
}

#define STM32_TEMP_INST(idx)    \
	static struct stm32_temp_data inst_##idx##_data = {  \
		.adc = DEVICE_DT_GET(DT_IO_CHANNELS_CTLR(  \
						DT_INST(idx, st_stm32_temp))), \
		.channel = DT_IO_CHANNELS_INPUT( \
						DT_INST(idx, st_stm32_temp)) \
	};  \
	static const struct stm32_temp_config inst_##idx##_config = { \
		.avgslope = DT_INST_PROP(idx, avgslope),  \
		.v25_mv = DT_INST_PROP(idx, v25),  \
		.tsv_mv = DT_INST_PROP(idx, ts_voltage_mv),  \
		.is_ntc = DT_INST_PROP(idx, ntc)  \
	};  \
	DEVICE_DT_INST_DEFINE(idx,  \
		    stm32_temp_init,    \
		    NULL,               \
		    &inst_##idx##_data, \
			&inst_##idx##_config,  \
		    POST_KERNEL,           \
		    CONFIG_SENSOR_INIT_PRIORITY, \
		    &stm32_temp_driver_api);

DT_INST_FOREACH_STATUS_OKAY(STM32_TEMP_INST)