From: Andy Shevchenko <andriy.shevchenko@intel.com>
To: Nikhil Gautam <nikhilgtr@gmail.com>
Cc: linux-iio@vger.kernel.org, jic23@kernel.org,
dlechner@baylibre.com, nuno.sa@analog.com, andy@kernel.org,
u.kleine-koenig@baylibre.com, robh@kernel.org,
krzk+dt@kernel.org, conor+dt@kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v3 2/2] iio: magnetometer: add support for Melexis MLX90393
Date: Mon, 29 Jun 2026 16:50:56 +0300 [thread overview]
Message-ID: <akJ4QCdhIKpfaNDD@ashevche-desk.local> (raw)
In-Reply-To: <20260627005843.7786-3-nikhilgtr@gmail.com>
On Sat, Jun 27, 2026 at 06:28:43AM +0530, Nikhil Gautam wrote:
> Add Industrial I/O subsystem support for the Melexis
> MLX90393 3-axis magnetometer and temperature sensor.
>
> The driver currently supports:
>
> raw magnetic field measurements
> raw temperature measurements
> configurable gain/scale selection
> configurable oversampling ratio
> direct mode operation
Can you add '-' or '*' in front of each item in the list?
> The MLX90393 supports both I2C and SPI interfaces. This
> initial implementation adds support for the I2C interface.
>
> The device uses a command-based communication protocol
> rather than a conventional register-addressed interface.
> A small transport abstraction layer is therefore used
> instead of regmap to share the common sensor logic
> between the current I2C implementation and future SPI
> support without duplicating code.
The commit message seems wrapped around 56 characters. We have capacity up to
~72, please use it.
...
> + * Datasheet: https://media.melexis.com/-/media/files/documents/datasheets/mlx90393-datasheet-melexis.pdf
Also add this line as a tag in the commit message (before your SoB).
...
> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
+ dev_printk.h // dev_err_probe()
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/types.h>
> +#include <linux/time64.h>
Please, keep them sorted alphabetically.
> +#include <linux/unaligned.h>
> +#include <linux/units.h>
...
> +struct mlx90393_data {
> + /* Protects sensor configuration and measurement operations */
> + struct mutex lock;
> + struct device *dev;
You need a forward declaration for struct device.
> + void *bus_context;
> + const struct mlx90393_transfer_ops *ops;
> + u8 gain_sel;
> + u8 hallconf;
> +
> + u8 res_xy;
> + u8 res_z;
> +
> + u8 dig_filt;
> + u8 osr;
> + u8 osr2;
> +};
...
> +/* Datasheet: Table no.17 */
> +static const int mlx90393_scale_table[MLX90393_AXIS_MAX][MLX90393_GAIN_MAX]
> + [MLX90393_RES_MAX] = {
Not good indentation. Just make it
static const int mlx90393_scale_table[][MLX90393_GAIN_MAX][MLX90393_RES_MAX] = {
which is precisely a single line.
> + /* XY axis */
> + {
> + { 751, 1502, 3004, 6009},
> + { 601, 1202, 2403, 4840},
> + { 451, 901, 1803, 3605},
> + { 376, 751, 1502, 3004},
> + { 300, 601, 1202, 2403},
> + { 250, 501, 1001, 2003},
> + { 200, 401, 801, 1602},
> + { 150, 300, 601, 1202},
> + },
> + /* Z axis */
> + {
> + { 1210, 2420, 4840, 9680},
> + { 968, 1936, 3872, 7744},
> + { 726, 1452, 2904, 5808},
> + { 605, 1210, 2420, 4840},
> + { 484, 968, 1936, 3872},
> + { 403, 807, 1613, 3227},
> + { 323, 645, 1291, 2581},
> + { 242, 484, 968, 1936},
> + }
Keep trailing comma(s) for non-terminator entries.
> +};
...
> +#define MLX90393_CHAN(idx, axis, addr) { \
> + .type = IIO_MAGN, \
> + .modified = 1, \
> + .channel = idx, \
> + .address = addr, \
> + .channel2 = IIO_MOD_##axis, \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
It's harder to read, make it either
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
or (which is also my preference)
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_SCALE), \
Do it for all such cases.
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\
> + .info_mask_separate_available = \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_shared_by_type_available = \
> + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
> +}
...
> + * Datasheet: Table 8, Page no. 12
If Table has a title, also mention it here
Table 8 "...title of the table..."
in such a case the page reference most likely may be dropped.
Same comment for all the similar cases.
...
> +static int mlx90393_get_tconv_us(struct mlx90393_data *data)
Can it return negative? What will be the meaning?
> +{
> + const int osr = data->osr;
> + const int osr2 = data->osr2;
> + const int df = data->dig_filt;
> +
No blank line in the definition block.
> + int tconvm;
> + int tconvt;
> +
Ditto, and why are tconv* signed?
> + int m = 3; /* X,Y,Z */
> +
> + /*
> + * TCONVM = 67 + 64 * 2^OSR * (2 + 2^DIG_FILT)
> + */
> + tconvm = 67 + (64 * BIT(osr) * (2 + BIT(df)));
> +
> + /*
> + * TCONVT = 67 + 192 * 2^OSR2
> + */
> + tconvt = 67 + (192 * BIT(osr2));
> + /*
> + * Total conversion time:
> + * TSTBY + TACTIVE + m * TCONVM + TCONVT + TCONV_END
> + */
> + return 220 + 360 + (m * tconvm) + tconvt + 120;
> +}
...
> +static int mlx90393_xfer(struct mlx90393_data *data,
> + const u8 *tx, int tx_len,
> + u8 *rx, int rx_len)
The last two lines may be joined.
> +{
> + return data->ops->xfer(data->bus_context, tx, tx_len, rx, rx_len);
> +}
...
> +static int mlx90393_update_bits(struct mlx90393_data *data, u8 reg,
> + u16 mask, u16 val)
> +{
> + u16 reg_val;
> + int ret;
> +
> + ret = mlx90393_read_reg(data, reg, ®_val);
> + if (ret)
> + return ret;
> +
> + reg_val &= ~mask;
> + reg_val |= (val << __ffs(mask)) & mask;
Isn't it field_prep() reinvention? (Note small letters in the name!)
> + return mlx90393_write_reg(data, reg, reg_val);
> +}
...
> + /* Wait conversion */
/* Wait for conversion to be done */
("wait for" is an English stanza that in great majority of the cases is used).
> + fsleep(mlx90393_get_tconv_us(data));
...
> +static int mlx90393_find_scale(struct mlx90393_data *data, bool z_axis,
> + int val, int val2,
> + int *gain)
> +{
> + u8 res;
> + enum mlx90393_axis_type axis;
Prefer reversed xmas tree order.
> + if (z_axis) {
> + axis = MLX90393_AXIS_TYPE_Z;
> + res = data->res_z;
> + } else {
> + axis = MLX90393_AXIS_TYPE_XY;
> + res = data->res_xy;
> + }
> + if (val != 0)
> + return -EINVAL;
This doesn't use res or axis, move it above.
> + for (unsigned int i = 0; i < ARRAY_SIZE(mlx90393_scale_table[0]); i++) {
Use [axis] instead of [0] for the consistency's sake.
> + if (mlx90393_scale_table[axis][i][res] == val2) {
> + *gain = i;
> + return 0;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +static int mlx90393_set_scale(struct mlx90393_data *data,
> + const struct iio_chan_spec *chan,
> + int val, int val2)
> +{
> + bool z_axis;
> + int gain;
> + int ret;
> + z_axis = chan->channel2 == IIO_MOD_Z;
Can we rather split _find_scale() to two and replace this with
if (chan->channel2 == IIO_MOD_Z)
ret = mlx90393_find_z_scale(data, val, val2, &gain);
else
ret = mlx90393_find_xy_scale(data, val, val2, &gain);
? I believe it will be less LoC.
> + ret = mlx90393_find_scale(data, z_axis, val, val2, &gain);
> + if (ret)
> + return ret;
> +
> + ret = mlx90393_update_bits(data, MLX90393_REG_CONF1, MLX90393_CONF1_GAIN_SEL,
> + gain);
> + if (ret)
> + return ret;
> +
> + data->gain_sel = gain;
> + return 0;
> +}
...
> +static int mlx90393_find_osr(int val, int *osr)
> +{
> + for (unsigned int i = 0; i < MLX90393_OSR_MAX; i++) {
One space too many.
> + if (mlx90393_osr_avail[i] == val) {
> + *osr = i;
> + return 0;
> + }
> + }
> +
> + return -EINVAL;
> +}
...
> +static int mlx90393_set_osr(struct mlx90393_data *data, int val)
> +{
> + int osr;
> + int ret;
> +
> + ret = mlx90393_find_osr(val, &osr);
> + if (ret)
> + return ret;
> +
> + if (osr == data->osr)
> + return 0;
> +
> + ret = mlx90393_update_bits(data, MLX90393_REG_CONF3, MLX90393_CONF3_OSR,
> + osr);
Just make line longer, in this case it will be better to read in my opinion.
> + if (ret)
> + return ret;
> +
> + data->osr = osr;
> + return 0;
> +}
...
> +static int mlx90393_set_temp_osr2(struct mlx90393_data *data, int val)
> +{
> + int ret;
> +
> + if (val < 0 || val >= MLX90393_OSR2_MAX)
> + return -EINVAL;
> +
> + if (val == data->osr2)
> + return 0;
> +
> + ret = mlx90393_update_bits(data, MLX90393_REG_CONF3, MLX90393_CONF3_OSR2,
> + val);
Ditto.
> + if (ret)
> + return ret;
> +
> + data->osr2 = val;
> +
> + return 0;
> +}
...
> +static int mlx90393_write_raw(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int val, int val2,
> + long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE: {
> + guard(mutex)(&data->lock);
> + return mlx90393_set_scale(data, chan, val, val2);
> + }
> +
Be consistent with the style of switch-case. Either add blank lines in all of
them in all cases, or drop everywhere.
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
> + guard(mutex)(&data->lock);
> + switch (chan->type) {
> + case IIO_TEMP:
> + return mlx90393_set_temp_osr2(data, val);
> +
> + case IIO_MAGN:
> + return mlx90393_set_osr(data, val);
> +
> + default:
> + return -EINVAL;
> + }
> + }
> + default:
> + return -EINVAL;
> + }
> +}
...
> +static int mlx90393_read_raw(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int *val, int *val2, long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> + int ret;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_RAW: {
> + guard(mutex)(&data->lock);
> + ret = mlx90393_read_measurement(data, chan->address, val);
> + if (ret)
> + return ret;
> +
> + return IIO_VAL_INT;
> + }
Misindented.
> + case IIO_CHAN_INFO_SCALE:
> + switch (chan->type) {
> + case IIO_MAGN:
> + return mlx90393_get_scale(data, chan, val, val2);
> +
> + case IIO_TEMP:
> + /*
> + * Datasheet Table 7: Thermal Specification
> + */
> + *val = 0;
> + *val2 = 22124;
> + return IIO_VAL_INT_PLUS_MICRO;
> +
> + default:
> + return -EINVAL;
> + }
> +
> + case IIO_CHAN_INFO_OFFSET:
> + if (chan->type != IIO_TEMP)
> + return -EINVAL;
> + /*
> + * Datasheet Table 7: Thermal Specification
> + */
This is a single line comment.
> +
This blank line should be before the comment and not after.
> + *val = -45114;
> + return IIO_VAL_INT;
> +
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + switch (chan->type) {
> + case IIO_TEMP:
> + return mlx90393_get_temp_osr2(data, val);
> + case IIO_MAGN:
> + return mlx90393_get_osr(data, val);
> + default:
> + return -EINVAL;
> + }
> + default:
> + return -EINVAL;
> + }
> +}
...
> +static int mlx90393_read_avail(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + const int **vals,
> + int *type,
> + int *length,
> + long mask)
> +{
> + struct mlx90393_data *data = iio_priv(indio_dev);
> + static int scale_avail[MLX90393_GAIN_MAX][MLX90393_AXIS_MAX];
> + enum mlx90393_axis_type axis;
> + u8 res;
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SCALE: {
> + guard(mutex)(&data->lock);
> + axis = chan->channel2 == IIO_MOD_Z;
> + res = axis ? data->res_z : data->res_xy;
res = chan->channel2 == IIO_MOD_Z ? data->res_z : data->res_xy;
This fits a single line, no axis variable is needed.
> + for (unsigned int i = 0; i < MLX90393_GAIN_MAX; i++) {
> + scale_avail[i][0] = 0;
> + scale_avail[i][1] =
> + mlx90393_scale_table[axis][i][res];
> + }
> +
> + *vals = &scale_avail[0][0];
> + *type = IIO_VAL_INT_PLUS_NANO;
> + *length = MLX90393_GAIN_MAX * MLX90393_AXIS_MAX;
> + return IIO_AVAIL_LIST;
> + }
> +
> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + if (chan->type == IIO_TEMP) {
> + *vals = mlx90393_osr2_avail;
> + *type = IIO_VAL_INT;
> + *length = MLX90393_OSR2_MAX;
> + } else {
> + *vals = mlx90393_osr_avail;
> + *type = IIO_VAL_INT;
> + *length = MLX90393_OSR_MAX;
> + }
> + return IIO_AVAIL_LIST;
> +
> + default:
> + return -EINVAL;
> + }
> +}
...
> +static int mlx90393_init(struct mlx90393_data *data)
> +{
> + int ret;
> + u16 reg;
> +
> + /* Exit mode */
> + ret = mlx90393_write_cmd(data, MLX90393_CMD_EX);
> + if (ret)
> + return ret;
> +
> + /*
> + * Datasheet section 15.4.1.2 (RT command), Figure 16:
> + * Wait 1 ms after EX command before issuing RT.
> + */
> + fsleep(1 * USEC_PER_MSEC);
> +
> + /* Reset device */
> + ret = mlx90393_write_cmd(data, MLX90393_CMD_RT);
> + if (ret)
> + return ret;
> +
> + /*
> + * Datasheet section 15.4.1.2 (RT command), Figure 16:
> + * Wait 1.5 ms for the start-up sequence to complete.
> + */
> + fsleep(1.5 * USEC_PER_MSEC);
He-he, this is a float number. While it might work, it's better to use integer
numbers. Yeah, for the sake of consistency the above also better with 1000.
> +
> + ret = mlx90393_read_reg(data, MLX90393_REG_CONF1, ®);
> + if (ret)
> + return ret;
> +
> + data->gain_sel = FIELD_GET(MLX90393_CONF1_GAIN_SEL, reg);
> + data->hallconf = FIELD_GET(MLX90393_CONF1_HALLCONF, reg);
> +
> + ret = mlx90393_read_reg(data, MLX90393_REG_CONF3, ®);
> + if (ret)
> + return ret;
> +
> + data->res_xy = FIELD_GET(MLX90393_CONF3_RES_X, reg);
> + data->res_z = FIELD_GET(MLX90393_CONF3_RES_Z, reg);
> + data->dig_filt = FIELD_GET(MLX90393_CONF3_DIG_FILT, reg);
> + data->osr = FIELD_GET(MLX90393_CONF3_OSR, reg);
> + data->osr2 = FIELD_GET(MLX90393_CONF3_OSR2, reg);
> +
> + return 0;
> +}
...
> +#include <linux/array_size.h>
> +#include <linux/errno.h>
> +#include <linux/i2c.h>
> +#include <linux/module.h>
> +#include <linux/mod_devicetable.h>
+ types.h // uXX
...
> + struct i2c_client *client = context;
> + int ret;
> + struct i2c_msg msgs[2] = {
Keep reversed xmas tree order.
> + [0] = {
> + .addr = client->addr,
> + .len = tx_len,
> + .buf = (u8 *)tx,
> + },
> + [1] = {
> + .addr = client->addr,
> + .flags = I2C_M_RD,
> + .len = rx_len,
> + .buf = rx,
> + },
> + };
...
> + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> + if (ret != ARRAY_SIZE(msgs))
> + return ret < 0 ? ret : -EIO;
> +
> + return 0;
Do it in a regular pattern
if (ret < 0)
return ret;
if (ret != ARRAY_SIZE(msgs))
return -EIO;
> +}
> +static struct i2c_driver mlx90393_i2c_driver = {
> + .driver = {
> + .name = "mlx90393",
> + .of_match_table = mlx90393_of_match,
> + },
> + .probe = mlx90393_i2c_probe,
> + .id_table = mlx90393_id,
> +};
> +
Redundant blank line.
> +module_i2c_driver(mlx90393_i2c_driver);
--
With Best Regards,
Andy Shevchenko
next prev parent reply other threads:[~2026-06-29 13:51 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-27 0:58 Nikhil Gautam
2026-06-27 0:58 ` [PATCH v3 1/2] dt-bindings: iio: magnetometer: add Melexis MLX90393 Nikhil Gautam
2026-06-27 1:04 ` sashiko-bot
2026-06-27 2:26 ` Rob Herring (Arm)
2026-06-27 18:41 ` David Lechner
2026-06-27 0:58 ` [PATCH v3 2/2] iio: magnetometer: add support for " Nikhil Gautam
2026-06-27 1:10 ` sashiko-bot
2026-06-29 13:50 ` Andy Shevchenko [this message]
2026-06-27 18:30 ` [PATCH v3 0/2] " David Lechner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=akJ4QCdhIKpfaNDD@ashevche-desk.local \
--to=andriy.shevchenko@intel.com \
--cc=andy@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dlechner@baylibre.com \
--cc=jic23@kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=nikhilgtr@gmail.com \
--cc=nuno.sa@analog.com \
--cc=robh@kernel.org \
--cc=u.kleine-koenig@baylibre.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox