On Mon, 2020-01-13 at 16:15 +0200, Beniamin Bia wrote: > From: Michael Hennerich <michael.hennerich@xxxxxxxxxx> > > This patch adds support for the HMC425A 0.5 dB LSB GaAs MMIC 6-BIT > DIGITAL POSITIVE CONTROL ATTENUATOR, 2.2 - 8.0 GHz. > > Datasheet: > https://www.analog.com/media/en/technical-documentation/data-sheets/hmc425A.pdf > > Signed-off-by: Michael Hennerich <michael.hennerich@xxxxxxxxxx> > Signed-off-by: Alexandru Ardelean <alexandru.ardelean@xxxxxxxxxx> > Signed-off-by: Beniamin Bia <beniamin.bia@xxxxxxxxxx> > --- > drivers/iio/amplifiers/Kconfig | 10 ++ > drivers/iio/amplifiers/Makefile | 1 + > drivers/iio/amplifiers/hmc425a.c | 247 +++++++++++++++++++++++++++++++ > 3 files changed, 258 insertions(+) > create mode 100644 drivers/iio/amplifiers/hmc425a.c > > diff --git a/drivers/iio/amplifiers/Kconfig > b/drivers/iio/amplifiers/Kconfig > index da7f126d197b..9b02c9a2bc8a 100644 > --- a/drivers/iio/amplifiers/Kconfig > +++ b/drivers/iio/amplifiers/Kconfig > @@ -22,4 +22,14 @@ config AD8366 > To compile this driver as a module, choose M here: the > module will be called ad8366. > > +config HMC425 > + tristate "Analog Devices HMC425A and similar GPIO Gain Amplifiers" > + depends on GPIOLIB > + help > + Say yes here to build support for Analog Devices HMC425A and > similar > + gain amplifiers or step attenuators. > + > + To compile this driver as a module, choose M here: the > + module will be called hmc425a. > + > endmenu > diff --git a/drivers/iio/amplifiers/Makefile > b/drivers/iio/amplifiers/Makefile > index 9abef2ebe9bc..19a89db1d9b1 100644 > --- a/drivers/iio/amplifiers/Makefile > +++ b/drivers/iio/amplifiers/Makefile > @@ -5,3 +5,4 @@ > > # When adding new entries keep the list in alphabetical order > obj-$(CONFIG_AD8366) += ad8366.o > +obj-$(CONFIG_HMC425) += hmc425a.o > \ No newline at end of file > diff --git a/drivers/iio/amplifiers/hmc425a.c > b/drivers/iio/amplifiers/hmc425a.c > new file mode 100644 > index 000000000000..525ebadaf1e8 > --- /dev/null > +++ b/drivers/iio/amplifiers/hmc425a.c > @@ -0,0 +1,247 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * HMC425A and similar Gain Amplifiers > + * > + * Copyright 2020 Analog Devices Inc. > + */ > + > +#include <linux/device.h> > +#include <linux/err.h> > +#include <linux/gpio/consumer.h> > +#include <linux/iio/iio.h> > +#include <linux/iio/sysfs.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/of_platform.h> > +#include <linux/platform_device.h> > +#include <linux/slab.h> > +#include <linux/regulator/consumer.h> > +#include <linux/sysfs.h> > + > +enum hmc425a_type { > + ID_HMC425A, > +}; > + > +struct hmc425a_chip_info { > + const struct iio_chan_spec *channels; > + unsigned int num_channels; > + unsigned int num_gpios; > + int gain_min; > + int gain_max; > + int default_gain; > +}; > + > +struct hmc425a_state { > + struct regulator *reg; > + struct mutex lock; /* protect sensor state */ > + struct hmc425a_chip_info *chip_info; > + struct gpio_descs *gpios; > + enum hmc425a_type type; > + u32 gain; > +}; > + > +static int hmc425a_write(struct iio_dev *indio_dev, u32 value) > +{ > + struct hmc425a_state *st = iio_priv(indio_dev); > + int i, *values; > + > + values = kmalloc_array(st->chip_info->num_gpios, sizeof(int), > + GFP_KERNEL); > + if (!values) > + return -ENOMEM; > + > + for (i = 0; i < st->chip_info->num_gpios; i++) > + values[i] = (value >> i) & 1; > + > + gpiod_set_array_value_cansleep(st->gpios->ndescs, st->gpios->desc, > + values); > + kfree(values); I think this may be an older variant of the driver. The 'values' array could be static with a max value. And the return value of gpiod_set_array_value_cansleep() could be returned from this function. > + return 0; > +} > + > +static int hmc425a_read_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int *val, > + int *val2, long m) > +{ > + struct hmc425a_state *st = iio_priv(indio_dev); > + int code, gain = 0; > + int ret; > + > + mutex_lock(&st->lock); > + switch (m) { > + case IIO_CHAN_INFO_HARDWAREGAIN: > + code = st->gain; > + > + switch (st->type) { > + case ID_HMC425A: > + gain = ~code * -500; > + break; > + } > + > + /* Values in dB */ > + *val = gain / 1000; > + *val2 = (gain % 1000) * 1000; > + > + ret = IIO_VAL_INT_PLUS_MICRO_DB; > + break; > + default: > + ret = -EINVAL; > + } > + mutex_unlock(&st->lock); > + > + return ret; > +}; > + > +static int hmc425a_write_raw(struct iio_dev *indio_dev, > + struct iio_chan_spec const *chan, int val, > + int val2, long mask) > +{ > + struct hmc425a_state *st = iio_priv(indio_dev); > + struct hmc425a_chip_info *inf = st->chip_info; > + int code = 0, gain; > + int ret; > + > + /* Values in dB */ > + if (val < 0) > + gain = (val * 1000) - (val2 / 1000); > + else > + gain = (val * 1000) + (val2 / 1000); > + > + if (gain > inf->gain_max || gain < inf->gain_min) > + return -EINVAL; > + > + switch (st->type) { > + case ID_HMC425A: > + code = ~((abs(gain) / 500) & 0x3F); > + break; > + } > + > + mutex_lock(&st->lock); > + switch (mask) { > + case IIO_CHAN_INFO_HARDWAREGAIN: > + st->gain = code; > + > + ret = hmc425a_write(indio_dev, st->gain); > + break; > + default: > + ret = -EINVAL; > + } > + mutex_unlock(&st->lock); > + > + return ret; > +} > + > +static const struct iio_info hmc425a_info = { > + .read_raw = &hmc425a_read_raw, > + .write_raw = &hmc425a_write_raw, > +}; > + > +#define > HMC425A_CHAN(_channel) \ > +{ \ > + .type = IIO_VOLTAGE, .output = 1, .indexed = 1, \ > + .channel = _channel, \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_HARDWAREGAIN), \ > +} > + > +static const struct iio_chan_spec hmc425a_channels[] = { > + HMC425A_CHAN(0), > +}; > + > +/* Match table for of_platform binding */ > +static const struct of_device_id hmc425a_of_match[] = { > + { .compatible = "adi,hmc425a", .data = (void *)ID_HMC425A }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, hmc425a_of_match); > + > +static void hmc425a_remove(void *data) > +{ > + struct hmc425a_state *st = data; > + > + regulator_disable(st->reg); > +} > + > +static struct hmc425a_chip_info hmc425a_chip_info_tbl[] = { > + [ID_HMC425A] = { > + .channels = hmc425a_channels, > + .num_channels = ARRAY_SIZE(hmc425a_channels), > + .num_gpios = 6, > + .gain_min = -31500, > + .gain_max = 0, > + .default_gain = -0x40, /* set default gain -31.5db*/ > + }, > +}; > + > +static int hmc425a_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + const struct of_device_id *id; > + struct iio_dev *indio_dev; > + struct hmc425a_state *st; > + int ret; > + > + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); > + if (!indio_dev) > + return -ENOMEM; > + > + st = iio_priv(indio_dev); > + id = of_match_device(hmc425a_of_match, &pdev->dev); > + if (!id) > + ret = -ENODEV; > + > + st->type = (enum hmc425a_type)id->data; > + > + st->chip_info = &hmc425a_chip_info_tbl[st->type]; > + indio_dev->num_channels = st->chip_info->num_channels; > + indio_dev->channels = st->chip_info->channels; > + st->gain = st->chip_info->default_gain; > + > + st->gpios = devm_gpiod_get_array(&pdev->dev, "ctrl", > GPIOD_OUT_LOW); > + if (IS_ERR(st->gpios)) { > + ret = PTR_ERR(st->gpios); > + if (ret != -EPROBE_DEFER) > + dev_err(&pdev->dev, "failed to get gpios\n"); > + return ret; > + } > + > + if (st->gpios->ndescs != st->chip_info->num_gpios) { > + dev_err(&pdev->dev, "%d GPIOs needed to operate\n", > + st->chip_info->num_gpios); > + return -ENODEV; > + } > + > + st->reg = devm_regulator_get(&pdev->dev, "vcc"); > + if (!IS_ERR(st->reg)) { > + ret = regulator_enable(st->reg); > + if (ret) > + return ret; > + ret = devm_add_action_or_reset(&pdev->dev, hmc425a_remove, > st); > + if (ret) > + return ret; > + } > + > + platform_set_drvdata(pdev, indio_dev); > + mutex_init(&st->lock); > + > + indio_dev->dev.parent = &pdev->dev; > + indio_dev->name = np->name; > + indio_dev->info = &hmc425a_info; > + indio_dev->modes = INDIO_DIRECT_MODE; > + > + return devm_iio_device_register(&pdev->dev, indio_dev); > +} > + > +static struct platform_driver hmc425a_driver = { > + .driver = { > + .name = KBUILD_MODNAME, > + .of_match_table = hmc425a_of_match, > + }, > + .probe = hmc425a_probe, > +}; > +module_platform_driver(hmc425a_driver); > + > +MODULE_AUTHOR("Michael Hennerich <michael.hennerich@xxxxxxxxxx>"); > +MODULE_DESCRIPTION( > + "Analog Devices HMC425A and similar GPIO control Gain Amplifiers"); > +MODULE_LICENSE("GPL v2");