Message ID | 20230323194550.1914725-1-Naresh.Solanki@9elements.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:604a:0:0:0:0:0 with SMTP id j10csp3118293wrt; Thu, 23 Mar 2023 13:27:49 -0700 (PDT) X-Google-Smtp-Source: AKy350bvCjpXGNgolctPxyg1mCDVj2VWP3r5l1Rf+K160FO9lCwpnwW2DCUhFTBKoDY2yQWnqGTf X-Received: by 2002:a17:903:244e:b0:1a1:bbcd:917e with SMTP id l14-20020a170903244e00b001a1bbcd917emr273466pls.10.1679603269075; Thu, 23 Mar 2023 13:27:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1679603269; cv=none; d=google.com; s=arc-20160816; b=y5ey+oOO91LC1z02kajUPYzb6pgWeONbe0SLiTYqBPPKrXnvuEFP4269lJWSRKn5EE fo2tkxhmyEYf10LQ50kKtdzbahjuY3mwN4uz/GUQb0OelXVCzgHrVIR1jAaz6ylnUtic Ml9G82J7WG2DJq01NVS2sGhhOczqWTnO7M2Vew3lYUqvktPj3Ij6Hs2KXCcZ6HC1bM7o Qh1pzMm4nrv7sF7R3GjkyC84FxdW/9KlHY5txhaucN3eYod/S1KuiPrU/d1kpoQjNnwo 8UVGDnPiL+4Q2XFqqwN/y6drUnwV0SR/N0beJmurquMyWOxog1a7NDRXkFBAbZ8JDUvI g2iA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :message-id:date:subject:cc:to:from:dkim-signature; bh=LQ1nJXr1Ls+1dU6XKA8MmxhSLFVWreUpD8IWDUhzFhY=; b=uCjzbvxundZJh1VZBf3xUh0w7IDvr696Nw7hSXd4xS4pQJyAXGftnWSIiryimJGdkL GULGgrjaXKxj3Ar2lZmCbOVKm9/1hWsLtysHJfwheH84jL7sFKJOQWAazIeeK7HazWfx GKwnoAdyey4QMZGGMFPotNaQ8cU4nUNsLgQvwtWPY4tB52NA6jLajbllLls3UEsy6tD/ VxGJIuagIbe2aMmSIsYQZPS6B7Vsf2emlW4clAO3kgqAj2wYwNjIy7ARpfCZVmjVsPxL aRXMnXNP2yLmKrtKMB08u9MmqID3oCDbCkkqzO2cI+OS2oFXnYdxFP9VFvfqKQQn83Ne nnow== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@9elements.com header.s=google header.b=MrXgf3wl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=9elements.com Received: from out1.vger.email (out1.vger.email. [2620:137:e000::1:20]) by mx.google.com with ESMTP id bg6-20020a1709028e8600b001a0468c4e3dsi17881612plb.137.2023.03.23.13.27.36; Thu, 23 Mar 2023 13:27:49 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) client-ip=2620:137:e000::1:20; Authentication-Results: mx.google.com; dkim=pass header.i=@9elements.com header.s=google header.b=MrXgf3wl; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:20 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=9elements.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231319AbjCWTp7 (ORCPT <rfc822;ezelljr.billy@gmail.com> + 99 others); Thu, 23 Mar 2023 15:45:59 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50676 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229734AbjCWTp4 (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Thu, 23 Mar 2023 15:45:56 -0400 Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com [IPv6:2a00:1450:4864:20::433]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A7F7D24713 for <linux-kernel@vger.kernel.org>; Thu, 23 Mar 2023 12:45:54 -0700 (PDT) Received: by mail-wr1-x433.google.com with SMTP id l12so21765000wrm.10 for <linux-kernel@vger.kernel.org>; Thu, 23 Mar 2023 12:45:54 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=9elements.com; s=google; t=1679600753; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=LQ1nJXr1Ls+1dU6XKA8MmxhSLFVWreUpD8IWDUhzFhY=; b=MrXgf3wlMHGA+NOsifBp9w7Ns+rO6KX/55+LtSllE7Vp5KcFOG2qMazmy66L67akqP n3atKBeRnZ/FAkDp5RuUG2D4TVbaHKkBoWhAaCr96CxlrNxaZIzbvU7yhIHq0xHJNyuR J1JsbDwdLyeyWkgd2nE8Bqeh5mUYry+91pEpm3JIfaFgXM+tX+RcIUXt/idZROwQqZ90 LO/lJGPZhAv9LX2bLAYn2rR5ARBYUlifkH1UEhEUsSAxw4LZXGLTZIFZ8MIi4s7SVm3m 5WtW1OfHzCHHALYdjhMkLVpdVNBt1LY1RTGBTACMzKHXlOh9LyQI+2Mk0Xk52/+HT5YB nv3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; t=1679600753; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=LQ1nJXr1Ls+1dU6XKA8MmxhSLFVWreUpD8IWDUhzFhY=; b=WJxU2dvsq8LMXK5xlByM/MxPUYEwtPnGoa7girv0xdBrHTYUK6D/jYV3mPnNcI/A6s AFx2YswZ1UHtLhugmXiy+8zVjEb7PkrsfnivQfLFGwS8DVdFIJCiDhAlm6gOfzGzjt4X hasxavJFXP8pZsxHescy8FEkBcK/5nuOgM9fVWrhZ0vsor+Z47p8c2/K+BmsbmjXUii3 o98D7iHCNQATSLnNN0/Som8Qi9jZRS2P9ZCbiydf8OGGsTWHMFl6hTojQ7ZkattNaCTG yThYE4f5SfHFQfDZmiEVsKrvrSVczAY0rO9KKyqwOQPp8ErJlCy37TVZJiYkY4YEUGwU xW7w== X-Gm-Message-State: AAQBX9eTUOF55y8zxUT1w5wm8yk+zGuOf839vthLj54o8j2ODls5tFzs O9ijARPUCD7iNeW+FnqRlZFz1w== X-Received: by 2002:a5d:5687:0:b0:2c5:518a:f6e0 with SMTP id f7-20020a5d5687000000b002c5518af6e0mr212822wrv.6.1679600753132; Thu, 23 Mar 2023 12:45:53 -0700 (PDT) Received: from stroh80.sec.9e.network (ip-078-094-000-051.um19.pools.vodafone-ip.de. [78.94.0.51]) by smtp.gmail.com with ESMTPSA id s17-20020a5d4251000000b002d1801018e2sm16829348wrr.63.2023.03.23.12.45.52 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 23 Mar 2023 12:45:52 -0700 (PDT) From: Naresh Solanki <naresh.solanki@9elements.com> X-Google-Original-From: Naresh Solanki <Naresh.Solanki@9elements.com> To: Lee Jones <lee@kernel.org>, Jonathan Cameron <jic23@kernel.org>, Lars-Peter Clausen <lars@metafoo.de> Cc: Patrick Rudolph <patrick.rudolph@9elements.com>, Naresh Solanki <Naresh.Solanki@9elements.com>, linux-kernel@vger.kernel.org, linux-iio@vger.kernel.org Subject: [PATCH v2 1/2] iio: max597x: Add support for max597x Date: Thu, 23 Mar 2023 20:45:48 +0100 Message-Id: <20230323194550.1914725-1-Naresh.Solanki@9elements.com> X-Mailer: git-send-email 2.39.1 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.2 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,DKIM_VALID_EF,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1761073141459800408?= X-GMAIL-MSGID: =?utf-8?q?1761191677349245627?= |
Series |
[v2,1/2] iio: max597x: Add support for max597x
|
|
Commit Message
Naresh Solanki
March 23, 2023, 7:45 p.m. UTC
From: Patrick Rudolph <patrick.rudolph@9elements.com> max597x has 10bit ADC for voltage & current monitoring. Use iio framework to expose the same in sysfs. Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com> ... Changes in V2: - Remove fallthrough - Use pdev->dev instead of i2c->dev - Init indio_dev->name based on device type. --- drivers/iio/adc/Kconfig | 15 ++++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max597x-iio.c | 152 ++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 drivers/iio/adc/max597x-iio.c base-commit: 368eb79f738a21e16c2bdbcac2444dfa96b01aaa
Comments
Le 23/03/2023 à 20:45, Naresh Solanki a écrit : > From: Patrick Rudolph <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> > > max597x has 10bit ADC for voltage & current monitoring. > Use iio framework to expose the same in sysfs. > > Signed-off-by: Patrick Rudolph <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> > Signed-off-by: Naresh Solanki <Naresh.Solanki-cWEv32IpryCakBO8gow8eQ@public.gmane.org> Hi, a few nits below, should there be a v3. CJ > ... > Changes in V2: > - Remove fallthrough > - Use pdev->dev instead of i2c->dev > - Init indio_dev->name based on device type. > --- > drivers/iio/adc/Kconfig | 15 ++++ > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/max597x-iio.c | 152 ++++++++++++++++++++++++++++++++++ > 3 files changed, 168 insertions(+) > create mode 100644 drivers/iio/adc/max597x-iio.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 45af2302be53..0d1a3dea0b7d 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -735,6 +735,21 @@ config MAX1363 > To compile this driver as a module, choose M here: the module will be > called max1363. > > +config MAX597X_IIO > + tristate "Maxim 597x power switch and monitor" > + depends on I2C && OF > + select MFD_MAX597X > + help > + This driver enables support for the Maxim 597x smart switch and > + voltage/current monitoring interface using the Industrial I/O (IIO) > + framework. The Maxim 597x is a power switch and monitor that can > + provide voltage and current measurements via the I2C bus. Enabling > + this driver will allow user space applications to read the voltage > + and current measurements using IIO interfaces. > + > + To compile this driver as a module, choose M here: the module will be > + called max597x-iio. > + > config MAX9611 > tristate "Maxim max9611/max9612 ADC driver" > depends on I2C > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > index 36c18177322a..7ec0c2cf7bbb 100644 > --- a/drivers/iio/adc/Makefile > +++ b/drivers/iio/adc/Makefile > @@ -67,6 +67,7 @@ obj-$(CONFIG_MAX11205) += max11205.o > obj-$(CONFIG_MAX11410) += max11410.o > obj-$(CONFIG_MAX1241) += max1241.o > obj-$(CONFIG_MAX1363) += max1363.o > +obj-$(CONFIG_MAX597X_IIO) += max597x-iio.o > obj-$(CONFIG_MAX9611) += max9611.o > obj-$(CONFIG_MCP320X) += mcp320x.o > obj-$(CONFIG_MCP3422) += mcp3422.o > diff --git a/drivers/iio/adc/max597x-iio.c b/drivers/iio/adc/max597x-iio.c > new file mode 100644 > index 000000000000..8a9fc27ff71e > --- /dev/null > +++ b/drivers/iio/adc/max597x-iio.c > @@ -0,0 +1,152 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Device driver for IIO in MAX5970 and MAX5978 IC > + * > + * Copyright (c) 2022 9elements GmbH > + * > + * Author: Patrick Rudolph <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> > + */ > + > +#include <linux/iio/iio.h> > +#include <linux/mfd/max597x.h> > +#include <linux/platform_device.h> > +#include <linux/regmap.h> > + > +struct max597x_iio { > + struct regmap *regmap; > + int shunt_micro_ohms[MAX5970_NUM_SWITCHES]; > + unsigned int irng[MAX5970_NUM_SWITCHES]; > + unsigned int mon_rng[MAX5970_NUM_SWITCHES]; > +}; > + > +#define MAX597X_ADC_CHANNEL(_idx, _type) { \ > + .type = IIO_ ## _type, \ > + .indexed = 1, \ > + .channel = (_idx), \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ > + BIT(IIO_CHAN_INFO_SCALE), \ > + .address = MAX5970_REG_ ## _type ## _L(_idx), \ > +} > + > +static const struct iio_chan_spec max5978_adc_iio_channels[] = { > + MAX597X_ADC_CHANNEL(0, VOLTAGE), > + MAX597X_ADC_CHANNEL(0, CURRENT), > +}; > + > +static const struct iio_chan_spec max5970_adc_iio_channels[] = { > + MAX597X_ADC_CHANNEL(0, VOLTAGE), > + MAX597X_ADC_CHANNEL(0, CURRENT), > + MAX597X_ADC_CHANNEL(1, VOLTAGE), > + MAX597X_ADC_CHANNEL(1, CURRENT), > +}; > + > +static int max597x_iio_read_raw(struct iio_dev *iio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long info) > +{ > + int ret; > + struct max597x_iio *data = iio_priv(iio_dev); > + unsigned int reg_l, reg_h; > + > + switch (info) { > + case IIO_CHAN_INFO_RAW: > + ret = regmap_read(data->regmap, chan->address, ®_l); > + if (ret < 0) > + return ret; > + ret = regmap_read(data->regmap, chan->address - 1, ®_h); > + if (ret < 0) > + return ret; > + *val = (reg_h << 2) | (reg_l & 3); > + > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + Nit: This blank line would look nicer if above the case: > + switch (chan->address) { > + case MAX5970_REG_CURRENT_L(0): > + case MAX5970_REG_CURRENT_L(1): > + /* in A, convert to mA */ > + *val = data->irng[chan->channel] * 1000; > + *val2 = > + data->shunt_micro_ohms[chan->channel] * ADC_MASK; > + return IIO_VAL_FRACTIONAL; > + > + case MAX5970_REG_VOLTAGE_L(0): > + case MAX5970_REG_VOLTAGE_L(1): > + /* in uV, convert to mV */ > + *val = data->mon_rng[chan->channel]; > + *val2 = ADC_MASK * 1000; > + return IIO_VAL_FRACTIONAL; > + } > + > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info max597x_adc_iio_info = { > + .read_raw = &max597x_iio_read_raw, > +}; > + > +static int max597x_iio_probe(struct platform_device *pdev) > +{ > + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); > + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); > + struct iio_dev *indio_dev; > + struct max597x_iio *priv; > + int ret, i; > + > + if (!regmap) > + return -EPROBE_DEFER; > + > + if (!max597x || !max597x->num_switches) > + return -EPROBE_DEFER; > + > + /* registering iio */ > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); > + if (!indio_dev) > + return dev_err_probe(&pdev->dev, -ENOMEM, > + "failed to allocate iio device\n"); > + > + indio_dev->info = &max597x_adc_iio_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + switch (max597x->num_switches) { > + case MAX597x_TYPE_MAX5970: > + indio_dev->channels = max5970_adc_iio_channels; > + indio_dev->num_channels = ARRAY_SIZE(max5970_adc_iio_channels); > + indio_dev->name = "max5970"; > + break; > + case MAX597x_TYPE_MAX5978: > + indio_dev->channels = max5978_adc_iio_channels; > + indio_dev->num_channels = ARRAY_SIZE(max5978_adc_iio_channels); > + indio_dev->name = "max5978"; > + break; > + } > + > + priv = iio_priv(indio_dev); > + priv->regmap = regmap; > + for (i = 0; i < indio_dev->num_channels; i++) { > + priv->irng[i] = max597x->irng[i]; > + priv->mon_rng[i] = max597x->mon_rng[i]; > + priv->shunt_micro_ohms[i] = max597x->shunt_micro_ohms[i]; > + } > + > + ret = devm_iio_device_register(&pdev->dev, indio_dev); > + if (ret) > + dev_err_probe(&pdev->dev, ret, "could not register iio device"); Nit: \n missing > + > + return ret; Nit: return 0; > +} > + > +static struct platform_driver max597x_iio_driver = { > + .driver = { > + .name = "max597x-iio", > + }, > + .probe = max597x_iio_probe, > +}; > + > +module_platform_driver(max597x_iio_driver); > + > +MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org>"); > +MODULE_DESCRIPTION("MAX5970_hot-swap controller driver"); > +MODULE_LICENSE("GPL"); > > base-commit: 368eb79f738a21e16c2bdbcac2444dfa96b01aaa
Hi, On 24-03-2023 01:36 am, Christophe JAILLET wrote: > Le 23/03/2023 à 20:45, Naresh Solanki a écrit : >> From: Patrick Rudolph >> <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> >> >> max597x has 10bit ADC for voltage & current monitoring. >> Use iio framework to expose the same in sysfs. >> >> Signed-off-by: Patrick Rudolph >> <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> >> Signed-off-by: Naresh Solanki >> <Naresh.Solanki-cWEv32IpryCakBO8gow8eQ@public.gmane.org> > > Hi, a few nits below, should there be a v3. > > CJ > >> ... >> Changes in V2: >> - Remove fallthrough >> - Use pdev->dev instead of i2c->dev >> - Init indio_dev->name based on device type. >> --- >> drivers/iio/adc/Kconfig | 15 ++++ >> drivers/iio/adc/Makefile | 1 + >> drivers/iio/adc/max597x-iio.c | 152 ++++++++++++++++++++++++++++++++++ >> 3 files changed, 168 insertions(+) >> create mode 100644 drivers/iio/adc/max597x-iio.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 45af2302be53..0d1a3dea0b7d 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -735,6 +735,21 @@ config MAX1363 >> To compile this driver as a module, choose M here: the module >> will be >> called max1363. >> +config MAX597X_IIO >> + tristate "Maxim 597x power switch and monitor" >> + depends on I2C && OF >> + select MFD_MAX597X >> + help >> + This driver enables support for the Maxim 597x smart switch and >> + voltage/current monitoring interface using the Industrial I/O >> (IIO) >> + framework. The Maxim 597x is a power switch and monitor that can >> + provide voltage and current measurements via the I2C bus. Enabling >> + this driver will allow user space applications to read the voltage >> + and current measurements using IIO interfaces. >> + >> + To compile this driver as a module, choose M here: the module >> will be >> + called max597x-iio. >> + >> config MAX9611 >> tristate "Maxim max9611/max9612 ADC driver" >> depends on I2C >> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile >> index 36c18177322a..7ec0c2cf7bbb 100644 >> --- a/drivers/iio/adc/Makefile >> +++ b/drivers/iio/adc/Makefile >> @@ -67,6 +67,7 @@ obj-$(CONFIG_MAX11205) += max11205.o >> obj-$(CONFIG_MAX11410) += max11410.o >> obj-$(CONFIG_MAX1241) += max1241.o >> obj-$(CONFIG_MAX1363) += max1363.o >> +obj-$(CONFIG_MAX597X_IIO) += max597x-iio.o >> obj-$(CONFIG_MAX9611) += max9611.o >> obj-$(CONFIG_MCP320X) += mcp320x.o >> obj-$(CONFIG_MCP3422) += mcp3422.o >> diff --git a/drivers/iio/adc/max597x-iio.c >> b/drivers/iio/adc/max597x-iio.c >> new file mode 100644 >> index 000000000000..8a9fc27ff71e >> --- /dev/null >> +++ b/drivers/iio/adc/max597x-iio.c >> @@ -0,0 +1,152 @@ >> +// SPDX-License-Identifier: GPL-2.0 >> +/* >> + * Device driver for IIO in MAX5970 and MAX5978 IC >> + * >> + * Copyright (c) 2022 9elements GmbH >> + * >> + * Author: Patrick Rudolph >> <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org> >> + */ >> + >> +#include <linux/iio/iio.h> >> +#include <linux/mfd/max597x.h> >> +#include <linux/platform_device.h> >> +#include <linux/regmap.h> >> + >> +struct max597x_iio { >> + struct regmap *regmap; >> + int shunt_micro_ohms[MAX5970_NUM_SWITCHES]; >> + unsigned int irng[MAX5970_NUM_SWITCHES]; >> + unsigned int mon_rng[MAX5970_NUM_SWITCHES]; >> +}; >> + >> +#define MAX597X_ADC_CHANNEL(_idx, _type) { \ >> + .type = IIO_ ## _type, \ >> + .indexed = 1, \ >> + .channel = (_idx), \ >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ >> + BIT(IIO_CHAN_INFO_SCALE), \ >> + .address = MAX5970_REG_ ## _type ## _L(_idx), \ >> +} >> + >> +static const struct iio_chan_spec max5978_adc_iio_channels[] = { >> + MAX597X_ADC_CHANNEL(0, VOLTAGE), >> + MAX597X_ADC_CHANNEL(0, CURRENT), >> +}; >> + >> +static const struct iio_chan_spec max5970_adc_iio_channels[] = { >> + MAX597X_ADC_CHANNEL(0, VOLTAGE), >> + MAX597X_ADC_CHANNEL(0, CURRENT), >> + MAX597X_ADC_CHANNEL(1, VOLTAGE), >> + MAX597X_ADC_CHANNEL(1, CURRENT), >> +}; >> + >> +static int max597x_iio_read_raw(struct iio_dev *iio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long info) >> +{ >> + int ret; >> + struct max597x_iio *data = iio_priv(iio_dev); >> + unsigned int reg_l, reg_h; >> + >> + switch (info) { >> + case IIO_CHAN_INFO_RAW: >> + ret = regmap_read(data->regmap, chan->address, ®_l); >> + if (ret < 0) >> + return ret; >> + ret = regmap_read(data->regmap, chan->address - 1, ®_h); >> + if (ret < 0) >> + return ret; >> + *val = (reg_h << 2) | (reg_l & 3); >> + >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_SCALE: >> + > > Nit: This blank line would look nicer if above the case: Oh yes. Will do that in V3 > >> + switch (chan->address) { >> + case MAX5970_REG_CURRENT_L(0): >> + case MAX5970_REG_CURRENT_L(1): >> + /* in A, convert to mA */ >> + *val = data->irng[chan->channel] * 1000; >> + *val2 = >> + data->shunt_micro_ohms[chan->channel] * ADC_MASK; >> + return IIO_VAL_FRACTIONAL; >> + >> + case MAX5970_REG_VOLTAGE_L(0): >> + case MAX5970_REG_VOLTAGE_L(1): >> + /* in uV, convert to mV */ >> + *val = data->mon_rng[chan->channel]; >> + *val2 = ADC_MASK * 1000; >> + return IIO_VAL_FRACTIONAL; >> + } >> + >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info max597x_adc_iio_info = { >> + .read_raw = &max597x_iio_read_raw, >> +}; >> + >> +static int max597x_iio_probe(struct platform_device *pdev) >> +{ >> + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); >> + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); >> + struct iio_dev *indio_dev; >> + struct max597x_iio *priv; >> + int ret, i; >> + >> + if (!regmap) >> + return -EPROBE_DEFER; >> + >> + if (!max597x || !max597x->num_switches) >> + return -EPROBE_DEFER; >> + >> + /* registering iio */ >> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); >> + if (!indio_dev) >> + return dev_err_probe(&pdev->dev, -ENOMEM, >> + "failed to allocate iio device\n"); >> + >> + indio_dev->info = &max597x_adc_iio_info; >> + indio_dev->modes = INDIO_DIRECT_MODE; >> + >> + switch (max597x->num_switches) { >> + case MAX597x_TYPE_MAX5970: >> + indio_dev->channels = max5970_adc_iio_channels; >> + indio_dev->num_channels = ARRAY_SIZE(max5970_adc_iio_channels); >> + indio_dev->name = "max5970"; >> + break; >> + case MAX597x_TYPE_MAX5978: >> + indio_dev->channels = max5978_adc_iio_channels; >> + indio_dev->num_channels = ARRAY_SIZE(max5978_adc_iio_channels); >> + indio_dev->name = "max5978"; >> + break; >> + } >> + >> + priv = iio_priv(indio_dev); >> + priv->regmap = regmap; >> + for (i = 0; i < indio_dev->num_channels; i++) { >> + priv->irng[i] = max597x->irng[i]; >> + priv->mon_rng[i] = max597x->mon_rng[i]; >> + priv->shunt_micro_ohms[i] = max597x->shunt_micro_ohms[i]; >> + } >> + >> + ret = devm_iio_device_register(&pdev->dev, indio_dev); >> + if (ret) >> + dev_err_probe(&pdev->dev, ret, "could not register iio device"); > > Nit: \n missing > Sure will make it like this: if (ret) return dev_err_probe(&pdev->dev, ret, "could not register iio device\n"); >> + >> + return ret; > > Nit: return 0; Sure > >> +} >> + >> +static struct platform_driver max597x_iio_driver = { >> + .driver = { >> + .name = "max597x-iio", >> + }, >> + .probe = max597x_iio_probe, >> +}; >> + >> +module_platform_driver(max597x_iio_driver); >> + >> +MODULE_AUTHOR("Patrick Rudolph >> <patrick.rudolph-cWEv32IpryCakBO8gow8eQ@public.gmane.org>"); >> +MODULE_DESCRIPTION("MAX5970_hot-swap controller driver"); >> +MODULE_LICENSE("GPL"); >> >> base-commit: 368eb79f738a21e16c2bdbcac2444dfa96b01aaa >
On Thu, 23 Mar 2023 20:45:48 +0100 Naresh Solanki <naresh.solanki@9elements.com> wrote: > From: Patrick Rudolph <patrick.rudolph@9elements.com> > > max597x has 10bit ADC for voltage & current monitoring. > Use iio framework to expose the same in sysfs. > > Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> > Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com> I'm not a fan of wild cards in driver names. This doesn't for example support the max5974, max5971 etc Much better to name it after one of the supported parts. Obviously can't do much about the mfd driver now, but I'd prefer not to carry that through to the IIO driver if possible. One concern I have here is that from the max5978 datasheet I see this device supports features that are very much directed at hwmon type usecases. In particular warning and critical threshold detection. We don't support multiple thresholds (in same direction) for a single channel via IIO. If you want those features in the future you may want to consider using the hwmon subsystem. We tend to be flexible with devices that sit near the boundary of IIO and hwmon because we can bridge many of the features using the iio-hwmon bridge driver. That doesn't work for more complex event handling and I suspect some of the other features this device provides. > ... > Changes in V2: > - Remove fallthrough > - Use pdev->dev instead of i2c->dev > - Init indio_dev->name based on device type. > --- > drivers/iio/adc/Kconfig | 15 ++++ > drivers/iio/adc/Makefile | 1 + > drivers/iio/adc/max597x-iio.c | 152 ++++++++++++++++++++++++++++++++++ > 3 files changed, 168 insertions(+) > create mode 100644 drivers/iio/adc/max597x-iio.c > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > index 45af2302be53..0d1a3dea0b7d 100644 > --- a/drivers/iio/adc/Kconfig > +++ b/drivers/iio/adc/Kconfig > @@ -735,6 +735,21 @@ config MAX1363 > To compile this driver as a module, choose M here: the module will be > called max1363. > > +config MAX597X_IIO > + tristate "Maxim 597x power switch and monitor" > + depends on I2C && OF > + select MFD_MAX597X > + help > + This driver enables support for the Maxim 597x smart switch and > + voltage/current monitoring interface using the Industrial I/O (IIO) > + framework. The Maxim 597x is a power switch and monitor that can > + provide voltage and current measurements via the I2C bus. Enabling > + this driver will allow user space applications to read the voltage > + and current measurements using IIO interfaces. Call out the actual part numbers supported in this help text to make it easy to grep for them. > + > + To compile this driver as a module, choose M here: the module will be > + called max597x-iio. > + ... > + > +static int max597x_iio_read_raw(struct iio_dev *iio_dev, > + struct iio_chan_spec const *chan, > + int *val, int *val2, long info) > +{ > + int ret; > + struct max597x_iio *data = iio_priv(iio_dev); > + unsigned int reg_l, reg_h; > + > + switch (info) { > + case IIO_CHAN_INFO_RAW: > + ret = regmap_read(data->regmap, chan->address, ®_l); > + if (ret < 0) > + return ret; > + ret = regmap_read(data->regmap, chan->address - 1, ®_h); > + if (ret < 0) > + return ret; > + *val = (reg_h << 2) | (reg_l & 3); I replied late to previous patch, but I'd prefer to see a bulk read if possible. It might ensure a matched pair, or if not reduce the chance of tearing (when reg_l & 3 transitions from 3 to 0 for example and reg_h & 1 is going from 0 to 1) You could try a repeated read if the sampling rate is fairly low as simply getting same high bits on either side of the low bit read is probably enough to say tearing didn't happen. > + > + return IIO_VAL_INT; > + case IIO_CHAN_INFO_SCALE: > + > + switch (chan->address) { > + case MAX5970_REG_CURRENT_L(0): > + case MAX5970_REG_CURRENT_L(1): > + /* in A, convert to mA */ > + *val = data->irng[chan->channel] * 1000; > + *val2 = > + data->shunt_micro_ohms[chan->channel] * ADC_MASK; Don't worry about 80 char limit when it hurts readability. Just put that on one line. > + return IIO_VAL_FRACTIONAL; > + > + case MAX5970_REG_VOLTAGE_L(0): > + case MAX5970_REG_VOLTAGE_L(1): > + /* in uV, convert to mV */ > + *val = data->mon_rng[chan->channel]; > + *val2 = ADC_MASK * 1000; > + return IIO_VAL_FRACTIONAL; > + } > + > + break; > + } > + return -EINVAL; > +} > + > +static const struct iio_info max597x_adc_iio_info = { > + .read_raw = &max597x_iio_read_raw, > +}; > + > +static int max597x_iio_probe(struct platform_device *pdev) > +{ > + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); > + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); > + struct iio_dev *indio_dev; > + struct max597x_iio *priv; > + int ret, i; > + > + if (!regmap) > + return -EPROBE_DEFER; > + > + if (!max597x || !max597x->num_switches) > + return -EPROBE_DEFER; > + > + /* registering iio */ Comment doesn't add anything is is wrong anyway as this doesn't do the majority of the registration. Dropt he comment. > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); > + if (!indio_dev) > + return dev_err_probe(&pdev->dev, -ENOMEM, > + "failed to allocate iio device\n"); ...
Hi, On 26-03-2023 01:06 am, Jonathan Cameron wrote: > On Thu, 23 Mar 2023 20:45:48 +0100 > Naresh Solanki <naresh.solanki@9elements.com> wrote: > >> From: Patrick Rudolph <patrick.rudolph@9elements.com> >> >> max597x has 10bit ADC for voltage & current monitoring. >> Use iio framework to expose the same in sysfs. >> >> Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> >> Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com> > > I'm not a fan of wild cards in driver names. This doesn't > for example support the max5974, max5971 etc > > Much better to name it after one of the supported parts. > Obviously can't do much about the mfd driver now, but I'd prefer > not to carry that through to the IIO driver if possible. > > One concern I have here is that from the max5978 datasheet I see > this device supports features that are very much directed at hwmon > type usecases. In particular warning and critical threshold detection. > We don't support multiple thresholds (in same direction) for a single > channel via IIO. If you want those features in the future you may want > to consider using the hwmon subsystem. > > We tend to be flexible with devices that sit near the boundary of IIO > and hwmon because we can bridge many of the features using the iio-hwmon > bridge driver. That doesn't work for more complex event handling and > I suspect some of the other features this device provides. I believe it is the most appropriate approach for our use case at the moment. If we decide to incorporate more complex event handling or need to support multiple thresholds in the future, we will definitely consider using the hwmon subsystem. Thank for your input. > >> ... >> Changes in V2: >> - Remove fallthrough >> - Use pdev->dev instead of i2c->dev >> - Init indio_dev->name based on device type. >> --- >> drivers/iio/adc/Kconfig | 15 ++++ >> drivers/iio/adc/Makefile | 1 + >> drivers/iio/adc/max597x-iio.c | 152 ++++++++++++++++++++++++++++++++++ >> 3 files changed, 168 insertions(+) >> create mode 100644 drivers/iio/adc/max597x-iio.c >> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig >> index 45af2302be53..0d1a3dea0b7d 100644 >> --- a/drivers/iio/adc/Kconfig >> +++ b/drivers/iio/adc/Kconfig >> @@ -735,6 +735,21 @@ config MAX1363 >> To compile this driver as a module, choose M here: the module will be >> called max1363. >> >> +config MAX597X_IIO >> + tristate "Maxim 597x power switch and monitor" >> + depends on I2C && OF >> + select MFD_MAX597X >> + help >> + This driver enables support for the Maxim 597x smart switch and >> + voltage/current monitoring interface using the Industrial I/O (IIO) >> + framework. The Maxim 597x is a power switch and monitor that can >> + provide voltage and current measurements via the I2C bus. Enabling >> + this driver will allow user space applications to read the voltage >> + and current measurements using IIO interfaces. > > Call out the actual part numbers supported in this help text to make it easy > to grep for them. Sure. Will mention max5970 & max5978 in help section. > >> + >> + To compile this driver as a module, choose M here: the module will be >> + called max597x-iio. >> + > > ... > > >> + >> +static int max597x_iio_read_raw(struct iio_dev *iio_dev, >> + struct iio_chan_spec const *chan, >> + int *val, int *val2, long info) >> +{ >> + int ret; >> + struct max597x_iio *data = iio_priv(iio_dev); >> + unsigned int reg_l, reg_h; >> + >> + switch (info) { >> + case IIO_CHAN_INFO_RAW: >> + ret = regmap_read(data->regmap, chan->address, ®_l); >> + if (ret < 0) >> + return ret; >> + ret = regmap_read(data->regmap, chan->address - 1, ®_h); >> + if (ret < 0) >> + return ret; >> + *val = (reg_h << 2) | (reg_l & 3); > > I replied late to previous patch, but I'd prefer to see a bulk read if > possible. It might ensure a matched pair, or if not reduce the chance of > tearing (when reg_l & 3 transitions from 3 to 0 for example and > reg_h & 1 is going from 0 to 1) > > You could try a repeated read if the sampling rate is fairly low as > simply getting same high bits on either side of the low bit read is probably > enough to say tearing didn't happen. Yes. will use something like: ret = regmap_bulk_read(data->regmap, chan->address - 1, ®_l, 2); if (ret < 0) return ret; reg_h = reg_l & 0xff; reg_l = (reg_l >> 8) & 0xff; *val = (reg_h << 2) | (reg_l & 3); > >> + >> + return IIO_VAL_INT; >> + case IIO_CHAN_INFO_SCALE: >> + >> + switch (chan->address) { >> + case MAX5970_REG_CURRENT_L(0): >> + case MAX5970_REG_CURRENT_L(1): >> + /* in A, convert to mA */ >> + *val = data->irng[chan->channel] * 1000; >> + *val2 = >> + data->shunt_micro_ohms[chan->channel] * ADC_MASK; > Don't worry about 80 char limit when it hurts readability. Just put that > on one line. Sure > >> + return IIO_VAL_FRACTIONAL; >> + >> + case MAX5970_REG_VOLTAGE_L(0): >> + case MAX5970_REG_VOLTAGE_L(1): >> + /* in uV, convert to mV */ >> + *val = data->mon_rng[chan->channel]; >> + *val2 = ADC_MASK * 1000; >> + return IIO_VAL_FRACTIONAL; >> + } >> + >> + break; >> + } >> + return -EINVAL; >> +} >> + >> +static const struct iio_info max597x_adc_iio_info = { >> + .read_raw = &max597x_iio_read_raw, >> +}; >> + >> +static int max597x_iio_probe(struct platform_device *pdev) >> +{ >> + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); >> + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); >> + struct iio_dev *indio_dev; >> + struct max597x_iio *priv; >> + int ret, i; >> + >> + if (!regmap) >> + return -EPROBE_DEFER; >> + >> + if (!max597x || !max597x->num_switches) >> + return -EPROBE_DEFER; >> + >> + /* registering iio */ > > Comment doesn't add anything is is wrong anyway as this doesn't do the > majority of the registration. Dropt he comment. Sure. > >> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); >> + if (!indio_dev) >> + return dev_err_probe(&pdev->dev, -ENOMEM, >> + "failed to allocate iio device\n"); > > ... > BR, Naresh
On Tue, 28 Mar 2023 00:14:16 +0530 Naresh Solanki <naresh.solanki@9elements.com> wrote: > Hi, > > On 26-03-2023 01:06 am, Jonathan Cameron wrote: > > On Thu, 23 Mar 2023 20:45:48 +0100 > > Naresh Solanki <naresh.solanki@9elements.com> wrote: > > > >> From: Patrick Rudolph <patrick.rudolph@9elements.com> > >> > >> max597x has 10bit ADC for voltage & current monitoring. > >> Use iio framework to expose the same in sysfs. > >> > >> Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com> > >> Signed-off-by: Naresh Solanki <Naresh.Solanki@9elements.com> > > > > I'm not a fan of wild cards in driver names. This doesn't > > for example support the max5974, max5971 etc > > > > Much better to name it after one of the supported parts. > > Obviously can't do much about the mfd driver now, but I'd prefer > > not to carry that through to the IIO driver if possible. > > > > One concern I have here is that from the max5978 datasheet I see > > this device supports features that are very much directed at hwmon > > type usecases. In particular warning and critical threshold detection. > > We don't support multiple thresholds (in same direction) for a single > > channel via IIO. If you want those features in the future you may want > > to consider using the hwmon subsystem. > > > > We tend to be flexible with devices that sit near the boundary of IIO > > and hwmon because we can bridge many of the features using the iio-hwmon > > bridge driver. That doesn't work for more complex event handling and > > I suspect some of the other features this device provides. > I believe it is the most appropriate approach for our use case at the > moment. If we decide to incorporate more complex event handling or need > to support multiple thresholds in the future, we will definitely > consider using the hwmon subsystem. Thank for your input. It's not easy to move a driver (because of need to maintain ABI compatibility in most cases). Hence I'd suggest at least CCing the hwmon list and maintainers on future versions with a cover letter than explains your reasoning on why this particular support should use IIO. > > > > > >> + > >> +static int max597x_iio_read_raw(struct iio_dev *iio_dev, > >> + struct iio_chan_spec const *chan, > >> + int *val, int *val2, long info) > >> +{ > >> + int ret; > >> + struct max597x_iio *data = iio_priv(iio_dev); > >> + unsigned int reg_l, reg_h; > >> + > >> + switch (info) { > >> + case IIO_CHAN_INFO_RAW: > >> + ret = regmap_read(data->regmap, chan->address, ®_l); > >> + if (ret < 0) > >> + return ret; > >> + ret = regmap_read(data->regmap, chan->address - 1, ®_h); > >> + if (ret < 0) > >> + return ret; > >> + *val = (reg_h << 2) | (reg_l & 3); > > > > I replied late to previous patch, but I'd prefer to see a bulk read if > > possible. It might ensure a matched pair, or if not reduce the chance of > > tearing (when reg_l & 3 transitions from 3 to 0 for example and > > reg_h & 1 is going from 0 to 1) > > > > You could try a repeated read if the sampling rate is fairly low as > > simply getting same high bits on either side of the low bit read is probably > > enough to say tearing didn't happen. > Yes. will use something like: > ret = regmap_bulk_read(data->regmap, chan->address - 1, ®_l, 2); > if (ret < 0) > return ret; > reg_h = reg_l & 0xff; > reg_l = (reg_l >> 8) & 0xff; > *val = (reg_h << 2) | (reg_l & 3); As you are going to handle them as separate registers (which makes sense under the circumstances) read into a u8 regs[2] then express this as the following which also deals with endian issues by make the registering ordering explicit. *val = (reg[0] << 2) | (reg[1] & 3); Thanks, Jonathan
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 45af2302be53..0d1a3dea0b7d 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -735,6 +735,21 @@ config MAX1363 To compile this driver as a module, choose M here: the module will be called max1363. +config MAX597X_IIO + tristate "Maxim 597x power switch and monitor" + depends on I2C && OF + select MFD_MAX597X + help + This driver enables support for the Maxim 597x smart switch and + voltage/current monitoring interface using the Industrial I/O (IIO) + framework. The Maxim 597x is a power switch and monitor that can + provide voltage and current measurements via the I2C bus. Enabling + this driver will allow user space applications to read the voltage + and current measurements using IIO interfaces. + + To compile this driver as a module, choose M here: the module will be + called max597x-iio. + config MAX9611 tristate "Maxim max9611/max9612 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 36c18177322a..7ec0c2cf7bbb 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_MAX11205) += max11205.o obj-$(CONFIG_MAX11410) += max11410.o obj-$(CONFIG_MAX1241) += max1241.o obj-$(CONFIG_MAX1363) += max1363.o +obj-$(CONFIG_MAX597X_IIO) += max597x-iio.o obj-$(CONFIG_MAX9611) += max9611.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o diff --git a/drivers/iio/adc/max597x-iio.c b/drivers/iio/adc/max597x-iio.c new file mode 100644 index 000000000000..8a9fc27ff71e --- /dev/null +++ b/drivers/iio/adc/max597x-iio.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device driver for IIO in MAX5970 and MAX5978 IC + * + * Copyright (c) 2022 9elements GmbH + * + * Author: Patrick Rudolph <patrick.rudolph@9elements.com> + */ + +#include <linux/iio/iio.h> +#include <linux/mfd/max597x.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +struct max597x_iio { + struct regmap *regmap; + int shunt_micro_ohms[MAX5970_NUM_SWITCHES]; + unsigned int irng[MAX5970_NUM_SWITCHES]; + unsigned int mon_rng[MAX5970_NUM_SWITCHES]; +}; + +#define MAX597X_ADC_CHANNEL(_idx, _type) { \ + .type = IIO_ ## _type, \ + .indexed = 1, \ + .channel = (_idx), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .address = MAX5970_REG_ ## _type ## _L(_idx), \ +} + +static const struct iio_chan_spec max5978_adc_iio_channels[] = { + MAX597X_ADC_CHANNEL(0, VOLTAGE), + MAX597X_ADC_CHANNEL(0, CURRENT), +}; + +static const struct iio_chan_spec max5970_adc_iio_channels[] = { + MAX597X_ADC_CHANNEL(0, VOLTAGE), + MAX597X_ADC_CHANNEL(0, CURRENT), + MAX597X_ADC_CHANNEL(1, VOLTAGE), + MAX597X_ADC_CHANNEL(1, CURRENT), +}; + +static int max597x_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + int ret; + struct max597x_iio *data = iio_priv(iio_dev); + unsigned int reg_l, reg_h; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = regmap_read(data->regmap, chan->address, ®_l); + if (ret < 0) + return ret; + ret = regmap_read(data->regmap, chan->address - 1, ®_h); + if (ret < 0) + return ret; + *val = (reg_h << 2) | (reg_l & 3); + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + + switch (chan->address) { + case MAX5970_REG_CURRENT_L(0): + case MAX5970_REG_CURRENT_L(1): + /* in A, convert to mA */ + *val = data->irng[chan->channel] * 1000; + *val2 = + data->shunt_micro_ohms[chan->channel] * ADC_MASK; + return IIO_VAL_FRACTIONAL; + + case MAX5970_REG_VOLTAGE_L(0): + case MAX5970_REG_VOLTAGE_L(1): + /* in uV, convert to mV */ + *val = data->mon_rng[chan->channel]; + *val2 = ADC_MASK * 1000; + return IIO_VAL_FRACTIONAL; + } + + break; + } + return -EINVAL; +} + +static const struct iio_info max597x_adc_iio_info = { + .read_raw = &max597x_iio_read_raw, +}; + +static int max597x_iio_probe(struct platform_device *pdev) +{ + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); + struct regmap *regmap = dev_get_regmap(pdev->dev.parent, NULL); + struct iio_dev *indio_dev; + struct max597x_iio *priv; + int ret, i; + + if (!regmap) + return -EPROBE_DEFER; + + if (!max597x || !max597x->num_switches) + return -EPROBE_DEFER; + + /* registering iio */ + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); + if (!indio_dev) + return dev_err_probe(&pdev->dev, -ENOMEM, + "failed to allocate iio device\n"); + + indio_dev->info = &max597x_adc_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + switch (max597x->num_switches) { + case MAX597x_TYPE_MAX5970: + indio_dev->channels = max5970_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(max5970_adc_iio_channels); + indio_dev->name = "max5970"; + break; + case MAX597x_TYPE_MAX5978: + indio_dev->channels = max5978_adc_iio_channels; + indio_dev->num_channels = ARRAY_SIZE(max5978_adc_iio_channels); + indio_dev->name = "max5978"; + break; + } + + priv = iio_priv(indio_dev); + priv->regmap = regmap; + for (i = 0; i < indio_dev->num_channels; i++) { + priv->irng[i] = max597x->irng[i]; + priv->mon_rng[i] = max597x->mon_rng[i]; + priv->shunt_micro_ohms[i] = max597x->shunt_micro_ohms[i]; + } + + ret = devm_iio_device_register(&pdev->dev, indio_dev); + if (ret) + dev_err_probe(&pdev->dev, ret, "could not register iio device"); + + return ret; +} + +static struct platform_driver max597x_iio_driver = { + .driver = { + .name = "max597x-iio", + }, + .probe = max597x_iio_probe, +}; + +module_platform_driver(max597x_iio_driver); + +MODULE_AUTHOR("Patrick Rudolph <patrick.rudolph@9elements.com>"); +MODULE_DESCRIPTION("MAX5970_hot-swap controller driver"); +MODULE_LICENSE("GPL");