Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.19 214 lines 5.0 kB view raw
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 26static const char * const ad5446_powerdown_modes[] = { 27 "1kohm_to_gnd", "100kohm_to_gnd", "three_state" 28}; 29 30static 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 41static 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 49static 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 56static 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 66static 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 98const 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}; 109EXPORT_SYMBOL_NS_GPL(ad5446_ext_info_powerdown, "IIO_AD5446"); 110 111static 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 131static 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 150static 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 162static const struct iio_info ad5446_info = { 163 .read_raw = ad5446_read_raw, 164 .write_raw = ad5446_write_raw, 165}; 166 167int 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} 210EXPORT_SYMBOL_NS_GPL(ad5446_probe, "IIO_AD5446"); 211 212MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>"); 213MODULE_DESCRIPTION("Analog Devices CORE AD5446 DAC and similar devices"); 214MODULE_LICENSE("GPL v2");