Thread (14 messages) 14 messages, 7 authors, 2025-06-10

Re: [PATCH v2 2/2] iio: temperature: add support for MCP998X

From: Andy Shevchenko <hidden>
Date: 2025-05-29 18:37:14
Also in: linux-iio, lkml

On Thu, May 29, 2025 at 12:37 PM [off-list ref] wrote:
This is the driver for Microchip MCP998X/33 and MCP998XD/33D
Multichannel Automotive Temperature Monitor Family.
...
+/*
+ * (Sampling_Frequency * 1000000) / (Window_Size * 2)
+ */
+static int mcp9982_calc_all_3db_values(void)
+{
+       int i, j;
Why signed?
+       u64 numerator;
+       u32 denominator, remainder;
+
+       for (i = 0; i < ARRAY_SIZE(mcp9982_window_size); i++)
+               for (j = 0; j <  ARRAY_SIZE(mcp9982_sampl_fr); j++) {
+                       numerator = 1000000 * mcp9982_sampl_fr[j][0];
MICRO ? Ditto for the below case.
+                       denominator = 2 * mcp9982_window_size[i] * mcp9982_sampl_fr[j][1];
+                       numerator = div_u64_rem(numerator, denominator, &remainder);
The remainder seems unused here. So, why do you use the div_u64_rem()
and not simply do_div()?
+                       mcp9982_3db_values_map_tbl[j][i][0] = div_u64_rem(numerator, 1000000,
+                                                                         &remainder);
+                       mcp9982_3db_values_map_tbl[j][i][1] = remainder;
+               }
+       return 0;
+}
...
+struct mcp9982_priv {
+       u8 num_channels;
+       bool extended_temp_range;
+       bool beta_autodetect[2];
+       /*
+        * Synchronize access to private members, and ensure
+        * atomicity of consecutive regmap operations.
+        */
+       struct mutex lock;
+       struct regmap *regmap;
Wouldn't moving this to be the first member help with the code
generation (size)?
+       struct iio_chan_spec *iio_chan;
+       char *labels[MCP9982_MAX_NUM_CHANNELS];
+       int ideality_value[4];
+       int recd34_enable;
+       int recd12_enable;
+       int apdd_enable;
+       int sampl_idx;
+};
...
+static int mcp9982_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+                           int *val, int *val2, long mask)
+{
+       struct mcp9982_priv *priv = iio_priv(indio_dev);
+       int ret, index, index2;
regmap API takes unsigned int, why are these signed? And in general
it's better not to mix the returning variable with something
semantically different.
+       ret = regmap_write(priv->regmap, MCP9982_ONE_SHOT_ADDR, 1);
+       if (ret)
+               return ret;
+
+       guard(mutex)(&priv->lock);
+
+       switch (mask) {
+       case IIO_CHAN_INFO_RAW:
+               ret = regmap_read(priv->regmap, MCP9982_INT_VALUE_ADDR(chan->channel), val);
+               if (ret)
+                       return ret;
+
+               /* The extended temperature range is offset by 64 degrees C */
+               if (priv->extended_temp_range)
+                       *val -= 64;
+
+               ret = regmap_read(priv->regmap, MCP9982_FRAC_VALUE_ADDR(chan->channel), val2);
+               if (ret)
+                       return ret;
+
+               /* Only the 3 MSB in low byte registers are used */
+               *val2 = mcp9982_fractional_values[*val2 >> 5];
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               *val = mcp9982_conv_rate[priv->sampl_idx][0];
+               *val2 = mcp9982_conv_rate[priv->sampl_idx][1];
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+
+               ret = regmap_read(priv->regmap, MCP9982_RUNNING_AVG_ADDR, &index2);
+               if (ret)
+                       return ret;
+
+               if (index2 >= 2)
+                       index2 -= 1;
Sounds like a clamp(). (Note, what if for whatever reason you will get
index2 bigger than array size?
+               *val = mcp9982_3db_values_map_tbl[priv->sampl_idx][index2][0];
+               *val2 = mcp9982_3db_values_map_tbl[priv->sampl_idx][index2][1];
+               return IIO_VAL_INT_PLUS_MICRO;
+       case IIO_CHAN_INFO_HYSTERESIS:
+               ret = regmap_read(priv->regmap, MCP9982_HYS_ADDR, &index);
+               if (ret)
+                       return ret;
+
+               *val = index;
+               return IIO_VAL_INT;
+       default:
+               return -EINVAL;
+       }
+}
...
+static int mcp9982_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
+                            int val, int val2, long mask)
+{
+       struct mcp9982_priv *priv = iio_priv(indio_dev);
+       int i, ret;
Why is 'i' signed?
+       guard(mutex)(&priv->lock);
+       switch (mask) {
+       case IIO_CHAN_INFO_SAMP_FREQ:
+               for (i = 0; i < ARRAY_SIZE(mcp9982_conv_rate); i++)
+                       if (val == mcp9982_conv_rate[i][0] && val2 == mcp9982_conv_rate[i][1])
+                               break;
+               if (i >= ARRAY_SIZE(mcp9982_conv_rate))
What is the meaning of '>' here?
+                       return -EINVAL;
+               ret = regmap_write(priv->regmap, MCP9982_CONV_ADDR, i);
+               if (ret)
+                       return ret;
+
+               priv->sampl_idx = i;
+               return 0;
+       case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+               for (i = 0; i < ARRAY_SIZE(mcp9982_3db_values_map_tbl[priv->sampl_idx]); i++)
+                       if (val == mcp9982_3db_values_map_tbl[priv->sampl_idx][i][0] &&
+                           val2 == mcp9982_3db_values_map_tbl[priv->sampl_idx][i][1])
+                               break;
+               if (i >= ARRAY_SIZE(mcp9982_3db_values_map_tbl[priv->sampl_idx]))
+                       return -EINVAL;
Ditto.
+               /*
+                * Filter register is coded with values:
+                *-0 for OFF
+                *-1 or 2 for level 1
+                *-3 for level 2
This lists the negative values.... Are you sure the comment is aligned
with what the code is really doing?
+                */
+               if (i == 2)
+                       i = 3;
+               ret = regmap_write(priv->regmap, MCP9982_RUNNING_AVG_ADDR, i);
+
+               return ret;
+       case IIO_CHAN_INFO_HYSTERESIS:
+               if (val < 0 || val > 255)
+                       return -EINVAL;
+
+               ret = regmap_write(priv->regmap, MCP9982_HYS_ADDR, val);
+               return ret;
+       default:
+               return -EINVAL;
+       }
+}
...
+static ssize_t mcp9982_extended_temp_range_store(struct device *dev,
+                                                struct device_attribute *attr,
+                                                const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct mcp9982_priv *priv = iio_priv(indio_dev);
+       int ret, val;
+
+       ret = kstrtouint(buf, 10, &val);
+       if (ret)
+               return -EINVAL;
Why is the error code shadowed?
+       switch (val) {
+       case 0:
+               priv->extended_temp_range = false;
+               break;
+       case 1:
+               priv->extended_temp_range = true;
+               break;
+       default:
+               return -EINVAL;
+       }
Useless, use kstrtobool() instead.
+       guard(mutex)(&priv->lock);
+       ret = regmap_assign_bits(priv->regmap, MCP9982_CFG_ADDR, MCP9982_CFG_RANGE,
+                                priv->extended_temp_range);
+
+       if (ret)
+               return ret;
+
+       return count;
+}
...
+static ssize_t mcp9982_show_beta(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct mcp9982_priv *priv = iio_priv(indio_dev);
+       struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+       int val, ret;
Why is val signed? Please, check your code and fix types all over the place.
+       /* When APDD is enabled, betas are locked to autodetection */
+       if (priv->apdd_enable)
+               return sysfs_emit(buf, "Auto\n");
+
+       ret = regmap_read(priv->regmap, MCP9982_EXT_BETA_CFG_ADDR(this_attr->address), &val);
+       if (ret)
+               return ret;
+
+       if (val < 15)
+               return sysfs_emit(buf, "%s\n", mcp9982_beta_values[val]);
+       if (val == 15)
+               return sysfs_emit(buf, "Diode_Mode\n");
+       else
Redundant 'else'.
+               return sysfs_emit(buf, "Auto\n");
+}
+
+static ssize_t mcp9982_store_beta(struct device *dev, struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+       struct mcp9982_priv *priv = iio_priv(indio_dev);
+       struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+       int i;
Signed? Why?
+       /* When APDD is enabled, betas are locked to autodetection */
+       if (priv->apdd_enable)
+               return -EINVAL;
The below looks like an attempt to reimplement sysfs_match_string().
+       for (i = 0; i < ARRAY_SIZE(mcp9982_beta_values); i++)
+               if (strncmp(buf, mcp9982_beta_values[i], 5) == 0)
+                       break;
+
+       if (i < ARRAY_SIZE(mcp9982_beta_values)) {
+               regmap_write(priv->regmap, MCP9982_EXT_BETA_CFG_ADDR(this_attr->address), i);
+               return count;
+       }
+
+       if (strncmp(buf, "Diode_Mode", 10) == 0) {
+               regmap_write(priv->regmap, MCP9982_EXT_BETA_CFG_ADDR(this_attr->address), 15);
+               return count;
+       }
+
+       if (strncmp(buf, "Auto", 4) == 0) {
+               regmap_write(priv->regmap, MCP9982_EXT_BETA_CFG_ADDR(this_attr->address), BIT(4));
+               return count;
+       }
+
+       return -EINVAL;
+}
+
+static ssize_t mcp9982_beta_available_show(struct device *dev,
+                                          struct device_attribute *attr, char *buf)
+{
+       int i;
+
+       for (i = 0; i < 15; i++) {
+               strcat(buf, mcp9982_beta_values[i]);
+               strcat(buf, " ");
Huh?!
+       }
+       strcat(buf, "Diode_Mode Auto\n");
+       return sysfs_emit(buf, buf);
What does this mean, please?
+}
+
+static IIO_DEVICE_ATTR(enable_extended_temp_range, 0644, mcp9982_extended_temp_range_show,
+                      mcp9982_extended_temp_range_store, 0);
+static IIO_DEVICE_ATTR(in_beta1, 0644, mcp9982_show_beta, mcp9982_store_beta, 0);
+static IIO_DEVICE_ATTR(in_beta2, 0644, mcp9982_show_beta, mcp9982_store_beta, 1);
+static IIO_DEVICE_ATTR(in_beta_available, 0444, mcp9982_beta_available_show, NULL, 0);
First of all, we have IIO_DEVICE_ATTR_RO/RW, second, move each of them
to be closer to the related callback(s).

...
+static struct attribute *mcp9982_attributes[] = {
+       &iio_dev_attr_enable_extended_temp_range.dev_attr.attr,
+       &iio_dev_attr_in_beta1.dev_attr.attr,
+       &iio_dev_attr_in_beta2.dev_attr.attr,
+       &iio_dev_attr_in_beta_available.dev_attr.attr,
+       NULL,
No comma for the terminator.
+};
I stop there as it reminds me that I had commented on something
similar in the past and nothing here was following those comments. Am
I correct?

--
With Best Regards,
Andy Shevchenko
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help