/*
 * Copyright (c) 2022, Felipe Neves
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT ams_as5600

#include <errno.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/util.h>
#include <zephyr/device.h>
#include <zephyr/init.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(ams_as5600, CONFIG_SENSOR_LOG_LEVEL);

#define AS5600_ANGLE_REGISTER_H 0x0E
#define AS5600_FULL_ANGLE		360
#define AS5600_PULSES_PER_REV	4096
#define AS5600_MILLION_UNIT	1000000

struct as5600_dev_cfg {
	struct i2c_dt_spec i2c_port;
};

/* Device run time data */
struct as5600_dev_data {
	uint16_t position;
};

static int as5600_fetch(const struct device *dev, enum sensor_channel chan)
{
	struct as5600_dev_data *dev_data = dev->data;
	const struct as5600_dev_cfg *dev_cfg = dev->config;

	uint8_t read_data[2] = {0, 0};
	uint8_t angle_reg = AS5600_ANGLE_REGISTER_H;

	int err = i2c_write_read_dt(&dev_cfg->i2c_port,
						&angle_reg,
						1,
						&read_data,
						sizeof(read_data));

	/* invalid readings preserves the last good value */
	if (!err) {
		dev_data->position = ((uint16_t)read_data[0] << 8) | read_data[1];
	}

	return err;
}

static int as5600_get(const struct device *dev, enum sensor_channel chan,
			struct sensor_value *val)
{
	struct as5600_dev_data *dev_data = dev->data;

	if (chan == SENSOR_CHAN_ROTATION) {
		val->val1 = ((int32_t)dev_data->position * AS5600_FULL_ANGLE) /
							AS5600_PULSES_PER_REV;

		val->val2 = (((int32_t)dev_data->position * AS5600_FULL_ANGLE) %
			     AS5600_PULSES_PER_REV) * (AS5600_MILLION_UNIT / AS5600_PULSES_PER_REV);
	} else {
		return -ENOTSUP;
	}

	return 0;
}

static int as5600_initialize(const struct device *dev)
{
	struct as5600_dev_data *const dev_data = dev->data;

	dev_data->position = 0;

	LOG_INF("Device %s initialized", dev->name);

	return 0;
}

static const struct sensor_driver_api as5600_driver_api = {
	.sample_fetch = as5600_fetch,
	.channel_get = as5600_get,
};

#define AS5600_INIT(n)						\
	static struct as5600_dev_data as5600_data##n;		\
	static const struct as5600_dev_cfg as5600_cfg##n = {\
		.i2c_port = I2C_DT_SPEC_INST_GET(n)	\
	};	\
									\
	SENSOR_DEVICE_DT_INST_DEFINE(n, as5600_initialize, NULL,	\
			    &as5600_data##n, &as5600_cfg##n, \
			    POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,	\
			    &as5600_driver_api);

DT_INST_FOREACH_STATUS_OKAY(AS5600_INIT)
