Hi Jonathan,

Thanks for the review!

On 09/13/2014 08:27 PM, Jonathan Cameron wrote:
> On 13/09/14 00:27, Hartmut Knaack wrote:
>> Stanimir Varbanov schrieb, Am 11.09.2014 17:13:
>>> The voltage ADC is peripheral of Qualcomm SPMI PMIC chips. It has
>>> 15bits resolution and register space inside PMIC accessible across
>>> SPMI bus.
>>>
>>> The vadc driver registers itself through IIO interface.
>>>
>> Looks already pretty good. Things you should consider in regard of common 
>> coding style are to use the variable name ret instead of rc, since it is 
>> used in almost all adc drivers and thus makes reviewing a bit easier. 
>> Besides that, you seem to use unsigned as well as unsigned int, so to be 
>> consistent, please stick to one of them. Other comments in line.
> 
> A few additional comments from me.  My biggest question is whether
> you are actually making life difficult for yourself by having
> vadc_channels and vadc->channels (don't like the similar naming btw!)
> in different orders.  I think you can move the ordering into the device
> tree reading code rather than doing it in lots of other places.  Hence
> rather than an order based on the device tree description, put the
> data into a fixed ofer in vadc->channels.
> 
> Entirely possible I'm missing something though :)
>>> Signed-off-by: Stanimir Varbanov <svarba...@mm-sol.com>
>>> Signed-off-by: Ivan T. Ivanov <iiva...@mm-sol.com>
>>> ---
>>>  drivers/iio/adc/Kconfig                       |   11 +
>>>  drivers/iio/adc/Makefile                      |    1 +
>>>  drivers/iio/adc/qcom-spmi-vadc.c              |  999 
>>> +++++++++++++++++++++++++
>>>  include/dt-bindings/iio/qcom,spmi-pmic-vadc.h |  119 +++
>>>  4 files changed, 1130 insertions(+), 0 deletions(-)
>>>  create mode 100644 drivers/iio/adc/qcom-spmi-vadc.c
>>>  create mode 100644 include/dt-bindings/iio/qcom,spmi-pmic-vadc.h

<snip>

>>> +
>>> +static int
>> Don't wrap line here.
>>> +vadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
>>> +         int *val, int *val2, long mask)
>>> +{
>>> +   struct vadc_priv *vadc = iio_priv(indio_dev);
>>> +   struct vadc_channel *vchan;
>>> +   struct vadc_result result;
>>> +   int rc;
>>> +
> It is a bit of a pitty we can't avoid this lookup. Normally I'd suggest
> putting an index in chan->address but you've already used that.
> The purpose of this is to (I think) allow you to have the private
> data stored in a random order... What is the benefit of doing that?
> (see various comments elsewhere)

So the vadc_channels array describe all possible vadc channels for all
supported PMICs from this driver. On the other side vadc->channels
pointer should contain only the channels described in DT.

Thus we need a below function to check is the current channel is active
for the current DT (current PMIC version). This is because we have in
vadc_channels the full set of channels but not every supported PMIC have
support for them.

I agree that this peace of code is not so clear. So I will try to rework
this and register to the IIO core only those channels that have channel
descriptions in DT.

Also I wonder can I use iio_chan_spec::address field as a pointer to
private structure with vadc channel properties like decimation, prescale
etc. got from DT or the default values.

> 
>>> +   vchan = vadc_find_channel(vadc, chan->channel);
>>> +   if (!vchan)
>>> +           return -EINVAL;
>>> +
>>> +   if (!vadc->is_ref_measured) {
>>> +           rc = vadc_measure_reference_points(vadc);
>>> +           if (rc)
>>> +                   return rc;
>>> +
>>> +           vadc->is_ref_measured = true;
>>> +   }
>>> +
>>> +   switch (mask) {
>>> +   case IIO_CHAN_INFO_PROCESSED:
>>> +           rc = vadc_do_conversion(vadc, vchan, &result.adc_code);
>>> +           if (rc)
>>> +                   return rc;
>>> +
>>> +           vadc_calibrate(vadc, vchan, &result);
>>> +
>>> +           *val = result.physical;
> I'm a little suspicious here.  Are the resulting values in milivolts for
> all the channels?  Very handy if so, but seems a little unlikely with 15 bit
> ADC that you'd have no part of greater accuracy than a milivolt.

In fact *val is in microvolts. What is the expected unit from IIO ADC users?

>>> +           rc = IIO_VAL_INT;
>> return IIO_VAL_INT;
>>> +           break;
>>> +   default:
>>> +           rc = -EINVAL;
>>> +           break;
>> Drop default case, or leave empty.
>>> +   }
>>> +
>>> +   return rc;
>> return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info vadc_info = {
>>> +   .read_raw = vadc_read_raw,
>>> +   .driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +#define VADC_CHAN(_id, _pre)                                               
>>> \
>>> +   [VADC_##_id] = {                                                \
>>> +           .type = IIO_VOLTAGE,                                    \
> A few of the below look to be temp sensors.  If they are hardwired
> in some way to this functionality (i.e. is it on chip) then it might be
> nice to reflect this in the channel type.

There are a dedicated channels to measure temperature. Those channels
have connected thermistor but I don't think it is on chip. So the
returned adc code is in microvolts and we have a translation table to
convert measured voltage to miliCelsius. I thought that if I mark the
channel type as IIO_TEMP then the user would expect the returned units
to be miliCelsius. If that assumption is not correct I can change the
type of those channels.

>>> +           .indexed = 1,                                           \
>>> +           .channel = VADC_##_id,                                  \
>>> +           .address = _pre,                                        \
>>> +           .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),     \
>>> +           .datasheet_name = __stringify(VADC_##_id),              \
>>> +           .scan_type = {                                          \
>>> +                   .sign           = 's',                          \
>>> +                   .realbits       = 15,                           \
>>> +                   .storagebits    = 16,                           \
>>> +                   .endianness     = IIO_CPU,                      \
>>> +           },                                                      \
>>> +   },
>>> +
>>> +static const struct iio_chan_spec vadc_channels[] = {
>>> +   VADC_CHAN(USBIN, 4)                             /* 0x00 */
>>> +   VADC_CHAN(DCIN, 4)
>>> +   VADC_CHAN(VCHG_SNS, 3)
>>> +   VADC_CHAN(SPARE1_03, 1)
>>> +   VADC_CHAN(USB_ID_MV, 1)
>>> +   VADC_CHAN(VCOIN, 1)
>>> +   VADC_CHAN(VBAT_SNS, 1)
>>> +   VADC_CHAN(VSYS, 1)
>>> +   VADC_CHAN(DIE_TEMP, 0)
>>> +   VADC_CHAN(REF_625MV, 0)
>>> +   VADC_CHAN(REF_1250MV, 0)
>>> +   VADC_CHAN(CHG_TEMP, 0)
>>> +   VADC_CHAN(SPARE1, 0)
>>> +   VADC_CHAN(SPARE2, 0)
>>> +   VADC_CHAN(GND_REF, 0)
>>> +   VADC_CHAN(VDD_VADC, 0)                          /* 0x0f */
>>> +
>>> +   VADC_CHAN(P_MUX1_1_1, 0)                        /* 0x10 */
>>> +   VADC_CHAN(P_MUX2_1_1, 0)
>>> +   VADC_CHAN(P_MUX3_1_1, 0)
>>> +   VADC_CHAN(P_MUX4_1_1, 0)
>>> +   VADC_CHAN(P_MUX5_1_1, 0)
>>> +   VADC_CHAN(P_MUX6_1_1, 0)
>>> +   VADC_CHAN(P_MUX7_1_1, 0)
>>> +   VADC_CHAN(P_MUX8_1_1, 0)
>>> +   VADC_CHAN(P_MUX9_1_1, 0)
>>> +   VADC_CHAN(P_MUX10_1_1, 0)
>>> +   VADC_CHAN(P_MUX11_1_1, 0)
>>> +   VADC_CHAN(P_MUX12_1_1, 0)
>>> +   VADC_CHAN(P_MUX13_1_1, 0)
>>> +   VADC_CHAN(P_MUX14_1_1, 0)
>>> +   VADC_CHAN(P_MUX15_1_1, 0)
>>> +   VADC_CHAN(P_MUX16_1_1, 0)                       /* 0x1f */
>>> +
>>> +   VADC_CHAN(P_MUX1_1_3, 1)                        /* 0x20 */
>>> +   VADC_CHAN(P_MUX2_1_3, 1)
>>> +   VADC_CHAN(P_MUX3_1_3, 1)
>>> +   VADC_CHAN(P_MUX4_1_3, 1)
>>> +   VADC_CHAN(P_MUX5_1_3, 1)
>>> +   VADC_CHAN(P_MUX6_1_3, 1)
>>> +   VADC_CHAN(P_MUX7_1_3, 1)
>>> +   VADC_CHAN(P_MUX8_1_3, 1)
>>> +   VADC_CHAN(P_MUX9_1_3, 1)
>>> +   VADC_CHAN(P_MUX10_1_3, 1)
>>> +   VADC_CHAN(P_MUX11_1_3, 1)
>>> +   VADC_CHAN(P_MUX12_1_3, 1)
>>> +   VADC_CHAN(P_MUX13_1_3, 1)
>>> +   VADC_CHAN(P_MUX14_1_3, 1)
>>> +   VADC_CHAN(P_MUX15_1_3, 1)
>>> +   VADC_CHAN(P_MUX16_1_3, 1)                       /* 0x2f */
>>> +
>>> +   VADC_CHAN(LR_MUX1_BAT_THERM, 0)                 /* 0x30 */
>>> +   VADC_CHAN(LR_MUX2_BAT_ID, 0)
>>> +   VADC_CHAN(LR_MUX3_XO_THERM, 0)
>>> +   VADC_CHAN(LR_MUX4_AMUX_THM1, 0)
>>> +   VADC_CHAN(LR_MUX5_AMUX_THM2, 0)
>>> +   VADC_CHAN(LR_MUX6_AMUX_THM3, 0)
>>> +   VADC_CHAN(LR_MUX7_HW_ID, 0)
>>> +   VADC_CHAN(LR_MUX8_AMUX_THM4, 0)
>>> +   VADC_CHAN(LR_MUX9_AMUX_THM5, 0)
>>> +   VADC_CHAN(LR_MUX10_USB_ID, 0)
>>> +   VADC_CHAN(AMUX_PU1, 0)
>>> +   VADC_CHAN(AMUX_PU2, 0)
>>> +   VADC_CHAN(LR_MUX3_BUF_XO_THERM, 0)              /* 0x3c */
>>> +
>>> +   VADC_CHAN(LR_MUX1_PU1_BAT_THERM, 0)             /* 0x70 */
>>> +   VADC_CHAN(LR_MUX2_PU1_BAT_ID, 0)
>>> +   VADC_CHAN(LR_MUX3_PU1_XO_THERM, 0)
>>> +   VADC_CHAN(LR_MUX4_PU1_AMUX_THM1, 0)
>>> +   VADC_CHAN(LR_MUX5_PU1_AMUX_THM2, 0)
>>> +   VADC_CHAN(LR_MUX6_PU1_AMUX_THM3, 0)
>>> +   VADC_CHAN(LR_MUX7_PU1_AMUX_HW_ID, 0)
>>> +   VADC_CHAN(LR_MUX8_PU1_AMUX_THM4, 0)
>>> +   VADC_CHAN(LR_MUX9_PU1_AMUX_THM5, 0)
>>> +   VADC_CHAN(LR_MUX10_PU1_AMUX_USB_ID, 0)          /* 0x79 */
>>> +   VADC_CHAN(LR_MUX3_BUF_PU1_XO_THERM, 0)          /* 0x7c */
>>> +
>>> +   VADC_CHAN(LR_MUX1_PU2_BAT_THERM, 0)             /* 0xb0 */
>>> +   VADC_CHAN(LR_MUX2_PU2_BAT_ID, 0)
>>> +   VADC_CHAN(LR_MUX3_PU2_XO_THERM, 0)
>>> +   VADC_CHAN(LR_MUX4_PU2_AMUX_THM1, 0)
>>> +   VADC_CHAN(LR_MUX5_PU2_AMUX_THM2, 0)
>>> +   VADC_CHAN(LR_MUX6_PU2_AMUX_THM3, 0)
>>> +   VADC_CHAN(LR_MUX7_PU2_AMUX_HW_ID, 0)
>>> +   VADC_CHAN(LR_MUX8_PU2_AMUX_THM4, 0)
>>> +   VADC_CHAN(LR_MUX9_PU2_AMUX_THM5, 0)
>>> +   VADC_CHAN(LR_MUX10_PU2_AMUX_USB_ID, 0)          /* 0xb9 */
>>> +   VADC_CHAN(LR_MUX3_BUF_PU2_XO_THERM, 0)          /* 0xbc */
>>> +
>>> +   VADC_CHAN(LR_MUX1_PU1_PU2_BAT_THERM, 0)         /* 0xf0 */
>>> +   VADC_CHAN(LR_MUX2_PU1_PU2_BAT_ID, 0)
>>> +   VADC_CHAN(LR_MUX3_PU1_PU2_XO_THERM, 0)
>>> +   VADC_CHAN(LR_MUX4_PU1_PU2_AMUX_THM1, 0)
>>> +   VADC_CHAN(LR_MUX5_PU1_PU2_AMUX_THM2, 0)
>>> +   VADC_CHAN(LR_MUX6_PU1_PU2_AMUX_THM3, 0)
>>> +   VADC_CHAN(LR_MUX7_PU1_PU2_AMUX_HW_ID, 0)
>>> +   VADC_CHAN(LR_MUX8_PU1_PU2_AMUX_THM4, 0)
>>> +   VADC_CHAN(LR_MUX9_PU1_PU2_AMUX_THM5, 0)
>>> +   VADC_CHAN(LR_MUX10_PU1_PU2_AMUX_USB_ID, 0)      /* 0xf9 */
>>> +   VADC_CHAN(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)      /* 0xfc */
>>> +};
>>> +
>>> +static int
>>> +vadc_get_dt_channel_data(struct vadc_priv *vadc, struct device_node *node)
>>> +{
>>> +   struct vadc_channel *vchan;
>>> +   u32 channel, value, varr[2];
>>> +   int rc, pre, time, avg, decim;
>> Drop pre, time, avg and decim and reuse rc instead?
>>> +   const char *name = node->name;
>>> +
>>> +   rc = of_property_read_u32(node, "reg", &channel);
>>> +   if (rc) {
>>> +           dev_dbg(vadc->dev, "invalid channel number %s\n", name);
>>> +           return -EINVAL;
>>> +   }
>>> +
>>> +   if (channel >= vadc->nchannels) {
>>> +           dev_dbg(vadc->dev, "%s invalid channel number %d\n", name,
>>> +                   channel);
>>> +           return -EINVAL;
>>> +   }
>>> +
>>> +   vchan = &vadc->channels[channel];
> Could you not have these in the same order as the iio_chan_spec array?
> Hence move the lookups that are scattered elsewhere in the driver to here?
> 

<snip>

>>> +static int vadc_probe(struct platform_device *pdev)
>>> +{
>>> +   struct device_node *node = pdev->dev.of_node;
>>> +   struct device *dev = &pdev->dev;
>>> +   struct iio_dev *indio_dev;
>>> +   struct vadc_channel *vchan;
>>> +   struct vadc_priv *vadc;
>>> +   struct resource *res;
>>> +   struct regmap *regmap;
>>> +   int rc, irq_eoc, n;
>> unsigned int n?
>>> +
>>> +   regmap = dev_get_regmap(dev->parent, NULL);
>>> +   if (!regmap)
>>> +           return -ENODEV;
>>> +
>>> +   indio_dev = devm_iio_device_alloc(dev, sizeof(*vadc));
>>> +   if (!indio_dev)
>>> +           return -ENOMEM;
>>> +
>>> +   vadc = iio_priv(indio_dev);
>>> +   vadc->dev = dev;
>>> +   vadc->regmap = regmap;
>>> +   vadc->is_ref_measured = false;
>>> +   init_completion(&vadc->complete);
>>> +
>>> +   vadc->nchannels = ARRAY_SIZE(vadc_channels);
>>> +   vadc->channels = devm_kcalloc(dev, vadc->nchannels,
>>> +                                 sizeof(*vadc->channels), GFP_KERNEL);
> Interesting.  This is always the same size as the vadc_channels so you
> might as well keep them in the same order and simplify various corners of
> the code.
>>> +   if (!vadc->channels)
>>> +           return -ENOMEM;
>>> +
> I wonder if we can't vadc->channels rather more different from vadc_channels?
> Perhaps vadc->channelspriv or similar? Confused me a little at first.
>>> +   for (n = 0; n < vadc->nchannels; n++) {
>>> +           vchan = &vadc->channels[n];
>>> +           /* set default channel properties */
>>> +           vchan->number = -1;     /* inactive */
>>> +           vchan->prescaling = vadc_channels[n].address;
>>> +           vchan->decimation = VADC_DEF_DECIMATION;
>>> +           vchan->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
>>> +           vchan->avg_samples = VADC_DEF_AVG_SAMPLES;
>>> +           vchan->calibration = VADC_DEF_CALIB_TYPE;
>>> +   }
>>> +
>>> +   platform_set_drvdata(pdev, vadc);
>>> +
>>> +   res = platform_get_resource(pdev, IORESOURCE_REG, 0);
>>> +   if (!res)
>>> +           return -ENODEV;
>>> +
>>> +   vadc->base = res->start;
>>> +
>>> +   rc = vadc_version_check(vadc);
>>> +   if (rc < 0)
>>> +           return -ENODEV;
>>> +
>>> +   rc = vadc_get_dt_data(vadc, node);
>>> +   if (rc < 0)
>>> +           return rc;
>>> +
> Do we need an explicit flag to indicate poll mode rather than just
> using the absense of the irq being specified to select that mode?

I will think about this. Maybe I will check the returned value from
platform_get_irq to see is the IRQ resource exist. If the IRQ doesn't
exist I will fallback to polling.

<snip>


-- 
regards,
Stan
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to