Re: [PATCH resend] iio: dac: add support for max5522

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

 



Hi Jonathan,

thanks a lot for all the feedbacks,

On Sun, Oct 2, 2022 at 1:56 PM Jonathan Cameron <jic23@xxxxxxxxxx> wrote:
>
> On Sun,  2 Oct 2022 00:12:25 +0200
> Angelo Dureghello <angelo.dureghello@xxxxxxxxxxx> wrote:
>
> > Add initial support for dac max5522.
>
> DAC preferred for comments.
>
ok, fixed

> >
> > Tested writing DAC A and B with some values,
> > from 0 to 1023, measured output voltages, driver works properly.
> >
> > Signed-off-by: Angelo Dureghello <angelo.dureghello@xxxxxxxxxxx>
>
> Hi Angelo
>
> A quick review inline.
>
> Thanks,
>
> Jonathan
>
> > ---
> >  drivers/iio/dac/Kconfig   |  13 ++
> >  drivers/iio/dac/Makefile  |   1 +
> >  drivers/iio/dac/max5522.c | 246 ++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 260 insertions(+)
> >  create mode 100644 drivers/iio/dac/max5522.c
> >
> > diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> > index 80521bd28d0f..262688b5e39f 100644
> > --- a/drivers/iio/dac/Kconfig
> > +++ b/drivers/iio/dac/Kconfig
> > @@ -357,6 +357,19 @@ config MAX517
> >         This driver can also be built as a module.  If so, the module
> >         will be called max517.
> >
> > +config MAX5522
> > +     tristate "Maxim MAX5522 DAC driver"
> > +     depends on SPI
> Hmm. We only have one instance of the pattern and that's more complex because
> it's a driver that supports both SPI and I2C. Simpler to have (unless I'm missing
> something!)
>
>         depends on SPI_MASTER
>         select REGMAP_SPI
>
Not sure i am understanding this point, device is SPI only.
Anyway, ok, i changed as you are suggesting.

>
> > +     select REGMAP_SPI if SPI_MASTER
> > +     help
> > +       Say Y here if you want to build a driver for the Maxinm MAX5522.
>
> Maxim
ack, fixed

>
> > +
> > +       MAX5522 is a dual, ultra-low-power, 10-Bit, voltage-output
> > +       digital to analog converter (DAC) offering rail-to-rail buffered
> > +       voltage outputs.
> > +
> > +       If compiled as a module, it will be called max5522.
> > +
> >  config MAX5821
> >       tristate "Maxim MAX5821 DAC driver"
> >       depends on I2C
> > diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
> > index ec3e42713f00..6c74fea21736 100644
> > --- a/drivers/iio/dac/Makefile
> > +++ b/drivers/iio/dac/Makefile
> > @@ -38,6 +38,7 @@ obj-$(CONFIG_LTC2632) += ltc2632.o
> >  obj-$(CONFIG_LTC2688) += ltc2688.o
> >  obj-$(CONFIG_M62332) += m62332.o
> >  obj-$(CONFIG_MAX517) += max517.o
> > +obj-$(CONFIG_MAX5522) += max5522.o
> >  obj-$(CONFIG_MAX5821) += max5821.o
> >  obj-$(CONFIG_MCP4725) += mcp4725.o
> >  obj-$(CONFIG_MCP4922) += mcp4922.o
> > diff --git a/drivers/iio/dac/max5522.c b/drivers/iio/dac/max5522.c
> > new file mode 100644
> > index 000000000000..aa4098a1d68c
> > --- /dev/null
> > +++ b/drivers/iio/dac/max5522.c
> > @@ -0,0 +1,246 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * Maxim MAX5522
> > + * Dual, Ultra-Low-Power 10-Bit, Voltage-Output DACs
> > + *
> > + * Copyright 2022 Timesys Corp.
> > + */
> > +
> > +#include <linux/device.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/spi/spi.h>
> > +#include <linux/slab.h>
> > +#include <linux/sysfs.h>
> > +#include <linux/regmap.h>
> > +#include <linux/regulator/consumer.h>
> Alphabetical order for this block is slightly wrong.
ack, fixed

>
> > +
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
>
> You aren't using any custom attributes so shouldn't need iio/sysfs.h
ack, fixed

> > +
> > +#define MAX5522_MAX_ADDR     15
> > +#define MAX5522_CTRL_NONE    0
> > +#define MAX5522_CTRL_LOAD_IN_A       9
> > +#define MAX5522_CTRL_LOAD_IN_B       10
> > +
> > +#define MAX5522_REG_DATA(x)  (x + MAX5522_CTRL_LOAD_IN_A)
>
> ((x) + MAX5522_CTRL_LOAD_IN_A)
> to protect against cases where the macro parameter is non trivial.
> It's easier to do this here than make a reader go check!
>
ack, fixed

> > +
> > +struct max5522_chip_info {
> > +     const struct iio_chan_spec *channels;
> > +     unsigned int num_channels;
> > +};
> > +
> > +struct max5522_state {
> > +     struct regmap *regmap;
> > +     const struct max5522_chip_info *chip_info;
> > +     unsigned short dac_cache[2];
> > +     unsigned int vrefin_mv;
>
> In theory voltages can change and sensible userspace software will only read them
> in a slow path anyway, so I'd just move the voltage readback into the
> read_raw() callback and drop this cache of the value.
>
Sorry, not clear. This device does not provide read operations.
There is only write operation and DIN spi pin.

> > +     struct regulator *vrefin_reg;
> > +};
> > +
> > +#define MAX5522_CHANNEL(chan) {      \
> > +     .type = IIO_VOLTAGE, \
> > +     .indexed = 1, \
> > +     .output = 1, \
> > +     .channel = chan, \
> > +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> > +                           BIT(IIO_CHAN_INFO_SCALE), \
> > +     .scan_type = { \
> As this driver doesn't yet support buffered mode, most of this info
> isn't used.  So just specify the bits you actually use (shift I think).
> > +             .sign = 'u', \
> > +             .realbits = 10, \
> > +             .storagebits = 16, \
> > +             .shift = 2, \
> > +     } \
> > +}
> > +
> > +const struct iio_chan_spec max5522_channels[] = {
> > +     MAX5522_CHANNEL(0),
> > +     MAX5522_CHANNEL(1),
> > +};
> > +
> > +enum max5522_type {
> > +     ID_MAX5522,
> > +};
> > +
> > +static const struct max5522_chip_info max5522_chip_info_tbl[] = {
>
> Unless you are going to follow this patch very soon with support for more devices,
> I'd prefer seeing this indirection only when it becomes necessary.
> For now, it just leads to less readable and longer code.
>
idea is to follow up with MAX5523/5524/5525,
not sure when right now, since i cannot test them, but code was ready
for addition

> > +     [ID_MAX5522] = {
> > +             .channels = max5522_channels,
> > +             .num_channels = 2,
> > +     },
> > +};
> > +
> > +static inline int max5522_info_to_reg(struct iio_chan_spec const *chan)
> > +{
> > +     return MAX5522_REG_DATA(chan->channel);
> > +}
> > +
> > +static int max5522_read_raw(struct iio_dev *indio_dev,
> > +     struct iio_chan_spec const *chan, int *val, int *val2, long info)
> > +{
> > +     struct max5522_state *state = iio_priv(indio_dev);
> > +
> > +     switch (info) {
> > +     case IIO_CHAN_INFO_RAW:
> > +             *val = state->dac_cache[chan->channel];
> > +             return IIO_VAL_INT;
> > +     case IIO_CHAN_INFO_SCALE:
> > +             *val = state->vrefin_mv;
> > +             *val2 = 10;
> > +             return IIO_VAL_FRACTIONAL_LOG2;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return -EINVAL;
> > +}
> > +
> > +static int max5522_write_raw(struct iio_dev *indio_dev,
> > +     struct iio_chan_spec const *chan, int val, int val2, long info)
> > +{
> > +     struct max5522_state *state = iio_priv(indio_dev);
> > +     int rval;
> > +
> > +     if (val > 1023 || val < 0)
> > +             return -EINVAL;
> > +
> > +     rval = regmap_write(state->regmap, max5522_info_to_reg(chan),
> > +                             val << chan->scan_type.shift);
> > +     if (rval < 0)
> > +             return rval;
> > +
> > +     state->dac_cache[chan->channel] = val;
> > +
> > +     return 0;
> > +}
> > +
> > +static const struct iio_info max5522_info = {
> > +     .read_raw = max5522_read_raw,
> > +     .write_raw = max5522_write_raw,
> > +};
> > +
> > +static void max5522_remove(struct device *dev)
> > +{
> > +     struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +
> > +     iio_device_unregister(indio_dev);
>
> Look at this and consider if there are appropriate devm_ forms
> for the two things you are doing in here.  If you use
> devm_iio_device_register() below and
> devm_kzalloc() or similar for the channels then the managed cleanup
> will allow you to drop remove entirely.
>
> > +     kfree(indio_dev->channels);
>
> You seem to be freeing static const data...
>
ops. Ok, used devm_iio_device_register and dropped remove entirely.

> > +}
> > +
> > +static const struct regmap_config max5522_regmap_config = {
> > +     .reg_bits = 4,
> > +     .val_bits = 12,
> > +     .max_register = MAX5522_MAX_ADDR,
> > +};
> > +
> > +static int max5522_spi_probe(struct spi_device *spi)
> > +{
> > +     const struct spi_device_id *id = spi_get_device_id(spi);
> > +     struct iio_dev *indio_dev;
> > +     struct max5522_state *state;
> > +     int ret;
> > +
> > +     indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
> > +     if (indio_dev == NULL) {
> > +             dev_err(&spi->dev, "failed to allocate iio device\n");
> > +             return  -ENOMEM;
> > +     }
> > +
> > +     state = iio_priv(indio_dev);
> > +     state->chip_info = &max5522_chip_info_tbl[id->driver_data];
>
> Whilst driver only supports one device hard code this - or just hard
> code the values it contains.
>
As above, idea was to add 5523/5524/5525, but i cannot test them rtight now.

> > +
> > +     state->vrefin_reg = devm_regulator_get(&spi->dev, "vrefin");
> > +     if (IS_ERR(state->vrefin_reg))
> > +             return dev_err_probe(&spi->dev, PTR_ERR(state->vrefin_reg),
> > +                                  "Vrefin regulator not specified\n");
>
> Turn the regulator on.  Not that you'll then want to add
> a devm_add_action_or_reset() call to call a callback to turn it fof again
> in the remove path.  We can't us the devm_regulator_get_enabled() path
> here because we need the pointer to get the voltage.
>
Thanks, added regulator_enable()

> > +
> > +     ret = regulator_get_voltage(state->vrefin_reg);
> > +     if (ret < 0) {
> > +             dev_err(&spi->dev, "Failed to read vrefin regulator: %d\n",
> > +                             ret);
> > +             goto error_disable_vrefin_reg;
> > +     }
> > +     state->vrefin_mv = ret / 1000;
> > +
> > +     spi_set_drvdata(spi, indio_dev);
>
> Once you've moved everything over to devm, you won't need this.
>
Thanks, removed

> > +
> > +     state->regmap = devm_regmap_init_spi(spi, &max5522_regmap_config);
> No blank line between a call and it's error handler.  Keeps the blocks
> slightly easier to read.
> > +
> > +     if (IS_ERR(state->regmap))
> > +             return PTR_ERR(state->regmap);
> > +
> > +     dev_info(&spi->dev, "iio dac ready");
>
> Don't add this noise to the boot log.  If it worked, you'll have lots
> of easy ways to find out!
>
Ok, removed, was just for initial debug

> > +
> > +     indio_dev->info = &max5522_info;
> > +     indio_dev->modes = INDIO_DIRECT_MODE;
> > +     indio_dev->channels = max5522_channels;
> > +     indio_dev->num_channels = ARRAY_SIZE(max5522_channels);
> > +     indio_dev->name = id->name;
>
> Hard code the name preferred as it makes it easier to be sure it's exactly what
> we expect when reading the code and does rely on the fallback compatible matching
> in the spi core for dt described devices.
>
Ok, if possible i would keep the id table for next additions.

> > +
> > +     return iio_device_register(indio_dev);
> > +
> > +error_disable_vrefin_reg:
> > +     regulator_disable(state->vrefin_reg);
>
> Disabing a regulator you haven't enabled.  Use devm_add_action_or_reset()
> to register managed cleanup of this.
>
ops, fixed

> > +
> > +     return ret;
> > +}
> > +
> > +static void max5522_spi_remove(struct spi_device *spi)
> > +{
> > +     max5522_remove(&spi->dev);
> > +}
> > +
> > +static const struct spi_device_id max5522_ids[] = {
> > +     { "max5522", ID_MAX5522 },
>
> As below, don't introduce the data part until you need it.
>
Same as above, let me know if keeping it for next additions is ok.

> > +     {}
> > +};
> > +MODULE_DEVICE_TABLE(spi, ad5360_ids);
> > +
> > +
> > +static const struct of_device_id max5522_of_match[] = {
> > +     { .compatible = "maxim,max5522", },
>
> Be consistent on whether you are going to put commas after
> the end of structure intialisers.  For this I don't have
> a particular preference, just make them all the same!
>
ok

> Definitely prefer to have data set here and then
> use device_get_match_data() first then if that fails, fall back to
> using the spi_device_id path. However, only introduce that complexity
> when the driver supports multiple parts.  For now there is no point
> in introducing the data at all.
>
> > +     {},
>
> No comma after null terminators.
>
ok, done

> > +};
> > +
> > +MODULE_DEVICE_TABLE(of, max5522_of_match);
> > +
> > +static struct spi_driver max5522_spi_driver = {
> > +     .driver = {
> > +                .name = "max5522",
> > +                .of_match_table = max5522_of_match,
> > +     },
> > +     .probe = max5522_spi_probe,
> > +     .remove = max5522_spi_remove,
> > +     .id_table = max5522_ids,
> > +};
> > +
> > +static inline int max5522_spi_register_driver(void)
> > +{
> > +     return spi_register_driver(&max5522_spi_driver);
> > +}
> > +
> > +static inline void max5522_spi_unregister_driver(void)
> > +{
> > +     spi_unregister_driver(&max5522_spi_driver);
> > +}
> > +
> > +static int __init max5522_spi_init(void)
> > +{
> > +     int ret;
> > +
> > +     ret = max5522_spi_register_driver();
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
> > +}
> > +module_init(max5522_spi_init);
> > +
> > +static void __exit max5522_spi_exit(void)
> > +{
> > +     max5522_spi_unregister_driver();
> > +}
> > +module_exit(max5522_spi_exit);
>
> Why not use
> module_spi_driver() to get rid of all this boilerplate?
>
ok, much more simplier, removed all and used module_spi_driver()
> > +
> > +MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@xxxxxxxxxxx");
> > +MODULE_DESCRIPTION("MAX5522 DAC driver");
> > +MODULE_LICENSE("GPL");
>

Waiting for further comments before v2.

Thanks a lot,
--
Angelo Dureghello
Timesys
e. angelo.dureghello@xxxxxxxxxxx



[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