Re: [PATCH v1 1/2] iio: adc: driver for texas instruments ads7142

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

 



On Sat, May 15, 2021 at 10:15:16AM +0300, Alexandru Ardelean wrote:
> On Fri, May 14, 2021 at 5:10 PM József Horváth <info@xxxxxxxxxxx> wrote:
> >
> > Hi Alexandru,
> >
> > On Fri, May 14, 2021 at 11:50:00AM +0300, Alexandru Ardelean wrote:
> > > On Fri, May 14, 2021 at 1:52 AM Jozsef Horvath <info@xxxxxxxxxxx> wrote:
> > > >
> > > > This is an iio driver for
> > > >  Texas Instruments ADS7142 dual-channel, programmable sensor monitor.
> > > >
> > >
> > > Hey,
> > >
> > > Looks good overall.
> > > Some comments inline.
> > >
> > > > All of the data buffer modes, supported by the device are selectable
> > > >  over sysfs:
> > > >   /sys/bus/iio/devices/iio:deviceX/buffer_mode
> > > >
> > > > Data buffer operation modes:
> > > >   - start_burst:
> > > >     In triggered buffer preenable hook: not relevant.
> > > >     In trigger handler: the driver selects the autonomous monitoring
> > > >       (see chapter 7.4.3 in datasheet) operation mode,
> > > >           configures the channels in sequencer as specified by
> > > >       sysfs(scan_elements/in_voltageY_en),
> > > >           sets the data buffer mode to "Start Burst Mode",
> > > >       then starts the conversion.
> > > >     In irq handler: the driver pushes the conversion results received
> > > >       from the device to the buffer,
> > > >       then restarts the conversion like in trigger handler.
> > > >     Both IRQ and trigger are required to support this mode.
> > > >     See chapter 7.4.3.2.1 "Autonomous Mode with Start Burst"
> > > >     in datasheet.
> > > >   - stop_burst:
> > > >     In triggered buffer preenable hook: the driver selects the
> > > >       autonomous monitoring (see chapter 7.4.3 in datasheet)
> > > >       operation mode,
> > > >       configures the channels in sequencer as
> > > >       specified by sysfs(scan_elements/in_voltageY_en),
> > > >           sets the data buffer mode to "Stop Burst Mode",
> > > >       then starts the conversion.
> > > >     In trigger handler: the driver pushes the conversion results received
> > > >       from the device to the buffer,
> > > >           then restarts the conversion like in preenable hook.
> > > >     In irq handler: not relevant.
> > > >     Trigger is required to support this mode.
> > > >     See chapter 7.4.3.2.2 "Autonomous Mode with Stop Burst"
> > > >     in datasheet.
> > > >   - pre_alert:
> > > >     In triggered buffer preenable hook: the driver selects the autonomous
> > > >       monitoring (see chapter 7.4.3 in datasheet) operation mode,
> > > >       configures the channels in sequencer
> > > >         as specified by sysfs(scan_elements/in_voltageY_en),
> > > >       configures the digital window comparator and alert flags,
> > > >           sets the data buffer mode to "Pre Alert Data Mode",
> > > >       then starts the conversion.
> > > >     In trigger handler: not relevant.
> > > >     In irq handler: the driver pushes the conversion results received
> > > >       from the device to the buffer,
> > > >       then restarts the conversion like in preenable hook.
> > > >     IRQ is required to support this mode.
> > > >     See chapter 7.4.3.1.1 "Autonomous Mode with Pre Alert Data"
> > > >     in datasheet
> > > >   - post_alert:
> > > >     The operations are same like in pre_alert mode,
> > > >       except the data buffer mode selection, the selected mode is
> > > >       "Post Alert Data Mode".
> > > >     See chapter 7.4.3.1.2 "Autonomous Mode with Post Alert Data"
> > > >     in datasheet
> > > >
> > > > The in_voltageY_raw can be used, if the buffering mode is not enabled
> > > >  in sysfs(buffer/enable).
> > > >  The driver initiates a single conversion in the device for each
> > > >  read request(in_voltageY_raw).
> > > >  This is a one-shot conversion.
> > > >  See chapter 7.4.2.2 "Manual Mode with AUTO Sequence" in datasheet.
> > > >
> > > > Datasheet: https://www.ti.com/lit/ds/symlink/ads7142.pdf
> > > >
> > > > Signed-off-by: Jozsef Horvath <info@xxxxxxxxxxx>
> > > > ---
> > > >
> > > > changes v1
> > > >   - All of the buffer opertaion modes
> > > >     (pre_alert, post_alert, start_burst, stop_burst)
> > > >         are added
> > > >   - Added triggered buffer
> > > >   - Added buffer operation mode selection sysfs support
> > > >   - Redundant parameters (ti,threshold-rising, etc.)
> > > >     are removed
> > > >   - Supply name changed(vref -> avdd)
> > > >   - Added dvdd supply
> > > >   - Added device sampling rate calculation
> > > >   - Use device-managed functions for regulator, iio device register
> > > >     and triggered buffer
> > > > ---
> > > >  .../ABI/testing/sysfs-bus-iio-adc-ti-ads7142  |   11 +
> > > >  MAINTAINERS                                   |    7 +
> > > >  drivers/iio/adc/Kconfig                       |   13 +
> > > >  drivers/iio/adc/Makefile                      |    1 +
> > > >  drivers/iio/adc/ti-ads7142.c                  | 1469 +++++++++++++++++
> > > >  5 files changed, 1501 insertions(+)
> > > >  create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-ti-ads7142
> > > >  create mode 100644 drivers/iio/adc/ti-ads7142.c
> > > >
> > ...
> > > > +static int ti_ads7142_soft_reset(const struct i2c_client *client)
> > > > +{
> > > > +       u8 buf[2] = { TI_ADS7142_OC_GENERAL, 0x06 };
> > > > +       int ret;
> > > > +
> > > > +       ret = i2c_master_send(client, buf, sizeof(buf));
> > > > +       if (ret >= 0 && ret != sizeof(buf))
> > > > +               ret = -EIO;
> > > > +
> > > > +       return ret == sizeof(buf) ? 0 : ret;
> > > > +}
> > > > +
> > > > +static int ti_ads7142_address2channel(struct iio_dev *indio_dev,
> > > > +                                     int address,
> > > > +                                     struct ti_ads7142_channel **channel)
> > >
> > > This could be simplified a bit to return "const struct
> > > ti_ads7142_channel *" and then when NULL is returned, the caller
> > > assumes -ENODEV.
> > > Though, in  ti_ads7142_ist()  care should be taken to assign  ret =
> > > -ENODEV; but that's only one place.
> >
> > I don't like returning with pointer, but I can live with it. I'll do that.
> 
> Personally, I don't have a strong opinion on the matter.
> But kernel code tends to try to avoid double pointers [as parameters]
> where possible.
> 
> Even the IS_ERR() / PTR_ERR() constructs suggest that.

This is an important info, thank you.
I made the changes already.

My personal preference is to separate the reason of why the pointer is NULL,
 and the "returned" pointer value. This is why I don't like return with pointers.

> 
> >
> > >
> > >
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       int i;
> > > > +
> > > > +       for (i = 0; i < indio_dev->num_channels; i++) {
> > > > +               if (address == priv->channels[i].channel) {
> > > > +                       *channel = &priv->channels[i];
> > > > +                       return 0;
> > > > +               }
> > > > +       }
> > > > +       return -ENODEV;
> > > > +}
> > > > +
> > > > +static int ti_ads7142_sequence_start(struct iio_dev *indio_dev)
> > > > +{
> > > > +       struct i2c_client *client = to_i2c_client(indio_dev->dev.parent);
> > > > +
> > > > +       return ti_ads7142_reg_write(client, TI_ADS7142_START_SEQUENCE,
> > > > +                                   TI_ADS7142_START_SEQUENCE_SEQ_START);
> > > > +}
> > > > +
> > ...
> > > > +
> > > > +static int ti_ads7142_buffered_setup_and_start(struct iio_dev *indio_dev)
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       struct i2c_client *client = to_i2c_client(indio_dev->dev.parent);
> > > > +       struct ti_ads7142_channel *channel;
> > > > +       int i;
> > > > +       u8 alert_ch = 0;
> > > > +       u8 buffer_op_mode;
> > > > +       u8 seq_channels = 0;
> > > > +       int ret;
> > > > +
> > > > +       if (!priv->config.buffer_mode)
> > > > +               return 0;
> > > > +
> > > > +       priv->monitor_pending = false;
> > > > +
> > > > +       ret = ti_ads7142_sequence_abort(indio_dev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_osc_set(indio_dev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_input_cfg_set(indio_dev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_dout_format_set(indio_dev);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       switch (priv->config.buffer_mode) {
> > > > +       case TI_ADS7142_BUFFM_STOP_BURST:
> > > > +               buffer_op_mode = TI_ADS7142_DATA_BUFFER_OPMODE_STOP_BURST;
> > > > +       break;
> > > > +       case TI_ADS7142_BUFFM_START_BURST:
> > > > +               buffer_op_mode = TI_ADS7142_DATA_BUFFER_OPMODE_START_BURST;
> > > > +       break;
> > > > +       case TI_ADS7142_BUFFM_PRE_ALERT:
> > > > +               buffer_op_mode = TI_ADS7142_DATA_BUFFER_OPMODE_PRE_ALERT;
> > > > +       break;
> > >
> > > the indentation of the break; statements is a bit odd;
> > > IIRC, they usually go aligned with the case block;
> > > though i could be wrong;
> >
> > Ok, I'll fix it here and below.
> >
> > >
> > > > +       case TI_ADS7142_BUFFM_POST_ALERT:
> > > > +               buffer_op_mode = TI_ADS7142_DATA_BUFFER_OPMODE_POST_ALERT;
> > > > +       break;
> > > > +       default:
> > > > +               return -EINVAL;
> > > > +       break;
> > >
> > > also an unreachable statement
> >
> > Ok, I'll remove it here and below.
> >
> > >
> > > > +       }
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_DATA_BUFFER_OPMODE,
> > > > +                                  buffer_op_mode);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_OPMODE_SEL,
> > > > +                                  TI_ADS7142_OPMODE_SEL_MONITORING);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       for_each_set_bit(i, indio_dev->active_scan_mask,
> > > > +                        indio_dev->masklength) {
> > > > +               seq_channels |= 1 << i;
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_AUTO_SEQ_CHEN,
> > > > +                                  seq_channels);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       if (priv->config.buffer_mode < TI_ADS7142_BUFFM_PRE_ALERT)
> > > > +               goto seq_start;
> > > > +
> > > > +       /*
> > > > +        * Pre and post alert settings
> > > > +        */
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_PRE_ALT_EVT_CNT, 0);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_ALT_LOW_FLAGS,
> > > > +                                  TI_ADS7142_ALT_LOW_FLAGS_CH0
> > > > +                                  | TI_ADS7142_ALT_LOW_FLAGS_CH1);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_ALT_HIGH_FLAGS,
> > > > +                                  TI_ADS7142_ALT_HIGH_FLAGS_CH0
> > > > +                                  | TI_ADS7142_ALT_HIGH_FLAGS_CH1);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       for_each_set_bit(i, indio_dev->active_scan_mask,
> > > > +                        indio_dev->masklength) {
> > > > +               ret = ti_ads7142_address2channel(indio_dev, i,
> > > > +                                                &channel);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +
> > > > +               ret = ti_ads7142_hth_set(indio_dev, channel->channel,
> > > > +                                        channel->config.high_threshold);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +
> > > > +               ret = ti_ads7142_lth_set(indio_dev, channel->channel,
> > > > +                                        channel->config.low_threshold);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +
> > > > +               ret = ti_ads7142_hys_set(indio_dev, channel->channel,
> > > > +                                        channel->config.hysteresis);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +
> > > > +               if (channel->config.alert_low ||
> > > > +                   channel->config.alert_high) {
> > > > +                       alert_ch |= 1 << channel->channel;
> > > > +               }
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_ALERT_DWC_EN,
> > > > +                                  alert_ch ? TI_ADS7142_ALERT_DWC_EN_BLOCK_EN : 0);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       ret = ti_ads7142_reg_write(client, TI_ADS7142_ALERT_CHEN,
> > > > +                                  alert_ch);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       if (!alert_ch)
> > > > +               return ret;
> > >
> > > this looks the same as
> > >           if (!alert_ch)
> > >               return 0;
> > > for a while, this looked like 'ret' was not initialized;
> > >
> >
> > I've rethought this condition, and returning anything else than -EINVAL, is wrong.
> >
> > Story:
> >   1. this get called first from triggered_buffer_preenable hook
> >   2. the alert_ch == 0 if no thresh_{rising/falling} enabled on any channel
> >   3. returning 0 from here will cause that, the user waits for nothing
> >
> > > > +
> > > > +seq_start:
> > > > +       ret = ti_ads7142_sequence_start(indio_dev);
> > > > +       priv->monitor_pending = !ret;
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > ...
> > > > +
> > > > +static irqreturn_t ti_ads7142_ist(int irq, void *dev_id)
> > > > +{
> > > > +       struct iio_dev *indio_dev = dev_id;
> > > > +       struct i2c_client *client = to_i2c_client(indio_dev->dev.parent);
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       struct ti_ads7142_channel *channel;
> > > > +       u8 low_flags;
> > > > +       u8 high_flags;
> > > > +       u8 seq_st;
> > > > +       int i;
> > > > +       int ret;
> > > > +       int channel_collected;
> > > > +       s64 timestamp = iio_get_time_ns(indio_dev);
> > > > +
> > > > +       mutex_lock(&priv->lock);
> > > > +       if (!priv->config.buffer_mode || !priv->monitor_pending) {
> > > > +               mutex_unlock(&priv->lock);
> > > > +               return IRQ_NONE;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * BUSY/PDY fires when the sequence stopped in
> > > > +        * trigger handler(ti_ads7142_trigger_handler),
> > > > +        * if buffer mode is stop_burst, all the required
> > > > +        * operations are in trigger handler, so irq handler
> > > > +        * simple returns at this point.
> > > > +        */
> > > > +       if (priv->config.buffer_mode == TI_ADS7142_BUFFM_STOP_BURST) {
> > > > +               mutex_unlock(&priv->lock);
> > > > +               return IRQ_NONE;
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_reg_read(client, TI_ADS7142_SEQUENCE_STATUS, &seq_st);
> > > > +       if (ret) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: SEQUENCE_STATUS reg read error(%i)",
> > > > +                       __func__, ret);
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       if ((seq_st & TI_ADS7142_SEQUENCE_STATUS_SEQ_ERR_ST_MSK)
> > > > +           != TI_ADS7142_SEQUENCE_STATUS_SEQ_ENABLED) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: SEQUENCE_STATUS error(%i)",
> > > > +                       __func__, seq_st);
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_reg_read(client, TI_ADS7142_ALT_LOW_FLAGS,
> > > > +                                 &low_flags);
> > > > +       if (ret) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: ALT_LOW_FLAGS reg read error(%i)",
> > > > +                       __func__, ret);
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_reg_read(client, TI_ADS7142_ALT_HIGH_FLAGS,
> > > > +                                 &high_flags);
> > > > +       if (ret) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: ALT_HIGH_FLAGS reg read error(%i)",
> > > > +                       __func__, ret);
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       ret = ti_ads7142_sequence_abort(indio_dev);
> > > > +       if (ret)
> > > > +               goto final;
> > > > +
> > > > +       priv->monitor_pending = false;
> > > > +
> > > > +       channel_collected = 0;
> > > > +       ret = ti_ads7142_buffered_collect(indio_dev, &channel_collected);
> > > > +       if (ret && ret != -ENOENT) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: error(%d) when collecting result for %s mode",
> > > > +                       __func__, ret,
> > > > +                       ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       if (ret == -ENOENT)
> > > > +               ret = 0;
> > > > +
> > > > +       if (!channel_collected)
> > > > +               goto final;
> > > > +
> > > > +       if (priv->config.buffer_mode < TI_ADS7142_BUFFM_PRE_ALERT)
> > > > +               goto final;
> > > > +
> > > > +       for_each_set_bit(i, indio_dev->active_scan_mask,
> > > > +                        indio_dev->masklength) {
> > > > +               ret = ti_ads7142_address2channel(indio_dev, i,
> > > > +                                                &channel);
> > > > +               if (ret)
> > > > +                       goto final;
> > > > +
> > > > +               if (!(channel_collected & (1 << channel->channel)))
> > > > +                       continue;
> > > > +               if (channel->config.alert_low &&
> > > > +                   (low_flags & (1 << channel->channel))) {
> > > > +                       iio_push_event(indio_dev,
> > > > +                                      IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> > > > +                                                           i,
> > > > +                                                           IIO_EV_TYPE_THRESH,
> > > > +                                                           IIO_EV_DIR_FALLING),
> > > > +                                      timestamp);
> > > > +               }
> > > > +
> > > > +               if (channel->config.alert_high &&
> > > > +                   (high_flags & (1 << channel->channel))) {
> > > > +                       iio_push_event(indio_dev,
> > > > +                                      IIO_UNMOD_EVENT_CODE(IIO_VOLTAGE,
> > > > +                                                           i,
> > > > +                                                           IIO_EV_TYPE_THRESH,
> > > > +                                                           IIO_EV_DIR_RISING),
> > > > +                                      timestamp);
> > > > +               }
> > > > +       }
> > > > +
> > > > +final:
> > >
> > > This final label looks a bit busy.
> > > Maybe an idea would be to have 2 labels, and depdending on 'ret' you
> > > do "goto {err,out}_unlock":
> > >
> > > out_unlock:
> > >       if (priv->config.buffer_mode >= TI_ADS7142_BUFFM_PRE_ALERT) {
> > >               ret = ti_ads7142_buffered_setup_and_start(indio_dev);
> > >                if (ret) {
> > >                       dev_err(indio_dev->dev.parent,
> > >                               "%s: error(%d) when starting %s mode",
> > >                               __func__, ret,
> > >
> > > ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > >                       goto err_unlock;
> > >               }
> > >       }
> > >       mutex_unlock(&priv->lock);
> > >       return IRQ_HANDLED;
> > >
> > > err_unlock:
> > >        mutex_unlock(&priv->lock);
> > >        return IRQ_NONE;
> > >
> > >
> >
> > You are right, I'll do that.
> >
> > > > +       if (!ret && priv->config.buffer_mode >= TI_ADS7142_BUFFM_PRE_ALERT) {
> > > > +               ret = ti_ads7142_buffered_setup_and_start(indio_dev);
> > > > +               if (ret) {
> > > > +                       dev_err(indio_dev->dev.parent,
> > > > +                               "%s: error(%d) when starting %s mode",
> > > > +                               __func__, ret,
> > > > +                               ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > > > +               }
> > > > +       }
> > > > +       mutex_unlock(&priv->lock);
> > > > +       if (ret)
> > > > +               return IRQ_NONE;
> > > > +
> > > > +       return IRQ_HANDLED;
> > > > +}
> > > > +
> > > > +static int ti_ads7142_read_raw(struct iio_dev *indio_dev,
> > > > +                              struct iio_chan_spec const *chan,
> > > > +                              int *val, int *val2, long info)
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       int ret;
> > > > +
> > > > +       switch (info) {
> > > > +       case IIO_CHAN_INFO_RAW:
> > > > +               if (iio_buffer_enabled(indio_dev))
> > > > +                       return -EBUSY;
> > > > +               ret = iio_device_claim_direct_mode(indio_dev);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +               ret = ti_ads7142_manual_read(indio_dev, chan->address,
> > > > +                                            val);
> > > > +               if (!ret)
> > > > +                       ret = IIO_VAL_INT;
> > > > +               iio_device_release_direct_mode(indio_dev);
> > > > +               return ret;
> > > > +       case IIO_CHAN_INFO_SAMP_FREQ:
> > > > +               ti_ads7142_osc_calc_get(indio_dev, val);
> > > > +               return IIO_VAL_INT;
> > > > +       case IIO_CHAN_INFO_SCALE:
> > > > +               if (IS_ERR(priv->avdd)) {
> > > > +                       ret = -EINVAL;
> > > > +               } else {
> > > > +                       *val = regulator_get_voltage(priv->avdd) / 1000;
> > > > +                       *val2 = chan->scan_type.realbits;
> > > > +                       ret = IIO_VAL_FRACTIONAL_LOG2;
> > > > +               }
> > >
> > > purely stylistic change; but this could also be:
> > >
> > >               if (IS_ERR(priv->avdd))
> > >                   return -EINVAL
> > >
> > >               *val = regulator_get_voltage(priv->avdd) / 1000;
> > >               *val2 = chan->scan_type.realbits;
> > >               return IIO_VAL_FRACTIONAL_LOG2;
> > >
> >
> > Looks better, thank you.
> >
> > > > +               return ret;
> > > > +       default:
> > > > +               return -EINVAL;
> > > > +       }
> > > > +       return 0;
> > >
> > > This "return 0;" looks like an unreachable statement.
> > > Some GCC versions complain about it. Not sure which.
> > >
> > > > +}
> > > > +
> > > > +static int ti_ads7142_write_raw(struct iio_dev *indio_dev,
> > > > +                               struct iio_chan_spec const *chan,
> > > > +                               int val, int val2, long mask)
> > > > +{
> > > > +       switch (mask) {
> > > > +       case IIO_CHAN_INFO_SAMP_FREQ:
> > > > +               return ti_ads7142_osc_calc_set(indio_dev, val);
> > > > +       default:
> > > > +               return -EINVAL;
> > > > +       }
> > > > +
> > > > +       return 0;
> > >
> > > Also, an unreachale statement.
> > >
> >
> > I'll remove these statements.
> >
> > > > +}
> > > > +
> > > > +static int ti_ads7142_read_event_value(struct iio_dev *indio_dev,
> > > > +                                      const struct iio_chan_spec *chan,
> > > > +                                      enum iio_event_type type,
> > > > +                                      enum iio_event_direction dir,
> > > > +                                      enum iio_event_info info,
> > > > +                                      int *val, int *val2)
> > > > +{
> > > > +       struct ti_ads7142_channel *channel;
> > > > +       int ret;
> > > > +
> > > > +       ret = ti_ads7142_address2channel(indio_dev, chan->address,
> > > > +                                        &channel);
> > > > +       if (ret)
> > > > +               return ret;
> > > > +
> > > > +       switch (info) {
> > > > +       case IIO_EV_INFO_VALUE:
> > > > +               if (dir == IIO_EV_DIR_RISING)
> > > > +                       *val = channel->config.high_threshold;
> > > > +               else
> > > > +                       *val = channel->config.low_threshold;
> > > > +               return IIO_VAL_INT;
> > > > +       case IIO_EV_INFO_HYSTERESIS:
> > > > +               *val = channel->config.hysteresis;
> > > > +               return IIO_VAL_INT;
> > > > +       default:
> > > > +               return -EINVAL;
> > > > +       }
> > > > +       return 0;
> > >
> > > Also an unreachable statement.
> > >
> > > > +}
> > > > +
> > ...
> > > > +
> > > > +static int ti_ads7142_triggered_buffer_preenable(struct iio_dev *indio_dev)
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       int ret;
> > > > +
> > > > +       if (!priv->config.buffer_mode)
> > > > +               return -EINVAL;
> > > > +
> > > > +       /*
> > > > +        * Start/stop burst buffer modes requires trigger
> > > > +        */
> > > > +       if (priv->config.buffer_mode <= TI_ADS7142_BUFFM_START_BURST &&
> > > > +           !indio_dev->trig) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: Start/stop burst buffer modes requires trigger",
> > > > +                       __func__);
> > > > +               return -EINVAL;
> > > > +       }
> > > > +
> > > > +       /*
> > > > +        * Start burst and pre/post alert modes requires irq
> > > > +        */
> > > > +       if (priv->config.buffer_mode >= TI_ADS7142_BUFFM_START_BURST &&
> > > > +           !priv->irq_present) {
> > > > +               dev_err(indio_dev->dev.parent,
> > > > +                       "%s: Start burst and pre/post alert modes requires irq",
> > > > +                       __func__);
> > > > +               return -EINVAL;
> > > > +       }
> > > > +
> > > > +       priv->scan_data = devm_krealloc(indio_dev->dev.parent,
> > > > +                                       priv->scan_data,
> > > > +                                       indio_dev->scan_bytes, GFP_KERNEL);
> > >
> > > the scan_data doesn't look like it would be awefully big;
> > > so, it could be static buffer on 'priv';
> > > it should be marked with ____cacheline_aligned  though  [if moved on
> > > the priv object].
> > > though, i don't feel too strongly about moving it;
> > >
> >
> > Ok, I'll do that.
> >
> > >
> > > > +       if (!priv->scan_data)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       mutex_lock(&priv->lock);
> > > > +       /*
> > > > +        * Start burst mode started in trigger handler.
> > > > +        * Sequencer aborted here, just for safe.
> > > > +        */
> > > > +       if (priv->config.buffer_mode == TI_ADS7142_BUFFM_START_BURST)
> > > > +               ret = ti_ads7142_buffered_abort(indio_dev);
> > > > +       else
> > > > +               ret = ti_ads7142_buffered_setup_and_start(indio_dev);
> > > > +       mutex_unlock(&priv->lock);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > ...
> > > > +
> > > > +static irqreturn_t ti_ads7142_trigger_handler(int irq, void *p)
> > > > +{
> > > > +       struct iio_poll_func *pf = p;
> > > > +       struct iio_dev *indio_dev = pf->indio_dev;
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       int ret = 0;
> > > > +
> > > > +       mutex_lock(&priv->lock);
> > > > +       if (priv->config.buffer_mode == TI_ADS7142_BUFFM_STOP_BURST) {
> > > > +               ret = ti_ads7142_buffered_abort(indio_dev);
> > > > +               if (ret) {
> > > > +                       dev_err(indio_dev->dev.parent,
> > > > +                               "%s: error(%d) when aborting in %s mode",
> > > > +                               __func__, ret,
> > > > +                               ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > > > +               }
> > > > +
> > > > +               ret = ti_ads7142_buffered_collect(indio_dev, NULL);
> > > > +               if (ret && ret != -ENOENT) {
> > > > +                       dev_err(indio_dev->dev.parent,
> > > > +                               "%s: error(%d) when collecting result for %s mode",
> > > > +                               __func__, ret,
> > > > +                               ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > > > +               }
> > > > +
> > > > +               if (ret == -ENOENT)
> > > > +                       ret = 0;
> > > > +       }
> > > > +       if (!ret &&
> > > > +           (priv->config.buffer_mode == TI_ADS7142_BUFFM_START_BURST ||
> > > > +            priv->config.buffer_mode == TI_ADS7142_BUFFM_STOP_BURST)) {
> > > > +               ret = ti_ads7142_buffered_setup_and_start(indio_dev);
> > > > +               if (ret) {
> > > > +                       dev_err(indio_dev->dev.parent,
> > > > +                               "%s: error(%d) when starting %s mode",
> > > > +                               __func__, ret,
> > > > +                               ti_ads7142_buffer_modes[priv->config.buffer_mode]);
> > > > +               }
> > > > +       }
> > > > +
> > > > +       mutex_unlock(&priv->lock);
> > > > +
> > > > +       if (ret)
> > > > +               return IRQ_NONE;
> > >
> > > maybe doing 2 labels here (with goto err_unlock/out_unlock) would be
> > > an idea here as well;
> > >
> >
> > Ok, I'll do that.
> >
> > > > +
> > > > +       iio_trigger_notify_done(indio_dev->trig);
> > > > +
> > > > +       return IRQ_HANDLED;
> > > > +}
> > > > +
> > ...
> > > > +
> > > > +static const struct iio_chan_spec_ext_info ti_ads7142_ext_info[] = {
> > > > +       IIO_ENUM("buffer_mode", IIO_SHARED_BY_ALL,
> > > > +                &ti_ads7142_buffer_modes_enum),
> > > > +       {
> > > > +               .name = "buffer_mode_available",
> > > > +               .shared = IIO_SHARED_BY_ALL,
> > > > +               .read = iio_enum_available_read,
> > > > +               .private = (uintptr_t)&ti_ads7142_buffer_modes_enum,
> > > > +       },
> > > > +       { },
> > >
> > > the comma can be removed here;
> > > since it's a null terminator;
> > >
> >
> > Ok, I'll remove it.
> >
> > > > +};
> > > > +
> > > > +static int ti_ads7142_parse_channel_config(struct device *dev,
> > > > +                                          struct iio_dev *indio_dev)
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       struct fwnode_handle *channel_node;
> > > > +       struct iio_chan_spec *iio_channels;
> > > > +       struct iio_chan_spec *iio_channel;
> > > > +       struct ti_ads7142_channel *ads_channel;
> > > > +       int channel_index = 0;
> > > > +       int channel_count;
> > > > +       int ret;
> > > > +
> > > > +       channel_count = device_get_child_node_count(dev);
> > > > +       if (!channel_count) {
> > > > +               dev_err(dev, "dt: there is no channel definition");
> > > > +               return -ENODEV;
> > > > +       }
> > > > +
> > > > +       if (channel_count > TI_ADS7142_CHANNEL_COUNT) {
> > > > +               dev_err(dev, "dt: invalid number of channel definitions");
> > > > +               return -ENODEV;
> > > > +       }
> > > > +
> > > > +       priv->channels = devm_kcalloc(dev, channel_count,
> > > > +                                     sizeof(*priv->channels),
> > > > +                                     GFP_KERNEL);
> > > > +       if (!priv->channels)
> > > > +               return -ENOMEM;
> > >
> > > It looks to me that this device has up-to 2 channels.
> > > For such a low channel count, it may make sense to just keep a static
> > > table of iio_channels pointers [1].
> > >
> > > > +
> > > > +       indio_dev->num_channels = channel_count;
> > > > +       iio_channels = devm_kcalloc(dev, channel_count, sizeof(*iio_channels),
> > > > +                                   GFP_KERNEL);
> > > > +       if (!iio_channels)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       indio_dev->channels = iio_channels;
> > > > +
> > > > +       device_for_each_child_node(dev, channel_node) {
> > > > +               ads_channel = &priv->channels[channel_index];
> > > > +
> > > > +               ret = fwnode_property_read_u32(channel_node, "reg",
> > > > +                                              &ads_channel->channel);
> > > > +               if (ret) {
> > > > +                       fwnode_handle_put(channel_node);
> > > > +                       return ret;
> > > > +               }
> > > > +
> > > > +               iio_channel = &iio_channels[channel_index];
> > > > +               iio_channel->datasheet_name = ti_ads7142_ain_names[ads_channel->channel];
> > > > +               iio_channel->type = IIO_VOLTAGE;
> > > > +               iio_channel->indexed = 1;
> > > > +               iio_channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> > > > +               if (!IS_ERR_OR_NULL(priv->avdd))
> > > > +                       iio_channel->info_mask_separate |= BIT(IIO_CHAN_INFO_SCALE);
> > > > +               iio_channel->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ);
> > > > +               iio_channel->scan_type.sign = 'u';
> > > > +               iio_channel->scan_type.realbits = 12;
> > > > +               iio_channel->scan_type.storagebits = 16;
> > > > +               iio_channel->scan_type.endianness = IIO_CPU;
> > > > +               iio_channel->address = ads_channel->channel;
> > > > +               iio_channel->scan_index = ads_channel->channel;
> > > > +               iio_channel->channel = ads_channel->channel;
> > > > +               iio_channel->event_spec = ti_ads7142_events;
> > > > +               iio_channel->num_event_specs = ARRAY_SIZE(ti_ads7142_events);
> > > > +               iio_channel->ext_info = ti_ads7142_ext_info;
> > > > +
> > > > +               ads_channel->config.high_threshold = TI_ADS7142_THRESHOLD_MSK;
> > > > +               channel_index++;
> > >
> > > Also, here I think it may make sense to keep a global const-static
> > > table of iio_channel object-parameters.
> > > Then it can be assigned to the iio_channels [1] pointer-list and then
> > > to indio_dev->channels/num_channels.
> >
> > This device have more features, like high precision mode(16 bits of data) and pseudo-differential mode.
> >  I would implement these features in the next weeks/months.
> >  So I would keep this dynamic initialization, and after the features above has implemented,
> >  I'll change it to static, if it will optimal.
> 
> So, you can always choose to allocate iio_channels later.
> 
> Or you can do it now, and a global-static "iio_channel[s]" object as template.
> See this example:
>   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git/tree/drivers/iio/adc/ad7124.c#n794
> It's essentially a memcpy() done via C compiler, where a lot of the
> base information from the channel is stored in a global-const object
> and copied over.
> Then if something else needs tuning, it is applied from the DT,.

I felt this kind of initialization less readable, but ok.

> 
> >
> > >
> > > > +       }
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int ti_ads7142_parse_config(struct device *dev,
> > > > +                                  struct iio_dev *indio_dev)
> > > > +{
> > > > +       return ti_ads7142_parse_channel_config(dev, indio_dev);
> > > > +}
> > > > +
> > > > +static void ti_ads7142_regulators_disable(void *data)
> > > > +{
> > > > +       struct ti_ads7142_priv *priv = data;
> > > > +
> > > > +       if (!IS_ERR_OR_NULL(priv->avdd))
> > > > +               regulator_disable(priv->avdd);
> > > > +       if (!IS_ERR_OR_NULL(priv->dvdd))
> > > > +               regulator_disable(priv->dvdd);
> > > > +}
> > >
> > > So, I've also learned this the hard way, but the general rule of these
> > > callbacks is one per resource.
> > > Something like:
> > >
> > > static void ti_ads7142_regulators_disable(void *reg)
> > > {
> > >         regulator_disable(reg);
> > > }
> > >
> >
> > Thank you for sharing your experience, I will do this
> >
> > > Then some calls are needed for each regulator.
> > > Also, IS_ERR() can be used instead of IS_ERR_OR_NULL()
> > > {devm_}regulator_get() always returns non-NULL.
> >
> > The devm_regulator_get() can return NULL, if the CONFIG_REGULATOR is not selected.
> 
> Oh right.
> My bad here. [1]
> 
> >   I didnt saw any system without CONFIG_REGULATOR, but who knows...
> >   See: include/linux/regulator/consumer.h line:325
> 
> I guess the thing is that the usual construct is:
>          priv->avdd = devm_regulator_get(&client->dev, "avdd");
>          if (IS_ERR(priv->avdd))
>              return PTR(priv->avdd);
> Maybe there was a broader discussion about regulators being NULL, but
> from the code in other drivers, this doesn't seem to be the case.

You are right, I missed the error handling here.
Returned NULL value can be ok (no CONFIG_REGULATOR), but IS_ERR is exacly an error.

> 
> >
> > >
> > >
> > >         priv->avdd = devm_regulator_get(&client->dev, "avdd");
> > >         if (!IS_ERR(priv->avdd)) {
> > >                 ret = regulator_enable(priv->avdd);
> > >                 if (ret)
> > >                         return ret;
> > >
> > >                 ret = devm_add_action_or_reset(&client->dev,
> > >                                                ti_ads7142_regulators_disable,
> > >                                                priv->avdd);
> > >                 if (ret)
> > >                         return ret;
> > >         }
> > >
> > >         priv->dvdd = devm_regulator_get(&client->dev, "dvdd");
> > >         if (!IS_ERR(priv->dvdd)) {
> > >                 ret = regulator_enable(priv->dvdd);
> > >                 if (ret)
> > >                         return ret;
> > >
> > >                 ret = devm_add_action_or_reset(&client->dev,
> > >                                                ti_ads7142_regulators_disable,
> > >                                                priv->dvdd);
> > >                 if (ret)
> > >                         return ret;
> > >         }
> > >
> > >
> > > > +
> > > > +static int ti_ads7142_probe(struct i2c_client *client,
> > > > +                           const struct i2c_device_id *id)
> > > > +{
> > > > +       struct iio_dev *indio_dev;
> > > > +       struct ti_ads7142_priv *priv;
> > > > +       int ret;
> > > > +
> > > > +       indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
> > > > +       if (!indio_dev)
> > > > +               return -ENOMEM;
> > > > +
> > > > +       i2c_set_clientdata(client, indio_dev);
> > > > +       priv = iio_priv(indio_dev);
> > > > +
> > > > +       /**
> > > > +        * starting from v5.9-rc1 iio_device_alloc
> > > > +        *  sets indio_dev->dev.parent, but older versions not :(
> > > > +        **/
> > > > +       if (!indio_dev->dev.parent)
> > > > +               indio_dev->dev.parent = &client->dev;
> > > > +       indio_dev->name = TI_ADS7142_NAME;
> > > > +       indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> > > > +       indio_dev->info = &ti_ads7142_iio_info;
> > > > +
> > > > +       priv->avdd = devm_regulator_get(&client->dev, "avdd");
> > > > +       if (!IS_ERR_OR_NULL(priv->avdd)) {
> > > > +               ret = regulator_enable(priv->avdd);
> > > > +               if (ret)
> > > > +                       return ret;
> > > > +       }
> > > > +
> > > > +       priv->dvdd = devm_regulator_get(&client->dev, "dvdd");
> > > > +       if (!IS_ERR_OR_NULL(priv->dvdd)) {
> > > > +               ret = regulator_enable(priv->dvdd);
> > > > +               if (ret)
> > > > +                       goto final;
> > > > +       }
> > > > +
> > > > +       ret = devm_add_action_or_reset(&client->dev,
> > > > +                                      ti_ads7142_regulators_disable,
> > > > +                                      priv);
> > > > +       if (ret)
> > > > +               goto final;
> > > > +
> > > > +       ret = ti_ads7142_soft_reset(client);
> > > > +       if (ret)
> > > > +               goto final;
> > > > +
> > > > +       ret = ti_ads7142_parse_config(&client->dev, indio_dev);
> > > > +       if (ret)
> > > > +               goto final;
> > > > +
> > > > +       mutex_init(&priv->lock);
> > > > +
> > > > +       if (client->irq) {
> > > > +               ret = devm_request_threaded_irq(&client->dev, client->irq,
> > > > +                                               NULL, ti_ads7142_ist,
> > > > +                                               IRQF_ONESHOT | IRQF_SHARED,
> > > > +                                               dev_name(&client->dev),
> > > > +                                               indio_dev);
> > > > +               if (ret) {
> > > > +                       dev_err(&client->dev, "Unable to request IRQ %i",
> > > > +                               client->irq);
> > > > +                       goto final;
> > > > +               }
> > > > +               priv->irq_present = true;
> > > > +       }
> > > > +
> > > > +       ret = devm_iio_triggered_buffer_setup(&client->dev,
> > > > +                                             indio_dev,
> > > > +                                             &iio_pollfunc_store_time,
> > > > +                                             &ti_ads7142_trigger_handler,
> > > > +                                             &ti_ads7142_triggered_buffer_ops);
> > > > +       if (ret)
> > > > +               goto final;
> > > > +
> > > > +       ret = devm_iio_device_register(&client->dev, indio_dev);
> > >
> > > Usually "return devm_iio_device_register(&client->dev, indio_dev);" is
> > > sufficient.
> > > The rest is syslog spam.
> >
> > Ok, I'll do that.
> >
> > >
> > > > +       if (ret) {
> > > > +               dev_err(&client->dev, "Failed to register iio device");
> > > > +               goto final;
> > > > +       }
> > > > +
> > > > +       dev_info(&client->dev, "%s is a %s device at address 0x%X",
> > > > +                dev_name(&indio_dev->dev), indio_dev->name,
> > > > +                client->addr);
> > > > +final:
> > >
> > > This 'final' label looks odd.
> > > It looks like all 'goto final' calls in this function could be
> > > replaced with 'return ret'
> > > And the return below could be 'return 0;'
> >
> > Yes, you are right, this is not required, I'll remove it.
> >
> > >
> > >
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static int __maybe_unused ti_ads7142_suspend(struct device *dev)
> > > > +{
> > > > +       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +
> > > > +       mutex_lock(&priv->lock);
> > > > +       do {
> > > > +               /*
> > > > +                * Keep all regulators on when the device in autonomous
> > > > +                *  monitoring mode.
> > > > +                * The device can wake up the system with ALERT pin
> > > > +                */
> > > > +               if (priv->monitor_pending &&
> > > > +                   (priv->config.buffer_mode == TI_ADS7142_BUFFM_PRE_ALERT ||
> > > > +                    priv->config.buffer_mode == TI_ADS7142_BUFFM_POST_ALERT))
> > > > +                       continue;
> > > > +
> > > > +               if (!IS_ERR_OR_NULL(priv->avdd))
> > > > +                       regulator_disable(priv->avdd);
> > > > +               if (!IS_ERR_OR_NULL(priv->dvdd))
> > > > +                       regulator_disable(priv->dvdd);
> > > > +       } while (0);
> > > > +       mutex_unlock(&priv->lock);
> > > > +
> > > > +       return 0;
> > > > +}
> > > > +
> > > > +static int __maybe_unused ti_ads7142_resume(struct device *dev)
> > > > +{
> > > > +       struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> > > > +       struct ti_ads7142_priv *priv = iio_priv(indio_dev);
> > > > +       int ret = 0;
> > > > +
> > > > +       mutex_lock(&priv->lock);
> > > > +       do {
> > > > +               /*
> > > > +                * Nothing to do when the device in autonomous monitoring mode.
> > > > +                */
> > > > +               if (priv->monitor_pending &&
> > > > +                   (priv->config.buffer_mode == TI_ADS7142_BUFFM_PRE_ALERT ||
> > > > +                    priv->config.buffer_mode == TI_ADS7142_BUFFM_POST_ALERT))
> > > > +                       continue;
> > > > +
> > > > +               if (!IS_ERR_OR_NULL(priv->avdd)) {
> > > > +                       ret = regulator_enable(priv->avdd);
> > > > +                       if (ret)
> > > > +                               continue;
> > > > +               }
> > > > +               if (!IS_ERR_OR_NULL(priv->dvdd)) {
> > > > +                       ret = regulator_enable(priv->dvdd);
> > > > +                       if (ret)
> > > > +                               continue;
> > > > +               }
> > > > +       } while (0);
> > >
> > > This while(0) loop looks a bit funky.
> > > I understand why it's here; but it still looks a bit weird.
> > >
> >
> > It is a short block and readable.
> >
> > > > +       mutex_unlock(&priv->lock);
> > > > +
> > > > +       return ret;
> > > > +}
> > > > +
> > > > +static SIMPLE_DEV_PM_OPS(ti_ads7142_pm_ops, ti_ads7142_suspend,
> > > > +                        ti_ads7142_resume);
> > > > +
> > > > +static const struct i2c_device_id ti_ads7142_id[] = {
> > > > +       { TI_ADS7142_NAME, 0 },
> > > > +       { }
> > > > +};
> > > > +MODULE_DEVICE_TABLE(i2c, ti_ads7142_id);
> > > > +
> > > > +static const struct of_device_id ti_ads7142_of_match[] = {
> > > > +       { .compatible = "ti,ads7142" },
> > > > +       {}
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, ti_ads7142_of_match);
> > > > +
> > > > +static struct i2c_driver ti_ads7142_driver = {
> > > > +       .driver = {
> > > > +               .name = TI_ADS7142_NAME,
> > > > +               .of_match_table = ti_ads7142_of_match,
> > > > +               .pm = &ti_ads7142_pm_ops,
> > > > +       },
> > > > +       .probe          = ti_ads7142_probe,
> > > > +       .id_table       = ti_ads7142_id,
> > > > +};
> > > > +
> > > > +module_i2c_driver(ti_ads7142_driver);
> > > > +
> > > > +MODULE_LICENSE("GPL");
> > > > +MODULE_AUTHOR("Jozsef Horvath <info@xxxxxxxxxxx>");
> > > > +MODULE_DESCRIPTION("Texas Instruments ADS7142 ADC driver");
> > > > --
> > > > 2.17.1
> > > >
> > > >
> >
> > Thank you for review and suggestions.
> >
> > Best regards
> > József
> >
> 

Thank you.

Best regards
József




[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