> -----Original Message----- > From: Jonathan Cameron <jic23@xxxxxxxxxx> > Sent: Sunday, September 8, 2024 1:15 AM > To: Tinaco, Mariel <Mariel.Tinaco@xxxxxxxxxx> > Cc: linux-iio@xxxxxxxxxxxxxxx; devicetree@xxxxxxxxxxxxxxx; linux- > kernel@xxxxxxxxxxxxxxx; Lars-Peter Clausen <lars@xxxxxxxxxx>; Rob Herring > <robh@xxxxxxxxxx>; Krzysztof Kozlowski <krzk+dt@xxxxxxxxxx>; Hennerich, > Michael <Michael.Hennerich@xxxxxxxxxx>; Conor Dooley > <conor+dt@xxxxxxxxxx>; Marcelo Schmitt <marcelo.schmitt1@xxxxxxxxx>; > Dimitri Fedrau <dima.fedrau@xxxxxxxxx>; David Lechner > <dlechner@xxxxxxxxxxxx>; Nuno Sá <noname.nuno@xxxxxxxxx> > Subject: Re: [PATCH v3 2/2] iio: dac: support the ad8460 Waveform DAC > > [External] > > On Wed, 4 Sep 2024 10:30:40 +0800 > Mariel Tinaco <Mariel.Tinaco@xxxxxxxxxx> wrote: > > > The AD8460 is a “bits in, power out” high voltage, high-power, > > high-speed driver optimized for large output current (up to ±1 A) and > > high slew rate (up to ±1800 V/μs) at high voltage (up to ±40 V) into > > capacitive loads. > > > > A digital engine implements user-configurable features: modes for > > digital input, programmable supply current, and fault monitoring and > > programmable protection settings for output current, output voltage, > > and junction temperature. The AD8460 operates on high voltage dual > > supplies up to ±55 V and a single low voltage supply of 5 V. > > > > Signed-off-by: Mariel Tinaco <Mariel.Tinaco@xxxxxxxxxx> > A few comments inline. > > Jonathan > > > > obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o diff --git > > a/drivers/iio/dac/ad8460.c b/drivers/iio/dac/ad8460.c new file mode > > 100644 index 000000000000..6428bfaf0bd7 > > --- /dev/null > > +++ b/drivers/iio/dac/ad8460.c > > @@ -0,0 +1,932 @@ > > +// SPDX-License-Identifier: GPL-2.0 > > +/* > > + * AD8460 Waveform generator DAC Driver > > + * > > + * Copyright (C) 2024 Analog Devices, Inc. > > + */ > > + > > +#include <linux/bitfield.h> > > +#include <linux/cleanup.h> > > +#include <linux/clk.h> > > +#include <linux/debugfs.h> > > +#include <linux/delay.h> > > +#include <linux/dmaengine.h> > > +#include <linux/gpio/consumer.h> > > +#include <linux/iio/buffer.h> > > +#include <linux/iio/buffer-dma.h> > > +#include <linux/iio/buffer-dmaengine.h> #include > > +<linux/iio/consumer.h> #include <linux/iio/events.h> #include > > +<linux/iio/iio.h> > > Given there are lots of IIO specific includes, probably a case where pulling > them out as a separate block after the main includes makes sense. > > > +#include <linux/kernel.h> > > +#include <linux/module.h> > > +#include <linux/mod_devicetable.h> > > +#include <linux/regmap.h> > > +#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> > > > ... > > > > + > > + state = iio_priv(indio_dev); > > + mutex_init(&state->lock); > > Trivial but there is no a devm_mutex_init() that deals with the obscure debug > case where mutex uninit makes sense. Might as well use it here. > > > + > > + indio_dev->name = "ad8460"; > > + indio_dev->info = &ad8460_info; > > + > > + state->spi = spi; > > + dev = &spi->dev; > > + > > + state->regmap = devm_regmap_init_spi(spi, > &ad8460_regmap_config); > > + if (IS_ERR(state->regmap)) > > + return dev_err_probe(dev, PTR_ERR(state->regmap), > > + "Failed to initialize regmap"); > > + > > + state->sync_clk = devm_clk_get_enabled(dev, NULL); > > + if (IS_ERR(state->sync_clk)) > > + return dev_err_probe(dev, PTR_ERR(state->sync_clk), > > + "Failed to get sync clk\n"); > > + > > + state->tmp_adc_channel = devm_iio_channel_get(dev, "ad8460- > tmp"); > > + if (IS_ERR_OR_NULL(state->tmp_adc_channel)) { > > + indio_dev->channels = ad8460_channels; > > + indio_dev->num_channels = ARRAY_SIZE(ad8460_channels); > > + } else { > > + indio_dev->channels = ad8460_channels_with_tmp_adc; > > + indio_dev->num_channels = > ARRAY_SIZE(ad8460_channels_with_tmp_adc); > > + } > > + > Add and enable the various other supplies. They are probably always on in > which case the regulator framework will work it's magic to avoid use having to > care that they aren't in the dts. If the other supplies are added, do they need to be tied up as well to the Private structure just like ref_1p2v? Or do I just apply the devm_regulator_get_enable_read_voltage to it? ret = devm_regulator_get_enable_read_voltage(&client->dev, "vdd"); If (ret < 0 && ret != -ENODEV) return dev_err_probe(ltc2309->dev, ret, "failed to get vref voltage\n"); > > > + ret = devm_regulator_get_enable_read_voltage(dev, "refio_1p2v"); > > + if (ret == -ENODEV) > > + state->refio_1p2v_mv = 1200; > > + else if (ret >= 0) > > + state->refio_1p2v_mv = ret / 1000; > > + else > > + return dev_err_probe(dev, ret, "Failed to get reference > > +voltage\n"); > > + > ... > > > + ret = device_property_read_u32_array(dev, "adi,range-microamp", > > + tmp, ARRAY_SIZE(tmp)); > > + if (!ret) { > > + if (in_range(tmp[1], 0, > AD8460_ABS_MAX_OVERCURRENT_UA)) > > + regmap_write(state->regmap, > AD8460_CTRL_REG(0x08), > > + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) > | > > + AD8460_CURRENT_LIMIT_CONV(tmp[1])); > > Your binding has a fixed set of values. Yet this is anything in range. > Which is correct? Based on datasheet I think it is flexible but that you have > listed the example values instead of the limits. > > > > + > > + if (in_range(tmp[0], -AD8460_ABS_MAX_OVERCURRENT_UA, > 0)) > > + regmap_write(state->regmap, > AD8460_CTRL_REG(0x09), > > + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) > | > > + > AD8460_CURRENT_LIMIT_CONV(abs(tmp[0]))); > > + } > > + > > + ret = device_property_read_u32_array(dev, "adi,range-microvolt", > > + tmp, ARRAY_SIZE(tmp)); > > + if (!ret) { > > + if (in_range(tmp[1], 0, > AD8460_ABS_MAX_OVERVOLTAGE_UV)) > > + regmap_write(state->regmap, > AD8460_CTRL_REG(0x0A), > > + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) > | > > + AD8460_VOLTAGE_LIMIT_CONV(tmp[1])); > > + > > + if (in_range(tmp[0], -AD8460_ABS_MAX_OVERVOLTAGE_UV, > 0)) > > + regmap_write(state->regmap, > AD8460_CTRL_REG(0x0B), > > + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) > | > > + > AD8460_VOLTAGE_LIMIT_CONV(abs(tmp[0]))); > > + } > > + > > + ret = device_property_read_u32(dev, "adi,max-millicelsius", &temp); > > + if (!ret) { > > + if (in_range(temp, AD8460_MIN_OVERTEMPERATURE_MC, > > + AD8460_MAX_OVERTEMPERATURE_MC)) > > + regmap_write(state->regmap, > AD8460_CTRL_REG(0x0C), > > + FIELD_PREP(AD8460_FAULT_ARM_MSK, 1) > | > > + AD8460_TEMP_LIMIT_CONV(abs(temp))); > > + } > > + > > + ret = ad8460_reset(state); > > + if (ret) > > + return ret; > > + > > + /* Enables DAC by default */ > > + ret = regmap_update_bits(state->regmap, AD8460_CTRL_REG(0x01), > > + AD8460_HVDAC_SLEEP_MSK, > > + FIELD_PREP(AD8460_HVDAC_SLEEP_MSK, > 0)); > > + if (ret) > > + return ret; > > + > > + indio_dev->modes = INDIO_DIRECT_MODE; > > + indio_dev->setup_ops = &ad8460_buffer_setup_ops; > > + > > + ret = devm_iio_dmaengine_buffer_setup_ext(dev, indio_dev, "tx", > > + > IIO_BUFFER_DIRECTION_OUT); > > + if (ret) > > + return dev_err_probe(dev, ret, > > + "Failed to get DMA buffer\n"); > > + > > + return devm_iio_device_register(dev, indio_dev); }