From nobody Sat Jun 13 04:48:22 2026 Received: from mail-pl1-f174.google.com (mail-pl1-f174.google.com [209.85.214.174]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 524113290D8 for ; Sun, 10 May 2026 19:10:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.174 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778440227; cv=none; b=W8sl7o8TjY4aWZDCRjFPe07h0anjm4rEK7SqvHK4qcmXx0pZOliFQGna/gMYhDrCGCg2mnK855dCRLem7TYeH+lbNvJtWZbg8AvsluplN4OQBhGBJdxTAdKJwPX1n+/SLi/IAJYbFfXIpbCZ30CNnfeMjmQ6hpqoHX+V2MGOeBI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778440227; c=relaxed/simple; bh=M6Dx83Ui69Hc+PRyWZEaRE3zW/Ly+1sPAzVLBH6r4vA=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=P7/C0Y1mKTmE/16ibLjkO4zwAjf+Le0Y7ac6agvYIoA3rO1esE/EgRLHn0ojq1e17sdEg11UxqqTCkWqOOII3rJB2lYNYuAitdyvP48xKyjqdA6P5qcdv3Dirh7r/uFm50ytb8iykFm/6XbBKJDuO6p2rVrW3ayeLGaTDafaBvA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hpL5b9r5; arc=none smtp.client-ip=209.85.214.174 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hpL5b9r5" Received: by mail-pl1-f174.google.com with SMTP id d9443c01a7336-2b7d3ecc10dso35358645ad.2 for ; Sun, 10 May 2026 12:10:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778440225; x=1779045025; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SYxE166qH0s2fjU8ewtwMDqscaCiwZ73Fz5HIN1pOHQ=; b=hpL5b9r5ByAv4fyPu+p/AlKCxo8Jb/vNZcgEfHX2XrKgRhUTuDvzAM7QGMMjaHiKPb GSKoymBirTVCkXUhybBPRr9FKiBgE3rVUbVNNGV9QdyNqplPVkk4BgdprwjikjTJchb2 ws61aCktcMq8r9GqN3rAJzp6wJINZMfHN1VyaU7uYc6yWV2T6iYi8gE4L8eW9WsSRn1A Fq1WsTiyJWYgUyLmnRoDFuSWcDzbbHldP1bZ/KHUofwsgfPwBcF277cS8EuBL+R7l7RX jN4VHp2iWUi3pYsMksOnnl9IQ6c+cDtTdy1F6ELk2ip6V8ASJequZ03j5T76Oe5gESt3 fDlw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778440225; x=1779045025; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=SYxE166qH0s2fjU8ewtwMDqscaCiwZ73Fz5HIN1pOHQ=; b=SNJ6ESEntsW4nUshyGcD/6XOq0JJZ0Zsged4O5fqWGYcg9JwU6WUKjM43+QGP4f8VR IMimxvf+xqeNRs/+uJRid5GxJmgTUWzunc/tXuxCvW5RolPAfZmyNyjRjRpU7e1NJIEX HsbXBO4H+yyX5zPpJAOejIR8YajXNElQaSljjy8D7IT0pH1FL99b38HGazxiuGduHTz/ adxYKmP/95rMg3wrIFBrEkUhU9h0G2gRaiPeBzZERZJk0o5tofKCKgsVoWy2RTMtsMgI 4EhzLviqKG2TENkyJuzfCQQBZk+xTKgC1gk8UjiERvQhsvSuFzl7JWXbS5o0xzCgZJAI dPUA== X-Forwarded-Encrypted: i=1; AFNElJ8s0VefV1ZJBn1kIt3hmGscglYTgQvAR+MKRNdbfNDgjVbtW30YhnDUvP0ysUGtRQNCuQHqJ/DjR6mwafM=@vger.kernel.org X-Gm-Message-State: AOJu0YyGwYJqFboEzvFiZ+VwXmpbz0u5DxFJD4jygee+2U3+rbw3aHAC n2JvfPZErru48Uj1CMRoPkvI7Tf6N5EWy9pQ/pNTWOEObOYFenoR1U4= X-Gm-Gg: Acq92OGau8lHtA2Q9J+A895lKIR31OVXSpAer1Qodvw8+9WzJ1EG17RwRUE3jnkj6XR SxSgfIgbvKhUPDHOr02N5G2cUg4tR1GtLJn2OwcFfbePpOGEqCLuO6en3MPwdiaAcxMjpVg3sfl luef01fnyP2Dh0WPNuxxHTHCWyjqG/TkWyI63x63JwKJO9YGiE90NGOemGySLFIBL5BWqLzwEIV 9spvkXmTc1Qn5z53Wy5iZLntNF+ph+jFvDZ+8Evj9bpCPudpfiHRrYcKUFzuWc20sB7tdZQnAHh eQpgZj/CvK0btibCOvdhYx3VRp37JW1+pn51FJCHxA5GUUnPtaub1OYZ+fdqmbxPq1SOzKwxGK3 xZW5cy1fp97XYZdlAcgNQ+NKi7+dMHOCn/ibOHfcw2t9euF2aDwpypHG+0Z65Bgtiu7hyDqf6Q6 EEY0DP+FcRu9H+afUUsTy9XikutiEDmTxL04cg0H3MkwT1g/30QiWz5f1SogOzOpMOD2d8Jgp+3 V8= X-Received: by 2002:a17:903:3bac:b0:2bc:7662:90b6 with SMTP id d9443c01a7336-2bc7aa11bbcmr69633725ad.25.1778440225491; Sun, 10 May 2026 12:10:25 -0700 (PDT) Received: from localhost.localdomain ([103.76.103.36]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1c5466bsm81817305ad.0.2026.05.10.12.10.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:10:24 -0700 (PDT) From: Nikhil Gautam To: jic23@kernel.org Cc: dlechner@baylibre.com, nuno.sa@analog.com, andy@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Nikhil Gautam Subject: [RFC PATCH 1/2] dt-bindings: iio: magnetometer: add Melexis MLX90393 Date: Mon, 11 May 2026 00:40:09 +0530 Message-Id: <20260510191010.155380-2-nikhilgtr@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260510191010.155380-1-nikhilgtr@gmail.com> References: <20260510191010.155380-1-nikhilgtr@gmail.com> 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 devicetree bindings for the Melexis MLX90393 3-axis magnetometer and temperature sensor. The device supports magnetic field and temperature measurements over I2C and SPI interfaces. This initial binding documents the I2C interface. Signed-off-by: Nikhil Gautam --- .../iio/magnetometer/melexis,mlx90393.yaml | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/mele= xis,mlx90393.yaml diff --git a/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx= 90393.yaml b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx= 90393.yaml new file mode 100644 index 000000000000..b99629ff2585 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/magnetometer/melexis,mlx90393.y= aml @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/magnetometer/melexis,mlx90393.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Melexis MLX90393 magnetometer sensor + +maintainers: + - Nikhil Gautam + +description: + Melexis MLX90393 3-axis magnetometer and temperature sensor. + +properties: + compatible: + enum: + - melexis,mlx90393 + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells =3D <1>; + #size-cells =3D <0>; + + magnetometer@c { + compatible =3D "melexis,mlx90393"; + reg =3D <0x0c>; + }; + }; --=20 2.39.5 From nobody Sat Jun 13 04:48:22 2026 Received: from mail-pl1-f171.google.com (mail-pl1-f171.google.com [209.85.214.171]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DF82832860F for ; Sun, 10 May 2026 19:10:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778440232; cv=none; b=HP0Id9EsbEBQA6Cday65Q4Zk953AZwmMjGhjr9wDMrjAa8G94JmRTJiRU4MnANY/GuwPaI/ebcGTrPflwCPUgdBwwEoL6OE012eo7Qn870OKVy7CcS2TRiXolqqgh8ISMalbM9ksjZURTswx5v70NeVewxOcSpcNaMR15BSE0bw= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1778440232; c=relaxed/simple; bh=vzSsKGshhUbpMOZOIwtoF22JmWWhGHoJPNpEsPY0288=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version:Content-Type; b=LHcBMtIsrAqUJyD3RjJ/+LKphTxsdfLrso0Eyr1Owbaxh3yLzsmwe7PCscdB6p6MP6CJSXwW/ZrpN+BiuRVxHX3TpD9CXEMQfapSCJucH/qQvzr9Bo0ZgNWkqtYyMN1CzJwmoC0/0F5vhv7BEASnsmZLoY6wjjFBOjfi+lb4Z4U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=BON2zEC+; arc=none smtp.client-ip=209.85.214.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BON2zEC+" Received: by mail-pl1-f171.google.com with SMTP id d9443c01a7336-2ab077e3f32so16742655ad.3 for ; Sun, 10 May 2026 12:10:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1778440230; x=1779045030; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=IabnayRaO+hNjLcbI/uyI/ECM9I5btvQAktpmQXZKn8=; b=BON2zEC+VFWkX9oTS/jve6ZXyVA6bSl/S3d69L1/U++w6oxlwEhIXepE2Bwp2WEKms vyb1wUdX2ccHDsqqoKkSy3KBLOS+bluGSm8OVP6vl9oBpusopw7vXmIB99NrxCZDSQOt qLNaTZwHTFR/VAT7aVIyMwMdI80wYD2YkDDH0ytyAGShUPdWQIarvENPHssyzarMK1fV 6GxGp1u4thj8ccKhdmi+wcJmD9taS7rM3c1f7bwponMf4/fQLdA2c+MQG0R3xT2q0rWP 31a6pvPp/Xm82+gkRx+x9SsfyNoAMxjcUgpeLkeamKnBgqIZ0T8ENX9cx4BQzeuo2njO IJYQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1778440230; x=1779045030; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=IabnayRaO+hNjLcbI/uyI/ECM9I5btvQAktpmQXZKn8=; b=qkdgmpRb2Fgazfdsv/lXp3ebfgLvvwQx2/fO4tT5UXblw939Ut4q+liW2EjUJpCcw+ dCji0vb/8XspgAjk+TsCeWb1280FdWFv0K/Pvrnw9K18CTtJeIfksDhmjA5l4/4YXeX3 t+AqE9S2eRaxa8IBE0r+SEcM8+NqfjiszlXqN2ruGjeOzai4G67uegdVtjMajKyfFzV2 BJLb/g11tUym9muCvtgElkXnt+l45e7giL/rgUD3XSyxAyA3mBz3xk1z5++4wLE3ztyn mIDaDhSLGK8zMKo56GXThuAddL6WB2K0G+677gOPATUOYAMFmzQojLVo1CnDc3kMn/Ly XJgA== X-Forwarded-Encrypted: i=1; AFNElJ8y2qhiTH5heiDwIJMqxaAhc+DDSQ5xR1RgHjrmEPmgSpZHQ98EDuNK695GcKYERZvJz8HDKgV+6m2aSYE=@vger.kernel.org X-Gm-Message-State: AOJu0YzVi3pre5DBatIqH5zyrQGKBKQXOAMMbSjbvspF6V+Zm7uHws6V b/P9Ay0Hjgo71wXxs79ZV4jD5/ek21zoWXOMajYZzYaXYteOI2cHmFI= X-Gm-Gg: Acq92OHEdYN2EHtkDsJIyEx/IHGHgv/M5sF1LFQ4HHRhn9o4Exx6uF1rt/zUKpnvHfP B9eO0qza3dDbKI7G5mAJ3DcVNj7UGH8mn74e5r42uiH008F88mvrfPj/gPg5895zYl+oDZe5QjH CmrD30rAKeexXECgVvGwURFOnxuo1vX1Nk6gssY0BLYBm5uIa8U/xUsAc+/Xjcj6PKjxZpcwa0k TkqjOd0HmAHQg1K9NsCQI8iF8eYVr0K4f893E1ZLCuB1owXhdrO7tFRJvjE/+jdEoxsCy7n/9zW 1Bn0i33NsjOeKnjMfZoW4pZPvEchb+5Xv+NmmISOn2ya3xNnpqLQDWNel0bVs6c3w8pI4AlGLsb USY0wQL6k/cT6ADR8/IeWPZRuR0JZz2wD7OuViGC4xuqqnctT6wA0UXcVCtMj1bV0dnV3cIbQkf d6o5L6SUDFr4xHGxa1c0LcnAtLKg2GgvEQLs5LNWSNne3Qos6QKnEb/B03BTyrJR/R X-Received: by 2002:a17:903:2cb:b0:2ba:9587:25c3 with SMTP id d9443c01a7336-2ba9587276emr204732545ad.1.1778440230017; Sun, 10 May 2026 12:10:30 -0700 (PDT) Received: from localhost.localdomain ([103.76.103.36]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2baf1c5466bsm81817305ad.0.2026.05.10.12.10.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 10 May 2026 12:10:29 -0700 (PDT) From: Nikhil Gautam To: jic23@kernel.org Cc: dlechner@baylibre.com, nuno.sa@analog.com, andy@kernel.org, robh@kernel.org, krzk+dt@kernel.org, conor+dt@kernel.org, linux-iio@vger.kernel.org, devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, Nikhil Gautam Subject: [RFC PATCH 2/2] iio: magnetometer: add support for Melexis MLX90393 Date: Mon, 11 May 2026 00:40:10 +0530 Message-Id: <20260510191010.155380-3-nikhilgtr@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260510191010.155380-1-nikhilgtr@gmail.com> References: <20260510191010.155380-1-nikhilgtr@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add Industrial I/O subsystem support for the Melexis MLX90393 3-axis magnetometer and temperature sensor. The driver currently supports: raw magnetic field measurements raw temperature measurements configurable gain/scale selection configurable oversampling ratio direct mode operation The MLX90393 supports both I2C and SPI interfaces. This initial implementation adds support for the I2C interface. The driver is structured around a shared sensor core with a small transport abstraction layer to simplify future SPI support without duplicating sensor logic. Signed-off-by: Nikhil Gautam --- drivers/iio/magnetometer/Kconfig | 10 + drivers/iio/magnetometer/Makefile | 2 + drivers/iio/magnetometer/mlx90393.h | 76 +++ drivers/iio/magnetometer/mlx90393_core.c | 724 +++++++++++++++++++++++ drivers/iio/magnetometer/mlx90393_i2c.c | 71 +++ 5 files changed, 883 insertions(+) create mode 100644 drivers/iio/magnetometer/mlx90393.h create mode 100644 drivers/iio/magnetometer/mlx90393_core.c create mode 100644 drivers/iio/magnetometer/mlx90393_i2c.c diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kc= onfig index 3debf1320ad1..e6b74e7e3317 100644 --- a/drivers/iio/magnetometer/Kconfig +++ b/drivers/iio/magnetometer/Kconfig @@ -128,6 +128,16 @@ config HID_SENSOR_MAGNETOMETER_3D Say yes here to build support for the HID SENSOR Magnetometer 3D. =20 +config MLX90393 + tristate "MELEXIS MLX90393 3-axis magnetometer sensor" + depends on I2C + help + Say yes here to build support for the MELEXIS MLX90393 3-axis + magnetometer. + + To compile this driver as a module, choose M here: the module + will be called mlx90393. + config MMC35240 tristate "MEMSIC MMC35240 3-axis magnetic sensor" select REGMAP_I2C diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/M= akefile index 9297723a97d8..542c89d38a59 100644 --- a/drivers/iio/magnetometer/Makefile +++ b/drivers/iio/magnetometer/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_BMC150_MAGN_SPI) +=3D bmc150_magn_spi.o =20 obj-$(CONFIG_MAG3110) +=3D mag3110.o obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) +=3D hid-sensor-magn-3d.o +obj-$(CONFIG_MLX90393) +=3D mlx90393_core.o +obj-$(CONFIG_MLX90393) +=3D mlx90393_i2c.o obj-$(CONFIG_MMC35240) +=3D mmc35240.o =20 obj-$(CONFIG_IIO_ST_MAGN_3AXIS) +=3D st_magn.o diff --git a/drivers/iio/magnetometer/mlx90393.h b/drivers/iio/magnetometer= /mlx90393.h new file mode 100644 index 000000000000..f3e04aed67de --- /dev/null +++ b/drivers/iio/magnetometer/mlx90393.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * MLX90393 magnetometer & temperature sensor driver + * + * Copyright (c) 2026 Nikhil Gautam + */ + +#ifndef MLX90393_H +#define MLX90393_H + +#include +#include +#include +#include + +#define MLX90393_AXIS_MAX 2 +#define MLX90393_GAIN_MAX 8 +#define MLX90393_RES_MAX 4 +#define MLX90393_OSR2_MAX 4 +#define MLX90393_OSR_MAX 4 + +#define MLX90393_CMD_MASK GENMASK(7, 4) + +/* Commands */ +#define MLX90393_CMD_SB 0x10 +#define MLX90393_CMD_SW 0x20 +#define MLX90393_CMD_SM 0x30 +#define MLX90393_CMD_RM 0x40 +#define MLX90393_CMD_RR 0x50 +#define MLX90393_CMD_WR 0x60 +#define MLX90393_CMD_EX 0x80 +#define MLX90393_CMD_HR 0xD0 +#define MLX90393_CMD_HS 0xE0 +#define MLX90393_CMD_RT 0xF0 + +#define MLX90393_MEASURE_Z BIT(0) +#define MLX90393_MEASURE_Y BIT(1) +#define MLX90393_MEASURE_X BIT(2) +#define MLX90393_MEASURE_TEMP BIT(3) + +#define MLX90393_MEASURE_ALL \ + (MLX90393_MEASURE_TEMP | MLX90393_MEASURE_X | \ + MLX90393_MEASURE_Y | MLX90393_MEASURE_Z) + +#define MLX90393_NUM_CHANNELS 4 + +#define MLX90393_STATUS_RESP GENMASK(1, 0) +#define MLX90393_STATUS_RT BIT(2) +#define MLX90393_STATUS_ERROR BIT(4) + +#define MLX90393_REG_CONF1 0x00 +#define MLX90393_REG_CONF2 0x01 +#define MLX90393_REG_CONF3 0x02 +#define MLX90393_REG_CONF4 0x03 + +#define MLX90393_CONF1_GAIN_SEL GENMASK(6, 4) +#define MLX90393_CONF1_HALLCONF GENMASK(3, 0) + +#define MLX90393_CONF3_OSR GENMASK(1, 0) +#define MLX90393_CONF3_DIG_FILT GENMASK(4, 2) +#define MLX90393_CONF3_RES_X GENMASK(6, 5) +#define MLX90393_CONF3_RES_Y GENMASK(8, 7) +#define MLX90393_CONF3_RES_Z GENMASK(10, 9) +#define MLX90393_CONF3_OSR2 GENMASK(12, 11) + +struct mlx90393_transfer_ops { + int (*xfer)(void *context, + const u8 *tx, int tx_len, + u8 *rx, int rx_len); +}; + +int mlx90393_core_probe(struct device *dev, + const struct mlx90393_transfer_ops *ops, + void *context); + +#endif diff --git a/drivers/iio/magnetometer/mlx90393_core.c b/drivers/iio/magneto= meter/mlx90393_core.c new file mode 100644 index 000000000000..c79f2b8c20d8 --- /dev/null +++ b/drivers/iio/magnetometer/mlx90393_core.c @@ -0,0 +1,724 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * MLX90393 magnetometer & temperature sensor driver + * + * Copyright (c) 2026 Nikhil Gautam + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "mlx90393.h" + +struct mlx90393_data { + struct device *dev; + struct mutex lock; + void *bus_context; + const struct mlx90393_transfer_ops *ops; + u8 gain_sel; + u8 hallconf; + + u8 res_xy; + u8 res_z; + + u8 dig_filt; + u8 osr; + u8 osr2; +}; + +enum mlx90393_channels { + MLX90393_CHAN_X, + MLX90393_CHAN_Y, + MLX90393_CHAN_Z, + MLX90393_CHAN_TEMP, +}; + +enum mlx90393_axis_type { + MLX90393_AXIS_TYPE_XY, + MLX90393_AXIS_TYPE_Z, +}; + +/* Datasheet: Table no.17 */ +static const int mlx90393_scale_table[MLX90393_AXIS_MAX] + [MLX90393_GAIN_MAX] + [MLX90393_RES_MAX] =3D { + /* XY axis */ + { + {751, 1502, 3004, 6009}, + {601, 1202, 2403, 4840}, + {451, 901, 1803, 3605}, + {376, 751, 1502, 3004}, + {300, 601, 1202, 2403}, + {250, 501, 1001, 2003}, + {200, 401, 801, 1602}, + {150, 300, 601, 1202}, + }, + /* Z axis */ + { + {1210, 2420, 4840, 9680}, + {968, 1936, 3872, 7744}, + {726, 1452, 2904, 5808}, + {605, 1210, 2420, 4840}, + {484, 968, 1936, 3872}, + {403, 807, 1613, 3227}, + {323, 645, 1291, 2581}, + {242, 484, 968, 1936}, + } +}; + +static const int mlx90393_osr2_avail[MLX90393_OSR2_MAX] =3D { + 0, 1, 2, 3, +}; + +static const int mlx90393_osr_avail[MLX90393_OSR_MAX] =3D { + 1, 2, 4, 8, +}; + +#define MLX90393_CHAN(idx, axis, addr) { \ + .type =3D IIO_MAGN, \ + .modified =3D 1, \ + .channel =3D idx, \ + .address =3D addr, \ + .channel2 =3D IIO_MOD_##axis, \ + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),\ + .info_mask_separate_available =3D \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available =3D \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ +} + +static const struct iio_chan_spec mlx90393_channels[] =3D { + MLX90393_CHAN(0, X, MLX90393_CHAN_X), + MLX90393_CHAN(1, Y, MLX90393_CHAN_Y), + MLX90393_CHAN(2, Z, MLX90393_CHAN_Z), + { + .type =3D IIO_TEMP, + .address =3D MLX90393_CHAN_TEMP, + .info_mask_separate =3D BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_separate_available =3D + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), + .info_mask_shared_by_type =3D BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +/* + * Calculate total conversion time in microseconds. + * + * Formula derived from datasheet timing equations. + */ + +static int mlx90393_get_tconv_us(struct mlx90393_data *data) +{ + const int osr =3D data->osr; + const int osr2 =3D data->osr2; + const int df =3D data->dig_filt; + + int tconvm; + int tconvt; + + int m =3D 3; /* X,Y,Z */ + + /* + * Datasheet: + * + * TCONVM =3D + * 67 + 64 * 2^OSR * (2 + 2^DIG_FILT) + */ + tconvm =3D 67 + + (64 * BIT(osr) * + (2 + BIT(df))); + + /* + * Datasheet: + * + * TCONVT =3D + * 67 + 192 * 2^OSR2 + */ + tconvt =3D 67 + + (192 * BIT(osr2)); + /* + * Total conversion time: + * TSTBY + TACTIVE + m * TCONVM + + * TCONVT + TCONV_END + */ + return 220 + + 360 + + (m * tconvm) + + tconvt + + 100; +} + +static int mlx90393_xfer(struct mlx90393_data *data, + const u8 *tx, int tx_len, + u8 *rx, int rx_len) +{ + return data->ops->xfer(data->bus_context, + tx, tx_len, + rx, rx_len); +} + +static int mlx90393_check_status(u8 cmd, u8 status) +{ + /* Always validate error bit */ + if (status & MLX90393_STATUS_ERROR) + return -EIO; + + switch (cmd & MLX90393_CMD_MASK) { + case MLX90393_CMD_RM: + /* + * D1:D0 indicates response availability + * 00 means invalid/no measurement + */ + if ((status & MLX90393_STATUS_RESP) =3D=3D 0) + return -EIO; + break; + + case MLX90393_CMD_RT: + /* Reset acknowledge */ + if (!(status & MLX90393_STATUS_RT)) + return -EIO; + break; + + default: + break; + } + + return 0; +} + +static int mlx90393_write_cmd(struct mlx90393_data *data, + u8 cmd) +{ + u8 status; + int ret; + + ret =3D mlx90393_xfer(data, &cmd, 1, + &status, 1); + if (ret) + return ret; + + return mlx90393_check_status(cmd, status); +} + +static int mlx90393_read_cmd(struct mlx90393_data *data, + u8 cmd, + u8 *rx, int rx_len) +{ + int ret; + + ret =3D mlx90393_xfer(data, &cmd, 1, + rx, rx_len); + if (ret) + return ret; + + return mlx90393_check_status(cmd, rx[0]); +} + +static int mlx90393_read_reg(struct mlx90393_data *data, + u8 reg, u16 *val) +{ + u8 tx[2]; + u8 rx[3]; + int ret; + + tx[0] =3D MLX90393_CMD_RR; + /* Register address is encoded in bits [7:2] */ + tx[1] =3D reg << 2; + + ret =3D mlx90393_xfer(data, + tx, sizeof(tx), + rx, sizeof(rx)); + if (ret) + return ret; + + ret =3D mlx90393_check_status(tx[0], rx[0]); + if (ret) + return ret; + + *val =3D get_unaligned_be16(&rx[1]); + + return 0; +} + +static int mlx90393_write_reg(struct mlx90393_data *data, + u8 reg, u16 val) +{ + u8 tx[4]; + u8 status; + int ret; + + tx[0] =3D MLX90393_CMD_WR; + put_unaligned_be16(val, &tx[1]); + /* Register address is encoded in bits [7:2] */ + tx[3] =3D reg << 2; + + ret =3D mlx90393_xfer(data, + tx, sizeof(tx), + &status, 1); + if (ret) + return ret; + + return mlx90393_check_status(tx[0], status); +} + +static int mlx90393_update_bits(struct mlx90393_data *data, + u8 reg_addr, + u16 mask, u16 val) +{ + u16 reg; + int ret; + + ret =3D mlx90393_read_reg(data, reg_addr, ®); + if (ret) + return ret; + + reg &=3D ~mask; + reg |=3D (val << __ffs(mask)) & mask; + + return mlx90393_write_reg(data, reg_addr, reg); +} + +static int mlx90393_read_measurement(struct mlx90393_data *data, + enum mlx90393_channels chan, int *val) +{ + u8 cmd; + u8 rx[9]; + int ret; + int tconv_us =3D mlx90393_get_tconv_us(data); + + /* Start measurement */ + cmd =3D MLX90393_CMD_SM | MLX90393_MEASURE_ALL; + + ret =3D mlx90393_write_cmd(data, cmd); + if (ret) + return ret; + + /* Wait conversion */ + usleep_range(tconv_us, tconv_us + 1000); + + /* Read measurement */ + cmd =3D MLX90393_CMD_RM | MLX90393_MEASURE_ALL; + + ret =3D mlx90393_read_cmd(data, cmd, rx, sizeof(rx)); + if (ret) + return ret; + /* + * Measurement response layout: + * [status][temp][x][y][z] + */ + + switch (chan) { + case MLX90393_CHAN_TEMP: + *val =3D get_unaligned_be16(&rx[1]); + break; + + case MLX90393_CHAN_X: + *val =3D sign_extend32(get_unaligned_be16(&rx[3]), 15); + break; + + case MLX90393_CHAN_Y: + *val =3D sign_extend32(get_unaligned_be16(&rx[5]), 15); + break; + + case MLX90393_CHAN_Z: + *val =3D sign_extend32(get_unaligned_be16(&rx[7]), 15); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int mlx90393_get_scale(struct mlx90393_data *data, + const struct iio_chan_spec *chan, + int *val, int *val2) +{ + enum mlx90393_axis_type axis; + u8 res; + + if (chan->channel2 =3D=3D IIO_MOD_Z) { + axis =3D MLX90393_AXIS_TYPE_Z; + res =3D data->res_z; + } else { + axis =3D MLX90393_AXIS_TYPE_XY; + res =3D data->res_xy; + } + + /* + * Convert: + * =C2=B5T =C3=97 1000 =E2=86=92 nT + */ + *val =3D 0; + *val2 =3D mlx90393_scale_table[axis][data->gain_sel][res]; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int mlx90393_find_scale(struct mlx90393_data *data, + bool z_axis, + int val, int val2, + int *gain) +{ + int i; + u8 res; + enum mlx90393_axis_type axis; + + axis =3D z_axis ? MLX90393_AXIS_TYPE_Z : + MLX90393_AXIS_TYPE_XY; + + res =3D z_axis ? data->res_z : data->res_xy; + + if (val !=3D 0) + return -EINVAL; + + for (i =3D 0; i < ARRAY_SIZE(mlx90393_scale_table[0]); i++) { + if (mlx90393_scale_table[axis][i][res] + =3D=3D val2) { + *gain =3D i; + return 0; + } + } + + return -EINVAL; +} + +static int mlx90393_set_scale(struct mlx90393_data *data, + const struct iio_chan_spec *chan, + int val, int val2) +{ + bool z_axis; + int gain; + int ret; + + z_axis =3D chan->channel2 =3D=3D IIO_MOD_Z; + + ret =3D mlx90393_find_scale(data, z_axis, + val, val2, + &gain); + if (ret) + return ret; + + ret =3D mlx90393_update_bits(data, + MLX90393_REG_CONF1, + MLX90393_CONF1_GAIN_SEL, + gain); + if (ret) + return ret; + + data->gain_sel =3D gain; + + return 0; +} + +static int mlx90393_get_osr(struct mlx90393_data *data, int *val) +{ + *val =3D mlx90393_osr_avail[data->osr]; + + return IIO_VAL_INT; +} + +static int mlx90393_find_osr(int val, int *osr) +{ + int i; + + for (i =3D 0; i < MLX90393_OSR_MAX; i++) { + if (mlx90393_osr_avail[i] =3D=3D val) { + *osr =3D i; + return 0; + } + } + + return -EINVAL; +} + +static int mlx90393_get_temp_osr2(struct mlx90393_data *data, int *val) +{ + *val =3D mlx90393_osr2_avail[data->osr2]; + return IIO_VAL_INT; +} + +static int mlx90393_set_osr(struct mlx90393_data *data, int val) +{ + int osr; + int ret; + + ret =3D mlx90393_find_osr(val, &osr); + if (ret) + return ret; + + if (osr =3D=3D data->osr) + return 0; + + ret =3D mlx90393_update_bits(data, MLX90393_REG_CONF3, + MLX90393_CONF3_OSR, + osr); + if (ret) + return ret; + + data->osr =3D osr; + return 0; +} + +static int mlx90393_set_temp_osr2(struct mlx90393_data *data, int val) +{ + int ret; + + if (val < 0 || val >=3D MLX90393_OSR2_MAX) + return -EINVAL; + + if (val =3D=3D data->osr2) + return 0; + + ret =3D mlx90393_update_bits(data, MLX90393_REG_CONF3, + MLX90393_CONF3_OSR2, + val); + if (ret) + return ret; + + data->osr2 =3D val; + + return 0; +} + +static int mlx90393_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_OVERSAMPLING_RATIO: + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int mlx90393_write_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int val, int val2, + long mask) +{ + struct mlx90393_data *data =3D iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + mutex_lock(&data->lock); + ret =3D mlx90393_set_scale(data, chan, val, val2); + mutex_unlock(&data->lock); + return ret; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + mutex_lock(&data->lock); + if (chan->type =3D=3D IIO_TEMP) + ret =3D mlx90393_set_temp_osr2(data, val); + else if (chan->type =3D=3D IIO_MAGN) + ret =3D mlx90393_set_osr(data, val); + else + ret =3D -EINVAL; + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static int mlx90393_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long mask) +{ + struct mlx90393_data *data =3D iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&data->lock); + ret =3D mlx90393_read_measurement(data, chan->address, val); + mutex_unlock(&data->lock); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_MAGN: + return mlx90393_get_scale(data, chan, val, val2); + + case IIO_TEMP: + /* Datasheet: 22124 millidegC/LSB */ + *val =3D 0; + *val2 =3D 22124; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } + + case IIO_CHAN_INFO_OFFSET: + if (chan->type =3D=3D IIO_TEMP) { + /* Datasheet: temperature offset */ + *val =3D -45114; + return IIO_VAL_INT; + } + return -EINVAL; + + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (chan->type =3D=3D IIO_TEMP) + return mlx90393_get_temp_osr2(data, val); + if (chan->type =3D=3D IIO_MAGN) + return mlx90393_get_osr(data, val); + return -EINVAL; + default: + return -EINVAL; + } +} + +static int mlx90393_read_avail(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + const int **vals, + int *type, + int *length, + long mask) +{ + struct mlx90393_data *data =3D iio_priv(indio_dev); + + static int scale_avail[MLX90393_GAIN_MAX][MLX90393_AXIS_MAX]; + enum mlx90393_axis_type axis; + int i; + u8 res; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + axis =3D chan->channel2 =3D=3D IIO_MOD_Z; + res =3D axis ? data->res_z : data->res_xy; + + for (i =3D 0; i < MLX90393_GAIN_MAX; i++) { + scale_avail[i][0] =3D 0; + scale_avail[i][1] =3D + mlx90393_scale_table[axis][i][res]; + } + + *vals =3D &scale_avail[0][0]; + *type =3D IIO_VAL_INT_PLUS_NANO; + *length =3D MLX90393_GAIN_MAX * MLX90393_AXIS_MAX; + break; + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + if (chan->type =3D=3D IIO_TEMP) { + *vals =3D mlx90393_osr2_avail; + *type =3D IIO_VAL_INT; + *length =3D MLX90393_OSR2_MAX; + } else { + *vals =3D mlx90393_osr_avail; + *type =3D IIO_VAL_INT; + *length =3D MLX90393_OSR_MAX; + } + + break; + default: + return -EINVAL; + } + return IIO_AVAIL_LIST; +} + +static const struct iio_info mlx90393_info =3D { + .read_raw =3D mlx90393_read_raw, + .write_raw =3D mlx90393_write_raw, + .read_avail =3D mlx90393_read_avail, + .write_raw_get_fmt =3D mlx90393_write_raw_get_fmt, +}; + +static int mlx90393_init(struct mlx90393_data *data) +{ + int ret; + u8 cmd; + u16 reg; + + /* Exit mode */ + cmd =3D MLX90393_CMD_EX; + ret =3D mlx90393_write_cmd(data, cmd); + if (ret) + return ret; + + usleep_range(1000, 1500); + + /* Reset device */ + cmd =3D MLX90393_CMD_RT; + ret =3D mlx90393_write_cmd(data, cmd); + if (ret) + return ret; + + /* Wait for device to reset */ + usleep_range(5000, 6000); + + ret =3D mlx90393_read_reg(data, MLX90393_REG_CONF1, ®); + if (ret) + return ret; + + data->gain_sel =3D FIELD_GET(MLX90393_CONF1_GAIN_SEL, reg); + data->hallconf =3D FIELD_GET(MLX90393_CONF1_HALLCONF, reg); + + ret =3D mlx90393_read_reg(data, MLX90393_REG_CONF3, ®); + if (ret) + return ret; + + data->res_xy =3D FIELD_GET(MLX90393_CONF3_RES_X, reg); + data->res_z =3D FIELD_GET(MLX90393_CONF3_RES_Z, reg); + data->dig_filt =3D FIELD_GET(MLX90393_CONF3_DIG_FILT, reg); + data->osr =3D FIELD_GET(MLX90393_CONF3_OSR, reg); + data->osr2 =3D FIELD_GET(MLX90393_CONF3_OSR2, reg); + + return 0; +} + +int mlx90393_core_probe(struct device *dev, + const struct mlx90393_transfer_ops *ops, + void *context) +{ + struct iio_dev *indio_dev; + struct mlx90393_data *data; + int ret; + + indio_dev =3D devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data =3D iio_priv(indio_dev); + mutex_init(&data->lock); + + data->dev =3D dev; + data->ops =3D ops; + data->bus_context =3D context; + + indio_dev->name =3D "mlx90393"; + indio_dev->info =3D &mlx90393_info; + indio_dev->modes =3D INDIO_DIRECT_MODE; + indio_dev->channels =3D mlx90393_channels; + indio_dev->num_channels =3D ARRAY_SIZE(mlx90393_channels); + + ret =3D mlx90393_init(data); + if (ret) { + dev_err_probe(dev, ret, "failed to initialize device\n"); + return ret; + } + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(mlx90393_core_probe); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nikhil Gautam "); +MODULE_DESCRIPTION("MLX90393 magnetometer sensor driver"); diff --git a/drivers/iio/magnetometer/mlx90393_i2c.c b/drivers/iio/magnetom= eter/mlx90393_i2c.c new file mode 100644 index 000000000000..09d533a96907 --- /dev/null +++ b/drivers/iio/magnetometer/mlx90393_i2c.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include + +#include "mlx90393.h" + +/* + * MLX90393 commands use repeated-start transfers where + * every command is followed by a status/data response. + */ +static int mlx90393_i2c_xfer(void *context, + const u8 *tx, int tx_len, + u8 *rx, int rx_len) +{ + struct i2c_client *client =3D context; + int ret; + struct i2c_msg msgs[2]; + + msgs[0].addr =3D client->addr; + msgs[0].flags =3D 0; + msgs[0].len =3D tx_len; + msgs[0].buf =3D (u8 *)tx; + + msgs[1].addr =3D client->addr; + msgs[1].flags =3D I2C_M_RD; + msgs[1].len =3D rx_len; + msgs[1].buf =3D rx; + + ret =3D i2c_transfer(client->adapter, msgs, 2); + if (ret !=3D 2) + return ret < 0 ? ret : -EIO; + + return 0; +} + +static const struct mlx90393_transfer_ops mlx90393_i2c_ops =3D { + .xfer =3D mlx90393_i2c_xfer, +}; + +static int mlx90393_i2c_probe(struct i2c_client *client) +{ + return mlx90393_core_probe(&client->dev, + &mlx90393_i2c_ops, + client); +} + +static const struct i2c_device_id mlx90393_id[] =3D { + { "mlx90393", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mlx90393_id); + +static const struct of_device_id mlx90393_of_match[] =3D { + { .compatible =3D "melexis,mlx90393" }, + { } +}; +MODULE_DEVICE_TABLE(of, mlx90393_of_match); + +static struct i2c_driver mlx90393_i2c_driver =3D { + .driver =3D { + .name =3D "mlx90393", + .of_match_table =3D mlx90393_of_match, + }, + .probe =3D mlx90393_i2c_probe, +}; + +module_i2c_driver(mlx90393_i2c_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nikhil Gautam "); +MODULE_DESCRIPTION("MLX90393 magnetometer sensor driver"); --=20 2.39.5