Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 0/3] iio: magnetometer: add driver for QST QMC5883L Sensor
From: Siratul Islam @ 2026-06-16 11:49 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: siratul.islam, dlechner, nuno.sa, andy, linux-iio, devicetree,
	linux-kernel

This patch series introduces the QST QMC5883L 3-Axis Magnetic Sensor
driver. It is a simple device with minimal magnetometer functionalities.
Commonly used as (software incompatible) replacement for the
Honeywell HMC5883L sensor.

This driver implements the basic functionalities of the QMC5883L sensor,
and intentionally leaves out some features like DRDY interrupt pin support
and power management for simplicity, both of which will be addressed
in future patches.

There was an attempt to introduce this device about an year ago but
the author seems to have abandoned the patch series. Since the device
is simple enough, I decided to start from scratch.

Note: I also noticed a patch for the QMC5883P variant. Despite similar
naming, the sensors are different including different register maps,
so these devices are not compatible with each other.

---
Changes in v2:
- Update commit message in binding.
- Reformat header includes.
- Remove iio_device_claim_direct()/iio_device_release_direct()
- Read the measurement registers before checking the OVL status bit
- Replace scoped_guard() with guard()
- Fix function parameter styling
- Replace FIELD_PREP_CONST() with FIELD_PREP()
- Replace qmc5883l_rng_avail[] with a 2D qmc5883l_scales[][2] array
- Update mutex comment
- Add a devm_add_action_or_reset() callback
- Use a named initializer in the i2c_device_id table
- Combine u8 rng, osr, odr; onto one line
- Various styling fixes
- Update maintainer email to siratul.islam@linux.dev

Link to v1: https://lore.kernel.org/linux-iio/20260612124557.13750-1-email@sirat.me/

Siratul Islam (3):
  dt-bindings: add entry for qstcorp
  dt-bindings: iio: magnetometer: add QST QMC5883L Sensor
  iio: magnetometer: add driver for QST QMC5883L Sensor

 .../iio/magnetometer/qstcorp,qmc5883l.yaml    |  52 ++
 .../devicetree/bindings/vendor-prefixes.yaml  |   2 +
 MAINTAINERS                                   |   7 +
 drivers/iio/magnetometer/Kconfig              |  11 +
 drivers/iio/magnetometer/Makefile             |   2 +
 drivers/iio/magnetometer/qmc5883l.c           | 516 ++++++++++++++++++
 6 files changed, 590 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
 create mode 100644 drivers/iio/magnetometer/qmc5883l.c

--
2.54.0


^ permalink raw reply

* [PATCH v2 1/3] dt-bindings: add entry for qstcorp
From: Siratul Islam @ 2026-06-16 11:49 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: siratul.islam, dlechner, nuno.sa, andy, linux-iio, devicetree,
	linux-kernel, Conor Dooley
In-Reply-To: <20260616114942.37241-1-siratul.islam@linux.dev>

Add an entry for QST Corporation Limited
Link: https://www.qstcorp.com/

Acked-by: Conor Dooley <conor.dooley@microchip.com>
Signed-off-by: Siratul Islam <siratul.islam@linux.dev>
---
 Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index 28784d66ae7b..11aac47f90ce 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1355,6 +1355,8 @@ patternProperties:
     description: Shenzhen QiShenglong Industrialist Co., Ltd.
   "^qnap,.*":
     description: QNAP Systems, Inc.
+  "^qstcorp,.*":
+    description: QST Corporation Limited
   "^quanta,.*":
     description: Quanta Computer Inc.
   "^radxa,.*":
-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 2/3] dt-bindings: iio: magnetometer: add QST QMC5883L Sensor
From: Siratul Islam @ 2026-06-16 11:49 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: siratul.islam, dlechner, nuno.sa, andy, linux-iio, devicetree,
	linux-kernel
In-Reply-To: <20260616114942.37241-1-siratul.islam@linux.dev>

Add devicetree binding for the QST QMC5883L 3-Axis Magnetic Sensor
connected via I2C.
Used enum so that more devices could use this binding

Signed-off-by: Siratul Islam <siratul.islam@linux.dev>
---
 .../iio/magnetometer/qstcorp,qmc5883l.yaml    | 52 +++++++++++++++++++
 MAINTAINERS                                   |  6 +++
 2 files changed, 58 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml

diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
new file mode 100644
index 000000000000..38abd083a4fb
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883l.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: QST QMC5883L 3-Axis Magnetic Sensor
+
+maintainers:
+  - Siratul Islam <siratul.islam@linux.dev>
+
+description: |
+  QST QMC5883L 3-Axis Magnetic Sensor on I2C bus.
+  https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf
+
+properties:
+  compatible:
+    enum:
+      - qstcorp,qmc5883l
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  vdd-supply: true
+
+  vddio-supply: true
+
+additionalProperties: false
+
+required:
+  - compatible
+  - reg
+  - vdd-supply
+  - vddio-supply
+
+examples:
+  - |
+    i2c {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        magnetometer@d {
+            compatible = "qstcorp,qmc5883l";
+            reg = <0x0d>;
+            vdd-supply = <&vdd_3v3_reg>;
+            vddio-supply = <&vdd_3v3_reg>;
+        };
+    };
+...
diff --git a/MAINTAINERS b/MAINTAINERS
index e035a3be797c..1127403c579b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21787,6 +21787,12 @@ F:	Documentation/networking/device_drivers/ethernet/freescale/dpaa2/overview.rst
 F:	drivers/bus/fsl-mc/
 F:	include/uapi/linux/fsl_mc.h
 
+QST QMC5883L 3-Axis Magnetic Sensor
+M:	Siratul Islam <siratul.islam@linux.dev>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
+
 QT1010 MEDIA DRIVER
 L:	linux-media@vger.kernel.org
 S:	Orphan
-- 
2.54.0


^ permalink raw reply related

* [PATCH v2 3/3] iio: magnetometer: add driver for QST QMC5883L Sensor
From: Siratul Islam @ 2026-06-16 11:49 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: siratul.islam, dlechner, nuno.sa, andy, linux-iio, devicetree,
	linux-kernel
In-Reply-To: <20260616114942.37241-1-siratul.islam@linux.dev>

Add driver for the QST QMC5883L 3-Axis Magnetic Sensor
connected via i2c.

Signed-off-by: Siratul Islam <siratul.islam@linux.dev>
---
 MAINTAINERS                         |   1 +
 drivers/iio/magnetometer/Kconfig    |  11 +
 drivers/iio/magnetometer/Makefile   |   2 +
 drivers/iio/magnetometer/qmc5883l.c | 516 ++++++++++++++++++++++++++++
 4 files changed, 530 insertions(+)
 create mode 100644 drivers/iio/magnetometer/qmc5883l.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 1127403c579b..0f9ad3b49a5d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -21792,6 +21792,7 @@ M:	Siratul Islam <siratul.islam@linux.dev>
 L:	linux-iio@vger.kernel.org
 S:	Maintained
 F:	Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883l.yaml
+F:	drivers/iio/magnetometer/qmc5883l.c
 
 QT1010 MEDIA DRIVER
 L:	linux-media@vger.kernel.org
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index fb313e591e85..615564174086 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -198,6 +198,17 @@ config INFINEON_TLV493D
 	  To compile this driver as a module, choose M here: the module
 	  will be called tlv493d.
 
+config QMC5883L
+	tristate "QST QMC5883L 3-Axis Magnetic Sensor"
+	depends on I2C
+	select REGMAP_I2C
+	help
+	  Say Y here to add support driver for QST QMC5883L 3-Axis
+	  Magnetic Sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called qmc5883l.
+
 config SENSORS_HMC5843
 	tristate
 	select IIO_BUFFER
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 5bd227f8c120..552682555d86 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -26,6 +26,8 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
 
 obj-$(CONFIG_INFINEON_TLV493D)		+= tlv493d.o
 
+obj-$(CONFIG_QMC5883L)			+= qmc5883l.o
+
 obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
 obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
 obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
diff --git a/drivers/iio/magnetometer/qmc5883l.c b/drivers/iio/magnetometer/qmc5883l.c
new file mode 100644
index 000000000000..e1addcaf0551
--- /dev/null
+++ b/drivers/iio/magnetometer/qmc5883l.c
@@ -0,0 +1,516 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
+/*
+ * Support for QST QMC5883L 3-Axis Magnetic Sensor on I2C bus.
+ *
+ * Copyright (C) 2026 Siratul Islam <siratul.islam@linux.dev>
+ *
+ * Datasheet available at
+ * <https://www.qstcorp.com/upload/pdf/202512/13-52-04%20QMC5883L%20Datasheet%20Rev.%20B.pdf>
+ *
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+
+#include <linux/iio/iio.h>
+
+#include <asm/byteorder.h>
+
+#define QMC5883L_REG_X_LSB	0x00
+#define QMC5883L_REG_STATUS1	0x06
+#define QMC5883L_REG_CTRL1	0x09
+#define QMC5883L_REG_CTRL2	0x0A
+#define QMC5883L_REG_SET_RESET	0x0B
+#define QMC5883L_REG_ID		0x0D
+
+#define QMC5883L_CHIP_ID	0xFF
+
+#define QMC5883L_MODE_MASK	GENMASK(1, 0)
+#define QMC5883L_ODR_MASK	GENMASK(3, 2)
+#define QMC5883L_RNG_MASK	GENMASK(5, 4)
+#define QMC5883L_OSR_MASK	GENMASK(7, 6)
+
+#define QMC5883L_MODE_STANDBY	0x00
+#define QMC5883L_MODE_CONT	0x01
+
+#define QMC5883L_ODR_10HZ	0x00
+#define QMC5883L_ODR_50HZ	0x01
+#define QMC5883L_ODR_100HZ	0x02
+#define QMC5883L_ODR_200HZ	0x03
+
+#define QMC5883L_RNG_2G		0x00
+#define QMC5883L_RNG_8G		0x01
+
+#define QMC5883L_OSR_512	0x00
+#define QMC5883L_OSR_256	0x01
+#define QMC5883L_OSR_128	0x02
+#define QMC5883L_OSR_64		0x03
+
+#define QMC5883L_STATUS_DRDY	BIT(0)
+#define QMC5883L_STATUS_OVL	BIT(1)
+
+#define QMC5883L_SET_RESET_VAL	BIT(0)
+#define QMC5883L_INT_DISABLE	BIT(0)
+#define QMC5883L_SOFT_RESET	BIT(7)
+
+/* POR completion time max per datasheet */
+#define QMC5883L_PORT_US	350
+
+struct qmc5883l_data {
+	struct regmap *regmap;
+	/*
+	 * Protect data->range/odr/osr.
+	 * Protect poll and read during measurement.
+	 */
+	struct mutex mutex;
+	u8 range;
+	u8 odr;
+	u8 osr;
+};
+
+enum qmc5883l_chan {
+	QMC5883L_AXIS_X,
+	QMC5883L_AXIS_Y,
+	QMC5883L_AXIS_Z
+};
+
+static const int qmc5883l_odr_avail[] = { 10, 50, 100, 200 };
+
+static const int qmc5883l_osr_avail[] = { 512, 256, 128, 64 };
+
+static const int qmc5883l_scales[][2] = {
+	[QMC5883L_RNG_2G] = { 0, 83333 },
+	[QMC5883L_RNG_8G] = { 0, 333333 },
+};
+
+static int qmc5883l_take_measurement(struct iio_dev *indio_dev, int index,
+				     int *val)
+{
+	struct qmc5883l_data *data = iio_priv(indio_dev);
+	unsigned int status;
+	__le16 buf[3];
+	int ret;
+
+	guard(mutex) (&data->mutex);
+
+	/* 50ms headroom over the slowest ODR (10Hz) */
+	ret = regmap_read_poll_timeout(data->regmap,
+				       QMC5883L_REG_STATUS1,
+				       status, (status & QMC5883L_STATUS_DRDY),
+				       2 * USEC_PER_MSEC, 150 * USEC_PER_MSEC);
+	if (ret)
+		return ret;
+
+	ret = regmap_bulk_read(data->regmap, QMC5883L_REG_X_LSB, buf,
+			       sizeof(buf));
+	if (ret)
+		return ret;
+
+	if (status & QMC5883L_STATUS_OVL)
+		return -ERANGE;
+
+	*val = (s16)le16_to_cpu(buf[index]);
+
+	return 0;
+}
+
+static int qmc5883l_read_raw(struct iio_dev *indio_dev,
+			     const struct iio_chan_spec *chan,
+			     int *val, int *val2, long mask)
+{
+	struct qmc5883l_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = qmc5883l_take_measurement(indio_dev, chan->address, val);
+		if (ret)
+			return ret;
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE: {
+		guard(mutex)(&data->mutex);
+
+		*val = qmc5883l_scales[data->range][0];
+		*val2 = qmc5883l_scales[data->range][1];
+
+		return IIO_VAL_INT_PLUS_NANO;
+	}
+	case IIO_CHAN_INFO_SAMP_FREQ: {
+		guard(mutex)(&data->mutex);
+
+		switch (data->odr) {
+		case QMC5883L_ODR_200HZ:
+			*val = 200;
+			break;
+		case QMC5883L_ODR_100HZ:
+			*val = 100;
+			break;
+		case QMC5883L_ODR_50HZ:
+			*val = 50;
+			break;
+		case QMC5883L_ODR_10HZ:
+			*val = 10;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		return IIO_VAL_INT;
+	}
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+		guard(mutex)(&data->mutex);
+
+		switch (data->osr) {
+		case QMC5883L_OSR_64:
+			*val = 64;
+			break;
+		case QMC5883L_OSR_128:
+			*val = 128;
+			break;
+		case QMC5883L_OSR_256:
+			*val = 256;
+			break;
+		case QMC5883L_OSR_512:
+			*val = 512;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		return IIO_VAL_INT;
+	}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int qmc5883l_write_raw(struct iio_dev *indio_dev,
+			      const struct iio_chan_spec *chan,
+			      int val, int val2, long mask)
+{
+	struct qmc5883l_data *data = iio_priv(indio_dev);
+	u8 rng, osr, odr;
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE: {
+		if (val != 0)
+			return -EINVAL;
+
+		if (val2 == qmc5883l_scales[QMC5883L_RNG_2G][1])
+			rng = QMC5883L_RNG_2G;
+		else if (val2 == qmc5883l_scales[QMC5883L_RNG_8G][1])
+			rng = QMC5883L_RNG_8G;
+		else
+			return -EINVAL;
+
+		guard(mutex)(&data->mutex);
+
+		ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+					 QMC5883L_RNG_MASK,
+					 FIELD_PREP(QMC5883L_RNG_MASK, rng));
+		if (ret)
+			return ret;
+
+		data->range = rng;
+
+		return 0;
+	}
+	case IIO_CHAN_INFO_SAMP_FREQ: {
+		switch (val) {
+		case 200:
+			odr = QMC5883L_ODR_200HZ;
+			break;
+		case 100:
+			odr = QMC5883L_ODR_100HZ;
+			break;
+		case 50:
+			odr = QMC5883L_ODR_50HZ;
+			break;
+		case 10:
+			odr = QMC5883L_ODR_10HZ;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		guard(mutex)(&data->mutex);
+
+		ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+					 QMC5883L_ODR_MASK,
+					 FIELD_PREP(QMC5883L_ODR_MASK, odr));
+		if (ret)
+			return ret;
+
+		data->odr = odr;
+
+		return 0;
+	}
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+		switch (val) {
+		case 64:
+			osr = QMC5883L_OSR_64;
+			break;
+		case 128:
+			osr = QMC5883L_OSR_128;
+			break;
+		case 256:
+			osr = QMC5883L_OSR_256;
+			break;
+		case 512:
+			osr = QMC5883L_OSR_512;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		guard(mutex)(&data->mutex);
+
+		ret = regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+					 QMC5883L_OSR_MASK,
+					 FIELD_PREP(QMC5883L_OSR_MASK, osr));
+		if (ret)
+			return ret;
+
+		data->osr = osr;
+
+		return 0;
+	}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int qmc5883l_read_avail(struct iio_dev *indio_dev,
+			       struct iio_chan_spec const *chan,
+			       const int **vals, int *type, int *length,
+			       long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*vals = qmc5883l_odr_avail;
+		*type = IIO_VAL_INT;
+		*length = ARRAY_SIZE(qmc5883l_odr_avail);
+		return IIO_AVAIL_LIST;
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		*vals = qmc5883l_osr_avail;
+		*type = IIO_VAL_INT;
+		*length = ARRAY_SIZE(qmc5883l_osr_avail);
+		return IIO_AVAIL_LIST;
+	case IIO_CHAN_INFO_SCALE:
+		*vals = (const int *)qmc5883l_scales;
+		*type = IIO_VAL_INT_PLUS_NANO;
+		*length = ARRAY_SIZE(qmc5883l_scales) * 2;
+		return IIO_AVAIL_LIST;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int qmc5883l_write_raw_get_fmt(struct iio_dev *indio_dev,
+				      struct iio_chan_spec const *chan,
+				      long mask)
+{
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return IIO_VAL_INT_PLUS_NANO;
+	default:
+		return IIO_VAL_INT;
+	}
+}
+
+static const struct iio_info qmc5883l_info = {
+	.read_raw = qmc5883l_read_raw,
+	.write_raw = qmc5883l_write_raw,
+	.read_avail = qmc5883l_read_avail,
+	.write_raw_get_fmt = qmc5883l_write_raw_get_fmt,
+};
+
+static int qmc5883l_init(struct qmc5883l_data *data)
+{
+	struct regmap *regmap = data->regmap;
+	unsigned int reg;
+	int ret;
+
+	ret = regmap_read(regmap, QMC5883L_REG_ID, &reg);
+	if (ret)
+		return ret;
+
+	/* Not failing because rev 1.0 had this register reserved */
+	if (reg != QMC5883L_CHIP_ID)
+		dev_warn(regmap_get_device(regmap),
+			 "Unknown chip id: 0x%02x, continuing\n", reg);
+
+	ret = regmap_write(regmap, QMC5883L_REG_CTRL2, QMC5883L_SOFT_RESET);
+	if (ret)
+		return ret;
+
+	fsleep(QMC5883L_PORT_US);
+
+	/* DRDY pin no used in this version of the driver */
+	ret = regmap_write(regmap, QMC5883L_REG_CTRL2, QMC5883L_INT_DISABLE);
+	if (ret)
+		return ret;
+
+	ret = regmap_write(regmap, QMC5883L_REG_SET_RESET, QMC5883L_SET_RESET_VAL);
+	if (ret)
+		return ret;
+
+	data->odr = QMC5883L_ODR_50HZ;
+	data->range = QMC5883L_RNG_2G;
+	data->osr = QMC5883L_OSR_64;
+
+	return regmap_write(regmap, QMC5883L_REG_CTRL1,
+			    FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_CONT) |
+			    FIELD_PREP(QMC5883L_ODR_MASK, data->odr) |
+			    FIELD_PREP(QMC5883L_RNG_MASK, data->range) |
+			    FIELD_PREP(QMC5883L_OSR_MASK, data->osr));
+}
+
+static void qmc5883l_power_down_action(void *priv)
+{
+	struct qmc5883l_data *data = priv;
+
+	regmap_update_bits(data->regmap, QMC5883L_REG_CTRL1,
+			   QMC5883L_MODE_MASK,
+			   FIELD_PREP(QMC5883L_MODE_MASK, QMC5883L_MODE_STANDBY));
+}
+
+static bool qmc5883l_volatile_reg(struct device *dev, unsigned int reg)
+{
+	return reg <= QMC5883L_REG_STATUS1;
+}
+
+static bool qmc5883l_writable_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case QMC5883L_REG_CTRL1:
+	case QMC5883L_REG_CTRL2:
+	case QMC5883L_REG_SET_RESET:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config qmc5883l_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 8,
+	.max_register = QMC5883L_REG_ID,
+	.cache_type = REGCACHE_MAPLE,
+	.volatile_reg = qmc5883l_volatile_reg,
+	.writeable_reg = qmc5883l_writable_reg
+};
+
+#define QMC5883L_CHANNEL(_axis)                                \
+	{                                                      \
+		.type = IIO_MAGN,                              \
+		.modified = 1,                                 \
+		.channel2 = IIO_MOD_##_axis,                   \
+		.address = QMC5883L_AXIS_##_axis,              \
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),  \
+		.info_mask_shared_by_type =                    \
+			BIT(IIO_CHAN_INFO_SCALE) |             \
+			BIT(IIO_CHAN_INFO_SAMP_FREQ) |         \
+			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+		.info_mask_shared_by_type_available =          \
+			BIT(IIO_CHAN_INFO_SCALE) |             \
+			BIT(IIO_CHAN_INFO_SAMP_FREQ) |         \
+			BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+	}
+
+static const struct iio_chan_spec qmc5883l_channels[] = {
+	QMC5883L_CHANNEL(X),
+	QMC5883L_CHANNEL(Y),
+	QMC5883L_CHANNEL(Z)
+};
+
+static int qmc5883l_probe(struct i2c_client *client)
+{
+	struct device *dev = &client->dev;
+	struct qmc5883l_data *data;
+	struct iio_dev *indio_dev;
+	struct regmap *regmap;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	regmap = devm_regmap_init_i2c(client, &qmc5883l_regmap_config);
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap),
+				     "regmap initialization failed\n");
+
+	ret = devm_regulator_get_enable(dev, "vdd");
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to enable VDD regulator\n");
+
+	ret = devm_regulator_get_enable(dev, "vddio");
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "Failed to enable VDDIO regulator\n");
+
+	fsleep(QMC5883L_PORT_US);
+
+	data = iio_priv(indio_dev);
+	data->regmap = regmap;
+
+	ret = devm_mutex_init(dev, &data->mutex);
+	if (ret)
+		return ret;
+
+	indio_dev->name = "qmc5883l";
+	indio_dev->info = &qmc5883l_info;
+	indio_dev->channels = qmc5883l_channels;
+	indio_dev->num_channels = ARRAY_SIZE(qmc5883l_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	ret = qmc5883l_init(data);
+	if (ret)
+		return dev_err_probe(dev, ret, "qmc5883l init failed\n");
+
+	ret = devm_add_action_or_reset(dev, qmc5883l_power_down_action, data);
+	if (ret)
+		return ret;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id qmc5883l_match[] = {
+	{ .compatible = "qstcorp,qmc5883l" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, qmc5883l_match);
+
+static const struct i2c_device_id qmc5883l_id[] = {
+	{ .name = "qmc5883l" },
+	{ }
+};
+MODULE_DEVICE_TABLE(i2c, qmc5883l_id);
+
+static struct i2c_driver qmc5883l_driver = {
+	.driver = {
+		.name = "qmc5883l",
+		.of_match_table = qmc5883l_match,
+	},
+	.id_table = qmc5883l_id,
+	.probe = qmc5883l_probe
+};
+module_i2c_driver(qmc5883l_driver);
+
+MODULE_DESCRIPTION("QST QMC5883L 3-Axis Magnetic Sensor driver");
+MODULE_AUTHOR("Siratul Islam <siratul.islam@linux.dev>");
+MODULE_LICENSE("Dual BSD/GPL");
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v5] arm64: dts: qcom: glymur: add coresight nodes
From: Konrad Dybcio @ 2026-06-16 11:51 UTC (permalink / raw)
  To: Jie Gan, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Tingwei Zhang
  Cc: linux-arm-msm, devicetree, linux-kernel
In-Reply-To: <a8bb974f-4886-4adb-baf1-9e6c352bc6cc@oss.qualcomm.com>

On 6/12/26 1:11 AM, Jie Gan wrote:
> 
> 
> On 6/11/2026 5:28 PM, Konrad Dybcio wrote:
>> On 5/19/26 3:14 PM, Jie Gan wrote:
>>> Add CoreSight nodes to enable trace paths like TPDM->ETF/STM->ETF.
>>> These devices are part of the AOSS, CDSP, QDSS, PCIe5, TraceNoc and
>>> some small subsystems, such as GCC, IPCC, PMU and so on.
>>>
>>> Delete cti_wpss DT node on Mahua since this device will cause NoC issue
>>> on Mahua device.
>>
>> Another good explanation is that it simply doesn't exist there!
>>
>>> Signed-off-by: Jie Gan <jie.gan@oss.qualcomm.com>
>>> ---
>>
>> [...]
>>
>>
>>> +    tpdm-cdsp-llm {
>>
>> Nodes without a unit address should be sorted alphabetically
>>
>>> +        compatible = "qcom,coresight-static-tpdm";
>>> +        qcom,cmb-element-bits = <32>;
>>
>> Do these other TPDMs not have the "DSB element" as the driver
>> calls it?
> 
> Depends on the design of the TPDM.
> 
> tpdm-cdsp-llm here only supports CMB mode.
> 
> There are three types of TPDM:
> CMB only
> DSB only
> support both

OK, thank you

Konrad

^ permalink raw reply

* Re: [PATCH v7 1/2] arm64: dts: qcom: sm8250: sort out Iris power domains
From: Konrad Dybcio @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Bjorn Andersson, Michael Turquette, Stephen Boyd, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Taniya Das, Jonathan Marek,
	Rafael J. Wysocki, Bryan O'Donoghue, Vikash Garodia,
	Dikshita Agarwal, Mauro Carvalho Chehab, Stanimir Varbanov,
	Abhinav Kumar, Hans Verkuil, Stefan Schmidt, Konrad Dybcio,
	Bryan O'Donoghue, Dikshita Agarwal, Ulf Hansson,
	linux-arm-msm, linux-clk, devicetree, linux-kernel, linux-pm,
	linux-media, Mauro Carvalho Chehab
In-Reply-To: <ijevonu6ib5daesvvzis53qh5ztufrdlqdsfmx33kiajplqwhm@muhxbznlx5k4>

On 6/12/26 9:55 AM, Dmitry Baryshkov wrote:
> On Wed, Jun 10, 2026 at 03:52:09PM +0200, Konrad Dybcio wrote:
>> On 6/10/26 3:34 PM, Dmitry Baryshkov wrote:
>>> On Wed, Jun 10, 2026 at 02:24:24PM +0200, Konrad Dybcio wrote:
>>>> On 6/4/26 6:22 PM, Dmitry Baryshkov wrote:
>>>>> On SM8250 Iris core requires two power rails to function, MX (for PLLs)
>>>>> and MMCX (for everything else). The commit fa245b3f06cd ("arm64: dts:
>>>>> qcom: sm8250: Add venus DT node") added only MX power rail, but omitted
>>>>> MMCX voltage levels.
>>>>>
>>>>> Add MMCX domain to the Iris device node.
>>>>>
>>>>> Fixes: fa245b3f06cd ("arm64: dts: qcom: sm8250: Add venus DT node")
>>>>> Reported-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>>>>> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
>>>>> ---
>>>>
>>>> [...]
>>>>
>>>>>  				opp-720000000 {
>>>>>  					opp-hz = /bits/ 64 <720000000>;
>>>>> -					required-opps = <&rpmhpd_opp_low_svs>;
>>>>> +					required-opps = <&rpmhpd_opp_svs>,
>>>>> +							<&rpmhpd_opp_low_svs>;
>>>>
>>>> So the computer tells me low_svs would be enough for PLL0 to generate 720MHz
>>>>
>>>> Is there some transient dependency that bumps this to svs?
>>>>
>>>> Your changelog mentions you altered this in v6, but I don't see any related
>>>> discussion
>>>
>>> There are two sources of information. The "clocks plan" and the "pll
>>> info". For some reason, the clock plan doesn't reflect actual PLL
>>> requirements. See the info on the corresponding PLL type.
>>
>> OK thanks, now I think I asked this already in the past..
>>
>> still, both point to LOWSVS @ 720 for sm8250
> 
> Not really, PLL type points to 615. I've posted you the screenshots
> privately (sorry).

Alright then

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* [PATCH 0/9] Support ELE API in i.MX OCOTP NVMEM driver
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf

The imx-ocotp-ele.c driver currently uses the limited FSB (fuseblock)
to access the fuse registers. In order to gain full read/write access
to all fuses, we need to use the Edgelock Secure Enclave firmware.

This patchset does:

* Add bindings to reference ELE device in OCOTP driver (patch 1)
* Add ELE API functions for accessing the fuses (patch 3)
* Let the OCOTP driver use the ELE API (patch 6)
* Extend the devicetree of the Kontron boards for using the ELE API (patch 9)

The rest of the patches contain cleanups that were implemented along
the way.

This was tested using the 'crucible' tool and through hexdump on the
nvmem device.

---
Frieder Schrempf (9):
      dt-bindings: nvmem: imx-ocotp: Add support for secure-enclave
      firmware: imx: ele: Fix indentation in ele_base_msg.h
      firmware: imx: ele: Add API functions for OCOTP fuse access
      nvmem: imx-ocotp-ele: Add keepout table for i.MX93
      nvmem: imx-ocotp-ele: Remove device-specific reg_read()
      nvmem: imx-ocotp-ele: Support the ELE API
      nvmem: imx-ocotp-ele: Remove the FUSE_ELE type
      nvmem: imx-ocotp-ele: Rename FSB access map
      arm64: dts: imx93-kontron: Enable ELE firmware driver

 .../devicetree/bindings/nvmem/imx-ocotp.yaml       |   4 +
 .../boot/dts/freescale/imx93-kontron-osm-s.dtsi    |  26 ++++
 drivers/firmware/imx/ele_base_msg.c                | 122 ++++++++++++++++++
 drivers/firmware/imx/ele_base_msg.h                |  22 ++--
 drivers/nvmem/imx-ocotp-ele.c                      | 137 ++++++++++++++++-----
 include/linux/firmware/imx/se_api.h                |   3 +
 6 files changed, 277 insertions(+), 37 deletions(-)
---
base-commit: 49e02880ec0a8c378e811bc9d85da188d7c6204c
change-id: 20260616-upstreaming-next-20260609-imx-ocotp-ele-a512ddf96b03

Best regards,
--  
Frieder Schrempf <frieder.schrempf@kontron.de>


^ permalink raw reply

* [PATCH 1/9] dt-bindings: nvmem: imx-ocotp: Add support for secure-enclave
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

Some SoCs like the i.MX9 family allow full access to the fuses only
through the secure enclave firmware API. Add a property to reference
the secure enclave node and let the driver use the API.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
index a8076d0e2737..14a6429f4a4c 100644
--- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
+++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
@@ -53,6 +53,10 @@ properties:
   reg:
     maxItems: 1
 
+  secure-enclave:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: A phandle to the secure enclave node
+
   clocks:
     maxItems: 1
 

-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 2/7] riscv: dts: eswin: add clock generator for EIC7700 SoC
From: Pinkesh Vaghela @ 2026-06-16 11:53 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-riscv@lists.infradead.org, Min Lin, Yulin Lu,
	Samuel Holland, Darshan Prajapati, Pritesh Patel
In-Reply-To: <20260615-overshot-pellet-5e527a14133e@spud>

Hi Conor,

Thanks for your time to review the patch.

On Mon, Jun 15, 2026 at 10:01 PM +0530, Conor Dooley wrote:
> On Mon, Jun 15, 2026 at 05:50:11PM +0530, Pinkesh Vaghela wrote:
> > From: Pritesh Patel <pritesh.patel@einfochips.com>
> >
> > Add clock generator node for EIC7700 SoC.
> > HiFive Premier P550 boards have 24MHz crystal oscillator to provide
> > the input clock.
> >
> > Signed-off-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > Signed-off-by: Pinkesh Vaghela <pinkesh.vaghela@einfochips.com>
> > ---
> >  .../boot/dts/eswin/eic7700-hifive-premier-p550.dts  |  5 +++++
> >  arch/riscv/boot/dts/eswin/eic7700.dtsi              | 13 +++++++++++++
> >  2 files changed, 18 insertions(+)
> >
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > index 131ed1fc6b2e..1fb92f0e7c55 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > +++ b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > @@ -20,6 +20,11 @@ chosen {
> >  	};
> >  };
> >
> > +&xtal {
> > +	clock-frequency = <24000000>;
> > +	clock-output-names = "xtal24m";
> > +};
> > +
> >  &uart0 {
> >  	status = "okay";
> >  };
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > index 430a210f01e6..a7ebb1115958 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > +++ b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > @@ -4,6 +4,7 @@
> >   */
> >
> >  /dts-v1/;
> > +#include <dt-bindings/clock/eswin,eic7700-clock.h>
> >  #include <dt-bindings/reset/eswin,eic7700-reset.h>
> >
> >  / {
> > @@ -203,6 +204,11 @@ pmu {
> >  				<0x00000000 0x0000000f 0xfffffffc 0x000000ff
> 0x00000078>;
> >  	};
> >
> > +	xtal: oscillator {
> 
> Sashiko feedback here on making this clk-<hz> or clk-<something> should be
> implemented.

We will change this to "clock-24m" in v2 patch

Regards,
Pinkesh

> 
> > +		compatible = "fixed-clock";
> > +		#clock-cells = <0>;
> > +	};
> > +
> >  	soc {
> >  		compatible = "simple-bus";
> >  		ranges;
> > @@ -343,6 +349,13 @@ gpioD: gpio-port@3 {
> >  			};
> >  		};
> >
> > +		clk: clock-controller@51828000 {
> > +			compatible = "eswin,eic7700-clock";
> > +			reg = <0x0 0x51828000 0x0 0x300>;
> > +			clocks = <&xtal>;
> > +			#clock-cells = <1>;
> > +		};
> > +
> >  		reset: reset-controller@51828300 {
> >  			compatible = "eswin,eic7700-reset";
> >  			reg = <0x0 0x51828300 0x0 0x200>;
> > --
> > 2.34.1
> >

^ permalink raw reply

* [PATCH 2/9] firmware: imx: ele: Fix indentation in ele_base_msg.h
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

The file uses mixed indentation characters. Use tabs everywhere.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/firmware/imx/ele_base_msg.h | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/drivers/firmware/imx/ele_base_msg.h b/drivers/firmware/imx/ele_base_msg.h
index 75e65e279193..1c5d6791b323 100644
--- a/drivers/firmware/imx/ele_base_msg.h
+++ b/drivers/firmware/imx/ele_base_msg.h
@@ -27,19 +27,19 @@
 #define ELE_GEN_KEY_BLOB_REQ		0xaf
 #define ELE_GET_FW_STATUS_REQ		0xc5
 #define ELE_XIP_DECRYPT_REQ		0xc6
-#define ELE_WRITE_FUSE                  0xd6
-#define ELE_DEV_ATTEST_REQ              0xdb
-#define ELE_WRITE_SHADOW_FUSE_REQ       0xf2
-#define ELE_READ_SHADOW_FUSE_REQ        0xf3
+#define ELE_WRITE_FUSE			0xd6
+#define ELE_DEV_ATTEST_REQ		0xdb
+#define ELE_WRITE_SHADOW_FUSE_REQ	0xf2
+#define ELE_READ_SHADOW_FUSE_REQ	0xf3
 
 #define ELE_GET_INFO_REQ		0xda
 #define ELE_GET_INFO_REQ_MSG_SZ		0x10
 #define ELE_GET_INFO_RSP_MSG_SZ		0x08
 
-#define MAX_UID_SIZE                     (16)
-#define DEV_GETINFO_ROM_PATCH_SHA_SZ     (32)
-#define DEV_GETINFO_FW_SHA_SZ            (32)
-#define DEV_GETINFO_OEM_SRKH_SZ          (64)
+#define MAX_UID_SIZE			(16)
+#define DEV_GETINFO_ROM_PATCH_SHA_SZ	(32)
+#define DEV_GETINFO_FW_SHA_SZ		(32)
+#define DEV_GETINFO_OEM_SRKH_SZ		(64)
 #define DEV_GETINFO_MIN_VER_MASK	0xff
 #define DEV_GETINFO_MAJ_VER_MASK	0xff00
 #define ELE_DEV_INFO_EXTRA_SZ		0x60

-- 
2.54.0


^ permalink raw reply related

* [PATCH 3/9] firmware: imx: ele: Add API functions for OCOTP fuse access
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

The ELE S400 API provides read and write access to the OCOTP fuse
registers. This adds the necessary API functions imx_se_read_fuse()
and imx_se_write_fuse() to be used by other drivers such as the
OCOTP S400 NVMEM driver.

This is ported from the downstream vendor kernel.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/firmware/imx/ele_base_msg.c | 122 ++++++++++++++++++++++++++++++++++++
 drivers/firmware/imx/ele_base_msg.h |   6 ++
 include/linux/firmware/imx/se_api.h |   3 +
 3 files changed, 131 insertions(+)

diff --git a/drivers/firmware/imx/ele_base_msg.c b/drivers/firmware/imx/ele_base_msg.c
index ec718d322abc..281d223aa144 100644
--- a/drivers/firmware/imx/ele_base_msg.c
+++ b/drivers/firmware/imx/ele_base_msg.c
@@ -8,6 +8,7 @@
 #include <linux/cleanup.h>
 #include <linux/completion.h>
 #include <linux/dma-mapping.h>
+#include <linux/firmware/imx/se_api.h>
 #include <linux/genalloc.h>
 
 #include "ele_base_msg.h"
@@ -303,3 +304,124 @@ int ele_debug_dump(struct se_if_priv *priv)
 
 	return ret;
 }
+
+static int ele_read_fuse(struct se_if_priv *priv, uint16_t fuse_id, u32 *value)
+{
+	struct se_api_msg *tx_msg __free(kfree) = NULL;
+	struct se_api_msg *rx_msg __free(kfree) = NULL;
+	int rx_msg_sz = ELE_READ_FUSE_RSP_MSG_SZ;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	tx_msg = kzalloc(ELE_READ_FUSE_REQ_MSG_SZ, GFP_KERNEL);
+	if (!tx_msg)
+		return -ENOMEM;
+
+	rx_msg = kzalloc(rx_msg_sz, GFP_KERNEL);
+	if (!rx_msg)
+		return -ENOMEM;
+
+	ret = se_fill_cmd_msg_hdr(priv, (struct se_msg_hdr *)&tx_msg->header,
+				  ELE_READ_FUSE_REQ, ELE_READ_FUSE_REQ_MSG_SZ,
+				  true);
+	if (ret)
+		return ret;
+
+	tx_msg->data[0] = fuse_id;
+
+	ret = ele_msg_send_rcv(priv->priv_dev_ctx, tx_msg,
+			       ELE_READ_FUSE_REQ_MSG_SZ, rx_msg, rx_msg_sz);
+	if (ret < 0)
+		return ret;
+
+	ret = se_val_rsp_hdr_n_status(priv, rx_msg, ELE_READ_FUSE_REQ,
+				      rx_msg_sz, true);
+	if (ret)
+		return ret;
+
+	*value = rx_msg->data[1];
+
+	return 0;
+}
+
+/**
+ * imx_se_read_fuse() - API to request SE-FW to read the fuse value.
+ * @se_if_data: refs to data attached to the se interface.
+ * @fuse_id: fuse identifier to read.
+ * @value: unsigned integer array to store the fuse values.
+ *
+ * Secure enclave like EdgeLock Enclave, manages the fuses. This API
+ * requests the FW to read the fuses. FW responds with the read values.
+ *
+ * Context:
+ *
+ * Return value:
+ *   0,   means success.
+ *   < 0, means failure.
+ */
+int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value)
+{
+	return ele_read_fuse((struct se_if_priv *)se_if_data, fuse_id, value);
+}
+EXPORT_SYMBOL_GPL(imx_se_read_fuse);
+
+static int ele_write_fuse(struct se_if_priv *priv, uint16_t fuse_id, u32 value)
+{
+	struct se_api_msg *tx_msg __free(kfree) = NULL;
+	struct se_api_msg *rx_msg __free(kfree) = NULL;
+	int ret = 0;
+
+	if (!priv)
+		return -EINVAL;
+
+	tx_msg = kzalloc(ELE_WRITE_FUSE_REQ_MSG_SZ, GFP_KERNEL);
+	if (!tx_msg)
+		return -ENOMEM;
+
+	rx_msg = kzalloc(ELE_WRITE_FUSE_RSP_MSG_SZ, GFP_KERNEL);
+	if (!rx_msg)
+		return -ENOMEM;
+
+	ret = se_fill_cmd_msg_hdr(priv, (struct se_msg_hdr *)&tx_msg->header,
+				  ELE_WRITE_FUSE, ELE_WRITE_FUSE_REQ_MSG_SZ,
+				  true);
+	if (ret)
+		return ret;
+
+	tx_msg->data[0] = (32 << 16) | (fuse_id << 5);
+	tx_msg->data[1] = value;
+
+	ret = ele_msg_send_rcv(priv->priv_dev_ctx, tx_msg,
+			       ELE_WRITE_FUSE_REQ_MSG_SZ, rx_msg,
+			       ELE_WRITE_FUSE_RSP_MSG_SZ);
+	if (ret < 0)
+		return ret;
+
+	ret = se_val_rsp_hdr_n_status(priv, rx_msg, ELE_WRITE_FUSE,
+				      ELE_WRITE_FUSE_RSP_MSG_SZ, true);
+
+	return ret;
+}
+
+/**
+ * imx_se_write_fuse() - API to request SE-FW to write to fuses.
+ * @se_if_data: refs to data attached to the se interface.
+ * @fuse_id: fuse identifier to write to.
+ * @value: unsigned integer value that to be written to the fuse.
+ *
+ * Secure enclave like EdgeLock Enclave, manages the fuses. This API
+ * requests the FW to write the fuse with the given value.
+ *
+ * Context:
+ *
+ * Return value:
+ *   0,   means success.
+ *   < 0, means failure.
+ */
+int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value)
+{
+	return ele_write_fuse((struct se_if_priv *)se_if_data, fuse_id, value);
+}
+EXPORT_SYMBOL_GPL(imx_se_write_fuse);
diff --git a/drivers/firmware/imx/ele_base_msg.h b/drivers/firmware/imx/ele_base_msg.h
index 1c5d6791b323..2af3ada2ad07 100644
--- a/drivers/firmware/imx/ele_base_msg.h
+++ b/drivers/firmware/imx/ele_base_msg.h
@@ -36,6 +36,12 @@
 #define ELE_GET_INFO_REQ_MSG_SZ		0x10
 #define ELE_GET_INFO_RSP_MSG_SZ		0x08
 
+#define ELE_READ_FUSE_REQ_MSG_SZ	(8)
+#define ELE_READ_FUSE_RSP_MSG_SZ	(12)
+
+#define ELE_WRITE_FUSE_REQ_MSG_SZ	(12)
+#define ELE_WRITE_FUSE_RSP_MSG_SZ	(12)
+
 #define MAX_UID_SIZE			(16)
 #define DEV_GETINFO_ROM_PATCH_SHA_SZ	(32)
 #define DEV_GETINFO_FW_SHA_SZ		(32)
diff --git a/include/linux/firmware/imx/se_api.h b/include/linux/firmware/imx/se_api.h
index b1c4c9115d7b..ed766b1d48f8 100644
--- a/include/linux/firmware/imx/se_api.h
+++ b/include/linux/firmware/imx/se_api.h
@@ -11,4 +11,7 @@
 #define SOC_ID_OF_IMX8ULP		0x084d
 #define SOC_ID_OF_IMX93			0x9300
 
+int imx_se_read_fuse(void *se_if_data, uint16_t fuse_id, u32 *value);
+int imx_se_write_fuse(void *se_if_data, uint16_t fuse_id, u32 value);
+
 #endif /* __SE_API_H__ */

-- 
2.54.0


^ permalink raw reply related

* [PATCH 4/9] nvmem: imx-ocotp-ele: Add keepout table for i.MX93
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

Add an overall keepout table to describe which fuse registers are
invalid and not accessible. No functional changes intended.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/nvmem/imx-ocotp-ele.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index a0d2985c6d03..dadec19ca252 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -34,6 +34,8 @@ struct ocotp_devtype_data {
 	u32 num_entry;
 	u32 flag;
 	nvmem_reg_read_t reg_read;
+	const struct nvmem_keepout *keepout;
+	unsigned int nkeepout;
 	struct ocotp_map_entry entry[];
 };
 
@@ -163,6 +165,12 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
 	priv->config.read_only = true;
 	priv->config.add_legacy_fixed_of_cells = true;
 	priv->config.fixup_dt_cell_info = imx_ocotp_fixup_dt_cell_info;
+
+	if (priv->data->nkeepout) {
+		priv->config.keepout = priv->data->keepout;
+		priv->config.nkeepout = priv->data->nkeepout;
+	}
+
 	mutex_init(&priv->lock);
 
 	nvmem = devm_nvmem_register(dev, &priv->config);
@@ -172,6 +180,14 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct nvmem_keepout imx93_ocotp_keepout[] = {
+	{.start = 208, .end = 252},
+	{.start = 256, .end = 512},
+	{.start = 576, .end = 728},
+	{.start = 732, .end = 752},
+	{.start = 756, .end = 1248},
+};
+
 static const struct ocotp_devtype_data imx93_ocotp_data = {
 	.reg_off = 0x8000,
 	.reg_read = imx_ocotp_reg_read,
@@ -185,6 +201,8 @@ static const struct ocotp_devtype_data imx93_ocotp_data = {
 		{ 188, 1, FUSE_ELE },
 		{ 312, 200, FUSE_FSB }
 	},
+	.keepout = imx93_ocotp_keepout,
+	.nkeepout = ARRAY_SIZE(imx93_ocotp_keepout),
 };
 
 static const struct ocotp_devtype_data imx94_ocotp_data = {

-- 
2.54.0


^ permalink raw reply related

* [PATCH 5/9] nvmem: imx-ocotp-ele: Remove device-specific reg_read()
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

We only have a single implementation of the read hook. There is no
need to store it in the device data.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/nvmem/imx-ocotp-ele.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index dadec19ca252..9d3f94e35508 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -33,7 +33,6 @@ struct ocotp_devtype_data {
 	u32 size;
 	u32 num_entry;
 	u32 flag;
-	nvmem_reg_read_t reg_read;
 	const struct nvmem_keepout *keepout;
 	unsigned int nkeepout;
 	struct ocotp_map_entry entry[];
@@ -158,7 +157,7 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
 	priv->config.id = NVMEM_DEVID_AUTO;
 	priv->config.owner = THIS_MODULE;
 	priv->config.size = priv->data->size;
-	priv->config.reg_read = priv->data->reg_read;
+	priv->config.reg_read = imx_ocotp_reg_read;
 	priv->config.word_size = 1;
 	priv->config.stride = 1;
 	priv->config.priv = priv;
@@ -190,7 +189,6 @@ static const struct nvmem_keepout imx93_ocotp_keepout[] = {
 
 static const struct ocotp_devtype_data imx93_ocotp_data = {
 	.reg_off = 0x8000,
-	.reg_read = imx_ocotp_reg_read,
 	.size = 2048,
 	.num_entry = 6,
 	.entry = {
@@ -207,7 +205,6 @@ static const struct ocotp_devtype_data imx93_ocotp_data = {
 
 static const struct ocotp_devtype_data imx94_ocotp_data = {
 	.reg_off = 0x8000,
-	.reg_read = imx_ocotp_reg_read,
 	.size = 3296, /* 103 Banks */
 	.num_entry = 10,
 	.entry = {
@@ -226,7 +223,6 @@ static const struct ocotp_devtype_data imx94_ocotp_data = {
 
 static const struct ocotp_devtype_data imx95_ocotp_data = {
 	.reg_off = 0x8000,
-	.reg_read = imx_ocotp_reg_read,
 	.size = 2048,
 	.num_entry = 12,
 	.entry = {

-- 
2.54.0


^ permalink raw reply related

* [PATCH 7/9] nvmem: imx-ocotp-ele: Remove the FUSE_ELE type
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

There is no use in tracking the fuses that are only accessible via
ELE API and the current lists are incomplete. If the ELE API is
available it will be used as primary access method anyway, otherwise
the fuses not listed as accessible through the FSB can be considered
invalid.

No functional changes intended.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/nvmem/imx-ocotp-ele.c | 19 +++++--------------
 1 file changed, 5 insertions(+), 14 deletions(-)

diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index 92cc061b9b34..3d6fad149ed5 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -18,8 +18,7 @@
 
 enum fuse_type {
 	FUSE_FSB = BIT(0),
-	FUSE_ELE = BIT(1),
-	FUSE_ECC = BIT(2),
+	FUSE_ECC = BIT(1),
 	FUSE_INVALID = -1
 };
 
@@ -111,7 +110,7 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz
 		}
 
 		type = imx_ocotp_fuse_type(context, i);
-		if (type == FUSE_INVALID || type == FUSE_ELE) {
+		if (type == FUSE_INVALID) {
 			*buf++ = 0;
 			continue;
 		}
@@ -264,13 +263,9 @@ static const struct nvmem_keepout imx93_ocotp_keepout[] = {
 static const struct ocotp_devtype_data imx93_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 2048,
-	.num_entry = 6,
+	.num_entry = 2,
 	.entry = {
 		{ 0, 52, FUSE_FSB },
-		{ 63, 1, FUSE_ELE},
-		{ 128, 16, FUSE_ELE },
-		{ 182, 1, FUSE_ELE },
-		{ 188, 1, FUSE_ELE },
 		{ 312, 200, FUSE_FSB }
 	},
 	.keepout = imx93_ocotp_keepout,
@@ -280,7 +275,7 @@ static const struct ocotp_devtype_data imx93_ocotp_data = {
 static const struct ocotp_devtype_data imx94_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 3296, /* 103 Banks */
-	.num_entry = 10,
+	.num_entry = 9,
 	.entry = {
 		{ 0, 1, FUSE_FSB | FUSE_ECC },
 		{ 7, 1, FUSE_FSB | FUSE_ECC },
@@ -288,7 +283,6 @@ static const struct ocotp_devtype_data imx94_ocotp_data = {
 		{ 12, 24, FUSE_FSB },
 		{ 36, 2, FUSE_FSB  | FUSE_ECC },
 		{ 38, 14, FUSE_FSB },
-		{ 59, 1, FUSE_ELE },
 		{ 525, 2, FUSE_FSB | FUSE_ECC },
 		{ 528, 7, FUSE_FSB },
 		{ 536, 280, FUSE_FSB },
@@ -298,7 +292,7 @@ static const struct ocotp_devtype_data imx94_ocotp_data = {
 static const struct ocotp_devtype_data imx95_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 2048,
-	.num_entry = 12,
+	.num_entry = 9,
 	.entry = {
 		{ 0, 1, FUSE_FSB | FUSE_ECC },
 		{ 7, 1, FUSE_FSB | FUSE_ECC },
@@ -306,9 +300,6 @@ static const struct ocotp_devtype_data imx95_ocotp_data = {
 		{ 12, 24, FUSE_FSB },
 		{ 36, 2, FUSE_FSB  | FUSE_ECC },
 		{ 38, 14, FUSE_FSB },
-		{ 63, 1, FUSE_ELE },
-		{ 128, 16, FUSE_ELE },
-		{ 188, 1, FUSE_ELE },
 		{ 317, 2, FUSE_FSB | FUSE_ECC },
 		{ 320, 7, FUSE_FSB },
 		{ 328, 184, FUSE_FSB }

-- 
2.54.0


^ permalink raw reply related

* [PATCH 6/9] nvmem: imx-ocotp-ele: Support the ELE API
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

The fuses inside the Edgelock Secure Enclave are currently not
accessed via its API but through the FSB block which provides
limited access to some fuses.

The ELE API allows us to access all fuses with read/write
permissions. Therefore use it as primary method and only fall
back to the limited FSB if the ELE API is not available.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/nvmem/imx-ocotp-ele.c | 76 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 75 insertions(+), 1 deletion(-)

diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index 9d3f94e35508..92cc061b9b34 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -6,10 +6,12 @@
  */
 
 #include <linux/device.h>
+#include <linux/firmware/imx/se_api.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/nvmem-provider.h>
 #include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/if_ether.h>	/* ETH_ALEN */
@@ -44,6 +46,8 @@ struct imx_ocotp_priv {
 	struct nvmem_config config;
 	struct mutex lock;
 	const struct ocotp_devtype_data *data;
+	void *se_data;
+	struct platform_device *se_dev;
 };
 
 static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index)
@@ -72,6 +76,7 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz
 	enum fuse_type type;
 	u32 *buf;
 	void *p;
+	int ret;
 	int i;
 	u8 skipbytes;
 
@@ -92,6 +97,19 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz
 	buf = p;
 
 	for (i = index; i < (index + count); i++) {
+		/*
+		 * All fuse registers can be read via ELE. If the SE device is
+		 * available, always prefer it.
+		 */
+		if (priv->se_data) {
+			ret = imx_se_read_fuse(priv->se_data, i, buf++);
+			if (ret) {
+				mutex_unlock(&priv->lock);
+				return ret;
+			}
+			continue;
+		}
+
 		type = imx_ocotp_fuse_type(context, i);
 		if (type == FUSE_INVALID || type == FUSE_ELE) {
 			*buf++ = 0;
@@ -113,6 +131,32 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz
 	return 0;
 };
 
+static int imx_ocotp_reg_write(void *context, unsigned int offset, void *val, size_t bytes)
+{
+	struct imx_ocotp_priv *priv = context;
+	u32 word = offset >> 2;
+	u32 *buf = val;
+	int ret;
+
+	/* allow only writing one complete OTP word at a time */
+	if ((bytes != 4) || (offset % 4 != 0))
+		return -EINVAL;
+
+	/*
+	 * The ELE API returns an error when writing an all-zero value. As
+	 * OTP fuse bits can not be switched from 1 to 0 anyway, skip these
+	 * values.
+	 */
+	if (!*buf)
+		return 0;
+
+	mutex_lock(&priv->lock);
+	ret = imx_se_write_fuse(priv->se_data, word, *buf);
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
 static int imx_ocotp_cell_pp(void *context, const char *id, int index,
 			     unsigned int offset, void *data, size_t bytes)
 {
@@ -136,11 +180,18 @@ static void imx_ocotp_fixup_dt_cell_info(struct nvmem_device *nvmem,
 	cell->read_post_process = imx_ocotp_cell_pp;
 }
 
+static void imx_ocotp_put_se_dev(void *data)
+{
+	platform_device_put(data);
+}
+
 static int imx_ele_ocotp_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
 	struct imx_ocotp_priv *priv;
 	struct nvmem_device *nvmem;
+	struct device_node *np;
+	int ret;
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	if (!priv)
@@ -152,16 +203,36 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->base))
 		return PTR_ERR(priv->base);
 
+	np = of_parse_phandle(pdev->dev.of_node, "secure-enclave", 0);
+	if (!np) {
+		dev_info(&pdev->dev, "missing or invalid SE handle, using readonly FSB\n");
+	} else {
+		priv->se_dev = of_find_device_by_node(np);
+		of_node_put(np);
+		if (!priv->se_dev)
+			return dev_err_probe(&pdev->dev, -ENODEV, "failed to find SE device\n");
+
+		ret = devm_add_action_or_reset(&pdev->dev, imx_ocotp_put_se_dev,
+					       priv->se_dev);
+		if (ret)
+			return ret;
+
+		priv->se_data = platform_get_drvdata(priv->se_dev);
+		if (!priv->se_data)
+			return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
+					     "SE device not ready\n");
+	}
+
 	priv->config.dev = dev;
 	priv->config.name = "ELE-OCOTP";
 	priv->config.id = NVMEM_DEVID_AUTO;
 	priv->config.owner = THIS_MODULE;
 	priv->config.size = priv->data->size;
 	priv->config.reg_read = imx_ocotp_reg_read;
+	priv->config.reg_write = imx_ocotp_reg_write;
 	priv->config.word_size = 1;
 	priv->config.stride = 1;
 	priv->config.priv = priv;
-	priv->config.read_only = true;
 	priv->config.add_legacy_fixed_of_cells = true;
 	priv->config.fixup_dt_cell_info = imx_ocotp_fixup_dt_cell_info;
 
@@ -170,6 +241,9 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
 		priv->config.nkeepout = priv->data->nkeepout;
 	}
 
+	if (!priv->se_data)
+		priv->config.read_only = true;
+
 	mutex_init(&priv->lock);
 
 	nvmem = devm_nvmem_register(dev, &priv->config);

-- 
2.54.0


^ permalink raw reply related

* [PATCH 9/9] arm64: dts: imx93-kontron: Enable ELE firmware driver
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

Add the ELE firmware API node and pass its handle to the OCOTP
driver. This allows us to gain read/write access to the OTP fuses.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 .../boot/dts/freescale/imx93-kontron-osm-s.dtsi    | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/arch/arm64/boot/dts/freescale/imx93-kontron-osm-s.dtsi b/arch/arm64/boot/dts/freescale/imx93-kontron-osm-s.dtsi
index c79b1df339db..c1da4b52c878 100644
--- a/arch/arm64/boot/dts/freescale/imx93-kontron-osm-s.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx93-kontron-osm-s.dtsi
@@ -24,6 +24,15 @@ chosen {
 		stdout-path = &lpuart1;
 	};
 
+	firmware {
+		hsm0: secure-enclave {
+			compatible = "fsl,imx93-se-ele-hsm";
+			mbox-names = "tx", "rx";
+			mboxes = <&s4muap 0 0>, <&s4muap 1 0>;
+			memory-region = <&ele_memory>;
+		};
+	};
+
 	reg_usdhc2_vcc: regulator-usdhc2-vcc {
 		compatible = "regulator-fixed";
 		pinctrl-names = "default";
@@ -57,6 +66,19 @@ regulator-state-disk {
 			regulator-off-in-suspend;
 		};
 	};
+
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		ele_memory: ele-memory {
+			compatible = "shared-dma-pool";
+			alloc-ranges = <0 0x80000000 0 0x40000000>;
+			size = <0 0x100000>;
+			no-map;
+		};
+	};
 };
 
 &flexcan1 { /* OSM-S CAN_A */
@@ -253,6 +275,10 @@ &lpuart7 { /* OSM-S UART_A */
 	pinctrl-0 = <&pinctrl_lpuart7>;
 };
 
+&ocotp {
+	secure-enclave = <&hsm0>;
+};
+
 &tpm3 { /* OSM-S PWM_0 */
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_tpm3>;

-- 
2.54.0


^ permalink raw reply related

* [PATCH 8/9] nvmem: imx-ocotp-ele: Rename FSB access map
From: Frieder Schrempf @ 2026-06-16 11:52 UTC (permalink / raw)
  To: Srinivas Kandagatla, Rob Herring, Krzysztof Kozlowski,
	Conor Dooley, Frank Li, Sascha Hauer, Pengutronix Kernel Team,
	Fabio Estevam, Shawn Guo
  Cc: devicetree, imx, linux-arm-kernel, linux-kernel, Frieder Schrempf
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de>

From: Frieder Schrempf <frieder.schrempf@kontron.de>

The table is used to declare which fuse registers are accessible
through the FSB block. Name it accordingly to make this clearer for
the reader.

No functional changes intended.

Signed-off-by: Frieder Schrempf <frieder.schrempf@kontron.de>
---
 drivers/nvmem/imx-ocotp-ele.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
index 3d6fad149ed5..766b0d746f6c 100644
--- a/drivers/nvmem/imx-ocotp-ele.c
+++ b/drivers/nvmem/imx-ocotp-ele.c
@@ -32,11 +32,11 @@ struct ocotp_devtype_data {
 	u32 reg_off;
 	char *name;
 	u32 size;
-	u32 num_entry;
+	u32 num_fsb_map;
 	u32 flag;
 	const struct nvmem_keepout *keepout;
 	unsigned int nkeepout;
-	struct ocotp_map_entry entry[];
+	struct ocotp_map_entry fsb_map[];
 };
 
 struct imx_ocotp_priv {
@@ -56,12 +56,12 @@ static enum fuse_type imx_ocotp_fuse_type(void *context, u32 index)
 	u32 start, end;
 	int i;
 
-	for (i = 0; i < data->num_entry; i++) {
-		start = data->entry[i].start;
-		end = data->entry[i].start + data->entry[i].num;
+	for (i = 0; i < data->num_fsb_map; i++) {
+		start = data->fsb_map[i].start;
+		end = data->fsb_map[i].start + data->fsb_map[i].num;
 
 		if (index >= start && index < end)
-			return data->entry[i].type;
+			return data->fsb_map[i].type;
 	}
 
 	return FUSE_INVALID;
@@ -263,8 +263,8 @@ static const struct nvmem_keepout imx93_ocotp_keepout[] = {
 static const struct ocotp_devtype_data imx93_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 2048,
-	.num_entry = 2,
-	.entry = {
+	.num_fsb_map = 2,
+	.fsb_map = {
 		{ 0, 52, FUSE_FSB },
 		{ 312, 200, FUSE_FSB }
 	},
@@ -275,8 +275,8 @@ static const struct ocotp_devtype_data imx93_ocotp_data = {
 static const struct ocotp_devtype_data imx94_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 3296, /* 103 Banks */
-	.num_entry = 9,
-	.entry = {
+	.num_fsb_map = 9,
+	.fsb_map = {
 		{ 0, 1, FUSE_FSB | FUSE_ECC },
 		{ 7, 1, FUSE_FSB | FUSE_ECC },
 		{ 9, 3, FUSE_FSB | FUSE_ECC },
@@ -292,8 +292,8 @@ static const struct ocotp_devtype_data imx94_ocotp_data = {
 static const struct ocotp_devtype_data imx95_ocotp_data = {
 	.reg_off = 0x8000,
 	.size = 2048,
-	.num_entry = 9,
-	.entry = {
+	.num_fsb_map = 9,
+	.fsb_map = {
 		{ 0, 1, FUSE_FSB | FUSE_ECC },
 		{ 7, 1, FUSE_FSB | FUSE_ECC },
 		{ 9, 3, FUSE_FSB | FUSE_ECC },

-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH 6/7] riscv: dts: eswin: add I2C controller support
From: Pinkesh Vaghela @ 2026-06-16 11:57 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Lee Jones, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-riscv@lists.infradead.org, Min Lin, Yulin Lu,
	Samuel Holland, Darshan Prajapati, Pritesh Patel
In-Reply-To: <20260615-lumpiness-profusely-155422288c31@spud>

Hi Conor,

On Mon, Jun 15, 2026 at 10:05 PM +0530, Conor Dooley wrote:
> On Mon, Jun 15, 2026 at 05:50:15PM +0530, Pinkesh Vaghela wrote:
> > From: Pritesh Patel <pritesh.patel@einfochips.com>
> >
> > Add I2C nodes for EIC7700 SoC.
> > Also add nodes for corresponding slave devices in dts file and enable
> > them for HiFive Premier P550 board
> >
> > Signed-off-by: Pritesh Patel <pritesh.patel@einfochips.com>
> > Signed-off-by: Pinkesh Vaghela <pinkesh.vaghela@einfochips.com>
> > ---
> >  .../dts/eswin/eic7700-hifive-premier-p550.dts |  52 ++++++
> >  arch/riscv/boot/dts/eswin/eic7700.dtsi        | 156 ++++++++++++++++++
> >  2 files changed, 208 insertions(+)
> >
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > index e7bb96e14958..0f0c98474c62 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > +++ b/arch/riscv/boot/dts/eswin/eic7700-hifive-premier-p550.dts
> > @@ -130,6 +130,58 @@ &gpio111_pins {
> >  	input-disable;
> >  };
> >
> > +&aon_i2c0 {
> > +	status = "okay";
> > +
> > +	eeprom@50 {
> > +		compatible = "atmel,24c02";
> > +		reg = <0x50>;
> > +	};
> > +};
> > +
> > +&aon_i2c1 {
> > +	status = "okay";
> > +
> > +	pac1934@10 {
> 
> Generic node name here please. adc I think.

We will change this to "adc@10" in v2 patch

> 
> > +		compatible = "microchip,pac1934";
> > +		reg = <0x10>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +
> > +		channel@1 {
> > +			reg = <0x1>;
> > +			shunt-resistor-micro-ohms = <1000>;
> > +			label = "VDD_SOM";
> > +		};
> > +
> > +		channel@2 {
> > +			reg = <0x2>;
> > +			shunt-resistor-micro-ohms = <1000>;
> > +			label = "VDD_SOC";
> > +		};
> > +
> > +		channel@3 {
> > +			reg = <0x3>;
> > +			shunt-resistor-micro-ohms = <1000>;
> > +			label = "VDD_CPU";
> > +		};
> > +
> > +		channel@4 {
> > +			reg = <0x4>;
> > +			shunt-resistor-micro-ohms = <1000>;
> > +			label = "VDD_LPDDR";
> > +		};
> > +	};
> > +
> > +	ina226@44 {
> 
> And here. power-sensor.

We will change this to "power-sensor@44" in v2 patch

> 
> > +		compatible = "ti,ina226";
> > +		reg = <0x44>;
> > +		#io-channel-cells = <1>;
> > +		label = "sys_power";
> > +		shunt-resistor = <1000>;
> > +	};
> > +};
> > +
> >  &pinctrl {
> >  	vrgmii-supply = <&vcc_1v8>;
> >  };
> > diff --git a/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > index f8caf39616b2..28706431b2c0 100644
> > --- a/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > +++ b/arch/riscv/boot/dts/eswin/eic7700.dtsi
> > @@ -315,6 +315,162 @@ uart4: serial@50940000 {
> >  			status = "disabled";
> >  		};
> >
> > +		i2c0: i2c@50950000 {
> > +			compatible = "snps,designware-i2c";
> 
> Missing a soc-specific compatible here for all i2c controllers.

We are using generic DesignWare I2C driver for all I2C controllers with no changes required in the driver. Do we still need to add soc-specific compatible for all i2c controllers?

Regards,
Pinkesh

> 
> 
> Cheers,
> Conor.

^ permalink raw reply

* Re: [PATCH 5/5] arm64: dts: qcom: qcs6490-rubikpi3: Add audio support
From: Konrad Dybcio @ 2026-06-16 11:58 UTC (permalink / raw)
  To: Hongyang Zhao, Liam Girdwood, Mark Brown, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Daniel Drake, Katsuhiro Suzuki,
	Matteo Martelli, Binbin Zhou, Srinivas Kandagatla,
	Jaroslav Kysela, Takashi Iwai, Bjorn Andersson, Konrad Dybcio
  Cc: linux-sound, devicetree, linux-kernel, linux-arm-msm,
	mohammad.rafi.shaik, rosh
In-Reply-To: <20260607-rubikpi-next-20260605-v1-5-7f334e16fea6@thundersoft.com>

On 6/6/26 8:58 PM, Hongyang Zhao wrote:
> Add audio support for the Thundercomm RubikPi3 board.
> 
> Enable AudioReach and describe the ES8316 headset codec, the LT9611
> HDMI codec endpoint and the SPDIF TX/RX endpoints exposed on the
> 40-pin header. Add the sound card DAI links, LPASS pin configuration
> and ES8316 fixed 3.3V supply.
> 
> Signed-off-by: Hongyang Zhao <hongyang.zhao@thundersoft.com>
> ---

[...]

> +&q6apmbedai {
> +	pinctrl-0 = <&mi2s0_data0>, <&mi2s0_data1>, <&mi2s0_mclk>,
> +		    <&mi2s0_sclk>, <&mi2s0_ws>,
> +		    <&lpass_qua_mi2s_sclk>, <&lpass_qua_mi2s_ws>, <&lpass_qua_mi2s_data>,
> +		    <&lpass_lpi_i2s1_clk>, <&lpass_lpi_i2s1_ws>,
> +		    <&lpass_lpi_i2s1_data0>, <&lpass_lpi_i2s1_data1>;
> +	pinctrl-names = "default";
> +};

I'm slightly confused as to why you bound it to the DAI node, others
seem to couple these pins with the soundcard itself

> +
>  &qupv3_id_0 {
>  	firmware-name = "qcom/qcm6490/qupv3fw.elf";
>  
> @@ -1006,6 +1061,96 @@ &sdhc_2 {
>  	status = "okay";
>  };
>  
> +&sound {
> +	compatible = "thundercomm,qcs6490-rubikpi3-sndcard";
> +	model = "QCS6490-Thundercomm-RubikPi3";
> +
> +	audio-routing =
> +		"Headphone Jack", "HPOL",
> +		"Headphone Jack", "HPOR",
> +		"MIC2", "Mic Jack";

Let's keep the first entry right after the equals sign and align the
other ones on doublequotes

Konrad

^ permalink raw reply

* Re: [PATCH] arm64: dts: qcom: sm8750: Add UART15
From: Konrad Dybcio @ 2026-06-16 12:00 UTC (permalink / raw)
  To: webgeek1234, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley
  Cc: linux-arm-msm, devicetree, linux-kernel, Teguh Sobirin
In-Reply-To: <20260605-sm8750-uart15-v1-1-93e660722e61@gmail.com>

On 6/6/26 12:49 AM, Aaron Kling via B4 Relay wrote:
> From: Teguh Sobirin <teguh@sobir.in>
> 
> Add uart15 node for the UART bus present on the sm8750 SoC.
> 
> Signed-off-by: Teguh Sobirin <teguh@sobir.in>
> Signed-off-by: Aaron Kling <webgeek1234@gmail.com>
> ---

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH] arm64: dts: qcom: talos-evk: fix sdhc_2 vqmmc-supply for UHS-I mode
From: Konrad Dybcio @ 2026-06-16 12:01 UTC (permalink / raw)
  To: monish.chunara, andersson, konradybcio, robh, krzk+dt, conor+dt
  Cc: linux-arm-msm, devicetree, linux-kernel, linux-mmc, ulf.hansson,
	nitin.rawat, pradeep.pragallapati, komal.bajaj, jsodhapa
In-Reply-To: <20260609073509.2453273-1-mchunara@oss.qualcomm.com>

On 6/9/26 9:35 AM, monish.chunara@oss.qualcomm.com wrote:
> From: Monish Chunara <monish.chunara@oss.qualcomm.com>
> 
> SD card is detected as SDHS instead of UHS-I because sdhc_2 was
> configured with vreg_s4a as vqmmc-supply, which cannot switch
> between 1.8V and 3.3V.
> 
> Switch vqmmc-supply to vreg_l2a and update its voltage range to
> 1800000-2960000 uV to enable proper UHS-I signaling.
> 
> Signed-off-by: Jaypal Sodhaparmar <jsodhapa@qti.qualcomm.com>

Incorrect s-o-b chain - is Jaypal the original author, perhaps?

Konrad

^ permalink raw reply

* Re: [PATCH 1/9] dt-bindings: nvmem: imx-ocotp: Add support for secure-enclave
From: sashiko-bot @ 2026-06-16 12:02 UTC (permalink / raw)
  To: Frieder Schrempf; +Cc: Frank.Li, devicetree, imx, robh, conor+dt
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-1-cb7f3698c3e6@kontron.de>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The newly added `secure-enclave` property lacks a vendor prefix.
--

commit cf8315ecdb4e65137976e7833794688f5241f64e
Author: Frieder Schrempf <frieder.schrempf@kontron.de>

dt-bindings: nvmem: imx-ocotp: Add support for secure-enclave

Some SoCs like the i.MX9 family allow full access to the fuses only
through the secure enclave firmware API. Add a property to reference
the secure enclave node and let the driver use the API.

> diff --git a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
> index a8076d0e2737c..14a6429f4a4c0 100644
> --- a/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
> +++ b/Documentation/devicetree/bindings/nvmem/imx-ocotp.yaml
> @@ -53,6 +53,10 @@ properties:
>    reg:
>      maxItems: 1
>  
> +  secure-enclave:

[Severity: Low]
Does this new property need a vendor prefix? Since this appears to be specific
to NXP/Freescale i.MX hardware for referencing the secure enclave firmware, it
should likely be named with an appropriate vendor prefix like
"fsl,secure-enclave" rather than as a standard generic property.

> +    $ref: /schemas/types.yaml#/definitions/phandle
> +    description: A phandle to the secure enclave node
> +
>    clocks:
>      maxItems: 1

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de?part=1

^ permalink raw reply

* Re: [PATCH 4/9] nvmem: imx-ocotp-ele: Add keepout table for i.MX93
From: sashiko-bot @ 2026-06-16 12:04 UTC (permalink / raw)
  To: Frieder Schrempf; +Cc: Frank.Li, imx, conor+dt, robh, devicetree
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-4-cb7f3698c3e6@kontron.de>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Initialization of a flexible array member is not at the end of the initializer list, causing build errors.
--

commit d9179d39f1569f2fd5d16521edccfaa2096500c4
Author: Frieder Schrempf <frieder.schrempf@kontron.de>

nvmem: imx-ocotp-ele: Add keepout table for i.MX93

This commit adds an overall keepout table to describe which fuse
registers are invalid and inaccessible for the i.MX93.

> diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
> index a0d2985c6d030..dadec19ca2527 100644
> --- a/drivers/nvmem/imx-ocotp-ele.c
> +++ b/drivers/nvmem/imx-ocotp-ele.c
[ ... ]
> @@ -185,6 +201,8 @@ static const struct ocotp_devtype_data imx93_ocotp_data = {
>  		{ 188, 1, FUSE_ELE },
>  		{ 312, 200, FUSE_FSB }
>  	},
> +	.keepout = imx93_ocotp_keepout,
> +	.nkeepout = ARRAY_SIZE(imx93_ocotp_keepout),

[Severity: Low]
Does this cause a build failure? It looks like the designated initializers
for .keepout and .nkeepout are placed after the initialization of the flexible
array member .entry.

In C, flexible array members must be the last items initialized in a struct,
otherwise compilers like GCC will typically throw an "initialization of a
flexible array member not at end of struct" error.

Could these initializers be moved above the .entry initialization?

>  };
>  
>  static const struct ocotp_devtype_data imx94_ocotp_data = {

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de?part=4

^ permalink raw reply

* Re: [PATCH v2] arm64: dts: qcom: glymur-crd: Move common board nodes to shared DTSI
From: Konrad Dybcio @ 2026-06-16 12:04 UTC (permalink / raw)
  To: Gopikrishna Garmidi, Bjorn Andersson, Konrad Dybcio, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Pankaj Patil
  Cc: linux-arm-msm, devicetree, linux-kernel, sibi.sankar,
	rajendra.nayak
In-Reply-To: <b61ec109-92db-4dc1-ba7d-a5ce79fea08a@oss.qualcomm.com>

On 6/8/26 12:03 PM, Gopikrishna Garmidi wrote:
> 
> 
> On 5/19/2026 7:55 PM, Gopikrishna Garmidi wrote:
>> The Glymur and Mahua CRDs use the same board-level hardware for the
>> eDP display panel, MDSS DP3 controller and PHY, USB-C ports (via
>> pmic-glink), USB 0/1/HS/MP controllers, QMP PHYs, eUSB2 repeaters,
>> HID peripherals (touchpad, keyboard, touchscreen) and their dependent
>> regulators and pin control states. This has been verified against
>> both CRD schematics.
>>
>> Move these nodes from glymur-crd.dts to glymur-crd.dtsi to enable code
>> reuse with the Mahua CRD.
>>
>> Signed-off-by: Gopikrishna Garmidi <gopikrishna.garmidi@oss.qualcomm.com>
>> ---
>> Changes in v2:
>> - Rebased on top of next-20260518
>> - Updated subject to include glymur-crd scope prefix
>> - Rewrote commit message to describe the actual shared physical hardware
>>    rather than the code-sharing intent; the commonality was verified
>>    against Glymur CRD and Mahua CRD schematics
>> - Link to v1: https://lore.kernel.org/r/20260326-glymur-mahua-common-nodes-v1-1-12bb26920ea4@oss.qualcomm.com
>> ---
>>   arch/arm64/boot/dts/qcom/glymur-crd.dts  | 399 -------------------------------
>>   arch/arm64/boot/dts/qcom/glymur-crd.dtsi | 396 ++++++++++++++++++++++++++++++
>>   2 files changed, 396 insertions(+), 399 deletions(-)
> 
> 
> Hi Krzysztof, Konrad,
> 
> This has been waiting for a while now and already has a Reviewed-by from Dmitry. Could you take a look when you get a chance?

Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>

Konrad

^ permalink raw reply

* Re: [PATCH 6/9] nvmem: imx-ocotp-ele: Support the ELE API
From: sashiko-bot @ 2026-06-16 12:04 UTC (permalink / raw)
  To: Frieder Schrempf; +Cc: robh, devicetree, Frank.Li, conor+dt, imx
In-Reply-To: <20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-6-cb7f3698c3e6@kontron.de>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The dynamically allocated read buffer `p` is leaked when `imx_se_read_fuse()` returns an error in `imx_ocotp_reg_read()`.
- [High] Lack of unbind protection for the supplier Secure Enclave (`se_dev`) device leads to a potential Use-After-Free of its cached private data.
--

commit ce01a1e44e5619e1342b66aca8b1bf267d01e93e
Author: Frieder Schrempf <frieder.schrempf@kontron.de>

nvmem: imx-ocotp-ele: Support the ELE API

The fuses inside the Edgelock Secure Enclave are currently not accessed via
its API but through the FSB block which provides limited access to some
fuses. The ELE API allows us to access all fuses with read/write
permissions.

> diff --git a/drivers/nvmem/imx-ocotp-ele.c b/drivers/nvmem/imx-ocotp-ele.c
> index 9d3f94e35508f..92cc061b9b347 100644
> --- a/drivers/nvmem/imx-ocotp-ele.c
> +++ b/drivers/nvmem/imx-ocotp-ele.c

[ ... ]

> @@ -92,6 +97,19 @@ static int imx_ocotp_reg_read(void *context, unsigned int offset, void *val, siz
>  	buf = p;
>  
>  	for (i = index; i < (index + count); i++) {
> +		/*
> +		 * All fuse registers can be read via ELE. If the SE device is
> +		 * available, always prefer it.
> +		 */
> +		if (priv->se_data) {
> +			ret = imx_se_read_fuse(priv->se_data, i, buf++);
> +			if (ret) {
> +				mutex_unlock(&priv->lock);
> +				return ret;

[Severity: High]
Does this error path leak the memory allocated for p earlier in the function?

The allocation happens here:

	p = kzalloc(num_bytes, GFP_KERNEL);
	if (!p)
		return -ENOMEM;

Returning directly here will bypass the kfree(p) at the end of the function.

> +			}
> +			continue;
> +		}
> +

[ ... ]

> @@ -152,16 +203,36 @@ static int imx_ele_ocotp_probe(struct platform_device *pdev)
>  	if (IS_ERR(priv->base))
>  		return PTR_ERR(priv->base);
>  
> +	np = of_parse_phandle(pdev->dev.of_node, "secure-enclave", 0);
> +	if (!np) {
> +		dev_info(&pdev->dev, "missing or invalid SE handle, using readonly FSB\n");
> +	} else {
> +		priv->se_dev = of_find_device_by_node(np);
> +		of_node_put(np);
> +		if (!priv->se_dev)
> +			return dev_err_probe(&pdev->dev, -ENODEV, "failed to find SE device\n");
> +
> +		ret = devm_add_action_or_reset(&pdev->dev, imx_ocotp_put_se_dev,
> +					       priv->se_dev);
> +		if (ret)
> +			return ret;
> +
> +		priv->se_data = platform_get_drvdata(priv->se_dev);

[Severity: High]
Is there anything preventing the SE driver from being unbound via sysfs after we
cache its private data in priv->se_data here?

If the SE driver is unbound, its devres allocations (which likely include the
se_data) would be freed. Since we haven't established a device link
(e.g. using device_link_add) to enforce unbind ordering, we might be left
with a dangling pointer in priv->se_data.

Could this result in a use-after-free when imx_ocotp_reg_read() or
imx_ocotp_reg_write() later attempts to dereference priv->se_data?

> +		if (!priv->se_data)
> +			return dev_err_probe(&pdev->dev, -EPROBE_DEFER,
> +					     "SE device not ready\n");
> +	}
>

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260616-upstreaming-next-20260609-imx-ocotp-ele-v1-0-cb7f3698c3e6@kontron.de?part=6

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox