/*
 * Copyright (c) 2023 FTP Technologies
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT microchip_mcp970x

#include <zephyr/drivers/adc.h>
#include <zephyr/drivers/sensor.h>

#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(mcp970x, CONFIG_SENSOR_LOG_LEVEL);

enum ic_family {
	FAMILY_MCP9700_9700A,
	FAMILY_MCP9701_9701A
};

/* Milli degrees C per degree C */
#define MC_PER_C 1000

#define MV_AT_0C_MCP9700_9700A 500
#define MV_AT_0C_MCP9701_9701A 400

#define T_COEFF_MCP9700_9700A 10
#define T_COEFF_MCP9701_9701A 19.5

struct mcp970x_config {
	struct adc_dt_spec adc;
	enum ic_family family;
};

struct mcp970x_data {
	struct adc_sequence sequence;
	int16_t raw;
};

static int fetch(const struct device *dev, enum sensor_channel chan)
{
	const struct mcp970x_config *config = dev->config;
	struct mcp970x_data *data = dev->data;
	int ret;

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

	ret = adc_read(config->adc.dev, &data->sequence);
	if (ret != 0) {
		LOG_ERR("adc_read: %d", ret);
	}

	return ret;
}

static int get(const struct device *dev, enum sensor_channel chan, struct sensor_value *val)
{
	const struct mcp970x_config *config = dev->config;
	struct mcp970x_data *data = dev->data;
	int32_t raw_val = data->raw;
	int32_t t;
	int ret;

	__ASSERT_NO_MSG(val != NULL);

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

	ret = adc_raw_to_millivolts_dt(&config->adc, &raw_val);
	if (ret != 0) {
		LOG_ERR("to_mv: %d", ret);
		return ret;
	}

	if (config->family == FAMILY_MCP9700_9700A) {
		t = (MC_PER_C * (raw_val - MV_AT_0C_MCP9700_9700A)) / T_COEFF_MCP9700_9700A;
	} else {
		int32_t t_coeff = 10 * T_COEFF_MCP9701_9701A; /* float to int */

		t = (MC_PER_C * 10 * (raw_val - MV_AT_0C_MCP9701_9701A)) / t_coeff;
	}

	val->val1 = t / MC_PER_C;
	val->val2 = 1000 * (t % MC_PER_C);

	LOG_DBG("%d of %d, %dmV, %dmC", data->raw, (1 << data->sequence.resolution) - 1, raw_val,
		t);

	return 0;
}

static const struct sensor_driver_api mcp970x_api = {
	.sample_fetch = fetch,
	.channel_get = get,
};

static int init(const struct device *dev)
{
	const struct mcp970x_config *config = dev->config;
	struct mcp970x_data *data = dev->data;
	int ret;

	if (!adc_is_ready_dt(&config->adc)) {
		LOG_ERR("ADC is not ready");
		return -ENODEV;
	}

	ret = adc_channel_setup_dt(&config->adc);
	if (ret != 0) {
		LOG_ERR("setup: %d", ret);
		return ret;
	}

	ret = adc_sequence_init_dt(&config->adc, &data->sequence);
	if (ret != 0) {
		LOG_ERR("sequence: %d", ret);
		return ret;
	}

	data->sequence.buffer = &data->raw;
	data->sequence.buffer_size = sizeof(data->raw);

	return 0;
}

#define MCP970X_INIT(inst)                                                                         \
	static struct mcp970x_data mcp970x_##inst##_data = {0};                                    \
                                                                                                   \
	static const struct mcp970x_config mcp970x_##inst##_config = {                             \
		.adc = ADC_DT_SPEC_INST_GET(inst),                                                 \
		.family = DT_INST_ENUM_IDX(inst, family),                                          \
	};                                                                                         \
                                                                                                   \
	SENSOR_DEVICE_DT_INST_DEFINE(inst, &init, NULL, &mcp970x_##inst##_data,                    \
				     &mcp970x_##inst##_config, POST_KERNEL,                        \
				     CONFIG_SENSOR_INIT_PRIORITY, &mcp970x_api);

DT_INST_FOREACH_STATUS_OKAY(MCP970X_INIT)
