On September 15, 2014 3:12:50 PM GMT+01:00, Stanimir Varbanov <svarbanov@xxxxxxxxxx> wrote: >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 <svarbanov@xxxxxxxxxx> >>>> Signed-off-by: Ivan T. Ivanov <iivanov@xxxxxxxxxx> >>>> --- >>>> 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. Cool > >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. Yes or an index into an array of them perhaps? > >> >>>> + 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? See Documentation/ABI/sysfs-bus-iio Millivolts I think... We copied hwmon where possible. > >>>> + 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. You are correct. Leave them as voltages. If they were hardwired inside the chip it would be different. > >>>> + .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. That was my thought. > ><snip> -- Sent from my Android phone with K-9 Mail. Please excuse my brevity. -- 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