RE: [PATCH v7 2/4] iio: adc: Add Xilinx AMS driver

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

 



Hey Jonathan,

Thanks for the review. 

> On Tue, 19 Oct 2021 16:20:46 +0100
> 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
> > 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>
> > Signed-off-by: Anand Ashok Dumbre <anand.ashok.dumbre@xxxxxxxxxx>
> 
> It would be good at some point to move away from of specific firmware
> property reading, but on a platform like this I guess you can be fairly sure no
> one will be using ACPI or other firmware description options so I'm not going
> to insist on it for an initial merge.
> 
> Other comment I have are fairly minor but we need to leave some time for
> other reviews and in particular DT binding review.

Sure. I will wait for the DT binding review before I send my fixes.

> 
> > ---
> >  drivers/iio/adc/Kconfig      |   13 +
> >  drivers/iio/adc/Makefile     |    1 +
> >  drivers/iio/adc/xilinx-ams.c | 1341
> > ++++++++++++++++++++++++++++++++++
> >  3 files changed, 1355 insertions(+)
> >  create mode 100644 drivers/iio/adc/xilinx-ams.c
> 
> ...
> 
> > diff --git a/drivers/iio/adc/xilinx-ams.c
> > b/drivers/iio/adc/xilinx-ams.c new file mode 100644 index
> > 000000000000..597cdda8a618
> > --- /dev/null
> > +++ b/drivers/iio/adc/xilinx-ams.c
> > @@ -0,0 +1,1341 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Xilinx AMS driver
> > + *
> > + *  Copyright (C) 2021 Xilinx, Inc.
> > + *
> > + *  Manish Narani <mnarani@xxxxxxxxxx>
> > + *  Rajnikant Bhojani <rajnikant.bhojani@xxxxxxxxxx>  */
> > +
> > +#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> for the of_device_id structure
> defintion.
> 
Will add it in the next series.

> > +#include <linux/of_address.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +
> > +#include <linux/iio/events.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +
> ...
> 
> > +/**
> > + * struct ams - Driver data for xilinx-ams
> > + * @base: physical base address of device
> > + * @ps_base: physical base address of PS device
> > + * @pl_base: physical base address of PL device
> > + * @clk: clocks associated with the device
> > + * @dev: pointer to device struct
> > + * @lock: to handle multiple user interaction
> > + * @intr_lock: to protect interrupt mask values
> > + * @masked_alarm: currently masked due to alarm
> > + * @alarm_mask: alarm configuration
> > + * @interrupt_mask: interrupt configuration
> > + * @irq: interrupt number of the sysmon
> > + * @ams_unmask_work: re-enables event once the event condition
> > +disappears
> > + *
> > + * This structure contains necessary state for Sysmon driver to
> > +operate  */ struct ams {
> > +	void __iomem *base;
> > +	void __iomem *ps_base;
> > +	void __iomem *pl_base;
> > +	struct clk *clk;
> > +	struct device *dev;
> > +	/* check kernel doc above */
> > +	struct mutex lock;
> > +	/* check kernel doc above */
> > +	spinlock_t intr_lock;
> > +	unsigned int alarm_mask;
> Docs should be same order as the fields.
> > +	unsigned int masked_alarm;
> > +	u64 intr_mask;
> 
> That's not the name in the docs.  Run kernel-doc script over this and fix all the
> errors / warnings.
> 
Will do it before I send next series.
Will fix the error.

> > +	int irq;
> > +	struct delayed_work ams_unmask_work; };
> > +
> 
> ...
> 
> > +
> > +static irqreturn_t ams_irq(int irq, void *data) {
> > +	struct iio_dev *indio_dev = data;
> > +	struct ams *ams = iio_priv(indio_dev);
> > +	u32 isr0;
> > +
> > +	spin_lock(&ams->intr_lock);
> > +
> > +	isr0 = readl(ams->base + AMS_ISR_0);
> > +
> > +	/* only process alarms that are not masked */
> > +	isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) |
> > +ams->masked_alarm);
> > +
> > +	if (!isr0)
> 
> lock held.

Will fix.

> 
> > +		return IRQ_NONE;
> > +
> > +	/* clear interrupt */
> > +	writel(isr0, ams->base + AMS_ISR_0);
> > +
> > +	/* Mask the alarm interrupts until cleared */
> > +	ams->masked_alarm |= isr0;
> > +	ams_update_intrmask(ams, 0, 0);
> > +
> > +	ams_handle_events(indio_dev, isr0);
> > +
> > +	schedule_delayed_work(&ams->ams_unmask_work,
> > +			      msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS));
> > +
> > +	spin_unlock(&ams->intr_lock);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> 
> ...
> 
> > +
> > +static int ams_parse_dt(struct iio_dev *indio_dev, struct
> > +platform_device *pdev) {
> > +	struct ams *ams = iio_priv(indio_dev);
> > +	struct iio_chan_spec *ams_channels, *dev_channels;
> > +	struct device_node *child_node = NULL, *np = pdev->dev.of_node;
> > +	int ret, vol_ch_cnt = 0, temp_ch_cnt = 0, i, rising_off, falling_off;
> > +	unsigned int num_channels = 0;
> > +
> > +	/* Initialize buffer for channel specification */
> > +	ams_channels = kzalloc(sizeof(ams_ps_channels) +
> > +			       sizeof(ams_pl_channels) +
> > +			       sizeof(ams_ctrl_channels), GFP_KERNEL);
> > +	if (!ams_channels)
> > +		return -ENOMEM;
> > +
> > +	if (of_device_is_available(np)) {
> > +		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;
> 
> for_each_child_of_node() holds a reference if we jump out of the loop
> before it terminates.
> https://elixir.bootlin.com/linux/latest/source/drivers/of/base.c#L715
> is where it ultimately releases that reference when the loop is terminating.
> Her you need to do it manually with an of_node_put() call
> 
Correct. Will fix this. 
> > +
> > +			num_channels += ret;
> > +		}
> > +	}
> > +
> > +	for (i = 0; i < num_channels; i++) {
> > +		if (ams_channels[i].type == IIO_VOLTAGE)
> > +			ams_channels[i].channel = vol_ch_cnt++;
> > +		else
> > +			ams_channels[i].channel = temp_ch_cnt++;
> > +
> > +		if (ams_channels[i].scan_index < (AMS_PS_SEQ_MAX * 3)) {
> > +			/* set threshold to max and min for each channel */
> > +			falling_off =
> > +
> 	ams_get_alarm_offset(ams_channels[i].scan_index,
> > +						     IIO_EV_DIR_FALLING);
> > +			rising_off =
> > +
> 	ams_get_alarm_offset(ams_channels[i].scan_index,
> > +						     IIO_EV_DIR_RISING);
> > +			if (ams_channels[i].scan_index >=
> AMS_PS_SEQ_MAX) {
> > +				writel(AMS_ALARM_THR_MIN,
> > +				       ams->pl_base + falling_off);
> > +				writel(AMS_ALARM_THR_MAX,
> > +				       ams->pl_base + rising_off);
> > +			} else {
> > +				writel(AMS_ALARM_THR_MIN,
> > +				       ams->ps_base + falling_off);
> > +				writel(AMS_ALARM_THR_MAX,
> > +				       ams->ps_base + rising_off);
> > +			}
> > +		}
> > +	}
> > +
> > +	dev_channels = devm_kzalloc(&pdev->dev, sizeof(*dev_channels) *
> > +				    num_channels, GFP_KERNEL);
> > +	if (!dev_channels) {
> > +		ret = -ENOMEM;
> > +		goto err;
> > +	}
> 
> We now have the option of devm_krealloc()   If you used that in conjunction
> with devm_kzalloc to replace the kzalloc above, you could avoid this need to
> copy.  Not important though if you prefer doing this manual version.
> 
For now I will leave this as is. But will update after initial check-in.

> > +
> > +	memcpy(dev_channels, ams_channels,
> > +	       sizeof(*ams_channels) * num_channels);
> > +	indio_dev->channels = dev_channels;
> > +	indio_dev->num_channels = num_channels;
> > +
> > +	ret = 0;
> > +err:
> > +	kfree(ams_channels);
> > +
> > +	return ret;
> > +}
> > +
> 
> ...
> 
> > +static int ams_probe(struct platform_device *pdev) {
> > +	struct iio_dev *indio_dev;
> > +	struct ams *ams;
> > +	int ret;
> > +
> > +	if (!pdev->dev.of_node)
> > +		return -ENODEV;
> > +
> > +	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ams));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	ams = iio_priv(indio_dev);
> > +	mutex_init(&ams->lock);
> > +	spin_lock_init(&ams->intr_lock);
> > +
> > +	indio_dev->name = "xilinx-ams";
> > +
> > +	indio_dev->info = &iio_ams_info;
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > +	ams->base = devm_platform_ioremap_resource(pdev, 0);
> > +	if (IS_ERR(ams->base))
> > +		return PTR_ERR(ams->base);
> > +
> > +	ams->clk = devm_clk_get(&pdev->dev, NULL);
> > +	if (IS_ERR(ams->clk))
> > +		return PTR_ERR(ams->clk);
> > +	clk_prepare_enable(ams->clk);
> > +	devm_add_action_or_reset(&pdev->dev,
> ams_clk_disable_unprepare,
> > +				 ams->clk);
> > +
> > +	INIT_DELAYED_WORK(&ams->ams_unmask_work,
> ams_unmask_worker);
> > +	devm_add_action_or_reset(&pdev->dev,
> ams_cancel_delayed_work,
> > +				 &ams->ams_unmask_work);
> > +
> > +	ret = ams_init_device(ams);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "failed to initialize AMS\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = ams_parse_dt(indio_dev, pdev);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "failure in parsing DT\n");
> > +		return ret;
> > +	}
> > +
> > +	ams_enable_channel_sequence(indio_dev);
> > +
> > +	ams->irq = platform_get_irq(pdev, 0);
> > +	if (ams->irq == -EPROBE_DEFER) {
> > +		ret = -EPROBE_DEFER;
> > +		return ret;
> > +	}
> > +
> > +	ret = devm_request_irq(&pdev->dev, ams->irq, &ams_irq, 0, "ams-
> irq",
> > +			       indio_dev);
> > +	if (ret < 0) {
> > +		dev_err(&pdev->dev, "failed to register interrupt\n");
> > +		return ret;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, indio_dev);
> > +
> > +	ret = devm_iio_device_register(&pdev->dev, indio_dev);
> > +
> 
> return devm_...
> 
Will do.

> > +	return ret;
> > +}
> > +
> 

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