RE: [PATCH v7 2/4] iio: adc: Add Xilinx AMS driver
From: Anand Ashok Dumbre
Date: Tue Nov 02 2021 - 17:31:38 EST
Hi Andy,
Thanks for the review.
> -----Original Message-----
> From: Andy Shevchenko <andy.shevchenko@xxxxxxxxx>
> Sent: Monday 25 October 2021 11:11 AM
> To: Anand Ashok Dumbre <ANANDASH@xxxxxxxxxx>
> Cc: Linux Kernel Mailing List <linux-kernel@xxxxxxxxxxxxxxx>; Jonathan
> Cameron <jic23@xxxxxxxxxx>; Lars-Peter Clausen <lars@xxxxxxxxxx>; linux-
> iio <linux-iio@xxxxxxxxxxxxxxx>; git <git@xxxxxxxxxx>; Michal Simek
> <michals@xxxxxxxxxx>; Peter Meerwald <pmeerw@xxxxxxxxxx>;
> devicetree <devicetree@xxxxxxxxxxxxxxx>; Manish Narani
> <MNARANI@xxxxxxxxxx>
> Subject: Re: [PATCH v7 2/4] iio: adc: Add Xilinx AMS driver
>
> On Tue, Oct 19, 2021 at 6:22 PM Anand Ashok Dumbre
> <anand.ashok.dumbre@xxxxxxxxxx> 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 external master. Out of these interface currently only DRP is
>
> from an external
> interfaces
Will fix the grammar.
>
> > 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.
> >
> > Signed-off-by: Manish Narani <manish.narani@xxxxxxxxxx>
>
> What does this SoB mean here? Have you read Submitting Patches?
Will add the co-developed by tag here.
>
> > Signed-off-by: Anand Ashok Dumbre <anand.ashok.dumbre@xxxxxxxxxx>
>
> ...
>
> > +config XILINX_AMS
> > + tristate "Xilinx AMS driver"
> > + depends on ARCH_ZYNQMP || COMPILE_TEST
> > + depends on HAS_IOMEM
> > + help
>
> > + Say yes here to have support for the Xilinx AMS.
>
> It's not important for most of the users. Please, strat help with more useful
> information like below.
Will do.
>
> > + The driver supports Voltage and Temperature monitoring on Xilinx
> Ultrascale
> > + devices.
> > +
> > + The driver can also be built as a module. If so, the module will be
> called
> > + xilinx-ams.
>
> ...
>
> > + * Manish Narani <mnarani@xxxxxxxxxx>
>
> A-ha! You probably forgot the Co-developed-by tag above.
Correct will add.
>
> > + * Rajnikant Bhojani <rajnikant.bhojani@xxxxxxxxxx>
>
> ...
>
> Missed headers, like bits.h.
Will add.
>
> > +#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/of_address.h>
>
> Do you need this? Maybe mod_devicetable.h?
>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
>
> ...
>
> > +static const unsigned int AMS_UNMASK_TIMEOUT_MS = 500;
>
> Why not define (esp. taking into account another similar define below?
>
Makes sense
> ...
>
> > +#define AMS_REGCFG1_ALARM_MASK 0xF0F
> > +#define AMS_REGCFG3_ALARM_MASK 0x3F
>
> > +#define AMS_PL_ALARM_MASK 0xFFFF0000U
> > +#define AMS_ISR0_ALARM_MASK 0xFFFFFFFFU
> > +#define AMS_ISR1_ALARM_MASK 0xE000001FU
> > +#define AMS_ISR1_EOC_MASK 0x00000008U
>
> What is so special about these that they are not using combinations of
> GENMASK() / BIT()?
>
Will add for those.
> ...
>
> > +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
>
> Is it terminator line? Doesn't sound like it to me. So, please add a comma.
> Same for the rest.
>
> > +};
>
> ...
>
> > + AMS_SEQ_MAX
>
> This seems correct, no comma is needed :-)
>
> ...
>
> > +struct ams {
> > + void __iomem *base;
> > + void __iomem *ps_base;
> > + void __iomem *pl_base;
> > + struct clk *clk;
> > + struct device *dev;
>
> > + /* check kernel doc above */
>
> Useless
>
> > + struct mutex lock;
>
> > + /* check kernel doc above */
>
> Ditto.
>
Will remove the comments
> > + spinlock_t intr_lock;
> > + unsigned int alarm_mask;
> > + unsigned int masked_alarm;
> > + u64 intr_mask;
> > + int irq;
> > + struct delayed_work ams_unmask_work; };
>
> ...
>
> > + writel((val & ~mask) | (data & mask), ams->ps_base + offset);
>
> Split to assignment and simple writel() call. Same to the rest.
>
Will do.
> ...
>
> > + ams->intr_mask &= ~mask;
> > + ams->intr_mask |= (val & mask);
>
> This may be combined to one line as it's a standard pattern.
>
Will do.
> ...
>
> > + if (ams->ps_base) {
> > + /* Configuring PS alarm enable */
> > + cfg = ~((alarm_mask & AMS_ISR0_ALARM_2_TO_0_MASK) <<
> > + AMS_CONF1_ALARM_2_TO_0_SHIFT);
> > + cfg &= ~((alarm_mask & AMS_ISR0_ALARM_6_TO_3_MASK) <<
> > + AMS_CONF1_ALARM_6_TO_3_SHIFT);
> > + ams_ps_update_reg(ams, AMS_REG_CONFIG1,
> AMS_REGCFG1_ALARM_MASK,
> > + cfg);
> > +
> > + cfg = ~((alarm_mask >> AMS_CONF3_ALARM_12_TO_7_SHIFT) &
> > + AMS_ISR0_ALARM_12_TO_7_MASK);
> > + ams_ps_update_reg(ams, AMS_REG_CONFIG3,
> AMS_REGCFG3_ALARM_MASK,
> > + cfg);
> > + }
>
> By factoring out the body of the conditional to a helper function you may:
> - decrease indentation
> - make code better to read
> - reduce LOCs
Will do.
>
> > + if (ams->pl_base) {
> > + pl_alarm_mask = (alarm_mask >> AMS_PL_ALARM_START);
> > + pl_alarm_mask = FIELD_GET(AMS_PL_ALARM_MASK,
> alarm_mask);
> > + /* Configuring PL alarm enable */
> > + cfg = ~((pl_alarm_mask & AMS_ISR0_ALARM_2_TO_0_MASK) <<
> > + AMS_CONF1_ALARM_2_TO_0_SHIFT);
> > + cfg &= ~((pl_alarm_mask & AMS_ISR0_ALARM_6_TO_3_MASK)
> <<
> > + AMS_CONF1_ALARM_6_TO_3_SHIFT);
> > + ams_pl_update_reg(ams, AMS_REG_CONFIG1,
> > + AMS_REGCFG1_ALARM_MASK, cfg);
> > +
> > + cfg = ~((pl_alarm_mask >> AMS_CONF3_ALARM_12_TO_7_SHIFT)
> &
> > + AMS_ISR0_ALARM_12_TO_7_MASK);
> > + ams_pl_update_reg(ams, AMS_REG_CONFIG3,
> > + AMS_REGCFG3_ALARM_MASK, cfg);
> > + }
>
> Ditto. And the same applies to all the rest where it gains something from the
> above list of improvements.
>
Will do.
> ...
>
> > + int i;
> > + unsigned long long scan_mask;
> > + struct ams *ams = iio_priv(indio_dev);
>
> Reversed xmas tree order, please.
> Same for the rest.
>
Will do.
> ...
>
> > + /* Run calibration of PS & PL as part of the sequence */
> > + scan_mask = 0x1 | BIT(AMS_PS_SEQ_MAX);
>
> BIT(0) ?
>
Will fix.
> ...
>
> > + ams_update_intrmask(ams, ~0, ~0);
>
> Replace ~0 to proper GENMASK()./BIT() combination which takes into
> account real bits used by hardware.
>
Will fix.
> ...
>
> > + case IIO_CHAN_INFO_RAW:
> > + mutex_lock(&ams->lock);
> > + if (chan->scan_index >= (AMS_PS_SEQ_MAX * 3)) {
>
> Too many parens.
>
> > + ret = ams_read_vcc_reg(ams, chan->address, val);
> > + if (ret) {
> > + mutex_unlock(&ams->lock);
> > + return -EINVAL;
> > + }
> > + ams_enable_channel_sequence(indio_dev);
> > + } else if (chan->scan_index >= AMS_PS_SEQ_MAX)
> > + *val = readl(ams->pl_base + chan->address);
> > + else
> > + *val = readl(ams->ps_base + chan->address);
> > + mutex_unlock(&ams->lock);
> > +
> > + return IIO_VAL_INT;
>
> ...
>
> > + return -EINVAL;
>
> Use corresponding defaul cases in each of the switches.
>
Will do.
> ...
>
> > + int offset = 0;
>
> Make the assignment as an else branch, so all offset assignments will be
> grouped together.
>
> > + if (dir == IIO_EV_DIR_FALLING) {
> > + if (scan_index < AMS_SEQ_SUPPLY7)
> > + offset = AMS_ALARM_THRESHOLD_OFF_10;
> > + else
> > + offset = AMS_ALARM_THRESHOLD_OFF_20;
> > + }
>
> ...
>
> > + return 0;
>
> default case.
Will do.
>
> > +}
>
> ...
>
> > +static const struct iio_chan_spec
> > +*ams_event_to_channel(struct iio_dev *indio_dev, u32 event)
>
> Unusual indentation.
Will fix.
>
> ...
>
> > + case AMS_ALARM_BIT_TEMP_REMOTE:
> > + scan_index += AMS_SEQ_TEMP_REMOTE;
> > + break;
>
> default?
> Same for the rest of the cases like this.
Will add.
>
> ...
>
> > + return (ams->alarm_mask &
> > + ams_get_alarm_mask(chan->scan_index)) ? 1 : 0;
>
> !! would work as well.
>
> ...
>
> > + /*
> > + * The temperature channel only supports over-temperature
> > + * events
>
> Missed period.
>
> > + */
>
> ...
>
> > + /* only process alarms that are not masked */
>
> Inconsistent style (here capitalization is missed). Make all comments in the
> code consistent.
>
> > + isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) |
> > + ams->masked_alarm);
>
> > +
>
> Redundant blank line.
>
> > + if (!isr0)
>
> How did you test this branch? (Hint: something very important should be
> done here)
Missing spin_unlock.
>
> > + return IRQ_NONE;
>
> ...
>
> > + for_each_child_of_node(chan_node, child) {
> > + ret = of_property_read_u32(child, "reg", ®);
> > + if (ret || reg > (AMS_PL_MAX_EXT_CHANNEL + 30))
> > + continue;
> > +
> > + memcpy(&channels[num_channels], &ams_pl_channels[reg +
> > + AMS_PL_MAX_FIXED_CHANNEL - 30],
> > + sizeof(*channels));
> > +
> > + if (of_property_read_bool(child, "xlnx,bipolar"))
> > + channels[num_channels].scan_type.sign = 's';
> > +
> > + num_channels++;
> > + }
>
> Use device property API here instead of *of_*() calls.
>
> ...
>
> > + /* Initialize buffer for channel specification */
> > + ams_channels = kzalloc(sizeof(ams_ps_channels) +
> > + sizeof(ams_pl_channels) +
> > + sizeof(ams_ctrl_channels), GFP_KERNEL);
>
> Use the corresponding macro from overflow.h.
>
> > + if (!ams_channels)
> > + return -ENOMEM;
>
> ...
>
> > + if (of_device_is_available(np)) {
>
> fwnode_device_is_available()
Currently acpi is not supported with this driver. But I will add support in the next series of patches.
I don’t have a full understanding of ACPI and its interfaces. So would it be okay once the first iteration
gets checked in, I will add ACPI support on top.
>
> > + ret = ams_init_module(indio_dev, np, ams_channels);
> > + if (ret < 0)
> > + goto err;
> > +
> > + num_channels += ret;
> > + }
>
> ...
>
> > + for_each_child_of_node(np, child_node) {
> > + if (of_device_is_available(child_node)) {
> > + ret = ams_init_module(indio_dev, child_node,
> > + ams_channels + num_channels);
> > + if (ret < 0)
> > + goto err;
> > +
> > + num_channels += ret;
> > + }
> > + }
>
> As per above.
>
> ...
>
> > + if (!pdev->dev.of_node)
> > + return -ENODEV;
>
> Drop this, please. It will allow reuse of the driver in ACPI environments.
>
> ...
>
> > + ams->irq = platform_get_irq(pdev, 0);
> > + if (ams->irq == -EPROBE_DEFER) {
>
> Is IRQ optional or not?
Its not. I will add a generic handling.
>
> > + ret = -EPROBE_DEFER;
> > + return ret;
> > + }
>
> ...
>
> > + ret = devm_iio_device_register(&pdev->dev, indio_dev);
> > +
> > + return ret;
>
> return devm_...
Will do.
>
> ...
>
> > + clk_prepare_enable(ams->clk);
>
> It might fail.
Will add return
>
>
> --
> With Best Regards,
> Andy Shevchenko