From nobody Tue Jun 16 12:44:22 2026 Received: from mail-43103.protonmail.ch (mail-43103.protonmail.ch [185.70.43.103]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7EE1130C372; Sun, 19 Apr 2026 22:32:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.103 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637957; cv=none; b=q6oZ4nZj5Gg6+O0uNLWWi9QA6LIx0RiqYVVVW2tY3FPFSYJS75l4uRICTFZD4dTOj9LOo9qFRaACQ+hWUFHcT/3/76e6HJDmLsDYx3foAyqyltvWkRSKqm8qjd+f4hDdZRNxEP96Kso8L7f0gHKch2Zzvwl11/07SMdcVxZtI6k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637957; c=relaxed/simple; bh=O7NUV8Pe2RmvQWUsJqHIlQefURCpl02rH1+1ojlP9QE=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=XjY32wugr87e20BfU9ZNn496txIaj+AnbWJlSQLy7+3HJ+745o0xzsj07dmP2JehRjw27nJp05jNv1BiirWWg4WTveFePDuQfO4d2bsHwUF4wBBbZHBGeSIIMIrjAYYnod9dmQ0pHMUIGhUGrk8Q2BeIOkkWv8F3ASF3P3qDh9Y= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=T/tt5ccK; arc=none smtp.client-ip=185.70.43.103 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="T/tt5ccK" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1776637948; x=1776897148; bh=RyR+f1EAhSv32vDVp/1bJqSCJenBgZC75f+fh+zlEFs=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=T/tt5ccK+EloMuoaUUbueRv73Vl9NSKLQ0+qZfKLAqlvbVEeeXkEWq302FzsmpO3g So4ZAYfRrpjABhS5QY50SGuX2fLCwiKe6n02RFYlrZcD7H9CjoN4xeryThW+OZl09w X7Rpj0b4oOxPIpmhQ8hSEf+ApIj1PKlM/JtuVsf5KdOs0Gl7xpCh9ggM5NJ5w/qlXT MeDuyBQ+prAGEgLyZcIbYk5L4O7mW3c4E2lpJW+3rwHxQDo/qVAIhRKaC197LAHuL0 cVFl4Rvli++MxQUDSWKXgzfzYahSqFxU/q2kcGgK4DWsB7+ywMp5GFjWk8N+EK/6ue xUx4FI5ZTghzA== Date: Sun, 19 Apr 2026 22:32:20 +0000 To: gregkh@linuxfoundation.org, jic23@kernel.org From: Hardik Phalet Cc: andy@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org, dlechner@baylibre.com, krzk+dt@kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, me@brighamcampbell.com, nuno.sa@analog.com, robh@kernel.org, skhan@linuxfoundation.org, Hardik Phalet , Hardik Phalet Subject: [PATCH v3 1/5] dt-bindings: vendor-prefixes: Add QST Corporation Message-ID: <20260420-qmc5883p-driver-v3-1-da1e97088f8b@pm.me> In-Reply-To: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> References: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> Feedback-ID: 166659585:user:proton X-Pm-Message-ID: e391c6cfe0ebbe4e32b2f4dc70f0430f507aca13 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the vendor prefix 'qstcorp' for QST Corporation, a manufacturer of MEMS sensors. Signed-off-by: Hardik Phalet Acked-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Docum= entation/devicetree/bindings/vendor-prefixes.yaml index ee7fd3cfe203..4ecf438f1a4a 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -1337,6 +1337,8 @@ patternProperties: description: Shenzhen QiShenglong Industrialist Co., Ltd. "^qnap,.*": description: QNAP Systems, Inc. + "^qstcorp,.*": + description: QST Corporation "^quanta,.*": description: Quanta Computer Inc. "^radxa,.*": --=20 2.53.0 From nobody Tue Jun 16 12:44:22 2026 Received: from mail-106121.protonmail.ch (mail-106121.protonmail.ch [79.135.106.121]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 28B7A30DEBA; Sun, 19 Apr 2026 22:32:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.135.106.121 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637966; cv=none; b=r9AbfzCcw7vXYuQMxYJJI/UZQ34KKSDInatl1PjqTwhp87uFHUnbaAABqdb8hhJqqAdSFIc1tg8uxpE33hdQbauAagx04KNdXx8MAcDYZHIgTPXiPWb1RpbKqEvuj5fkHh6Snbx9kwD9eeQtaaoyhaFMxAeRrQJB/DA5P6NQdC4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637966; c=relaxed/simple; bh=h4m4u0qkmJAER5pKaCfe8t7x9hCuAS05rfldDLVor6A=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=s1376OSiC+V3K8eNRLAU8OYpgnruE1crKnSZbfF0tThJBbveIXv6zrM+lvhXdTpq+mpJaKGRlzF0XUNc5gSm2+bqCKjx3tPdtSPmrtR6ATj6Xjk0/t+rnyEy2DUMoKI0xs560AeTNfHg23/nHua/7i8d65vJZA/UQbXl/pLPblo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=dZYC5gCp; arc=none smtp.client-ip=79.135.106.121 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="dZYC5gCp" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1776637957; x=1776897157; bh=yvoexbK0dmENpV9aFI3G1FiFeiAaZWAdXU+Z85ZXwl4=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=dZYC5gCpz7XagwRQ+epVGiUMV+0q3SE4SSgkSIXsmIQ8GdUeOyFivfKVjP/qx+OAK Y/Hqc2FKK17BBFM+7nD1YHw7BGCPdDINt8ITXhPN1irmNa3lfI2VnJOY1J/N3CbZXX YiqMBeeiA/Mi9eEPTisvmGqGZjHUc6DW9mG9ldkqd0veNI/3hl5rd0w7hX/Hikbt2l Xt/7GIZNTGnnP1w4O9pRbV82sfcl3QbrR9zrWUIfs9pfKOhmIiiESXOn0hdOVVoyO9 3Y5w4TASzk7Fr3EX9jZAfsg72pdTAuCEUgkCl8ywoTBwOQsWgb4q2XiSl0wv2vPZft ADUj/7o7Y3LKg== Date: Sun, 19 Apr 2026 22:32:32 +0000 To: gregkh@linuxfoundation.org, jic23@kernel.org From: Hardik Phalet Cc: andy@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org, dlechner@baylibre.com, krzk+dt@kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, me@brighamcampbell.com, nuno.sa@analog.com, robh@kernel.org, skhan@linuxfoundation.org, Hardik Phalet , Hardik Phalet Subject: [PATCH v3 2/5] dt-bindings: iio: magnetometer: QSTCORP QMC5883P Message-ID: <20260420-qmc5883p-driver-v3-2-da1e97088f8b@pm.me> In-Reply-To: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> References: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> Feedback-ID: 166659585:user:proton X-Pm-Message-ID: 0c087ad38cf9953db3d14364c90a411258dbab38 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add the device tree binding document for the QST QMC5883P, a 3-axis anisotropic magneto-resistive (AMR) sensor with a 16-bit ADC that communicates over I2C. Add a MAINTAINERS entry for the QSTCORP QMC5883P devicetree binding. Signed-off-by: Hardik Phalet Reviewed-by: Krzysztof Kozlowski --- .../iio/magnetometer/qstcorp,qmc5883p.yaml | 48 ++++++++++++++++++= ++++ MAINTAINERS | 6 +++ 2 files changed, 54 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc= 5883p.yaml b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc= 5883p.yaml new file mode 100644 index 000000000000..72cc3fef2226 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883p.y= aml @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/qstcorp,qmc5883p.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: QSTCORP QMC5883P 3-axis magnetometer + +maintainers: + - Hardik Phalet + +description: + The QMC5883P is a 3-axis anisotropic magneto-resistive (AMR) sensor with= a + 16-bit ADC. It communicates over I2C (standard and fast modes) and is + targeted at compass, navigation, and industrial applications. + +properties: + compatible: + const: qstcorp,qmc5883p + + reg: + maxItems: 1 + description: I2C address of the device; the default address is 0x2c + + vdd-supply: + description: + VDD power supply (2.5 V to 3.6 V). Powers all internal analog and + digital functional blocks. + +required: + - compatible + - reg + - vdd-supply + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + magnetometer@2c { + compatible =3D "qstcorp,qmc5883p"; + reg =3D <0x2c>; + vdd-supply =3D <&vdd_3v3>; + }; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 48fda1f8332e..d41f6b33d0e5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21554,6 +21554,12 @@ F: Documentation/networking/device_drivers/etherne= t/freescale/dpaa2/overview.rst F: drivers/bus/fsl-mc/ F: include/uapi/linux/fsl_mc.h =20 +QSTCORP QMC5883P MAGNETOMETER DRIVER +M: Hardik Phalet +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883p.yaml + QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org S: Orphan --=20 2.53.0 From nobody Tue Jun 16 12:44:22 2026 Received: from mail-24418.protonmail.ch (mail-24418.protonmail.ch [109.224.244.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 03827299927; Sun, 19 Apr 2026 22:32:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.18 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637975; cv=none; b=dLCcH1ZE3dwuIb4qKeTPxVNKfXjb1vBoh02o8RtSmr8NhdXKnuh1iZpBFeQkEAUGUl1s9teXbDIbsCm22K5K0wtnSRpvVpxqxC2w7msSd4MC9WaciQsKN0zU82TOxKHmECjf5mI+nmsh9MNA7Duusk2SWkB/g/5GIthIYuzNsZc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637975; c=relaxed/simple; bh=RdrYI8d7MRzIOIffUkJ3BjgaLqK28C4nChlQPSFNH4Y=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PftIHVOaZX/2CN0nLW9Vh2Hh3Mzw2Q4u/xen3fQomyEsxZ6LvF4X8lcIRQpq3Z9twnoG1Ou+PE4pSKubFd4tSI86JX+bSHe7Wd3Yp7LgH6+iGxU6fCHCb3kxHVXhkZL5MBu3De/gqueNJR/7ZWFMyvR9QyTBUvPHBucilNSfSXo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=dTBD07f4; arc=none smtp.client-ip=109.224.244.18 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="dTBD07f4" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1776637970; x=1776897170; bh=gbfBHWjgij5xGXlBJPoqmjo9JkASOqq4KGyWcMZoXZ0=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=dTBD07f4URKxmIrnl03j0/SmV+o/Jig7IN3i4MxyRU3nxlFSDSYEYLN2vPuwpQ0yE 20Mna+WfdFwQeKuYyH6ogvabtPbOod4INF2361PZgIw4vymEQRv/yXIirrhE5s+efJ oKoM3yWaiZsOd2T2KyMrbYy1keH4MnJd1NGs9keAjsaf9KNljc4KP4msOPuSb1tMJf E1Gne4rCPerneDzUxyrUnN8c9HWsMLBlSGE3cuDPC196pKed1OQy7IcwrcQ1qqgrPu wvYfWA7RoupoHcfTGuf4AYq8HHmosJQeGFIOEMxzAXgPlGaJW6jJF6Bgbo+tB5UymF +OWszqlO1SuPQ== Date: Sun, 19 Apr 2026 22:32:43 +0000 To: gregkh@linuxfoundation.org, jic23@kernel.org From: Hardik Phalet Cc: andy@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org, dlechner@baylibre.com, krzk+dt@kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, me@brighamcampbell.com, nuno.sa@analog.com, robh@kernel.org, skhan@linuxfoundation.org, Hardik Phalet , Hardik Phalet Subject: [PATCH v3 3/5] iio: magnetometer: add driver for QST QMC5883P Message-ID: <20260420-qmc5883p-driver-v3-3-da1e97088f8b@pm.me> In-Reply-To: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> References: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> Feedback-ID: 166659585:user:proton X-Pm-Message-ID: e9cb1788ddeec0df51519ccee696170b45a0d127 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add an IIO driver for the QST QMC5883P, a 3-axis anisotropic magneto-resistive (AMR) magnetometer with a 16-bit ADC, communicating over I2C. There is no existing upstream driver for this device. The driver supports: - Raw magnetic field readings on X, Y and Z axes - Four full-scale ranges (+/-2 G, +/-8 G, +/-12 G, +/-30 G) selectable via IIO_CHAN_INFO_SCALE - Output data rate configurable via IIO_CHAN_INFO_SAMP_FREQ (10, 50, 100, 200 Hz) - vdd-supply regulator management Regmap with an rbtree cache is used throughout. CTRL_1 and CTRL_2 bit fields are accessed via regmap_field to avoid read-modify-write races. The STATUS register is marked precious so regmap never reads it speculatively and clears the DRDY/OVFL bits unexpectedly. The probe-time init sequence is: soft reset, wait 300 us for POR completion, deassert reset, then drop the register cache so subsequent RMW writes read fresh values from the device. After reset the chip is in MODE_SUSPEND per datasheet =C2=A76.2.4, and is left there; the first userspace access will wake it via runtime PM (added in a follow-up patch). Cleanup is fully devm-managed via devm_regulator_get_enable() and devm_iio_device_register(). Oversampling ratio and runtime PM are added in follow-up patches. Signed-off-by: Hardik Phalet --- MAINTAINERS | 1 + drivers/iio/magnetometer/Kconfig | 11 + drivers/iio/magnetometer/Makefile | 2 + drivers/iio/magnetometer/qmc5883p.c | 574 ++++++++++++++++++++++++++++++++= ++++ 4 files changed, 588 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d41f6b33d0e5..2fbbe8831a7c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21559,6 +21559,7 @@ M: Hardik Phalet L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/magnetometer/qstcorp,qmc5883p.yaml +F: drivers/iio/magnetometer/qmc5883p.c =20 QT1010 MEDIA DRIVER L: linux-media@vger.kernel.org diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kc= onfig index fb313e591e85..333c5e6f231d 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -298,4 +298,15 @@ config YAMAHA_YAS530 To compile this driver as a module, choose M here: the module will be called yamaha-yas. =20 +config QMC5883P + tristate "QMC5883P 3-Axis Magnetometer" + depends on I2C + select REGMAP_I2C + help + Say yes here to build support for QMC5883P I2C-based + 3-axis magnetometer chip. + + To compile this driver as a module, choose M here: the + module will be called qmc5883p. + endmenu diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/M= akefile index 5bd227f8c120..ff519a055d77 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -39,3 +39,5 @@ obj-$(CONFIG_SI7210) +=3D si7210.o obj-$(CONFIG_TI_TMAG5273) +=3D tmag5273.o =20 obj-$(CONFIG_YAMAHA_YAS530) +=3D yamaha-yas530.o + +obj-$(CONFIG_QMC5883P) +=3D qmc5883p.o diff --git a/drivers/iio/magnetometer/qmc5883p.c b/drivers/iio/magnetometer= /qmc5883p.c new file mode 100644 index 000000000000..e4a76ae7c2cf --- /dev/null +++ b/drivers/iio/magnetometer/qmc5883p.c @@ -0,0 +1,574 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * qmc5883p.c - QMC5883P magnetometer driver + * + * Copyright 2026 Hardik Phalet + * + * TODO: add triggered buffer support, PM, OSR, DSR + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Register definition + */ +#define QMC5883P_REG_CHIP_ID 0x00 +#define QMC5883P_REG_X_LSB 0x01 +#define QMC5883P_REG_X_MSB 0x02 +#define QMC5883P_REG_Y_LSB 0x03 +#define QMC5883P_REG_Y_MSB 0x04 +#define QMC5883P_REG_Z_LSB 0x05 +#define QMC5883P_REG_Z_MSB 0x06 +#define QMC5883P_REG_STATUS 0x09 +#define QMC5883P_REG_CTRL_1 0x0A +#define QMC5883P_REG_CTRL_2 0x0B + +/* + * Value definition + */ +#define QMC5883P_MODE_SUSPEND 0x00 +#define QMC5883P_MODE_NORMAL 0x01 +#define QMC5883P_MODE_SINGLE 0x02 +#define QMC5883P_MODE_CONTINUOUS 0x03 + +/* + * Output data rate + */ +#define QMC5883P_ODR_10 0x00 +#define QMC5883P_ODR_50 0x01 +#define QMC5883P_ODR_100 0x02 +#define QMC5883P_ODR_200 0x03 + +/* + * Oversampling rate + */ +#define QMC5883P_OSR_8 0x00 +#define QMC5883P_OSR_4 0x01 +#define QMC5883P_OSR_2 0x02 +#define QMC5883P_OSR_1 0x03 + +#define QMC5883P_RNG_30G 0x00 +#define QMC5883P_RNG_12G 0x01 +#define QMC5883P_RNG_08G 0x02 +#define QMC5883P_RNG_02G 0x03 + +#define QMC5883P_DRDY_POLL_US 1000 + +#define QMC5883P_CHIP_ID 0x80 + +#define QMC5883P_STATUS_DRDY BIT(0) +#define QMC5883P_STATUS_OVFL BIT(1) + +struct qmc5883p_rf { + struct regmap_field *osr; + struct regmap_field *odr; + struct regmap_field *mode; + struct regmap_field *rng; + struct regmap_field *sftrst; + struct regmap_field *chip_id; +}; + +struct qmc5883p_data { + struct device *dev; + struct regmap *regmap; + struct mutex mutex; /* protects regmap and rf field accesses */ + struct qmc5883p_rf rf; +}; + +enum qmc5883p_channels { + AXIS_X =3D 0, + AXIS_Y, + AXIS_Z, +}; + +/* + * Scale factors in nT/LSB for IIO_VAL_INT_PLUS_NANO, derived from datashe= et + * Table 2 sensitivities (LSB/G) converted to LSB/T (1 G =3D 1e-4 T): + * sensitivity_T =3D sensitivity_G * 10000 + * scale_nT =3D 1e9 / sensitivity_T + * + * The 8G and 2G entries truncate 26.666... and 6.666... nT/LSB respective= ly; + * IIO_VAL_INT_PLUS_NANO cannot carry the exact rationals, but the chosen + * values match what IIO_VAL_FRACTIONAL would have rendered and therefore + * round-trip cleanly through sysfs write back. + * + * Index matches register value: RNG<1:0> =3D 0b00..0b11 + */ +static const int qmc5883p_scale[][2] =3D { + [QMC5883P_RNG_30G] =3D { 0, 100 }, + [QMC5883P_RNG_12G] =3D { 0, 40 }, + [QMC5883P_RNG_08G] =3D { 0, 26 }, + [QMC5883P_RNG_02G] =3D { 0, 6 }, +}; + +static const int qmc5883p_odr[] =3D { + [QMC5883P_ODR_10] =3D 10, + [QMC5883P_ODR_50] =3D 50, + [QMC5883P_ODR_100] =3D 100, + [QMC5883P_ODR_200] =3D 200, +}; + +static const struct regmap_range qmc5883p_readable_ranges[] =3D { + regmap_reg_range(QMC5883P_REG_CHIP_ID, QMC5883P_REG_Z_MSB), + regmap_reg_range(QMC5883P_REG_STATUS, QMC5883P_REG_CTRL_2), +}; + +static const struct regmap_range qmc5883p_writable_ranges[] =3D { + regmap_reg_range(QMC5883P_REG_CTRL_1, QMC5883P_REG_CTRL_2), +}; + +/* + * Volatile registers: hardware updates these independently of the driver. + * regmap will never serve these from cache. + */ +static const struct regmap_range qmc5883p_volatile_ranges[] =3D { + regmap_reg_range(QMC5883P_REG_X_LSB, QMC5883P_REG_Z_MSB), + regmap_reg_range(QMC5883P_REG_STATUS, QMC5883P_REG_STATUS), + regmap_reg_range(QMC5883P_REG_CTRL_2, QMC5883P_REG_CTRL_2), +}; + +/* + * Precious registers: reading has a side effect (clears DRDY/OVFL bits). + * regmap will never read these speculatively. + */ +static const struct regmap_range qmc5883p_precious_ranges[] =3D { + regmap_reg_range(QMC5883P_REG_STATUS, QMC5883P_REG_STATUS), +}; + +static const struct regmap_access_table qmc5883p_readable_table =3D { + .yes_ranges =3D qmc5883p_readable_ranges, + .n_yes_ranges =3D ARRAY_SIZE(qmc5883p_readable_ranges), +}; + +static const struct regmap_access_table qmc5883p_writable_table =3D { + .yes_ranges =3D qmc5883p_writable_ranges, + .n_yes_ranges =3D ARRAY_SIZE(qmc5883p_writable_ranges), +}; + +static const struct regmap_access_table qmc5883p_volatile_table =3D { + .yes_ranges =3D qmc5883p_volatile_ranges, + .n_yes_ranges =3D ARRAY_SIZE(qmc5883p_volatile_ranges), +}; + +static const struct regmap_access_table qmc5883p_precious_table =3D { + .yes_ranges =3D qmc5883p_precious_ranges, + .n_yes_ranges =3D ARRAY_SIZE(qmc5883p_precious_ranges), +}; + +static const struct regmap_config qmc5883p_regmap_config =3D { + .reg_bits =3D 8, + .val_bits =3D 8, + .max_register =3D 0x0B, + .cache_type =3D REGCACHE_RBTREE, + .rd_table =3D &qmc5883p_readable_table, + .wr_table =3D &qmc5883p_writable_table, + .volatile_table =3D &qmc5883p_volatile_table, + .precious_table =3D &qmc5883p_precious_table, +}; + +static const struct reg_field qmc5883p_rf_osr =3D + REG_FIELD(QMC5883P_REG_CTRL_1, 4, 5); +static const struct reg_field qmc5883p_rf_odr =3D + REG_FIELD(QMC5883P_REG_CTRL_1, 2, 3); +static const struct reg_field qmc5883p_rf_mode =3D + REG_FIELD(QMC5883P_REG_CTRL_1, 0, 1); +static const struct reg_field qmc5883p_rf_rng =3D + REG_FIELD(QMC5883P_REG_CTRL_2, 2, 3); +static const struct reg_field qmc5883p_rf_sftrst =3D + REG_FIELD(QMC5883P_REG_CTRL_2, 7, 7); +static const struct reg_field qmc5883p_rf_chip_id =3D + REG_FIELD(QMC5883P_REG_CHIP_ID, 0, 7); + +/* + * qmc5883p_get_measure - read all three axes. + * Must be called with data->mutex held. + */ +static int qmc5883p_get_measure(struct qmc5883p_data *data, s16 *x, s16 *y, + s16 *z) +{ + int ret; + u8 reg_data[6]; + unsigned int status; + + /* + * Poll the status register until DRDY is set or timeout. + * Read the whole register in one shot so that OVFL is captured from + * the same read: reading 0x09 clears both DRDY and OVFL, so a second + * read would always see OVFL=3D0. + * At ODR=3D10Hz one period is 100ms; use 150ms as a safe upper bound. + */ + ret =3D regmap_read_poll_timeout(data->regmap, QMC5883P_REG_STATUS, + status, status & QMC5883P_STATUS_DRDY, + QMC5883P_DRDY_POLL_US, + 150 * (MICRO / MILLI)); + if (ret) + return ret; + + if (status & QMC5883P_STATUS_OVFL) { + dev_warn_ratelimited(data->dev, + "data overflow, consider reducing field range\n"); + ret =3D -ERANGE; + return ret; + } + + ret =3D regmap_bulk_read(data->regmap, QMC5883P_REG_X_LSB, reg_data, + ARRAY_SIZE(reg_data)); + if (ret) + return ret; + + *x =3D (s16)get_unaligned_le16(®_data[0]); + *y =3D (s16)get_unaligned_le16(®_data[2]); + *z =3D (s16)get_unaligned_le16(®_data[4]); + + return ret; +} + +static int qmc5883p_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + s16 x, y, z; + struct qmc5883p_data *data =3D iio_priv(indio_dev); + int ret; + unsigned int regval; + + guard(mutex)(&data->mutex); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret =3D qmc5883p_get_measure(data, &x, &y, &z); + if (ret < 0) + return ret; + switch (chan->address) { + case AXIS_X: + *val =3D x; + break; + case AXIS_Y: + *val =3D y; + break; + case AXIS_Z: + *val =3D z; + break; + } + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + ret =3D regmap_field_read(data->rf.rng, ®val); + if (ret < 0) + return ret; + *val =3D qmc5883p_scale[regval][0]; + *val2 =3D qmc5883p_scale[regval][1]; + return IIO_VAL_INT_PLUS_NANO; + + case IIO_CHAN_INFO_SAMP_FREQ: + ret =3D regmap_field_read(data->rf.odr, ®val); + if (ret < 0) + return ret; + *val =3D qmc5883p_odr[regval]; + return IIO_VAL_INT; + } + + return -EINVAL; +} + +static int qmc5883p_write_scale(struct qmc5883p_data *data, int val, int v= al2) +{ + int i; + + for (i =3D 0; i < ARRAY_SIZE(qmc5883p_scale); i++) { + if (qmc5883p_scale[i][0] =3D=3D val && qmc5883p_scale[i][1] =3D=3D val2) + return regmap_field_write(data->rf.rng, i); + } + + return -EINVAL; +} + +static int qmc5883p_write_odr(struct qmc5883p_data *data, int val) +{ + int i; + + for (i =3D 0; i < ARRAY_SIZE(qmc5883p_odr); i++) { + if (qmc5883p_odr[i] =3D=3D val) + return regmap_field_write(data->rf.odr, i); + } + + return -EINVAL; +} + +static int qmc5883p_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct qmc5883p_data *data =3D iio_priv(indio_dev); + int ret, restore; + + guard(mutex)(&data->mutex); + + ret =3D regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND); + if (ret) + return ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret =3D qmc5883p_write_odr(data, val); + break; + case IIO_CHAN_INFO_SCALE: + ret =3D qmc5883p_write_scale(data, val, val2); + break; + default: + ret =3D -EINVAL; + break; + } + + restore =3D regmap_field_write(data->rf.mode, QMC5883P_MODE_NORMAL); + if (restore && !ret) + ret =3D restore; + + return ret; +} + +/* + * qmc5883p_read_avail - expose available values to userspace. + * + * Creates the _available sysfs attributes automatically: + * in_magn_sampling_frequency_available + * in_magn_scale_available + */ +static int qmc5883p_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 =3D qmc5883p_odr; + *type =3D IIO_VAL_INT; + *length =3D ARRAY_SIZE(qmc5883p_odr); + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_SCALE: + *vals =3D (const int *)qmc5883p_scale; + *type =3D IIO_VAL_INT_PLUS_NANO; + *length =3D ARRAY_SIZE(qmc5883p_scale) * 2; + return IIO_AVAIL_LIST; + + default: + return -EINVAL; + } +} + +/* + * Tell the IIO core how to parse sysfs writes. Without this, the core + * defaults to IIO_VAL_INT_PLUS_MICRO (6 fractional digits), which would + * silently truncate nano-scale writes like "0.000000040" to 0. + */ +static int qmc5883p_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; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static const struct iio_info qmc5883p_info =3D { + .read_raw =3D qmc5883p_read_raw, + .write_raw =3D qmc5883p_write_raw, + .write_raw_get_fmt =3D qmc5883p_write_raw_get_fmt, + .read_avail =3D qmc5883p_read_avail, +}; + +static int qmc5883p_rf_init(struct qmc5883p_data *data) +{ + struct regmap *regmap =3D data->regmap; + struct device *dev =3D data->dev; + struct qmc5883p_rf *rf =3D &data->rf; + + rf->osr =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_osr); + if (IS_ERR(rf->osr)) + return PTR_ERR(rf->osr); + + rf->odr =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_odr); + if (IS_ERR(rf->odr)) + return PTR_ERR(rf->odr); + + rf->mode =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_mode); + if (IS_ERR(rf->mode)) + return PTR_ERR(rf->mode); + + rf->rng =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_rng); + if (IS_ERR(rf->rng)) + return PTR_ERR(rf->rng); + + rf->sftrst =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_sftrst); + if (IS_ERR(rf->sftrst)) + return PTR_ERR(rf->sftrst); + + rf->chip_id =3D devm_regmap_field_alloc(dev, regmap, qmc5883p_rf_chip_id); + if (IS_ERR(rf->chip_id)) + return PTR_ERR(rf->chip_id); + + return 0; +} + +static int qmc5883p_read_chip_id(struct qmc5883p_data *data) +{ + int ret, regval; + + ret =3D regmap_field_read(data->rf.chip_id, ®val); + if (ret) + return dev_err_probe(data->dev, ret, + "failed to read chip ID\n"); + + if (regval !=3D QMC5883P_CHIP_ID) + dev_info(data->dev, "unexpected chip ID %#x, expected %#x\n", + regval, QMC5883P_CHIP_ID); + + return 0; +} + +#define QMC5883P_CHAN(ch) \ + { \ + .type =3D IIO_MAGN, \ + .channel2 =3D IIO_MOD_##ch, \ + .modified =3D 1, \ + .address =3D AXIS_##ch, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + } + +static const struct iio_chan_spec qmc5883p_channels[] =3D { + QMC5883P_CHAN(X), + QMC5883P_CHAN(Y), + QMC5883P_CHAN(Z), +}; + +static int qmc5883p_chip_init(struct qmc5883p_data *data) +{ + int ret; + + ret =3D regmap_field_write(data->rf.sftrst, 1); + if (ret) + return ret; + + /* + * The datasheet does not specify a post-reset delay, but POR + * completion takes up to 250 microseconds. Use 300 microseconds + * to be safe. + */ + fsleep(300); + + ret =3D regmap_field_write(data->rf.sftrst, 0); + if (ret) + return ret; + + /* + * Soft reset restored every register to its default. Drop the cache + * so subsequent RMW writes read fresh values from the device. + */ + regcache_drop_region(data->regmap, QMC5883P_REG_CHIP_ID, + QMC5883P_REG_CTRL_2); + + /* Chip is now in MODE_SUSPEND per datasheet =C2=A76.2.4. Leave it there.= */ + return 0; +} + +static int qmc5883p_probe(struct i2c_client *client) +{ + struct device *dev =3D &client->dev; + struct qmc5883p_data *data; + struct iio_dev *indio_dev; + struct regmap *regmap; + int ret; + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + regmap =3D devm_regmap_init_i2c(client, &qmc5883p_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "regmap initialization failed\n"); + + data =3D iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->dev =3D dev; + data->regmap =3D regmap; + + mutex_init(&data->mutex); + + ret =3D qmc5883p_rf_init(data); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize regmap fields\n"); + + indio_dev->name =3D "qmc5883p"; + indio_dev->info =3D &qmc5883p_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D qmc5883p_channels; + indio_dev->num_channels =3D ARRAY_SIZE(qmc5883p_channels); + + ret =3D devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize vdd regulator\n"); + + /* Datasheet specifies up to 50 ms supply ramp + 250 us POR time. */ + fsleep(50 * (MICRO / MILLI) + 250); + + ret =3D qmc5883p_read_chip_id(data); + if (ret) + return ret; + + ret =3D qmc5883p_chip_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to initialize chip\n"); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id qmc5883p_of_match[] =3D { + { .compatible =3D "qstcorp,qmc5883p" }, + { } +}; +MODULE_DEVICE_TABLE(of, qmc5883p_of_match); + +static const struct i2c_device_id qmc5883p_id[] =3D { + { "qmc5883p", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, qmc5883p_id); + +static struct i2c_driver qmc5883p_driver =3D { + .driver =3D { + .name =3D "qmc5883p", + .of_match_table =3D qmc5883p_of_match, + }, + .probe =3D qmc5883p_probe, + .id_table =3D qmc5883p_id, +}; +module_i2c_driver(qmc5883p_driver); + +MODULE_AUTHOR("Hardik Phalet "); +MODULE_DESCRIPTION("QST QMC5883P 3-axis magnetometer driver"); +MODULE_LICENSE("GPL"); --=20 2.53.0 From nobody Tue Jun 16 12:44:22 2026 Received: from mail-10628.protonmail.ch (mail-10628.protonmail.ch [79.135.106.28]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id A9EC125EF87 for ; Sun, 19 Apr 2026 22:33:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=79.135.106.28 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637986; cv=none; b=LNMBv0ib9LrAzBn8C6BwyqNisfq/PugLaTXb2tQ+++CNd7FM4CYOCMvBpL4U5PgUBGQzvCVWCfGOF1G9jrBH+93w1zRm73a+AF5Sn5hpR8M7C/0LG9gHhwcgCzRo9ORsahQBOOq0OhqQtdYHGMXMxXhW8Faf1eUTuFwPAXf3KMI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776637986; c=relaxed/simple; bh=a8ZGYGuefHGOs5YQ/RdZh+1MbmDGMOH4isE8RgypgcY=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=PSM3VSIvvsek7tlik0bYF9KcTEuSNE+M/RrBffRHX2PCPgH1KG1ZWl51EffZAxsoheLnvpsxiZBzhfQrnazse4KsMXpR7tP2/ey3HUiFySKGv96VfgMt2e3OsYqSdiHUYS8YQsG49KoJWlJ5YJn+lCqwzLutuXku3AqBkdvK3Eg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=GtgzBV8e; arc=none smtp.client-ip=79.135.106.28 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="GtgzBV8e" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1776637982; x=1776897182; bh=2x7EboPkNic0+Mq4adzJ5RyZZdI9Rg7BjDNqD1bzzBA=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=GtgzBV8eyRd2JGlWtEaJhrV7p0QcBCFF0CoNIv2HY8h+1ab/djBWdLiYqpdNna3Xh Hjf4Ne6jtgKmicUEs+rZdM6kl2n1O6LLEvN8mgkXQUqnhav9uc2Sa6o0thD4JQJrhP Ojk64PEk8Owbtu46sKHRx5Q1cm478a2ThVgp6Uv9q2hPGV100OnbT91sjepizN7lNG Qu4PlIoRDGj0MJcTr5bhOHLyk6K6P9lX/+cj5r5JtnM0uM1tGW5xdikWLLrk4ZIw9k u8J1Vx+bqpa3uV1GfBougoD9THdQBPZw9DpdWVegZ1VDIrALomIEvYJ9bTU1qxhu4T MWLiW1DIXHLtA== Date: Sun, 19 Apr 2026 22:32:56 +0000 To: gregkh@linuxfoundation.org, jic23@kernel.org From: Hardik Phalet Cc: andy@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org, dlechner@baylibre.com, krzk+dt@kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, me@brighamcampbell.com, nuno.sa@analog.com, robh@kernel.org, skhan@linuxfoundation.org, Hardik Phalet , Hardik Phalet Subject: [PATCH v3 4/5] iio: magnetometer: qmc5883p: add oversampling ratio support Message-ID: <20260420-qmc5883p-driver-v3-4-da1e97088f8b@pm.me> In-Reply-To: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> References: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> Feedback-ID: 166659585:user:proton X-Pm-Message-ID: 20cd3cfe6bb6b0447260ccc827e3e999713a35c1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Expose the CTRL_1 OSR field through IIO_CHAN_INFO_OVERSAMPLING_RATIO so userspace can select among the four oversampling settings (1, 2, 4, 8) supported by the device. Read, write and available handlers mirror the existing SAMP_FREQ plumbing and use the already-present rf.osr regmap field. Signed-off-by: Hardik Phalet --- drivers/iio/magnetometer/qmc5883p.c | 46 +++++++++++++++++++++++++++++++++= +--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/drivers/iio/magnetometer/qmc5883p.c b/drivers/iio/magnetometer= /qmc5883p.c index e4a76ae7c2cf..d0e4a1a600b6 100644 --- a/drivers/iio/magnetometer/qmc5883p.c +++ b/drivers/iio/magnetometer/qmc5883p.c @@ -4,7 +4,7 @@ * * Copyright 2026 Hardik Phalet * - * TODO: add triggered buffer support, PM, OSR, DSR + * TODO: add triggered buffer support, PM, DSR * */ =20 @@ -119,6 +119,13 @@ static const int qmc5883p_odr[] =3D { [QMC5883P_ODR_200] =3D 200, }; =20 +static const int qmc5883p_osr[] =3D { + [QMC5883P_OSR_1] =3D 1, + [QMC5883P_OSR_2] =3D 2, + [QMC5883P_OSR_4] =3D 4, + [QMC5883P_OSR_8] =3D 8, +}; + static const struct regmap_range qmc5883p_readable_ranges[] =3D { regmap_reg_range(QMC5883P_REG_CHIP_ID, QMC5883P_REG_Z_MSB), regmap_reg_range(QMC5883P_REG_STATUS, QMC5883P_REG_CTRL_2), @@ -277,6 +284,13 @@ static int qmc5883p_read_raw(struct iio_dev *indio_dev, return ret; *val =3D qmc5883p_odr[regval]; return IIO_VAL_INT; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret =3D regmap_field_read(data->rf.osr, ®val); + if (ret < 0) + return ret; + *val =3D qmc5883p_osr[regval]; + return IIO_VAL_INT; } =20 return -EINVAL; @@ -306,6 +320,18 @@ static int qmc5883p_write_odr(struct qmc5883p_data *da= ta, int val) return -EINVAL; } =20 +static int qmc5883p_write_osr(struct qmc5883p_data *data, int val) +{ + int i; + + for (i =3D 0; i < ARRAY_SIZE(qmc5883p_osr); i++) { + if (qmc5883p_osr[i] =3D=3D val) + return regmap_field_write(data->rf.osr, i); + } + + return -EINVAL; +} + static int qmc5883p_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -323,6 +349,9 @@ static int qmc5883p_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SAMP_FREQ: ret =3D qmc5883p_write_odr(data, val); break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + ret =3D qmc5883p_write_osr(data, val); + break; case IIO_CHAN_INFO_SCALE: ret =3D qmc5883p_write_scale(data, val, val2); break; @@ -357,6 +386,12 @@ static int qmc5883p_read_avail(struct iio_dev *indio_d= ev, *length =3D ARRAY_SIZE(qmc5883p_odr); return IIO_AVAIL_LIST; =20 + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + *vals =3D qmc5883p_osr; + *type =3D IIO_VAL_INT; + *length =3D ARRAY_SIZE(qmc5883p_osr); + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: *vals =3D (const int *)qmc5883p_scale; *type =3D IIO_VAL_INT_PLUS_NANO; @@ -382,6 +417,8 @@ static int qmc5883p_write_raw_get_fmt(struct iio_dev *i= ndio_dev, return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_SAMP_FREQ: return IIO_VAL_INT; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return IIO_VAL_INT; default: return -EINVAL; } @@ -452,9 +489,12 @@ static int qmc5883p_read_chip_id(struct qmc5883p_data = *data) .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_separate_available =3D BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type =3D \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .info_mask_shared_by_type_available =3D \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ } =20 static const struct iio_chan_spec qmc5883p_channels[] =3D { --=20 2.53.0 From nobody Tue Jun 16 12:44:22 2026 Received: from mail-06.mail-europe.com (mail-06.mail-europe.com [85.9.210.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BBDEE299927 for ; Sun, 19 Apr 2026 22:33:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=85.9.210.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776638010; cv=none; b=CLU+o4QSNtEH6bhHxjJUE8l3whgV9HFOvnl0vVvD59pKyc19rUYxcelntb3XqM3EdZItGMzY+ffR9FjqGbtDCvLYNvVCFwFuqefgp/cUpx2sVzF+vvE25rvRYm07D3s11TULFF5eq4N4VSF2wc/lQo/yhpGyxA5LeCjL4vNk6ME= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1776638010; c=relaxed/simple; bh=jVHKcrdHgp2mPFVrsxUyuoHHQKHWNv48U5w4Ana3cX0=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=kX1YJuiE1U1IY1xRsT0YE22m7gX6yWtedQ8fBay/rZlgNfvCxnYE+FKoOkYQCTbq25YaV1ZpFn6/OVqSlAr1rljWmk/W829Yyr8rc3xtcNIqfMQG+NRmy+X5t3tUgauaQgvx0USCpM/hkye8BsOohvR41sY4kOgfZF63qM/yceI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me; spf=pass smtp.mailfrom=pm.me; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b=O26cj/vB; arc=none smtp.client-ip=85.9.210.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=pm.me Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=pm.me Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=pm.me header.i=@pm.me header.b="O26cj/vB" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pm.me; s=protonmail3; t=1776637992; x=1776897192; bh=fY6tuBw83PLvCS7CgtqrueeoVYZLrpLZCqPb3f5s+wg=; h=Date:To:From:Cc:Subject:Message-ID:In-Reply-To:References: Feedback-ID:From:To:Cc:Date:Subject:Reply-To:Feedback-ID: Message-ID:BIMI-Selector; b=O26cj/vBj5E2Mrer2SEcbIfQfRbxXARf2Fk7KOoGFuKuGNfP8yB8Ef2+LiovlGmVF 3hHELKFCvoJnxCNmqKgZf5e20EEH5FRIOLJ9zjmkNbmbcAPYxo2L9wXcql7bK2Fhe/ SD1dL4zIkcliObqFBIWdGeFeoj72oEoPwdCgSNcePjlKRxQCdTqZb0qlu9NWN+75q+ 3bPSrtWZZh+YkiDhQhDZ8zgCDh4wJJ/fAy7sT56Pdv5TzY/+dxjsyLjvsiyOGOyzjS wRDjtopxII7n335ER/pYneiiBjFNzmHpFuYK38knrsR/DeYcnfqv8hxAlXRQArgquP 5AeV8fmQb90qw== Date: Sun, 19 Apr 2026 22:33:06 +0000 To: gregkh@linuxfoundation.org, jic23@kernel.org From: Hardik Phalet Cc: andy@kernel.org, conor+dt@kernel.org, devicetree@vger.kernel.org, dlechner@baylibre.com, krzk+dt@kernel.org, linux-iio@vger.kernel.org, linux-kernel@vger.kernel.org, linux-staging@lists.linux.dev, me@brighamcampbell.com, nuno.sa@analog.com, robh@kernel.org, skhan@linuxfoundation.org, Hardik Phalet , Hardik Phalet Subject: [PATCH v3 5/5] iio: magnetometer: qmc5883p: add PM support Message-ID: <20260420-qmc5883p-driver-v3-5-da1e97088f8b@pm.me> In-Reply-To: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> References: <20260420-qmc5883p-driver-v3-0-da1e97088f8b@pm.me> Feedback-ID: 166659585:user:proton X-Pm-Message-ID: 3be41c2537fd802e31929c05de99c7bcd3c89245 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add runtime PM with a 2 s autosuspend delay. Per datasheet =C2=A76.2.1 the chip continuously samples in MODE_NORMAL; putting it into MODE_SUSPEND when idle drops current from up to 1180 uA to ~22 uA (datasheet Table 2). Wrap qmc5883p_get_measure() and qmc5883p_write_raw() with pm_runtime_resume_and_get() / pm_runtime_put_autosuspend(), converting early returns to a goto so the put is always paired. System sleep is delegated to the runtime callbacks via SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume). A devm action is registered before devm_pm_runtime_enable() so that LIFO teardown on unbind runs pm_runtime_disable() first (freezing PM state) and then suspends the hardware via MODE_SUSPEND. Signed-off-by: Hardik Phalet --- drivers/iio/magnetometer/qmc5883p.c | 69 +++++++++++++++++++++++++++++++++= +--- 1 file changed, 64 insertions(+), 5 deletions(-) diff --git a/drivers/iio/magnetometer/qmc5883p.c b/drivers/iio/magnetometer= /qmc5883p.c index d0e4a1a600b6..0ff635924abf 100644 --- a/drivers/iio/magnetometer/qmc5883p.c +++ b/drivers/iio/magnetometer/qmc5883p.c @@ -4,7 +4,7 @@ * * Copyright 2026 Hardik Phalet * - * TODO: add triggered buffer support, PM, DSR + * TODO: add triggered buffer support, DSR * */ =20 @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,10 @@ static int qmc5883p_get_measure(struct qmc5883p_data *= data, s16 *x, s16 *y, u8 reg_data[6]; unsigned int status; =20 + ret =3D pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + /* * Poll the status register until DRDY is set or timeout. * Read the whole register in one shot so that OVFL is captured from @@ -220,24 +225,26 @@ static int qmc5883p_get_measure(struct qmc5883p_data = *data, s16 *x, s16 *y, QMC5883P_DRDY_POLL_US, 150 * (MICRO / MILLI)); if (ret) - return ret; + goto out; =20 if (status & QMC5883P_STATUS_OVFL) { dev_warn_ratelimited(data->dev, "data overflow, consider reducing field range\n"); ret =3D -ERANGE; - return ret; + goto out; } =20 ret =3D regmap_bulk_read(data->regmap, QMC5883P_REG_X_LSB, reg_data, ARRAY_SIZE(reg_data)); if (ret) - return ret; + goto out; =20 *x =3D (s16)get_unaligned_le16(®_data[0]); *y =3D (s16)get_unaligned_le16(®_data[2]); *z =3D (s16)get_unaligned_le16(®_data[4]); =20 +out: + pm_runtime_put_autosuspend(data->dev); return ret; } =20 @@ -341,10 +348,14 @@ static int qmc5883p_write_raw(struct iio_dev *indio_d= ev, =20 guard(mutex)(&data->mutex); =20 - ret =3D regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND); + ret =3D pm_runtime_resume_and_get(data->dev); if (ret) return ret; =20 + ret =3D regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND); + if (ret) + goto out; + switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: ret =3D qmc5883p_write_odr(data, val); @@ -364,6 +375,8 @@ static int qmc5883p_write_raw(struct iio_dev *indio_dev, if (restore && !ret) ret =3D restore; =20 +out: + pm_runtime_put_autosuspend(data->dev); return ret; } =20 @@ -533,6 +546,18 @@ static int qmc5883p_chip_init(struct qmc5883p_data *da= ta) return 0; } =20 +static void qmc5883p_suspend_action(void *arg) +{ + struct qmc5883p_data *data =3D arg; + + /* + * PM is already disabled at this point (devm LIFO); put the hardware + * into MODE_SUSPEND directly so the chip is not left sampling after + * unbind. + */ + regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND); +} + static int qmc5883p_probe(struct i2c_client *client) { struct device *dev =3D &client->dev; @@ -584,9 +609,42 @@ static int qmc5883p_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "failed to initialize chip\n"); =20 + ret =3D devm_add_action_or_reset(dev, qmc5883p_suspend_action, data); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 2000); + pm_runtime_use_autosuspend(dev); + + ret =3D devm_pm_runtime_enable(dev); + if (ret) + return ret; + return devm_iio_device_register(dev, indio_dev); } =20 +static int qmc5883p_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct qmc5883p_data *data =3D iio_priv(indio_dev); + + return regmap_field_write(data->rf.mode, QMC5883P_MODE_SUSPEND); +} + +static int qmc5883p_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev =3D dev_get_drvdata(dev); + struct qmc5883p_data *data =3D iio_priv(indio_dev); + + return regmap_field_write(data->rf.mode, QMC5883P_MODE_NORMAL); +} + +static const struct dev_pm_ops qmc5883p_dev_pm_ops =3D { + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + RUNTIME_PM_OPS(qmc5883p_runtime_suspend, + qmc5883p_runtime_resume, NULL) +}; + static const struct of_device_id qmc5883p_of_match[] =3D { { .compatible =3D "qstcorp,qmc5883p" }, { } @@ -603,6 +661,7 @@ static struct i2c_driver qmc5883p_driver =3D { .driver =3D { .name =3D "qmc5883p", .of_match_table =3D qmc5883p_of_match, + .pm =3D pm_ptr(&qmc5883p_dev_pm_ops), }, .probe =3D qmc5883p_probe, .id_table =3D qmc5883p_id, --=20 2.53.0