1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * AD5446 CORE DAC driver 4 * 5 * Copyright 2010 Analog Devices Inc. 6 */ 7 8 #include <linux/array_size.h> 9 #include <linux/cleanup.h> 10 #include <linux/device.h> 11 #include <linux/err.h> 12 #include <linux/export.h> 13 #include <linux/iio/iio.h> 14 #include <linux/kstrtox.h> 15 #include <linux/module.h> 16 #include <linux/mutex.h> 17 #include <linux/regulator/consumer.h> 18 #include <linux/sysfs.h> 19 20 #include "ad5446.h" 21 22 #define MODE_PWRDWN_1k 0x1 23 #define MODE_PWRDWN_100k 0x2 24 #define MODE_PWRDWN_TRISTATE 0x3 25 26 static const char * const ad5446_powerdown_modes[] = { 27 "1kohm_to_gnd", "100kohm_to_gnd", "three_state" 28 }; 29 30 static int ad5446_set_powerdown_mode(struct iio_dev *indio_dev, 31 const struct iio_chan_spec *chan, 32 unsigned int mode) 33 { 34 struct ad5446_state *st = iio_priv(indio_dev); 35 36 st->pwr_down_mode = mode + 1; 37 38 return 0; 39 } 40 41 static int ad5446_get_powerdown_mode(struct iio_dev *indio_dev, 42 const struct iio_chan_spec *chan) 43 { 44 struct ad5446_state *st = iio_priv(indio_dev); 45 46 return st->pwr_down_mode - 1; 47 } 48 49 static const struct iio_enum ad5446_powerdown_mode_enum = { 50 .items = ad5446_powerdown_modes, 51 .num_items = ARRAY_SIZE(ad5446_powerdown_modes), 52 .get = ad5446_get_powerdown_mode, 53 .set = ad5446_set_powerdown_mode, 54 }; 55 56 static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, 57 uintptr_t private, 58 const struct iio_chan_spec *chan, 59 char *buf) 60 { 61 struct ad5446_state *st = iio_priv(indio_dev); 62 63 return sysfs_emit(buf, "%d\n", st->pwr_down); 64 } 65 66 static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, 67 uintptr_t private, 68 const struct iio_chan_spec *chan, 69 const char *buf, size_t len) 70 { 71 struct ad5446_state *st = iio_priv(indio_dev); 72 unsigned int shift; 73 unsigned int val; 74 bool powerdown; 75 int ret; 76 77 ret = kstrtobool(buf, &powerdown); 78 if (ret) 79 return ret; 80 81 guard(mutex)(&st->lock); 82 st->pwr_down = powerdown; 83 84 if (st->pwr_down) { 85 shift = chan->scan_type.realbits + chan->scan_type.shift; 86 val = st->pwr_down_mode << shift; 87 } else { 88 val = st->cached_val; 89 } 90 91 ret = st->chip_info->write(st, val); 92 if (ret) 93 return ret; 94 95 return len; 96 } 97 98 const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { 99 { 100 .name = "powerdown", 101 .read = ad5446_read_dac_powerdown, 102 .write = ad5446_write_dac_powerdown, 103 .shared = IIO_SEPARATE, 104 }, 105 IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum), 106 IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5446_powerdown_mode_enum), 107 { } 108 }; 109 EXPORT_SYMBOL_NS_GPL(ad5446_ext_info_powerdown, "IIO_AD5446"); 110 111 static int ad5446_read_raw(struct iio_dev *indio_dev, 112 struct iio_chan_spec const *chan, 113 int *val, 114 int *val2, 115 long m) 116 { 117 struct ad5446_state *st = iio_priv(indio_dev); 118 119 switch (m) { 120 case IIO_CHAN_INFO_RAW: 121 *val = st->cached_val >> chan->scan_type.shift; 122 return IIO_VAL_INT; 123 case IIO_CHAN_INFO_SCALE: 124 *val = st->vref_mv; 125 *val2 = chan->scan_type.realbits; 126 return IIO_VAL_FRACTIONAL_LOG2; 127 } 128 return -EINVAL; 129 } 130 131 static int ad5446_write_dac_raw(struct iio_dev *indio_dev, 132 const struct iio_chan_spec *chan, 133 int val) 134 { 135 struct ad5446_state *st = iio_priv(indio_dev); 136 137 if (val >= (1 << chan->scan_type.realbits) || val < 0) 138 return -EINVAL; 139 140 val <<= chan->scan_type.shift; 141 guard(mutex)(&st->lock); 142 143 st->cached_val = val; 144 if (st->pwr_down) 145 return 0; 146 147 return st->chip_info->write(st, val); 148 } 149 150 static int ad5446_write_raw(struct iio_dev *indio_dev, 151 struct iio_chan_spec const *chan, int val, 152 int val2, long mask) 153 { 154 switch (mask) { 155 case IIO_CHAN_INFO_RAW: 156 return ad5446_write_dac_raw(indio_dev, chan, val); 157 default: 158 return -EINVAL; 159 } 160 } 161 162 static const struct iio_info ad5446_info = { 163 .read_raw = ad5446_read_raw, 164 .write_raw = ad5446_write_raw, 165 }; 166 167 int ad5446_probe(struct device *dev, const char *name, 168 const struct ad5446_chip_info *chip_info) 169 { 170 struct ad5446_state *st; 171 struct iio_dev *indio_dev; 172 int ret; 173 174 indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); 175 if (!indio_dev) 176 return -ENOMEM; 177 178 st = iio_priv(indio_dev); 179 st->chip_info = chip_info; 180 181 st->dev = dev; 182 183 indio_dev->name = name; 184 indio_dev->info = &ad5446_info; 185 indio_dev->modes = INDIO_DIRECT_MODE; 186 indio_dev->channels = &st->chip_info->channel; 187 indio_dev->num_channels = 1; 188 189 ret = devm_mutex_init(dev, &st->lock); 190 if (ret) 191 return ret; 192 193 st->pwr_down_mode = MODE_PWRDWN_1k; 194 195 ret = devm_regulator_get_enable_read_voltage(dev, "vcc"); 196 if (ret < 0 && ret != -ENODEV) 197 return ret; 198 if (ret == -ENODEV) { 199 if (!chip_info->int_vref_mv) 200 return dev_err_probe(dev, ret, 201 "reference voltage unspecified\n"); 202 203 st->vref_mv = chip_info->int_vref_mv; 204 } else { 205 st->vref_mv = ret / 1000; 206 } 207 208 return devm_iio_device_register(dev, indio_dev); 209 } 210 EXPORT_SYMBOL_NS_GPL(ad5446_probe, "IIO_AD5446"); 211 212 MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); 213 MODULE_DESCRIPTION("Analog Devices CORE AD5446 DAC and similar devices"); 214 MODULE_LICENSE("GPL v2"); 215