Re: [PATCH v2 1/2] iio: vadc: Qualcomm SPMI PMIC voltage ADC driver

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

 




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




[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