xref: /openbmc/linux/drivers/iio/dac/ad5592r-base.c (revision 8e472061)
1fda8d26eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
256ca9db8SPaul Cercueil /*
356ca9db8SPaul Cercueil  * AD5592R Digital <-> Analog converters driver
456ca9db8SPaul Cercueil  *
556ca9db8SPaul Cercueil  * Copyright 2014-2016 Analog Devices Inc.
656ca9db8SPaul Cercueil  * Author: Paul Cercueil <paul.cercueil@analog.com>
756ca9db8SPaul Cercueil  */
856ca9db8SPaul Cercueil 
956ca9db8SPaul Cercueil #include <linux/bitops.h>
1056ca9db8SPaul Cercueil #include <linux/delay.h>
1156ca9db8SPaul Cercueil #include <linux/iio/iio.h>
1256ca9db8SPaul Cercueil #include <linux/module.h>
1356ca9db8SPaul Cercueil #include <linux/mutex.h>
1456ca9db8SPaul Cercueil #include <linux/regulator/consumer.h>
1556ca9db8SPaul Cercueil #include <linux/gpio/consumer.h>
1656ca9db8SPaul Cercueil #include <linux/gpio/driver.h>
1756ca9db8SPaul Cercueil #include <linux/property.h>
1856ca9db8SPaul Cercueil 
1956ca9db8SPaul Cercueil #include <dt-bindings/iio/adi,ad5592r.h>
2056ca9db8SPaul Cercueil 
2156ca9db8SPaul Cercueil #include "ad5592r-base.h"
2256ca9db8SPaul Cercueil 
ad5592r_gpio_get(struct gpio_chip * chip,unsigned offset)2356ca9db8SPaul Cercueil static int ad5592r_gpio_get(struct gpio_chip *chip, unsigned offset)
2456ca9db8SPaul Cercueil {
2556ca9db8SPaul Cercueil 	struct ad5592r_state *st = gpiochip_get_data(chip);
2656ca9db8SPaul Cercueil 	int ret = 0;
2756ca9db8SPaul Cercueil 	u8 val;
2856ca9db8SPaul Cercueil 
2956ca9db8SPaul Cercueil 	mutex_lock(&st->gpio_lock);
3056ca9db8SPaul Cercueil 
3156ca9db8SPaul Cercueil 	if (st->gpio_out & BIT(offset))
3256ca9db8SPaul Cercueil 		val = st->gpio_val;
3356ca9db8SPaul Cercueil 	else
3456ca9db8SPaul Cercueil 		ret = st->ops->gpio_read(st, &val);
3556ca9db8SPaul Cercueil 
3656ca9db8SPaul Cercueil 	mutex_unlock(&st->gpio_lock);
3756ca9db8SPaul Cercueil 
3856ca9db8SPaul Cercueil 	if (ret < 0)
3956ca9db8SPaul Cercueil 		return ret;
4056ca9db8SPaul Cercueil 
4156ca9db8SPaul Cercueil 	return !!(val & BIT(offset));
4256ca9db8SPaul Cercueil }
4356ca9db8SPaul Cercueil 
ad5592r_gpio_set(struct gpio_chip * chip,unsigned offset,int value)4456ca9db8SPaul Cercueil static void ad5592r_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
4556ca9db8SPaul Cercueil {
4656ca9db8SPaul Cercueil 	struct ad5592r_state *st = gpiochip_get_data(chip);
4756ca9db8SPaul Cercueil 
4856ca9db8SPaul Cercueil 	mutex_lock(&st->gpio_lock);
4956ca9db8SPaul Cercueil 
5056ca9db8SPaul Cercueil 	if (value)
5156ca9db8SPaul Cercueil 		st->gpio_val |= BIT(offset);
5256ca9db8SPaul Cercueil 	else
5356ca9db8SPaul Cercueil 		st->gpio_val &= ~BIT(offset);
5456ca9db8SPaul Cercueil 
5556ca9db8SPaul Cercueil 	st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
5656ca9db8SPaul Cercueil 
5756ca9db8SPaul Cercueil 	mutex_unlock(&st->gpio_lock);
5856ca9db8SPaul Cercueil }
5956ca9db8SPaul Cercueil 
ad5592r_gpio_direction_input(struct gpio_chip * chip,unsigned offset)6056ca9db8SPaul Cercueil static int ad5592r_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
6156ca9db8SPaul Cercueil {
6256ca9db8SPaul Cercueil 	struct ad5592r_state *st = gpiochip_get_data(chip);
6356ca9db8SPaul Cercueil 	int ret;
6456ca9db8SPaul Cercueil 
6556ca9db8SPaul Cercueil 	mutex_lock(&st->gpio_lock);
6656ca9db8SPaul Cercueil 
6756ca9db8SPaul Cercueil 	st->gpio_out &= ~BIT(offset);
6856ca9db8SPaul Cercueil 	st->gpio_in |= BIT(offset);
6956ca9db8SPaul Cercueil 
7056ca9db8SPaul Cercueil 	ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
7156ca9db8SPaul Cercueil 	if (ret < 0)
7256ca9db8SPaul Cercueil 		goto err_unlock;
7356ca9db8SPaul Cercueil 
7456ca9db8SPaul Cercueil 	ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
7556ca9db8SPaul Cercueil 
7656ca9db8SPaul Cercueil err_unlock:
7756ca9db8SPaul Cercueil 	mutex_unlock(&st->gpio_lock);
7856ca9db8SPaul Cercueil 
7956ca9db8SPaul Cercueil 	return ret;
8056ca9db8SPaul Cercueil }
8156ca9db8SPaul Cercueil 
ad5592r_gpio_direction_output(struct gpio_chip * chip,unsigned offset,int value)8256ca9db8SPaul Cercueil static int ad5592r_gpio_direction_output(struct gpio_chip *chip,
8356ca9db8SPaul Cercueil 					 unsigned offset, int value)
8456ca9db8SPaul Cercueil {
8556ca9db8SPaul Cercueil 	struct ad5592r_state *st = gpiochip_get_data(chip);
8656ca9db8SPaul Cercueil 	int ret;
8756ca9db8SPaul Cercueil 
8856ca9db8SPaul Cercueil 	mutex_lock(&st->gpio_lock);
8956ca9db8SPaul Cercueil 
9056ca9db8SPaul Cercueil 	if (value)
9156ca9db8SPaul Cercueil 		st->gpio_val |= BIT(offset);
9256ca9db8SPaul Cercueil 	else
9356ca9db8SPaul Cercueil 		st->gpio_val &= ~BIT(offset);
9456ca9db8SPaul Cercueil 
9556ca9db8SPaul Cercueil 	st->gpio_in &= ~BIT(offset);
9656ca9db8SPaul Cercueil 	st->gpio_out |= BIT(offset);
9756ca9db8SPaul Cercueil 
9856ca9db8SPaul Cercueil 	ret = st->ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
9956ca9db8SPaul Cercueil 	if (ret < 0)
10056ca9db8SPaul Cercueil 		goto err_unlock;
10156ca9db8SPaul Cercueil 
10256ca9db8SPaul Cercueil 	ret = st->ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
10356ca9db8SPaul Cercueil 	if (ret < 0)
10456ca9db8SPaul Cercueil 		goto err_unlock;
10556ca9db8SPaul Cercueil 
10656ca9db8SPaul Cercueil 	ret = st->ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
10756ca9db8SPaul Cercueil 
10856ca9db8SPaul Cercueil err_unlock:
10956ca9db8SPaul Cercueil 	mutex_unlock(&st->gpio_lock);
11056ca9db8SPaul Cercueil 
11156ca9db8SPaul Cercueil 	return ret;
11256ca9db8SPaul Cercueil }
11356ca9db8SPaul Cercueil 
ad5592r_gpio_request(struct gpio_chip * chip,unsigned offset)11456ca9db8SPaul Cercueil static int ad5592r_gpio_request(struct gpio_chip *chip, unsigned offset)
11556ca9db8SPaul Cercueil {
11656ca9db8SPaul Cercueil 	struct ad5592r_state *st = gpiochip_get_data(chip);
11756ca9db8SPaul Cercueil 
11856ca9db8SPaul Cercueil 	if (!(st->gpio_map & BIT(offset))) {
11956ca9db8SPaul Cercueil 		dev_err(st->dev, "GPIO %d is reserved by alternate function\n",
12056ca9db8SPaul Cercueil 			offset);
12156ca9db8SPaul Cercueil 		return -ENODEV;
12256ca9db8SPaul Cercueil 	}
12356ca9db8SPaul Cercueil 
12456ca9db8SPaul Cercueil 	return 0;
12556ca9db8SPaul Cercueil }
12656ca9db8SPaul Cercueil 
12739073859SAntoniu Miclaus static const char * const ad5592r_gpio_names[] = {
12839073859SAntoniu Miclaus 	"GPIO0", "GPIO1", "GPIO2", "GPIO3", "GPIO4", "GPIO5", "GPIO6", "GPIO7",
12939073859SAntoniu Miclaus };
13039073859SAntoniu Miclaus 
ad5592r_gpio_init(struct ad5592r_state * st)13156ca9db8SPaul Cercueil static int ad5592r_gpio_init(struct ad5592r_state *st)
13256ca9db8SPaul Cercueil {
13356ca9db8SPaul Cercueil 	if (!st->gpio_map)
13456ca9db8SPaul Cercueil 		return 0;
13556ca9db8SPaul Cercueil 
13656ca9db8SPaul Cercueil 	st->gpiochip.label = dev_name(st->dev);
13756ca9db8SPaul Cercueil 	st->gpiochip.base = -1;
13856ca9db8SPaul Cercueil 	st->gpiochip.ngpio = 8;
13956ca9db8SPaul Cercueil 	st->gpiochip.parent = st->dev;
14056ca9db8SPaul Cercueil 	st->gpiochip.can_sleep = true;
14156ca9db8SPaul Cercueil 	st->gpiochip.direction_input = ad5592r_gpio_direction_input;
14256ca9db8SPaul Cercueil 	st->gpiochip.direction_output = ad5592r_gpio_direction_output;
14356ca9db8SPaul Cercueil 	st->gpiochip.get = ad5592r_gpio_get;
14456ca9db8SPaul Cercueil 	st->gpiochip.set = ad5592r_gpio_set;
14556ca9db8SPaul Cercueil 	st->gpiochip.request = ad5592r_gpio_request;
14656ca9db8SPaul Cercueil 	st->gpiochip.owner = THIS_MODULE;
14739073859SAntoniu Miclaus 	st->gpiochip.names = ad5592r_gpio_names;
14856ca9db8SPaul Cercueil 
14956ca9db8SPaul Cercueil 	mutex_init(&st->gpio_lock);
15056ca9db8SPaul Cercueil 
15156ca9db8SPaul Cercueil 	return gpiochip_add_data(&st->gpiochip, st);
15256ca9db8SPaul Cercueil }
15356ca9db8SPaul Cercueil 
ad5592r_gpio_cleanup(struct ad5592r_state * st)15456ca9db8SPaul Cercueil static void ad5592r_gpio_cleanup(struct ad5592r_state *st)
15556ca9db8SPaul Cercueil {
15656ca9db8SPaul Cercueil 	if (st->gpio_map)
15756ca9db8SPaul Cercueil 		gpiochip_remove(&st->gpiochip);
15856ca9db8SPaul Cercueil }
15956ca9db8SPaul Cercueil 
ad5592r_reset(struct ad5592r_state * st)16056ca9db8SPaul Cercueil static int ad5592r_reset(struct ad5592r_state *st)
16156ca9db8SPaul Cercueil {
16256ca9db8SPaul Cercueil 	struct gpio_desc *gpio;
16356ca9db8SPaul Cercueil 
16456ca9db8SPaul Cercueil 	gpio = devm_gpiod_get_optional(st->dev, "reset", GPIOD_OUT_LOW);
16556ca9db8SPaul Cercueil 	if (IS_ERR(gpio))
16656ca9db8SPaul Cercueil 		return PTR_ERR(gpio);
16756ca9db8SPaul Cercueil 
16856ca9db8SPaul Cercueil 	if (gpio) {
16956ca9db8SPaul Cercueil 		udelay(1);
17056ca9db8SPaul Cercueil 		gpiod_set_value(gpio, 1);
17156ca9db8SPaul Cercueil 	} else {
17233c53cbfSSergiu Cuciurean 		mutex_lock(&st->lock);
17356ca9db8SPaul Cercueil 		/* Writing this magic value resets the device */
17456ca9db8SPaul Cercueil 		st->ops->reg_write(st, AD5592R_REG_RESET, 0xdac);
17533c53cbfSSergiu Cuciurean 		mutex_unlock(&st->lock);
17656ca9db8SPaul Cercueil 	}
17756ca9db8SPaul Cercueil 
17856ca9db8SPaul Cercueil 	udelay(250);
17956ca9db8SPaul Cercueil 
18056ca9db8SPaul Cercueil 	return 0;
18156ca9db8SPaul Cercueil }
18256ca9db8SPaul Cercueil 
ad5592r_get_vref(struct ad5592r_state * st)18356ca9db8SPaul Cercueil static int ad5592r_get_vref(struct ad5592r_state *st)
18456ca9db8SPaul Cercueil {
18556ca9db8SPaul Cercueil 	int ret;
18656ca9db8SPaul Cercueil 
18756ca9db8SPaul Cercueil 	if (st->reg) {
18856ca9db8SPaul Cercueil 		ret = regulator_get_voltage(st->reg);
18956ca9db8SPaul Cercueil 		if (ret < 0)
19056ca9db8SPaul Cercueil 			return ret;
19156ca9db8SPaul Cercueil 
19256ca9db8SPaul Cercueil 		return ret / 1000;
19356ca9db8SPaul Cercueil 	} else {
19456ca9db8SPaul Cercueil 		return 2500;
19556ca9db8SPaul Cercueil 	}
19656ca9db8SPaul Cercueil }
19756ca9db8SPaul Cercueil 
ad5592r_set_channel_modes(struct ad5592r_state * st)19856ca9db8SPaul Cercueil static int ad5592r_set_channel_modes(struct ad5592r_state *st)
19956ca9db8SPaul Cercueil {
20056ca9db8SPaul Cercueil 	const struct ad5592r_rw_ops *ops = st->ops;
20156ca9db8SPaul Cercueil 	int ret;
20256ca9db8SPaul Cercueil 	unsigned i;
20356ca9db8SPaul Cercueil 	u8 pulldown = 0, tristate = 0, dac = 0, adc = 0;
20456ca9db8SPaul Cercueil 	u16 read_back;
20556ca9db8SPaul Cercueil 
20656ca9db8SPaul Cercueil 	for (i = 0; i < st->num_channels; i++) {
20756ca9db8SPaul Cercueil 		switch (st->channel_modes[i]) {
20856ca9db8SPaul Cercueil 		case CH_MODE_DAC:
20956ca9db8SPaul Cercueil 			dac |= BIT(i);
21056ca9db8SPaul Cercueil 			break;
21156ca9db8SPaul Cercueil 
21256ca9db8SPaul Cercueil 		case CH_MODE_ADC:
21356ca9db8SPaul Cercueil 			adc |= BIT(i);
21456ca9db8SPaul Cercueil 			break;
21556ca9db8SPaul Cercueil 
21656ca9db8SPaul Cercueil 		case CH_MODE_DAC_AND_ADC:
21756ca9db8SPaul Cercueil 			dac |= BIT(i);
21856ca9db8SPaul Cercueil 			adc |= BIT(i);
21956ca9db8SPaul Cercueil 			break;
22056ca9db8SPaul Cercueil 
22156ca9db8SPaul Cercueil 		case CH_MODE_GPIO:
22256ca9db8SPaul Cercueil 			st->gpio_map |= BIT(i);
22356ca9db8SPaul Cercueil 			st->gpio_in |= BIT(i); /* Default to input */
22456ca9db8SPaul Cercueil 			break;
22556ca9db8SPaul Cercueil 
22656ca9db8SPaul Cercueil 		case CH_MODE_UNUSED:
22756ca9db8SPaul Cercueil 		default:
22856ca9db8SPaul Cercueil 			switch (st->channel_offstate[i]) {
22956ca9db8SPaul Cercueil 			case CH_OFFSTATE_OUT_TRISTATE:
23056ca9db8SPaul Cercueil 				tristate |= BIT(i);
23156ca9db8SPaul Cercueil 				break;
23256ca9db8SPaul Cercueil 
23356ca9db8SPaul Cercueil 			case CH_OFFSTATE_OUT_LOW:
23456ca9db8SPaul Cercueil 				st->gpio_out |= BIT(i);
23556ca9db8SPaul Cercueil 				break;
23656ca9db8SPaul Cercueil 
23756ca9db8SPaul Cercueil 			case CH_OFFSTATE_OUT_HIGH:
23856ca9db8SPaul Cercueil 				st->gpio_out |= BIT(i);
23956ca9db8SPaul Cercueil 				st->gpio_val |= BIT(i);
24056ca9db8SPaul Cercueil 				break;
24156ca9db8SPaul Cercueil 
24256ca9db8SPaul Cercueil 			case CH_OFFSTATE_PULLDOWN:
24356ca9db8SPaul Cercueil 			default:
24456ca9db8SPaul Cercueil 				pulldown |= BIT(i);
24556ca9db8SPaul Cercueil 				break;
24656ca9db8SPaul Cercueil 			}
24756ca9db8SPaul Cercueil 		}
24856ca9db8SPaul Cercueil 	}
24956ca9db8SPaul Cercueil 
25033c53cbfSSergiu Cuciurean 	mutex_lock(&st->lock);
25156ca9db8SPaul Cercueil 
25256ca9db8SPaul Cercueil 	/* Pull down unused pins to GND */
25356ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_PULLDOWN, pulldown);
25456ca9db8SPaul Cercueil 	if (ret)
25556ca9db8SPaul Cercueil 		goto err_unlock;
25656ca9db8SPaul Cercueil 
25756ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_TRISTATE, tristate);
25856ca9db8SPaul Cercueil 	if (ret)
25956ca9db8SPaul Cercueil 		goto err_unlock;
26056ca9db8SPaul Cercueil 
26156ca9db8SPaul Cercueil 	/* Configure pins that we use */
26256ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_DAC_EN, dac);
26356ca9db8SPaul Cercueil 	if (ret)
26456ca9db8SPaul Cercueil 		goto err_unlock;
26556ca9db8SPaul Cercueil 
26656ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_ADC_EN, adc);
26756ca9db8SPaul Cercueil 	if (ret)
26856ca9db8SPaul Cercueil 		goto err_unlock;
26956ca9db8SPaul Cercueil 
27056ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_GPIO_SET, st->gpio_val);
27156ca9db8SPaul Cercueil 	if (ret)
27256ca9db8SPaul Cercueil 		goto err_unlock;
27356ca9db8SPaul Cercueil 
27456ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_GPIO_OUT_EN, st->gpio_out);
27556ca9db8SPaul Cercueil 	if (ret)
27656ca9db8SPaul Cercueil 		goto err_unlock;
27756ca9db8SPaul Cercueil 
27856ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_GPIO_IN_EN, st->gpio_in);
27956ca9db8SPaul Cercueil 	if (ret)
28056ca9db8SPaul Cercueil 		goto err_unlock;
28156ca9db8SPaul Cercueil 
28256ca9db8SPaul Cercueil 	/* Verify that we can read back at least one register */
28356ca9db8SPaul Cercueil 	ret = ops->reg_read(st, AD5592R_REG_ADC_EN, &read_back);
28456ca9db8SPaul Cercueil 	if (!ret && (read_back & 0xff) != adc)
28556ca9db8SPaul Cercueil 		ret = -EIO;
28656ca9db8SPaul Cercueil 
28756ca9db8SPaul Cercueil err_unlock:
28833c53cbfSSergiu Cuciurean 	mutex_unlock(&st->lock);
28956ca9db8SPaul Cercueil 	return ret;
29056ca9db8SPaul Cercueil }
29156ca9db8SPaul Cercueil 
ad5592r_reset_channel_modes(struct ad5592r_state * st)29256ca9db8SPaul Cercueil static int ad5592r_reset_channel_modes(struct ad5592r_state *st)
29356ca9db8SPaul Cercueil {
29456ca9db8SPaul Cercueil 	int i;
29556ca9db8SPaul Cercueil 
29656ca9db8SPaul Cercueil 	for (i = 0; i < ARRAY_SIZE(st->channel_modes); i++)
29756ca9db8SPaul Cercueil 		st->channel_modes[i] = CH_MODE_UNUSED;
29856ca9db8SPaul Cercueil 
29956ca9db8SPaul Cercueil 	return ad5592r_set_channel_modes(st);
30056ca9db8SPaul Cercueil }
30156ca9db8SPaul Cercueil 
ad5592r_write_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)30256ca9db8SPaul Cercueil static int ad5592r_write_raw(struct iio_dev *iio_dev,
30356ca9db8SPaul Cercueil 	struct iio_chan_spec const *chan, int val, int val2, long mask)
30456ca9db8SPaul Cercueil {
30556ca9db8SPaul Cercueil 	struct ad5592r_state *st = iio_priv(iio_dev);
30656ca9db8SPaul Cercueil 	int ret;
30756ca9db8SPaul Cercueil 
30856ca9db8SPaul Cercueil 	switch (mask) {
30956ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_RAW:
31056ca9db8SPaul Cercueil 
31156ca9db8SPaul Cercueil 		if (val >= (1 << chan->scan_type.realbits) || val < 0)
31256ca9db8SPaul Cercueil 			return -EINVAL;
31356ca9db8SPaul Cercueil 
31456ca9db8SPaul Cercueil 		if (!chan->output)
31556ca9db8SPaul Cercueil 			return -EINVAL;
31656ca9db8SPaul Cercueil 
31733c53cbfSSergiu Cuciurean 		mutex_lock(&st->lock);
31856ca9db8SPaul Cercueil 		ret = st->ops->write_dac(st, chan->channel, val);
31956ca9db8SPaul Cercueil 		if (!ret)
32056ca9db8SPaul Cercueil 			st->cached_dac[chan->channel] = val;
32133c53cbfSSergiu Cuciurean 		mutex_unlock(&st->lock);
32256ca9db8SPaul Cercueil 		return ret;
32356ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_SCALE:
32456ca9db8SPaul Cercueil 		if (chan->type == IIO_VOLTAGE) {
32556ca9db8SPaul Cercueil 			bool gain;
32656ca9db8SPaul Cercueil 
32756ca9db8SPaul Cercueil 			if (val == st->scale_avail[0][0] &&
32856ca9db8SPaul Cercueil 				val2 == st->scale_avail[0][1])
32956ca9db8SPaul Cercueil 				gain = false;
33056ca9db8SPaul Cercueil 			else if (val == st->scale_avail[1][0] &&
33156ca9db8SPaul Cercueil 				 val2 == st->scale_avail[1][1])
33256ca9db8SPaul Cercueil 				gain = true;
33356ca9db8SPaul Cercueil 			else
33456ca9db8SPaul Cercueil 				return -EINVAL;
33556ca9db8SPaul Cercueil 
33633c53cbfSSergiu Cuciurean 			mutex_lock(&st->lock);
33756ca9db8SPaul Cercueil 
33856ca9db8SPaul Cercueil 			ret = st->ops->reg_read(st, AD5592R_REG_CTRL,
33956ca9db8SPaul Cercueil 						&st->cached_gp_ctrl);
34056ca9db8SPaul Cercueil 			if (ret < 0) {
34133c53cbfSSergiu Cuciurean 				mutex_unlock(&st->lock);
34256ca9db8SPaul Cercueil 				return ret;
34356ca9db8SPaul Cercueil 			}
34456ca9db8SPaul Cercueil 
34556ca9db8SPaul Cercueil 			if (chan->output) {
34656ca9db8SPaul Cercueil 				if (gain)
34756ca9db8SPaul Cercueil 					st->cached_gp_ctrl |=
34856ca9db8SPaul Cercueil 						AD5592R_REG_CTRL_DAC_RANGE;
34956ca9db8SPaul Cercueil 				else
35056ca9db8SPaul Cercueil 					st->cached_gp_ctrl &=
35156ca9db8SPaul Cercueil 						~AD5592R_REG_CTRL_DAC_RANGE;
35256ca9db8SPaul Cercueil 			} else {
35356ca9db8SPaul Cercueil 				if (gain)
35456ca9db8SPaul Cercueil 					st->cached_gp_ctrl |=
35556ca9db8SPaul Cercueil 						AD5592R_REG_CTRL_ADC_RANGE;
35656ca9db8SPaul Cercueil 				else
35756ca9db8SPaul Cercueil 					st->cached_gp_ctrl &=
35856ca9db8SPaul Cercueil 						~AD5592R_REG_CTRL_ADC_RANGE;
35956ca9db8SPaul Cercueil 			}
36056ca9db8SPaul Cercueil 
36156ca9db8SPaul Cercueil 			ret = st->ops->reg_write(st, AD5592R_REG_CTRL,
36256ca9db8SPaul Cercueil 						 st->cached_gp_ctrl);
36333c53cbfSSergiu Cuciurean 			mutex_unlock(&st->lock);
36456ca9db8SPaul Cercueil 
36556ca9db8SPaul Cercueil 			return ret;
36656ca9db8SPaul Cercueil 		}
36756ca9db8SPaul Cercueil 		break;
36856ca9db8SPaul Cercueil 	default:
36956ca9db8SPaul Cercueil 		return -EINVAL;
37056ca9db8SPaul Cercueil 	}
37156ca9db8SPaul Cercueil 
37256ca9db8SPaul Cercueil 	return 0;
37356ca9db8SPaul Cercueil }
37456ca9db8SPaul Cercueil 
ad5592r_read_raw(struct iio_dev * iio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long m)37556ca9db8SPaul Cercueil static int ad5592r_read_raw(struct iio_dev *iio_dev,
37656ca9db8SPaul Cercueil 			   struct iio_chan_spec const *chan,
37756ca9db8SPaul Cercueil 			   int *val, int *val2, long m)
37856ca9db8SPaul Cercueil {
37956ca9db8SPaul Cercueil 	struct ad5592r_state *st = iio_priv(iio_dev);
38056ca9db8SPaul Cercueil 	u16 read_val;
381b004fe33SAlexandru Ardelean 	int ret, mult;
38256ca9db8SPaul Cercueil 
38356ca9db8SPaul Cercueil 	switch (m) {
38456ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_RAW:
38556ca9db8SPaul Cercueil 		if (!chan->output) {
386c8bb10c5SAlexandru Ardelean 			mutex_lock(&st->lock);
38756ca9db8SPaul Cercueil 			ret = st->ops->read_adc(st, chan->channel, &read_val);
388c8bb10c5SAlexandru Ardelean 			mutex_unlock(&st->lock);
38956ca9db8SPaul Cercueil 			if (ret)
390c8bb10c5SAlexandru Ardelean 				return ret;
39156ca9db8SPaul Cercueil 
39256ca9db8SPaul Cercueil 			if ((read_val >> 12 & 0x7) != (chan->channel & 0x7)) {
39356ca9db8SPaul Cercueil 				dev_err(st->dev, "Error while reading channel %u\n",
39456ca9db8SPaul Cercueil 						chan->channel);
395c8bb10c5SAlexandru Ardelean 				return -EIO;
39656ca9db8SPaul Cercueil 			}
39756ca9db8SPaul Cercueil 
39856ca9db8SPaul Cercueil 			read_val &= GENMASK(11, 0);
39956ca9db8SPaul Cercueil 
40056ca9db8SPaul Cercueil 		} else {
401c8bb10c5SAlexandru Ardelean 			mutex_lock(&st->lock);
40256ca9db8SPaul Cercueil 			read_val = st->cached_dac[chan->channel];
403c8bb10c5SAlexandru Ardelean 			mutex_unlock(&st->lock);
40456ca9db8SPaul Cercueil 		}
40556ca9db8SPaul Cercueil 
40656ca9db8SPaul Cercueil 		dev_dbg(st->dev, "Channel %u read: 0x%04hX\n",
40756ca9db8SPaul Cercueil 				chan->channel, read_val);
40856ca9db8SPaul Cercueil 
40956ca9db8SPaul Cercueil 		*val = (int) read_val;
410c8bb10c5SAlexandru Ardelean 		return IIO_VAL_INT;
41156ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_SCALE:
41256ca9db8SPaul Cercueil 		*val = ad5592r_get_vref(st);
41356ca9db8SPaul Cercueil 
41456ca9db8SPaul Cercueil 		if (chan->type == IIO_TEMP) {
41556ca9db8SPaul Cercueil 			s64 tmp = *val * (3767897513LL / 25LL);
41656ca9db8SPaul Cercueil 			*val = div_s64_rem(tmp, 1000000000LL, val2);
41756ca9db8SPaul Cercueil 
4188e472061SMarc Ferland 			return IIO_VAL_INT_PLUS_NANO;
419b004fe33SAlexandru Ardelean 		}
42056ca9db8SPaul Cercueil 
42133c53cbfSSergiu Cuciurean 		mutex_lock(&st->lock);
42256ca9db8SPaul Cercueil 
42356ca9db8SPaul Cercueil 		if (chan->output)
42456ca9db8SPaul Cercueil 			mult = !!(st->cached_gp_ctrl &
42556ca9db8SPaul Cercueil 				AD5592R_REG_CTRL_DAC_RANGE);
42656ca9db8SPaul Cercueil 		else
42756ca9db8SPaul Cercueil 			mult = !!(st->cached_gp_ctrl &
42856ca9db8SPaul Cercueil 				AD5592R_REG_CTRL_ADC_RANGE);
42956ca9db8SPaul Cercueil 
430c8bb10c5SAlexandru Ardelean 		mutex_unlock(&st->lock);
431c8bb10c5SAlexandru Ardelean 
43256ca9db8SPaul Cercueil 		*val *= ++mult;
43356ca9db8SPaul Cercueil 
43456ca9db8SPaul Cercueil 		*val2 = chan->scan_type.realbits;
435c8bb10c5SAlexandru Ardelean 
436c8bb10c5SAlexandru Ardelean 		return IIO_VAL_FRACTIONAL_LOG2;
43756ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_OFFSET:
43856ca9db8SPaul Cercueil 		ret = ad5592r_get_vref(st);
43956ca9db8SPaul Cercueil 
44033c53cbfSSergiu Cuciurean 		mutex_lock(&st->lock);
44156ca9db8SPaul Cercueil 
44256ca9db8SPaul Cercueil 		if (st->cached_gp_ctrl & AD5592R_REG_CTRL_ADC_RANGE)
44356ca9db8SPaul Cercueil 			*val = (-34365 * 25) / ret;
44456ca9db8SPaul Cercueil 		else
44556ca9db8SPaul Cercueil 			*val = (-75365 * 25) / ret;
446c8bb10c5SAlexandru Ardelean 
447c8bb10c5SAlexandru Ardelean 		mutex_unlock(&st->lock);
448c8bb10c5SAlexandru Ardelean 
449c8bb10c5SAlexandru Ardelean 		return IIO_VAL_INT;
45056ca9db8SPaul Cercueil 	default:
45165afb093SAlexandru Ardelean 		return -EINVAL;
45256ca9db8SPaul Cercueil 	}
45356ca9db8SPaul Cercueil }
45456ca9db8SPaul Cercueil 
ad5592r_write_raw_get_fmt(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,long mask)45556ca9db8SPaul Cercueil static int ad5592r_write_raw_get_fmt(struct iio_dev *indio_dev,
45656ca9db8SPaul Cercueil 				 struct iio_chan_spec const *chan, long mask)
45756ca9db8SPaul Cercueil {
45856ca9db8SPaul Cercueil 	switch (mask) {
45956ca9db8SPaul Cercueil 	case IIO_CHAN_INFO_SCALE:
46056ca9db8SPaul Cercueil 		return IIO_VAL_INT_PLUS_NANO;
46156ca9db8SPaul Cercueil 
46256ca9db8SPaul Cercueil 	default:
46356ca9db8SPaul Cercueil 		return IIO_VAL_INT_PLUS_MICRO;
46456ca9db8SPaul Cercueil 	}
46556ca9db8SPaul Cercueil 
46656ca9db8SPaul Cercueil 	return -EINVAL;
46756ca9db8SPaul Cercueil }
46856ca9db8SPaul Cercueil 
46956ca9db8SPaul Cercueil static const struct iio_info ad5592r_info = {
47056ca9db8SPaul Cercueil 	.read_raw = ad5592r_read_raw,
47156ca9db8SPaul Cercueil 	.write_raw = ad5592r_write_raw,
47256ca9db8SPaul Cercueil 	.write_raw_get_fmt = ad5592r_write_raw_get_fmt,
47356ca9db8SPaul Cercueil };
47456ca9db8SPaul Cercueil 
ad5592r_show_scale_available(struct iio_dev * iio_dev,uintptr_t private,const struct iio_chan_spec * chan,char * buf)47556ca9db8SPaul Cercueil static ssize_t ad5592r_show_scale_available(struct iio_dev *iio_dev,
47656ca9db8SPaul Cercueil 					   uintptr_t private,
47756ca9db8SPaul Cercueil 					   const struct iio_chan_spec *chan,
47856ca9db8SPaul Cercueil 					   char *buf)
47956ca9db8SPaul Cercueil {
48056ca9db8SPaul Cercueil 	struct ad5592r_state *st = iio_priv(iio_dev);
48156ca9db8SPaul Cercueil 
48256ca9db8SPaul Cercueil 	return sprintf(buf, "%d.%09u %d.%09u\n",
48356ca9db8SPaul Cercueil 		st->scale_avail[0][0], st->scale_avail[0][1],
48456ca9db8SPaul Cercueil 		st->scale_avail[1][0], st->scale_avail[1][1]);
48556ca9db8SPaul Cercueil }
48656ca9db8SPaul Cercueil 
4876110cdceSRikard Falkeborn static const struct iio_chan_spec_ext_info ad5592r_ext_info[] = {
48856ca9db8SPaul Cercueil 	{
48956ca9db8SPaul Cercueil 	 .name = "scale_available",
49056ca9db8SPaul Cercueil 	 .read = ad5592r_show_scale_available,
4915fe68a4dSJonathan Cameron 	 .shared = IIO_SHARED_BY_TYPE,
49256ca9db8SPaul Cercueil 	 },
49356ca9db8SPaul Cercueil 	{},
49456ca9db8SPaul Cercueil };
49556ca9db8SPaul Cercueil 
ad5592r_setup_channel(struct iio_dev * iio_dev,struct iio_chan_spec * chan,bool output,unsigned id)49656ca9db8SPaul Cercueil static void ad5592r_setup_channel(struct iio_dev *iio_dev,
49756ca9db8SPaul Cercueil 		struct iio_chan_spec *chan, bool output, unsigned id)
49856ca9db8SPaul Cercueil {
49956ca9db8SPaul Cercueil 	chan->type = IIO_VOLTAGE;
50056ca9db8SPaul Cercueil 	chan->indexed = 1;
50156ca9db8SPaul Cercueil 	chan->output = output;
50256ca9db8SPaul Cercueil 	chan->channel = id;
50356ca9db8SPaul Cercueil 	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
50456ca9db8SPaul Cercueil 	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
50556ca9db8SPaul Cercueil 	chan->scan_type.sign = 'u';
50656ca9db8SPaul Cercueil 	chan->scan_type.realbits = 12;
50756ca9db8SPaul Cercueil 	chan->scan_type.storagebits = 16;
50856ca9db8SPaul Cercueil 	chan->ext_info = ad5592r_ext_info;
50956ca9db8SPaul Cercueil }
51056ca9db8SPaul Cercueil 
ad5592r_alloc_channels(struct iio_dev * iio_dev)511723151a2SAlexandru Ardelean static int ad5592r_alloc_channels(struct iio_dev *iio_dev)
51256ca9db8SPaul Cercueil {
513723151a2SAlexandru Ardelean 	struct ad5592r_state *st = iio_priv(iio_dev);
51456ca9db8SPaul Cercueil 	unsigned i, curr_channel = 0,
51556ca9db8SPaul Cercueil 		 num_channels = st->num_channels;
51656ca9db8SPaul Cercueil 	struct iio_chan_spec *channels;
51756ca9db8SPaul Cercueil 	struct fwnode_handle *child;
51856ca9db8SPaul Cercueil 	u32 reg, tmp;
51956ca9db8SPaul Cercueil 	int ret;
52056ca9db8SPaul Cercueil 
52156ca9db8SPaul Cercueil 	device_for_each_child_node(st->dev, child) {
52256ca9db8SPaul Cercueil 		ret = fwnode_property_read_u32(child, "reg", &reg);
52304bf0217SDan Carpenter 		if (ret || reg >= ARRAY_SIZE(st->channel_modes))
52456ca9db8SPaul Cercueil 			continue;
52556ca9db8SPaul Cercueil 
52656ca9db8SPaul Cercueil 		ret = fwnode_property_read_u32(child, "adi,mode", &tmp);
52756ca9db8SPaul Cercueil 		if (!ret)
52856ca9db8SPaul Cercueil 			st->channel_modes[reg] = tmp;
52956ca9db8SPaul Cercueil 
530b55b38f7SZizhuang Deng 		ret = fwnode_property_read_u32(child, "adi,off-state", &tmp);
53156ca9db8SPaul Cercueil 		if (!ret)
53256ca9db8SPaul Cercueil 			st->channel_offstate[reg] = tmp;
53356ca9db8SPaul Cercueil 	}
53456ca9db8SPaul Cercueil 
535a86854d0SKees Cook 	channels = devm_kcalloc(st->dev,
536a86854d0SKees Cook 			1 + 2 * num_channels, sizeof(*channels),
537a86854d0SKees Cook 			GFP_KERNEL);
53856ca9db8SPaul Cercueil 	if (!channels)
53956ca9db8SPaul Cercueil 		return -ENOMEM;
54056ca9db8SPaul Cercueil 
54156ca9db8SPaul Cercueil 	for (i = 0; i < num_channels; i++) {
54256ca9db8SPaul Cercueil 		switch (st->channel_modes[i]) {
54356ca9db8SPaul Cercueil 		case CH_MODE_DAC:
54456ca9db8SPaul Cercueil 			ad5592r_setup_channel(iio_dev, &channels[curr_channel],
54556ca9db8SPaul Cercueil 					true, i);
54656ca9db8SPaul Cercueil 			curr_channel++;
54756ca9db8SPaul Cercueil 			break;
54856ca9db8SPaul Cercueil 
54956ca9db8SPaul Cercueil 		case CH_MODE_ADC:
55056ca9db8SPaul Cercueil 			ad5592r_setup_channel(iio_dev, &channels[curr_channel],
55156ca9db8SPaul Cercueil 					false, i);
55256ca9db8SPaul Cercueil 			curr_channel++;
55356ca9db8SPaul Cercueil 			break;
55456ca9db8SPaul Cercueil 
55556ca9db8SPaul Cercueil 		case CH_MODE_DAC_AND_ADC:
55656ca9db8SPaul Cercueil 			ad5592r_setup_channel(iio_dev, &channels[curr_channel],
55756ca9db8SPaul Cercueil 					true, i);
55856ca9db8SPaul Cercueil 			curr_channel++;
55956ca9db8SPaul Cercueil 			ad5592r_setup_channel(iio_dev, &channels[curr_channel],
56056ca9db8SPaul Cercueil 					false, i);
56156ca9db8SPaul Cercueil 			curr_channel++;
56256ca9db8SPaul Cercueil 			break;
56356ca9db8SPaul Cercueil 
56456ca9db8SPaul Cercueil 		default:
56556ca9db8SPaul Cercueil 			continue;
56656ca9db8SPaul Cercueil 		}
56756ca9db8SPaul Cercueil 	}
56856ca9db8SPaul Cercueil 
56956ca9db8SPaul Cercueil 	channels[curr_channel].type = IIO_TEMP;
57056ca9db8SPaul Cercueil 	channels[curr_channel].channel = 8;
57156ca9db8SPaul Cercueil 	channels[curr_channel].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
57256ca9db8SPaul Cercueil 				   BIT(IIO_CHAN_INFO_SCALE) |
57356ca9db8SPaul Cercueil 				   BIT(IIO_CHAN_INFO_OFFSET);
57456ca9db8SPaul Cercueil 	curr_channel++;
57556ca9db8SPaul Cercueil 
57656ca9db8SPaul Cercueil 	iio_dev->num_channels = curr_channel;
57756ca9db8SPaul Cercueil 	iio_dev->channels = channels;
57856ca9db8SPaul Cercueil 
57956ca9db8SPaul Cercueil 	return 0;
58056ca9db8SPaul Cercueil }
58156ca9db8SPaul Cercueil 
ad5592r_init_scales(struct ad5592r_state * st,int vref_mV)58256ca9db8SPaul Cercueil static void ad5592r_init_scales(struct ad5592r_state *st, int vref_mV)
58356ca9db8SPaul Cercueil {
58456ca9db8SPaul Cercueil 	s64 tmp = (s64)vref_mV * 1000000000LL >> 12;
58556ca9db8SPaul Cercueil 
58656ca9db8SPaul Cercueil 	st->scale_avail[0][0] =
58756ca9db8SPaul Cercueil 		div_s64_rem(tmp, 1000000000LL, &st->scale_avail[0][1]);
58856ca9db8SPaul Cercueil 	st->scale_avail[1][0] =
58956ca9db8SPaul Cercueil 		div_s64_rem(tmp * 2, 1000000000LL, &st->scale_avail[1][1]);
59056ca9db8SPaul Cercueil }
59156ca9db8SPaul Cercueil 
ad5592r_probe(struct device * dev,const char * name,const struct ad5592r_rw_ops * ops)59256ca9db8SPaul Cercueil int ad5592r_probe(struct device *dev, const char *name,
59356ca9db8SPaul Cercueil 		const struct ad5592r_rw_ops *ops)
59456ca9db8SPaul Cercueil {
59556ca9db8SPaul Cercueil 	struct iio_dev *iio_dev;
59656ca9db8SPaul Cercueil 	struct ad5592r_state *st;
59756ca9db8SPaul Cercueil 	int ret;
59856ca9db8SPaul Cercueil 
59956ca9db8SPaul Cercueil 	iio_dev = devm_iio_device_alloc(dev, sizeof(*st));
60056ca9db8SPaul Cercueil 	if (!iio_dev)
60156ca9db8SPaul Cercueil 		return -ENOMEM;
60256ca9db8SPaul Cercueil 
60356ca9db8SPaul Cercueil 	st = iio_priv(iio_dev);
60456ca9db8SPaul Cercueil 	st->dev = dev;
60556ca9db8SPaul Cercueil 	st->ops = ops;
60656ca9db8SPaul Cercueil 	st->num_channels = 8;
60756ca9db8SPaul Cercueil 	dev_set_drvdata(dev, iio_dev);
60856ca9db8SPaul Cercueil 
60956ca9db8SPaul Cercueil 	st->reg = devm_regulator_get_optional(dev, "vref");
61056ca9db8SPaul Cercueil 	if (IS_ERR(st->reg)) {
6113b9b4357SAndy Shevchenko 		if ((PTR_ERR(st->reg) != -ENODEV) && dev_fwnode(dev))
61256ca9db8SPaul Cercueil 			return PTR_ERR(st->reg);
61356ca9db8SPaul Cercueil 
61456ca9db8SPaul Cercueil 		st->reg = NULL;
61556ca9db8SPaul Cercueil 	} else {
61656ca9db8SPaul Cercueil 		ret = regulator_enable(st->reg);
61756ca9db8SPaul Cercueil 		if (ret)
61856ca9db8SPaul Cercueil 			return ret;
61956ca9db8SPaul Cercueil 	}
62056ca9db8SPaul Cercueil 
62156ca9db8SPaul Cercueil 	iio_dev->name = name;
62256ca9db8SPaul Cercueil 	iio_dev->info = &ad5592r_info;
62356ca9db8SPaul Cercueil 	iio_dev->modes = INDIO_DIRECT_MODE;
62456ca9db8SPaul Cercueil 
62533c53cbfSSergiu Cuciurean 	mutex_init(&st->lock);
62633c53cbfSSergiu Cuciurean 
62756ca9db8SPaul Cercueil 	ad5592r_init_scales(st, ad5592r_get_vref(st));
62856ca9db8SPaul Cercueil 
62956ca9db8SPaul Cercueil 	ret = ad5592r_reset(st);
63056ca9db8SPaul Cercueil 	if (ret)
63156ca9db8SPaul Cercueil 		goto error_disable_reg;
63256ca9db8SPaul Cercueil 
63356ca9db8SPaul Cercueil 	ret = ops->reg_write(st, AD5592R_REG_PD,
63456ca9db8SPaul Cercueil 		     (st->reg == NULL) ? AD5592R_REG_PD_EN_REF : 0);
63556ca9db8SPaul Cercueil 	if (ret)
63656ca9db8SPaul Cercueil 		goto error_disable_reg;
63756ca9db8SPaul Cercueil 
638723151a2SAlexandru Ardelean 	ret = ad5592r_alloc_channels(iio_dev);
63956ca9db8SPaul Cercueil 	if (ret)
64056ca9db8SPaul Cercueil 		goto error_disable_reg;
64156ca9db8SPaul Cercueil 
64256ca9db8SPaul Cercueil 	ret = ad5592r_set_channel_modes(st);
64356ca9db8SPaul Cercueil 	if (ret)
64456ca9db8SPaul Cercueil 		goto error_reset_ch_modes;
64556ca9db8SPaul Cercueil 
64656ca9db8SPaul Cercueil 	ret = iio_device_register(iio_dev);
64756ca9db8SPaul Cercueil 	if (ret)
64856ca9db8SPaul Cercueil 		goto error_reset_ch_modes;
64956ca9db8SPaul Cercueil 
65056ca9db8SPaul Cercueil 	ret = ad5592r_gpio_init(st);
65156ca9db8SPaul Cercueil 	if (ret)
65256ca9db8SPaul Cercueil 		goto error_dev_unregister;
65356ca9db8SPaul Cercueil 
65456ca9db8SPaul Cercueil 	return 0;
65556ca9db8SPaul Cercueil 
65656ca9db8SPaul Cercueil error_dev_unregister:
65756ca9db8SPaul Cercueil 	iio_device_unregister(iio_dev);
65856ca9db8SPaul Cercueil 
65956ca9db8SPaul Cercueil error_reset_ch_modes:
66056ca9db8SPaul Cercueil 	ad5592r_reset_channel_modes(st);
66156ca9db8SPaul Cercueil 
66256ca9db8SPaul Cercueil error_disable_reg:
66356ca9db8SPaul Cercueil 	if (st->reg)
66456ca9db8SPaul Cercueil 		regulator_disable(st->reg);
66556ca9db8SPaul Cercueil 
66656ca9db8SPaul Cercueil 	return ret;
66756ca9db8SPaul Cercueil }
6684dcd7384SJonathan Cameron EXPORT_SYMBOL_NS_GPL(ad5592r_probe, IIO_AD5592R);
66956ca9db8SPaul Cercueil 
ad5592r_remove(struct device * dev)67072ba4505SUwe Kleine-König void ad5592r_remove(struct device *dev)
67156ca9db8SPaul Cercueil {
67256ca9db8SPaul Cercueil 	struct iio_dev *iio_dev = dev_get_drvdata(dev);
67356ca9db8SPaul Cercueil 	struct ad5592r_state *st = iio_priv(iio_dev);
67456ca9db8SPaul Cercueil 
67556ca9db8SPaul Cercueil 	iio_device_unregister(iio_dev);
67656ca9db8SPaul Cercueil 	ad5592r_reset_channel_modes(st);
67756ca9db8SPaul Cercueil 	ad5592r_gpio_cleanup(st);
67856ca9db8SPaul Cercueil 
67956ca9db8SPaul Cercueil 	if (st->reg)
68056ca9db8SPaul Cercueil 		regulator_disable(st->reg);
68156ca9db8SPaul Cercueil }
6824dcd7384SJonathan Cameron EXPORT_SYMBOL_NS_GPL(ad5592r_remove, IIO_AD5592R);
68356ca9db8SPaul Cercueil 
68456ca9db8SPaul Cercueil MODULE_AUTHOR("Paul Cercueil <paul.cercueil@analog.com>");
68556ca9db8SPaul Cercueil MODULE_DESCRIPTION("Analog Devices AD5592R multi-channel converters");
68656ca9db8SPaul Cercueil MODULE_LICENSE("GPL v2");
687