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] [day] [month] [year] [list]
Message-ID: <f19cf5b0-39d8-4297-96c5-e00c4f61c8f6@linaro.org>
Date: Fri, 18 Oct 2024 12:40:21 +0200
From: Neil Armstrong <neil.armstrong@...aro.org>
To: Jonathan Cameron <jic23@...nel.org>
Cc: Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>,
 Conor Dooley <conor+dt@...nel.org>, Lars-Peter Clausen <lars@...afoo.de>,
 devicetree@...r.kernel.org, linux-kernel@...r.kernel.org,
 linux-iio@...r.kernel.org
Subject: Re: [PATCH 3/3] iio: magnetometer: add Allegro MicroSystems ALS31300
 3-D Linear Hall Effect driver

Hi Jonathan,

On 12/10/2024 16:50, Jonathan Cameron wrote:
> On Mon, 07 Oct 2024 15:14:40 +0200
> Neil Armstrong <neil.armstrong@...aro.org> wrote:
> 
>> The Allegro MicroSystems ALS31300 is a 3-D Linear Hall Effect Sensor
>> mainly used for 3D head-on motion sensing applications.
>>
>> The device is configured over I2C, and as part of the Sensor
>> data the temperature core is also provided.
>>
>> While the device provides an IRQ gpio, it depends on a configuration
>> programmed into the internal EEPROM, thus only the default mode
>> is supported and buffered input via trigger is also supported
>> to allow streaming values with the same sensing timestamp.
>>
>> The device can be configured with different sensitivities in factory,
>> but the sensitivity value used to calculate value into the Gauss
>> unit is not available from registers, thus the sensitivity is
>> provided by the compatible/device-id string which is based
>> on the part number as described in the datasheet page 2.
>>
>> Signed-off-by: Neil Armstrong <neil.armstrong@...aro.org>
> Hi Neil.
> 
> Pretty clean driver. Just a few minor comments inline.
> 
> Thanks,
> 
> Jonathan
> 
> 
>> diff --git a/drivers/iio/magnetometer/als31300.c b/drivers/iio/magnetometer/als31300.c
>> new file mode 100644
>> index 000000000000..123e6a63b516
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/als31300.c
>> @@ -0,0 +1,459 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Driver for the Allegro MicroSystems ALS31300 3-D Linear Hall Effect Sensor
>> + *
>> + * Copyright (c) 2024 Linaro Limited
>> + */
>> +
>> +#include <linux/bitfield.h>
>> +#include <linux/bits.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/i2c.h>
>> +#include <linux/regmap.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/regulator/consumer.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +
>> +/*
>> + * The Allegro MicroSystems ALS31300 has an EEPROM space to configure how
>> + * the device works and how the interrupt line behaves,
> 
> behaves.
> 
>> + * we only support the default setup with external trigger
> 
> 	Only the default setup with external trigger is supported.
> etc.

Ack, I'll rephrase that part

> 
>> + *
>> + * Since by default the interrupt line is disable, we don't
>> + * support GPIO interrupt events for now.
>> + *
>> + * It should be possible to adapt the driver to the current
>> + * device setup, but we leave it as a future exercise.
>> + */
>> +
>> +#define ALS31300_EEPROM_CONFIG		0x02
>> +#define ALS31300_EEPROM_INTERRUPT	0x03
>> +#define ALS31300_EEPROM_CUSTOMER_1	0x0d
>> +#define ALS31300_EEPROM_CUSTOMER_2	0x0e
>> +#define ALS31300_EEPROM_CUSTOMER_3	0x0f
>> +#define ALS31300_VOLATILE_MODE		0x27
> 
> Is spelling out volatile needed? Maybe VOL or just V or skip
> it completely as it makes for some long lines?

Yep, there's no formal naming on the datasheet for those so I'll
reduce to _VOL

> 
>> +#define ALS31300_VOLATILE_MODE_LPDCM		GENMASK(6, 4)
>> +#define ALS31300_VOLATILE_MODE_SLEEP		GENMASK(1, 0)
>> +#define ALS31300_VOLATILE_MSB		0x28
>> +#define ALS31300_VOLATILE_MSB_TEMPERATURE	GENMASK(5, 0)
>> +#define ALS31300_VOLATILE_MSB_INTERRUPT		BIT(6)
>> +#define ALS31300_VOLATILE_MSB_NEW_DATA		BIT(7)
>> +#define ALS31300_VOLATILE_MSB_Z_AXIS		GENMASK(15, 8)
>> +#define ALS31300_VOLATILE_MSB_Y_AXIS		GENMASK(23, 16)
>> +#define ALS31300_VOLATILE_MSB_X_AXIS		GENMASK(31, 24)
>> +#define ALS31300_VOLATILE_LSB		0x29
>> +#define ALS31300_VOLATILE_LSB_TEMPERATURE	GENMASK(5, 0)
>> +#define ALS31300_VOLATILE_LSB_HALL_STATUS	GENMASK(7, 7)
>> +#define ALS31300_VOLATILE_LSB_Z_AXIS		GENMASK(11, 8)
>> +#define ALS31300_VOLATILE_LSB_Y_AXIS		GENMASK(15, 12)
>> +#define ALS31300_VOLATILE_LSB_X_AXIS		GENMASK(19, 16)
>> +#define ALS31300_VOLATILE_LSB_INTERRUPT_WRITE	BIT(20)
>> +#define ALS31300_CUSTOMER_ACCESS	0x35
>> +
>> +#define ALS31300_LPDCM_INACTIVE_0_5_MS		0
>> +#define ALS31300_LPDCM_INACTIVE_1_0_MS		1
>> +#define ALS31300_LPDCM_INACTIVE_5_0_MS		2
>> +#define ALS31300_LPDCM_INACTIVE_10_0_MS		3
>> +#define ALS31300_LPDCM_INACTIVE_50_0_MS		4
>> +#define ALS31300_LPDCM_INACTIVE_100_0_MS	5
>> +#define ALS31300_LPDCM_INACTIVE_500_0_MS	6
>> +#define ALS31300_LPDCM_INACTIVE_1000_0_MS	7
> I'd move these up to next to the field def above.
> Can play games with indent to make it clear they are the contents of
> that field.
> 
> #define ALS31300_VOLATILE_MODE_LPDCM		GENMASK(6, 4)
> #define   ALS31300_LPDCM_INACTIVE_0_5_MS	0
> etc

If it's accepted, then I'll do it!

> 
> 
>> +
>> +#define ALS31300_VOLATILE_MODE_ACTIVE_MODE	0
>> +#define ALS31300_VOLATILE_MODE_SLEEP_MODE	1
>> +#define ALS31300_VOLATILE_MODE_LPDCM_MODE	2
>> +
>> +#define ALS31300_DATA_X_GET(__buf)			\
> 
> Why __buf?  I'd just use b

Right

> 
>> +		((int)(s8)FIELD_GET(ALS31300_VOLATILE_MSB_X_AXIS, __buf[0]) << 4 | \
>> +			  FIELD_GET(ALS31300_VOLATILE_LSB_X_AXIS, __buf[1]))
>> +#define ALS31300_DATA_Y_GET(__buf)			\
>> +		((int)(s8)FIELD_GET(ALS31300_VOLATILE_MSB_Y_AXIS, __buf[0]) << 4 | \
>> +			  FIELD_GET(ALS31300_VOLATILE_LSB_Y_AXIS, __buf[1]))
>> +#define ALS31300_DATA_Z_GET(__buf)			\
>> +		((int)(s8)FIELD_GET(ALS31300_VOLATILE_MSB_Z_AXIS, __buf[0]) << 4 | \
>> +			  FIELD_GET(ALS31300_VOLATILE_LSB_Z_AXIS, __buf[1]))
> 
> Nice way to make these more readable is sign_extend32() rather than the casts.
> So
> 	sign_extend32(FIELD_GET(ALS31300_VOLATILE_MSB_X_AXIS, b[0]) << 4 |
> 		      FIELD_GET(ALS31300_VOLATILE_LSB_X_AXIS, b[1]),
> 		      11);

Thanks for the suggestion, it's indeed better

> 
> 
>> +#define ALS31300_TEMPERATURE_GET(__buf)			\
>> +		((u32)(u8)FIELD_GET(ALS31300_VOLATILE_MSB_TEMPERATURE, __buf[0]) << 6 | \
>> +			  FIELD_GET(ALS31300_VOLATILE_LSB_TEMPERATURE, __buf[1]))
> 
> What does the u8 cast change?

It was to align with the s8 defines, but it won't be needed with sign_extend32

> 
>> +
> 
>> +struct als31300_data {
>> +	struct device *dev;
>> +	/* protects power on/off the device and access HW */
>> +	struct mutex mutex;
>> +	unsigned long sensitivity;
>> +	struct regmap *map;
>> +	struct {
>> +		u16 temperature;
>> +		s16 channels[3];
>> +		s64 timestamp __aligned(8);
> aligned_s64 timestamp
> 
> 
> It's new so for now only in the togreg branch of iio.git.

Ack, I'll use it and rebase on your togreg branch

> 
>> +	} scan;
>> +};
>> +
>> +/* The whole measure is split into 2x32bit registers, we need to read them both at once */
>> +static int als31300_get_measure(struct als31300_data *data, s16 *t, s16 *x,
>> +				s16 *y, s16 *z)
>> +{
>> +	unsigned int count = 0;
>> +	u32 buf[2];
>> +	int ret;
>> +
>> +	mutex_lock(&data->mutex);
> 
> 	guard(mutex)(&data->mutex) and drop the unlock handling.
> It's a small simplification but still nice to have here.

Ok, it'll be my first time, hope I'll get it right

> 
>> +	ret = pm_runtime_resume_and_get(data->dev);
>> +	if (ret)
>> +		goto unlock;
>> +
>> +	/* Max update rate it 2KHz, wait up to 1ms */
>> +	while (count < 50) {
>> +		/* Read Data */
>> +		ret = regmap_bulk_read(data->map, ALS31300_VOLATILE_MSB, buf, 2);
>> +		if (ret) {
>> +			dev_err(data->dev, "read data failed, error %d\n", ret);
>> +			goto out;
>> +		}
>> +
>> +		/* Check if data is valid, happens right after getting out of sleep mode */
>> +		if (FIELD_GET(ALS31300_VOLATILE_MSB_NEW_DATA, buf[0]))
>> +			break;
>> +
>> +		usleep_range(10, 20);
>> +		++count;
>> +	}
>> +
>> +	if (count >= 50) {
>> +		ret = -ETIMEDOUT;
>> +		goto out;
>> +	}
>> +
>> +	*t = ALS31300_TEMPERATURE_GET(buf);
>> +	*x = ALS31300_DATA_X_GET(buf);
>> +	*y = ALS31300_DATA_Y_GET(buf);
>> +	*z = ALS31300_DATA_Z_GET(buf);
>> +
>> +out:
>> +	pm_runtime_mark_last_busy(data->dev);
>> +	pm_runtime_put_autosuspend(data->dev);
>> +
>> +unlock:
>> +	mutex_unlock(&data->mutex);
>> +
>> +	return ret;
>> +}
>> +
>> +static int als31300_read_raw(struct iio_dev *indio_dev,
>> +			     const struct iio_chan_spec *chan, int *val,
>> +			     int *val2, long mask)
>> +{
>> +	struct als31300_data *data = iio_priv(indio_dev);
>> +	s16 t, x, y, z;
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_PROCESSED:
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = als31300_get_measure(data, &t, &x, &y, &z);
>> +		if (ret)
>> +			return ret;
> 
> blank line here would perhaps make this a tiny bit easier to read.

Ack

> 
>> +		switch (chan->address) {
>> +		case TEMPERATURE:
>> +			*val = t;
>> +			return IIO_VAL_INT;
>> +		case AXIS_X:
>> +			*val = x;
>> +			return IIO_VAL_INT;
>> +		case AXIS_Y:
>> +			*val = y;
>> +			return IIO_VAL_INT;
>> +		case AXIS_Z:
>> +			*val = z;
>> +			return IIO_VAL_INT;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	case IIO_CHAN_INFO_SCALE:
>> +		switch (chan->type) {
>> +		case IIO_TEMP:
>> +			/*
>> +			 * Fractional part of:
>> +			 *         302(value - 1708)
>> +			 * temp = ------------------
>> +			 *             4096
>> +			 * to convert temperature in Celcius
> 
> Units in IIO ABI (because we copied hwmon) are millidegrees celcius.
> Bad decision a long time back, but we are stuck with it.
> See Documentation/ABI/testing/sysfs-bus-iio

I was wondering, now I have my answer :-)

> 
>> +			 */
>> +			*val = 302;
>> +			*val2 = 4096;
>> +			return IIO_VAL_FRACTIONAL;
>> +		case IIO_MAGN:
>> +			/*
>> +			 * Devices are configured in factory
>> +			 * with different sensitivities:
>> +			 * - 500 GAUSS <-> 4 LSB/Gauss
>> +			 * - 1000 GAUSS <-> 2 LSB/Gauss
>> +			 * - 2000 GAUSS <-> 1 LSB/Gauss
>> +			 * with translates by a division of the returned
>> +			 * value to get Gauss value.
>> +			 * The sensisitivity cannot be read at runtime
>> +			 * so the value depends on the model compatible
>> +			 * or device id.
>> +			 */
>> +			*val = 1;
>> +			*val2 = data->sensitivity;
>> +			return IIO_VAL_FRACTIONAL;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +	case IIO_CHAN_INFO_OFFSET:
>> +		switch (chan->type) {
>> +		case IIO_TEMP:
>> +			*val = -1708;
>> +			return IIO_VAL_INT;
>> +		default:
>> +			return -EINVAL;
>> +		}
>> +
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static irqreturn_t als31300_trigger_handler(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct als31300_data *data = iio_priv(indio_dev);
>> +	s16 x, y, z;
>> +	u16 t;
>> +	int ret;
>> +
>> +	ret = als31300_get_measure(data, &t, &x, &y, &z);
>> +	if (ret)
>> +		goto trigger_out;
>> +
>> +	data->scan.temperature = t;
>> +	data->scan.channels[0] = x;
>> +	data->scan.channels[1] = y;
>> +	data->scan.channels[2] = z;
> 
> This is pretty small. I'd just put scan on the stack in this function.

Ack

> 
>> +	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
>> +					   iio_get_time_ns(indio_dev));
> 
> pf->timestamp given you are providing a non threaded interrupt handler
> to fill that in.

Ok

> 
>> +
>> +trigger_out:
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
> 
>> +static const struct iio_chan_spec als31300_channels[] = {
>> +	{
>> +		.type = IIO_TEMP,
>> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +			BIT(IIO_CHAN_INFO_SCALE) |
>> +			BIT(IIO_CHAN_INFO_OFFSET),
>> +		.address = TEMPERATURE,
>> +		.scan_index = TEMPERATURE,
>> +		.scan_type = {
>> +			.sign = 'u',
>> +			.realbits = 16,
>> +			.storagebits = 16,
>> +			.endianness = IIO_CPU,
>> +		},
>> +	},
>> +	ALS31300_AXIS_CHANNEL(X, AXIS_X),
>> +	ALS31300_AXIS_CHANNEL(Y, AXIS_Y),
>> +	ALS31300_AXIS_CHANNEL(Z, AXIS_Z),
>> +	IIO_CHAN_SOFT_TIMESTAMP(6),
> 
> Why 6?
> 
> Technically it's not wrong ABI, just odd to leave a gap between the channels
> and the timestamp.  Probably wants to be 4

Seems it's a bad copy-paste and not knowing what 6 was meaning, thx for the clarification

> 
>> +};
> 
>> +static int als31300_probe(struct i2c_client *i2c)
>> +{
>> +	struct device *dev = &i2c->dev;
>> +	struct als31300_data *data;
>> +	struct iio_dev *indio_dev;
>> +	int ret;
>> +
>> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	data = iio_priv(indio_dev);
>> +	data->dev = dev;
>> +	i2c_set_clientdata(i2c, indio_dev);
>> +
>> +	mutex_init(&data->mutex);
>> +
>> +	data->sensitivity = (unsigned long)of_device_get_match_data(dev);
> After changing the data to pointers to structures below use
> i2c_get_match_data() That will try various types of firmware and fall
> back to the id tables if appropriate.

Yep I have a change that does that already, I figured that out after sending v1...

> 
>> +
>> +	data->map = devm_regmap_init_i2c(i2c, &als31300_regmap_config);
>> +	if (IS_ERR(data->map))
>> +		return dev_err_probe(dev, PTR_ERR(data->map),
>> +				     "failed to allocate register map\n");
> 
> ...
> 
> 
>> +
>> +static DEFINE_RUNTIME_DEV_PM_OPS(als31300_pm_ops,
>> +				 als31300_runtime_suspend, als31300_runtime_resume,
>> +				 NULL);
>> +
>> +static const struct i2c_device_id als31300_id[] = {
>> +	{ "als31300-500" },
> 
> This needs data as well because you can probe via the sysfs interface instead
> of DT which will use these ids.
> 
>> +	{ "als31300-1000" },
>> +	{ "als31300-2000" },
>> +	{ /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(i2c, als31300_id);
>> +
>> +static const struct of_device_id als31300_of_match[] = {
>> +	{ .compatible = "allegromicro,als31300-500", .data = (void *)4 },
>> +	{ .compatible = "allegromicro,als31300-1000", .data = (void *)2 },
>> +	{ .compatible = "allegromicro,als31300-2000", .data = (void *)1 },
> 
> Use pointers to structures and also use them above.  Even if those structures
> have just one value in them for now.
> 
> Just have something like
> 
> struct als31300_variant_info {
> 	u8 sensitivity;
> };
> 
> static const struct als31300_variant_info al31300_variant_500 = {
> 	.sensitivity = 4;
> };
> 
> etc.

Yep I'll switch to that

> 
> 
>> +	{ /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, als31300_of_match);
>> +
>> +static struct i2c_driver als31300_driver = {
>> +	.driver	 = {
>> +		.name = "als31300",
>> +		.of_match_table = als31300_of_match,
>> +		.pm = pm_ptr(&als31300_pm_ops),
>> +	},
>> +	.probe = als31300_probe,
>> +	.id_table = als31300_id,
>> +};
>> +module_i2c_driver(als31300_driver);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_DESCRIPTION("ALS31300 3-D Linear Hall Effect Driver");
>> +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@...aro.org>");
>>
> 

Thanks,
Neil

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ