RE: [PATCH 2/2] iio: temperature: Add threshold interrupt support for max31722

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

 



> -----Original Message-----
> From: Daniel Baluta [mailto:daniel.baluta@xxxxxxxxx]
> Sent: Monday, March 14, 2016 2:26 PM
> To: Jonathan Cameron <jic23@xxxxxxxxxx>
> Cc: Breana, Tiberiu A <tiberiu.a.breana@xxxxxxxxx>; linux-
> iio@xxxxxxxxxxxxxxx
> Subject: Re: [PATCH 2/2] iio: temperature: Add threshold interrupt support
> for max31722
> 
> On Sat, Mar 12, 2016 at 12:17 PM, Jonathan Cameron <jic23@xxxxxxxxxx>
> wrote:
> > On 09/03/16 13:30, Tiberiu Breana wrote:
> >> Added interrupt support for high/low temperature threshold events to
> >> the max31722 driver.
> >>
> >> Signed-off-by: Tiberiu Breana <tiberiu.a.breana@xxxxxxxxx>
> > Only comment in here is that you might be better off reporting the
> > event direction as IIO_EVENT_DIR_EITHER and leaving the figuring out
> > which case occured to userspace.
> >
> > This case is iritatingly common!
> >
> > Jonathan
> >> ---
> >>  drivers/iio/temperature/max31722.c | 303
> >> ++++++++++++++++++++++++++++++++++++-
> >>  1 file changed, 301 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/iio/temperature/max31722.c
> >> b/drivers/iio/temperature/max31722.c
> >> index fa833b6..a8bfe35 100644
> >> --- a/drivers/iio/temperature/max31722.c
> >> +++ b/drivers/iio/temperature/max31722.c
> >> @@ -10,21 +10,32 @@
> >>
> >>  #include <linux/kernel.h>
> >>  #include <linux/acpi.h>
> >> +#include <linux/gpio/consumer.h>
> >> +#include <linux/interrupt.h>
> >>  #include <linux/module.h>
> >>  #include <linux/regmap.h>
> >>  #include <linux/spi/spi.h>
> >> +#include <linux/iio/events.h>
> >>  #include <linux/iio/iio.h>
> >>  #include <linux/iio/sysfs.h>
> >>
> >>  #define MAX31722_REG_CFG                             0x00
> >>  #define MAX31722_REG_TEMP_LSB                        0x01
> >>  #define MAX31722_REG_TEMP_MSB                        0x02
> >> +#define MAX31722_REG_THIGH_LSB         0x03
> >> +#define MAX31722_REG_TLOW_LSB          0x05
> >>  #define MAX31722_MAX_REG                             0x86
> >>
> >>  #define MAX31722_MODE_CONTINUOUS             0x00
> >>  #define MAX31722_MODE_STANDBY                        0x01
> >> +#define MAX31722_MODE_THERMOSTAT       0x80
> >> +
> >> +#define MAX31722_MIN_TEMP          -55
> >> +#define MAX31722_MAX_TEMP          125
> >>
> >>  #define MAX31722_REGMAP_NAME                 "max31722_regmap"
> >> +#define MAX31722_EVENT             "max31722_event"
> >> +#define MAX31722_GPIO              "max31722_gpio"
> >>
> >>  #define MAX31722_SCALE_AVAILABLE             "0.5 0.25 0.125 0.0625"
> >>
> >> @@ -43,6 +54,8 @@ static const struct reg_field
> max31722_reg_field_state =
> >>                               REG_FIELD(MAX31722_REG_CFG, 0, 0);
> >> static const struct reg_field max31722_reg_field_resolution =
> >>                               REG_FIELD(MAX31722_REG_CFG, 1, 2);
> >> +static const struct reg_field max31722_reg_field_thermostat =
> >> +                             REG_FIELD(MAX31722_REG_CFG, 3, 3);
> >>
> >>  struct max31722_data {
> >>       struct spi_device *spi_device;
> >> @@ -50,6 +63,8 @@ struct max31722_data {
> >>       struct regmap *regmap;
> >>       struct regmap_field *reg_state;
> >>       struct regmap_field *reg_resolution;
> >> +     struct regmap_field *reg_thermostat;
> >> +     u64 timestamp;
> >>  };
> >>
> >>  /*
> >> @@ -71,11 +86,30 @@ static const struct attribute_group
> max31722_attribute_group = {
> >>       .attrs = max31722_attributes
> >>  };
> >>
> >> +static const struct iio_event_spec max31722_events[] = {
> >> +     /* High temperature event */
> >> +     {
> >> +             .type = IIO_EV_TYPE_THRESH,
> >> +             .dir = IIO_EV_DIR_RISING,
> >> +             .mask_separate = BIT(IIO_EV_INFO_VALUE) |
> >> +                              BIT(IIO_EV_INFO_ENABLE),
> >> +     },
> >> +     /* Low temperature event */
> >> +     {
> >> +             .type = IIO_EV_TYPE_THRESH,
> >> +             .dir = IIO_EV_DIR_FALLING,
> >> +             .mask_separate = BIT(IIO_EV_INFO_VALUE) |
> >> +                              BIT(IIO_EV_INFO_ENABLE),
> >> +     },
> >> +};
> >> +
> >>  static const struct iio_chan_spec max31722_channels[] = {
> >>       {
> >>               .type = IIO_TEMP,
> >>               .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> >>                                     BIT(IIO_CHAN_INFO_SCALE),
> >> +             .event_spec = max31722_events,
> >> +             .num_event_specs = ARRAY_SIZE(max31722_events),
> >>       },
> >>  };
> >>
> >> @@ -167,6 +201,172 @@ static void max31722_reg_to_float(u16 reg_val,
> int *val, int *val2)
> >>       }
> >>  }
> >>
> >> +/* Convert a floating point value to a register value. */ static u16
> >> +max31722_float_to_reg(int val, int val2) {
> >> +     int i;
> >> +     bool negative_nr;
> >> +     u8 lsb;
> >> +     u16 reg_val;
> >> +
> >> +     negative_nr = (val & 0x80) || val2 < 0;
> >> +     if (val2 < 0)
> >> +             val2 *= -1;
> >> +
> >> +     /*
> >> +      * The LSB value will be an approximation of val2
> >> +      * due to its limited 4-bit range.
> >> +      */
> >> +     lsb = 0;
> >> +     for (i = 0 ; i < ARRAY_SIZE(max31722_scale_table) && val2 > 0; i++)
> >> +             if (val2 - max31722_scale_table[i] >= 0) {
> >> +                     val2 -= max31722_scale_table[i];
> >> +                     lsb += 1 << (4 - i - 1);
> >> +             }
> >> +     lsb <<= 4;
> >> +
> >> +     if (negative_nr) {
> >> +             /*
> >> +              * Negative value. Temporarily use the complement of val for
> >> +              * the MSB, then concatenate the LSB, reverse bits & add 1 for
> >> +              * the final value.
> >> +              */
> >> +             u8 msb = (u8)(-1 * val);
> >> +             u16 rev = 0;
> >> +             int num_bits = sizeof(rev) * 8;
> >> +
> >> +             reg_val = (msb << 8) | lsb;
> >> +
> >> +             for (i = 0 ; i < num_bits ; i++) {
> >> +                     rev |= !(reg_val & 0x01) << i;
> >> +                     reg_val >>= 1;
> >> +             }
> >> +             rev += 1;
> >> +             return rev;
> >> +
> >> +     } else {
> >> +             reg_val = ((u8)val << 8) | lsb;
> >> +     }
> >> +
> >> +     return reg_val;
> >> +}
> >> +
> >> +static int max31722_read_event_config(struct iio_dev *indio_dev,
> >> +                                   const struct iio_chan_spec *chan,
> >> +                                   enum iio_event_type type,
> >> +                                   enum iio_event_direction dir) {
> >> +     unsigned int event_val;
> >> +     int ret;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +     struct spi_device *spi = data->spi_device;
> >> +
> >> +     mutex_lock(&data->lock);
> >> +     ret = regmap_field_read(data->reg_thermostat, &event_val);
> >> +     if (ret < 0) {
> >> +             dev_err(&spi->dev, "failed to read thermostat status\n");
> >> +             mutex_unlock(&data->lock);
> >> +             return ret;
> >> +     }
> >> +     mutex_unlock(&data->lock);
> >> +
> >> +     return event_val;
> >> +}
> >> +
> >> +static int max31722_write_event_config(struct iio_dev *indio_dev,
> >> +                                    const struct iio_chan_spec *chan,
> >> +                                    enum iio_event_type type,
> >> +                                    enum iio_event_direction dir,
> >> +                                    int state) {
> >> +     int ret;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +     struct spi_device *spi = data->spi_device;
> >> +
> >> +     if (state != 0 && state != 1)
> >> +             return -EINVAL;
> >> +
> >> +     /*
> >> +      * Set thermostat mode:
> >> +      * 0 : comparator mode (default)
> >> +      * 1 : interrupt mode
> >> +      */
> >> +     mutex_lock(&data->lock);
> >> +     ret = regmap_field_write(data->reg_thermostat, state);
> >> +     if (ret < 0)
> >> +             dev_err(&spi->dev, "failed to set thermostat mode\n");
> >> +     mutex_unlock(&data->lock);
> >> +
> >> +     return ret;
> >> +}
> >> +
> >> +static int max31722_read_event(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) {
> >> +     int ret;
> >> +     u8 reg;
> >> +     u16 buf;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +
> >> +     if (info != IIO_EV_INFO_VALUE)
> >> +             return -EINVAL;
> >> +
> >> +     if (dir == IIO_EV_DIR_RISING)
> >> +             reg = MAX31722_REG_THIGH_LSB;
> >> +     else if (dir == IIO_EV_DIR_FALLING)
> >> +             reg = MAX31722_REG_TLOW_LSB;
> >> +     else
> >> +             return -EINVAL;
> >> +
> >> +     mutex_lock(&data->lock);
> >> +     ret = regmap_bulk_read(data->regmap, reg, &buf, 2);
> >> +     mutex_unlock(&data->lock);
> >> +     if (ret < 0) {
> >> +             dev_err(&data->spi_device->dev,
> >> +                     "failed to read threshold register\n");
> >> +             return ret;
> >> +     }
> >> +     max31722_reg_to_float(buf, val, val2);
> >> +
> >> +     return IIO_VAL_INT_PLUS_MICRO;
> >> +}
> >> +
> >> +static int max31722_write_event(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) {
> >> +     int ret;
> >> +     u8 reg;
> >> +     u16 buf;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +
> >> +     if (val < MAX31722_MIN_TEMP || val > MAX31722_MAX_TEMP)
> >> +             return -EINVAL;
> >> +
> >> +     if (dir == IIO_EV_DIR_RISING)
> >> +             reg = MAX31722_REG_THIGH_LSB;
> >> +     else if (dir == IIO_EV_DIR_FALLING)
> >> +             reg = MAX31722_REG_TLOW_LSB;
> >> +     else
> >> +             return -EINVAL;
> >> +
> >> +     buf = max31722_float_to_reg(val, val2);
> >> +
> >> +     ret = regmap_bulk_write(data->regmap, reg, &buf, 2);
> >> +     if (ret < 0)
> >> +             dev_err(&data->spi_device->dev,
> >> +                     "failed to write threshold register\n");
> >> +
> >> +     return ret;
> >> +}
> >> +
> >>  static int max31722_read_raw(struct iio_dev *indio_dev,
> >>                            struct iio_chan_spec const *chan,
> >>                            int *val, int *val2, long mask) @@ -241,8
> >> +441,63 @@ static const struct iio_info max31722_info = {
> >>       .read_raw               = max31722_read_raw,
> >>       .write_raw              = max31722_write_raw,
> >>       .attrs                  = &max31722_attribute_group,
> >> +     .read_event_value       = max31722_read_event,
> >> +     .write_event_value      = max31722_write_event,
> >> +     .read_event_config      = max31722_read_event_config,
> >> +     .write_event_config     = max31722_write_event_config,
> >>  };
> >>
> >> +static irqreturn_t max31722_irq_handler(int irq, void *private) {
> >> +     struct iio_dev *indio_dev = private;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +
> >> +     data->timestamp = iio_get_time_ns();
> >> +
> >> +     return IRQ_WAKE_THREAD;
> >> +}
> >> +
> >> +static irqreturn_t max31722_irq_event_handler(int irq, void
> >> +*private) {
> >> +     int ret;
> >> +     u64 event;
> >> +     u16 temp;
> >> +     u16 tlow;
> >> +     u16 thigh;
> >> +     unsigned int dir;
> >> +     struct iio_dev *indio_dev = private;
> >> +     struct max31722_data *data = iio_priv(indio_dev);
> >> +
> >> +     /*
> >> +      * Do a quick temperature reading and compare it with TLOW/THIGH
> >> +      * so we can tell which threshold has been met.
> > Hmm. Might end up detecting neither occured if the condition is no
> > longer met.  Iritating hardware!
> >
> > We do have the 'magic' option of IIO_EV_DIR_EITHER to specify that we
> > don't know which one occured.  The idea of that has always been that
> > userspace can elect to do what you have here if it cares rather than
> > pushing this code into drivers.
> >> +      */
> >> +     mutex_lock(&data->lock);
> >> +     ret = regmap_bulk_read(data->regmap, MAX31722_REG_TEMP_LSB,
> &temp, 2);
> >> +     if (ret < 0)
> >> +             goto exit_err;
> >> +     ret = regmap_bulk_read(data->regmap, MAX31722_REG_TLOW_LSB,
> &tlow, 2);
> >> +     if (ret < 0)
> >> +             goto exit_err;
> >> +     ret = regmap_bulk_read(data->regmap, MAX31722_REG_THIGH_LSB,
> &thigh, 2);
> >> +     if (ret < 0)
> >> +             goto exit_err;
> >> +     mutex_unlock(&data->lock);
> >> +
> >> +     dir = (temp > thigh ? 1 : 0);
> >> +     event = IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
> IIO_EV_TYPE_THRESH,
> >> +                                  (dir ? IIO_EV_DIR_RISING :
> >> +                                         IIO_EV_DIR_FALLING));
> >> +     iio_push_event(indio_dev, event, data->timestamp);
> >> +
> >> +     return IRQ_HANDLED;
> >> +
> >> +exit_err:
> >> +     dev_err(&data->spi_device->dev, "failed to read temperature
> register\n");
> >> +     mutex_unlock(&data->lock);
> >> +     return ret;
> >> +}
> >> +
> >>  static int max31722_init(struct max31722_data *data)  {
> >>       int ret = 0;
> >> @@ -259,15 +514,40 @@ static int max31722_init(struct max31722_data
> >> *data)
> >>
> >>       MAX31722_REGFIELD(state);
> >>       MAX31722_REGFIELD(resolution);
> >> +     MAX31722_REGFIELD(thermostat);
> >>
> >>       /* Set SD bit to 0 so we can have continuous measurements. */
> >>       ret = regmap_field_write(data->reg_state,
> >> MAX31722_MODE_CONTINUOUS);
> >> +     if (ret < 0) {
> >> +             dev_err(&spi->dev, "failed to configure sensor.\n");
> >> +             return ret;
> >> +     }
> >> +
> >> +     /* Set thermostat interrupt mode */
> >> +     ret = regmap_field_write(data->reg_thermostat, 1);
> >>       if (ret < 0)
> >>               dev_err(&spi->dev, "failed to configure sensor.\n");
> >>
> >>       return ret;
> >>  }
> >>
> >> +static int max31722_gpio_probe(struct spi_device *spi) {
> >> +     struct gpio_desc *gpio;
> >> +
> >> +     if (!spi)
> >> +             return -EINVAL;
> >> +
> >> +     /* gpio interrupt pin */
> >> +     gpio = devm_gpiod_get_index(&spi->dev, MAX31722_GPIO, 0,
> GPIOD_IN);
> >> +     if (IS_ERR(gpio)) {
> >> +             dev_err(&spi->dev, "gpio request failed.\n");
> >> +             return PTR_ERR(gpio);
> >> +     }
> > If this is an interrupt, why are we specifying it as a gpio?
> 
> It is an interrupt requested on a GPIO pin. Isn't this the standard way of
> mapping a GPIO pin to an IRQ? Anyhow, as explained below this is no longer
> needed.

Actually, without calling devm_gpiod_get_index, which eventually calls gpiod_request,
I'm unable to receive interrupts.

> 
> >> +
> >> +     return gpiod_to_irq(gpio);
> >> +}
> >> +
> >>  static int max31722_probe(struct spi_device *spi)  {
> >>       int ret;
> >> @@ -294,15 +574,34 @@ static int max31722_probe(struct spi_device
> >> *spi)
> >>
> >>       ret = max31722_init(data);
> >>       if (ret < 0)
> >> -             return ret;
> >> +             goto err_standby;
> >> +
> >> +     ret = max31722_gpio_probe(data->spi_device);
> >> +     if (ret < 0)
> >> +             goto err_standby;
> >> +
> >> +     spi->irq = ret;
> 
> If ACPI or DT is properly configured spi->irq should already contain the
> correct irq number. We used the gpio_probe approach when ACPI didn't
> save the interrupt number. Newer kernel version shouldn't have this
> problem.
>

The ACPI core is indeed allocating a valid spi->irq number, however, gpio
Interrupts don't work unless gpiod_request is called. Perhaps this is due to
an issue in the SPI core?

> >> +     ret = devm_request_threaded_irq(&spi->dev, spi->irq,
> >> +                                     max31722_irq_handler,
> >> +                                     max31722_irq_event_handler,
> >> +                                     IRQF_TRIGGER_LOW,
> >> +                                     MAX31722_EVENT, indio_dev);
> >> +     if (ret < 0) {
> >> +             dev_err(&spi->dev, "request irq %d failed\n", spi->irq);
> >> +             goto err_standby;
> >> +     }
> > I would suggest you want to make the irq optional, though that could
> > happen at a later date when someone has a board where it isn't wired.
> 
> Agree. We had previous experience with unwired interrupts pins.
> 
> >>
> >>       ret = iio_device_register(indio_dev);
> >>       if (ret < 0) {
> >>               dev_err(&spi->dev, "iio_device_register failed\n");
> >> -             regmap_field_write(data->reg_state,
> MAX31722_MODE_STANDBY);
> >> +             goto err_standby;
> >>       }
> >>
> >>       return ret;
> >> +
> >> +err_standby:
> >> +     regmap_field_write(data->reg_state, MAX31722_MODE_STANDBY);
> >> +     return ret;
> >>  }
> >>
> >>  static int max31722_remove(struct spi_device *spi)
> >>
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-iio"
> > in the body of a message to majordomo@xxxxxxxxxxxxxxx More
> majordomo
> > info at  http://vger.kernel.org/majordomo-info.html
��.n��������+%������w��{.n�����{��(��)��jg��������ݢj����G�������j:+v���w�m������w�������h�����٥




[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