// SPDX-License-Identifier: GPL-2.0+
/*
* IIO driver for MCP47FEB02 Multi-Channel DAC with I2C interface
*
* Copyright (C) 2025 Microchip Technology Inc. and its subsidiaries
*
* Author: Ariana Lazar <ariana.lazar@microchip.com>
*
* Datasheet links:
* [MCP47FEBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005375A.pdf
* [MCP47FVBxx] https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/20005405A.pdf
* [MCP47FxBx4/8] https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/MCP47FXBX48-Data-Sheet-DS200006368A.pdf
*/
#include <linux/array_size.h>
#include <linux/bits.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/kstrtox.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/time64.h>
#include <linux/types.h>
#include <linux/units.h>
/* Register addresses must be left shifted with 3 positions in order to append command mask */
#define MCP47FEB02_DAC0_REG_ADDR 0x00
#define MCP47FEB02_VREF_REG_ADDR 0x40
#define MCP47FEB02_POWER_DOWN_REG_ADDR 0x48
#define MCP47FEB02_DAC_CTRL_MASK GENMASK(1, 0)
#define MCP47FEB02_GAIN_CTRL_STATUS_REG_ADDR 0x50
#define MCP47FEB02_GAIN_BIT_MASK BIT(0)
#define MCP47FEB02_GAIN_BIT_STATUS_EEWA_MASK BIT(6)
#define MCP47FEB02_GAIN_BITS_MASK GENMASK(15, 8)
#define MCP47FEB02_WIPERLOCK_STATUS_REG_ADDR 0x58
#define MCP47FEB02_NV_DAC0_REG_ADDR 0x80
#define MCP47FEB02_NV_VREF_REG_ADDR 0xC0
#define MCP47FEB02_NV_POWER_DOWN_REG_ADDR 0xC8
#define MCP47FEB02_NV_GAIN_CTRL_I2C_SLAVE_REG_ADDR 0xD0
#define MCP47FEB02_NV_I2C_SLAVE_ADDR_MASK GENMASK(7, 0)
/* Voltage reference, Power-Down control register and DAC Wiperlock status register fields */
#define DAC_CTRL_MASK(ch) (GENMASK(1, 0) << (2 * (ch)))
#define DAC_CTRL_VAL(ch, val) ((val) << (2 * (ch)))
/* Gain Control and I2C Slave Address Reguster fields */
#define DAC_GAIN_MASK(ch) (BIT(0) << (8 + (ch)))
#define DAC_GAIN_VAL(ch, val) ((val) << (8 + (ch)))
#define REG_ADDR(reg) ((reg) << 3)
#define NV_REG_ADDR(reg) ((NV_DAC_ADDR_OFFSET + (reg)) << 3)
#define READFLAG_MASK GENMASK(2, 1)
#define MCP47FEB02_MAX_CH 8
#define MCP47FEB02_MAX_SCALES_CH 3
#define MCP47FEB02_DAC_WIPER_UNLOCKED 0
#define MCP47FEB02_NORMAL_OPERATION 0
#define MCP47FEB02_INTERNAL_BAND_GAP_uV 2440000
#define NV_DAC_ADDR_OFFSET 0x10
enum mcp47feb02_vref_mode {
MCP47FEB02_VREF_VDD = 0,
MCP47FEB02_INTERNAL_BAND_GAP = 1,
MCP47FEB02_EXTERNAL_VREF_UNBUFFERED = 2,
MCP47FEB02_EXTERNAL_VREF_BUFFERED = 3,
};
enum mcp47feb02_scale {
MCP47FEB02_SCALE_VDD = 0,
MCP47FEB02_SCALE_GAIN_X1 = 1,
MCP47FEB02_SCALE_GAIN_X2 = 2,
};
enum mcp47feb02_gain_bit_mode {
MCP47FEB02_GAIN_BIT_X1 = 0,
MCP47FEB02_GAIN_BIT_X2 = 1,
};
static const char * const mcp47feb02_powerdown_modes[] = {
"1kohm_to_gnd",
"100kohm_to_gnd",
"open_circuit",
};
/**
* struct mcp47feb02_features - chip specific data
* @name: device name
* @phys_channels: number of hardware channels
* @resolution: DAC resolution
* @have_ext_vref1: does the hardware have an the second external voltage reference?
* @have_eeprom: does the hardware have an internal eeprom?
*/
struct mcp47feb02_features {
const char *name;
unsigned int phys_channels;
unsigned int resolution;
bool have_ext_vref1;
bool have_eeprom;
};
static const struct mcp47feb02_features mcp47feb01_chip_features = {
.name = "mcp47feb01",
.phys_channels = 1,
.resolution = 8,
.have_ext_vref1 = false,
.have_eeprom = true,
};
static const struct mcp47feb02_features mcp47feb02_chip_features = {
.name = "mcp47feb02",
.phys_channels = 2,
.resolution = 8,
.have_ext_vref1 = false,
.have_eeprom = true,
};
static const struct mcp47feb02_features mcp47feb04_chip_features = {
.name = "mcp47feb04",
.phys_channels = 4,
.resolution =