Re: [RFC PATCH 1/1] iio: dac: add support for ltc2688

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Nov 11, 2021 at 1:01 PM Nuno Sá <nuno.sa@xxxxxxxxxx> wrote:
>
> The LTC2688 is a 16 channel, 16 bit, +-15V DAC with an integrated
> precission reference. It is guaranteed monotonic and has built in

precision

> rail-to-rail output buffers that can source or sink up to 20 mA.

It's in good shape, several comments below, though.

...

> +enum {
> +       LTC2688_SPAN_RANGE_0V_5V,
> +       LTC2688_SPAN_RANGE_M10V_10V,
> +       LTC2688_SPAN_RANGE_M15V_15V,
> +       /*
> +        * These 2 were deliberately re-arranged to be the last items as they
> +        * have the same fs and we will just return 1 in read_available.

WTH "fs" mean?

> +        */
> +       LTC2688_SPAN_RANGE_0V_10V,
> +       LTC2688_SPAN_RANGE_M5V_5V,
> +       LTC2688_SPAN_RANGE_MAX
> +};

...

> +static const int ltc2688_off_tbl[LTC2688_SPAN_RANGE_MAX] = {
> +       [LTC2688_SPAN_RANGE_0V_5V] = 0,
> +       [LTC2688_SPAN_RANGE_M10V_10V] = -32768,
> +       [LTC2688_SPAN_RANGE_M15V_15V] = -32768,

> +       [LTC2688_SPAN_RANGE_0V_10V] = 0,
> +       [LTC2688_SPAN_RANGE_M5V_5V] = -32768

+ Comma

Isn't it more natural to move them up by two lines?

> +};
> +
> +static const int ltc2688_reg_val[LTC2688_SPAN_RANGE_MAX] = {
> +       [LTC2688_SPAN_RANGE_0V_5V] = 0,
> +       [LTC2688_SPAN_RANGE_M10V_10V] = 3,
> +       [LTC2688_SPAN_RANGE_M15V_15V] = 4,
> +       [LTC2688_SPAN_RANGE_0V_10V] = 1,
> +       [LTC2688_SPAN_RANGE_M5V_5V] = 2

As per above.

> +};

...

> +static int ltc2688_span_set(const struct ltc2688_state *st, int c, int span)
> +{
> +       const struct ltc2688_chan *chan = &st->channels[c];
> +       u32 i = ARRAY_SIZE(chan->span_tbl);
> +       int off = ltc2688_offset_avail[chan->offset_idx];

> +       while (i--) {

Can ARRAY_SIZE() give 0?
If not, do {} while (--i); looks more natural.

> +               if (span == chan->span_tbl[i][1]) {
> +                       /*
> +                        * The selected scale needs to fit  the current offset.
> +                        * Note that for [0V 10V] and [-5V 5V], as the scale is
> +                        * the same, we will always succeed here. Hence if
> +                        * someone really wants [0 10V] and does not set the
> +                        * offset to 0, then :man-shrugging:
> +                        */
> +                       if (off == ltc2688_off_tbl[i]) {
> +                               break;

> +                       } else if (i < LTC2688_SPAN_RANGE_0V_10V) {

Redundant 'else'.

> +                               /*
> +                                * At this point, only one offset is valid so we
> +                                * already can assume error.
> +                                */
> +                               dev_err(&st->spi->dev, "Offset(%d) not valid for current scale(0.%09d)\n",
> +                                       off, span);
> +                               return -EPERM;
> +                       }

> +                       continue;

Redundant, see below.

> +               }

> +               if (!i) {
> +                       dev_err(&st->spi->dev, "span(%d) not available\n", span);
> +                       return -EINVAL;
> +               }

This is invariant to the loop.

> +       }
> +
> +       return regmap_update_bits(st->regmap, LTC2688_CMD_CH_SETTING(c),
> +                                 LTC2688_CH_SPAN_MSK,
> +                                 FIELD_PREP(LTC2688_CH_SPAN_MSK, ltc2688_reg_val[i]));
> +};

...

> +       if (off != ltc2688_offset_avail[0] && off != ltc2688_offset_avail[1])
> +               return -EINVAL;

Extra condition, See below.

> +       if (off == ltc2688_offset_avail[0])
> +               chan->offset_idx = 0;
> +       else

else if

> +               chan->offset_idx = 1;

else
  return -EINVAL;

...

> +       ret = regmap_read(st->regmap, reg, &regval);
> +       if (ret < 0)

Is it the first time you tested explicitly for a negative result?

> +               return ret;

...

> +       return sprintf(buf, "%u\n", !!(regval & BIT(chan->channel)));

sysfs_emit() ?

> +}

...

> +       struct ltc2688_state *st = iio_priv(indio_dev);
> +       bool en;
> +       int ret;
> +       u32 val = 0, reg;

Reversed xmas tree order ?

> +       ret = strtobool(buf, &en);

kstrtobool()

...

> +       if (ret)
> +               return ret;
> +               break;

What does this mean?

...

> +       "4",
> +       "8",
> +       "16",
> +       "32",
> +       "64",

One line?

...

> +       "0",
> +       "90",
> +       "180",
> +       "270",

Ditto.

...

> +enum {
> +       LTC2688_CHAN_MODE_TOGGLE,
> +       LTC2688_CHAN_MODE_DITHER

+ Comma.

> +};

...

> +static const char * const ltc2688_clk_names[LTC2688_CHAN_TD_MAX] = {
> +       "TGP1", "TGP2", "TGP3"

In this notation, + comma.

> +};

...

> +       for_each_set_bit(bit, &mask, ARRAY_SIZE(ltc2688_clk_names)) {
> +               struct clk *clk;

+ blank line

> +               /*
> +                * If a TGP pin is set, then we need to provide a valid clock at
> +                * the pin.
> +                */

> +       }

...

> +       fwnode_for_each_available_child_node(fwnode, child) {
> +               struct ltc2688_chan *chan;
> +
> +               ret = fwnode_property_read_u32(child, "reg", &reg);
> +               if (ret) {
> +                       fwnode_handle_put(child);

> +                       return dev_err_probe(&st->spi->dev, ret,
> +                                            "Failed to get reg property\n");

One line.

> +               } else if (reg >= LTC2688_DAC_CHANNELS) {

Redundant 'else'.

> +                       fwnode_handle_put(child);
> +                       return dev_err_probe(&st->spi->dev, -EINVAL,

> +                                            "reg >= %d\n", LTC2688_DAC_CHANNELS);

The message is cryptic. Decrypt it for the user.

> +               }
> +
> +               chan = &st->channels[reg];
> +               ret = fwnode_property_read_u32(child, "adi,mode", &mode);
> +               if (!ret) {
> +                       if (mode > LTC2688_CHAN_MODE_DITHER) {
> +                               fwnode_handle_put(child);
> +                               return dev_err_probe(&st->spi->dev, -EINVAL,
> +                                                    "chan mode inv value(%d)\n",
> +                                                    mode);
> +                       }
> +
> +                       chan->dither_toggle = true;
> +                       if (mode == LTC2688_CHAN_MODE_TOGGLE) {
> +                               ltc2688_channels[reg].ext_info = ltc2688_toggle_ext_info;
> +                       } else {
> +                               ltc2688_channels[reg].ext_info = ltc2688_dither_ext_info;
> +                               /* enable dither mode */
> +                               mask = LTC2688_CH_MODE_MSK;
> +                               val = BIT(11);
> +                       }
> +               }
> +
> +               ret = fwnode_property_read_u32(child, "adi,toggle-dither-input",
> +                                              &clk_input);

> +               if (!ret) {

if (ret) {
  if ...
} else {
 ...
}

> +                       if (clk_input > LTC2688_CHAN_TD_TGP3) {
> +                               fwnode_handle_put(child);
> +                               return dev_err_probe(&st->spi->dev, -EINVAL,
> +                                                    "toggle-dither-input inv value(%d)\n",
> +                                                    clk_input);
> +                       }
> +
> +                       clk_msk |= BIT(clk_input);
> +                       mask |= LTC2688_CH_TD_SEL_MSK;
> +                       /*
> +                        * 0 means software toggle which we are not supporting
> +                        * for now. Hence the +1

In all multi-line comments miseed (grammatical) period.

> +                        */
> +                       val |= FIELD_PREP(LTC2688_CH_TD_SEL_MSK, clk_input + 1);
> +               } else if (chan->dither_toggle) {
> +                       /*
> +                        * As sw_toggle is not supported, we need to make sure
> +                        * a valid input is selected if toggle/dither mode is
> +                        * requested
> +                        */
> +                       return dev_err_probe(&st->spi->dev, -EINVAL,
> +                                            "toggle-dither set but no toggle-dither-input\n");
> +               }

> +               chan->overrange = fwnode_property_read_bool(child,
> +                                                           "adi,overrange");

One line?

> +               if (chan->overrange) {
> +                       val |= LTC2688_CH_OVERRANGE_MSK;
> +                       mask |= BIT(3);
> +               }
> +
> +               if (!mask)
> +                       continue;
> +
> +               ret = regmap_update_bits(st->regmap,
> +                                        LTC2688_CMD_CH_SETTING(reg), mask,
> +                                        val);
> +               if (ret) {
> +                       fwnode_handle_put(child);
> +                       return dev_err_probe(&st->spi->dev, -EINVAL,
> +                                            "failed to set chan settings\n");
> +               }
> +
> +               mask = 0;
> +               val = 0;
> +       }

...

> +       int fs = ltc2688_span_helper[idx][1] - ltc2688_span_helper[idx][0];
> +
> +       if (chan->overrange)
> +               fs = mult_frac(fs, 105, 100);
> +
> +       return fs;

if (...)
 return ...
?

...

> +       u32 c, i;

In one case you use int or unsigned int if I'm not mistaken, here out
of a sudden u32. Why? Please, be consistent over the code.

...

> +                       /* we will return IIO_VAL_INT_PLUS_NANO */
> +                       s = DIV_ROUND_CLOSEST_ULL(st->vref * fs * 1000000000ULL,
> +                                                 4096 * 1 << 16);

Can you make use of one of the SI prefixes here? (See unit.h)

...

> +       gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH);
> +       if (IS_ERR(gpio))
> +               return dev_err_probe(&st->spi->dev, PTR_ERR(gpio),
> +                                    "Failed to get reset gpio");

> +

Redundant blank line.

> +       if (gpio) {
> +               usleep_range(1000, 1200);
> +               /* bring device out of reset */
> +               gpiod_set_value_cansleep(gpio, 0);

> +       } else {

I'm wondering why 'else'? Can't it be both? Why not?

> +               ret = regmap_update_bits(st->regmap, LTC2688_CMD_CONFIG_REG,
> +                                        LTC2688_CONFIG_RST,
> +                                        LTC2688_CONFIG_RST);
> +               if (ret < 0)
> +                       return ret;
> +       }
> +
> +       usleep_range(10000, 12000);

+ blank line, so the reader won't be confused about the function of this sleep.

> +       ret = ltc2688_channel_config(st);
> +       if (ret < 0)
> +               return ret;
> +
> +       if (vref) {
> +               ret = regmap_update_bits(st->regmap, LTC2688_CMD_CONFIG_REG,
> +                                        LTC2688_CONFIG_EXT_REF, BIT(1));
> +               if (ret < 0)

These ' < 0' parts are inconsistent over the code.

> +                       return ret;
> +       }
> +
> +       ltc2688_span_avail_compute(st);
> +       return 0;
> +}

...

> +       /* ignoring the no op command */
> +       .max_register = U16_MAX - 1

Really?! Have you ever considered what burden you bring with this to
one who accidentally or on purpose tries to dump registers via
debugfs?

...

> +       st->regmap = devm_regmap_init(&spi->dev, NULL, st, &ltc2688_regmap_config);

I'm wondering why it's not a regmap SPI?

> +       if (IS_ERR(st->regmap))
> +               return dev_err_probe(&spi->dev, PTR_ERR(st->regmap),
> +                                    "Failed to init regmap");

...

> +       ret = ltc2688_regulator_supply_get(st, "vcc");
> +       if (ret)
> +               return ret;
> +
> +       ret = ltc2688_regulator_supply_get(st, "iovcc");
> +       if (ret)
> +               return ret;

Can bulk regulators be used?

...

> +               st->vref = ret / 1000;

Make use of unit.h

--
With Best Regards,
Andy Shevchenko




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux