| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * AD7303 Digital to analog converters driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright 2013 Analog Devices Inc. |
|---|
| 5 | | - * |
|---|
| 6 | | - * Licensed under the GPL-2. |
|---|
| 7 | 6 | */ |
|---|
| 8 | 7 | |
|---|
| 9 | 8 | #include <linux/err.h> |
|---|
| 10 | 9 | #include <linux/module.h> |
|---|
| 10 | +#include <linux/mod_devicetable.h> |
|---|
| 11 | 11 | #include <linux/kernel.h> |
|---|
| 12 | 12 | #include <linux/spi/spi.h> |
|---|
| 13 | 13 | #include <linux/slab.h> |
|---|
| 14 | 14 | #include <linux/sysfs.h> |
|---|
| 15 | 15 | #include <linux/regulator/consumer.h> |
|---|
| 16 | | -#include <linux/of.h> |
|---|
| 17 | 16 | |
|---|
| 18 | 17 | #include <linux/iio/iio.h> |
|---|
| 19 | 18 | #include <linux/iio/sysfs.h> |
|---|
| .. | .. |
|---|
| 31 | 30 | * @spi: the device for this driver instance |
|---|
| 32 | 31 | * @config: cached config register value |
|---|
| 33 | 32 | * @dac_cache: current DAC raw value (chip does not support readback) |
|---|
| 33 | + * @vdd_reg: reference to VDD regulator |
|---|
| 34 | + * @vref_reg: reference to VREF regulator |
|---|
| 35 | + * @lock: protect writes and cache updates |
|---|
| 34 | 36 | * @data: spi transfer buffer |
|---|
| 35 | 37 | */ |
|---|
| 36 | 38 | |
|---|
| .. | .. |
|---|
| 42 | 44 | struct regulator *vdd_reg; |
|---|
| 43 | 45 | struct regulator *vref_reg; |
|---|
| 44 | 46 | |
|---|
| 47 | + struct mutex lock; |
|---|
| 45 | 48 | /* |
|---|
| 46 | 49 | * DMA (thus cache coherency maintenance) requires the |
|---|
| 47 | 50 | * transfer buffers to live in their own cache lines. |
|---|
| .. | .. |
|---|
| 80 | 83 | if (ret) |
|---|
| 81 | 84 | return ret; |
|---|
| 82 | 85 | |
|---|
| 83 | | - mutex_lock(&indio_dev->mlock); |
|---|
| 86 | + mutex_lock(&st->lock); |
|---|
| 84 | 87 | |
|---|
| 85 | 88 | if (pwr_down) |
|---|
| 86 | 89 | st->config |= AD7303_CFG_POWER_DOWN(chan->channel); |
|---|
| .. | .. |
|---|
| 91 | 94 | * mode, so just write one of the DAC channels again */ |
|---|
| 92 | 95 | ad7303_write(st, chan->channel, st->dac_cache[chan->channel]); |
|---|
| 93 | 96 | |
|---|
| 94 | | - mutex_unlock(&indio_dev->mlock); |
|---|
| 97 | + mutex_unlock(&st->lock); |
|---|
| 95 | 98 | return len; |
|---|
| 96 | 99 | } |
|---|
| 97 | 100 | |
|---|
| .. | .. |
|---|
| 117 | 120 | |
|---|
| 118 | 121 | switch (info) { |
|---|
| 119 | 122 | case IIO_CHAN_INFO_RAW: |
|---|
| 123 | + mutex_lock(&st->lock); |
|---|
| 120 | 124 | *val = st->dac_cache[chan->channel]; |
|---|
| 125 | + mutex_unlock(&st->lock); |
|---|
| 121 | 126 | return IIO_VAL_INT; |
|---|
| 122 | 127 | case IIO_CHAN_INFO_SCALE: |
|---|
| 123 | 128 | vref_uv = ad7303_get_vref(st, chan); |
|---|
| .. | .. |
|---|
| 145 | 150 | if (val >= (1 << chan->scan_type.realbits) || val < 0) |
|---|
| 146 | 151 | return -EINVAL; |
|---|
| 147 | 152 | |
|---|
| 148 | | - mutex_lock(&indio_dev->mlock); |
|---|
| 153 | + mutex_lock(&st->lock); |
|---|
| 149 | 154 | ret = ad7303_write(st, chan->address, val); |
|---|
| 150 | 155 | if (ret == 0) |
|---|
| 151 | 156 | st->dac_cache[chan->channel] = val; |
|---|
| 152 | | - mutex_unlock(&indio_dev->mlock); |
|---|
| 157 | + mutex_unlock(&st->lock); |
|---|
| 153 | 158 | break; |
|---|
| 154 | 159 | default: |
|---|
| 155 | 160 | ret = -EINVAL; |
|---|
| .. | .. |
|---|
| 200 | 205 | const struct spi_device_id *id = spi_get_device_id(spi); |
|---|
| 201 | 206 | struct iio_dev *indio_dev; |
|---|
| 202 | 207 | struct ad7303_state *st; |
|---|
| 203 | | - bool ext_ref; |
|---|
| 204 | 208 | int ret; |
|---|
| 205 | 209 | |
|---|
| 206 | 210 | indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); |
|---|
| .. | .. |
|---|
| 212 | 216 | |
|---|
| 213 | 217 | st->spi = spi; |
|---|
| 214 | 218 | |
|---|
| 219 | + mutex_init(&st->lock); |
|---|
| 220 | + |
|---|
| 215 | 221 | st->vdd_reg = devm_regulator_get(&spi->dev, "Vdd"); |
|---|
| 216 | 222 | if (IS_ERR(st->vdd_reg)) |
|---|
| 217 | 223 | return PTR_ERR(st->vdd_reg); |
|---|
| .. | .. |
|---|
| 220 | 226 | if (ret) |
|---|
| 221 | 227 | return ret; |
|---|
| 222 | 228 | |
|---|
| 223 | | - if (spi->dev.of_node) { |
|---|
| 224 | | - ext_ref = of_property_read_bool(spi->dev.of_node, |
|---|
| 225 | | - "REF-supply"); |
|---|
| 226 | | - } else { |
|---|
| 227 | | - struct ad7303_platform_data *pdata = spi->dev.platform_data; |
|---|
| 228 | | - if (pdata && pdata->use_external_ref) |
|---|
| 229 | | - ext_ref = true; |
|---|
| 230 | | - else |
|---|
| 231 | | - ext_ref = false; |
|---|
| 229 | + st->vref_reg = devm_regulator_get_optional(&spi->dev, "REF"); |
|---|
| 230 | + if (IS_ERR(st->vref_reg)) { |
|---|
| 231 | + ret = PTR_ERR(st->vref_reg); |
|---|
| 232 | + if (ret != -ENODEV) |
|---|
| 233 | + goto err_disable_vdd_reg; |
|---|
| 234 | + st->vref_reg = NULL; |
|---|
| 232 | 235 | } |
|---|
| 233 | 236 | |
|---|
| 234 | | - if (ext_ref) { |
|---|
| 235 | | - st->vref_reg = devm_regulator_get(&spi->dev, "REF"); |
|---|
| 236 | | - if (IS_ERR(st->vref_reg)) { |
|---|
| 237 | | - ret = PTR_ERR(st->vref_reg); |
|---|
| 238 | | - goto err_disable_vdd_reg; |
|---|
| 239 | | - } |
|---|
| 240 | | - |
|---|
| 237 | + if (st->vref_reg) { |
|---|
| 241 | 238 | ret = regulator_enable(st->vref_reg); |
|---|
| 242 | 239 | if (ret) |
|---|
| 243 | 240 | goto err_disable_vdd_reg; |
|---|
| .. | .. |
|---|
| 245 | 242 | st->config |= AD7303_CFG_EXTERNAL_VREF; |
|---|
| 246 | 243 | } |
|---|
| 247 | 244 | |
|---|
| 248 | | - indio_dev->dev.parent = &spi->dev; |
|---|
| 249 | 245 | indio_dev->name = id->name; |
|---|
| 250 | 246 | indio_dev->info = &ad7303_info; |
|---|
| 251 | 247 | indio_dev->modes = INDIO_DIRECT_MODE; |
|---|
| .. | .. |
|---|
| 295 | 291 | static struct spi_driver ad7303_driver = { |
|---|
| 296 | 292 | .driver = { |
|---|
| 297 | 293 | .name = "ad7303", |
|---|
| 298 | | - .of_match_table = of_match_ptr(ad7303_spi_of_match), |
|---|
| 294 | + .of_match_table = ad7303_spi_of_match, |
|---|
| 299 | 295 | }, |
|---|
| 300 | 296 | .probe = ad7303_probe, |
|---|
| 301 | 297 | .remove = ad7303_remove, |
|---|