18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * AD5758 Digital to analog converters driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2018 Analog Devices Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * TODO: Currently CRC is not supported in this driver
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/bsearch.h>
108c2ecf20Sopenharmony_ci#include <linux/delay.h>
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/property.h>
148c2ecf20Sopenharmony_ci#include <linux/of.h>
158c2ecf20Sopenharmony_ci#include <linux/of_device.h>
168c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
178c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
208c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* AD5758 registers definition */
238c2ecf20Sopenharmony_ci#define AD5758_NOP				0x00
248c2ecf20Sopenharmony_ci#define AD5758_DAC_INPUT			0x01
258c2ecf20Sopenharmony_ci#define AD5758_DAC_OUTPUT			0x02
268c2ecf20Sopenharmony_ci#define AD5758_CLEAR_CODE			0x03
278c2ecf20Sopenharmony_ci#define AD5758_USER_GAIN			0x04
288c2ecf20Sopenharmony_ci#define AD5758_USER_OFFSET			0x05
298c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG			0x06
308c2ecf20Sopenharmony_ci#define AD5758_SW_LDAC				0x07
318c2ecf20Sopenharmony_ci#define AD5758_KEY				0x08
328c2ecf20Sopenharmony_ci#define AD5758_GP_CONFIG1			0x09
338c2ecf20Sopenharmony_ci#define AD5758_GP_CONFIG2			0x0A
348c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG1			0x0B
358c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG2			0x0C
368c2ecf20Sopenharmony_ci#define AD5758_WDT_CONFIG			0x0F
378c2ecf20Sopenharmony_ci#define AD5758_DIGITAL_DIAG_CONFIG		0x10
388c2ecf20Sopenharmony_ci#define AD5758_ADC_CONFIG			0x11
398c2ecf20Sopenharmony_ci#define AD5758_FAULT_PIN_CONFIG			0x12
408c2ecf20Sopenharmony_ci#define AD5758_TWO_STAGE_READBACK_SELECT	0x13
418c2ecf20Sopenharmony_ci#define AD5758_DIGITAL_DIAG_RESULTS		0x14
428c2ecf20Sopenharmony_ci#define AD5758_ANALOG_DIAG_RESULTS		0x15
438c2ecf20Sopenharmony_ci#define AD5758_STATUS				0x16
448c2ecf20Sopenharmony_ci#define AD5758_CHIP_ID				0x17
458c2ecf20Sopenharmony_ci#define AD5758_FREQ_MONITOR			0x18
468c2ecf20Sopenharmony_ci#define AD5758_DEVICE_ID_0			0x19
478c2ecf20Sopenharmony_ci#define AD5758_DEVICE_ID_1			0x1A
488c2ecf20Sopenharmony_ci#define AD5758_DEVICE_ID_2			0x1B
498c2ecf20Sopenharmony_ci#define AD5758_DEVICE_ID_3			0x1C
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/* AD5758_DAC_CONFIG */
528c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_RANGE_MSK		GENMASK(3, 0)
538c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_RANGE_MODE(x)		(((x) & 0xF) << 0)
548c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_INT_EN_MSK		BIT(5)
558c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_INT_EN_MODE(x)	(((x) & 0x1) << 5)
568c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_OUT_EN_MSK		BIT(6)
578c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_OUT_EN_MODE(x)	(((x) & 0x1) << 6)
588c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_EN_MSK		BIT(8)
598c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_EN_MODE(x)		(((x) & 0x1) << 8)
608c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_CLOCK_MSK		GENMASK(12, 9)
618c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_CLOCK_MODE(x)	(((x) & 0xF) << 9)
628c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_STEP_MSK		GENMASK(15, 13)
638c2ecf20Sopenharmony_ci#define AD5758_DAC_CONFIG_SR_STEP_MODE(x)	(((x) & 0x7) << 13)
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* AD5758_KEY */
668c2ecf20Sopenharmony_ci#define AD5758_KEY_CODE_RESET_1			0x15FA
678c2ecf20Sopenharmony_ci#define AD5758_KEY_CODE_RESET_2			0xAF51
688c2ecf20Sopenharmony_ci#define AD5758_KEY_CODE_SINGLE_ADC_CONV		0x1ADC
698c2ecf20Sopenharmony_ci#define AD5758_KEY_CODE_RESET_WDT		0x0D06
708c2ecf20Sopenharmony_ci#define AD5758_KEY_CODE_CALIB_MEM_REFRESH	0xFCBA
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* AD5758_DCDC_CONFIG1 */
738c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MSK	GENMASK(4, 0)
748c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x)	(((x) & 0x1F) << 0)
758c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK	GENMASK(6, 5)
768c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x)	(((x) & 0x3) << 5)
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/* AD5758_DCDC_CONFIG2 */
798c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG2_ILIMIT_MSK		GENMASK(3, 1)
808c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG2_ILIMIT_MODE(x)	(((x) & 0x7) << 1)
818c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG2_INTR_SAT_3WI_MSK	BIT(11)
828c2ecf20Sopenharmony_ci#define AD5758_DCDC_CONFIG2_BUSY_3WI_MSK	BIT(12)
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci/* AD5758_DIGITAL_DIAG_RESULTS */
858c2ecf20Sopenharmony_ci#define AD5758_CAL_MEM_UNREFRESHED_MSK		BIT(15)
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci/* AD5758_ADC_CONFIG */
888c2ecf20Sopenharmony_ci#define AD5758_ADC_CONFIG_PPC_BUF_EN(x)		(((x) & 0x1) << 11)
898c2ecf20Sopenharmony_ci#define AD5758_ADC_CONFIG_PPC_BUF_MSK		BIT(11)
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#define AD5758_WR_FLAG_MSK(x)		(0x80 | ((x) & 0x1F))
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci#define AD5758_FULL_SCALE_MICRO	65535000000ULL
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistruct ad5758_range {
968c2ecf20Sopenharmony_ci	int reg;
978c2ecf20Sopenharmony_ci	int min;
988c2ecf20Sopenharmony_ci	int max;
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/**
1028c2ecf20Sopenharmony_ci * struct ad5758_state - driver instance specific data
1038c2ecf20Sopenharmony_ci * @spi:	spi_device
1048c2ecf20Sopenharmony_ci * @lock:	mutex lock
1058c2ecf20Sopenharmony_ci * @gpio_reset:	gpio descriptor for the reset line
1068c2ecf20Sopenharmony_ci * @out_range:	struct which stores the output range
1078c2ecf20Sopenharmony_ci * @dc_dc_mode:	variable which stores the mode of operation
1088c2ecf20Sopenharmony_ci * @dc_dc_ilim:	variable which stores the dc-to-dc converter current limit
1098c2ecf20Sopenharmony_ci * @slew_time:	variable which stores the target slew time
1108c2ecf20Sopenharmony_ci * @pwr_down:	variable which contains whether a channel is powered down or not
1118c2ecf20Sopenharmony_ci * @d32:	spi transfer buffers
1128c2ecf20Sopenharmony_ci */
1138c2ecf20Sopenharmony_cistruct ad5758_state {
1148c2ecf20Sopenharmony_ci	struct spi_device *spi;
1158c2ecf20Sopenharmony_ci	struct mutex lock;
1168c2ecf20Sopenharmony_ci	struct gpio_desc *gpio_reset;
1178c2ecf20Sopenharmony_ci	struct ad5758_range out_range;
1188c2ecf20Sopenharmony_ci	unsigned int dc_dc_mode;
1198c2ecf20Sopenharmony_ci	unsigned int dc_dc_ilim;
1208c2ecf20Sopenharmony_ci	unsigned int slew_time;
1218c2ecf20Sopenharmony_ci	bool pwr_down;
1228c2ecf20Sopenharmony_ci	__be32 d32[3];
1238c2ecf20Sopenharmony_ci};
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci/*
1268c2ecf20Sopenharmony_ci * Output ranges corresponding to bits [3:0] from DAC_CONFIG register
1278c2ecf20Sopenharmony_ci * 0000: 0 V to 5 V voltage range
1288c2ecf20Sopenharmony_ci * 0001: 0 V to 10 V voltage range
1298c2ecf20Sopenharmony_ci * 0010: ±5 V voltage range
1308c2ecf20Sopenharmony_ci * 0011: ±10 V voltage range
1318c2ecf20Sopenharmony_ci * 1000: 0 mA to 20 mA current range
1328c2ecf20Sopenharmony_ci * 1001: 0 mA to 24 mA current range
1338c2ecf20Sopenharmony_ci * 1010: 4 mA to 20 mA current range
1348c2ecf20Sopenharmony_ci * 1011: ±20 mA current range
1358c2ecf20Sopenharmony_ci * 1100: ±24 mA current range
1368c2ecf20Sopenharmony_ci * 1101: -1 mA to +22 mA current range
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_cienum ad5758_output_range {
1398c2ecf20Sopenharmony_ci	AD5758_RANGE_0V_5V,
1408c2ecf20Sopenharmony_ci	AD5758_RANGE_0V_10V,
1418c2ecf20Sopenharmony_ci	AD5758_RANGE_PLUSMINUS_5V,
1428c2ecf20Sopenharmony_ci	AD5758_RANGE_PLUSMINUS_10V,
1438c2ecf20Sopenharmony_ci	AD5758_RANGE_0mA_20mA = 8,
1448c2ecf20Sopenharmony_ci	AD5758_RANGE_0mA_24mA,
1458c2ecf20Sopenharmony_ci	AD5758_RANGE_4mA_24mA,
1468c2ecf20Sopenharmony_ci	AD5758_RANGE_PLUSMINUS_20mA,
1478c2ecf20Sopenharmony_ci	AD5758_RANGE_PLUSMINUS_24mA,
1488c2ecf20Sopenharmony_ci	AD5758_RANGE_MINUS_1mA_PLUS_22mA,
1498c2ecf20Sopenharmony_ci};
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cienum ad5758_dc_dc_mode {
1528c2ecf20Sopenharmony_ci	AD5758_DCDC_MODE_POWER_OFF,
1538c2ecf20Sopenharmony_ci	AD5758_DCDC_MODE_DPC_CURRENT,
1548c2ecf20Sopenharmony_ci	AD5758_DCDC_MODE_DPC_VOLTAGE,
1558c2ecf20Sopenharmony_ci	AD5758_DCDC_MODE_PPC_CURRENT,
1568c2ecf20Sopenharmony_ci};
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic const struct ad5758_range ad5758_voltage_range[] = {
1598c2ecf20Sopenharmony_ci	{ AD5758_RANGE_0V_5V, 0, 5000000 },
1608c2ecf20Sopenharmony_ci	{ AD5758_RANGE_0V_10V, 0, 10000000 },
1618c2ecf20Sopenharmony_ci	{ AD5758_RANGE_PLUSMINUS_5V, -5000000, 5000000 },
1628c2ecf20Sopenharmony_ci	{ AD5758_RANGE_PLUSMINUS_10V, -10000000, 10000000 }
1638c2ecf20Sopenharmony_ci};
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const struct ad5758_range ad5758_current_range[] = {
1668c2ecf20Sopenharmony_ci	{ AD5758_RANGE_0mA_20mA, 0, 20000},
1678c2ecf20Sopenharmony_ci	{ AD5758_RANGE_0mA_24mA, 0, 24000 },
1688c2ecf20Sopenharmony_ci	{ AD5758_RANGE_4mA_24mA, 4, 24000 },
1698c2ecf20Sopenharmony_ci	{ AD5758_RANGE_PLUSMINUS_20mA, -20000, 20000 },
1708c2ecf20Sopenharmony_ci	{ AD5758_RANGE_PLUSMINUS_24mA, -24000, 24000 },
1718c2ecf20Sopenharmony_ci	{ AD5758_RANGE_MINUS_1mA_PLUS_22mA, -1000, 22000 },
1728c2ecf20Sopenharmony_ci};
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_cistatic const int ad5758_sr_clk[16] = {
1758c2ecf20Sopenharmony_ci	240000, 200000, 150000, 128000, 64000, 32000, 16000, 8000, 4000, 2000,
1768c2ecf20Sopenharmony_ci	1000, 512, 256, 128, 64, 16
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic const int ad5758_sr_step[8] = {
1808c2ecf20Sopenharmony_ci	4, 12, 64, 120, 256, 500, 1820, 2048
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic const int ad5758_dc_dc_ilim[6] = {
1848c2ecf20Sopenharmony_ci	150000, 200000, 250000, 300000, 350000, 400000
1858c2ecf20Sopenharmony_ci};
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic int ad5758_spi_reg_read(struct ad5758_state *st, unsigned int addr)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct spi_transfer t[] = {
1908c2ecf20Sopenharmony_ci		{
1918c2ecf20Sopenharmony_ci			.tx_buf = &st->d32[0],
1928c2ecf20Sopenharmony_ci			.len = 4,
1938c2ecf20Sopenharmony_ci			.cs_change = 1,
1948c2ecf20Sopenharmony_ci		}, {
1958c2ecf20Sopenharmony_ci			.tx_buf = &st->d32[1],
1968c2ecf20Sopenharmony_ci			.rx_buf = &st->d32[2],
1978c2ecf20Sopenharmony_ci			.len = 4,
1988c2ecf20Sopenharmony_ci		},
1998c2ecf20Sopenharmony_ci	};
2008c2ecf20Sopenharmony_ci	int ret;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	st->d32[0] = cpu_to_be32(
2038c2ecf20Sopenharmony_ci		(AD5758_WR_FLAG_MSK(AD5758_TWO_STAGE_READBACK_SELECT) << 24) |
2048c2ecf20Sopenharmony_ci		(addr << 8));
2058c2ecf20Sopenharmony_ci	st->d32[1] = cpu_to_be32(AD5758_WR_FLAG_MSK(AD5758_NOP) << 24);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = spi_sync_transfer(st->spi, t, ARRAY_SIZE(t));
2088c2ecf20Sopenharmony_ci	if (ret < 0)
2098c2ecf20Sopenharmony_ci		return ret;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	return (be32_to_cpu(st->d32[2]) >> 8) & 0xFFFF;
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_cistatic int ad5758_spi_reg_write(struct ad5758_state *st,
2158c2ecf20Sopenharmony_ci				unsigned int addr,
2168c2ecf20Sopenharmony_ci				unsigned int val)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	st->d32[0] = cpu_to_be32((AD5758_WR_FLAG_MSK(addr) << 24) |
2198c2ecf20Sopenharmony_ci				 ((val & 0xFFFF) << 8));
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return spi_write(st->spi, &st->d32[0], sizeof(st->d32[0]));
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic int ad5758_spi_write_mask(struct ad5758_state *st,
2258c2ecf20Sopenharmony_ci				 unsigned int addr,
2268c2ecf20Sopenharmony_ci				 unsigned long int mask,
2278c2ecf20Sopenharmony_ci				 unsigned int val)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	int regval;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	regval = ad5758_spi_reg_read(st, addr);
2328c2ecf20Sopenharmony_ci	if (regval < 0)
2338c2ecf20Sopenharmony_ci		return regval;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	regval &= ~mask;
2368c2ecf20Sopenharmony_ci	regval |= val;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	return ad5758_spi_reg_write(st, addr, regval);
2398c2ecf20Sopenharmony_ci}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic int cmpfunc(const void *a, const void *b)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	return *(int *)a - *(int *)b;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int ad5758_find_closest_match(const int *array,
2478c2ecf20Sopenharmony_ci				     unsigned int size, int val)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	int i;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
2528c2ecf20Sopenharmony_ci		if (val <= array[i])
2538c2ecf20Sopenharmony_ci			return i;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	return size - 1;
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_cistatic int ad5758_wait_for_task_complete(struct ad5758_state *st,
2608c2ecf20Sopenharmony_ci					 unsigned int reg,
2618c2ecf20Sopenharmony_ci					 unsigned int mask)
2628c2ecf20Sopenharmony_ci{
2638c2ecf20Sopenharmony_ci	unsigned int timeout;
2648c2ecf20Sopenharmony_ci	int ret;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	timeout = 10;
2678c2ecf20Sopenharmony_ci	do {
2688c2ecf20Sopenharmony_ci		ret = ad5758_spi_reg_read(st, reg);
2698c2ecf20Sopenharmony_ci		if (ret < 0)
2708c2ecf20Sopenharmony_ci			return ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci		if (!(ret & mask))
2738c2ecf20Sopenharmony_ci			return 0;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		usleep_range(100, 1000);
2768c2ecf20Sopenharmony_ci	} while (--timeout);
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	dev_err(&st->spi->dev,
2798c2ecf20Sopenharmony_ci		"Error reading bit 0x%x in 0x%x register\n", mask, reg);
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	return -EIO;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic int ad5758_calib_mem_refresh(struct ad5758_state *st)
2858c2ecf20Sopenharmony_ci{
2868c2ecf20Sopenharmony_ci	int ret;
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	ret = ad5758_spi_reg_write(st, AD5758_KEY,
2898c2ecf20Sopenharmony_ci				   AD5758_KEY_CODE_CALIB_MEM_REFRESH);
2908c2ecf20Sopenharmony_ci	if (ret < 0) {
2918c2ecf20Sopenharmony_ci		dev_err(&st->spi->dev,
2928c2ecf20Sopenharmony_ci			"Failed to initiate a calibration memory refresh\n");
2938c2ecf20Sopenharmony_ci		return ret;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	/* Wait to allow time for the internal calibrations to complete */
2978c2ecf20Sopenharmony_ci	return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
2988c2ecf20Sopenharmony_ci					     AD5758_CAL_MEM_UNREFRESHED_MSK);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic int ad5758_soft_reset(struct ad5758_state *st)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	int ret;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_1);
3068c2ecf20Sopenharmony_ci	if (ret < 0)
3078c2ecf20Sopenharmony_ci		return ret;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	ret = ad5758_spi_reg_write(st, AD5758_KEY, AD5758_KEY_CODE_RESET_2);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* Perform a software reset and wait at least 100us */
3128c2ecf20Sopenharmony_ci	usleep_range(100, 1000);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return ret;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int ad5758_set_dc_dc_conv_mode(struct ad5758_state *st,
3188c2ecf20Sopenharmony_ci				      enum ad5758_dc_dc_mode mode)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	int ret;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/*
3238c2ecf20Sopenharmony_ci	 * The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current
3248c2ecf20Sopenharmony_ci	 * mode.
3258c2ecf20Sopenharmony_ci	 */
3268c2ecf20Sopenharmony_ci	if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
3278c2ecf20Sopenharmony_ci		ret  = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
3288c2ecf20Sopenharmony_ci				    AD5758_ADC_CONFIG_PPC_BUF_MSK,
3298c2ecf20Sopenharmony_ci				    AD5758_ADC_CONFIG_PPC_BUF_EN(1));
3308c2ecf20Sopenharmony_ci		if (ret < 0)
3318c2ecf20Sopenharmony_ci			return ret;
3328c2ecf20Sopenharmony_ci	}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
3358c2ecf20Sopenharmony_ci				    AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
3368c2ecf20Sopenharmony_ci				    AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
3378c2ecf20Sopenharmony_ci	if (ret < 0)
3388c2ecf20Sopenharmony_ci		return ret;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/*
3418c2ecf20Sopenharmony_ci	 * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
3428c2ecf20Sopenharmony_ci	 * This allows the 3-wire interface communication to complete.
3438c2ecf20Sopenharmony_ci	 */
3448c2ecf20Sopenharmony_ci	ret = ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
3458c2ecf20Sopenharmony_ci					    AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
3468c2ecf20Sopenharmony_ci	if (ret < 0)
3478c2ecf20Sopenharmony_ci		return ret;
3488c2ecf20Sopenharmony_ci
3498c2ecf20Sopenharmony_ci	st->dc_dc_mode = mode;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	return ret;
3528c2ecf20Sopenharmony_ci}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_cistatic int ad5758_set_dc_dc_ilim(struct ad5758_state *st, unsigned int ilim)
3558c2ecf20Sopenharmony_ci{
3568c2ecf20Sopenharmony_ci	int ret;
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG2,
3598c2ecf20Sopenharmony_ci				    AD5758_DCDC_CONFIG2_ILIMIT_MSK,
3608c2ecf20Sopenharmony_ci				    AD5758_DCDC_CONFIG2_ILIMIT_MODE(ilim));
3618c2ecf20Sopenharmony_ci	if (ret < 0)
3628c2ecf20Sopenharmony_ci		return ret;
3638c2ecf20Sopenharmony_ci	/*
3648c2ecf20Sopenharmony_ci	 * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
3658c2ecf20Sopenharmony_ci	 * This allows the 3-wire interface communication to complete.
3668c2ecf20Sopenharmony_ci	 */
3678c2ecf20Sopenharmony_ci	return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
3688c2ecf20Sopenharmony_ci					     AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_cistatic int ad5758_slew_rate_set(struct ad5758_state *st,
3728c2ecf20Sopenharmony_ci				unsigned int sr_clk_idx,
3738c2ecf20Sopenharmony_ci				unsigned int sr_step_idx)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	unsigned int mode;
3768c2ecf20Sopenharmony_ci	unsigned long int mask;
3778c2ecf20Sopenharmony_ci	int ret;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	mask = AD5758_DAC_CONFIG_SR_EN_MSK |
3808c2ecf20Sopenharmony_ci	       AD5758_DAC_CONFIG_SR_CLOCK_MSK |
3818c2ecf20Sopenharmony_ci	       AD5758_DAC_CONFIG_SR_STEP_MSK;
3828c2ecf20Sopenharmony_ci	mode = AD5758_DAC_CONFIG_SR_EN_MODE(1) |
3838c2ecf20Sopenharmony_ci	       AD5758_DAC_CONFIG_SR_STEP_MODE(sr_step_idx) |
3848c2ecf20Sopenharmony_ci	       AD5758_DAC_CONFIG_SR_CLOCK_MODE(sr_clk_idx);
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG, mask, mode);
3878c2ecf20Sopenharmony_ci	if (ret < 0)
3888c2ecf20Sopenharmony_ci		return ret;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	/* Wait to allow time for the internal calibrations to complete */
3918c2ecf20Sopenharmony_ci	return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
3928c2ecf20Sopenharmony_ci					     AD5758_CAL_MEM_UNREFRESHED_MSK);
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic int ad5758_slew_rate_config(struct ad5758_state *st)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	unsigned int sr_clk_idx, sr_step_idx;
3988c2ecf20Sopenharmony_ci	int i, res;
3998c2ecf20Sopenharmony_ci	s64 diff_new, diff_old;
4008c2ecf20Sopenharmony_ci	u64 sr_step, calc_slew_time;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	sr_clk_idx = 0;
4038c2ecf20Sopenharmony_ci	sr_step_idx = 0;
4048c2ecf20Sopenharmony_ci	diff_old = S64_MAX;
4058c2ecf20Sopenharmony_ci	/*
4068c2ecf20Sopenharmony_ci	 * The slew time can be determined by using the formula:
4078c2ecf20Sopenharmony_ci	 * Slew Time = (Full Scale Out / (Step Size x Update Clk Freq))
4088c2ecf20Sopenharmony_ci	 * where Slew time is expressed in microseconds
4098c2ecf20Sopenharmony_ci	 * Given the desired slew time, the following algorithm determines the
4108c2ecf20Sopenharmony_ci	 * best match for the step size and the update clock frequency.
4118c2ecf20Sopenharmony_ci	 */
4128c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ad5758_sr_clk); i++) {
4138c2ecf20Sopenharmony_ci		/*
4148c2ecf20Sopenharmony_ci		 * Go through each valid update clock freq and determine a raw
4158c2ecf20Sopenharmony_ci		 * value for the step size by using the formula:
4168c2ecf20Sopenharmony_ci		 * Step Size = Full Scale Out / (Update Clk Freq * Slew Time)
4178c2ecf20Sopenharmony_ci		 */
4188c2ecf20Sopenharmony_ci		sr_step = AD5758_FULL_SCALE_MICRO;
4198c2ecf20Sopenharmony_ci		do_div(sr_step, ad5758_sr_clk[i]);
4208c2ecf20Sopenharmony_ci		do_div(sr_step, st->slew_time);
4218c2ecf20Sopenharmony_ci		/*
4228c2ecf20Sopenharmony_ci		 * After a raw value for step size was determined, find the
4238c2ecf20Sopenharmony_ci		 * closest valid match
4248c2ecf20Sopenharmony_ci		 */
4258c2ecf20Sopenharmony_ci		res = ad5758_find_closest_match(ad5758_sr_step,
4268c2ecf20Sopenharmony_ci						ARRAY_SIZE(ad5758_sr_step),
4278c2ecf20Sopenharmony_ci						sr_step);
4288c2ecf20Sopenharmony_ci		/* Calculate the slew time */
4298c2ecf20Sopenharmony_ci		calc_slew_time = AD5758_FULL_SCALE_MICRO;
4308c2ecf20Sopenharmony_ci		do_div(calc_slew_time, ad5758_sr_step[res]);
4318c2ecf20Sopenharmony_ci		do_div(calc_slew_time, ad5758_sr_clk[i]);
4328c2ecf20Sopenharmony_ci		/*
4338c2ecf20Sopenharmony_ci		 * Determine with how many microseconds the calculated slew time
4348c2ecf20Sopenharmony_ci		 * is different from the desired slew time and store the diff
4358c2ecf20Sopenharmony_ci		 * for the next iteration
4368c2ecf20Sopenharmony_ci		 */
4378c2ecf20Sopenharmony_ci		diff_new = abs(st->slew_time - calc_slew_time);
4388c2ecf20Sopenharmony_ci		if (diff_new < diff_old) {
4398c2ecf20Sopenharmony_ci			diff_old = diff_new;
4408c2ecf20Sopenharmony_ci			sr_clk_idx = i;
4418c2ecf20Sopenharmony_ci			sr_step_idx = res;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return ad5758_slew_rate_set(st, sr_clk_idx, sr_step_idx);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int ad5758_set_out_range(struct ad5758_state *st, int range)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	int ret;
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
4538c2ecf20Sopenharmony_ci				    AD5758_DAC_CONFIG_RANGE_MSK,
4548c2ecf20Sopenharmony_ci				    AD5758_DAC_CONFIG_RANGE_MODE(range));
4558c2ecf20Sopenharmony_ci	if (ret < 0)
4568c2ecf20Sopenharmony_ci		return ret;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	/* Wait to allow time for the internal calibrations to complete */
4598c2ecf20Sopenharmony_ci	return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
4608c2ecf20Sopenharmony_ci					     AD5758_CAL_MEM_UNREFRESHED_MSK);
4618c2ecf20Sopenharmony_ci}
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_cistatic int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
4648c2ecf20Sopenharmony_ci{
4658c2ecf20Sopenharmony_ci	int ret;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
4688c2ecf20Sopenharmony_ci				    AD5758_DAC_CONFIG_INT_EN_MSK,
4698c2ecf20Sopenharmony_ci				    AD5758_DAC_CONFIG_INT_EN_MODE(enable));
4708c2ecf20Sopenharmony_ci	if (ret < 0)
4718c2ecf20Sopenharmony_ci		return ret;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	/* Wait to allow time for the internal calibrations to complete */
4748c2ecf20Sopenharmony_ci	return ad5758_wait_for_task_complete(st, AD5758_DIGITAL_DIAG_RESULTS,
4758c2ecf20Sopenharmony_ci					     AD5758_CAL_MEM_UNREFRESHED_MSK);
4768c2ecf20Sopenharmony_ci}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_cistatic int ad5758_reset(struct ad5758_state *st)
4798c2ecf20Sopenharmony_ci{
4808c2ecf20Sopenharmony_ci	if (st->gpio_reset) {
4818c2ecf20Sopenharmony_ci		gpiod_set_value(st->gpio_reset, 0);
4828c2ecf20Sopenharmony_ci		usleep_range(100, 1000);
4838c2ecf20Sopenharmony_ci		gpiod_set_value(st->gpio_reset, 1);
4848c2ecf20Sopenharmony_ci		usleep_range(100, 1000);
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci		return 0;
4878c2ecf20Sopenharmony_ci	} else {
4888c2ecf20Sopenharmony_ci		/* Perform a software reset */
4898c2ecf20Sopenharmony_ci		return ad5758_soft_reset(st);
4908c2ecf20Sopenharmony_ci	}
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_cistatic int ad5758_reg_access(struct iio_dev *indio_dev,
4948c2ecf20Sopenharmony_ci			     unsigned int reg,
4958c2ecf20Sopenharmony_ci			     unsigned int writeval,
4968c2ecf20Sopenharmony_ci			     unsigned int *readval)
4978c2ecf20Sopenharmony_ci{
4988c2ecf20Sopenharmony_ci	struct ad5758_state *st = iio_priv(indio_dev);
4998c2ecf20Sopenharmony_ci	int ret;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	mutex_lock(&st->lock);
5028c2ecf20Sopenharmony_ci	if (readval) {
5038c2ecf20Sopenharmony_ci		ret = ad5758_spi_reg_read(st, reg);
5048c2ecf20Sopenharmony_ci		if (ret < 0) {
5058c2ecf20Sopenharmony_ci			mutex_unlock(&st->lock);
5068c2ecf20Sopenharmony_ci			return ret;
5078c2ecf20Sopenharmony_ci		}
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci		*readval = ret;
5108c2ecf20Sopenharmony_ci		ret = 0;
5118c2ecf20Sopenharmony_ci	} else {
5128c2ecf20Sopenharmony_ci		ret = ad5758_spi_reg_write(st, reg, writeval);
5138c2ecf20Sopenharmony_ci	}
5148c2ecf20Sopenharmony_ci	mutex_unlock(&st->lock);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return ret;
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_cistatic int ad5758_read_raw(struct iio_dev *indio_dev,
5208c2ecf20Sopenharmony_ci			   struct iio_chan_spec const *chan,
5218c2ecf20Sopenharmony_ci			   int *val, int *val2, long info)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct ad5758_state *st = iio_priv(indio_dev);
5248c2ecf20Sopenharmony_ci	int max, min, ret;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	switch (info) {
5278c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
5288c2ecf20Sopenharmony_ci		mutex_lock(&st->lock);
5298c2ecf20Sopenharmony_ci		ret = ad5758_spi_reg_read(st, AD5758_DAC_INPUT);
5308c2ecf20Sopenharmony_ci		mutex_unlock(&st->lock);
5318c2ecf20Sopenharmony_ci		if (ret < 0)
5328c2ecf20Sopenharmony_ci			return ret;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		*val = ret;
5358c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
5368c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
5378c2ecf20Sopenharmony_ci		min = st->out_range.min;
5388c2ecf20Sopenharmony_ci		max = st->out_range.max;
5398c2ecf20Sopenharmony_ci		*val = (max - min) / 1000;
5408c2ecf20Sopenharmony_ci		*val2 = 16;
5418c2ecf20Sopenharmony_ci		return IIO_VAL_FRACTIONAL_LOG2;
5428c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_OFFSET:
5438c2ecf20Sopenharmony_ci		min = st->out_range.min;
5448c2ecf20Sopenharmony_ci		max = st->out_range.max;
5458c2ecf20Sopenharmony_ci		*val = ((min * (1 << 16)) / (max - min)) / 1000;
5468c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
5478c2ecf20Sopenharmony_ci	default:
5488c2ecf20Sopenharmony_ci		return -EINVAL;
5498c2ecf20Sopenharmony_ci	}
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_cistatic int ad5758_write_raw(struct iio_dev *indio_dev,
5538c2ecf20Sopenharmony_ci			    struct iio_chan_spec const *chan,
5548c2ecf20Sopenharmony_ci			    int val, int val2, long info)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct ad5758_state *st = iio_priv(indio_dev);
5578c2ecf20Sopenharmony_ci	int ret;
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	switch (info) {
5608c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
5618c2ecf20Sopenharmony_ci		mutex_lock(&st->lock);
5628c2ecf20Sopenharmony_ci		ret = ad5758_spi_reg_write(st, AD5758_DAC_INPUT, val);
5638c2ecf20Sopenharmony_ci		mutex_unlock(&st->lock);
5648c2ecf20Sopenharmony_ci		return ret;
5658c2ecf20Sopenharmony_ci	default:
5668c2ecf20Sopenharmony_ci		return -EINVAL;
5678c2ecf20Sopenharmony_ci	}
5688c2ecf20Sopenharmony_ci}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_cistatic ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev,
5718c2ecf20Sopenharmony_ci				     uintptr_t priv,
5728c2ecf20Sopenharmony_ci				     const struct iio_chan_spec *chan,
5738c2ecf20Sopenharmony_ci				     char *buf)
5748c2ecf20Sopenharmony_ci{
5758c2ecf20Sopenharmony_ci	struct ad5758_state *st = iio_priv(indio_dev);
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", st->pwr_down);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistatic ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev,
5818c2ecf20Sopenharmony_ci				      uintptr_t priv,
5828c2ecf20Sopenharmony_ci				      struct iio_chan_spec const *chan,
5838c2ecf20Sopenharmony_ci				      const char *buf, size_t len)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	struct ad5758_state *st = iio_priv(indio_dev);
5868c2ecf20Sopenharmony_ci	bool pwr_down;
5878c2ecf20Sopenharmony_ci	unsigned int dac_config_mode, val;
5888c2ecf20Sopenharmony_ci	unsigned long int dac_config_msk;
5898c2ecf20Sopenharmony_ci	int ret;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	ret = kstrtobool(buf, &pwr_down);
5928c2ecf20Sopenharmony_ci	if (ret)
5938c2ecf20Sopenharmony_ci		return ret;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci	mutex_lock(&st->lock);
5968c2ecf20Sopenharmony_ci	if (pwr_down)
5978c2ecf20Sopenharmony_ci		val = 0;
5988c2ecf20Sopenharmony_ci	else
5998c2ecf20Sopenharmony_ci		val = 1;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
6028c2ecf20Sopenharmony_ci			  AD5758_DAC_CONFIG_INT_EN_MODE(val);
6038c2ecf20Sopenharmony_ci	dac_config_msk = AD5758_DAC_CONFIG_OUT_EN_MSK |
6048c2ecf20Sopenharmony_ci			 AD5758_DAC_CONFIG_INT_EN_MSK;
6058c2ecf20Sopenharmony_ci
6068c2ecf20Sopenharmony_ci	ret = ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
6078c2ecf20Sopenharmony_ci				    dac_config_msk,
6088c2ecf20Sopenharmony_ci				    dac_config_mode);
6098c2ecf20Sopenharmony_ci	if (ret < 0)
6108c2ecf20Sopenharmony_ci		goto err_unlock;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	st->pwr_down = pwr_down;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_cierr_unlock:
6158c2ecf20Sopenharmony_ci	mutex_unlock(&st->lock);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	return ret ? ret : len;
6188c2ecf20Sopenharmony_ci}
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_cistatic const struct iio_info ad5758_info = {
6218c2ecf20Sopenharmony_ci	.read_raw = ad5758_read_raw,
6228c2ecf20Sopenharmony_ci	.write_raw = ad5758_write_raw,
6238c2ecf20Sopenharmony_ci	.debugfs_reg_access = &ad5758_reg_access,
6248c2ecf20Sopenharmony_ci};
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_cistatic const struct iio_chan_spec_ext_info ad5758_ext_info[] = {
6278c2ecf20Sopenharmony_ci	{
6288c2ecf20Sopenharmony_ci		.name = "powerdown",
6298c2ecf20Sopenharmony_ci		.read = ad5758_read_powerdown,
6308c2ecf20Sopenharmony_ci		.write = ad5758_write_powerdown,
6318c2ecf20Sopenharmony_ci		.shared = IIO_SHARED_BY_TYPE,
6328c2ecf20Sopenharmony_ci	},
6338c2ecf20Sopenharmony_ci	{ }
6348c2ecf20Sopenharmony_ci};
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci#define AD5758_DAC_CHAN(_chan_type) {				\
6378c2ecf20Sopenharmony_ci	.type = (_chan_type),					\
6388c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_RAW) |	\
6398c2ecf20Sopenharmony_ci		BIT(IIO_CHAN_INFO_SCALE) |			\
6408c2ecf20Sopenharmony_ci		BIT(IIO_CHAN_INFO_OFFSET),			\
6418c2ecf20Sopenharmony_ci	.indexed = 1,						\
6428c2ecf20Sopenharmony_ci	.output = 1,						\
6438c2ecf20Sopenharmony_ci	.ext_info = ad5758_ext_info,				\
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic const struct iio_chan_spec ad5758_voltage_ch[] = {
6478c2ecf20Sopenharmony_ci	AD5758_DAC_CHAN(IIO_VOLTAGE)
6488c2ecf20Sopenharmony_ci};
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic const struct iio_chan_spec ad5758_current_ch[] = {
6518c2ecf20Sopenharmony_ci	AD5758_DAC_CHAN(IIO_CURRENT)
6528c2ecf20Sopenharmony_ci};
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic bool ad5758_is_valid_mode(enum ad5758_dc_dc_mode mode)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	switch (mode) {
6578c2ecf20Sopenharmony_ci	case AD5758_DCDC_MODE_DPC_CURRENT:
6588c2ecf20Sopenharmony_ci	case AD5758_DCDC_MODE_DPC_VOLTAGE:
6598c2ecf20Sopenharmony_ci	case AD5758_DCDC_MODE_PPC_CURRENT:
6608c2ecf20Sopenharmony_ci		return true;
6618c2ecf20Sopenharmony_ci	default:
6628c2ecf20Sopenharmony_ci		return false;
6638c2ecf20Sopenharmony_ci	}
6648c2ecf20Sopenharmony_ci}
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_cistatic int ad5758_crc_disable(struct ad5758_state *st)
6678c2ecf20Sopenharmony_ci{
6688c2ecf20Sopenharmony_ci	unsigned int mask;
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	mask = (AD5758_WR_FLAG_MSK(AD5758_DIGITAL_DIAG_CONFIG) << 24) | 0x5C3A;
6718c2ecf20Sopenharmony_ci	st->d32[0] = cpu_to_be32(mask);
6728c2ecf20Sopenharmony_ci
6738c2ecf20Sopenharmony_ci	return spi_write(st->spi, &st->d32[0], 4);
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_cistatic int ad5758_find_out_range(struct ad5758_state *st,
6778c2ecf20Sopenharmony_ci				 const struct ad5758_range *range,
6788c2ecf20Sopenharmony_ci				 unsigned int size,
6798c2ecf20Sopenharmony_ci				 int min, int max)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	int i;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++) {
6848c2ecf20Sopenharmony_ci		if ((min == range[i].min) && (max == range[i].max)) {
6858c2ecf20Sopenharmony_ci			st->out_range.reg = range[i].reg;
6868c2ecf20Sopenharmony_ci			st->out_range.min = range[i].min;
6878c2ecf20Sopenharmony_ci			st->out_range.max = range[i].max;
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci			return 0;
6908c2ecf20Sopenharmony_ci		}
6918c2ecf20Sopenharmony_ci	}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	return -EINVAL;
6948c2ecf20Sopenharmony_ci}
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_cistatic int ad5758_parse_dt(struct ad5758_state *st)
6978c2ecf20Sopenharmony_ci{
6988c2ecf20Sopenharmony_ci	unsigned int tmp, tmparray[2], size;
6998c2ecf20Sopenharmony_ci	const struct ad5758_range *range;
7008c2ecf20Sopenharmony_ci	int *index, ret;
7018c2ecf20Sopenharmony_ci
7028c2ecf20Sopenharmony_ci	st->dc_dc_ilim = 0;
7038c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&st->spi->dev,
7048c2ecf20Sopenharmony_ci				       "adi,dc-dc-ilim-microamp", &tmp);
7058c2ecf20Sopenharmony_ci	if (ret) {
7068c2ecf20Sopenharmony_ci		dev_dbg(&st->spi->dev,
7078c2ecf20Sopenharmony_ci			"Missing \"dc-dc-ilim-microamp\" property\n");
7088c2ecf20Sopenharmony_ci	} else {
7098c2ecf20Sopenharmony_ci		index = bsearch(&tmp, ad5758_dc_dc_ilim,
7108c2ecf20Sopenharmony_ci				ARRAY_SIZE(ad5758_dc_dc_ilim),
7118c2ecf20Sopenharmony_ci				sizeof(int), cmpfunc);
7128c2ecf20Sopenharmony_ci		if (!index)
7138c2ecf20Sopenharmony_ci			dev_dbg(&st->spi->dev, "dc-dc-ilim out of range\n");
7148c2ecf20Sopenharmony_ci		else
7158c2ecf20Sopenharmony_ci			st->dc_dc_ilim = index - ad5758_dc_dc_ilim;
7168c2ecf20Sopenharmony_ci	}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&st->spi->dev, "adi,dc-dc-mode",
7198c2ecf20Sopenharmony_ci				       &st->dc_dc_mode);
7208c2ecf20Sopenharmony_ci	if (ret) {
7218c2ecf20Sopenharmony_ci		dev_err(&st->spi->dev, "Missing \"dc-dc-mode\" property\n");
7228c2ecf20Sopenharmony_ci		return ret;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (!ad5758_is_valid_mode(st->dc_dc_mode))
7268c2ecf20Sopenharmony_ci		return -EINVAL;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE) {
7298c2ecf20Sopenharmony_ci		ret = device_property_read_u32_array(&st->spi->dev,
7308c2ecf20Sopenharmony_ci						     "adi,range-microvolt",
7318c2ecf20Sopenharmony_ci						     tmparray, 2);
7328c2ecf20Sopenharmony_ci		if (ret) {
7338c2ecf20Sopenharmony_ci			dev_err(&st->spi->dev,
7348c2ecf20Sopenharmony_ci				"Missing \"range-microvolt\" property\n");
7358c2ecf20Sopenharmony_ci			return ret;
7368c2ecf20Sopenharmony_ci		}
7378c2ecf20Sopenharmony_ci		range = ad5758_voltage_range;
7388c2ecf20Sopenharmony_ci		size = ARRAY_SIZE(ad5758_voltage_range);
7398c2ecf20Sopenharmony_ci	} else {
7408c2ecf20Sopenharmony_ci		ret = device_property_read_u32_array(&st->spi->dev,
7418c2ecf20Sopenharmony_ci						     "adi,range-microamp",
7428c2ecf20Sopenharmony_ci						     tmparray, 2);
7438c2ecf20Sopenharmony_ci		if (ret) {
7448c2ecf20Sopenharmony_ci			dev_err(&st->spi->dev,
7458c2ecf20Sopenharmony_ci				"Missing \"range-microamp\" property\n");
7468c2ecf20Sopenharmony_ci			return ret;
7478c2ecf20Sopenharmony_ci		}
7488c2ecf20Sopenharmony_ci		range = ad5758_current_range;
7498c2ecf20Sopenharmony_ci		size = ARRAY_SIZE(ad5758_current_range);
7508c2ecf20Sopenharmony_ci	}
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	ret = ad5758_find_out_range(st, range, size, tmparray[0], tmparray[1]);
7538c2ecf20Sopenharmony_ci	if (ret) {
7548c2ecf20Sopenharmony_ci		dev_err(&st->spi->dev, "range invalid\n");
7558c2ecf20Sopenharmony_ci		return ret;
7568c2ecf20Sopenharmony_ci	}
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	ret = device_property_read_u32(&st->spi->dev, "adi,slew-time-us", &tmp);
7598c2ecf20Sopenharmony_ci	if (ret) {
7608c2ecf20Sopenharmony_ci		dev_dbg(&st->spi->dev, "Missing \"slew-time-us\" property\n");
7618c2ecf20Sopenharmony_ci		st->slew_time = 0;
7628c2ecf20Sopenharmony_ci	} else {
7638c2ecf20Sopenharmony_ci		st->slew_time = tmp;
7648c2ecf20Sopenharmony_ci	}
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	return 0;
7678c2ecf20Sopenharmony_ci}
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_cistatic int ad5758_init(struct ad5758_state *st)
7708c2ecf20Sopenharmony_ci{
7718c2ecf20Sopenharmony_ci	int regval, ret;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
7748c2ecf20Sopenharmony_ci						 GPIOD_OUT_HIGH);
7758c2ecf20Sopenharmony_ci	if (IS_ERR(st->gpio_reset))
7768c2ecf20Sopenharmony_ci		return PTR_ERR(st->gpio_reset);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	/* Disable CRC checks */
7798c2ecf20Sopenharmony_ci	ret = ad5758_crc_disable(st);
7808c2ecf20Sopenharmony_ci	if (ret < 0)
7818c2ecf20Sopenharmony_ci		return ret;
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* Perform a reset */
7848c2ecf20Sopenharmony_ci	ret = ad5758_reset(st);
7858c2ecf20Sopenharmony_ci	if (ret < 0)
7868c2ecf20Sopenharmony_ci		return ret;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	/* Disable CRC checks */
7898c2ecf20Sopenharmony_ci	ret = ad5758_crc_disable(st);
7908c2ecf20Sopenharmony_ci	if (ret < 0)
7918c2ecf20Sopenharmony_ci		return ret;
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* Perform a calibration memory refresh */
7948c2ecf20Sopenharmony_ci	ret = ad5758_calib_mem_refresh(st);
7958c2ecf20Sopenharmony_ci	if (ret < 0)
7968c2ecf20Sopenharmony_ci		return ret;
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci	regval = ad5758_spi_reg_read(st, AD5758_DIGITAL_DIAG_RESULTS);
7998c2ecf20Sopenharmony_ci	if (regval < 0)
8008c2ecf20Sopenharmony_ci		return regval;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	/* Clear all the error flags */
8038c2ecf20Sopenharmony_ci	ret = ad5758_spi_reg_write(st, AD5758_DIGITAL_DIAG_RESULTS, regval);
8048c2ecf20Sopenharmony_ci	if (ret < 0)
8058c2ecf20Sopenharmony_ci		return ret;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	/* Set the dc-to-dc current limit */
8088c2ecf20Sopenharmony_ci	ret = ad5758_set_dc_dc_ilim(st, st->dc_dc_ilim);
8098c2ecf20Sopenharmony_ci	if (ret < 0)
8108c2ecf20Sopenharmony_ci		return ret;
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	/* Configure the dc-to-dc controller mode */
8138c2ecf20Sopenharmony_ci	ret = ad5758_set_dc_dc_conv_mode(st, st->dc_dc_mode);
8148c2ecf20Sopenharmony_ci	if (ret < 0)
8158c2ecf20Sopenharmony_ci		return ret;
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	/* Configure the output range */
8188c2ecf20Sopenharmony_ci	ret = ad5758_set_out_range(st, st->out_range.reg);
8198c2ecf20Sopenharmony_ci	if (ret < 0)
8208c2ecf20Sopenharmony_ci		return ret;
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci	/* Enable Slew Rate Control, set the slew rate clock and step */
8238c2ecf20Sopenharmony_ci	if (st->slew_time) {
8248c2ecf20Sopenharmony_ci		ret = ad5758_slew_rate_config(st);
8258c2ecf20Sopenharmony_ci		if (ret < 0)
8268c2ecf20Sopenharmony_ci			return ret;
8278c2ecf20Sopenharmony_ci	}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* Power up the DAC and internal (INT) amplifiers */
8308c2ecf20Sopenharmony_ci	ret = ad5758_internal_buffers_en(st, 1);
8318c2ecf20Sopenharmony_ci	if (ret < 0)
8328c2ecf20Sopenharmony_ci		return ret;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* Enable VIOUT */
8358c2ecf20Sopenharmony_ci	return ad5758_spi_write_mask(st, AD5758_DAC_CONFIG,
8368c2ecf20Sopenharmony_ci				     AD5758_DAC_CONFIG_OUT_EN_MSK,
8378c2ecf20Sopenharmony_ci				     AD5758_DAC_CONFIG_OUT_EN_MODE(1));
8388c2ecf20Sopenharmony_ci}
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_cistatic int ad5758_probe(struct spi_device *spi)
8418c2ecf20Sopenharmony_ci{
8428c2ecf20Sopenharmony_ci	struct ad5758_state *st;
8438c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
8448c2ecf20Sopenharmony_ci	int ret;
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
8478c2ecf20Sopenharmony_ci	if (!indio_dev)
8488c2ecf20Sopenharmony_ci		return -ENOMEM;
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	st = iio_priv(indio_dev);
8518c2ecf20Sopenharmony_ci	spi_set_drvdata(spi, indio_dev);
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	st->spi = spi;
8548c2ecf20Sopenharmony_ci
8558c2ecf20Sopenharmony_ci	mutex_init(&st->lock);
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	indio_dev->name = spi_get_device_id(spi)->name;
8588c2ecf20Sopenharmony_ci	indio_dev->info = &ad5758_info;
8598c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
8608c2ecf20Sopenharmony_ci	indio_dev->num_channels = 1;
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	ret = ad5758_parse_dt(st);
8638c2ecf20Sopenharmony_ci	if (ret < 0)
8648c2ecf20Sopenharmony_ci		return ret;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	if (st->dc_dc_mode == AD5758_DCDC_MODE_DPC_VOLTAGE)
8678c2ecf20Sopenharmony_ci		indio_dev->channels = ad5758_voltage_ch;
8688c2ecf20Sopenharmony_ci	else
8698c2ecf20Sopenharmony_ci		indio_dev->channels = ad5758_current_ch;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	ret = ad5758_init(st);
8728c2ecf20Sopenharmony_ci	if (ret < 0) {
8738c2ecf20Sopenharmony_ci		dev_err(&spi->dev, "AD5758 init failed\n");
8748c2ecf20Sopenharmony_ci		return ret;
8758c2ecf20Sopenharmony_ci	}
8768c2ecf20Sopenharmony_ci
8778c2ecf20Sopenharmony_ci	return devm_iio_device_register(&st->spi->dev, indio_dev);
8788c2ecf20Sopenharmony_ci}
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_cistatic const struct spi_device_id ad5758_id[] = {
8818c2ecf20Sopenharmony_ci	{ "ad5758", 0 },
8828c2ecf20Sopenharmony_ci	{}
8838c2ecf20Sopenharmony_ci};
8848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ad5758_id);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_cistatic const struct of_device_id ad5758_of_match[] = {
8878c2ecf20Sopenharmony_ci        { .compatible = "adi,ad5758" },
8888c2ecf20Sopenharmony_ci        { },
8898c2ecf20Sopenharmony_ci};
8908c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ad5758_of_match);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_cistatic struct spi_driver ad5758_driver = {
8938c2ecf20Sopenharmony_ci	.driver = {
8948c2ecf20Sopenharmony_ci		.name = KBUILD_MODNAME,
8958c2ecf20Sopenharmony_ci		.of_match_table = ad5758_of_match,
8968c2ecf20Sopenharmony_ci	},
8978c2ecf20Sopenharmony_ci	.probe = ad5758_probe,
8988c2ecf20Sopenharmony_ci	.id_table = ad5758_id,
8998c2ecf20Sopenharmony_ci};
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_cimodule_spi_driver(ad5758_driver);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
9048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices AD5758 DAC");
9058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
906