* [PATCH v4 09/19] arm64: dts: qcom: qcs8300: Add minidump SRAM config to SCM node
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
Point the SCM node at the minidump config slot in the always-on SRAM.
Boot firmware reads this word before DDR is initialised on a warm reset
to decide where to deliver the minidump.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/monaco.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/monaco.dtsi b/arch/arm64/boot/dts/qcom/monaco.dtsi
index e4c8466f941b..a05b8d28bdb8 100644
--- a/arch/arm64/boot/dts/qcom/monaco.dtsi
+++ b/arch/arm64/boot/dts/qcom/monaco.dtsi
@@ -634,6 +634,7 @@ firmware {
scm: scm {
compatible = "qcom,scm-qcs8300", "qcom,scm";
qcom,dload-mode = <&tcsr 0x13000>;
+ sram = <&minidump_config>;
};
};
@@ -7154,6 +7155,10 @@ sram: sram@146d8000 {
#address-cells = <1>;
#size-cells = <1>;
+ minidump_config: minidump-sram@1c {
+ reg = <0x1c 0x4>;
+ };
+
pil-reloc@94c {
compatible = "qcom,pil-reloc-info";
reg = <0x94c 0xc8>;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 08/19] arm64: dts: qcom: sa8775p: Add minidump SRAM config to SCM node
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
Point the SCM node at the minidump config slot in the always-on SRAM.
Boot firmware reads this word before DDR is initialised on a warm reset
to decide where to deliver the minidump.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/lemans.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/lemans.dtsi b/arch/arm64/boot/dts/qcom/lemans.dtsi
index 353a6e6fd3ac..a1708bd15076 100644
--- a/arch/arm64/boot/dts/qcom/lemans.dtsi
+++ b/arch/arm64/boot/dts/qcom/lemans.dtsi
@@ -524,6 +524,7 @@ firmware {
scm {
compatible = "qcom,scm-sa8775p", "qcom,scm";
qcom,dload-mode = <&tcsr 0x13000>;
+ sram = <&minidump_config>;
};
};
@@ -6825,6 +6826,10 @@ sram: sram@146d8000 {
#address-cells = <1>;
#size-cells = <1>;
+ minidump_config: minidump-sram@1c {
+ reg = <0x1c 0x4>;
+ };
+
pil-reloc@94c {
compatible = "qcom,pil-reloc-info";
reg = <0x94c 0xc8>;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 07/19] arm64: dts: qcom: sm8450: Add minidump SRAM config to SCM node
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
Point the SCM node at the minidump config slot in the always-on SRAM.
Boot firmware reads this word before DDR is initialised on a warm reset
to decide where to deliver the minidump.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/sm8450.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sm8450.dtsi b/arch/arm64/boot/dts/qcom/sm8450.dtsi
index 56cb6e959e4e..f819b4f7fdbc 100644
--- a/arch/arm64/boot/dts/qcom/sm8450.dtsi
+++ b/arch/arm64/boot/dts/qcom/sm8450.dtsi
@@ -479,6 +479,7 @@ scm: scm {
compatible = "qcom,scm-sm8450", "qcom,scm";
qcom,dload-mode = <&tcsr 0x13000>;
interconnects = <&aggre2_noc MASTER_CRYPTO 0 &mc_virt SLAVE_EBI1 0>;
+ sram = <&minidump_config>;
#reset-cells = <1>;
};
};
@@ -4978,6 +4979,10 @@ sram@146aa000 {
#address-cells = <1>;
#size-cells = <1>;
+ minidump_config: minidump-sram@1c {
+ reg = <0x1c 0x4>;
+ };
+
pil-reloc@94c {
compatible = "qcom,pil-reloc-info";
reg = <0x94c 0xc8>;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 06/19] arm64: dts: qcom: kaanapali: Add minidump SRAM config to SCM node
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
Point the SCM node at the minidump config slot in the always-on SRAM.
Boot firmware reads this word before DDR is initialised on a warm reset
to decide where to deliver the minidump.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/kaanapali.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/kaanapali.dtsi b/arch/arm64/boot/dts/qcom/kaanapali.dtsi
index 7aa9653bd456..0342fd28f9b9 100644
--- a/arch/arm64/boot/dts/qcom/kaanapali.dtsi
+++ b/arch/arm64/boot/dts/qcom/kaanapali.dtsi
@@ -224,6 +224,7 @@ scm: scm {
qcom,dload-mode = <&tcsr 0x19000>;
interconnects = <&aggre_noc MASTER_CRYPTO QCOM_ICC_TAG_ALWAYS
&mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
+ sram = <&minidump_config>;
};
scmi: scmi {
@@ -5452,6 +5453,10 @@ sram@14680000 {
#address-cells = <1>;
#size-cells = <1>;
+ minidump_config: minidump-sram@1c {
+ reg = <0x1c 0x4>;
+ };
+
pil-sram@94c {
compatible = "qcom,pil-reloc-info";
reg = <0x94c 0xc8>;
--
2.53.0
^ permalink raw reply related
* [PATCH v4 05/19] firmware: qcom: scm: Add minidump SRAM support
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
On most Qualcomm SoCs where minidump is supported, a word in always-on
SRAM is shared between the operating system (OS) and boot firmware.
Before DDR is initialized on the warm reset following a crash, firmware
reads this word to decide if minidump is enabled and collect a minidump,
and where to deliver it (USB upload to a host, or save to local
storage). The OS is expected to select one of the destinations.
The SRAM region is described by a 'sram' phandle on the SCM DT node.
If the property is absent the feature is silently disabled, keeping
existing SoCs unaffected.
Expose a 'minidump_dest' module parameter (default: usb) so the user
can select the destination. Only the string names "usb" or "storage"
are accepted.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 92 ++++++++++++++++++++++++++++++++
1 file changed, 92 insertions(+)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 83e4810f1c53..ba5cdeed8a04 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -57,6 +57,7 @@ struct qcom_scm {
int scm_vote_count;
u64 dload_mode_addr;
+ void __iomem *minidump_sram;
struct qcom_tzmem_pool *mempool;
unsigned int wq_cnt;
@@ -141,6 +142,20 @@ static const u8 qcom_scm_cpu_warm_bits[QCOM_SCM_BOOT_MAX_CPUS] = {
#define QCOM_DLOAD_MINIDUMP 2
#define QCOM_DLOAD_BOTHDUMP 3
+/* Minidump destination values written to always-on SRAM for boot firmware */
+#define QCOM_MINIDUMP_DEST_USB 0x0
+#define QCOM_MINIDUMP_DEST_STORAGE 0x2
+
+static u32 minidump_dest = QCOM_MINIDUMP_DEST_USB;
+
+static const struct {
+ const char *name;
+ u32 val;
+} minidump_dest_map[] = {
+ { "usb", QCOM_MINIDUMP_DEST_USB },
+ { "storage", QCOM_MINIDUMP_DEST_STORAGE },
+};
+
#define QCOM_SCM_DEFAULT_WAITQ_COUNT 1
static const char * const qcom_scm_convention_names[] = {
@@ -568,6 +583,14 @@ static void qcom_scm_set_download_mode(struct qcom_scm *scm, u32 dload_mode)
if (ret)
dev_err(scm->dev, "failed to set download mode: %d\n", ret);
+
+ /*
+ * Write the destination into the always-on SRAM so boot firmware
+ * can read it before DDR is initialised on the next warm reset.
+ * Only written when minidump is active;
+ */
+ if (scm->minidump_sram && (dload_mode & QCOM_DLOAD_MINIDUMP))
+ writel_relaxed(minidump_dest, scm->minidump_sram);
}
/**
@@ -2040,6 +2063,29 @@ int qcom_scm_gpu_init_regs(u32 gpu_req)
}
EXPORT_SYMBOL_GPL(qcom_scm_gpu_init_regs);
+static int qcom_scm_map_minidump_sram(struct device *dev, void __iomem **out)
+{
+ struct device_node *np = dev->of_node;
+ struct device_node *sram_np;
+ struct resource res;
+ int ret;
+
+ sram_np = of_parse_phandle(np, "sram", 0);
+ if (!sram_np)
+ return 0;
+
+ ret = of_address_to_resource(sram_np, 0, &res);
+ of_node_put(sram_np);
+ if (ret)
+ return ret;
+
+ *out = devm_ioremap(dev, res.start, resource_size(&res));
+ if (!*out)
+ return -ENOMEM;
+
+ return 0;
+}
+
static int qcom_scm_find_dload_address(struct device *dev, u64 *addr)
{
struct device_node *tcsr;
@@ -2737,6 +2783,47 @@ static const struct kernel_param_ops download_mode_param_ops = {
module_param_cb(download_mode, &download_mode_param_ops, NULL, 0644);
MODULE_PARM_DESC(download_mode, "download mode: off/0/N for no dump mode, full/on/1/Y for full dump mode, mini for minidump mode and full,mini for both full and minidump mode together are acceptable values");
+static int get_minidump_dest(char *buffer, const struct kernel_param *kp)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(minidump_dest_map); i++)
+ if (minidump_dest == minidump_dest_map[i].val)
+ return sysfs_emit(buffer, "%s\n", minidump_dest_map[i].name);
+
+ return sysfs_emit(buffer, "unknown\n");
+}
+
+static int set_minidump_dest(const char *val, const struct kernel_param *kp)
+{
+ struct qcom_scm *scm;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(minidump_dest_map); i++)
+ if (sysfs_streq(val, minidump_dest_map[i].name))
+ break;
+
+ if (i >= ARRAY_SIZE(minidump_dest_map))
+ return -EINVAL;
+
+ minidump_dest = minidump_dest_map[i].val;
+
+ /* Pairs with smp_store_release() in qcom_scm_probe(). */
+ scm = smp_load_acquire(&__scm);
+ if (scm && scm->minidump_sram && (download_mode & QCOM_DLOAD_MINIDUMP))
+ writel_relaxed(minidump_dest, scm->minidump_sram);
+
+ return 0;
+}
+
+static const struct kernel_param_ops minidump_dest_param_ops = {
+ .get = get_minidump_dest,
+ .set = set_minidump_dest,
+};
+
+module_param_cb(minidump_dest, &minidump_dest_param_ops, NULL, 0644);
+MODULE_PARM_DESC(minidump_dest, "Minidump SRAM destination: usb (default) or storage");
+
static int qcom_scm_probe(struct platform_device *pdev)
{
struct qcom_tzmem_pool_config pool_config;
@@ -2754,6 +2841,11 @@ static int qcom_scm_probe(struct platform_device *pdev)
return dev_err_probe(&pdev->dev, ret,
"Failed to get download mode address\n");
+ ret = qcom_scm_map_minidump_sram(&pdev->dev, &scm->minidump_sram);
+ if (ret < 0)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to map minidump SRAM\n");
+
mutex_init(&scm->scm_bw_lock);
scm->path = devm_of_icc_get(&pdev->dev, NULL);
--
2.53.0
^ permalink raw reply related
* [PATCH v4 04/19] firmware: qcom: scm: use dev_err_probe() for dload address failure
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha, Konrad Dybcio, Dmitry Baryshkov
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
Replace the bare `return ret` after qcom_scm_find_dload_address() with
dev_err_probe() to produce a consistent, deferred-probe-aware error
message when the download-mode address cannot be resolved.
Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index b78da40a4d60..83e4810f1c53 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -2751,7 +2751,8 @@ static int qcom_scm_probe(struct platform_device *pdev)
scm->dev = &pdev->dev;
ret = qcom_scm_find_dload_address(&pdev->dev, &scm->dload_mode_addr);
if (ret < 0)
- return ret;
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to get download mode address\n");
mutex_init(&scm->scm_bw_lock);
--
2.53.0
^ permalink raw reply related
* [PATCH v4 03/19] firmware: qcom: scm: Fix missing smp_load_acquire()
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
__scm is published in qcom_scm_probe() with smp_store_release()
but qcom_scm_set_download_mode() reads it directly without
smp_load_acquire(), creating a potential ordering violation where a
CPU could observe a stale or partially initialised __scm pointer.
Use smp_load_acquire() to acquire __scm in the
qcom_scm_set_download_mode() function.
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
drivers/firmware/qcom/qcom_scm.c | 25 ++++++++++++++-----------
1 file changed, 14 insertions(+), 11 deletions(-)
diff --git a/drivers/firmware/qcom/qcom_scm.c b/drivers/firmware/qcom/qcom_scm.c
index 6b601a4b89db..b78da40a4d60 100644
--- a/drivers/firmware/qcom/qcom_scm.c
+++ b/drivers/firmware/qcom/qcom_scm.c
@@ -551,23 +551,23 @@ static int qcom_scm_io_rmw(phys_addr_t addr, unsigned int mask, unsigned int val
return qcom_scm_io_writel(addr, new);
}
-static void qcom_scm_set_download_mode(u32 dload_mode)
+static void qcom_scm_set_download_mode(struct qcom_scm *scm, u32 dload_mode)
{
int ret = 0;
- if (__scm->dload_mode_addr) {
- ret = qcom_scm_io_rmw(__scm->dload_mode_addr, QCOM_DLOAD_MASK,
+ if (scm->dload_mode_addr) {
+ ret = qcom_scm_io_rmw(scm->dload_mode_addr, QCOM_DLOAD_MASK,
FIELD_PREP(QCOM_DLOAD_MASK, dload_mode));
- } else if (__qcom_scm_is_call_available(__scm->dev, QCOM_SCM_SVC_BOOT,
+ } else if (__qcom_scm_is_call_available(scm->dev, QCOM_SCM_SVC_BOOT,
QCOM_SCM_BOOT_SET_DLOAD_MODE)) {
- ret = __qcom_scm_set_dload_mode(__scm->dev, !!dload_mode);
+ ret = __qcom_scm_set_dload_mode(scm->dev, !!dload_mode);
} else if (dload_mode) {
- dev_err(__scm->dev,
+ dev_err(scm->dev,
"No available mechanism for setting download mode\n");
}
if (ret)
- dev_err(__scm->dev, "failed to set download mode: %d\n", ret);
+ dev_err(scm->dev, "failed to set download mode: %d\n", ret);
}
/**
@@ -2705,6 +2705,7 @@ static int get_download_mode(char *buffer, const struct kernel_param *kp)
static int set_download_mode(const char *val, const struct kernel_param *kp)
{
+ struct qcom_scm *scm;
bool tmp;
int ret;
@@ -2720,8 +2721,10 @@ static int set_download_mode(const char *val, const struct kernel_param *kp)
}
download_mode = ret;
- if (__scm)
- qcom_scm_set_download_mode(download_mode);
+ /* Pairs with smp_store_release() in qcom_scm_probe(). */
+ scm = smp_load_acquire(&__scm);
+ if (scm)
+ qcom_scm_set_download_mode(scm, download_mode);
return 0;
}
@@ -2842,7 +2845,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
* will cause the boot stages to enter download mode, unless
* disabled below by a clean shutdown/reboot.
*/
- qcom_scm_set_download_mode(download_mode);
+ qcom_scm_set_download_mode(scm, download_mode);
/*
* Disable SDI if indicated by DT that it is enabled by default.
@@ -2875,7 +2878,7 @@ static int qcom_scm_probe(struct platform_device *pdev)
static void qcom_scm_shutdown(struct platform_device *pdev)
{
/* Clean shutdown, disable download mode to allow normal restart */
- qcom_scm_set_download_mode(QCOM_DLOAD_NODUMP);
+ qcom_scm_set_download_mode(__scm, QCOM_DLOAD_NODUMP);
}
static const struct of_device_id qcom_scm_dt_match[] = {
--
2.53.0
^ permalink raw reply related
* [PATCH v4 02/19] dt-bindings: sram: qcom,imem: Add minidump-sram pattern property
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
The qcom,imem binding describes a single word in always-on SRAM shared
between the operating system (OS) and boot firmware. Before DDR is
initialized on the warm reset following a crash, firmware reads this
word to decide where to deliver the minidump, and the OS is expected to
select one of the destinations: either USB upload to a host PC or save
to on-device storage. If nothing is selected by the OS, USB is the
default.
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
.../devicetree/bindings/sram/qcom,imem.yaml | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml
index c63026904061..17adced6d3a4 100644
--- a/Documentation/devicetree/bindings/sram/qcom,imem.yaml
+++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml
@@ -67,6 +67,22 @@ properties:
$ref: /schemas/power/reset/syscon-reboot-mode.yaml#
patternProperties:
+ "^minidump-sram@[0-9a-f]+$":
+ type: object
+ description:
+ A word in always-on SRAM shared between the kernel and boot firmware.
+ Before DDR is initialised on the warm reset following a crash, firmware
+ reads this word to decide where to deliver the minidump (USB or storage).
+
+ properties:
+ reg:
+ maxItems: 1
+
+ required:
+ - reg
+
+ additionalProperties: false
+
"^modem-tables@[0-9a-f]+$":
type: object
description:
--
2.53.0
^ permalink raw reply related
* [PATCH v4 01/19] dt-bindings: firmware: qcom,scm: Add minidump SRAM property
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha
In-Reply-To: <20260624190830.3131112-1-mukesh.ojha@oss.qualcomm.com>
On Qualcomm SoCs that support minidump, a word in always-on SRAM
is shared between the Operating System(HLOS) and boot firmware.
OS is expected to select the minidump download destination
either USB upload to a host PC or save to on-device storage.
Boot firmware will reads this word before DDR is initialized
on the warm reset following a crash to decide where to deliver
the minidump.
Add a 'sram' property to the SCM binding to allow the OS to
identify this SRAM region via a phandle.
Signed-off-by: Mukesh Ojha <mukesh.ojha@oss.qualcomm.com>
---
Documentation/devicetree/bindings/firmware/qcom,scm.yaml | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
index 25f62bacbc91..2d68b4065341 100644
--- a/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
+++ b/Documentation/devicetree/bindings/firmware/qcom,scm.yaml
@@ -129,6 +129,13 @@ properties:
- description: offset of the download mode control register
description: TCSR hardware block
+ sram:
+ description:
+ Phandle to a region in always-on SRAM used to store the download
+ mode value for boot firmware to read before DDR is initialised on
+ the next warm reset.
+ maxItems: 1
+
allOf:
# Clocks
- if:
--
2.53.0
^ permalink raw reply related
* [PATCH v4 00/19] firmware: qcom: scm: Add minidump SRAM destination support
From: Mukesh Ojha @ 2026-06-24 19:08 UTC (permalink / raw)
To: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Robert Marko, Guru Das Srinagesh
Cc: cros-qcom-dts-watchers, linux-arm-msm, devicetree, linux-kernel,
Mukesh Ojha
On most Qualcomm SoCs where minidump is supported, a word in
always-on SRAM is shared between the operating system (OS) and
boot firmware. Before DDR is initialised on the warm reset
following a crash, firmware reads this word to decide if minidump
is enabled and collect a minidump and where to deliver it:
destination (USB upload to a host, or save to local storage) and
OS is expected to select one destination.
This series wires that mechanism into the SCM driver:
[1]- The SRAM word location is described via a 'sram' phandle on the
SCM DT node.
[2]- Add minidump-sram pattern property for older soc which supports
minidump destination support.
[3-4]- Trivial change for consistency.
[5]- A 'minidump_dest' module parameter (default: usb) selects the
destination. Custom kernel_param_ops expose it as the human-
readable strings "usb" or "storage".
[6-19]- Add the support for Kaanapali and other various SoCs.
Change in v4: https://lore.kernel.org/all/20260522195009.2961022-1-mukesh.ojha@oss.qualcomm.com/
- Refactor commit text for 1, 2, 5.
- added new commit(3/19) for existing issue reported by Sasiko .
Changes in v3: https://lore.kernel.org/lkml/20260519171442.1582987-1-mukesh.ojha@oss.qualcomm.com/
- Addressed some code improvement comments.
- Removed example from scm binding.
- Added minidump-sram binding which follows qcom,imem binding.
- Added some more SoCs which supports this .
Changes in v2: https://lore.kernel.org/lkml/20260507080727.3227367-1-mukesh.ojha@oss.qualcomm.com/
- Remove the restriction on the binding change done in v1.
- Remove sram-name from binding.
- sram definition is introduced and merged, so removed the refs from
v1.
- Minor change in the log as per comment s/find/get/
- remove reference of sram-names
- use minidump-sram instead of minidump-config.
Mukesh Ojha (19):
dt-bindings: firmware: qcom,scm: Add minidump SRAM property
dt-bindings: sram: qcom,imem: Add minidump-sram pattern property
firmware: qcom: scm: Fix missing smp_load_acquire()
firmware: qcom: scm: use dev_err_probe() for dload address failure
firmware: qcom: scm: Add minidump SRAM support
arm64: dts: qcom: kaanapali: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm8450: Add minidump SRAM config to SCM node
arm64: dts: qcom: sa8775p: Add minidump SRAM config to SCM node
arm64: dts: qcom: qcs8300: Add minidump SRAM config to SCM node
arm64: dts: qcom: qdu1000: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm8550: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm8650: Add minidump SRAM config to SCM node
arm64: dts: qcom: sc7280: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm8350: Add minidump SRAM config to SCM node
arm64: dts: qcom: sc7180: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm6350: Add minidump SRAM config to SCM node
arm64: dts: qcom: sm6375: Add minidump SRAM config to SCM node
arm64: dts: qcom: qcs615: Add minidump SRAM config to SCM node
arm64: dts: qcom: sdm845: Add minidump SRAM config to SCM node
.../bindings/firmware/qcom,scm.yaml | 7 +
.../devicetree/bindings/sram/qcom,imem.yaml | 16 +++
arch/arm64/boot/dts/qcom/kaanapali.dtsi | 5 +
arch/arm64/boot/dts/qcom/kodiak.dtsi | 5 +
arch/arm64/boot/dts/qcom/lemans.dtsi | 5 +
arch/arm64/boot/dts/qcom/monaco.dtsi | 5 +
arch/arm64/boot/dts/qcom/qdu1000.dtsi | 5 +
arch/arm64/boot/dts/qcom/sc7180.dtsi | 5 +
arch/arm64/boot/dts/qcom/sdm845.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm6350.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm6375.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm8350.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm8450.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm8550.dtsi | 5 +
arch/arm64/boot/dts/qcom/sm8650.dtsi | 5 +
arch/arm64/boot/dts/qcom/talos.dtsi | 5 +
drivers/firmware/qcom/qcom_scm.c | 120 ++++++++++++++++--
17 files changed, 201 insertions(+), 12 deletions(-)
--
2.53.0
^ permalink raw reply
* Re: [PATCH V14 5/9] iio: imu: inv_icm42607: Add PM support for icm42607
From: sashiko-bot @ 2026-06-24 18:54 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260624182350.50467-6-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The driver incorrectly uses the non-monotonic `ktime_get_real()` to measure the 45ms gyroscope power-off delay, risking bypassed hardware waits.
--
commit d9cd3cdeff3488026816468ab782366517d43120
Author: Chris Morgan <macromorgan@hotmail.com>
Subject: iio: imu: inv_icm42607: Add PM support for icm42607
Add power management support for the ICM42607 device driver.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index 9b82a0499d35b..ad23386a37b78 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> @@ -102,6 +106,71 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
> };
> EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
>
> +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
> + enum inv_icm42607_sensor_mode gyro,
> + enum inv_icm42607_sensor_mode accel)
> +{
> + unsigned int oldaccel, oldgyro;
> + unsigned int sleepval_us = 0;
> + unsigned int val;
> + s64 disable_wait;
> + int ret;
> +
> + ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
> + if (ret)
> + return ret;
> +
> + oldaccel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
> + oldgyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
> +
> + if (gyro == oldgyro && accel == oldaccel)
> + return 0;
> +
> + /*
> + * Datasheet on page 14.26 says we need to ensure the gyro sensor is on
> + * for a minimum of 45ms. So if we transition from an on state to an
> + * off state make sure at least 45ms have passed before power off and
> + * wait if it hasn't.
> + */
> + if (!gyro && oldgyro) {
> + disable_wait = ktime_us_delta(st->conf.gyro_stop,
> + ktime_get_real());
[Severity: Medium]
Is it safe to use the non-monotonic ktime_get_real() here to measure the
45ms gyroscope power-off delay?
If the system wall-clock (CLOCK_REALTIME) is stepped forward concurrently
(e.g., by an NTP sync), ktime_get_real() will jump forward. The calculated
disable_wait could become negative, get clamped to 0, and completely bypass
the mandatory wait before powering off the gyroscope, violating the datasheet
constraint.
Would it be better to use a monotonic clock like ktime_get() here and when
setting st->conf.gyro_stop below to track the elapsed interval reliably?
> + disable_wait = clamp(disable_wait, 0,
> + INV_ICM42607_GYRO_STOP_TIME_US);
> + fsleep(disable_wait);
> + }
> +
> + val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro);
> + val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel);
> + ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
> + if (ret)
> + return ret;
> +
> + /*
> + * If a state change occurs from off to on, sleep for the startup
> + * time of the sensor, unless a sleep_ms is specified. Since more
> + * than one sensor can be transitioned from off to on, select the
> + * maximum time from each of the sensors changing from off to on.
> + * The startup time for the temp sensor is considerably smaller
> + * than the startup time for the other sensors and one or more are
> + * required to be on for the temp sensor to function, so any start
> + * delay should be enough.
> + */
> + if (accel && !oldaccel)
> + sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US);
> +
> + if (gyro && !oldgyro) {
> + sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US);
> + /* Track the earliest we can turn off the gyroscope. */
> + st->conf.gyro_stop = ktime_add_us(ktime_get_real(),
> + INV_ICM42607_GYRO_STOP_TIME_US);
> + }
> +
> + fsleep(sleepval_us);
> +
> + return 0;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624182350.50467-1-macroalpha82@gmail.com?part=5
^ permalink raw reply
* Re: [PATCH V14 8/9] iio: imu: inv_icm42607: Add Temp Support in icm42607
From: sashiko-bot @ 2026-06-24 18:51 UTC (permalink / raw)
To: Chris Morgan; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260624182350.50467-9-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Low] Missing endianness initialization in INV_ICM42607_TEMP_CHAN.
- [Low] Leftover commented-out developmental code.
--
commit a30ed1de671168873a3fd8263c138bbcda62583a
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add Temp Support in icm42607
Add functions for reading temperature sensor data.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
> new file mode 100644
> index 0000000000000..f7e286ecb5cd6
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
[ ... ]
> +static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp)
> +{
[ ... ]
> + accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
> + gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
> + if (!gyro_mode && !accel_mode) {
> + /* enable accel sensor */
> + conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
> +// ret = inv_icm42607_set_accel_conf(st, &conf);
[Severity: Low]
This isn't a bug, but should this commented-out line of code be removed
from the final patch?
> + ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
> + if (ret)
> + return ret;
> + }
[ ... ]
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
> new file mode 100644
> index 0000000000000..cb7b460ffb441
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
[ ... ]
> +#define INV_ICM42607_TEMP_CHAN(_index) \
> +{ \
> + .type = IIO_TEMP, \
> + .info_mask_separate = \
> + BIT(IIO_CHAN_INFO_RAW) | \
> + BIT(IIO_CHAN_INFO_OFFSET) | \
> + BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_shared_by_all = \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .info_mask_shared_by_all_available = \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = _index, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 16, \
> + .storagebits = 16, \
> + }, \
[Severity: Low]
Does this scan_type struct need its endianness field initialized to IIO_BE?
Since get_unaligned_be16() is used when reading the sensor data in
inv_icm42607_temp_read(), it appears the hardware outputs big-endian data.
Omitting the endianness here defaults to IIO_CPU (0), which differs from the
gyro and accel channels that explicitly define it. Could this cause issues
if IIO buffered reads are implemented later?
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624182350.50467-1-macroalpha82@gmail.com?part=8
^ permalink raw reply
* Re: [PATCH V14 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607
From: sashiko-bot @ 2026-06-24 18:50 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260624182350.50467-7-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Missing update of software cache for sensor power mode causes unintended hardware power-downs.
- [Medium] Use of non-monotonic `ktime_get_real()` for hardware delay tracking.
--
commit 99de30070cdb6e08bed49a3aac077583415f3f84
Author: Chris Morgan <macromorgan@hotmail.com>
Subject: iio: imu: inv_icm42607: Add Accelerometer for icm42607
This patch adds accelerometer sensor support for the icm42607 device.
It introduces the accelerometer IIO channel configurations and integrates
the sensor state management.
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> index ad23386a37b78..26cedec0c97ce 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> @@ -106,6 +107,15 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
[ ... ]
> +static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
> + enum inv_icm42607_sensor_mode gyro,
> + enum inv_icm42607_sensor_mode accel)
> +{
[ ... ]
> + if (!gyro && oldgyro) {
> + disable_wait = ktime_us_delta(st->conf.gyro_stop,
> + ktime_get_real());
[Severity: Medium]
Is ktime_get_real() safe to use here for hardware delay tracking?
Because ktime_get_real() returns wall-clock time, it is susceptible to
time jumps, such as an NTP synchronization.
If the clock jumps forwards while the gyroscope is being powered off,
could the calculated disable_wait become negative and get clamped to 0,
thereby bypassing the 45ms minimum on-time requirement?
> + disable_wait = clamp(disable_wait, 0,
> + INV_ICM42607_GYRO_STOP_TIME_US);
> + fsleep(disable_wait);
> + }
[ ... ]
> + if (gyro && !oldgyro) {
> + sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US);
> + /* Track the earliest we can turn off the gyroscope. */
> + st->conf.gyro_stop = ktime_add_us(ktime_get_real(),
> + INV_ICM42607_GYRO_STOP_TIME_US);
[Severity: Medium]
Would using a monotonic time source like ktime_get() prevent potential
issues with time jumps during this delay calculation?
> + }
> +
> + fsleep(sleepval_us);
> +
> + return 0;
> +}
[ ... ]
> @@ -171,6 +181,160 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
[ ... ]
> +int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st,
> + struct inv_icm42607_sensor_conf *conf,
> + enum iio_chan_type chan_type)
> +{
[ ... ]
> + if (chan_type == IIO_ANGL_VEL)
> + return inv_icm42607_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode);
> +
> + return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode);
> +}
[Severity: High]
Does this code update the software state cache for the sensor mode?
It appears that inv_icm42607_set_sensor_conf() applies the new mode to
the hardware by calling inv_icm42607_set_pwr_mgmt0(), but returns without
saving conf->mode into oldconf->mode (or st->conf.accel.mode /
st->conf.gyro.mode).
If this cache remains at its initial OFF value, could a subsequent
configuration of the other sensor read this stale OFF state and
erroneously power down the currently active sensor?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624182350.50467-1-macroalpha82@gmail.com?part=6
^ permalink raw reply
* Re: [PATCH V14 2/9] dt-bindings: iio: imu: icm42600: Add icm42607
From: sashiko-bot @ 2026-06-24 18:39 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260624182350.50467-3-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The new compatibles 'invensense,icm42607' and 'invensense,icm42607p' are omitted from the conditional block requiring the 'interrupts' property, violating Device Tree hardware description rules.
--
commit dfee2e713ee93b8822c465d8c4cace6a935bc62d
Author: Chris Morgan <macromorgan@hotmail.com>
dt-bindings: iio: imu: icm42600: Add icm42607
This commit adds the icm42607 and icm42607p inertial measurement unit device tree bindings. It also refactors the required properties to make the interrupts property conditional, explicitly omitting the newly added compatibles.
> This device is functionally very similar to the icm42600 series with a
> very different register layout. The driver does not require an
> interrupt for these specific chip revisions.
[Severity: Medium]
Is it appropriate to omit the interrupts property based on driver behavior rather than hardware wiring?
Device tree bindings are meant to describe the hardware capabilities rather than specific operating system driver limitations. If the icm42607 hardware physically has an interrupt line, shouldn't it remain a required property regardless of whether the current driver uses it?
> diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
> index 9b2af104f186d..81b6e85decd52 100644
> --- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
> +++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
[ ... ]
> @@ -67,10 +69,24 @@ properties:
> required:
> - compatible
> - reg
> - - interrupts
>
> allOf:
> - $ref: /schemas/spi/spi-peripheral-props.yaml#
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - invensense,icm42600
> + - invensense,icm42602
> + - invensense,icm42605
> + - invensense,icm42622
> + - invensense,icm42631
> + - invensense,icm42686
> + - invensense,icm42688
> + then:
> + required:
> + - interrupts
[Severity: Medium]
Does omitting the newly added compatibles from this enum block bypass device tree schema validation for those variants?
If the hardware shares the same interrupt constraints as the previous generations listed here, device trees missing the interrupts property for invensense,icm42607 will now silently pass validation.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260624182350.50467-1-macroalpha82@gmail.com?part=2
^ permalink raw reply
* Re: [PATCH v2] ASoC: dt-bindings: Convert cirrus,cs35l36 to DT schema
From: David Heidelberg @ 2026-06-24 18:39 UTC (permalink / raw)
To: Rob Herring, David Rhodes
Cc: Richard Fitzgerald, Liam Girdwood, Mark Brown,
Krzysztof Kozlowski, Conor Dooley, patches, Bjorn Helgaas,
linux-sound, devicetree, linux-kernel, phone-devel
In-Reply-To: <CAL_JsqLE8Z-LbeF9r=sqRqAoGUcs7R-T4cN+hF3QzjGydHctgQ@mail.gmail.com>
On 24/06/2026 20:17, Rob Herring wrote:
> On Wed, Jun 24, 2026 at 11:02 AM David Heidelberg via B4 Relay
> <devnull+david.ixit.cz@kernel.org> wrote:
>>
>> From: David Heidelberg <david@ixit.cz>
>>
>> Convert CS35L36 Speaker Amplifier to yaml.
>>
>> Changes:
>> - maintainers email to the generic Cirrus email
>> - Both the codec and downstream worked just fine without
>> VP-supply provided. Align with datasheet for similar models.
>> - add dai-common.yaml to cover for '#sound-dai-cells',
>> 'sound-name-prefix'
>>
>> Reviewed-by: David Rhodes <David.Rhodes@cirrus.com>
>
> If you are going to take stuff I haven't fixed:
>
> Assisted-by: OpenAI:gpt-4
>
> (I don't remember the exact flavor I used)
>
>> Co-developed-by: Rob Herring (Arm) <robh@kernel.org>
>> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
>> Signed-off-by: David Heidelberg <david@ixit.cz>
>> ---
>> Relevant for Pixel 3 / 3XL / 4.
>> ---
>> Changes in v2:
>> - Rename the commit. (Mark)
>> - Link to v1: https://lore.kernel.org/r/20260618-dt-cirrus-cs35l36-v1-1-1a43515666ad@ixit.cz
>> ---
>> .../devicetree/bindings/sound/cirrus,cs35l36.yaml | 224 +++++++++++++++++++++
>> .../devicetree/bindings/sound/cs35l36.txt | 168 ----------------
>> 2 files changed, 224 insertions(+), 168 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml
>> new file mode 100644
>> index 0000000000000..af0acaaefb68e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l36.yaml
>> @@ -0,0 +1,224 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +%YAML 1.2
>> +---
>> +$id: http://devicetree.org/schemas/cirrus,cs35l36.yaml#
>> +$schema: http://devicetree.org/meta-schemas/core.yaml#
>> +
>> +title: Cirrus Logic CS35L36 Speaker Amplifier
>> +
>> +maintainers:
>> + - patches@opensource.cirrus.com
>> + - Bjorn Helgaas <bhelgaas@google.com>
>
> Bjorn is not correct. Generally we want a person, not a company list.
I'm adding back James, can I keep the patches at 2nd place?
>
>> +
>> +description: |
>
> Don't need '|'.
>
>> + CS35L36 is a boosted mono Class D amplifier
>> +
>> +allOf:
>> + - $ref: /schemas/sound/dai-common.yaml#
>> +
>> +properties:
>> + compatible:
>> + enum:
>> + - cirrus,cs35l36
>> +
>> + reg:
>> + maxItems: 1
>> +
>> + interrupts:
>> + maxItems: 1
>> +
>> + VA-supply:
>> + description: Voltage regulator of analog internal section
>> +
>> + VP-supply:
>> + description: Voltage regulator of boost converter
>> +
>> + reset-gpios:
>> + maxItems: 1
>> +
>> + cirrus,boost-ctl-millivolt:
>> + description: Boost converter output voltage in millivolts (step 50)
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + minimum: 2550
>> + maximum: 12000
>> +
>> + cirrus,boost-peak-milliamp:
>> + description: Boost-converter peak current limit in mA (step 50)
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 4500
>> + minimum: 1600
>> + maximum: 4500
>> +
>> + cirrus,boost-ind-nanohenry:
>> + description: Initial inductor estimation reference value in nanohenry (1000=1μH, 1200=1.2μH)
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 1000
>> +
>> + cirrus,multi-amp-mode:
>> + description: Hi-Z ASP port when more than one amplifier in system.
>> + type: boolean
>> +
>> + cirrus,boost-ctl-select:
>> + description: Boost converter control source selection
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 0x01
>> + enum:
>> + - 0x00 # Control Port
>> + - 0x01 # Class
>> + - 0x10 # Sync
>> +
>> + cirrus,amp-pcm-inv:
>> + description: Invert incoming PCM data when true.
>> + type: boolean
>> +
>> + cirrus,imon-pol-inv:
>> + description: Invert polarity of outbound IMON feedback when true.
>> + type: boolean
>> +
>> + cirrus,vmon-pol-inv:
>> + description: Invert polarity of outbound VMON feedback when true.
>> + type: boolean
>> +
>> + cirrus,dcm-mode-enable:
>> + description: Enable boost converter automatic Discontinuous Conduction Mode.
>> + type: boolean
>> +
>> + cirrus,weak-fet-disable:
>> + description: Reduce output driver strength in Weak-FET Drive Mode when true.
>> + type: boolean
>> +
>> + cirrus,classh-wk-fet-delay:
>> + description: Weak-FET entry delay in ms
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 100
>
> How? Range is 0-7.
Oh yeah, this was not thought, in next revision I converted this to
cirrus,classh-wk-fet-delay mentioned in the example. Then I checked for it in
the code, nowhere implemented or used, thus I switched to
cirrus,classh-wk-fet-delay-ms and using the values directly.
>
>> + enum:
>> + - 0 # 0
>> + - 1 # 5
>> + - 2 # 10
>> + - 3 # 50
>> + - 4 # 100
>> + - 5 # 200
>> + - 6 # 500
>> + - 7 # 1000
>> +
>> + cirrus,classh-weak-fet-thld-millivolt:
>> + description: Weak-FET drive threshold in mV
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + enum: [50, 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 600, 650, 700]
>> +
>> + cirrus,temp-warn-threshold:
>> + description: Overtemperature warning threshold
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 2
>> + enum:
>> + - 0 # 105°C
>> + - 1 # 115°C
>> + - 2 # 125°C
>> + - 3 # 135°C
>> +
>> + cirrus,irq-drive-select:
>> + description: Interrupt output driver type
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 1
>> + enum:
>> + - 0 # open-drain
>> + - 1 # push-pull
>> +
>> + cirrus,irq-gpio-select:
>> + description: Programmable IRQ pin selection
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + enum:
>> + - 0 # PDM_DATA/SWIRE_SD/INT
>> + - 1 # GPIO
>> +
>> + cirrus,vpbr-config:
>> + description: Brownout prevention configuration sub-node
>> + type: object
>> + additionalProperties: false
>> +
>> + properties:
>> + cirrus,vpbr-en:
>> + description: VBST brownout prevention enable
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> + default: 0
>> + enum:
>> + - 0 # disabled
>> + - 1 # enabled
>> +
>> + cirrus,vpbr-thld:
>> + description: Initial VPBR threshold voltage
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-atk-rate:
>> + description: Attenuation attack step rate
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-atk-vol:
>> + description: VP brownout prevention step size
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-max-attn:
>> + description: Maximum attenuation during VP brownout prevention
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-wait:
>> + description: Delay between brownout clearance and attenuation release
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-rel-rate:
>> + description: Attenuation release step rate
>> + $ref: /schemas/types.yaml#/definitions/uint32
>> +
>> + cirrus,vpbr-mute-en:
>> + description: Mute audio if maximum attenuation reached
>> + $ref: /schemas/types.yaml#/definitions/uint32
>
> Constraints on any of these?
Code just applies whatever is thrown at it, maybe David knows more?
#nodatasheet (but would be lovely to have one)
David
>
>> +
>> +required:
>> + - compatible
>> + - reg
>> + - interrupts
>> + - VA-supply
>> +
>> +unevaluatedProperties: false
>> +
>> +examples:
>> + - |
>> + #include <dt-bindings/gpio/gpio.h>
>> + #include <dt-bindings/interrupt-controller/irq.h>
>> +
>> + i2c {
>> + #address-cells = <1>;
>> + #size-cells = <0>;
>> +
>> + codec@40 {
>> + compatible = "cirrus,cs35l36";
>> + reg = <0x40>;
>> + VA-supply = <&dummy_vreg>;
>> + VP-supply = <&dummy_vreg>;
>> + reset-gpios = <&gpio0 54 GPIO_ACTIVE_HIGH>;
>> + interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
>> +
>> + cirrus,boost-ind-nanohenry = <1000>;
>> + cirrus,boost-ctl-millivolt = <10000>;
>> + cirrus,boost-peak-milliamp = <4500>;
>> + cirrus,boost-ctl-select = <0x00>;
>> + cirrus,weak-fet-delay = <4>;
>> + cirrus,weak-fet-thld = <0x01>;
>> + cirrus,temp-warn-threshold = <1>;
>> + cirrus,multi-amp-mode;
>> + cirrus,irq-drive-select = <1>;
>> + cirrus,irq-gpio-select = <0x01>;
>> +
>> + cirrus,vpbr-config {
>> + cirrus,vpbr-en = <0>;
>> + cirrus,vpbr-thld = <0x05>;
>> + cirrus,vpbr-atk-rate = <0x02>;
>> + cirrus,vpbr-atk-vol = <0x01>;
>> + cirrus,vpbr-max-attn = <0x09>;
>> + cirrus,vpbr-wait = <0x01>;
>> + cirrus,vpbr-rel-rate = <0x05>;
>> + cirrus,vpbr-mute-en = <0x00>;
>> + };
>> + };
>> + };
>> +...
>> diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt
>> deleted file mode 100644
>> index d34117b8558e5..0000000000000
>> --- a/Documentation/devicetree/bindings/sound/cs35l36.txt
>> +++ /dev/null
>> @@ -1,168 +0,0 @@
>> -CS35L36 Speaker Amplifier
>> -
>> -Required properties:
>> -
>> - - compatible : "cirrus,cs35l36"
>> -
>> - - reg : the I2C address of the device for I2C
>> -
>> - - VA-supply, VP-supply : power supplies for the device,
>> - as covered in
>> - Documentation/devicetree/bindings/regulator/regulator.txt.
>> -
>> - - cirrus,boost-ctl-millivolt : Boost Voltage Value. Configures the boost
>> - converter's output voltage in mV. The range is from 2550mV to 12000mV with
>> - increments of 50mV.
>> - (Default) VP
>> -
>> - - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA.
>> - Configures the peak current by monitoring the current through the boost FET.
>> - Range starts at 1600mA and goes to a maximum of 4500mA with increments of
>> - 50mA.
>> - (Default) 4.50 Amps
>> -
>> - - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value.
>> - Seeds the digital boost converter's inductor estimation block with the initial
>> - inductance value to reference.
>> -
>> - 1000 = 1uH (Default)
>> - 1200 = 1.2uH
>> -
>> -Optional properties:
>> - - cirrus,multi-amp-mode : Boolean to determine if there are more than
>> - one amplifier in the system. If more than one it is best to Hi-Z the ASP
>> - port to prevent bus contention on the output signal
>> -
>> - - cirrus,boost-ctl-select : Boost converter control source selection.
>> - Selects the source of the BST_CTL target VBST voltage for the boost
>> - converter to generate.
>> - 0x00 - Control Port Value
>> - 0x01 - Class H Tracking (Default)
>> - 0x10 - MultiDevice Sync Value
>> -
>> - - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming
>> - PCM data
>> -
>> - - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the
>> - polarity of outbound IMON feedback data
>> -
>> - - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the
>> - polarity of outbound VMON feedback data
>> -
>> - - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable.
>> - This enables the digital boost converter to operate in a low power
>> - (Discontinuous Conduction) mode during low loading conditions.
>> -
>> - - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is
>> - reduced when operating in a Weak-FET Drive Mode and must not be used to drive
>> - a large load.
>> -
>> - - cirrus,classh-wk-fet-delay : Weak-FET entry delay. Controls the delay
>> - (in ms) before the Class H algorithm switches to the weak-FET voltage
>> - (after the audio falls and remains below the value specified in WKFET_AMP_THLD).
>> -
>> - 0 = 0ms
>> - 1 = 5ms
>> - 2 = 10ms
>> - 3 = 50ms
>> - 4 = 100ms (Default)
>> - 5 = 200ms
>> - 6 = 500ms
>> - 7 = 1000ms
>> -
>> - - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold.
>> - Configures the signal threshold at which the PWM output stage enters
>> - weak-FET operation. The range is 50mV to 700mV in 50mV increments.
>> -
>> - - cirrus,temp-warn-threshold : Amplifier overtemperature warning threshold.
>> - Configures the threshold at which the overtemperature warning condition occurs.
>> - When the threshold is met, the overtemperature warning attenuation is applied
>> - and the TEMP_WARN_EINT interrupt status bit is set.
>> - If TEMP_WARN_MASK = 0, INTb is asserted.
>> -
>> - 0 = 105C
>> - 1 = 115C
>> - 2 = 125C (Default)
>> - 3 = 135C
>> -
>> - - cirrus,irq-drive-select : Selects the driver type of the selected interrupt
>> - output.
>> -
>> - 0 = Open-drain
>> - 1 = Push-pull (Default)
>> -
>> - - cirrus,irq-gpio-select : Selects the pin to serve as the programmable
>> - interrupt output.
>> -
>> - 0 = PDM_DATA / SWIRE_SD / INT (Default)
>> - 1 = GPIO
>> -
>> -Optional properties for the "cirrus,vpbr-config" Sub-node
>> -
>> - - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the
>> - VBST brownout prevention algorithm is enabled or disabled.
>> -
>> - 0 = VBST brownout prevention disabled (default)
>> - 1 = VBST brownout prevention enabled
>> -
>> - See Section 7.31.1 VPBR Config for configuration options & further details
>> -
>> - - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout
>> - threshold voltage
>> -
>> - - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the
>> - amount delay between consecutive volume attenuation steps when a brownout
>> - condition is present and the VP brownout condition is in an attacking state.
>> -
>> - - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP
>> - brownout prevention attacking attenuation step size when operating in either
>> - digital volume or analog gain modes.
>> -
>> - - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention
>> - can apply to the audio signal.
>> -
>> - - cirrus,vpbr-wait : Configures the delay time between a brownout condition
>> - no longer being present and the VP brownout prevention entering an attenuation
>> - release state.
>> -
>> - - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay
>> - between consecutive volume attenuation release steps when a brownout condition
>> - is not longer present and the VP brownout is in an attenuation release state.
>> -
>> - - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value
>> - is reached, the error condition still remains, and this bit is set, the audio
>> - is muted.
>> -
>> -Example:
>> -
>> -cs35l36: cs35l36@40 {
>> - compatible = "cirrus,cs35l36";
>> - reg = <0x40>;
>> - VA-supply = <&dummy_vreg>;
>> - VP-supply = <&dummy_vreg>;
>> - reset-gpios = <&gpio0 54 0>;
>> - interrupt-parent = <&gpio8>;
>> - interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
>> -
>> - cirrus,boost-ind-nanohenry = <1000>;
>> - cirrus,boost-ctl-millivolt = <10000>;
>> - cirrus,boost-peak-milliamp = <4500>;
>> - cirrus,boost-ctl-select = <0x00>;
>> - cirrus,weak-fet-delay = <0x04>;
>> - cirrus,weak-fet-thld = <0x01>;
>> - cirrus,temp-warn-threshold = <0x01>;
>> - cirrus,multi-amp-mode;
>> - cirrus,irq-drive-select = <0x01>;
>> - cirrus,irq-gpio-select = <0x01>;
>> -
>> - cirrus,vpbr-config {
>> - cirrus,vpbr-en = <0x00>;
>> - cirrus,vpbr-thld = <0x05>;
>> - cirrus,vpbr-atk-rate = <0x02>;
>> - cirrus,vpbr-atk-vol = <0x01>;
>> - cirrus,vpbr-max-attn = <0x09>;
>> - cirrus,vpbr-wait = <0x01>;
>> - cirrus,vpbr-rel-rate = <0x05>;
>> - cirrus,vpbr-mute-en = <0x00>;
>> - };
>> -};
>>
>> ---
>> base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
>> change-id: 20260618-dt-cirrus-cs35l36-99c466fb13fd
>>
>> Best regards,
>> --
>> David Heidelberg <david@ixit.cz>
>>
>>
>>
--
David Heidelberg
^ permalink raw reply
* [PATCH V14 9/9] arm64: dts: rockchip: Add icm42607p IMU for RG-DS
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add the Invensense ICM42607P IMU for the Anbernic RG-DS. Mount-matrix
was tested with iio-sensor-proxy and reports correct orientation.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
index 8d906ab02c5f..b770bfd5268d 100644
--- a/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
+++ b/arch/arm64/boot/dts/rockchip/rk3568-anbernic-rg-ds.dts
@@ -871,7 +871,13 @@ aw87391_pa_r: audio-codec@5b {
sound-name-prefix = "Right Amp";
};
- /* invensense,icm42607p at 0x68 */
+ icm42607p: imu@68 {
+ compatible = "invensense,icm42607p";
+ reg = <0x68>;
+ mount-matrix = "-1", "0", "0",
+ "0", "1", "0",
+ "0", "0", "-1";
+ };
};
&i2c3 {
--
2.43.0
^ permalink raw reply related
* [PATCH V14 8/9] iio: imu: inv_icm42607: Add Temp Support in icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add functions for reading temperature sensor data.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 8 ++
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 8 ++
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 99 +++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 37 +++++++
5 files changed, 153 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index 8e73385c8f4b..7b907e019601 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
inv-icm42607-y += inv_icm42607_gyro.o
inv-icm42607-y += inv_icm42607_accel.o
+inv-icm42607-y += inv_icm42607_temp.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
index 8ef9fdae1bc8..5ff6756b9515 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \
{ \
@@ -40,6 +41,7 @@ enum inv_icm42607_accel_scan {
INV_ICM42607_ACCEL_SCAN_X,
INV_ICM42607_ACCEL_SCAN_Y,
INV_ICM42607_ACCEL_SCAN_Z,
+ INV_ICM42607_ACCEL_SCAN_TEMP,
};
static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
@@ -54,6 +56,7 @@ static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
inv_icm42607_accel_ext_infos),
INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
inv_icm42607_accel_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_ACCEL_SCAN_TEMP),
};
static const int inv_icm42607_accel_scale_nano[][2] = {
@@ -186,6 +189,11 @@ static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ACCEL:
break;
+ case IIO_TEMP:
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return inv_icm42607_temp_read_raw(indio_dev, chan,
+ val, val2, mask);
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
index c7215b3826ad..4e5db5e19e9f 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -15,6 +15,7 @@
#include <linux/types.h>
#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \
{ \
@@ -40,6 +41,7 @@ enum inv_icm42607_gyro_scan {
INV_ICM42607_GYRO_SCAN_X,
INV_ICM42607_GYRO_SCAN_Y,
INV_ICM42607_GYRO_SCAN_Z,
+ INV_ICM42607_GYRO_SCAN_TEMP,
};
static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = {
@@ -54,6 +56,7 @@ static const struct iio_chan_spec inv_icm42607_gyro_channels[] = {
inv_icm42607_gyro_ext_infos),
INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z,
inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_TEMP_CHAN(INV_ICM42607_GYRO_SCAN_TEMP),
};
static const int inv_icm42607_gyro_scale_nano[][2] = {
@@ -182,6 +185,11 @@ static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ANGL_VEL:
break;
+ case IIO_TEMP:
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return inv_icm42607_temp_read_raw(indio_dev, chan,
+ val, val2, mask);
+ break;
default:
return -EINVAL;
}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
new file mode 100644
index 000000000000..f7e286ecb5cd
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include "inv_icm42607.h"
+#include "inv_icm42607_temp.h"
+
+static int inv_icm42607_temp_read(struct inv_icm42607_state *st, s16 *temp)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct device *dev = regmap_get_device(st->map);
+ int ret, gyro_mode, accel_mode;
+ unsigned int val;
+ u8 raw[2];
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /*
+ * Check if both the gyro and accel are off and if so, enable one
+ * of them. The temp sensor cannot be read if both the gyro and
+ * accel sensor are off. Prefer to enable the accel over the gyro
+ * as the datasheet says the gyro uses 5x more power and it has
+ * a minimum run time of 45ms.
+ */
+ ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
+ if (ret)
+ return ret;
+
+ accel_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
+ gyro_mode = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
+ if (!gyro_mode && !accel_mode) {
+ /* enable accel sensor */
+ conf.mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+// ret = inv_icm42607_set_accel_conf(st, &conf);
+ ret = inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_bulk_read(st->map, INV_ICM42607_REG_TEMP_DATA1,
+ raw, sizeof(raw));
+ if (ret)
+ return ret;
+
+ *temp = get_unaligned_be16(raw);
+ if (*temp == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 temp;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_temp_read(st, &temp);
+ if (ret)
+ return ret;
+ *val = temp;
+ return IIO_VAL_INT;
+ /*
+ * T°C = (temp / 128) + 25
+ * Tm°C = 1000 * ((temp * 100 / 12800) + 25)
+ * scale: 100000 / 12800 ~= 7.8125
+ * offset: 3200
+ */
+ case IIO_CHAN_INFO_SCALE:
+ *val = 7;
+ *val2 = 812500000;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 3200;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
new file mode 100644
index 000000000000..cb7b460ffb44
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_TEMP_H_
+#define INV_ICM42607_TEMP_H_
+
+#include <linux/bitops.h>
+
+struct iio_dev;
+struct iio_chan_spec;
+
+#define INV_ICM42607_TEMP_CHAN(_index) \
+{ \
+ .type = IIO_TEMP, \
+ .info_mask_separate = \
+ BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ }, \
+}
+
+int inv_icm42607_temp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask);
+
+#endif
--
2.43.0
^ permalink raw reply related
* [PATCH V14 7/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add gyroscope functions to the icm42607 driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 4 +
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 5 +
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 305 ++++++++++++++++++
4 files changed, 315 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index 372c6d6bdcec..8e73385c8f4b 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_gyro.o
inv-icm42607-y += inv_icm42607_accel.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index cd78b43f36fa..81d56920f356 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -142,6 +142,7 @@ struct inv_icm42607_suspended {
* @lock: lock for serializing multiple registers access.
* @map: regmap pointer.
* @indio_accel: accelerometer IIO device.
+ * @indio_gyro: gyroscope IIO device.
* @vddio_supply: I/O voltage regulator for the chip.
* @vddio_en: I/O voltage status for runtime PM.
* @suspended: suspended sensors configuration.
@@ -153,6 +154,7 @@ struct inv_icm42607_state {
struct mutex lock;
struct regmap *map;
struct iio_dev *indio_accel;
+ struct iio_dev *indio_gyro;
struct regulator *vddio_supply;
bool vddio_en;
struct inv_icm42607_suspended suspended;
@@ -414,6 +416,8 @@ int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st);
+
struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 26cedec0c97c..a849c68227ae 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -534,6 +534,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (IS_ERR(st->indio_accel))
return PTR_ERR(st->indio_accel);
+ /* Initialize IIO device for Gyro */
+ st->indio_gyro = inv_icm42607_gyro_init(st);
+ if (IS_ERR(st->indio_gyro))
+ return PTR_ERR(st->indio_gyro);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
new file mode 100644
index 000000000000..c7215b3826ad
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -0,0 +1,305 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm42607.h"
+
+#define INV_ICM42607_GYRO_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_gyro_scan {
+ INV_ICM42607_GYRO_SCAN_X,
+ INV_ICM42607_GYRO_SCAN_Y,
+ INV_ICM42607_GYRO_SCAN_Z,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_gyro_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_gyro_channels[] = {
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_X, INV_ICM42607_GYRO_SCAN_X,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Y, INV_ICM42607_GYRO_SCAN_Y,
+ inv_icm42607_gyro_ext_infos),
+ INV_ICM42607_GYRO_CHAN(IIO_MOD_Z, INV_ICM42607_GYRO_SCAN_Z,
+ inv_icm42607_gyro_ext_infos),
+};
+
+static const int inv_icm42607_gyro_scale_nano[][2] = {
+ [INV_ICM42607_GYRO_FS_2000DPS] = { 0, 1065264 },
+ [INV_ICM42607_GYRO_FS_1000DPS] = { 0, 532632 },
+ [INV_ICM42607_GYRO_FS_500DPS] = { 0, 266316 },
+ [INV_ICM42607_GYRO_FS_250DPS] = { 0, 133158 },
+};
+
+static int inv_icm42607_gyro_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ guard(mutex)(&st->lock);
+
+ idx = st->conf.gyro.fs;
+
+ *val = inv_icm42607_gyro_scale_nano[idx][0];
+ *val2 = inv_icm42607_gyro_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_gyro_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_gyro_scale_nano);
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_gyro_scale_nano[idx][0] &&
+ val2 == inv_icm42607_gyro_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL);
+}
+
+static const int inv_icm42607_gyro_odr[][2] = {
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+};
+
+static int inv_icm42607_gyro_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ guard(mutex)(&st->lock);
+
+ odr = st->conf.gyro.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_gyro_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_gyro_odr[i][0];
+ *val2 = inv_icm42607_gyro_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_gyro_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_gyro_odr); ++idx) {
+ if (val == inv_icm42607_gyro_odr[idx][0] &&
+ val2 == inv_icm42607_gyro_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_gyro_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ANGL_VEL);
+}
+
+static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_read_sensor(indio_dev, chan, &data);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_gyro_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_gyro_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_gyro_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_gyro_odr[5];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_gyro_odr) - 5) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = inv_icm42607_gyro_write_scale(indio_dev, val, val2);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_gyro_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_gyro_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info inv_icm42607_gyro_info = {
+ .read_raw = inv_icm42607_gyro_read_raw,
+ .read_avail = inv_icm42607_gyro_read_avail,
+ .write_raw = inv_icm42607_gyro_write_raw,
+ .write_raw_get_fmt = inv_icm42607_gyro_write_raw_get_fmt,
+};
+
+struct iio_dev *inv_icm42607_gyro_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ const char *name;
+ struct inv_icm42607_sensor_state *gyro_st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ gyro_st = iio_priv(indio_dev);
+
+ gyro_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ gyro_st->filter = INV_ICM42607_FILTER_BW_73HZ;
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_gyro_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_gyro_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_gyro_channels);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
--
2.43.0
^ permalink raw reply related
* [PATCH V14 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add icm42607 accelerometer sensor for icm42607.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Makefile | 1 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 38 +++
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 309 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 169 ++++++++++
4 files changed, 517 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index be109102e203..372c6d6bdcec 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
inv-icm42607-y += inv_icm42607_core.o
+inv-icm42607-y += inv_icm42607_accel.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 219cfcf7ac99..cd78b43f36fa 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -87,6 +87,17 @@ enum inv_icm42607_filter_bw {
INV_ICM42607_FILTER_BW_NB
};
+/* Low-Power mode sensor data filter (averaging) */
+enum inv_icm42607_filter_avg {
+ INV_ICM42607_FILTER_AVG_2X = 0,
+ INV_ICM42607_FILTER_AVG_4X = 1,
+ INV_ICM42607_FILTER_AVG_8X = 2,
+ INV_ICM42607_FILTER_AVG_16X = 3,
+ INV_ICM42607_FILTER_AVG_32X = 4,
+ INV_ICM42607_FILTER_AVG_64X = 5,
+ /* values 6 and 7 also correspond to 64x. */
+};
+
/* Temperature sensor data filter (bandwidth) */
enum inv_icm42607_temp_filter_bw {
INV_ICM42607_TEMP_FILTER_BYPASS = 0,
@@ -106,6 +117,7 @@ struct inv_icm42607_sensor_conf {
int odr;
int filter;
};
+#define INV_ICM42607_SENSOR_CONF_INIT { -1, -1, -1, -1 }
struct inv_icm42607_conf {
struct inv_icm42607_sensor_conf gyro;
@@ -129,6 +141,7 @@ struct inv_icm42607_suspended {
* @hw: Hardware specific data.
* @lock: lock for serializing multiple registers access.
* @map: regmap pointer.
+ * @indio_accel: accelerometer IIO device.
* @vddio_supply: I/O voltage regulator for the chip.
* @vddio_en: I/O voltage status for runtime PM.
* @suspended: suspended sensors configuration.
@@ -139,6 +152,7 @@ struct inv_icm42607_state {
const struct inv_icm42607_hw *hw;
struct mutex lock;
struct regmap *map;
+ struct iio_dev *indio_accel;
struct regulator *vddio_supply;
bool vddio_en;
struct inv_icm42607_suspended suspended;
@@ -146,6 +160,16 @@ struct inv_icm42607_state {
struct iio_mount_matrix orientation;
};
+/**
+ * struct inv_icm42607_sensor_state - sensor state variables
+ * @power_mode: sensor requested power mode (for common frequencies)
+ * @filter: sensor filter.
+ */
+struct inv_icm42607_sensor_state {
+ enum inv_icm42607_sensor_mode power_mode;
+ int filter;
+};
+
/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
/* Register Map for User Bank 0 */
@@ -374,8 +398,22 @@ extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
extern const struct dev_pm_ops inv_icm42607_pm_ops;
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan);
+
+int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ enum iio_chan_type chan_type);
+
+int inv_icm42607_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val);
+
int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
inv_icm42607_bus_setup bus_setup);
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st);
+
#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
new file mode 100644
index 000000000000..8ef9fdae1bc8
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -0,0 +1,309 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+#include "inv_icm42607.h"
+
+#define INV_ICM42607_ACCEL_CHAN(_modifier, _index, _ext_info) \
+{ \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = _modifier, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ .ext_info = _ext_info, \
+}
+
+enum inv_icm42607_accel_scan {
+ INV_ICM42607_ACCEL_SCAN_X,
+ INV_ICM42607_ACCEL_SCAN_Y,
+ INV_ICM42607_ACCEL_SCAN_Z,
+};
+
+static const struct iio_chan_spec_ext_info inv_icm42607_accel_ext_infos[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, inv_icm42607_get_mount_matrix),
+ { }
+};
+
+static const struct iio_chan_spec inv_icm42607_accel_channels[] = {
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_X, INV_ICM42607_ACCEL_SCAN_X,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Y, INV_ICM42607_ACCEL_SCAN_Y,
+ inv_icm42607_accel_ext_infos),
+ INV_ICM42607_ACCEL_CHAN(IIO_MOD_Z, INV_ICM42607_ACCEL_SCAN_Z,
+ inv_icm42607_accel_ext_infos),
+};
+
+static const int inv_icm42607_accel_scale_nano[][2] = {
+ [INV_ICM42607_ACCEL_FS_16G] = { 0, 4788403 },
+ [INV_ICM42607_ACCEL_FS_8G] = { 0, 2394202 },
+ [INV_ICM42607_ACCEL_FS_4G] = { 0, 1197101 },
+ [INV_ICM42607_ACCEL_FS_2G] = { 0, 598550 },
+};
+
+static int inv_icm42607_accel_read_scale(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ unsigned int idx;
+
+ guard(mutex)(&st->lock);
+
+ idx = st->conf.accel.fs;
+
+ *val = inv_icm42607_accel_scale_nano[idx][0];
+ *val2 = inv_icm42607_accel_scale_nano[idx][1];
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int inv_icm42607_accel_write_scale(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ size_t scales_len = ARRAY_SIZE(inv_icm42607_accel_scale_nano);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int ret;
+
+ for (idx = 0; idx < scales_len; idx++) {
+ if (val == inv_icm42607_accel_scale_nano[idx][0] &&
+ val2 == inv_icm42607_accel_scale_nano[idx][1])
+ break;
+ }
+ if (idx >= scales_len)
+ return -EINVAL;
+
+ conf.fs = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+}
+
+/* IIO format int + micro , values 0-5 reserved. */
+static const int inv_icm42607_accel_odr[][2] = {
+ [INV_ICM42607_ODR_1600HZ] = { 1600, 0 },
+ [INV_ICM42607_ODR_800HZ] = { 800, 0 },
+ [INV_ICM42607_ODR_400HZ] = { 400, 0 },
+ [INV_ICM42607_ODR_200HZ] = { 200, 0 },
+ [INV_ICM42607_ODR_100HZ] = { 100, 0 },
+ [INV_ICM42607_ODR_50HZ] = { 50, 0 },
+ [INV_ICM42607_ODR_25HZ] = { 25, 0 },
+ [INV_ICM42607_ODR_12_5HZ] = { 12, 500000 },
+ [INV_ICM42607_ODR_6_25HZ_LP] = { 6, 250000 },
+ [INV_ICM42607_ODR_3_125HZ_LP] = { 3, 125000 },
+ [INV_ICM42607_ODR_1_5625HZ_LP] = { 1, 562500 },
+};
+
+static int inv_icm42607_accel_read_odr(struct inv_icm42607_state *st,
+ int *val, int *val2)
+{
+ unsigned int odr;
+ unsigned int i;
+
+ guard(mutex)(&st->lock);
+
+ odr = st->conf.accel.odr;
+
+ for (i = 5; i < ARRAY_SIZE(inv_icm42607_accel_odr); ++i) {
+ if (i == odr)
+ break;
+ }
+ if (i >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ *val = inv_icm42607_accel_odr[i][0];
+ *val2 = inv_icm42607_accel_odr[i][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int inv_icm42607_accel_write_odr(struct iio_dev *indio_dev,
+ int val, int val2)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int idx;
+ int ret;
+
+ for (idx = 5; idx < ARRAY_SIZE(inv_icm42607_accel_odr); ++idx) {
+ if (val == inv_icm42607_accel_odr[idx][0] &&
+ val2 == inv_icm42607_accel_odr[idx][1])
+ break;
+ }
+ if (idx >= ARRAY_SIZE(inv_icm42607_accel_odr))
+ return -EINVAL;
+
+ conf.odr = idx;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ return inv_icm42607_set_sensor_conf(st, &conf, IIO_ACCEL);
+}
+
+static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ s16 data;
+ int ret;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = inv_icm42607_read_sensor(indio_dev, chan, &data);
+ if (ret)
+ return ret;
+ *val = data;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ return inv_icm42607_accel_read_scale(indio_dev, val, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_read_odr(st, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type, int *length, long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (const int *)inv_icm42607_accel_scale_nano;
+ *type = IIO_VAL_INT_PLUS_NANO;
+ *length = ARRAY_SIZE(inv_icm42607_accel_scale_nano) * 2;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)inv_icm42607_accel_odr[5];
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = (ARRAY_SIZE(inv_icm42607_accel_odr) - 5) * 2;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ ret = inv_icm42607_accel_write_scale(indio_dev, val, val2);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return inv_icm42607_accel_write_odr(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int inv_icm42607_accel_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info inv_icm42607_accel_info = {
+ .read_raw = inv_icm42607_accel_read_raw,
+ .read_avail = inv_icm42607_accel_read_avail,
+ .write_raw = inv_icm42607_accel_write_raw,
+ .write_raw_get_fmt = inv_icm42607_accel_write_raw_get_fmt,
+};
+
+struct iio_dev *inv_icm42607_accel_init(struct inv_icm42607_state *st)
+{
+ struct device *dev = regmap_get_device(st->map);
+ struct inv_icm42607_sensor_state *accel_st;
+ struct iio_dev *indio_dev;
+ const char *name;
+ int ret;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->hw->name);
+ if (!name)
+ return ERR_PTR(-ENOMEM);
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st));
+ if (!indio_dev)
+ return ERR_PTR(-ENOMEM);
+ accel_st = iio_priv(indio_dev);
+
+ accel_st->power_mode = INV_ICM42607_SENSOR_MODE_LOW_NOISE;
+ accel_st->filter = INV_ICM42607_FILTER_BW_73HZ;
+
+ iio_device_set_drvdata(indio_dev, st);
+ indio_dev->name = name;
+ indio_dev->info = &inv_icm42607_accel_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = inv_icm42607_accel_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm42607_accel_channels);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return indio_dev;
+}
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index ad23386a37b7..26cedec0c97c 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -19,6 +19,7 @@
#include <linux/time.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include "inv_icm42607.h"
@@ -106,6 +107,15 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+const struct iio_mount_matrix *
+inv_icm42607_get_mount_matrix(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+
+ return &st->orientation;
+}
+
static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
enum inv_icm42607_sensor_mode gyro,
enum inv_icm42607_sensor_mode accel)
@@ -171,6 +181,160 @@ static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
return 0;
}
+/*
+ * Sanity test between old and new config values, and note if we need
+ * to update register config0 or config1.
+ */
+static void inv_icm42607_update_config(struct inv_icm42607_sensor_conf *conf,
+ struct inv_icm42607_sensor_conf *oldconf,
+ bool *config0, bool *config1)
+{
+ *config0 = false;
+ *config1 = false;
+
+ if (conf->mode < 0)
+ conf->mode = oldconf->mode;
+ if (conf->fs < 0)
+ conf->fs = oldconf->fs;
+ if (conf->odr < 0)
+ conf->odr = oldconf->odr;
+ if (conf->filter < 0)
+ conf->filter = oldconf->filter;
+
+ if (conf->fs != oldconf->fs || conf->odr != oldconf->odr)
+ *config0 = true;
+
+ if (conf->filter != oldconf->filter)
+ *config1 = true;
+}
+
+int inv_icm42607_set_sensor_conf(struct inv_icm42607_state *st,
+ struct inv_icm42607_sensor_conf *conf,
+ enum iio_chan_type chan_type)
+{
+ struct inv_icm42607_sensor_conf *oldconf;
+ bool config0, config1;
+ unsigned int val;
+ int ret;
+
+ switch (chan_type) {
+ case IIO_ACCEL:
+ oldconf = &st->conf.accel;
+ break;
+ case IIO_ANGL_VEL:
+ oldconf = &st->conf.gyro;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ inv_icm42607_update_config(conf, oldconf, &config0, &config1);
+
+ if (config0) {
+ if (chan_type == IIO_ANGL_VEL) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ }
+ if (ret)
+ return ret;
+
+ oldconf->fs = conf->fs;
+ oldconf->odr = conf->odr;
+ }
+
+ if (config1) {
+ if (chan_type == IIO_ANGL_VEL) {
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ } else {
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK,
+ conf->filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ }
+ if (ret)
+ return ret;
+
+ oldconf->filter = conf->filter;
+ }
+
+ if (chan_type == IIO_ANGL_VEL)
+ return inv_icm42607_set_pwr_mgmt0(st, conf->mode, st->conf.accel.mode);
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode, conf->mode);
+}
+
+int inv_icm42607_read_sensor(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ s16 *val)
+{
+ struct inv_icm42607_sensor_conf conf = INV_ICM42607_SENSOR_CONF_INIT;
+ struct inv_icm42607_state *st = iio_device_get_drvdata(indio_dev);
+ struct inv_icm42607_sensor_state *sensor_st = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(st->map);
+ unsigned int reg;
+ u8 data[2];
+ int ret;
+
+ if ((chan->type != IIO_ANGL_VEL) && (chan->type != IIO_ACCEL))
+ return -EINVAL;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_X1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_X1;
+ break;
+ case IIO_MOD_Y:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_Y1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_Y1;
+ break;
+ case IIO_MOD_Z:
+ if (chan->type == IIO_ANGL_VEL)
+ reg = INV_ICM42607_REG_GYRO_DATA_Z1;
+ else
+ reg = INV_ICM42607_REG_ACCEL_DATA_Z1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(dev, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ guard(mutex)(&st->lock);
+
+ /* enable sensor */
+ conf.mode = sensor_st->power_mode;
+ conf.filter = sensor_st->filter;
+ ret = inv_icm42607_set_sensor_conf(st, &conf, chan->type);
+ if (ret)
+ return ret;
+
+ /* read sensor register data */
+ ret = regmap_bulk_read(st->map, reg, data, sizeof(data));
+ if (ret)
+ return ret;
+
+ *val = get_unaligned_be16(data);
+ if (*val == INV_ICM42607_DATA_INVALID)
+ return -EINVAL;
+
+ return 0;
+}
+
static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -365,6 +529,11 @@ int inv_icm42607_core_probe(struct regmap *regmap,
pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
pm_runtime_use_autosuspend(dev);
+ /* Initialize IIO device for Accel */
+ st->indio_accel = inv_icm42607_accel_init(st);
+ if (IS_ERR(st->indio_accel))
+ return PTR_ERR(st->indio_accel);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
--
2.43.0
^ permalink raw reply related
* [PATCH V14 5/9] iio: imu: inv_icm42607: Add PM support for icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add power management support for the ICM42607 device driver.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 17 ++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 146 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 1 +
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 1 +
4 files changed, 165 insertions(+)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 88d35323bade..219cfcf7ac99 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -10,6 +10,7 @@
#include <linux/bits.h>
#include <linux/iio/iio.h>
#include <linux/mutex.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/time.h>
@@ -118,12 +119,19 @@ struct inv_icm42607_hw {
u8 whoami;
};
+struct inv_icm42607_suspended {
+ enum inv_icm42607_sensor_mode gyro;
+ enum inv_icm42607_sensor_mode accel;
+};
+
/**
* struct inv_icm42607_state - driver state variables
* @hw: Hardware specific data.
* @lock: lock for serializing multiple registers access.
* @map: regmap pointer.
* @vddio_supply: I/O voltage regulator for the chip.
+ * @vddio_en: I/O voltage status for runtime PM.
+ * @suspended: suspended sensors configuration.
* @conf: chip sensors configurations.
* @orientation: sensor chip orientation relative to main hardware.
*/
@@ -132,6 +140,8 @@ struct inv_icm42607_state {
struct mutex lock;
struct regmap *map;
struct regulator *vddio_supply;
+ bool vddio_en;
+ struct inv_icm42607_suspended suspended;
struct inv_icm42607_conf conf;
struct iio_mount_matrix orientation;
};
@@ -351,11 +361,18 @@ struct inv_icm42607_state {
#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC)
#define INV_ICM42607_TEMP_STARTUP_TIME_US 77
+/*
+ * Suspend delay assumed from other icm42600 series device, not
+ * documented in datasheet.
+ */
+#define INV_ICM42607_SUSPEND_DELAY_MS 2000
+
typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
extern const struct regmap_config inv_icm42607_regmap_config;
extern const struct inv_icm42607_hw inv_icm42607_hw_data;
extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+extern const struct dev_pm_ops inv_icm42607_pm_ops;
int inv_icm42607_core_probe(struct regmap *regmap,
const struct inv_icm42607_hw *hw,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
index 9b82a0499d35..ad23386a37b7 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -4,16 +4,20 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/dev_printk.h>
#include <linux/device/devres.h>
#include <linux/err.h>
#include <linux/iio/iio.h>
+#include <linux/ktime.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/time.h>
+#include <linux/timekeeping.h>
#include <linux/types.h>
#include "inv_icm42607.h"
@@ -102,6 +106,71 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode gyro,
+ enum inv_icm42607_sensor_mode accel)
+{
+ unsigned int oldaccel, oldgyro;
+ unsigned int sleepval_us = 0;
+ unsigned int val;
+ s64 disable_wait;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
+ if (ret)
+ return ret;
+
+ oldaccel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
+ oldgyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
+
+ if (gyro == oldgyro && accel == oldaccel)
+ return 0;
+
+ /*
+ * Datasheet on page 14.26 says we need to ensure the gyro sensor is on
+ * for a minimum of 45ms. So if we transition from an on state to an
+ * off state make sure at least 45ms have passed before power off and
+ * wait if it hasn't.
+ */
+ if (!gyro && oldgyro) {
+ disable_wait = ktime_us_delta(st->conf.gyro_stop,
+ ktime_get_real());
+ disable_wait = clamp(disable_wait, 0,
+ INV_ICM42607_GYRO_STOP_TIME_US);
+ fsleep(disable_wait);
+ }
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, accel);
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ /*
+ * If a state change occurs from off to on, sleep for the startup
+ * time of the sensor, unless a sleep_ms is specified. Since more
+ * than one sensor can be transitioned from off to on, select the
+ * maximum time from each of the sensors changing from off to on.
+ * The startup time for the temp sensor is considerably smaller
+ * than the startup time for the other sensors and one or more are
+ * required to be on for the temp sensor to function, so any start
+ * delay should be enough.
+ */
+ if (accel && !oldaccel)
+ sleepval_us = max(sleepval_us, INV_ICM42607_ACCEL_STARTUP_TIME_US);
+
+ if (gyro && !oldgyro) {
+ sleepval_us = max(sleepval_us, INV_ICM42607_GYRO_STARTUP_TIME_US);
+ /* Track the earliest we can turn off the gyroscope. */
+ st->conf.gyro_stop = ktime_add_us(ktime_get_real(),
+ INV_ICM42607_GYRO_STOP_TIME_US);
+ }
+
+ fsleep(sleepval_us);
+
+ return 0;
+}
+
static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -214,12 +283,17 @@ static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
{
int ret;
+ if (st->vddio_en)
+ return 0;
+
ret = regulator_enable(st->vddio_supply);
if (ret)
return ret;
fsleep(INV_ICM42607_POWER_UP_TIME_US);
+ st->vddio_en = true;
+
return 0;
}
@@ -227,7 +301,12 @@ static void inv_icm42607_disable_vddio_reg(void *_data)
{
struct inv_icm42607_state *st = _data;
+ if (!st->vddio_en)
+ return;
+
regulator_disable(st->vddio_supply);
+
+ st->vddio_en = false;
}
int inv_icm42607_core_probe(struct regmap *regmap,
@@ -242,6 +321,8 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (!st)
return -ENOMEM;
+ dev_set_drvdata(dev, st);
+
ret = devm_mutex_init(dev, &st->lock);
if (ret)
return ret;
@@ -277,10 +358,75 @@ int inv_icm42607_core_probe(struct regmap *regmap,
if (ret)
return ret;
+ ret = devm_pm_runtime_set_active_enabled(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, INV_ICM42607_SUSPEND_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
return 0;
}
EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+static int inv_icm42607_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ inv_icm42607_disable_vddio_reg(st);
+
+ return 0;
+}
+
+static int inv_icm42607_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+ int ret;
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int inv_icm42607_runtime_suspend(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ /* save sensors state */
+ st->suspended.gyro = st->conf.gyro.mode;
+ st->suspended.accel = st->conf.accel.mode;
+
+ return inv_icm42607_set_pwr_mgmt0(st, INV_ICM42607_SENSOR_MODE_OFF,
+ INV_ICM42607_SENSOR_MODE_OFF);
+}
+
+static int inv_icm42607_runtime_resume(struct device *dev)
+{
+ struct inv_icm42607_state *st = dev_get_drvdata(dev);
+
+ guard(mutex)(&st->lock);
+
+ /* restore sensors state */
+ return inv_icm42607_set_pwr_mgmt0(st, st->suspended.gyro,
+ st->suspended.accel);
+}
+
+EXPORT_NS_GPL_DEV_PM_OPS(inv_icm42607_pm_ops, IIO_ICM42607) = {
+ SYSTEM_SLEEP_PM_OPS(inv_icm42607_suspend, inv_icm42607_resume)
+ RUNTIME_PM_OPS(inv_icm42607_runtime_suspend,
+ inv_icm42607_runtime_resume,
+ NULL)
+};
+
MODULE_AUTHOR("InvenSense, Inc.");
MODULE_DESCRIPTION("InvenSense ICM-42607 device driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
index bde931752eb8..aed04be28528 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -85,6 +85,7 @@ static struct i2c_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-i2c",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_id,
.probe = inv_icm42607_probe,
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
index 6072ed7adc86..137cd34582d8 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -95,6 +95,7 @@ static struct spi_driver inv_icm42607_driver = {
.driver = {
.name = "inv-icm42607-spi",
.of_match_table = inv_icm42607_of_matches,
+ .pm = pm_ptr(&inv_icm42607_pm_ops),
},
.id_table = inv_icm42607_spi_id_table,
.probe = inv_icm42607_probe,
--
2.43.0
^ permalink raw reply related
* [PATCH V14 4/9] iio: imu: inv_icm42607: Add SPI For icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add SPI driver support for InvenSense ICM-42607 devices.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/inv_icm42607/Kconfig | 12 ++
drivers/iio/imu/inv_icm42607/Makefile | 3 +
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 107 ++++++++++++++++++
3 files changed, 122 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig
index 083c212087ab..23f461f57afc 100644
--- a/drivers/iio/imu/inv_icm42607/Kconfig
+++ b/drivers/iio/imu/inv_icm42607/Kconfig
@@ -16,3 +16,15 @@ config INV_ICM42607_I2C
This driver can be built as a module. The module will be called
inv-icm42607-i2c.
+
+config INV_ICM42607_SPI
+ tristate "InvenSense ICM-42607 SPI driver"
+ depends on SPI_MASTER
+ select INV_ICM42607
+ select REGMAP_SPI
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over SPI.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-spi.
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
index 32046e2727d7..be109102e203 100644
--- a/drivers/iio/imu/inv_icm42607/Makefile
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -5,3 +5,6 @@ inv-icm42607-y += inv_icm42607_core.o
obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
inv-icm42607-i2c-y += inv_icm42607_i2c.o
+
+obj-$(CONFIG_INV_ICM42607_SPI) += inv-icm42607-spi.o
+inv-icm42607-spi-y += inv_icm42607_spi.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
new file mode 100644
index 000000000000..6072ed7adc86
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_spi_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ /* Only support 4-wire mode for now. */
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_DEVICE_CONFIG,
+ INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE);
+ if (ret)
+ return ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG3_SPI_MASK,
+ INV_ICM42607_SLEW_RATE_2NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG3,
+ INV_ICM42607_DRIVE_CONFIG3_SPI_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS);
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ val);
+}
+
+static int inv_icm42607_probe(struct spi_device *spi)
+{
+ const struct inv_icm42607_hw *hw;
+ struct device *dev = &spi->dev;
+ struct regmap *regmap;
+
+ hw = spi_get_device_match_data(spi);
+ if (!hw)
+ return dev_err_probe(dev, -ENODEV, "Failed to get SPI data\n");
+
+ if (spi->mode & SPI_3WIRE)
+ return dev_err_probe(dev, -ENODEV, "SPI 3-wire mode not supported\n");
+
+ regmap = devm_regmap_init_spi(spi, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register spi regmap\n");
+
+ return inv_icm42607_core_probe(regmap, hw,
+ inv_icm42607_spi_bus_setup);
+}
+
+static const struct spi_device_id inv_icm42607_spi_id_table[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data,
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, inv_icm42607_spi_id_table);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ },
+ {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct spi_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-spi",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_spi_id_table,
+ .probe = inv_icm42607_probe,
+};
+module_spi_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 SPI driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
--
2.43.0
^ permalink raw reply related
* [PATCH V14 3/9] iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add the core component of a new inv_icm42607 driver. This includes
a few setup functions and the full register definition in the
header file, as well as the bits necessary to compile and probe the
device when used on an i2c bus.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
---
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm42607/Kconfig | 18 +
drivers/iio/imu/inv_icm42607/Makefile | 7 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 364 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 286 ++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 97 +++++
7 files changed, 774 insertions(+)
create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 7e0181c27bb6..8bab4616be20 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -109,6 +109,7 @@ config KMX61
be called kmx61.
source "drivers/iio/imu/inv_icm42600/Kconfig"
+source "drivers/iio/imu/inv_icm42607/Kconfig"
source "drivers/iio/imu/inv_icm45600/Kconfig"
source "drivers/iio/imu/inv_mpu6050/Kconfig"
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 13fb7846e9c9..3268dc2371ae 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
obj-$(CONFIG_FXOS8700_SPI) += fxos8700_spi.o
obj-y += inv_icm42600/
+obj-y += inv_icm42607/
obj-y += inv_icm45600/
obj-y += inv_mpu6050/
diff --git a/drivers/iio/imu/inv_icm42607/Kconfig b/drivers/iio/imu/inv_icm42607/Kconfig
new file mode 100644
index 000000000000..083c212087ab
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+config INV_ICM42607
+ tristate
+ select IIO_BUFFER
+ select IIO_INV_SENSORS_TIMESTAMP
+
+config INV_ICM42607_I2C
+ tristate "InvenSense ICM-42607 I2C driver"
+ depends on I2C
+ select INV_ICM42607
+ select REGMAP_I2C
+ help
+ This driver supports the InvenSense ICM-42607 motion tracking
+ device over I2C.
+
+ This driver can be built as a module. The module will be called
+ inv-icm42607-i2c.
diff --git a/drivers/iio/imu/inv_icm42607/Makefile b/drivers/iio/imu/inv_icm42607/Makefile
new file mode 100644
index 000000000000..32046e2727d7
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+obj-$(CONFIG_INV_ICM42607) += inv-icm42607.o
+inv-icm42607-y += inv_icm42607_core.o
+
+obj-$(CONFIG_INV_ICM42607_I2C) += inv-icm42607-i2c.o
+inv-icm42607-i2c-y += inv_icm42607_i2c.o
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
new file mode 100644
index 000000000000..88d35323bade
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -0,0 +1,364 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#ifndef INV_ICM42607_H_
+#define INV_ICM42607_H_
+
+#include <asm/byteorder.h>
+#include <linux/bits.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+/*
+ * Serial bus slew rates. Rates are expressed as range between the two
+ * values with the midpoint as the typical rate. For the final value of
+ * 2ns, 2ns is considered the max value with no expressed minimum or
+ * typical value.
+ */
+enum inv_icm42607_slew_rate {
+ INV_ICM42607_SLEW_RATE_20_60NS = 0,
+ INV_ICM42607_SLEW_RATE_12_36NS = 1,
+ INV_ICM42607_SLEW_RATE_6_19NS = 2,
+ INV_ICM42607_SLEW_RATE_4_14NS = 3,
+ INV_ICM42607_SLEW_RATE_2_6NS = 4,
+ INV_ICM42607_SLEW_RATE_2NS = 5,
+ INV_ICM42607_SLEW_RATE_NB
+};
+
+enum inv_icm42607_sensor_mode {
+ INV_ICM42607_SENSOR_MODE_OFF = 0,
+ INV_ICM42607_SENSOR_MODE_STANDBY = 1,
+ INV_ICM42607_SENSOR_MODE_LOW_POWER = 2,
+ INV_ICM42607_SENSOR_MODE_LOW_NOISE = 3,
+ INV_ICM42607_SENSOR_MODE_NB
+};
+
+/* gyroscope fullscale values */
+enum inv_icm42607_gyro_fs {
+ INV_ICM42607_GYRO_FS_2000DPS = 0,
+ INV_ICM42607_GYRO_FS_1000DPS = 1,
+ INV_ICM42607_GYRO_FS_500DPS = 2,
+ INV_ICM42607_GYRO_FS_250DPS = 3,
+ INV_ICM42607_GYRO_FS_NB
+};
+
+/* accelerometer fullscale values */
+enum inv_icm42607_accel_fs {
+ INV_ICM42607_ACCEL_FS_16G = 0,
+ INV_ICM42607_ACCEL_FS_8G = 1,
+ INV_ICM42607_ACCEL_FS_4G = 2,
+ INV_ICM42607_ACCEL_FS_2G = 3,
+ INV_ICM42607_ACCEL_FS_NB
+};
+
+/* ODR values - Note Gyro does not support ODR less than 12.5Hz */
+enum inv_icm42607_odr {
+ INV_ICM42607_ODR_1600HZ = 5,
+ INV_ICM42607_ODR_800HZ = 6,
+ INV_ICM42607_ODR_400HZ = 7,
+ INV_ICM42607_ODR_200HZ = 8,
+ INV_ICM42607_ODR_100HZ = 9,
+ INV_ICM42607_ODR_50HZ = 10,
+ INV_ICM42607_ODR_25HZ = 11,
+ INV_ICM42607_ODR_12_5HZ = 12,
+ INV_ICM42607_ODR_6_25HZ_LP = 13,
+ INV_ICM42607_ODR_3_125HZ_LP = 14,
+ INV_ICM42607_ODR_1_5625HZ_LP = 15,
+ INV_ICM42607_ODR_NB
+};
+
+/* Low-Noise mode sensor data filter (bandwidth) */
+enum inv_icm42607_filter_bw {
+ INV_ICM42607_FILTER_BYPASS = 0,
+ INV_ICM42607_FILTER_BW_180HZ = 1,
+ INV_ICM42607_FILTER_BW_121HZ = 2,
+ INV_ICM42607_FILTER_BW_73HZ = 3,
+ INV_ICM42607_FILTER_BW_53HZ = 4,
+ INV_ICM42607_FILTER_BW_34HZ = 5,
+ INV_ICM42607_FILTER_BW_25HZ = 6,
+ INV_ICM42607_FILTER_BW_16HZ = 7,
+ INV_ICM42607_FILTER_BW_NB
+};
+
+/* Temperature sensor data filter (bandwidth) */
+enum inv_icm42607_temp_filter_bw {
+ INV_ICM42607_TEMP_FILTER_BYPASS = 0,
+ INV_ICM42607_TEMP_FILTER_BW_180HZ = 1,
+ INV_ICM42607_TEMP_FILTER_BW_72HZ = 2,
+ INV_ICM42607_TEMP_FILTER_BW_34HZ = 3,
+ INV_ICM42607_TEMP_FILTER_BW_16HZ = 4,
+ INV_ICM42607_TEMP_FILTER_BW_8HZ = 5,
+ INV_ICM42607_TEMP_FILTER_BW_4HZ = 6,
+ /* value 7 also corresponds to 4Hz */
+};
+
+/* Signed so that negative values can signify an invalid condition. */
+struct inv_icm42607_sensor_conf {
+ int mode;
+ int fs;
+ int odr;
+ int filter;
+};
+
+struct inv_icm42607_conf {
+ struct inv_icm42607_sensor_conf gyro;
+ struct inv_icm42607_sensor_conf accel;
+ ktime_t gyro_stop; /* earliest time to stop the gyro */
+};
+
+struct inv_icm42607_hw {
+ const char *name;
+ const struct inv_icm42607_conf *conf;
+ u8 whoami;
+};
+
+/**
+ * struct inv_icm42607_state - driver state variables
+ * @hw: Hardware specific data.
+ * @lock: lock for serializing multiple registers access.
+ * @map: regmap pointer.
+ * @vddio_supply: I/O voltage regulator for the chip.
+ * @conf: chip sensors configurations.
+ * @orientation: sensor chip orientation relative to main hardware.
+ */
+struct inv_icm42607_state {
+ const struct inv_icm42607_hw *hw;
+ struct mutex lock;
+ struct regmap *map;
+ struct regulator *vddio_supply;
+ struct inv_icm42607_conf conf;
+ struct iio_mount_matrix orientation;
+};
+
+/* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */
+
+/* Register Map for User Bank 0 */
+#define INV_ICM42607_REG_MCLK_RDY 0x00
+
+#define INV_ICM42607_REG_DEVICE_CONFIG 0x01
+#define INV_ICM42607_DEVICE_CONFIG_SPI_AP_4WIRE BIT(2)
+#define INV_ICM42607_DEVICE_CONFIG_SPI_MODE BIT(0)
+
+#define INV_ICM42607_REG_SIGNAL_PATH_RESET 0x02
+#define INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET BIT(4)
+#define INV_ICM42607_SIGNAL_PATH_RESET_FIFO_FLUSH BIT(2)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG1 0x03
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_DDR_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG1_I3C_SDR_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG2 0x04
+#define INV_ICM42607_DRIVE_CONFIG2_I2C_MASK GENMASK(5, 3)
+#define INV_ICM42607_DRIVE_CONFIG2_ALL_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_DRIVE_CONFIG3 0x05
+#define INV_ICM42607_DRIVE_CONFIG3_SPI_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_CONFIG 0x06
+#define INV_ICM42607_INT_CONFIG_INT2_LATCHED BIT(5)
+#define INV_ICM42607_INT_CONFIG_INT2_PUSH_PULL BIT(4)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_HIGH BIT(3)
+#define INV_ICM42607_INT_CONFIG_INT2_ACTIVE_LOW 0x00
+#define INV_ICM42607_INT_CONFIG_INT1_LATCHED BIT(2)
+#define INV_ICM42607_INT_CONFIG_INT1_PUSH_PULL BIT(1)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_HIGH BIT(0)
+#define INV_ICM42607_INT_CONFIG_INT1_ACTIVE_LOW 0x00
+
+/* all sensor data are 16 bits (2 registers wide) in big-endian */
+#define INV_ICM42607_REG_TEMP_DATA1 0x09
+#define INV_ICM42607_REG_TEMP_DATA0 0x0A
+#define INV_ICM42607_REG_ACCEL_DATA_X1 0x0B
+#define INV_ICM42607_REG_ACCEL_DATA_X0 0x0C
+#define INV_ICM42607_REG_ACCEL_DATA_Y1 0x0D
+#define INV_ICM42607_REG_ACCEL_DATA_Y0 0x0E
+#define INV_ICM42607_REG_ACCEL_DATA_Z1 0x0F
+#define INV_ICM42607_REG_ACCEL_DATA_Z0 0x10
+#define INV_ICM42607_REG_GYRO_DATA_X1 0x11
+#define INV_ICM42607_REG_GYRO_DATA_X0 0x12
+#define INV_ICM42607_REG_GYRO_DATA_Y1 0x13
+#define INV_ICM42607_REG_GYRO_DATA_Y0 0x14
+#define INV_ICM42607_REG_GYRO_DATA_Z1 0x15
+#define INV_ICM42607_REG_GYRO_DATA_Z0 0x16
+#define INV_ICM42607_DATA_INVALID -32768
+
+#define INV_ICM42607_REG_TMST_FSYNCH 0x17
+#define INV_ICM42607_REG_TMST_FSYNCL 0x18
+
+/* APEX Data Registers */
+#define INV_ICM42607_REG_APEX_DATA0 0x31
+#define INV_ICM42607_REG_APEX_DATA1 0x32
+#define INV_ICM42607_REG_APEX_DATA2 0x33
+#define INV_ICM42607_REG_APEX_DATA3 0x34
+#define INV_ICM42607_REG_APEX_DATA4 0x1D
+#define INV_ICM42607_REG_APEX_DATA5 0x1E
+
+#define INV_ICM42607_REG_PWR_MGMT0 0x1F
+#define INV_ICM42607_PWR_MGMT0_ACCEL_LP_CLK_SEL BIT(7)
+#define INV_ICM42607_PWR_MGMT0_IDLE BIT(4)
+#define INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK GENMASK(3, 2)
+#define INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_GYRO_CONFIG0 0x20
+#define INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_GYRO_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG0 0x21
+#define INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK GENMASK(6, 5)
+#define INV_ICM42607_ACCEL_CONFIG0_ODR_MASK GENMASK(3, 0)
+
+#define INV_ICM42607_REG_TEMP_CONFIG0 0x22
+#define INV_ICM42607_TEMP_CONFIG0_FILTER_MASK GENMASK(6, 4)
+
+#define INV_ICM42607_REG_GYRO_CONFIG1 0x23
+#define INV_ICM42607_GYRO_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_ACCEL_CONFIG1 0x24
+#define INV_ICM42607_ACCEL_CONFIG1_AVG_MASK GENMASK(6, 4)
+#define INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK GENMASK(2, 0)
+
+#define INV_ICM42607_REG_APEX_CONFIG0 0x25
+#define INV_ICM42607_APEX_CONFIG0_DMP_POWER_SAVE_EN BIT(3)
+#define INV_ICM42607_APEX_CONFIG0_DMP_INIT_EN BIT(2)
+#define INV_ICM42607_APEX_CONFIG0_DMP_MEM_RESET_EN BIT(0)
+
+#define INV_ICM42607_REG_APEX_CONFIG1 0x26
+#define INV_ICM42607_APEX_CONFIG1_SMD_ENABLE BIT(6)
+#define INV_ICM42607_APEX_CONFIG1_FF_ENABLE BIT(5)
+#define INV_ICM42607_APEX_CONFIG1_TILT_ENABLE BIT(4)
+#define INV_ICM42607_APEX_CONFIG1_PED_ENABLE BIT(3)
+#define INV_ICM42607_APEX_CONFIG1_DMP_ODR_MASK GENMASK(1, 0)
+
+#define INV_ICM42607_REG_WOM_CONFIG 0x27
+#define INV_ICM42607_WOM_CONFIG_INT_DUR_MASK GENMASK(4, 3)
+#define INV_ICM42607_WOM_CONFIG_INT_MODE BIT(2)
+#define INV_ICM42607_WOM_CONFIG_MODE BIT(1)
+#define INV_ICM42607_WOM_CONFIG_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG1 0x28
+#define INV_ICM42607_FIFO_CONFIG1_MODE BIT(1)
+#define INV_ICM42607_FIFO_CONFIG1_BYPASS BIT(0)
+
+#define INV_ICM42607_REG_FIFO_CONFIG2 0x29
+#define INV_ICM42607_REG_FIFO_CONFIG3 0x2A
+#define INV_ICM42607_FIFO_WATERMARK_VAL(_wm) \
+ cpu_to_le16((_wm) & GENMASK(11, 0))
+/* FIFO is 2048 bytes, let 12 samples for reading latency */
+#define INV_ICM42607_FIFO_WATERMARK_MAX (2048 - 12 * 16)
+#define INV_ICM42607_FIFO_1SENSOR_PACKET_SIZE 8
+#define INV_ICM42607_FIFO_2SENSORS_PACKET_SIZE 16
+
+#define INV_ICM42607_REG_INT_SOURCE0 0x2B
+#define INV_ICM42607_INT_SOURCE0_ST_INT1_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE0_FSYNC_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE0_PLL_RDY_INT1_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE0_RESET_DONE_INT1_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE0_DRDY_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE0_FIFO_THS_INT1_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE0_FIFO_FULL_INT1_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE0_AGC_RDY_INT1_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE1 0x2C
+#define INV_ICM42607_INT_SOURCE1_I3C_ERROR_INT1_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE1_SMD_INT1_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE1_WOM_INT1_EN GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_SOURCE3 0x2D
+#define INV_ICM42607_INT_SOURCE3_ST_INT2_EN BIT(7)
+#define INV_ICM42607_INT_SOURCE3_FSYNC_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE3_PLL_RDY_INT2_EN BIT(5)
+#define INV_ICM42607_INT_SOURCE3_RESET_DONE_INT2_EN BIT(4)
+#define INV_ICM42607_INT_SOURCE3_DRDY_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE3_FIFO_THS_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE3_FIFO_FULL_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE3_AGC_RDY_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_INT_SOURCE4 0x2E
+#define INV_ICM42607_INT_SOURCE4_I3C_ERROR_INT2_EN BIT(6)
+#define INV_ICM42607_INT_SOURCE4_SMD_INT2_EN BIT(3)
+#define INV_ICM42607_INT_SOURCE4_WOM_Z_INT2_EN BIT(2)
+#define INV_ICM42607_INT_SOURCE4_WOM_Y_INT2_EN BIT(1)
+#define INV_ICM42607_INT_SOURCE4_WOM_X_INT2_EN BIT(0)
+
+#define INV_ICM42607_REG_FIFO_LOST_PKT0 0x2F
+#define INV_ICM42607_REG_FIFO_LOST_PKT1 0x30
+
+#define INV_ICM42607_REG_INTF_CONFIG0 0x35
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_FORMAT BIT(6)
+#define INV_ICM42607_INTF_CONFIG0_FIFO_COUNT_ENDIAN BIT(5)
+#define INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN BIT(4)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS 2
+#define INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_I2C_DIS 3
+
+#define INV_ICM42607_REG_INTF_CONFIG1 0x36
+#define INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN BIT(3)
+#define INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN BIT(2)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK GENMASK(1, 0)
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_INT 0
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL 1
+#define INV_ICM42607_INTF_CONFIG1_CLKSEL_OFF 2
+
+#define INV_ICM42607_REG_INT_STATUS_DRDY 0x39
+#define INV_ICM42607_INT_STATUS_DRDY_DATA_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS 0x3A
+#define INV_ICM42607_INT_STATUS_ST BIT(7)
+#define INV_ICM42607_INT_STATUS_FSYNC BIT(6)
+#define INV_ICM42607_INT_STATUS_PLL_RDY BIT(5)
+#define INV_ICM42607_INT_STATUS_RESET_DONE BIT(4)
+#define INV_ICM42607_INT_STATUS_FIFO_THS BIT(2)
+#define INV_ICM42607_INT_STATUS_FIFO_FULL BIT(1)
+#define INV_ICM42607_INT_STATUS_AGC_RDY BIT(0)
+
+#define INV_ICM42607_REG_INT_STATUS2 0x3B
+#define INV_ICM42607_INT_STATUS2_SMD BIT(3)
+#define INV_ICM42607_INT_STATUS2_WOM_INT GENMASK(2, 0)
+
+#define INV_ICM42607_REG_INT_STATUS3 0x3C
+#define INV_ICM42607_INT_STATUS3_STEP_DET BIT(5)
+#define INV_ICM42607_INT_STATUS3_STEP_CNT_OVF BIT(4)
+#define INV_ICM42607_INT_STATUS3_TILT_DET BIT(3)
+#define INV_ICM42607_INT_STATUS3_FF_DET BIT(2)
+
+/*
+ * FIFO access registers
+ * FIFO count is 16 bits (2 registers) big-endian
+ * FIFO data is a continuous read register to read FIFO content
+ */
+#define INV_ICM42607_REG_FIFO_COUNTH 0x3D
+#define INV_ICM42607_REG_FIFO_COUNTL 0x3E
+#define INV_ICM42607_REG_FIFO_DATA 0x3F
+
+#define INV_ICM42607_REG_WHOAMI 0x75
+#define INV_ICM42607P_WHOAMI 0x60
+#define INV_ICM42607_WHOAMI 0x67
+
+/*
+ * Timings as listed in section 3 of datasheet, all values listed in datasheet
+ * in ms except temp startup time... setting all values in us and using
+ * USEC_PER_MSEC to convert from values displayed in datasheet.
+ */
+#define INV_ICM42607_POWER_UP_TIME_US (100 * USEC_PER_MSEC)
+#define INV_ICM42607_RESET_TIME_US (1 * USEC_PER_MSEC)
+#define INV_ICM42607_ACCEL_STARTUP_TIME_US (10 * USEC_PER_MSEC)
+#define INV_ICM42607_GYRO_STARTUP_TIME_US (30 * USEC_PER_MSEC)
+#define INV_ICM42607_GYRO_STOP_TIME_US (45 * USEC_PER_MSEC)
+#define INV_ICM42607_TEMP_STARTUP_TIME_US 77
+
+typedef int (*inv_icm42607_bus_setup)(struct inv_icm42607_state *);
+
+extern const struct regmap_config inv_icm42607_regmap_config;
+extern const struct inv_icm42607_hw inv_icm42607_hw_data;
+extern const struct inv_icm42607_hw inv_icm42607p_hw_data;
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup bus_setup);
+
+#endif
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
new file mode 100644
index 000000000000..9b82a0499d35
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -0,0 +1,286 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device/devres.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include "inv_icm42607.h"
+
+static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY ... INV_ICM42607_REG_INT_CONFIG:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_TMST_FSYNCL:
+ case INV_ICM42607_REG_APEX_DATA4 ... INV_ICM42607_REG_INTF_CONFIG1:
+ case INV_ICM42607_REG_INT_STATUS_DRDY ... INV_ICM42607_REG_FIFO_DATA:
+ case INV_ICM42607_REG_WHOAMI:
+ return true;
+ }
+
+ return false;
+}
+
+static bool inv_icm42607_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_DEVICE_CONFIG ... INV_ICM42607_REG_INT_CONFIG:
+ case INV_ICM42607_REG_PWR_MGMT0 ... INV_ICM42607_REG_INT_SOURCE4:
+ case INV_ICM42607_REG_INTF_CONFIG0 ... INV_ICM42607_REG_INTF_CONFIG1:
+ return true;
+ }
+
+ return false;
+}
+
+static bool inv_icm42607_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case INV_ICM42607_REG_MCLK_RDY:
+ case INV_ICM42607_REG_SIGNAL_PATH_RESET:
+ case INV_ICM42607_REG_TEMP_DATA1 ... INV_ICM42607_REG_APEX_DATA5:
+ case INV_ICM42607_REG_APEX_CONFIG0:
+ case INV_ICM42607_REG_FIFO_CONFIG2 ... INV_ICM42607_REG_FIFO_CONFIG3:
+ case INV_ICM42607_REG_FIFO_LOST_PKT0 ... INV_ICM42607_REG_APEX_DATA3:
+ case INV_ICM42607_REG_INT_STATUS_DRDY:
+ case INV_ICM42607_REG_INT_STATUS ... INV_ICM42607_REG_FIFO_DATA:
+ return true;
+ }
+
+ return false;
+}
+
+const struct regmap_config inv_icm42607_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = inv_icm42607_is_writeable_reg,
+ .readable_reg = inv_icm42607_is_readable_reg,
+ .volatile_reg = inv_icm42607_is_volatile_reg,
+ .max_register = INV_ICM42607_REG_WHOAMI,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_regmap_config, "IIO_ICM42607");
+
+/* chip initial default configuration */
+static const struct inv_icm42607_conf inv_icm42607_default_conf = {
+ .gyro = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_GYRO_FS_1000DPS,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+ .accel = {
+ .mode = INV_ICM42607_SENSOR_MODE_OFF,
+ .fs = INV_ICM42607_ACCEL_FS_4G,
+ .odr = INV_ICM42607_ODR_100HZ,
+ .filter = INV_ICM42607_FILTER_BW_25HZ,
+ },
+};
+
+const struct inv_icm42607_hw inv_icm42607_hw_data = {
+ .whoami = INV_ICM42607_WHOAMI,
+ .name = "icm42607",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_hw_data, "IIO_ICM42607");
+
+const struct inv_icm42607_hw inv_icm42607p_hw_data = {
+ .whoami = INV_ICM42607P_WHOAMI,
+ .name = "icm42607p",
+ .conf = &inv_icm42607_default_conf,
+};
+EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+
+static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st,
+ const struct inv_icm42607_conf *conf)
+{
+ unsigned int val;
+ int ret;
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, conf->gyro.mode);
+ val |= FIELD_PREP(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, conf->accel.mode);
+ ret = regmap_write(st->map, INV_ICM42607_REG_PWR_MGMT0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_FS_SEL_MASK, conf->gyro.fs);
+ val |= FIELD_PREP(INV_ICM42607_GYRO_CONFIG0_ODR_MASK, conf->gyro.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_GYRO_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_FS_SEL_MASK, conf->accel.fs);
+ val |= FIELD_PREP(INV_ICM42607_ACCEL_CONFIG0_ODR_MASK, conf->accel.odr);
+ ret = regmap_write(st->map, INV_ICM42607_REG_ACCEL_CONFIG0, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, conf->gyro.filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_GYRO_CONFIG1,
+ INV_ICM42607_GYRO_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, conf->accel.filter);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_ACCEL_CONFIG1,
+ INV_ICM42607_ACCEL_CONFIG1_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_TEMP_CONFIG0_FILTER_MASK,
+ INV_ICM42607_TEMP_FILTER_BW_34HZ);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_TEMP_CONFIG0,
+ INV_ICM42607_TEMP_CONFIG0_FILTER_MASK, val);
+ if (ret)
+ return ret;
+
+ st->conf = *conf;
+
+ return 0;
+}
+
+static int inv_icm42607_setup(struct inv_icm42607_state *st,
+ inv_icm42607_bus_setup inv_icm42607_bus_setup)
+{
+ const struct device *dev = regmap_get_device(st->map);
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_WHOAMI, &val);
+ if (ret)
+ return ret;
+
+ /* Warn, but don't fail. */
+ if (val != st->hw->whoami)
+ dev_warn(dev, "Unknown whoami %#02x expected %#02x (%s)\n",
+ val, st->hw->whoami, st->hw->name);
+
+ ret = regmap_write(st->map, INV_ICM42607_REG_SIGNAL_PATH_RESET,
+ INV_ICM42607_SIGNAL_PATH_RESET_SOFT_RESET);
+ if (ret)
+ return ret;
+
+ fsleep(1 * USEC_PER_MSEC);
+
+ /*
+ * No polling interval specified in datasheet, so use reset time as
+ * polling interval and 10x reset time as timeout period.
+ */
+ ret = regmap_read_poll_timeout(st->map, INV_ICM42607_REG_INT_STATUS,
+ val, val & INV_ICM42607_INT_STATUS_RESET_DONE,
+ 1 * USEC_PER_MSEC, 10 * USEC_PER_MSEC);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "reset error, reset done bit not set\n");
+
+ /* Sync the regcache again after a reset. */
+ regcache_mark_dirty(st->map);
+ ret = regcache_sync(st->map);
+ if (ret)
+ return ret;
+
+ ret = inv_icm42607_bus_setup(st);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_SENSOR_DATA_ENDIAN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_PLL);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_CLKSEL_MASK,
+ val);
+ if (ret)
+ return ret;
+
+ return inv_icm42607_set_init_conf(st, st->hw->conf);
+}
+
+static int inv_icm42607_enable_vddio_reg(struct inv_icm42607_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vddio_supply);
+ if (ret)
+ return ret;
+
+ fsleep(INV_ICM42607_POWER_UP_TIME_US);
+
+ return 0;
+}
+
+static void inv_icm42607_disable_vddio_reg(void *_data)
+{
+ struct inv_icm42607_state *st = _data;
+
+ regulator_disable(st->vddio_supply);
+}
+
+int inv_icm42607_core_probe(struct regmap *regmap,
+ const struct inv_icm42607_hw *hw,
+ inv_icm42607_bus_setup inv_icm42607_bus_setup)
+{
+ struct device *dev = regmap_get_device(regmap);
+ struct inv_icm42607_state *st;
+ int ret;
+
+ st = devm_kzalloc(dev, sizeof(*st), GFP_KERNEL);
+ if (!st)
+ return -ENOMEM;
+
+ ret = devm_mutex_init(dev, &st->lock);
+ if (ret)
+ return ret;
+
+ st->hw = hw;
+ st->map = regmap;
+
+ ret = iio_read_mount_matrix(dev, &st->orientation);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to retrieve mounting matrix\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get vdd regulator\n");
+
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply))
+ return dev_err_probe(dev, PTR_ERR(st->vddio_supply),
+ "Failed to get vddio regulator\n");
+
+ ret = inv_icm42607_enable_vddio_reg(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, inv_icm42607_disable_vddio_reg, st);
+ if (ret)
+ return ret;
+
+ /* Setup chip registers (includes WHOAMI check, reset check, bus setup) */
+ ret = inv_icm42607_setup(st, inv_icm42607_bus_setup);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(inv_icm42607_core_probe, "IIO_ICM42607");
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 device driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
new file mode 100644
index 000000000000..bde931752eb8
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2026 InvenSense, Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+
+#include "inv_icm42607.h"
+
+static int inv_icm42607_i2c_bus_setup(struct inv_icm42607_state *st)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_clear_bits(st->map, INV_ICM42607_REG_INTF_CONFIG1,
+ INV_ICM42607_INTF_CONFIG1_I3C_DDR_EN |
+ INV_ICM42607_INTF_CONFIG1_I3C_SDR_EN);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_DRIVE_CONFIG2_I2C_MASK,
+ INV_ICM42607_SLEW_RATE_12_36NS);
+ ret = regmap_update_bits(st->map, INV_ICM42607_REG_DRIVE_CONFIG2,
+ INV_ICM42607_DRIVE_CONFIG2_I2C_MASK, val);
+ if (ret)
+ return ret;
+
+ val = FIELD_PREP(INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_SPI_DIS);
+ return regmap_update_bits(st->map, INV_ICM42607_REG_INTF_CONFIG0,
+ INV_ICM42607_INTF_CONFIG0_UI_SIFS_CFG_MASK,
+ val);
+}
+
+static int inv_icm42607_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ const struct inv_icm42607_hw *hw;
+ struct regmap *regmap;
+
+ hw = i2c_get_match_data(client);
+ if (!hw)
+ return dev_err_probe(dev, -ENODEV, "Failed to get i2c data\n");
+
+ regmap = devm_regmap_init_i2c(client, &inv_icm42607_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to register i2c regmap\n");
+
+ return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
+}
+
+static const struct i2c_device_id inv_icm42607_id[] = {
+ {
+ .name = "icm42607",
+ .driver_data = (kernel_ulong_t)&inv_icm42607_hw_data,
+ }, {
+ .name = "icm42607p",
+ .driver_data = (kernel_ulong_t)&inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, inv_icm42607_id);
+
+static const struct of_device_id inv_icm42607_of_matches[] = {
+ {
+ .compatible = "invensense,icm42607",
+ .data = &inv_icm42607_hw_data,
+ }, {
+ .compatible = "invensense,icm42607p",
+ .data = &inv_icm42607p_hw_data,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, inv_icm42607_of_matches);
+
+static struct i2c_driver inv_icm42607_driver = {
+ .driver = {
+ .name = "inv-icm42607-i2c",
+ .of_match_table = inv_icm42607_of_matches,
+ },
+ .id_table = inv_icm42607_id,
+ .probe = inv_icm42607_probe,
+};
+module_i2c_driver(inv_icm42607_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense ICM-42607 I2C driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_ICM42607");
--
2.43.0
^ permalink raw reply related
* [PATCH V14 2/9] dt-bindings: iio: imu: icm42600: Add icm42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan, Krzysztof Kozlowski
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add the ICM42607 and ICM42607P inertial measurement unit.
This device is functionally very similar to the icm42600 series with a
very different register layout. The driver does not require an
interrupt for these specific chip revisions.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../bindings/iio/imu/invensense,icm42600.yaml | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 9b2af104f186..81b6e85decd5 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -30,6 +30,8 @@ properties:
- invensense,icm42600
- invensense,icm42602
- invensense,icm42605
+ - invensense,icm42607
+ - invensense,icm42607p
- invensense,icm42622
- invensense,icm42631
- invensense,icm42686
@@ -67,10 +69,24 @@ properties:
required:
- compatible
- reg
- - interrupts
allOf:
- $ref: /schemas/spi/spi-peripheral-props.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - invensense,icm42600
+ - invensense,icm42602
+ - invensense,icm42605
+ - invensense,icm42622
+ - invensense,icm42631
+ - invensense,icm42686
+ - invensense,icm42688
+ then:
+ required:
+ - interrupts
unevaluatedProperties: false
--
2.43.0
^ permalink raw reply related
* [PATCH V14 1/9] dt-bindings: iio: imu: icm42600: Add mount-matrix
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan, Krzysztof Kozlowski
In-Reply-To: <20260624182350.50467-1-macroalpha82@gmail.com>
From: Chris Morgan <macromorgan@hotmail.com>
Add mount-matrix attribute to schema. This attribute has been supported
since the first revision of this driver, but was not documented.
Signed-off-by: Chris Morgan <macromorgan@hotmail.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../devicetree/bindings/iio/imu/invensense,icm42600.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
index 119e28a833fd..9b2af104f186 100644
--- a/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
+++ b/Documentation/devicetree/bindings/iio/imu/invensense,icm42600.yaml
@@ -53,6 +53,8 @@ properties:
drive-open-drain:
type: boolean
+ mount-matrix: true
+
vdd-supply:
description: Regulator that provides power to the sensor
--
2.43.0
^ permalink raw reply related
* [PATCH V14 0/9] Add Invensense ICM42607
From: Chris Morgan @ 2026-06-24 18:23 UTC (permalink / raw)
To: linux-iio
Cc: andy, nuno.sa, dlechner, jic23, jean-baptiste.maneyrol,
linux-rockchip, devicetree, heiko, conor+dt, krzk+dt, robh,
andriy.shevchenko, Chris Morgan
From: Chris Morgan <macromorgan@hotmail.com>
Add support for the ICM42607 IMU. This sensor shares the same
functionality but a different register layout with the existing
ICM42600.
This driver should work with the ICM42607 and ICM42607P over both I2C
and SPI, however only the ICM42607P over I2C could be tested.
Changes Since V1:
- Instead of creating a new driver, merged with the existing inv_icm42600
driver. This necessitated adding some code to the existing driver to
permit using a different register layout for the same functionality.
- Split changes up a bit more to decrease the size of the individual
patches. Note that patch 0004 is still pretty hefty; if I need to split
further I may need to create some temporary stub functions.
- Used guard() and PM_RUNTIME_ACQUIRE_AUTOSUSPEND() on the new functions
per Jonathan's recommendations.
Changes Since V2:
- Went back to using a new driver on advice from Invensense engineer.
- Further split changes up into smaller chunks of functionality. Note
still that the largest patch is approximately 900 lines, and that while
the driver compiles cleanly at each commit it is not able to drive the
hardware until the commit that adds the Interrupt (as it also adds the
Makefile).
- Change the error to a warning when the devicetree binding does not match
the hardware ID.
- Dropped the ack on the devicetree bindings, as I am creating a new file
(for a new driver) instead of modifying the existing one.
Changes Since V3:
- Numerous small fixes (too many to list here). Thank you to everyone who
provided feedback.
- Split power management additions into an additional commit to break
things up further.
- Consolidated devicetree documentation in existing
invensense,icm42600.yaml file.
- Removed most of the FIELD_PREP from header file to c files to make code
easier to read.
- Changed scale values to 2D arrays for Gyro and Accelerometer.
- Removed IIO_CHAN_INFO_CALIBBIAS attribute.
Changes Since V4:
- Additional numerous small fixes, thank you again for all the feedback.
- Dropped power control API and instead run device in low noise mode.
- Split devicetree bindings into two distinct changes.
- Reordered adding of enums and structs to main header file so that they
are only brought in when needed.
- Stopped using enum for driver data and instead am using pointer to
device specific driver data.
Changes Since V5:
- Corrected use of "dev_warn_probe" to just "dev_warn".
- Fixed some return scenarios which would unconditionally return 0
when an error was present.
- Corrected use of max() to min() for bounds checking. max() was
incorrect.
- Fixed using "st->conf.accel.odr" in the gyroscope function. It
should have been "st->conf.gyro.odr" which it now is.
- Additional small fixes suggested by "sashiko.dev".
- Added a regmap cache. I used the datasheet to try and determine
which registers might change without explicit writes.
Changes Since V6:
- Corrected additional errors identified by sashiko.dev, mostly
fixing potential deadlocks, missing calls for pm runtime, and
potential overflow issues.
Changes Since V7:
- Dropped Wake on Movement patches, since some of the functionality
was only available for a device on which I cannot test.
- Dropped support for SPI 3-Wire mode, since it complicated the
bus setup (and I lack the hardware to test such features anyway).
- Fixed a few additional bugs identified by sashiko.dev bot.
Changes Since V8:
- Added back IRQ dropped accidentally when dropping wake on movement
patches.
- Dropped "Reviewed-By" tag on patch 2 because of substantial changes
made to devicetree binding documentation.
- Additional small fixes as suggested.
Changes Since V9:
- Removed interrupts (and buffers) from the driver. I previously was
unable to detect deadlocks because it turns out my IRQ was not even
wired correctly in my device.
- Updated devicetree binding commits to make interrupts optional for
users of the icm42607 driver.
Changes Since V10:
- Explicitly specified enum values in header file.
- Removed additional dead code for buffer handling.
- Cleaned up headers.
- Added additional locks as requested by sashiko.dev bot.
Changes Since V11:
- Since driver has shrunk in size considerably, moved i2c bits into
first code commit. This ensures that the very first commit with code
can now be compiled. The commit after that adds SPI support as it
was in the previous versions.
- Used pahole to optimize inv_icm42607_state. Reordering elements
reduced size in memory from 384 bytes to 256 bytes.
- Added a map of all readable registers and all writeable registers
according to the datasheet.
- Added back some missing headers pointed out by the maintainers.
- Added FIELD_PREP in a few more places to make the code more
obvious on what it's doing.
- Added a comment to the power management code to note that
temperature sensor being enabled doesn't matter as the clocks
are off by default when the gyro and accel channels are off.
- Removed iio_device_claim_direct() calls since it was no longer
needed.
- Fixed shared_by_all attributes for temperature sensor.
- Additional miscellanous fixes as requested.
Changes Since V12:
- Removed aligned buffer from inv_icm42607_state struct as we do not
currently have the need for it.
- Corrected the order of the odr values in the accel and gyro files
as the values were out of order (the place in the array corresponds
to the register value).
- Stopped setting the clock value depending upon the temp config. The
datasheet advised to keep using the default value.
- Corrected logic when changing between states. We only need to pause
when a sensor changes from off to an on state or when the gyro
changes from an on state to off.
- Added missing includes for several files.
Changes Since V13:
- Refactored inv_icm42607_set_accel_conf() and
inv_icm42607_set_gyro_conf() into a single function.
- Refactored inv_icm42607_accel_read_sensor() and
inv_icm42607_gyro_read_sensor() into a single function.
- Merged inv_icm42607_set_temp_conf() into initial init function
since it only really needs to be called once.
- Saved adding temp sensor for last and updated
inv_icm42607_temp_read() to either confirm other sensors are already
enabled or enable the accelerometer so it can get a reading.
- Updated inv_icm42607_set_pwr_mgmt0() so that it does not update the
sensor mode and forcibly keep the sensor enabled.
- Added inv_icm42607_temp_filter_bw enums since it appears to use
different values than the accel or gyro sensor.
- Set the temp startup time from 77ms to 77us, as I previously misread
the datasheet.
- Additional minor fixes.
Chris Morgan (9):
dt-bindings: iio: imu: icm42600: Add mount-matrix
dt-bindings: iio: imu: icm42600: Add icm42607
iio: imu: inv_icm42607: Add inv_icm42607 Core Driver
iio: imu: inv_icm42607: Add SPI For icm42607
iio: imu: inv_icm42607: Add PM support for icm42607
iio: imu: inv_icm42607: Add Accelerometer for icm42607
iio: imu: inv_icm42607: Add Gyroscope to icm42607
iio: imu: inv_icm42607: Add Temp Support in icm42607
arm64: dts: rockchip: Add icm42607p IMU for RG-DS
.../bindings/iio/imu/invensense,icm42600.yaml | 20 +-
.../dts/rockchip/rk3568-anbernic-rg-ds.dts | 8 +-
drivers/iio/imu/Kconfig | 1 +
drivers/iio/imu/Makefile | 1 +
drivers/iio/imu/inv_icm42607/Kconfig | 30 +
drivers/iio/imu/inv_icm42607/Makefile | 13 +
drivers/iio/imu/inv_icm42607/inv_icm42607.h | 423 ++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_accel.c | 317 +++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 606 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 313 +++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 98 +++
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 108 ++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 99 +++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 37 ++
14 files changed, 2072 insertions(+), 2 deletions(-)
create mode 100644 drivers/iio/imu/inv_icm42607/Kconfig
create mode 100644 drivers/iio/imu/inv_icm42607/Makefile
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607.h
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
create mode 100644 drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
--
2.43.0
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox