Received: by 2002:a05:6a10:f3d0:0:0:0:0 with SMTP id a16csp516404pxv; Thu, 1 Jul 2021 03:29:45 -0700 (PDT) X-Google-Smtp-Source: ABdhPJwPr+Wm+enW8yniLgiuteYCRPi6bLPqzxozFOWNJJMs+DsNoVTtERpX1Xx8mCJkMkLlvtMc X-Received: by 2002:a05:6e02:50c:: with SMTP id d12mr17120610ils.84.1625135384843; Thu, 01 Jul 2021 03:29:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1625135384; cv=none; d=google.com; s=arc-20160816; b=uhhoh8mWki7hXwQdn50RljYGB4G6UzDVHiSwr0Fm6sJwckKFLjKg7v5Ytq43S7a38J X8hW1nCTgKUF/s7yIJglKolpxKmMbU0RRvIZMYjAMaPYHRXUOF1NcfBZCj7+LrGedsbY wpBzpvpjXdx6LC+hjHyx77YItFl9U/eK/cTsPcwmPEGXyn7Zf8ahqQjFN1CxVDKWyDU2 7MC4O/OhjVm8vOJg+TI6I1sCF0WlzINfVM0b9ItPPpaAEsdslEMd50Azxw+amiQjzRXD uxvz+/xgMhVY7Z1SZizxxAv/Bce0X0i7vYuE1t39pF8pBa0+RoXxToUNDNeC9j0f3AQ4 zTKg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:dkim-signature; bh=0caUULNTjSFDAAGzN8loGN4ei/UA/fVndlI4l9KRnKY=; b=N30kzaUUNDmgRhcgm5Emr1KdeOBb1lHgY7nuDkKdZgBWxOjre6uGINfwZh9uCcm5H0 pzPNMZ8WSQ0q86s1vgEqZvqYLpLiEnQBVrsXO8tefSk5yd5bUi0cOEBeTop0ktBpW1za sgc6hM+dXdlNf6TtWvbevVzBB321FM5VwvRmBHbpF71/44QTGO4i9spxoP3Ys+9vJX1w OdczPwr0bIXs/CmFReOkv67GRuz1IZZUgy2S/SNDvJK9tR2u1kJHSdrGogMtwDkcmRgI e5QDZMkaytd2N8D5u6e0Z+St3M9Cgo6UbPWhbRis6Wbo5QL1ivXQEOGZWtD+kI3YOsSI /NxQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=fnINjeOH; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id j16si24137361ioo.11.2021.07.01.03.29.31; Thu, 01 Jul 2021 03:29:44 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20161025 header.b=fnINjeOH; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S236051AbhGAKbO (ORCPT + 99 others); Thu, 1 Jul 2021 06:31:14 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:36088 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229878AbhGAKbN (ORCPT ); Thu, 1 Jul 2021 06:31:13 -0400 Received: from mail-pl1-x631.google.com (mail-pl1-x631.google.com [IPv6:2607:f8b0:4864:20::631]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 5E6FBC061756; Thu, 1 Jul 2021 03:28:43 -0700 (PDT) Received: by mail-pl1-x631.google.com with SMTP id b5so3403971plg.2; Thu, 01 Jul 2021 03:28:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:references:in-reply-to:from:date:message-id:subject:to :cc; bh=0caUULNTjSFDAAGzN8loGN4ei/UA/fVndlI4l9KRnKY=; b=fnINjeOHDM9JyVVConsy/yhXTSe6XoPcVJJL6Etkj8PvzLJShKUL5y7Vo5/vx5grPt QhYsWUAqA6EuuxDZCJSXLNLIfgQLvsCRBAEnfaU42fwI+JY73E6xnh+hfC8E3z/YHonk zLwwcdZLYZh1SDeShMMaE0H9ZJ6PC7OUnRQfv4VqEqnw8sEsI1Lpy8dOG2OPCv3tohBF +DWIGRrIKIInNgth0YOR0I3hPSjs2Q/TmJjqd4gLJ66CO+k6m+3Wzqm4/rFFJrNQ0NxN clHxHii+fuR1RGijtIPn5kH70VR/IgD+6fuRLNwfbYT0E422F14Jb9PZl4OP1/JyTCqD p45w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:references:in-reply-to:from:date :message-id:subject:to:cc; bh=0caUULNTjSFDAAGzN8loGN4ei/UA/fVndlI4l9KRnKY=; b=ipfMALAEmwnhl0Y8oe9UpOHE/ux/INh+cBftoAd9IryIhci5tyommrLVBFEm87U2gv INg7NBDm5pnUPyi1utL4U1foUrn+eMXPAiTiZ+tByLc/x1N0E52dQ5echn6fEoSNAU2B 6CCYdIUtcReAUXRBRvpHjzbcLQ3S5/hFwWhxrVi1V4dXF+ILtVjgCSWUvMm4+Ki03CfC qfU30H3GdeNlHFkKv3RoxAacQtCs2YDmWwLFitPlFI1p1CuZv81NW3POTHZV2j74TFkz yeSNocJ+8wqXMhnvIuaL3PURJGCc6z9i6gUwVuCFuNhvwr8Fd+KzwjQN/XNkexpyEqGX DOgg== X-Gm-Message-State: AOAM532PwcQzBDSebtl0BFUtg2sZL/TB2yuDHcXhShopwYnHIlIUxGPa deS2x7SSeE/faYsnW4tFZeHZiGQfVSRvPUdbEoc= X-Received: by 2002:a17:90a:f2c2:: with SMTP id gt2mr18505539pjb.86.1625135322350; Thu, 01 Jul 2021 03:28:42 -0700 (PDT) MIME-Version: 1.0 References: <20210629220328.13366-1-prabhakar.mahadev-lad.rj@bp.renesas.com> <20210629220328.13366-3-prabhakar.mahadev-lad.rj@bp.renesas.com> In-Reply-To: <20210629220328.13366-3-prabhakar.mahadev-lad.rj@bp.renesas.com> From: Alexandru Ardelean Date: Thu, 1 Jul 2021 13:28:31 +0300 Message-ID: Subject: Re: [PATCH 2/2] iio: adc: Add driver for Renesas RZ/G2L A/D converter To: Lad Prabhakar Cc: Geert Uytterhoeven , Rob Herring , Jonathan Cameron , Lars-Peter Clausen , Philipp Zabel , linux-iio , devicetree , LKML , linux-renesas-soc@vger.kernel.org, Prabhakar , Biju Das Content-Type: text/plain; charset="UTF-8" Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, Jun 30, 2021 at 1:07 AM Lad Prabhakar wrote: > > Add ADC driver support for Renesas RZ/G2L A/D converter in SW > trigger mode. > > A/D Converter block is a successive approximation analog-to-digital > converter with a 12-bit accuracy and supports a maximum of 8 input > channels. Hey, Some comments inline. > > Signed-off-by: Lad Prabhakar > Reviewed-by: Biju Das > --- > MAINTAINERS | 8 + > drivers/iio/adc/Kconfig | 10 + > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/rzg2l_adc.c | 489 ++++++++++++++++++++++++++++++++++++ > 4 files changed, 508 insertions(+) > create mode 100644 drivers/iio/adc/rzg2l_adc.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 81e1edeceae4..bee4c3847e01 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -15622,6 +15622,14 @@ L: linux-renesas-soc@vger.kernel.org > S: Maintained > F: drivers/phy/renesas/phy-rcar-gen3-usb*.c > > +RENESAS RZ/G2L A/D DRIVER > +M: Lad Prabhakar > +L: linux-iio@vger.kernel.org > +L: linux-renesas-soc@vger.kernel.org > +S: Supported > +F: Documentation/devicetree/bindings/iio/adc/renesas,rzg2l-adc.yaml > +F: drivers/iio/adc/rzg2l_adc.c > + > RESET CONTROLLER FRAMEWORK > M: Philipp Zabel > S: Maintained > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index c7946c439612..9408cbf97acc 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -887,6 +887,16 @@ config ROCKCHIP_SARADC > To compile this driver as a module, choose M here: the > module will be called rockchip_saradc. > > +config RZG2L_ADC > + tristate "Renesas RZ/G2L ADC driver" > + depends on ARCH_R9A07G044 || COMPILE_TEST > + help > + Say yes here to build support for the ADC found in Renesas > + RZ/G2L family. > + > + To compile this driver as a module, choose M here: the > + module will be called rzg2l_adc. > + > config SC27XX_ADC > tristate "Spreadtrum SC27xx series PMICs ADC" > depends on MFD_SC27XX_PMIC || COMPILE_TEST > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index a226657d19c0..d92bcc9c5fbb 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -82,6 +82,7 @@ obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o > obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o > obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o > obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o > +obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o > obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o > obj-$(CONFIG_SPEAR_ADC) += spear_adc.o > obj-$(CONFIG_STX104) += stx104.o > diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c > new file mode 100644 > index 000000000000..1c58eb8ae1ec > --- /dev/null > +++ b/drivers/iio/adc/rzg2l_adc.c > @@ -0,0 +1,489 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * RZ/G2L A/D Converter driver > + * > + * Copyright (c) 2021 Renesas Electronics Europe GmbH > + * > + * Author: Lad Prabhakar > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define ADM(n) ((n) * 0x4) > +#define ADM0_ADCE BIT(0) > +#define ADM0_ADBSY BIT(1) > +#define ADM0_PWDWNB BIT(2) > +#define ADM0_SRESB BIT(15) > +#define ADM1_TRG BIT(0) > +#define ADM1_MS BIT(2) > +#define ADM1_BS BIT(4) > +#define ADM1_EGA_CLEAR ~GENMASK(13, 12) > +#define ADM2_CHSEL_CLEAR ~GENMASK(7, 0) > +#define ADM3_ADSMP 0x578 > +#define ADM3_ADCMP (0xe << 16) > +#define ADM3_ADIL_CLEAR ~GENMASK(31, 24) > + > +#define ADINT 0x20 > +#define ADINT_CH_CLEAR ~GENMASK(7, 0) > +#define ADINT_CSEEN BIT(16) > +#define ADINT_INTS BIT(31) > +#define ADSTS 0x24 > +#define ADINT_INTST_MASK GENMASK(7, 0) > +#define ADSTS_CSEST BIT(16) > +#define ADIVC 0x28 > +#define ADIVC_DIVADC_CLEAR ~GENMASK(8, 0) > +#define ADIVC_DIVADC_4 0x4 > +#define ADFIL 0x2c > +#define ADCR(n) (0x30 + ((n) * 0x4)) > +#define ADCR_AD_MASK GENMASK(11, 0) > + > +#define ADC_MAX_CHANNELS 8 > +#define ADC_CHN_MASK 0x7 > +#define ADC_TIMEOUT usecs_to_jiffies(1 * 4) > + > +enum trigger_mode { > + SW_TRIGGER = 0, > + SYNC_TRIGGER, > + ASYNC_TRIGGER, > +}; this enum could also be removed [for now] given that only SW_TRIGGER is supported; > + > +struct rzg2l_adc_data { > + const struct iio_chan_spec *channels; > + u8 num_channels; > + u8 trigger; > +}; > + > +struct rzg2l_adc { > + void __iomem *base; > + struct clk *pclk; > + struct clk *adclk; > + struct reset_control *presetn; > + struct reset_control *adrstn; > + struct completion completion; > + const struct rzg2l_adc_data *data; > + bool adc_disabled; /* protected with mlock mutex from indio_dev */ this adc_disabled flag looks a bit weird; it seems to guard against this driver being removed to prevent some reads. technically, this should be protected by IIO core; so the flag itself (or how it is being used) looks like it doesn't do much; > + u16 last_val[ADC_MAX_CHANNELS]; > +}; > + > +static unsigned int rzg2l_adc_readl(struct rzg2l_adc *adc, u32 reg) > +{ > + return readl(adc->base + reg); > +} > + > +static void rzg2l_adc_writel(struct rzg2l_adc *adc, unsigned int reg, u32 val) > +{ > + writel(val, adc->base + reg); > +} > + > +static int rzg2l_adc_adclk(struct rzg2l_adc *adc, bool prepare) > +{ > + if (prepare) > + return clk_prepare_enable(adc->adclk); > + > + clk_disable_unprepare(adc->adclk); > + return 0; > +} > + > +static void rzg2l_adc_pwr(struct rzg2l_adc *adc, bool on) > +{ > + u32 reg; > + > + reg = rzg2l_adc_readl(adc, ADM(0)); > + if (on) > + reg |= ADM0_PWDWNB; > + else > + reg &= ~ADM0_PWDWNB; > + rzg2l_adc_writel(adc, ADM(0), reg); > + udelay(2); > +} > + > +static void rzg2l_adc_conversion(struct rzg2l_adc *adc, bool start) > +{ > + int timeout = 5; > + u32 reg; > + > + /* stop A/D conversion */ > + reg = rzg2l_adc_readl(adc, ADM(0)); > + if (start) > + reg |= ADM0_ADCE; > + else > + reg &= ~ADM0_ADCE; > + rzg2l_adc_writel(adc, ADM(0), reg); > + > + if (start) > + return; > + > + do { > + usleep_range(100, 200); > + reg = rzg2l_adc_readl(adc, ADM(0)); > + timeout--; > + if (!timeout) { > + pr_err("%s stopping ADC timed out\n", __func__); > + break; > + } > + } while (((reg & ADM0_ADBSY) || (reg & ADM0_ADCE))); > +} > + > +static int rzg2l_adc_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long mask) > +{ > + struct rzg2l_adc *adc = iio_priv(indio_dev); > + u32 reg; > + int ret; > + u8 ch; > + > + switch (mask) { > + case IIO_CHAN_INFO_RAW: > + mutex_lock(&indio_dev->mlock); [1] acquiring indio_dev->mlock directly is discouraged; this lock is reserved for IIO core logic and will be moved into an iio_dev_opaque struct eventually; driver state locks should be defined in struct rzg2l_adc and managed by the driver to protect it's own internal state; > + > + if (adc->adc_disabled) { > + mutex_unlock(&indio_dev->mlock); > + return -EBUSY; > + } > + > + if (rzg2l_adc_readl(adc, ADM(0)) & ADM0_ADBSY) { > + mutex_unlock(&indio_dev->mlock); > + return -EBUSY; > + } > + > + ch = chan->channel & ADC_CHN_MASK; > + /* SW trigger */ > + reg = rzg2l_adc_readl(adc, ADM(1)); > + reg &= ADM1_EGA_CLEAR; > + reg &= ~ADM1_BS; > + reg |= ADM1_MS; > + reg &= ~ADM1_TRG; > + rzg2l_adc_writel(adc, ADM(1), reg); > + > + /* select channel */ > + reg = rzg2l_adc_readl(adc, ADM(2)); > + reg &= ADM2_CHSEL_CLEAR; > + reg |= BIT(ch); > + rzg2l_adc_writel(adc, ADM(2), reg); > + > + reg = rzg2l_adc_readl(adc, ADM(3)); > + reg &= ADM3_ADIL_CLEAR; > + reg |= ADM3_ADCMP; > + reg |= ADM3_ADSMP; > + rzg2l_adc_writel(adc, ADM(3), reg); > + > + reg = rzg2l_adc_readl(adc, ADIVC); > + reg &= ADIVC_DIVADC_CLEAR; > + reg |= ADIVC_DIVADC_4; > + rzg2l_adc_writel(adc, ADIVC, reg); > + > + reg = rzg2l_adc_readl(adc, ADINT); > + reg &= ~ADINT_INTS; > + reg &= ADINT_CH_CLEAR; > + reg |= ADINT_CSEEN; > + reg |= BIT(ch); > + rzg2l_adc_writel(adc, ADINT, reg); > + > + rzg2l_adc_pwr(adc, true); should all this clock & power management be done in this read function? it looks like an awful lot just to perform a single read maybe some PM suspend/resume hooks would be a better idea for these; > + > + ret = rzg2l_adc_adclk(adc, true); > + if (ret) { > + rzg2l_adc_pwr(adc, false); > + mutex_unlock(&indio_dev->mlock); > + return -EINVAL; > + } > + > + reinit_completion(&adc->completion); > + > + rzg2l_adc_conversion(adc, true); > + > + if (!wait_for_completion_timeout(&adc->completion, ADC_TIMEOUT)) { > + reg &= ADINT_CH_CLEAR; > + rzg2l_adc_writel(adc, ADINT, reg); > + rzg2l_adc_conversion(adc, false); > + rzg2l_adc_adclk(adc, false); > + rzg2l_adc_pwr(adc, false); > + mutex_unlock(&indio_dev->mlock); > + return -ETIMEDOUT; > + } > + > + *val = adc->last_val[ch]; > + rzg2l_adc_conversion(adc, false); > + rzg2l_adc_adclk(adc, false); > + rzg2l_adc_pwr(adc, false); > + mutex_unlock(&indio_dev->mlock); > + return IIO_VAL_INT; > + > + default: > + return -EINVAL; > + } > +} > + > +static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) > +{ > + struct rzg2l_adc *adc = (struct rzg2l_adc *)dev_id; > + u8 intst; > + u32 reg; > + u8 i; > + > + reg = rzg2l_adc_readl(adc, ADSTS); > + if (reg & ADSTS_CSEST) { > + rzg2l_adc_writel(adc, ADSTS, reg); > + return IRQ_HANDLED; > + } > + > + intst = reg & ADINT_INTST_MASK; > + if (!intst) > + return IRQ_HANDLED; > + > + for (i = 0; i < ADC_MAX_CHANNELS; i++) { > + if (intst & BIT(i)) > + adc->last_val[i] = rzg2l_adc_readl(adc, ADCR(i)) & ADCR_AD_MASK; > + } > + > + rzg2l_adc_writel(adc, ADSTS, reg); > + > + complete(&adc->completion); > + > + return IRQ_HANDLED; > +} > + > +static const struct iio_info rzg2l_adc_iio_info = { > + .read_raw = rzg2l_adc_read_raw, > +}; > + > +static const char * const rzg2l_adc_channel_name[] = { > + "adc0", > + "adc1", > + "adc2", > + "adc3", > + "adc4", > + "adc5", > + "adc6", > + "adc7", > +}; > + > +static int rzg2l_adc_parse_of(struct platform_device *pdev, struct rzg2l_adc *adc) > +{ > + struct device_node *node = pdev->dev.of_node; > + struct iio_chan_spec *chan_array; > + u8 channels[ADC_MAX_CHANNELS]; > + struct rzg2l_adc_data *data; > + int num_channels; > + int ret; > + u8 i; > + > + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + num_channels = of_property_count_u8_elems(node, "renesas-rzg2l,adc-channels"); > + if (num_channels <= 0 || num_channels > ADC_MAX_CHANNELS) > + return -EINVAL; > + > + ret = of_property_read_u8_array(node, "renesas-rzg2l,adc-channels", > + channels, num_channels); > + if (ret) > + return ret; > + > + chan_array = devm_kcalloc(&pdev->dev, num_channels, sizeof(*chan_array), > + GFP_KERNEL); > + if (!chan_array) > + return -ENOMEM; > + > + for (i = 0; i < num_channels; i++) { > + chan_array[i].type = IIO_VOLTAGE; > + chan_array[i].indexed = 1; > + chan_array[i].channel = channels[i]; > + chan_array[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW); > + chan_array[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE); > + chan_array[i].datasheet_name = rzg2l_adc_channel_name[i]; > + } > + > + ret = of_property_read_u8(node, "renesas-rzg2l,adc-trigger-mode", > + &data->trigger); > + if (ret) > + data->trigger = SW_TRIGGER; > + > + /* we support SW_TRIGGER as of now */ > + if (data->trigger != SW_TRIGGER) > + return -EINVAL; it would be an idea to remove this data->trigger field and the DT read for this property and add it when it's supported; typically these triggers don't get configured via DT; > + > + data->num_channels = num_channels; > + data->channels = chan_array; > + adc->data = data; > + > + return 0; > +} > + > +static int rzg2l_adc_sw_reset(struct rzg2l_adc *adc) > +{ > + int timeout = 5; > + u32 val; > + > + val = rzg2l_adc_readl(adc, ADM(0)); > + val |= ADM0_SRESB; > + rzg2l_adc_writel(adc, ADM(0), val); > + > + while (!(rzg2l_adc_readl(adc, ADM(0)) & ADM0_SRESB)) { > + if (!timeout) > + return -EINVAL; maybe -EBUSY is a bit better error code; > + timeout--; > + usleep_range(100, 200); > + } > + > + return 0; > +} > + > +static int rzg2l_adc_probe(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev; > + struct rzg2l_adc *adc; > + int ret; > + int irq; > + > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc)); > + if (!indio_dev) { > + dev_err(&pdev->dev, "failed allocating iio device\n"); this message can be removed; looks like log spam; and if it happens, the system will be in a pretty bad state anyway > + return -ENOMEM; > + } > + > + adc = iio_priv(indio_dev); > + if (!adc) > + return -ENOMEM; this check is redundant; if indio_dev is non-NULL then iio_priv() will be good as well; > + > + ret = rzg2l_adc_parse_of(pdev, adc); > + if (ret) > + return -ENOMEM; > + > + adc->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(adc->base)) { > + dev_err(&pdev->dev, "missing mem resource"); this message can be removed; looks like log-spam > + return PTR_ERR(adc->base); > + } > + > + irq = platform_get_irq(pdev, 0); > + if (irq < 0) { > + dev_err(&pdev->dev, "no irq resource\n"); > + return irq; > + } > + > + adc->pclk = devm_clk_get(&pdev->dev, "pclk"); > + if (IS_ERR(adc->pclk)) { > + dev_err(&pdev->dev, "Failed to get pclk"); > + return PTR_ERR(adc->pclk); > + } > + > + adc->adclk = devm_clk_get(&pdev->dev, "adclk"); > + if (IS_ERR(adc->adclk)) { > + dev_err(&pdev->dev, "Failed to get adclk"); > + return PTR_ERR(adc->adclk); > + } > + > + adc->adrstn = devm_reset_control_get_exclusive(&pdev->dev, "adrst-n"); > + if (IS_ERR(adc->adrstn)) { > + dev_err(&pdev->dev, "failed to get adrstn\n"); > + return PTR_ERR(adc->adrstn); > + } > + > + adc->presetn = devm_reset_control_get_exclusive(&pdev->dev, "presetn"); > + if (IS_ERR(adc->presetn)) { > + dev_err(&pdev->dev, "failed to get presetn\n"); > + return PTR_ERR(adc->presetn); > + } > + > + ret = reset_control_deassert(adc->adrstn); > + if (ret) > + return ret; > + > + ret = reset_control_deassert(adc->presetn); > + if (ret) > + goto assert_adrstn; > + > + ret = clk_prepare_enable(adc->pclk); > + if (ret) > + goto assert_presetn; > + > + ret = rzg2l_adc_sw_reset(adc); > + if (ret) > + goto unprepare_pclk; > + > + init_completion(&adc->completion); > + > + platform_set_drvdata(pdev, indio_dev); > + > + ret = devm_request_irq(&pdev->dev, irq, rzg2l_adc_isr, > + 0, dev_name(&pdev->dev), adc); > + if (ret < 0) > + goto unprepare_pclk; > + > + adc->adc_disabled = false; > + indio_dev->name = dev_name(&pdev->dev); indio_dev->name should be the part-name; since this driver supports a single part, this can be: indio_dev->name = "rzg2l-adc"; > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->dev.of_node = pdev->dev.of_node; The 2 assignments above can be removed in the mainline driver. They should be done in devm_iio_device_alloc() and iio_device_register() > + indio_dev->info = &rzg2l_adc_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + indio_dev->channels = adc->data->channels; > + indio_dev->num_channels = adc->data->num_channels; > + > + ret = iio_device_register(indio_dev); > + if (ret) > + goto unprepare_pclk; > + > + return 0; > + > +unprepare_pclk: > + clk_disable_unprepare(adc->pclk); > +assert_presetn: > + reset_control_assert(adc->presetn); > +assert_adrstn: > + reset_control_assert(adc->adrstn); > + return ret; > +} > + > +static int rzg2l_adc_remove(struct platform_device *pdev) > +{ > + struct iio_dev *indio_dev = platform_get_drvdata(pdev); > + struct rzg2l_adc *adc = iio_priv(indio_dev); > + > + mutex_lock(&indio_dev->mlock); > + adc->adc_disabled = true; > + mutex_unlock(&indio_dev->mlock); > + > + iio_device_unregister(indio_dev); > + > + clk_disable_unprepare(adc->pclk); > + reset_control_assert(adc->presetn); > + reset_control_assert(adc->adrstn); > + > + return 0; > +} > + > +static const struct of_device_id rzg2l_adc_match[] = { > + { > + .compatible = "renesas,rzg2l-adc", > + }, > + {}, comma can be removed; since this is a null terminator > +}; > +MODULE_DEVICE_TABLE(of, rzg2l_adc_match); > + > +static struct platform_driver rzg2l_adc_driver = { > + .probe = rzg2l_adc_probe, > + .remove = rzg2l_adc_remove, > + .driver = { > + .name = "rzg2l-adc", > + .of_match_table = rzg2l_adc_match, > + }, > +}; > + > +module_platform_driver(rzg2l_adc_driver); > + > +MODULE_AUTHOR("Lad Prabhakar "); > +MODULE_DESCRIPTION("Renesas RZ/G2L ADC driver"); > +MODULE_LICENSE("GPL v2"); > -- > 2.17.1 >