lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date:   Fri, 11 Aug 2023 11:47:36 +0300
From:   Andy Shevchenko <andriy.shevchenko@...ux.intel.com>
To:     Dumitru Ceclan <mitrutzceclan@...il.com>
Cc:     Lars-Peter Clausen <lars@...afoo.de>,
        Michael Hennerich <Michael.Hennerich@...log.com>,
        Jonathan Cameron <jic23@...nel.org>,
        Rob Herring <robh+dt@...nel.org>,
        Krzysztof Kozlowski <krzysztof.kozlowski+dt@...aro.org>,
        Conor Dooley <conor+dt@...nel.org>,
        Arnd Bergmann <arnd@...db.de>,
        Cosmin Tanislav <demonsingur@...il.com>,
        Alexander Sverdlin <alexander.sverdlin@...il.com>,
        Hugo Villeneuve <hvilleneuve@...onoff.com>,
        Okan Sahin <okan.sahin@...log.com>,
        Niklas Schnelle <schnelle@...ux.ibm.com>,
        ChiYuan Huang <cy_huang@...htek.com>,
        Ramona Bolboaca <ramona.bolboaca@...log.com>,
        Ibrahim Tilki <Ibrahim.Tilki@...log.com>,
        ChiaEn Wu <chiaen_wu@...htek.com>,
        William Breathitt Gray <william.gray@...aro.org>,
        Lee Jones <lee@...nel.org>, Haibo Chen <haibo.chen@....com>,
        Mike Looijmans <mike.looijmans@...ic.nl>,
        Leonard Göhrs <l.goehrs@...gutronix.de>,
        Ceclan Dumitru <dumitru.ceclan@...log.com>,
        linux-iio@...r.kernel.org, devicetree@...r.kernel.org,
        linux-kernel@...r.kernel.org
Subject: Re: [PATCH 2/2] iio: adc: ad717x: add AD717X driver

On Thu, Aug 10, 2023 at 12:33:17PM +0300, Dumitru Ceclan wrote:
> The AD717x family offer a complete integrated Sigma-Delta ADC solution
> which can be used in high precision, low noise single channel
> applications or higher speed multiplexed applications. The Sigma-Delta
> ADC is intended primarily for measurement of signals close to DC but also
> delivers outstanding performance with input bandwidths out to ~10kHz.

...

> +	help
> +	  Say yes here to build support for Analog Devices AD717x ADC.

I believe checkpatch.pl expects at least 3 lines of help description.

One of them may tell the user what will be the module name, if chosen
to be built as a module.

...

> +#include <linux/bitfield.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>

> +#include <linux/of.h>

There is no user of the header. But missing bits.h, mod_devicetable.h,
property.h, and might be more.

So, revisit this block to see which ones are used and which one are missed,
and which are redundant.

> +#include <linux/sched.h>

Is this used? How?

> +#include <linux/slab.h>
> +#include <linux/spi/spi.h>
> +#include <linux/sysfs.h>
> +#include <linux/units.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/regulator/consumer.h>

Can you keep the above sorted?

...

> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>

Can you keep the above sorted?

+ Blank line

> +#include <linux/iio/adc/ad_sigma_delta.h>

...

> +#define AD717X_ID_MASK			0xfff0

GENMASK()

> +#define AD717X_ADC_MODE_MODE_MASK	0x70

> +#define AD717X_ADC_MODE_CLOCKSEL_MASK	0xc

You are using BIT(), and not GENMASK()...
All can be converted.

...

> +#define AD717X_SETUP_AREF_BUF		(0x3 << 10)
> +#define AD717X_SETUP_AIN_BUF		(0x3 << 8)
> +#define AD717X_SETUP_REF_SEL_MASK	0x30

Ditto for all.

...

> +#define AD717X_SETUP_REF_SEL_AVDD1_AVSS	0x30
> +#define AD717X_SETUP_REF_SEL_INT_REF	0x20
> +#define AD717X_SETUP_REF_SEL_EXT_REF2	0x10
> +#define AD717X_SETUP_REF_SEL_EXT_REF	0x00

Seems to me like plain decimals with shift should be used

#define AD717X_SETUP_REF_SEL_AVDD1_AVSS	(3...
#define AD717X_SETUP_REF_SEL_INT_REF	(2...
#define AD717X_SETUP_REF_SEL_EXT_REF2	(1...
#define AD717X_SETUP_REF_SEL_EXT_REF	(0 << 4)

...

> +#define AD717X_FILTER_ODR0_MASK		0x1f

GENMASK()

...

> +static const struct ad717x_device_info ad717x_device_info[] = {
> +	[ID_AD7172_2] = {

> +		.clock = 2000000,

> +	},
> +	[ID_AD7173_8] = {

> +		.clock = 2000000,

> +	},
> +	[ID_AD7175_2] = {

> +		.clock = 16000000,

> +	},
> +	[ID_AD7176_2] = {

> +		.clock = 16000000,

> +	},
> +};

HZ_PER_MHZ from units.h?

...

> +static int ad717x_gpio_get(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct ad717x_state *st = gpiochip_to_ad717x(chip);
> +	unsigned int mask;
> +	unsigned int value;
> +	int ret;
> +
> +	switch (offset) {
> +	case 0:
> +		mask = AD717X_GPIO_GP_DATA0;
> +		break;
> +	case 1:
> +		mask = AD717X_GPIO_GP_DATA1;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ret = ad_sd_read_reg(&st->sd, AD717X_REG_GPIO, 2, &value);
> +	if (ret)
> +		return ret;
> +
> +	return (bool)(value & mask);

This is weird. You have int which you get from bool, wouldn't be better to use
!!(...) as other GPIO drivers do?

> +}

...

> +static void ad717x_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
> +{
> +	struct ad717x_state *st = gpiochip_to_ad717x(chip);
> +	unsigned int mask, set_mask, clr_mask;
> +
> +	switch (offset) {
> +	case 0:
> +		mask = AD717X_GPIO_GP_DATA0;
> +		break;
> +	case 1:
> +		mask = AD717X_GPIO_GP_DATA1;
> +		break;
> +	case 2:
> +		mask = AD717X_GPIO_GP_DATA2;
> +		break;
> +	case 3:
> +		mask = AD717X_GPIO_GP_DATA3;
> +		break;
> +	default:
> +		return;
> +	}

> +	if (value) {
> +		set_mask = mask;
> +		clr_mask = 0;
> +	} else {
> +		set_mask = 0;
> +		clr_mask = mask;
> +	}
> +
> +	ad717x_gpio_update(st, set_mask, clr_mask);

It's too verbose, just

	if (value)
		ad717x_gpio_update(st, mask, 0);
	else
		ad717x_gpio_update(st, 0, mask);

Would save a half a dozen LoCs.

> +}

...

> +static int ad717x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
> +{
> +	struct ad717x_state *st = gpiochip_to_ad717x(chip);
> +	unsigned int mask;
> +
> +	switch (offset) {
> +	case 0:
> +		mask = AD717X_GPIO_IP_EN0;
> +		break;
> +	case 1:
> +		mask = AD717X_GPIO_IP_EN1;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}

> +	return ad717x_gpio_update(st, mask, 0);

Return directly from the switch case.

> +}

...

The GPIO methods are too verbose, I stopped looking into them here.
The main question, why gpio-regmap is not used for this?

...

> +static int ad717x_gpio_init(struct ad717x_state *st)
> +{

	struct device *dev = &st->sd.spi->dev;

makes code neater here and elsewhere.

> +	st->gpiochip.label = dev_name(&st->sd.spi->dev);
> +	st->gpiochip.base = -1;

> +	if (st->info->has_gp23)
> +		st->gpiochip.ngpio = 4;
> +	else
> +		st->gpiochip.ngpio = 2;

Instead of keeping a boolean flag in the info, why you not keep ngpio there
(0 implies no GPIO)?

> +	st->gpiochip.parent = &st->sd.spi->dev;
> +	st->gpiochip.can_sleep = true;
> +	st->gpiochip.direction_input = ad717x_gpio_direction_input;
> +	st->gpiochip.direction_output = ad717x_gpio_direction_output;
> +	st->gpiochip.get = ad717x_gpio_get;
> +	st->gpiochip.set = ad717x_gpio_set;
> +	st->gpiochip.free = ad717x_gpio_free;
> +	st->gpiochip.owner = THIS_MODULE;
> +
> +	return devm_gpiochip_add_data(&st->sd.spi->dev, &st->gpiochip, NULL);
> +}

...

> +static void ad717x_reset_usage_cnts(struct ad717x_state *st)
> +{
> +	int i;
> +
> +	for (i = 0; i < st->info->num_configs; i++)

> +		(st->config_cnts)[i] = 0;

What are the parentheses for?
Wouldn't this a NIH one of memsetXX()?

> +	st->config_usage_counter = 0;
> +}

...

> +	offset = offsetof(struct ad717x_channel_config, cfg_slot) +
> +		 sizeof(cfg->cfg_slot);
> +	cmp_size = sizeof(*cfg) - offset;

My gut's feeling this is some NIH one of macros from overflow.h.

...

> +	for (i = 0; i < st->num_channels; i++) {
> +		cfg_aux = &st->channels[i].cfg;
> +
> +		if (cfg_aux->live && !memcmp(((void *)cfg) + offset,
> +					    ((void *)cfg_aux) + offset, cmp_size))

My gosh! Explicit castings should be really rear. Can't you use proper
struct / array pointers?

> +			return cfg_aux;
> +	}

...

> +	int ret = 0;

How is this 0 used?

> +	if (!cfg->live) {

> +		live_cfg = ad717x_find_live_config(st, cfg);
> +		if (!live_cfg) {

Why not positive check?

> +			ret = ad717x_load_config(st, cfg);
> +			if (ret < 0)
> +				return ret;
> +		} else {
> +			cfg->cfg_slot = live_cfg->cfg_slot;
> +		}
> +	}

> +	cfg->live = true;

Can be moved inside the conditional.

...

> +	ret = ad717x_config_channel(st, channel);
> +	if (ret < 0)

What is the meaning of > 0?
Same Q to other similar checks.

> +		return ret;

...

> +static int ad717x_setup(struct iio_dev *indio_dev)
> +{
> +	struct ad717x_state *st = iio_priv(indio_dev);
> +	unsigned int id;
> +	u8 *buf;
> +	int ret;
> +
> +	/* reset the serial interface */
> +	buf = kcalloc(8, sizeof(*buf), GFP_KERNEL);

Magic number!

> +	if (!buf)
> +		return -ENOMEM;
> +
> +	memset(buf, 0xff, 8);
> +	ret = spi_write(st->sd.spi, buf, 8);
> +	kfree(buf);
> +	if (ret < 0)
> +		return ret;

I agree with comments by Nuno on this.

> +	/* datasheet recommends a delay of at least 500us after reset */
> +	usleep_range(500, 1000);

	fsleep(500);

> +	ret = ad_sd_read_reg(&st->sd, AD717X_REG_ID, 2, &id);
> +	if (ret)
> +		return ret;
> +
> +	id &= AD717X_ID_MASK;
> +	if (id != st->info->id)
> +		dev_warn(&st->sd.spi->dev, "Unexpected device id: %x, expected: %x\n",
> +					    id, st->info->id);

Wrong indentation.

> +	mutex_init(&st->cfgs_lock);
> +	st->adc_mode |= AD717X_ADC_MODE_REF_EN | AD717X_ADC_MODE_SING_CYC;
> +	st->interface_mode = 0x0;
> +
> +	st->config_usage_counter = 0;
> +	st->config_cnts = devm_kzalloc(&indio_dev->dev,
> +				       st->info->num_configs * sizeof(u64),

No, let kernel do the right thing with this.

> +				       GFP_KERNEL);
> +	if (!st->config_cnts)
> +		return -ENOMEM;
> +
> +	/* All channels are enabled by default after a reset */
> +	ret = ad717x_disable_all(&st->sd);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}

...

> +static int ad717x_read_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *chan, int *val, int *val2, long info)
> +{
> +	struct ad717x_state *st = iio_priv(indio_dev);
> +	unsigned int reg;
> +	int ret;
> +
> +	switch (info) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* disable channel after single conversion */

> +		ret = ad_sd_write_reg(&st->sd, AD717X_REG_CH(chan->address), 2,
> +				      0);

This is much better on a single line.

> +		if (ret < 0)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		if (chan->type == IIO_TEMP) {
> +			*val = 250000000;
> +			*val2 = 800273203; /* (2**24 * 477) / 10 */

Use mathematical notations (TeX like)

	(2^24 * 477) / 10

> +			return IIO_VAL_FRACTIONAL;
> +		} else {
> +			*val = 2500;
> +			if (chan->differential)
> +				*val2 = 23;
> +			else
> +				*val2 = 24;
> +			return IIO_VAL_FRACTIONAL_LOG2;
> +		}
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (chan->type == IIO_TEMP) {
> +			*val = -874379;
> +		} else {
> +			if (chan->differential)
> +				*val = -(1 << (chan->scan_type.realbits - 1));
> +			else
> +				*val = 0;
> +		}
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		reg = st->channels[chan->address].cfg.odr;
> +
> +		*val = st->info->sinc5_data_rates[reg] / MILLI;
> +		*val2 = (st->info->sinc5_data_rates[reg] % MILLI) * MILLI;
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	}
> +	return -EINVAL;
> +}

...

> +static int ad717x_write_raw(struct iio_dev *indio_dev,
> +	struct iio_chan_spec const *chan, int val, int val2, long info)
> +{

> +	int ret = 0;

You won't need this if...

> +	mutex_lock(&st->cfgs_lock);

...you switch to use guarded mutex (see cleanup.h).

Same applicable to many other functions.

> +	mutex_unlock(&st->cfgs_lock);
> +	return ret;
> +}

...

> +static int ad717x_debug(struct iio_dev *indio_dev, unsigned int reg,
> +			unsigned int writeval, unsigned int *readval)
> +{
> +	struct ad717x_state *st = iio_priv(indio_dev);

> +	u8 reg_size = 2;

Better to make it an else branch...

> +	if (reg == 0)
> +		reg_size = 1;
> +	else if (reg == AD717X_REG_CRC || reg == AD717X_REG_DATA ||
> +		 reg >= AD717X_REG_OFFSET(0))
> +		reg_size = 3;

	else
		reg_size = 2;

> +	if (readval)
> +		return ad_sd_read_reg(&st->sd, reg, reg_size, readval);
> +
> +	return ad_sd_write_reg(&st->sd, reg, reg_size, writeval);
> +}

...

> +static int ad717x_of_parse_channel_config(struct iio_dev *indio_dev)

of --> fw

> +{
> +	struct ad717x_state *st = iio_priv(indio_dev);
> +	struct ad717x_channel *channels_st_priv;
> +	struct fwnode_handle *child;
> +	struct device *dev = indio_dev->dev.parent;
> +	struct iio_chan_spec *chan;

> +	unsigned int num_channels = 0;

How is this 0 being used?

> +	unsigned int ain[2], chan_index = 0;

> +	bool use_temp = false;

No assignment needed here, see below.

> +	int ret;
> +
> +	num_channels = device_get_child_node_count(dev);

> +	if (device_property_read_bool(dev, "adi,temp-channel")) {

	use_temp = device_property_...
	if (use_temp) {

> +		if (!st->info->has_temp) {

> +			dev_err(indio_dev->dev.parent,
> +				"Current chip does not support temperature channel");
> +			return -EINVAL;

			return dev_err_probe(...);

> +		}
> +
> +		num_channels++;
> +		use_temp = true;
> +	}

> +	st->num_channels = num_channels;

I believe that it's already 0, so this can be moved...

> +	if (num_channels == 0)
> +		return 0;

...after this check.

> +	chan = devm_kcalloc(indio_dev->dev.parent, sizeof(*chan), num_channels,

Why not use dev?

> +			    GFP_KERNEL);
> +	if (!chan)
> +		return -ENOMEM;
> +
> +	channels_st_priv = devm_kcalloc(indio_dev->dev.parent, num_channels,

Ditto.

> +					sizeof(*channels_st_priv), GFP_KERNEL);
> +	if (!channels_st_priv)
> +		return -ENOMEM;
> +
> +	indio_dev->channels = chan;
> +	indio_dev->num_channels = num_channels;
> +	st->channels = channels_st_priv;
> +
> +	if (use_temp) {
> +		chan[chan_index] = ad717x_temp_iio_channel_template;
> +		channels_st_priv[chan_index].ain =
> +			AD717X_CH_ADDRESS(chan[chan_index].channel, chan[chan_index].channel2);
> +		channels_st_priv[chan_index].cfg.bipolar = false;
> +		channels_st_priv[chan_index].cfg.input_buf = true;
> +		chan_index++;
> +	}
> +
> +	device_for_each_child_node(dev, child) {
> +		ret = fwnode_property_read_u32_array(child, "diff-channels", ain, 2);

ARRAY_SIZE() ?

> +		if (ret) {
> +			fwnode_handle_put(child);
> +			return ret;
> +		}
> +
> +		if (ain[0] >= st->info->num_inputs ||
> +		    ain[1] >= st->info->num_inputs) {
> +			dev_err(indio_dev->dev.parent,
> +				"Input pin number out of range for pair (%d %d).", ain[0], ain[1]);
> +			fwnode_handle_put(child);
> +			return -EINVAL;

			return dev_err_probe(...);

> +		}
> +
> +		chan[chan_index] = ad717x_channel_template;
> +		chan[chan_index].address = chan_index;
> +		chan[chan_index].scan_index = chan_index;
> +		chan[chan_index].channel = ain[0];
> +		chan[chan_index].channel2 = ain[1];

> +		channels_st_priv[chan_index].ain =
> +			AD717X_CH_ADDRESS(ain[0], ain[1]);

Why not one line here?

> +		channels_st_priv[chan_index].chan_reg = chan_index;
> +		channels_st_priv[chan_index].cfg.input_buf = true;
> +		channels_st_priv[chan_index].cfg.odr = 0;
> +
> +		chan[chan_index].differential = fwnode_property_read_bool(child, "adi,bipolar");
> +		if (chan[chan_index].differential) {
> +			chan[chan_index].info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET);
> +			channels_st_priv[chan_index].cfg.bipolar = true;
> +		}
> +
> +		chan_index++;
> +	}
> +
> +	return 0;
> +}

...

> +	if (!spi->irq) {
> +		dev_err(&spi->dev, "No IRQ specified\n");
> +		return -ENODEV;

		return dev_err_probe(...);

> +	}

...

> +static const struct spi_device_id ad717x_id_table[] = {
> +	{ "ad7172-2", },
> +	{ "ad7173-8", },
> +	{ "ad7175-2", },
> +	{ "ad7176-2", },
> +	{}

Missing driver_data here.

> +};

-- 
With Best Regards,
Andy Shevchenko


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ