text: /*
   * Copyright (c) 2020 Matija Tudan
   *
   * SPDX-License-Identifier: Apache-2.0
   */
  
  #include <zephyr.h>
  #include <kernel.h>
  #include <drivers/i2c.h>
  #include <drivers/dac.h>
  #include <sys/util.h>
  #include <sys/byteorder.h>
  #include <sys/__assert.h>
  #include <logging/log.h>
  
  LOG_MODULE_REGISTER(dac_dacx3608, CONFIG_DAC_LOG_LEVEL);
  
  /* Register addresses */
  #define DACX3608_REG_DEVICE_CONFIG  0x01U
  #define DACX3608_REG_STATUS_TRIGGER 0x02U
  #define DACX3608_REG_BRDCAST        0x03U
  #define DACX3608_REG_DACA_DATA      0x08U
  
  #define DAC43608_DEVICE_ID      0x500	/* STATUS_TRIGGER[DEVICE_ID] */
  #define DAC53608_DEVICE_ID      0x300	/* STATUS_TRIGGER[DEVICE_ID] */
  #define DACX3608_SW_RST         0x0A	/* STATUS_TRIGGER[SW_RST] */
  #define DACX3608_POR_DELAY      5
  #define DACX3608_MAX_CHANNEL    8
  
  struct dacx3608_config {
  	struct i2c_dt_spec bus;
  	uint8_t resolution;
  };
  
  struct dacx3608_data {
  	uint8_t configured;
  };
  
  static int dacx3608_reg_read(const struct device *dev, uint8_t reg,
  			      uint16_t *val)
  {
  	const struct dacx3608_config *cfg = dev->config;
  
  	if (i2c_burst_read_dt(&cfg->bus, reg, (uint8_t *) val, 2) < 0) {
  		LOG_ERR("I2C read failed");
  		return -EIO;
  	}
  
  	*val = sys_be16_to_cpu(*val);
  
  	return 0;
  }
  
  static int dacx3608_reg_write(const struct device *dev, uint8_t reg,
  			       uint16_t val)
  {
  	const struct dacx3608_config *cfg = dev->config;
  	uint8_t buf[3] = {reg, val >> 8, val & 0xFF};
  
  	return i2c_write_dt(&cfg->bus, buf, sizeof(buf));
  }
  
  int dacx3608_reg_update(const struct device *dev, uint8_t reg,
  			 uint16_t mask, bool setting)
  {
  	uint16_t regval;
  	int ret;
  
  	ret = dacx3608_reg_read(dev, reg, &regval);
  	if (ret) {
  		return -EIO;
  	}
  
  	if (setting) {
  		regval |= mask;
  	} else {
  		regval &= ~mask;
  	}
  
  	ret = dacx3608_reg_write(dev, reg, regval);
  	if (ret) {
  		return ret;
  	}
  
  	return 0;
  }
  
  static int dacx3608_channel_setup(const struct device *dev,
  				   const struct dac_channel_cfg *channel_cfg)
  {
  	const struct dacx3608_config *config = dev->config;
  	struct dacx3608_data *data = dev->data;
  	bool setting = false;
  	int ret;
  
  	if (channel_cfg->channel_id > DACX3608_MAX_CHANNEL - 1) {
  		LOG_ERR("Unsupported channel %d", channel_cfg->channel_id);
  		return -ENOTSUP;
  	}
  
  	if (channel_cfg->resolution != config->resolution) {
  		LOG_ERR("Unsupported resolution %d", channel_cfg->resolution);
  		return -ENOTSUP;
  	}
  
  	if (data->configured & BIT(channel_cfg->channel_id)) {
  		LOG_DBG("Channel %d already configured", channel_cfg->channel_id);
  		return 0;
  	}
  
  	/* Clear PDNn bit */
  	ret = dacx3608_reg_update(dev, DACX3608_REG_DEVICE_CONFIG,
  				BIT(channel_cfg->channel_id), setting);
  	if (ret) {
  		LOG_ERR("Unable to update DEVICE_CONFIG register");
  		return -EIO;
  	}
  
  	data->configured |= BIT(channel_cfg->channel_id);
  
  	LOG_DBG("Channel %d initialized", channel_cfg->channel_id);
  
  	return 0;
  }
  
  static int dacx3608_write_value(const struct device *dev, uint8_t channel,
  				uint32_t value)
  {
  	const struct dacx3608_config *config = dev->config;
  	struct dacx3608_data *data = dev->data;
  	uint16_t regval;
  	int ret;
  
  	if (channel > DACX3608_MAX_CHANNEL - 1) {
  		LOG_ERR("Unsupported channel %d", channel);
  		return -ENOTSUP;
  	}
  
  	if (!(data->configured & BIT(channel))) {
  		LOG_ERR("Channel %d not initialized", channel);
  		return -EINVAL;
  	}
  
  	if (value >= (1 << (config->resolution))) {
  		LOG_ERR("Value %d out of range", value);
  		return -EINVAL;
  	}
  
  	/*
  	 * Shift passed value two times left because first two bits are Don't Care
  	 *
  	 * DACn_DATA register format:
  	 *
  	 * | 15 14 13 12 |      11 10 9 8 7 6 5 4 3 2      |    1 0     |
  	 * |-------------|---------------------------------|------------|
  	 * | Don't Care  |  DAC53608[9:0] / DAC43608[7:0]  | Don't Care |
  	 */
  	regval = value << 2;
  	regval &= 0xFFFF;
  
  	ret = dacx3608_reg_write(dev, DACX3608_REG_DACA_DATA + channel, regval);
  	if (ret) {
  		LOG_ERR("Unable to set value %d on channel %d", value, channel);
  		return -EIO;
  	}
  
  	return 0;
  }
  
  static int dacx3608_soft_reset(const struct device *dev)
  {
  	uint16_t regval = DACX3608_SW_RST;
  	int ret;
  
  	ret = dacx3608_reg_write(dev, DACX3608_REG_STATUS_TRIGGER, regval);
  	if (ret) {
  		return -EIO;
  	}
  	k_msleep(DACX3608_POR_DELAY);
  
  	return 0;
  }
  
  static int dacx3608_device_id_check(const struct device *dev)
  {
  	uint16_t dev_id;
  	int ret;
  
  	ret = dacx3608_reg_read(dev, DACX3608_REG_STATUS_TRIGGER, &dev_id);
  	if (ret) {
  		LOG_ERR("Unable to read device ID");
  		return -EIO;
  	}
  
  	switch (dev_id) {
  	case DAC43608_DEVICE_ID:
  	case DAC53608_DEVICE_ID:
  		LOG_DBG("Device ID %#4x", dev_id);
  		break;
  	default:
  		LOG_ERR("Unknown Device ID %#4x", dev_id);
  		return -EIO;
  	}
  
  	return 0;
  }
  
  static int dacx3608_init(const struct device *dev)
  {
  	const struct dacx3608_config *config = dev->config;
  	struct dacx3608_data *data = dev->data;
  	int ret;
  
  	if (!device_is_ready(config->bus.bus)) {
  		LOG_ERR("I2C device not ready");
  		return -ENODEV;
  	}
  
  	ret = dacx3608_soft_reset(dev);
  	if (ret) {
  		LOG_ERR("Soft-reset failed");
  		return ret;
  	}
  
  	ret = dacx3608_device_id_check(dev);
  	if (ret) {
  		return ret;
  	}
  
  	data->configured = 0;
  
  	LOG_DBG("Init complete");
  
  	return 0;
  }
  
  static const struct dac_driver_api dacx3608_driver_api = {
  	.channel_setup = dacx3608_channel_setup,
  	.write_value = dacx3608_write_value,
  };
  
  #define INST_DT_DACX3608(inst, t) DT_INST(inst, ti_dac##t)
  
  #define DACX3608_DEVICE(t, n, res) \
  	static struct dacx3608_data dac##t##_data_##n; \
  	static const struct dacx3608_config dac##t##_config_##n = { \
  		.bus = I2C_DT_SPEC_GET(INST_DT_DACX3608(n, t)), \
  		.resolution = res, \
  	}; \
  	DEVICE_DT_DEFINE(INST_DT_DACX3608(n, t), \
  				&dacx3608_init, NULL, \
  				&dac##t##_data_##n, \
  				&dac##t##_config_##n, POST_KERNEL, \
  				CONFIG_DAC_INIT_PRIORITY, \
  				&dacx3608_driver_api)
  
  /*
   * DAC43608: 8-bit
   */
  #define DAC43608_DEVICE(n) DACX3608_DEVICE(43608, n, 8)
  
  /*
   * DAC53608: 10-bit
   */
  #define DAC53608_DEVICE(n) DACX3608_DEVICE(53608, n, 10)
  
  #define CALL_WITH_ARG(arg, expr) expr(arg)
  
  #define INST_DT_DACX3608_FOREACH(t, inst_expr) \
  	LISTIFY(DT_NUM_INST_STATUS_OKAY(ti_dac##t), \
  		     CALL_WITH_ARG, (), inst_expr)
  
  INST_DT_DACX3608_FOREACH(43608, DAC43608_DEVICE);
  INST_DT_DACX3608_FOREACH(53608, DAC53608_DEVICE);
  