RE: [PATCH v10 3/5] iio: adc: Add Xilinx AMS driver

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

 



Hi Andy,

Thanks for your review.

Apologies for sending patch too soon.
I missed your reply to my previous patches.

> -----Original Message-----
> From: Andy Shevchenko <andriy.shevchenko@xxxxxxxxxxxxxxx>
> Sent: Wednesday 17 November 2021 8:03 PM
> To: Anand Ashok Dumbre <ANANDASH@xxxxxxxxxx>
> Cc: linux-kernel@xxxxxxxxxxxxxxx; jic23@xxxxxxxxxx; lars@xxxxxxxxxx; linux-
> iio@xxxxxxxxxxxxxxx; git <git@xxxxxxxxxx>; Michal Simek
> <michals@xxxxxxxxxx>; gregkh@xxxxxxxxxxxxxxxxxxx; rafael@xxxxxxxxxx;
> linux-acpi@xxxxxxxxxxxxxxx; heikki.krogerus@xxxxxxxxxxxxxxx; Manish Narani
> <MNARANI@xxxxxxxxxx>
> Subject: Re: [PATCH v10 3/5] iio: adc: Add Xilinx AMS driver
> 
> On Wed, Nov 17, 2021 at 04:10:26PM +0000, Anand Ashok Dumbre wrote:
> > The AMS includes an ADC as well as on-chip sensors that can be used to
> > sample external voltages and monitor on-die operating conditions, such
> > as temperature and supply voltage levels. The AMS has two SYSMON
> blocks.
> > PL-SYSMON block is capable of monitoring off chip voltage and
> > temperature.
> >
> > PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring
> > from an external master. Out of these interfaces currently only DRP is
> > supported. Other block PS-SYSMON is memory mapped to PS.
> >
> > The AMS can use internal channels to monitor voltage and temperature
> > as well as one primary and up to 16 auxiliary channels for measuring
> > external voltages.
> >
> > The voltage and temperature monitoring channels also have event
> > capability which allows to generate an interrupt when their value
> > falls below or raises above a set threshold.
> 
> Thanks for an update, my comments below.
> 
> ...
> 
> Missed bitfields.h as kbuild bot noticed.
> 
> > +#include <linux/bits.h>
> > +#include <linux/clk.h>
> > +#include <linux/delay.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/iopoll.h>
> > +#include <linux/kernel.h>
> > +#include <linux/module.h>
> > +#include <linux/mod_devicetable.h>
> > +#include <linux/overflow.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/property.h>
> > +#include <linux/slab.h>
> 
> ...
> 
> > +#define AMS_ALARM_THR_DIRECT_MASK	BIT(1)
> 
> > +#define AMS_ALARM_THR_MIN		~(BIT(16) - 1)
> 
> This is wrong. I already said it.

Yes, understood.

> 
> > +#define AMS_ALARM_THR_MAX		(BIT(16) - 1)
> 
> ...
> 
> > +enum ams_alarm_bit {
> > +	AMS_ALARM_BIT_TEMP,
> > +	AMS_ALARM_BIT_SUPPLY1,
> > +	AMS_ALARM_BIT_SUPPLY2,
> > +	AMS_ALARM_BIT_SUPPLY3,
> > +	AMS_ALARM_BIT_SUPPLY4,
> > +	AMS_ALARM_BIT_SUPPLY5,
> > +	AMS_ALARM_BIT_SUPPLY6,
> > +	AMS_ALARM_BIT_RESERVED,
> > +	AMS_ALARM_BIT_SUPPLY7,
> > +	AMS_ALARM_BIT_SUPPLY8,
> > +	AMS_ALARM_BIT_SUPPLY9,
> > +	AMS_ALARM_BIT_SUPPLY10,
> > +	AMS_ALARM_BIT_VCCAMS,
> > +	AMS_ALARM_BIT_TEMP_REMOTE
> 
> +Comma, same to the rest where the last item is not a terminator one.

Will add.

> 
> > +};
> > +
> > +enum ams_seq {
> > +	AMS_SEQ_VCC_PSPLL,
> > +	AMS_SEQ_VCC_PSBATT,
> > +	AMS_SEQ_VCCINT,
> > +	AMS_SEQ_VCCBRAM,
> > +	AMS_SEQ_VCCAUX,
> > +	AMS_SEQ_PSDDRPLL,
> > +	AMS_SEQ_INTDDR
> 
> ...like here.

Will add.

> 
> > +};
> 
> > +enum ams_ps_pl_seq {
> > +	AMS_SEQ_CALIB,
> > +	AMS_SEQ_RSVD_1,
> > +	AMS_SEQ_RSVD_2,
> > +	AMS_SEQ_TEST,
> > +	AMS_SEQ_RSVD_4,
> > +	AMS_SEQ_SUPPLY4,
> > +	AMS_SEQ_SUPPLY5,
> > +	AMS_SEQ_SUPPLY6,
> > +	AMS_SEQ_TEMP,
> > +	AMS_SEQ_SUPPLY2,
> > +	AMS_SEQ_SUPPLY1,
> > +	AMS_SEQ_VP_VN,
> > +	AMS_SEQ_VREFP,
> > +	AMS_SEQ_VREFN,
> > +	AMS_SEQ_SUPPLY3,
> > +	AMS_SEQ_CURRENT_MON,
> > +	AMS_SEQ_SUPPLY7,
> > +	AMS_SEQ_SUPPLY8,
> > +	AMS_SEQ_SUPPLY9,
> > +	AMS_SEQ_SUPPLY10,
> > +	AMS_SEQ_VCCAMS,
> > +	AMS_SEQ_TEMP_REMOTE,
> > +	AMS_SEQ_MAX
> 
> ...but not here!
> 
> > +};
> 
> ...
> 
> > +#define AMS_SEQ(x)		(AMS_SEQ_MAX + (x))
> > +#define AMS_VAUX_SEQ(x)		(AMS_SEQ_MAX + (x))
> 
> What's the difference?

I will remove the AMS_VAUX_SEQ. They are the same thing.
I am not sure what the intent from the original author was.

> 
> > +#define AMS_PS_SEQ_MAX		AMS_SEQ_MAX
> 
> Perhaps this should be above (for the sake of easier reading).

Agreed. 

> 
> > +#define PS_SEQ(x)		(x)
> > +#define PL_SEQ(x)		(AMS_PS_SEQ_MAX + (x))
> > +#define AMS_CTRL_SEQ_BASE	(AMS_PS_SEQ_MAX * 3)
> 
> ...
> 
> > +		ret = readl_poll_timeout(ams->base + AMS_PS_CSTS, reg,
> > +					 (reg & expect), 0,
> 
> Parentheses are not needed.
> WHy 0? Is it okay to load CPU like this?

No its not. 

> 
> > +					 AMS_INIT_TIMEOUT_US);
> > +		if (ret)
> > +			return ret;
> 
> ...
> 
> > +	if (ams->pl_base) {
> > +		reg = readl(ams->base + AMS_PL_CSTS);
> > +		if (reg == 0)
> 
> > +			return (int) reg;
> 
> I already said that this is wrong.
> 
> > +		writel(AMS_PL_RESET_VALUE, ams->pl_base +
> AMS_VP_VN);
> > +
> > +		/* put sysmon in a default state */
> > +		ams_pl_update_reg(ams, AMS_REG_CONFIG1,
> AMS_CONF1_SEQ_MASK,
> > +				  AMS_CONF1_SEQ_DEFAULT);
> > +	}
> 
> ...
> 
> > +	ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg,
> > +				 (reg & expect), 0, AMS_INIT_TIMEOUT_US);
> > +	if (ret)
> > +		return ret;
> 
> Same two comments as per above readl_poll_timeout() usage.
> 
> ...
> 
> > +			ret = ams_read_vcc_reg(ams, chan->address, val);
> > +			if (ret) {
> > +				mutex_unlock(&ams->lock);
> > +				return -EINVAL;
> 
> Shadowed error code.

I don’t understand.

> 
> > +			}
> 
> ...
> 
> > +	case IIO_CHAN_INFO_OFFSET:
> > +		/* Only the temperature channel has an offset */
> > +		*val = AMS_TEMP_OFFSET;
> > +		return IIO_VAL_INT;
> > +	}
> 
> > +	return -EINVAL;
> 
> Why not keep it in the default case?

Oops missed one there.

> 
>
> 
> > +	switch (event) {
> > +	case AMS_ALARM_BIT_TEMP:
> > +		scan_index += AMS_SEQ_TEMP;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY1:
> > +		scan_index += AMS_SEQ_SUPPLY1;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY2:
> > +		scan_index += AMS_SEQ_SUPPLY2;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY3:
> > +		scan_index += AMS_SEQ_SUPPLY3;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY4:
> > +		scan_index += AMS_SEQ_SUPPLY4;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY5:
> > +		scan_index += AMS_SEQ_SUPPLY5;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY6:
> > +		scan_index += AMS_SEQ_SUPPLY6;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY7:
> > +		scan_index += AMS_SEQ_SUPPLY7;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY8:
> > +		scan_index += AMS_SEQ_SUPPLY8;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY9:
> > +		scan_index += AMS_SEQ_SUPPLY9;
> > +		break;
> > +	case AMS_ALARM_BIT_SUPPLY10:
> > +		scan_index += AMS_SEQ_SUPPLY10;
> > +		break;
> > +	case AMS_ALARM_BIT_VCCAMS:
> > +		scan_index += AMS_SEQ_VCCAMS;
> > +		break;
> > +	case AMS_ALARM_BIT_TEMP_REMOTE:
> > +		scan_index += AMS_SEQ_TEMP_REMOTE;
> > +		break;
> 
> default: ?

This is limited by hw bits.
For default I will use the default scan_index value.
Is that ok?

> 
> > +	}
> 
> ...
> 
> > +	if (chan->type == IIO_TEMP) {
> > +		offset = ams_get_alarm_offset(chan->scan_index,
> > +					      IIO_EV_DIR_FALLING);
> 
> One line?

Agreed. 
> 
> > +	}
> 
> ...
> 
> > +	const struct iio_chan_spec *chan;
> > +
> > +	chan = ams_event_to_channel(indio_dev, event);
> 
> Can be done in one line.
> 
> 
> > +	/* Only process alarms that are not masked */
> > +	isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) |
> > +ams->masked_alarm);
> 
> > +
> 
> Redundant blank line.

Agreed.

> 
> > +	if (!isr0) {
> > +		spin_unlock(&ams->intr_lock);
> > +		return IRQ_NONE;
> > +	}
> 
> ...
> 
> > +		.mask_separate = BIT(IIO_EV_INFO_ENABLE) |
> > +				BIT(IIO_EV_INFO_VALUE),
> 
> One line?

Agreed. 
> 
> ...
> 
> > +	fwnode_for_each_child_node(chan_node, child) {
> > +		ret = fwnode_property_read_u32(child, "reg", &reg);
> 
> > +		if (ret || reg > (AMS_PL_MAX_EXT_CHANNEL + 30))
> 
> Too many parentheses.

Is it a good practice to not have parantheses around (AMS_PL_MAX_EXT_CHANNEL + 30) ?

> 
> > +			continue;
> 
> > +		memcpy(&channels[num_channels], &ams_pl_channels[reg
> +
> > +		       AMS_PL_MAX_FIXED_CHANNEL - 30], sizeof(*channels));
> > +
> > +		if (fwnode_property_read_bool(child, "xlnx,bipolar"))
> > +			channels[num_channels].scan_type.sign =	's';
> 
> Use temporary variable for &channels[num_channels] in both cases.

Makes sense.

> 
> > +		num_channels++;
> > +	}
> 
> ...
> 
> > +		memcpy(channels + num_channels, ams_ps_channels,
> > +		       sizeof(ams_ps_channels));
> 
> Ditto.
> 
> ...
> 
> > +		memcpy(channels + num_channels, ams_pl_channels,
> > +		       AMS_PL_MAX_FIXED_CHANNEL * sizeof(*channels));
> 
> Ditto.
> 
> ...
> 
> > +		memcpy(channels + num_channels, ams_ctrl_channels,
> > +		       sizeof(ams_ctrl_channels));
> 
> Ditto.
> 

Will do for all cases.

> ...
> 
> > +static int ams_parse_firmware(struct iio_dev *indio_dev,
> > +			      struct platform_device *pdev)
> 
> Why do you need second parameter? Doesn't indio_dev already have it?

Will do.

> 
> > +{
> > +	struct ams *ams = iio_priv(indio_dev);
> > +	struct iio_chan_spec *ams_channels, *dev_channels;
> > +	struct fwnode_handle *child = NULL, *fwnode =
> dev_fwnode(&pdev->dev);
> > +	size_t dev_chan_size, ams_chan_size, num_chan;
> > +	int ret, ch_cnt = 0, i, rising_off, falling_off;
> > +	unsigned int num_channels = 0;
> > +
> > +
> 
> One blank line is enough.

Will remove it.

> 
> > +	num_chan = ARRAY_SIZE(ams_ps_channels) +
> ARRAY_SIZE(ams_pl_channels) +
> > +		ARRAY_SIZE(ams_ctrl_channels);
> 
> > +	ams_chan_size = array_size(num_chan, sizeof(struct
> iio_chan_spec));
> > +	if (ams_chan_size == SIZE_MAX)
> > +		return -EINVAL;
> 
> Why is this needed now since you are using kcalloc()?
> 
> > +	/* Initialize buffer for channel specification */
> > +	ams_channels = kcalloc(num_chan, sizeof(struct iio_chan_spec),
> > +GFP_KERNEL);
> 
> sizeof(*ams_channels)
> 
> > +	if (!ams_channels)
> > +		return -ENOMEM;
> 
> ...
> 
> > +			ret = ams_init_module(indio_dev, child,
> > +					      ams_channels + num_channels);
> 
> One line?
> 
> ...
> 
> > +				writel(AMS_ALARM_THR_MIN,
> > +				       ams->pl_base + falling_off);
> > +				writel(AMS_ALARM_THR_MAX,
> > +				       ams->pl_base + rising_off);
> 
> Ditto.
> 
> ...
> 
> > +				writel(AMS_ALARM_THR_MIN,
> > +				       ams->ps_base + falling_off);
> > +				writel(AMS_ALARM_THR_MAX,
> > +				       ams->ps_base + rising_off);
> 
> Ditto.
> 
> > +	dev_chan_size = array_size((size_t)num_channels, sizeof(struct
> iio_chan_spec));
> > +	if (dev_chan_size == SIZE_MAX)
> > +		return -EINVAL;
> 
> Why is it needed now?
> 
> > +	dev_channels = devm_kcalloc(&pdev->dev, (size_t)num_channels,
> 
> Why casting?
> 
> > +				    sizeof(struct iio_chan_spec), GFP_KERNEL);
> 
> sizeof(*dev_channels)
> 
> > +	if (!dev_channels) {
> > +		ret = -ENOMEM;
> > +		goto free_mem;
> > +	}
> 
> > +	memcpy(dev_channels, ams_channels,
> > +	       sizeof(*ams_channels) * num_channels);
> 
> Hmm... according to the code the num_channels can be less than or equal to
> num_chan. Hence, what you should use is the devm_krealloc_array().
> 
> static inline void *devm_krealloc_aray(...) {
> 	...see how krealloc_array() is defined...
> }
> 
> No need to copy memory again.

Will take a look.

> 
> ...
> 
> > +	ret = 0;
> > +
> > +free_mem:
> > +	kfree(ams_channels);
> > +
> > +	return ret;
> 
> This will go away after switching to devm_kmalloc_array() +
> devm_krealloc_array().
> 
> ...
> 
> > +	ret = ams_parse_firmware(indio_dev, pdev);
> > +	if (ret) {
> > +		dev_err_probe(&pdev->dev, ret, "failure in parsing DT\n");
> > +		return ret;
> 
> 	return dev_err_probe(...);
> 
> Ditto for the rest similar cases.

Sure.
> 
> > +	}
> 
> --
> With Best Regards,
> Andy Shevchenko
> 

Thanks,
Anand




[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