/*
 * Copyright (c) 2016, 2017 Intel Corporation
 * Copyright (c) 2017 IpTronix S.r.l.
 * Copyright (c) 2021 Nordic Semiconductor ASA
 * Copyright (c) 2022, Leonard Pollak
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/*
 * Bus-specific functionality for BME680s accessed via SPI.
 */

#include <zephyr/logging/log.h>
#include "bme680.h"

#if BME680_BUS_SPI

LOG_MODULE_DECLARE(bme680, CONFIG_SENSOR_LOG_LEVEL);

static int bme680_bus_check_spi(const union bme680_bus *bus)
{
	return spi_is_ready_dt(&bus->spi) ? 0 : -ENODEV;
}

static inline int bme680_set_mem_page(const struct device *dev, uint8_t addr)
{
	const struct bme680_config *config = dev->config;
	struct bme680_data *data = dev->data;
	uint8_t page = (addr > 0x7f) ? 0U : 1U;
	int err = 0;

	if (data->mem_page != page) {
		uint8_t buf[2];

		struct spi_buf tx_buf = {
			.buf = &buf[0],
			.len = 1,
		};
		const struct spi_buf_set tx = {
			.buffers = &tx_buf,
			.count = 1,
		};

		const struct spi_buf rx_buf[] = {
			{ .buf = NULL, .len = 1 },
			{ .buf = &buf[1], .len = 1 },
		};
		const struct spi_buf_set rx = {
			.buffers = rx_buf,
			.count = ARRAY_SIZE(rx_buf),
		};

		buf[0] = BME680_REG_STATUS | BME680_SPI_READ_BIT;
		err = spi_transceive_dt(&config->bus.spi, &tx, &rx);
		if (err < 0) {
			return err;
		}

		if (data->mem_page == 1U) {
			buf[1] &= ~BME680_SPI_MEM_PAGE_MSK;
		} else {
			buf[1] |= BME680_SPI_MEM_PAGE_MSK;
		}

		buf[0] = BME680_REG_STATUS & BME680_SPI_WRITE_MSK;
		tx_buf.len = 2;
		err = spi_write_dt(&config->bus.spi, &tx);
		if (err < 0) {
			return err;
		}

		data->mem_page = page;
	}

	return err;
}

static int bme680_reg_write_spi(const struct device *dev,
				uint8_t reg, uint8_t val)
{
	const struct bme680_config *config = dev->config;
	int err;
	uint8_t cmd[] = { reg & BME680_SPI_WRITE_MSK, val };
	const struct spi_buf tx_buf = {
		.buf = cmd,
		.len = sizeof(cmd)
	};
	const struct spi_buf_set tx = {
		.buffers = &tx_buf,
		.count = 1
	};

	err = bme680_set_mem_page(dev, reg);
	if (err) {
		return err;
	}

	err = spi_write_dt(&config->bus.spi, &tx);

	return err;
}

static int bme680_reg_read_spi(const struct device *dev,
			       uint8_t start, uint8_t *buf, int size)
{
	const struct bme680_config *config = dev->config;
	int err;
	uint8_t addr;
	const struct spi_buf tx_buf = {
		.buf = &addr,
		.len = 1
	};
	const struct spi_buf_set tx = {
		.buffers = &tx_buf,
		.count = 1
	};
	struct spi_buf rx_buf[2];
	const struct spi_buf_set rx = {
		.buffers = rx_buf,
		.count = ARRAY_SIZE(rx_buf)
	};

	rx_buf[0].buf = NULL;
	rx_buf[0].len = 1;

	addr = start | BME680_SPI_READ_BIT;
	rx_buf[1].buf = buf;
	rx_buf[1].len = size;

	err = bme680_set_mem_page(dev, start);
	if (err) {
		return err;
	}

	err = spi_transceive_dt(&config->bus.spi, &tx, &rx);

	return err;
}

const struct bme680_bus_io bme680_bus_io_spi = {
	.check = bme680_bus_check_spi,
	.read = bme680_reg_read_spi,
	.write = bme680_reg_write_spi,
};
#endif /* BME680_BUS_SPI */
