* Re: [PATCH v3 2/3] pmdomain: mediatek: Add support for secure modem power domain control
From: sashiko-bot @ 2026-07-20 20:58 UTC (permalink / raw)
To: Nikolai Burov; +Cc: conor+dt, robh, devicetree
In-Reply-To: <20260720-mt6858-pmdomain-v3-2-8966d8de93c8@jolla.com>
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Hardware sequence inversion for secure modem teardown
- [Medium] Unwarranted 1-second busy sleep loop on SMC hard errors
Pre-existing issues:
- [High] Missing power-off rollback in hardware bring-up error paths
- [High] Bus protection resource leak on early power-off failure
--
--- Patch [2]: [PATCH v3 2/3] pmdomain: mediatek: Add support for secure modem power domain control ---
commit a98929d49f9662dbe82719e18f3bfc5621c41558
Author: Nikolai Burov <nikolai.burov@jolla.com>
pmdomain: mediatek: Add support for secure modem power domain control
On recent MediaTek SoCs such as MT6858, the kernel is required to use
a secure monitor call (SMC) to enable or disable the modem power domain.
This patch adds a flag to enable this new power sequence.
> diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
> index 8309a4b46afb0..5b4d860318a46 100644
> --- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
> +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
[ ... ]
> @@ -668,6 +672,34 @@ static int scpsys_modem_pwrseq_off(struct scpsys_domain *pd)
> return 0;
> }
>
> +static bool scpsys_modem_sec_poll(unsigned long cmd)
> +{
> + struct arm_smccc_res res;
> +
> + arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 1, 0, 0, 0, 0, 0, &res);
> +
> + return res.a0 == 0;
> +}
> +
> +static int scpsys_modem_sec_power_on(bool on)
> +{
> + struct arm_smccc_res res;
> + unsigned long cmd = on ? MTK_MD_MTCMOS_ENABLE : MTK_MD_MTCMOS_DISABLE;
> + bool tmp;
> + int ret;
> +
> + arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 0, 0, 0, 0, 0, 0, &res);
> + if (res.a0 == 0)
> + return 0;
> +
> + ret = readx_poll_timeout(scpsys_modem_sec_poll, cmd, tmp, tmp,
> + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
[Severity: Medium]
Does this code assume any non-zero res.a0 indicates an 'in-progress'
operation? If the SMC call returns a negative error code (like
SMCCC_RET_NOT_SUPPORTED), will it dive into a 1-second busy sleep loop instead
of failing immediately?
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
[ ... ]
> @@ -739,7 +774,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
> err_enable_bus_protect:
> scpsys_bus_protect_enable(pd, 0);
> err_disable_sram:
> - if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
> + if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) &&
> + !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
> scpsys_sram_disable(pd);
> err_disable_subsys_clks:
[Severity: High]
This isn't a bug introduced by this patch, but does this error path block skip
reverting the MTCMOS/SMC power-on state before disabling the regulator?
If a step fails after powering on the MTCMOS domain (for example,
clk_bulk_prepare_enable()), the error paths drop the subsystem clocks and
regulator but fail to explicitly invoke the power-off rollback. Could this
leave the SMC firmware tracking a powered-on domain while physical power is
cut, potentially deadlocking firmware?
> if (!MTK_SCPD_CAPS(pd, MTK_SCPD_STRICT_BUS_PROTECTION))
> @@ -761,7 +797,11 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd)
> if (ret < 0)
> return ret;
>
> - if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
> + if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) {
> + ret = scpsys_modem_sec_power_on(false);
[Severity: High]
In scpsys_power_on(), the sequence un-isolates the buck, powers on the domain
via SMC, and then enables subsystem clocks. To maintain LIFO symmetry,
shouldn't the teardown sequence disable the clocks and isolate the buck before
cutting MTCMOS power?
Since the SMC power-off call is placed at the very beginning of the function
here, could this cut power while clocks are still actively toggling, causing
hardware bus hangs?
> + if (ret)
> + return ret;
[Severity: High]
This is a pre-existing issue, but does returning an error here leave the bus
protection enabled indefinitely?
If the SMC power-off fails and returns early, the genpd framework treats a
failed power-off as the domain remaining active, but the bus is now
permanently blocked. Does this error path need to revert bus protection to
avoid a system hang when accessing devices within the domain?
> + } else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
> ret = scpsys_sram_disable(pd);
> if (ret < 0)
> return ret;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com?part=2
^ permalink raw reply
* Re: [PATCH 0/4] media: rc: sunxi-cir: support the A523/H728/T527 IR receiver
From: Andre Przywara @ 2026-07-20 20:53 UTC (permalink / raw)
To: Justin Suess
Cc: Sean Young, Mauro Carvalho Chehab, Chen-Yu Tsai, Jernej Skrabec,
Samuel Holland, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Maxime Ripard, linux-media, devicetree, linux-arm-kernel,
linux-sunxi, Sashiko
In-Reply-To: <20260702214750.3428694-1-utilityemal77@gmail.com>
On Thu, 2 Jul 2026 17:47:46 -0400
Justin Suess <utilityemal77@gmail.com> wrote:
Hey Justin,
> This series adds support for the CIR receiver found in the Allwinner
> A523/T527/H728 family (sun55i). The only board in this family that I
will you be able to send a v2 with the fixes we discussed in this
thread? No driver changes, just the binding for the new specific
compatible string, and the DT changes.
I tested this on the X96QPro+, and also managed to enable IR operation
on the Avaota board, where it uses the other IP instance (and another
pin).
Please let us know if you can send a new version this week still, to
possibly allow a v7.3 merge. Happy to review that quickly then.
If not, please drop me a note, I can then take care of that.
Cheers,
Andre
> am in possession of that has the IR receiver is the x96qpro+, so it is
> just enabled for that board. The Avaota A1 may have it, but I don't have
> hardware to test, so it's just enabled on the x96qpro+ for now, but
> left in the a523 DTSI for future use.
>
> The sun55i family carries a newer revision of the sunxi CIR IP that is
> not backwards-compatible with the prior hardware. So a new pulse
> capture mode field in the control register resets to 0 by default,
> which captures nothing, and the sample clock divider became selectable
> instead of the fixed module clock / 64.
>
> Solve this by adding the two new registers, enabling the setting of the
> pulse capture mode register and the selection of the clock speed.
>
> The new compatible is therefore added standalone, without the
> usual sun6i-a31-ir fallback (it won't work).
>
> Tested on an X96Q Pro+ (H728) TV box with the Google TV remote that
> came in the box (using NEC keymap). Was able to press buttons and
> get readings from lirc / ir-keytable.
>
> The series is based on tag v7.2-rc1 on mainline.
>
> Justin Suess (4):
> media: dt-bindings: allwinner,sun4i-a10-ir: add A523 compatible
> media: rc: sunxi-cir: add support for the A523
> arm64: dts: allwinner: a523: add IR receiver node
> arm64: dts: allwinner: a523: enable IR receiver on the X96Q Pro+
>
> .../media/allwinner,sun4i-a10-ir.yaml | 1 +
> .../arm64/boot/dts/allwinner/sun55i-a523.dtsi | 19 +++++
> .../dts/allwinner/sun55i-h728-x96qpro+.dts | 4 +
> drivers/media/rc/sunxi-cir.c | 76 +++++++++++++++----
> 4 files changed, 87 insertions(+), 13 deletions(-)
>
^ permalink raw reply
* Re: [PATCH v3 1/3] dt-bindings: power: Add MediaTek MT6858 power domain controller
From: sashiko-bot @ 2026-07-20 20:55 UTC (permalink / raw)
To: Nikolai Burov; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260720-mt6858-pmdomain-v3-1-8966d8de93c8@jolla.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Relaxing the global maxItems for access-controllers weakens validation for older SoCs, and the MT6858 block lacks min/max constraints.
- [Medium] The parent SCPSYS compatible string for MT6858 is undocumented in the related MFD binding.
--
--- Patch [1]: [PATCH v3 1/3] dt-bindings: power: Add MediaTek MT6858 power domain controller ---
commit 2af17e3001f2b4860c3cbd850866a2a03694b597
Author: Nikolai Burov <nikolai.burov@jolla.com>
dt-bindings: power: Add MediaTek MT6858 power domain controller
Add a new compatible and document bindings for the power domain
controller of the MT6858 SoC.
> diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
> index 070c6e5666dc0..d03e4a9251635 100644
> --- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
> +++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
> @@ -25,6 +25,7 @@ properties:
> enum:
> - mediatek,mt6735-power-controller
> - mediatek,mt6795-power-controller
> + - mediatek,mt6858-power-controller
[Severity: Medium]
Since this power controller is a child of the SCPSYS system controller,
shouldn't the corresponding parent compatible string (e.g.
mediatek,mt6858-scpsys) be added to the parent MFD binding as well?
Without it, a complete Device Tree declaring the MT6858 SCPSYS parent node
might fail schema validation when checked against
Documentation/devicetree/bindings/mfd/mediatek,mt8195-scpsys.yaml.
[ ... ]
> @@ -56,7 +57,7 @@ properties:
> faults while enabling or disabling a power domain.
> For example, this may hold phandles to INFRACFG and SMI.
> minItems: 1
> - maxItems: 3
> + maxItems: 6
[Severity: Medium]
Does relaxing the global maxItems constraint unintentionally weaken schema
validation for older hardware generations?
Previous SoCs relied on this global maxItems: 3 because they lack their own
specific if-then blocks for access-controllers. Now they will be permitted to
have up to 6 without triggering a validation error.
[ ... ]
> @@ -156,6 +158,23 @@ required:
> - compatible
>
> allOf:
> + - if:
> + properties:
> + compatible:
> + contains:
> + enum:
> + - mediatek,mt6858-power-controller
> + then:
> + properties:
> + access-controllers:
> + items:
> + - description: handle to INFRACFG register block
> + - description: handle to IMG_SUB0 register block
> + - description: handle to CAM_SUB1 register block
> + - description: handle to CAM_SUB0 register block
> + - description: handle to IPE_SUB0 register block
> + - description: handle to VLPCFG register block
[Severity: Medium]
Does this block also need explicit minItems: 6 and maxItems: 6 constraints?
Without them, JSON schema will fall back to the global minItems: 1, allowing
incomplete MT6858 device tree nodes specifying fewer than 6 access controllers
to bypass strict validation.
> +
> - if:
> properties:
> compatible:
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com?part=1
^ permalink raw reply
* Re: [PATCH v5 0/7] dtc: dt-check-style: Improvement after using on DTS
From: Rob Herring @ 2026-07-20 20:51 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Saravana Kannan, Krzysztof Kozlowski, Conor Dooley, Test User,
devicetree, linux-kernel, Daniel Golle
In-Reply-To: <20260709-dts-style-checker-v5-0-fcc147cb697d@oss.qualcomm.com>
On Thu, Jul 09, 2026 at 07:41:28PM +0200, Krzysztof Kozlowski wrote:
> Changes in v5:
> 1. Patch #2, dtc: dt-check-style: Allow space-aligning indentation in
> DTS:
> Handle also line continuations for properties.
> 2. Update docs, other minor style/docs comments from Sashiko
> - Link to v4: https://patch.msgid.link/20260708-dts-style-checker-v4-0-c175e6401150@oss.qualcomm.com
>
> Changes in v4:
> 1. Drop patch:
> "dtc: dt-check-style: Do not sort top-level nodes in DTS by unit address"
> This needs more work.
> 2. Patch #1: Rename rule tab-in-dts to tab-in-yaml
> 3. Drop "memory@a0000000" in examples, because it messes with top-level
> sorting. Use a "cpus" node to test node and device_type property ordering.
> 4. Add more test-cases: within existing commits and a new patch at the
> end:
> "dtc: dt-check-style: Add more DTS test cases"
> 5. Handle #include and /include/ without trailing space
>
> Link to v3: https://patch.msgid.link/20260706-dts-style-checker-v3-0-03ffacff9226@oss.qualcomm.com
>
> Changes in v3:
> 1. Patch #2: Use re.search
> 2. Patch #2: Add dts-mixed-indent.dts test case
> 3. Several new patches 3-7.
> v2: https://lore.kernel.org/r/20260706102421.343639-4-krzysztof.kozlowski@oss.qualcomm.com/
>
> Changes in v2:
> 1. Patch #2: Rework idea.
> 2. Adjust function doc/comment.
> v1: https://lore.kernel.org/r/20260706071446.87669-2-krzysztof.kozlowski@oss.qualcomm.com/
>
> (I was not using b4 for v1 and v2, so b4 diff might fail)
>
> Best regards,
> Krzysztof
>
> ---
> Krzysztof Kozlowski (7):
> dtc: dt-check-style: Narrow disallowing of tab in DTS only to YAML
> dtc: dt-check-style: Allow space-aligning indentation in DTS
> dtc: dt-check-style: Rework handling YAML/DTS in rules
> dtc: dt-check-style: Expect first device_type
> dtc: dt-check-style: Handle properly DTC-style includes
> dtc: dt-check-style: Print proper line number of indentation detection place
> dtc: dt-check-style: Add more DTS test cases
Applied. Send a follow-up if the sashiko report is valid.
Rob
^ permalink raw reply
* [PATCH v3 2/3] pmdomain: mediatek: Add support for secure modem power domain control
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
On recent MediaTek SoCs such as MT6858, the kernel is required to use
a secure monitor call (SMC) to enable or disable the modem power domain.
The power domain control register can be read, but firmware prevents it
from being modified directly. Some other parts of the power sequence,
such as setting the ext_buck_iso register, still need to be performed on
the kernel side.
In preparation for modem support, add a flag to enable this new power
sequence for SoCs that need it. Power domains using this flag are not
expected to configure any bus protection registers, since these are
handled internally by the SMC call.
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
drivers/pmdomain/mediatek/mtk-pm-domains.c | 50 +++++++++++++++++++++++++++---
drivers/pmdomain/mediatek/mtk-pm-domains.h | 1 +
include/linux/soc/mediatek/mtk_sip_svc.h | 3 ++
3 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index 8309a4b46afb..5b4d860318a4 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -57,6 +57,10 @@
#define MTK_SIP_KERNEL_HWCCF_CONTROL MTK_SIP_SMC_CMD(0x540)
+/* Secure MTCMOS commands for modem subsystem */
+#define MTK_MD_MTCMOS_ENABLE 18
+#define MTK_MD_MTCMOS_DISABLE 19
+
struct scpsys_domain {
struct generic_pm_domain genpd;
const struct scpsys_domain_data *data;
@@ -668,6 +672,34 @@ static int scpsys_modem_pwrseq_off(struct scpsys_domain *pd)
return 0;
}
+static bool scpsys_modem_sec_poll(unsigned long cmd)
+{
+ struct arm_smccc_res res;
+
+ arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 1, 0, 0, 0, 0, 0, &res);
+
+ return res.a0 == 0;
+}
+
+static int scpsys_modem_sec_power_on(bool on)
+{
+ struct arm_smccc_res res;
+ unsigned long cmd = on ? MTK_MD_MTCMOS_ENABLE : MTK_MD_MTCMOS_DISABLE;
+ bool tmp;
+ int ret;
+
+ arm_smccc_smc(MTK_SIP_KERNEL_CCCI_CONTROL, cmd, 0, 0, 0, 0, 0, 0, &res);
+ if (res.a0 == 0)
+ return 0;
+
+ ret = readx_poll_timeout(scpsys_modem_sec_poll, cmd, tmp, tmp,
+ MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int scpsys_power_on(struct generic_pm_domain *genpd)
{
struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd);
@@ -686,7 +718,9 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
regmap_clear_bits(scpsys->base, pd->data->ext_buck_iso_offs,
pd->data->ext_buck_iso_mask);
- if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ))
+ if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
+ ret = scpsys_modem_sec_power_on(true);
+ else if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_PWRSEQ))
ret = scpsys_modem_pwrseq_on(pd);
else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
ret = scpsys_simple_pwrseq_on(pd);
@@ -717,7 +751,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
goto err_pwr_ack;
}
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
+ if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) &&
+ !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) {
ret = scpsys_sram_enable(pd);
if (ret < 0)
goto err_disable_subsys_clks;
@@ -739,7 +774,8 @@ static int scpsys_power_on(struct generic_pm_domain *genpd)
err_enable_bus_protect:
scpsys_bus_protect_enable(pd, 0);
err_disable_sram:
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
+ if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ) &&
+ !MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
scpsys_sram_disable(pd);
err_disable_subsys_clks:
if (!MTK_SCPD_CAPS(pd, MTK_SCPD_STRICT_BUS_PROTECTION))
@@ -761,7 +797,11 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd)
if (ret < 0)
return ret;
- if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
+ if (MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ)) {
+ ret = scpsys_modem_sec_power_on(false);
+ if (ret)
+ return ret;
+ } else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ)) {
ret = scpsys_sram_disable(pd);
if (ret < 0)
return ret;
@@ -781,7 +821,7 @@ static int scpsys_power_off_internal(struct scpsys_domain *pd)
ret = scpsys_modem_pwrseq_off(pd);
else if (MTK_SCPD_CAPS(pd, MTK_SCPD_SIMPLE_PWRSEQ))
ret = scpsys_simple_pwrseq_off(pd);
- else
+ else if (!MTK_SCPD_CAPS(pd, MTK_SCPD_MODEM_SECURE_PWRSEQ))
ret = scpsys_ctl_pwrseq_off(pd);
if (ret < 0) {
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h
index 092403de66fa..8690690335ad 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.h
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h
@@ -18,6 +18,7 @@
#define MTK_SCPD_SKIP_RESET_B BIT(11)
#define MTK_SCPD_INFRA_PWR_CTL BIT(12)
#define MTK_SCPD_SIMPLE_PWRSEQ BIT(13)
+#define MTK_SCPD_MODEM_SECURE_PWRSEQ BIT(14)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data ? \
(_scpd)->data->caps & (_x) : \
(_scpd)->hwv_data->caps & (_x))
diff --git a/include/linux/soc/mediatek/mtk_sip_svc.h b/include/linux/soc/mediatek/mtk_sip_svc.h
index abe24a73ee19..6c95a29b79fa 100644
--- a/include/linux/soc/mediatek/mtk_sip_svc.h
+++ b/include/linux/soc/mediatek/mtk_sip_svc.h
@@ -22,6 +22,9 @@
ARM_SMCCC_CALL_VAL(ARM_SMCCC_FAST_CALL, MTK_SIP_SMC_CONVENTION, \
ARM_SMCCC_OWNER_SIP, fn_id)
+/* Modem related SMC call */
+#define MTK_SIP_KERNEL_CCCI_CONTROL MTK_SIP_SMC_CMD(0x505)
+
/* DVFSRC SMC calls */
#define MTK_SIP_DVFSRC_VCOREFS_CONTROL MTK_SIP_SMC_CMD(0x506)
--
2.54.0
^ permalink raw reply related
* [PATCH v3 0/3] pmdomain: mediatek: Add MT6858 support
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov,
Krzysztof Kozlowski
Add power domain driver support for the MediaTek Dimensity 7100 (MT6858)
SoC. On this SoC, there are some new subsystem-specific bus protection
blocks, which are listed here in ascending address order:
- IMG_SUB0 at 0x1502f000
- CAM_SUB1 at 0x1a00c000
- CAM_SUB0 at 0x1a00d000
- IPE_SUB0 at 0x1b00e000
The VLPCFG bus configuration registers at 0x1c00c000 are also new.
Additionally, implement the SMC-based power sequence required for
enabling the modem power domain on this SoC.
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
Changes in v3:
- Fix error path to also skip sram_pdn for MODEM_SECURE_PWRSEQ
(discovered in Sashiko report)
- Link to v2: https://patch.msgid.link/20260715-mt6858-pmdomain-v2-0-6293e87fc093@jolla.com
Changes in v2:
- Rebase
- MTK_SCPD_MODEM_SECURE -> MTK_SCPD_MODEM_SECURE_PWRSEQ
- Convert SMC command enum to defines
- Based on the new SIMPLE_PWRSEQ changes, skip the sram_pdn stage for
MODEM_SECURE_PWRSEQ too since it wasn't intended to have an effect
- Clarify that the SMC call also handles bus protection
- Simply if/else code based on the assumption *_PWRSEQ are mutually
exclusive
- Add comment explaining that PWR_STA_2ND is skipped for modem
(mentioned in Sashiko report)
- Link to v1: https://patch.msgid.link/20260714-mt6858-pmdomain-v1-0-4f09bbb822e0@jolla.com
To: Rob Herring <robh@kernel.org>
To: Krzysztof Kozlowski <krzk+dt@kernel.org>
To: Conor Dooley <conor+dt@kernel.org>
To: Matthias Brugger <matthias.bgg@gmail.com>
To: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
To: Ulf Hansson <ulfh@kernel.org>
Cc: Matthias Brugger <mbrugger@suse.com>
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-mediatek@lists.infradead.org
Cc: linux-pm@vger.kernel.org
---
Nikolai Burov (3):
dt-bindings: power: Add MediaTek MT6858 power domain controller
pmdomain: mediatek: Add support for secure modem power domain control
pmdomain: mediatek: Add support for MT6858 SoC
.../bindings/power/mediatek,power-controller.yaml | 21 +-
drivers/pmdomain/mediatek/mt6858-pm-domains.h | 466 +++++++++++++++++++++
drivers/pmdomain/mediatek/mtk-pm-domains.c | 55 ++-
drivers/pmdomain/mediatek/mtk-pm-domains.h | 6 +
include/dt-bindings/power/mediatek,mt6858-power.h | 23 +
include/linux/soc/mediatek/mtk_sip_svc.h | 3 +
6 files changed, 568 insertions(+), 6 deletions(-)
---
base-commit: 3fe08b9796f36ef437ab9328e7dd1e5ff2d66603
change-id: 20260712-mt6858-pmdomain-b1b456c96675
Best regards,
--
Nikolai Burov <nikolai.burov@jolla.com>
^ permalink raw reply
* [PATCH v3 3/3] pmdomain: mediatek: Add support for MT6858 SoC
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
Add support for the power domains found in the MediaTek MT6858 SoC.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
drivers/pmdomain/mediatek/mt6858-pm-domains.h | 466 ++++++++++++++++++++++++++
drivers/pmdomain/mediatek/mtk-pm-domains.c | 5 +
drivers/pmdomain/mediatek/mtk-pm-domains.h | 5 +
3 files changed, 476 insertions(+)
diff --git a/drivers/pmdomain/mediatek/mt6858-pm-domains.h b/drivers/pmdomain/mediatek/mt6858-pm-domains.h
new file mode 100644
index 000000000000..177b2e86ab1e
--- /dev/null
+++ b/drivers/pmdomain/mediatek/mt6858-pm-domains.h
@@ -0,0 +1,466 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (c) 2025 MediaTek Inc.
+ * KY Liu <ky.liu@mediatek.com>
+ * Copyright (c) 2026 Jolla Mobile Ltd
+ * Nikolai Burov <nikolai.burov@jolla.com>
+ */
+
+#ifndef __SOC_MEDIATEK_MT6858_PM_DOMAINS_H
+#define __SOC_MEDIATEK_MT6858_PM_DOMAINS_H
+
+#include "mtk-pm-domains.h"
+#include <dt-bindings/power/mediatek,mt6858-power.h>
+
+/* TOP_AXI registers */
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET 0x0c14
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR 0x0c18
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY 0x0c1c
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0 (BIT(0) | BIT(1) | BIT(18))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE BIT(2)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1 BIT(3)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0 BIT(12)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0 BIT(20)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN (BIT(30) | BIT(31))
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET 0x0c24
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR 0x0c28
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY 0x0c2c
+
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1 BIT(7)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE BIT(8)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN (BIT(9) | BIT(10))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0 BIT(12)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0 BIT(13)
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA (GENMASK(3, 1) | BIT(6))
+#define MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND (BIT(0) | BIT(5) | GENMASK(15, 7))
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET 0x0c44
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR 0x0c48
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY 0x0c4c
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN BIT(8)
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA BIT(16)
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET 0x0c54
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR 0x0c58
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY 0x0c5c
+
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA BIT(11)
+#define MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN BIT(12)
+
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET 0x0c64
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR 0x0c68
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY 0x0c6c
+
+#define MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA (BIT(20) | BIT(21))
+
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET 0x0c84
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR 0x0c88
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY 0x0c8c
+
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO BIT(6)
+#define MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB BIT(7)
+
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET 0x0c94
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR 0x0c98
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY 0x0c9c
+
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN BIT(1)
+#define MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND BIT(0)
+
+/* {IMG,IPE,CAM}_SUBx registers */
+#define MT6858_SUBx_PROT_EN_SET 0x03c4
+#define MT6858_SUBx_PROT_EN_CLR 0x03c8
+#define MT6858_SUBx_PROT_EN_STA 0x03cc
+
+#define MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1 (BIT(0) | BIT(1))
+
+#define MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE (BIT(0) | BIT(1))
+
+#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN BIT(0)
+#define MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB BIT(1)
+
+#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN BIT(0)
+#define MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA BIT(1)
+
+/* VLP_AXI registers */
+#define MT6858_VLP_AXI_PROT_EN_SET 0x0214
+#define MT6858_VLP_AXI_PROT_EN_CLR 0x0218
+#define MT6858_VLP_AXI_PROT_EN_STA 0x021c
+
+#define MT6858_VLP_AXI_PROT_EN_MM_PROC BIT(8)
+#define MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND (BIT(9) | BIT(10))
+
+/* PWR_CON registers */
+#define MT6858_PWR_ACK BIT(30)
+#define MT6858_PWR_ACK_2ND BIT(31)
+
+static enum scpsys_bus_prot_block scpsys_bus_prot_blocks_mt6858[] = {
+ BUS_PROT_BLOCK_INFRA,
+ BUS_PROT_BLOCK_IMG_SUB0,
+ BUS_PROT_BLOCK_CAM_SUB1,
+ BUS_PROT_BLOCK_CAM_SUB0,
+ BUS_PROT_BLOCK_IPE_SUB0,
+ BUS_PROT_BLOCK_VLP,
+};
+
+static const struct scpsys_domain_data scpsys_domain_data_mt6858[] = {
+ [MT6858_POWER_DOMAIN_MD] = {
+ .name = "md",
+ /*
+ * Note: the PWR_ACK_2ND bit is not used for the modem domain.
+ * Skip it and fall back to checking the 1st bit twice.
+ */
+ .sta_mask = MT6858_PWR_ACK,
+ .ctl_offs = 0xe00,
+ .pwr_sta_offs = 0xe00,
+ .pwr_sta2nd_offs = 0xe00,
+ .ext_buck_iso_offs = 0xf20,
+ .ext_buck_iso_mask = GENMASK(1, 0),
+ .caps = MTK_SCPD_MODEM_SECURE_PWRSEQ | MTK_SCPD_EXT_BUCK_ISO |
+ MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CONN] = {
+ .name = "conn",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe04,
+ .pwr_sta_offs = 0xe04,
+ .pwr_sta2nd_offs = 0xe04,
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CONN,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CONN_2ND,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MCU_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CONN,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_AUDIO] = {
+ .name = "audio",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe18,
+ .pwr_sta_offs = 0xe18,
+ .pwr_sta2nd_offs = 0xe18,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_AUDIO,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_ISP_IMG1] = {
+ .name = "isp-img1",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe28,
+ .pwr_sta_offs = 0xe28,
+ .pwr_sta2nd_offs = 0xe28,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IMG1,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IMG1,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(IMG_SUB0,
+ MT6858_IMG_SUB0_PROT_EN_SMI_ISP_IMG1,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_ISP_IMG2] = {
+ .name = "isp-img2",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe2c,
+ .pwr_sta_offs = 0xe2c,
+ .pwr_sta2nd_offs = 0xe2c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_ISP_IPE] = {
+ .name = "isp-ipe",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe30,
+ .pwr_sta_offs = 0xe30,
+ .pwr_sta2nd_offs = 0xe30,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_ISP_IPE,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_ISP_IPE,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(IPE_SUB0,
+ MT6858_IPE_SUB0_PROT_EN_SMI_ISP_IPE,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_VDE0] = {
+ .name = "vde0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe34,
+ .pwr_sta_offs = 0xe34,
+ .pwr_sta2nd_offs = 0xe34,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VDE0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VDE0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_VEN0] = {
+ .name = "ven0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe3c,
+ .pwr_sta_offs = 0xe3c,
+ .pwr_sta2nd_offs = 0xe3c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_VEN0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_VEN0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_CAM_MAIN] = {
+ .name = "cam-main",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe44,
+ .pwr_sta_offs = 0xe44,
+ .pwr_sta2nd_offs = 0xe44,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CAM_MAIN,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CAM_MAIN,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(CAM_SUB0,
+ MT6858_CAM_SUB0_PROT_EN_SMI_CAM_MAIN,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ BUS_PROT_WR_IGN(CAM_SUB1,
+ MT6858_CAM_SUB1_PROT_EN_SMI_CAM_MAIN,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CAM_SUBA] = {
+ .name = "cam-suba",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe4c,
+ .pwr_sta_offs = 0xe4c,
+ .pwr_sta2nd_offs = 0xe4c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(CAM_SUB1,
+ MT6858_CAM_SUB1_PROT_EN_SMI_CAM_SUBA,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_CAM_SUBB] = {
+ .name = "cam-subb",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe50,
+ .pwr_sta_offs = 0xe50,
+ .pwr_sta2nd_offs = 0xe50,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(CAM_SUB0,
+ MT6858_CAM_SUB0_PROT_EN_SMI_CAM_SUBB,
+ MT6858_SUBx_PROT_EN_SET,
+ MT6858_SUBx_PROT_EN_CLR,
+ MT6858_SUBx_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_DIS0] = {
+ .name = "dis0",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe6c,
+ .pwr_sta_offs = 0xe6c,
+ .pwr_sta2nd_offs = 0xe6c,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_DIS0,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_MM_INFRA] = {
+ .name = "mm-infra",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe74,
+ .pwr_sta_offs = 0xe74,
+ .pwr_sta2nd_offs = 0xe74,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_MM_INFRA_2ND,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_SET,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_CLR,
+ MT6858_TOP_AXI_PROT_EN_MMSYS_STA_1_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_INFRASYS_STA_0_RDY),
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_MM_INFRA,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_EMISYS_STA_0_RDY),
+ },
+ },
+ [MT6858_POWER_DOMAIN_MM_PROC_DORMANT] = {
+ .name = "mm-proc-dormant",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe78,
+ .pwr_sta_offs = 0xe78,
+ .pwr_sta2nd_offs = 0xe78,
+ .sram_pdn_bits = BIT(9),
+ .sram_pdn_ack_bits = BIT(13),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(VLP,
+ MT6858_VLP_AXI_PROT_EN_MM_PROC,
+ MT6858_VLP_AXI_PROT_EN_SET,
+ MT6858_VLP_AXI_PROT_EN_CLR,
+ MT6858_VLP_AXI_PROT_EN_STA),
+ BUS_PROT_WR_IGN(VLP,
+ MT6858_VLP_AXI_PROT_EN_MM_PROC_2ND,
+ MT6858_VLP_AXI_PROT_EN_SET,
+ MT6858_VLP_AXI_PROT_EN_CLR,
+ MT6858_VLP_AXI_PROT_EN_STA),
+ },
+ .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_SRAM_PDN_INVERTED,
+ },
+ [MT6858_POWER_DOMAIN_CSI_RX] = {
+ .name = "csi-rx",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xe98,
+ .pwr_sta_offs = 0xe98,
+ .pwr_sta2nd_offs = 0xe98,
+ .caps = MTK_SCPD_KEEP_DEFAULT_OFF,
+ },
+ [MT6858_POWER_DOMAIN_SSUSB] = {
+ .name = "ssusb",
+ .sta_mask = MT6858_PWR_ACK,
+ .sta2nd_mask = MT6858_PWR_ACK_2ND,
+ .ctl_offs = 0xea4,
+ .pwr_sta_offs = 0xea4,
+ .pwr_sta2nd_offs = 0xea4,
+ .sram_pdn_bits = BIT(8),
+ .sram_pdn_ack_bits = BIT(12),
+ .bp_cfg = {
+ BUS_PROT_WR_IGN(INFRA,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SSUSB,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_SET,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_CLR,
+ MT6858_TOP_AXI_PROT_EN_PERISYS_STA_0_RDY),
+ },
+ },
+};
+
+static const struct scpsys_soc_data mt6858_scpsys_data = {
+ .domains_data = scpsys_domain_data_mt6858,
+ .num_domains = ARRAY_SIZE(scpsys_domain_data_mt6858),
+ .bus_prot_blocks = scpsys_bus_prot_blocks_mt6858,
+ .num_bus_prot_blocks = ARRAY_SIZE(scpsys_bus_prot_blocks_mt6858),
+ .type = SCPSYS_MTCMOS_TYPE_DIRECT_CTL,
+};
+
+#endif /* __SOC_MEDIATEK_MT6858_PM_DOMAINS_H */
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c
index 5b4d860318a4..b43f8b4a6a01 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.c
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c
@@ -20,6 +20,7 @@
#include "mt6735-pm-domains.h"
#include "mt6795-pm-domains.h"
+#include "mt6858-pm-domains.h"
#include "mt6893-pm-domains.h"
#include "mt8167-pm-domains.h"
#include "mt8173-pm-domains.h"
@@ -1288,6 +1289,10 @@ static const struct of_device_id scpsys_of_match[] = {
.compatible = "mediatek,mt6795-power-controller",
.data = &mt6795_scpsys_data,
},
+ {
+ .compatible = "mediatek,mt6858-power-controller",
+ .data = &mt6858_scpsys_data,
+ },
{
.compatible = "mediatek,mt6893-power-controller",
.data = &mt6893_scpsys_data,
diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h
index 8690690335ad..45da83e9d649 100644
--- a/drivers/pmdomain/mediatek/mtk-pm-domains.h
+++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h
@@ -66,6 +66,11 @@ enum scpsys_bus_prot_block {
BUS_PROT_BLOCK_INFRA_NAO,
BUS_PROT_BLOCK_SMI,
BUS_PROT_BLOCK_SPM,
+ BUS_PROT_BLOCK_IMG_SUB0,
+ BUS_PROT_BLOCK_CAM_SUB1,
+ BUS_PROT_BLOCK_CAM_SUB0,
+ BUS_PROT_BLOCK_IPE_SUB0,
+ BUS_PROT_BLOCK_VLP,
BUS_PROT_BLOCK_COUNT,
};
--
2.54.0
^ permalink raw reply related
* [PATCH v3 1/3] dt-bindings: power: Add MediaTek MT6858 power domain controller
From: Nikolai Burov via B4 Relay @ 2026-07-20 20:46 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Ulf Hansson
Cc: Matthias Brugger, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, linux-pm, Nikolai Burov, Nikolai Burov,
Krzysztof Kozlowski
In-Reply-To: <20260720-mt6858-pmdomain-v3-0-8966d8de93c8@jolla.com>
From: Nikolai Burov <nikolai.burov@jolla.com>
Add a new compatible and document bindings for the power domain
controller of the MT6858 SoC.
Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Nikolai Burov <nikolai.burov@jolla.com>
---
.../bindings/power/mediatek,power-controller.yaml | 21 +++++++++++++++++++-
include/dt-bindings/power/mediatek,mt6858-power.h | 23 ++++++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
index 070c6e5666dc..d03e4a925163 100644
--- a/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
+++ b/Documentation/devicetree/bindings/power/mediatek,power-controller.yaml
@@ -25,6 +25,7 @@ properties:
enum:
- mediatek,mt6735-power-controller
- mediatek,mt6795-power-controller
+ - mediatek,mt6858-power-controller
- mediatek,mt6893-power-controller
- mediatek,mt8167-power-controller
- mediatek,mt8173-power-controller
@@ -56,7 +57,7 @@ properties:
faults while enabling or disabling a power domain.
For example, this may hold phandles to INFRACFG and SMI.
minItems: 1
- maxItems: 3
+ maxItems: 6
patternProperties:
"^power-domain@[0-9a-f]+$":
@@ -103,6 +104,7 @@ $defs:
description: |
Power domain index. Valid values are defined in:
"include/dt-bindings/power/mt6795-power.h" - for MT8167 type power domain.
+ "include/dt-bindings/power/mediatek,mt6858-power.h" - for MT6858 type power domain.
"include/dt-bindings/power/mediatek,mt6893-power.h" - for MT6893 type power domain.
"include/dt-bindings/power/mt8167-power.h" - for MT8167 type power domain.
"include/dt-bindings/power/mt8173-power.h" - for MT8173 type power domain.
@@ -156,6 +158,23 @@ required:
- compatible
allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - mediatek,mt6858-power-controller
+ then:
+ properties:
+ access-controllers:
+ items:
+ - description: handle to INFRACFG register block
+ - description: handle to IMG_SUB0 register block
+ - description: handle to CAM_SUB1 register block
+ - description: handle to CAM_SUB0 register block
+ - description: handle to IPE_SUB0 register block
+ - description: handle to VLPCFG register block
+
- if:
properties:
compatible:
diff --git a/include/dt-bindings/power/mediatek,mt6858-power.h b/include/dt-bindings/power/mediatek,mt6858-power.h
new file mode 100644
index 000000000000..6ed9e82d4ad3
--- /dev/null
+++ b/include/dt-bindings/power/mediatek,mt6858-power.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_POWER_MT6858_POWER_H
+#define _DT_BINDINGS_POWER_MT6858_POWER_H
+
+#define MT6858_POWER_DOMAIN_MD 0
+#define MT6858_POWER_DOMAIN_CONN 1
+#define MT6858_POWER_DOMAIN_AUDIO 2
+#define MT6858_POWER_DOMAIN_MM_INFRA 3
+#define MT6858_POWER_DOMAIN_ISP_IMG1 4
+#define MT6858_POWER_DOMAIN_ISP_IMG2 5
+#define MT6858_POWER_DOMAIN_ISP_IPE 6
+#define MT6858_POWER_DOMAIN_VDE0 7
+#define MT6858_POWER_DOMAIN_VEN0 8
+#define MT6858_POWER_DOMAIN_CAM_MAIN 9
+#define MT6858_POWER_DOMAIN_CAM_SUBA 10
+#define MT6858_POWER_DOMAIN_CAM_SUBB 11
+#define MT6858_POWER_DOMAIN_DIS0 12
+#define MT6858_POWER_DOMAIN_MM_PROC_DORMANT 13
+#define MT6858_POWER_DOMAIN_CSI_RX 14
+#define MT6858_POWER_DOMAIN_SSUSB 15
+
+#endif /* _DT_BINDINGS_POWER_MT6858_POWER_H */
--
2.54.0
^ permalink raw reply related
* Re: [PATCH V18 5/9] iio: imu: inv_icm42607: Add PM support for icm42607
From: sashiko-bot @ 2026-07-20 20:40 UTC (permalink / raw)
To: Chris Morgan; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260720202343.70138-6-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] Calling `pm_runtime_force_suspend()` in the `.remove()` callbacks causes an unbalanced runtime PM `disable_depth` due to conflict with `devm_pm_runtime_set_active_enabled()`, breaking power management on driver rebind.
--
--- Patch [5]: [PATCH V18 5/9] iio: imu: inv_icm42607: Add PM support for icm42607 ---
commit ab55ef85474a1fe3f2d09669c982ebc220a804ca
Author: Chris Morgan <macromorgan@hotmail.com>
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 6ec1d730017e2..c127ea0a94c4c 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
[ ... ]
> @@ -275,10 +378,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;
> }
[ ... ]
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
> index f2b9067815b0e..04ad372917832 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
[ ... ]
> @@ -56,6 +57,11 @@ static int inv_icm42607_probe(struct i2c_client *client)
> return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
> }
>
> +static void inv_icm42607_i2c_remove(struct i2c_client *client)
> +{
> + pm_runtime_force_suspend(&client->dev);
> +}
[Severity: Medium]
Does calling pm_runtime_force_suspend() here cause an unbalanced
disable_depth counter?
Because devm_pm_runtime_set_active_enabled() is called during probe in
inv_icm42607_core_probe(), devres will automatically call
pm_runtime_disable() on device removal.
Since pm_runtime_force_suspend() also disables runtime PM internally,
it seems this results in the disable_depth counter being incremented twice.
If the driver is unbound and rebound later, will it fail to ever re-enable
runtime PM because the disable depth remains strictly greater than zero?
[ ... ]
> diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
> index eb04036a6712f..995393dce9429 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
[ ... ]
> @@ -65,6 +66,11 @@ static int inv_icm42607_probe(struct spi_device *spi)
> return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup);
> }
>
> +static void inv_icm42607_spi_remove(struct spi_device *spi)
> +{
> + pm_runtime_force_suspend(&spi->dev);
> +}
[Severity: Medium]
Could this run into the same double-disable issue as the I2C remove function
due to the devres pm_runtime_disable() action?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720202343.70138-1-macroalpha82@gmail.com?part=5
^ permalink raw reply
* Re: [PATCH V18 8/9] iio: imu: inv_icm42607: Add Temp Support in icm42607
From: sashiko-bot @ 2026-07-20 20:39 UTC (permalink / raw)
To: Chris Morgan; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260720202343.70138-9-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The temperature channel advertises IIO_CHAN_INFO_SAMP_FREQ but fails to handle it in its read_raw implementation, causing -EINVAL.
--
--- Patch [8]: [PATCH V18 8/9] iio: imu: inv_icm42607: Add Temp Support in icm42607 ---
commit d42e76e3efb32de0c89c91668f6d156c61cbf937
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_accel.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
> index ad1135d48902d..718a0201b6eda 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
[ ... ]
> @@ -187,6 +190,8 @@ static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
> switch (chan->type) {
> case IIO_ACCEL:
> break;
> + case IIO_TEMP:
> + return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
[Severity: Medium]
Will this early return bypass the IIO_CHAN_INFO_SAMP_FREQ handler located
further down in inv_icm42607_accel_read_raw()?
Since the temperature channel advertises IIO_CHAN_INFO_SAMP_FREQ in
INV_ICM42607_TEMP_CHAN, this causes requests for the sampling frequency to
return -EINVAL instead of the actual sampling frequency.
> 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 6af7b143e6a15..e7d5e552bb5cb 100644
> --- a/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
[ ... ]
> @@ -184,6 +187,8 @@ static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
> switch (chan->type) {
> case IIO_ANGL_VEL:
> break;
> + case IIO_TEMP:
> + return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
[Severity: Medium]
Similar to the accel implementation, does this early return intercept IIO_TEMP
and incorrectly skip the IIO_CHAN_INFO_SAMP_FREQ fallback?
> 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 0000000000000..8681ef2b49e21
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
[ ... ]
> @@ -0,0 +1,98 @@
[ ... ]
> +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:
[Severity: Medium]
Since this function handles the mask for IIO_TEMP, should it also handle
IIO_CHAN_INFO_SAMP_FREQ?
It currently falls through to the default -EINVAL case, causing in-kernel
reads for the sampling frequency to fail, despite the temperature channel
advertising it.
> + return -EINVAL;
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720202343.70138-1-macroalpha82@gmail.com?part=8
^ permalink raw reply
* Re: [PATCH V18 7/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607
From: sashiko-bot @ 2026-07-20 20:37 UTC (permalink / raw)
To: Chris Morgan; +Cc: devicetree, robh, conor+dt
In-Reply-To: <20260720202343.70138-8-macroalpha82@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Hardcoded array index `5` is used instead of the enum `INV_ICM42607_ODR_1600HZ`.
--
--- Patch [7]: [PATCH V18 7/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607 ---
commit 0113579c2d489acc250c7a465387a225805368b5
Author: Chris Morgan <macromorgan@hotmail.com>
iio: imu: inv_icm42607: Add Gyroscope to icm42607
This commit adds gyroscope functions to the icm42607 driver.
> 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 0000000000000..6af7b143e6a15
> --- /dev/null
> +++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
[ ... ]
> +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)
> +{
> + switch (mask) {
[ ... ]
> + 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;
[Severity: Low]
Would it be safer to use INV_ICM42607_ODR_1600HZ here instead of the
hardcoded index 5? If the enum ever changes, this hardcoded index will
silently misrepresent the available ODRs to userspace and cause incorrect
pointer and length calculations.
> + default:
> + return -EINVAL;
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720202343.70138-1-macroalpha82@gmail.com?part=7
^ permalink raw reply
* Re: [PATCH] dt-bindings: power: maxim,max17042: Fix temp types to signed
From: Rob Herring (Arm) @ 2026-07-20 20:35 UTC (permalink / raw)
To: Rob Herring (Arm)
Cc: linux-kernel, Sebastian Krzyszkowiak, Krzysztof Kozlowski,
Marek Szyprowski, Sebastian Reichel, devicetree, Conor Dooley,
Purism Kernel Team, Hans de Goede, linux-pm
In-Reply-To: <20260612215315.1889039-1-robh@kernel.org>
On Fri, 12 Jun 2026 16:53:15 -0500, Rob Herring (Arm) wrote:
> The max17042 binding described "maxim,cold-temp" and
> "maxim,over-heat-temp" as unsigned values, but in-tree DTS examples
> use negative temperatures such as a cold threshold below zero degrees.
>
> Document the temperature properties as signed int32 values so the
> schema matches existing DTS data and the driver can use signed helpers.
>
> Assisted-by: Codex:gpt-5-5
> Signed-off-by: Rob Herring (Arm) <robh@kernel.org>
> ---
> .../devicetree/bindings/power/supply/maxim,max17042.yaml | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH 4/4] arm64: dts: socfpga: agilex5: add support for the ECC manager
From: sashiko-bot @ 2026-07-20 20:35 UTC (permalink / raw)
To: Dinh Nguyen; +Cc: robh, conor+dt, devicetree
In-Reply-To: <20260720201425.1538771-5-dinguyen@kernel.org>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] Incorrect double-bit error (DBERR) interrupt index for `emac0-rx-ecc` causes interrupt collisions and probe failure.
Pre-existing issues:
- [High] The eccmgr IRQ mask and unmask functions fail for DBERR interrupts on 64-bit systems due to `BIT(hwirq)` truncation.
--
--- Patch [4]: [PATCH 4/4] arm64: dts: socfpga: agilex5: add support for the ECC manager ---
commit ee1cc5ec088dd853387503f3ab86c7b4f685d0e9
Author: Dinh Nguyen <dinguyen@kernel.org>
Subject: arm64: dts: socfpga: agilex5: add support for the ECC manager
This commit adds the EDAC device to the base Agilex5 platform. It also
adds OCRAM and Ethernet ECC device support to the device tree.
> diff --git a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi
> --- a/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi
> +++ b/arch/arm64/boot/dts/intel/socfpga_agilex5.dtsi
[ ... ]
> + eccmgr {
> + compatible = "altr,socfpga-agilex5-ecc-manager",
> + "altr,socfpga-a10-ecc-manager";
[ ... ]
> + ocram-ecc@108cc000 {
> + compatible = "altr,socfpga-a10-ocram-ecc";
> + reg = <0x108cc000 0x100>;
> + interrupts = <1 IRQ_TYPE_LEVEL_HIGH>, <33 IRQ_TYPE_LEVEL_HIGH>;
> + };
[Severity: High]
This is a pre-existing issue, but does routing double-bit error interrupts
like 33 through the eccmgr IRQ domain expose a truncation regression on
64-bit platforms?
Looking at a10_eccmgr_irq_mask() in drivers/edac/altera_edac.c:
a10_eccmgr_irq_mask() {
...
regmap_write(edac->ecc_mgr_map, A10_SYSMGR_ECC_INTMASK_SET_OFST,
BIT(d->hwirq));
...
}
On ARM64, if d->hwirq is 33, BIT(33) produces a 64-bit value. Since
regmap_write() takes a 32-bit value parameter, won't this silently truncate
to 0?
The unmask variant has the same behavior, which means the generic IRQ
subsystem cannot mask or unmask these interrupts during an IRQ storm.
> +
> + emac0-rx-ecc@108c0000 {
> + compatible = "altr,socfpga-eth-mac-ecc";
> + reg = <0x108c0000 0x100>;
> + altr,ecc-parent = <&gmac0>;
> + interrupts = <4 IRQ_TYPE_LEVEL_HIGH>, <38 IRQ_TYPE_LEVEL_HIGH>;
> + };
[Severity: High]
Does emac0-rx-ecc claim the wrong double-bit error interrupt?
Since emac1-rx-ecc correctly claims interrupt 38 below, devm_request_irq()
will fail with -EBUSY when emac1-rx-ecc attempts to probe because the IRQ
is not shared.
If the single-bit error is 4, should the double-bit error be 36 instead of
38 to avoid this collision regression?
> +
> + emac0-tx-ecc@108c0400 {
> + compatible = "altr,socfpga-eth-mac-ecc";
> + reg = <0x108c0400 0x100>;
> + altr,ecc-parent = <&gmac0>;
> + interrupts = <5 IRQ_TYPE_LEVEL_HIGH>, <37 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + emac1-rx-ecc@108c0800 {
> + compatible = "altr,socfpga-eth-mac-ecc";
> + reg = <0x108c0800 0x100>;
> + altr,ecc-parent = <&gmac1>;
> + interrupts = <6 IRQ_TYPE_LEVEL_HIGH>, <38 IRQ_TYPE_LEVEL_HIGH>;
> + };
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720201425.1538771-1-dinguyen@kernel.org?part=4
^ permalink raw reply
* Re: [PATCH v2 2/2] of: reserved_mem: print skipped node name when too many regions are defined
From: Rob Herring (Arm) @ 2026-07-20 20:35 UTC (permalink / raw)
To: Sang-Heon Jeon; +Cc: saravanak, devicetree
In-Reply-To: <20260614133807.2165124-3-ekffu200098@gmail.com>
On Sun, 14 Jun 2026 22:38:07 +0900, Sang-Heon Jeon wrote:
> When too many reserved memory regions are defined,
> fdt_init_reserved_mem_node() logs an error and skips the region without
> naming the node.
>
> Print the node name so the skipped node can be identified, matching
> fdt_scan_reserved_mem().
>
> Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
> ---
> drivers/of/of_reserved_mem.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH v3] docs: dt: maintainer: Add Devicetree and OF maintainer profile document
From: Frank Li @ 2026-07-20 20:34 UTC (permalink / raw)
To: Rob Herring
Cc: Krzysztof Kozlowski, Krzysztof Kozlowski, Conor Dooley,
Jonathan Corbet, Shuah Khan, devicetree, workflows, linux-doc,
linux-kernel, Saravana Kannan
In-Reply-To: <CAL_JsqKkxVTVE=o-uar=JEVvqw9NhBAQcBrJ0ukf+RBXCwFZJA@mail.gmail.com>
On Mon, Jul 20, 2026 at 03:12:17PM -0500, Rob Herring wrote:
> On Mon, Jul 20, 2026 at 1:40 PM Krzysztof Kozlowski
> <krzysztof.kozlowski@oss.qualcomm.com> wrote:
> >
> > Document how Devicetree and Open Firmware maintainers handle their
> > subsystem, especially focusing on two caveats:
> >
> > Devicetree subsystem handles patches with a minor difference comparing
> > to other subsystems: while DT maintainers pick up OF code, they only
> > provide review of DT bindings without applying these.
> >
> > All three DT bindings maintainers rely currently on Patchwork and due to
> > enormous amount of emails per day, regardless how much DT maintainers
> > try, they cannot read all the emails.
> >
> > Cc: Rob Herring <robh@kernel.org>
> > Cc: Conor Dooley <conor+dt@kernel.org>
> > Cc: Saravana Kannan <saravanak@kernel.org>
> > Cc: devicetree@vger.kernel.org
> > Signed-off-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> >
> > ---
> >
> > I expect patch to be picked up by Rob, after review.
> >
> > Changes in v3:
> > 1. Add also F: entry
> >
> > Changes in v2:
> > 1. Correct typos and trailing white spaces.
> > 2. Fix order of P: after C: in maintainers.
> > ---
> > .../process/maintainer-devicetree.rst | 70 +++++++++++++++++++
> > MAINTAINERS | 3 +
> > 2 files changed, 73 insertions(+)
> > create mode 100644 Documentation/process/maintainer-devicetree.rst
> >
> > diff --git a/Documentation/process/maintainer-devicetree.rst b/Documentation/process/maintainer-devicetree.rst
> > new file mode 100644
> > index 000000000000..d8ffe752bf5d
> > --- /dev/null
> > +++ b/Documentation/process/maintainer-devicetree.rst
> > @@ -0,0 +1,70 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +======================================
> > +Devicetree and Open Firmware Subsystem
> > +======================================
> > +
> > +Other Process Documents
> > +-----------------------
> > +
> > +Please see the documents in Documentation/devicetree/bindings/ for information
> > +on how to write proper Devicetree bindings and how to submit patches.
> > +
> > +Patch Review and Handling
> > +-------------------------
> > +
> > +Patches handled by Devicetree maintainers are processed differently depending
> > +on the patch type:
> > +
> > +1. Core OF driver code, e.g. drivers/of/:
> > + patches are reviewed and applied by DT maintainers.
> > +
> > +2. Devicetree bindings:
> > + patches are reviewed by DT maintainers but, except in certain cases, should
> > + be applied by subsystem maintainers. See also *For kernel maintainers* in
> > + Documentation/devicetree/bindings/submitting-patches.rst.
>
> I would reword:
>
> patches are reviewed by DT maintainers, but should be applied by
> subsystem maintainers except in certain cases.
It would be wonderful if provide a few sample *certain* cases.
Anyways, nice document.
Reviewed-by: Frank Li <Frank.Li@nxp.com>
>
> > +
> > +3. DTS and drivers:
> > + DT maintainers might provide comments, but review is generally not expected.
>
> For DTS, we expect to pass schema checks or at least not add new warnings.
>
> I can address these 2 when applying.
>
> Rob
^ permalink raw reply
* Re: [PATCH v2 1/2] of: reserved_mem: prevent OOB when too many dynamic regions are defined
From: Rob Herring (Arm) @ 2026-07-20 20:34 UTC (permalink / raw)
To: Sang-Heon Jeon; +Cc: devicetree, saravanak
In-Reply-To: <20260614133807.2165124-2-ekffu200098@gmail.com>
On Sun, 14 Jun 2026 22:38:06 +0900, Sang-Heon Jeon wrote:
> On boot, fdt_scan_reserved_mem() saves each dynamically-placed
> /reserved-memory subnode into a local array of size
> MAX_RESERVED_REGIONS.
>
> If the device tree defines more than MAX_RESERVED_REGIONS
> dynamically-placed regions, fdt_scan_reserved_mem() writes past the
> end of the local array.
>
> Add a bounds check that logs an error and skips the excess regions,
> restoring the original behavior.
>
> Fixes: 8a6e02d0c00e ("of: reserved_mem: Restructure how the reserved memory regions are processed")
> Signed-off-by: Sang-Heon Jeon <ekffu200098@gmail.com>
> ---
> drivers/of/of_reserved_mem.c | 14 +++++++++++---
> 1 file changed, 11 insertions(+), 3 deletions(-)
>
Applied, thanks!
^ permalink raw reply
* Re: [PATCH 0/4] EDAC/altera: Add support for Agilex5 platform
From: Borislav Petkov @ 2026-07-20 20:33 UTC (permalink / raw)
To: Dinh Nguyen
Cc: tony.luck, rounakdas2025, niravkumar.l.rabara, linux-edac,
linux-kernel, robh, krzk+dt, conor+dt, devicetree
In-Reply-To: <20260720201425.1538771-1-dinguyen@kernel.org>
On Mon, Jul 20, 2026 at 03:14:21PM -0500, Dinh Nguyen wrote:
> This series add preliminary EDAC support for the Agilex5 platform.
So I'll be looking at new features to this driver only after all existing
Sashiko issues have been addressed.
Thx.
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
^ permalink raw reply
* Re: [PATCH] dt-bindings: extcon: ti,extcon-palmas: Convert to DT schema
From: sashiko-bot @ 2026-07-20 20:33 UTC (permalink / raw)
To: Bhargav Joshi; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260721-ti-extcon-palmas-v1-1-cd4071f0b9d9@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The device tree node name in the example uses an underscore (`palmas_usb`), which violates standard DT naming conventions. It should use a hyphen (e.g., `palmas-usb` or a generic name like `usb-comparator`). This change introduces a `dtc` warning (`node_name_chars_strict`) during `make dt_binding_check` that was not present in the original text binding.
--
--- Patch [1]: [PATCH] dt-bindings: extcon: ti,extcon-palmas: Convert to DT schema ---
commit bda055e9092743c0cf88faae7d326b21581d09bb
Author: Bhargav Joshi <j.bhargav.u@gmail.com>
dt-bindings: extcon: ti,extcon-palmas: Convert to DT schema
This commit converts the Texas Instruments Palmas USB Comparator binding from
the legacy text format to a modern DT schema.
> diff --git a/Documentation/devicetree/bindings/extcon/ti,extcon-palmas.yaml b/Documentation/devicetree/bindings/extcon/ti,extcon-palmas.yaml
> new file mode 100644
> index 0000000000000..0bcd8df79414a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/extcon/ti,extcon-palmas.yaml
[ ... ]
> +examples:
> + - |
> + #include <dt-bindings/gpio/gpio.h>
> +
> + palmas_usb {
[Severity: Low]
Is the underscore in this node name intentional?
The original text binding used a hyphen (palmas-usb). Using an underscore
violates standard device tree naming conventions and will trigger a dtc
node_name_chars_strict warning during make dt_binding_check.
Would it be better to stick with the original palmas-usb or use a generic
node name like usb-comparator?
> + compatible = "ti,palmas-usb-vid";
> + ti,wakeup;
> + ti,enable-id-detection;
> + ti,enable-vbus-detection;
> + id-gpios = <&palmas_gpio 0 GPIO_ACTIVE_HIGH>;
> + };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260721-ti-extcon-palmas-v1-1-cd4071f0b9d9@gmail.com?part=1
^ permalink raw reply
* Re: [PATCH 2/4] EDAC/altera: use flag to differentiate 64-bit platforms
From: sashiko-bot @ 2026-07-20 20:30 UTC (permalink / raw)
To: Dinh Nguyen; +Cc: conor+dt, devicetree, robh
In-Reply-To: <20260720201425.1538771-3-dinguyen@kernel.org>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [Low] A syntax error prevents compilation due to a typographical error in variable initialization.
Pre-existing issues:
- [High] The IRQ domain is leaked and chained IRQ handlers are left dangling on probe error paths.
- [High] The driver registers a panic notifier but never unregisters it, leading to a Use-After-Free on unbind.
--
--- Patch [2]: [PATCH 2/4] EDAC/altera: use flag to differentiate 64-bit platforms ---
commit 4db36eaf83f8ef9e44a35979b3fccecc08fabc69
Author: Dinh Nguyen <dinguyen@kernel.org>
Subject: EDAC/altera: use flag to differentiate 64-bit platforms
This commit introduces a flag to differentiate platforms like Stratix10 and
Agilex5 by storing it in the driver match data. It updates the device setup
and probe functions to check this flag instead of the boolean is_s10 field.
> diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
> --- a/drivers/edac/altera_edac.c
> +++ b/drivers/edac/altera_edac.c
> @@ -1507,7 +1507,7 @@ static int altr_portb_setup(struct altr_edac_device_dev *device)
> int edac_idx, rc;
> struct device_node *np;
> const struct edac_device_prv_data *prv = &a10_sdmmceccb_data;
> - bool is_s10 = device->edac->is_s10;
> + unsigned long flag = {unsigned long)device->edac->flag;
[Severity: Low]
Is there a syntax error in this type cast? Using a curly brace instead of a
parenthesis causes a build failure.
[ ... ]
> @@ -2152,8 +2152,8 @@ static int altr_edac_a10_probe(struct platform_device *pdev)
> irq_set_chained_handler_and_data(edac->sb_irq,
> altr_edac_a10_irq_handler,
> edac);
> - if (edac->is_s10)
> - {
> +
> + if (edac->flag == SOCFPGA_S10) {
[Severity: High]
This is a pre-existing issue, but does this probe error path leak the chained
IRQ handler and IRQ domain?
In altr_edac_a10_probe(), irq_set_chained_handler_and_data() is called for
sb_irq. If a later step fails, like fetching db_irq:
edac->db_irq = platform_get_irq(pdev, 1);
if (edac->db_irq < 0)
return edac->db_irq;
The probe function returns the error directly without unregistering the chained
handler. Since the edac structure is devm-allocated, it gets freed on probe
failure.
If the interrupt triggers later, could this cause a use-after-free when the
handler executes with a dangling edac pointer?
> int dberror, err_addr;
>
> edac->panic_notifier.notifier_call = s10_edac_dberr_handler;
> atomic_notifier_chain_register(&panic_notifier_list,
> &edac->panic_notifier);
[Severity: High]
This is a pre-existing issue, but does this driver ever unregister its panic
notifier?
The edac structure is devm-allocated, but I don't see a devm action or a
.remove callback to unregister the panic_notifier.
If the driver is unbound via sysfs, devres will free the edac structure,
leaving a dangling pointer in panic_notifier_list.
Could a subsequent panic attempt to call the freed notifier block and trigger
a nested panic?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260720201425.1538771-1-dinguyen@kernel.org?part=2
^ permalink raw reply
* Re: [PATCH 1/2] arm64: dts: allwinner: a523: Add missing SPDIF pin
From: Andre Przywara @ 2026-07-20 20:24 UTC (permalink / raw)
To: Per Larsson
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
moderated list:ARM/Allwinner sunXi SoC support,
open list:ARM/Allwinner sunXi SoC support, open list
In-Reply-To: <20260720174253.7686-1-per@palvencia.se>
On Mon, 20 Jul 2026 19:42:42 +0200
Per Larsson <per@palvencia.se> wrote:
Hi,
thanks for takign the time to send a patch!
> When spdif support was added for the Allwinner A523 family of SoCs, only
> two of the three possible pins were added to the dtsi, since the third
> would clash with the first ethernet port (emac0)
> However, some devices don't use emac0 and instead use emac1 for the only
> available ethernet port, leaving the pin free to use for spdif.
Not sure we need such an elaborate excuse to add a pinmux that is
used on one device - you could just say *that* instead.
But that's not a big issue, as the patch itself is correct.
> Add the missing pin to the dtsi so such devices can (later) use it
>
> Signed-off-by: Per Larsson <per@palvencia.se>
Checked the details against the user manual: they match:
Reviewed-by: Andre Przywara <andre.przywara@arm.com>
Cheers,
Andre
> ---
> arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi | 7 +++++++
> 1 file changed, 7 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
> index ca6a16807049..7370e8ed24ec 100644
> --- a/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
> +++ b/arch/arm64/boot/dts/allwinner/sun55i-a523.dtsi
> @@ -216,6 +216,13 @@ spdif_out_pb_pin: spdif-pb-pin {
> allwinner,pinmux = <2>;
> };
>
> + /omit-if-no-ref/
> + spdif_out_ph_pin: spdif-ph-pin {
> + pins = "PH7";
> + function = "spdif";
> + allwinner,pinmux = <4>;
> + };
> +
> /omit-if-no-ref/
> spdif_out_pi_pin: spdif-pi-pin {
> pins = "PI10";
^ permalink raw reply
* [PATCH V18 9/9] arm64: dts: rockchip: Add icm42607p IMU for RG-DS
From: Chris Morgan @ 2026-07-20 20: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: <20260720202343.70138-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 V18 8/9] iio: imu: inv_icm42607: Add Temp Support in icm42607
From: Chris Morgan @ 2026-07-20 20: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: <20260720202343.70138-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 | 5 +
.../iio/imu/inv_icm42607/inv_icm42607_gyro.c | 5 +
.../iio/imu/inv_icm42607/inv_icm42607_temp.c | 98 +++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_temp.h | 38 +++++++
5 files changed, 147 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 ad1135d48902..718a0201b6ed 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] = {
@@ -187,6 +190,8 @@ static int inv_icm42607_accel_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ACCEL:
break;
+ case IIO_TEMP:
+ return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
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 6af7b143e6a1..e7d5e552bb5c 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] = {
@@ -184,6 +187,8 @@ static int inv_icm42607_gyro_read_raw(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ANGL_VEL:
break;
+ case IIO_TEMP:
+ return inv_icm42607_temp_read_raw(indio_dev, chan, val, val2, mask);
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..8681ef2b49e2
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.c
@@ -0,0 +1,98 @@
+// 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_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..18499b4d0b94
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_temp.h
@@ -0,0 +1,38 @@
+/* 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, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+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 V18 7/9] iio: imu: inv_icm42607: Add Gyroscope to icm42607
From: Chris Morgan @ 2026-07-20 20: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: <20260720202343.70138-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 | 304 ++++++++++++++++++
4 files changed, 314 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 fd3e57ee4f14..4d51b0da1aa1 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -138,6 +138,7 @@ struct inv_icm42607_hw {
* @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.
* @conf: chip sensors configurations.
@@ -148,6 +149,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_conf conf;
@@ -412,6 +414,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 924a3698dfbc..d9e924f3ca93 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -569,6 +569,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..6af7b143e6a1
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_gyro.c
@@ -0,0 +1,304 @@
+// 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);
+}
+
+/* IIO format int + micro , values 0-4 reserved. */
+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 = INV_ICM42607_ODR_1600HZ; 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 = INV_ICM42607_ODR_1600HZ;
+ 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)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ *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;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ 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)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ANGL_VEL)
+ return -EINVAL;
+ 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;
+
+ 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);
+ iio_device_set_drvdata(indio_dev, st);
+
+ 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 V18 6/9] iio: imu: inv_icm42607: Add Accelerometer for icm42607
From: Chris Morgan @ 2026-07-20 20: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: <20260720202343.70138-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 | 307 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 184 +++++++++++
4 files changed, 530 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 61927a6c2663..fd3e57ee4f14 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -88,6 +88,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,
@@ -107,6 +118,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;
@@ -125,6 +137,7 @@ struct inv_icm42607_hw {
* @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.
* @conf: chip sensors configurations.
@@ -134,12 +147,23 @@ 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_conf conf;
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 */
@@ -368,12 +392,26 @@ 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_get_pwr_mgmt0(struct inv_icm42607_state *st,
enum inv_icm42607_sensor_mode *gyro,
enum inv_icm42607_sensor_mode *accel);
+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..ad1135d48902
--- /dev/null
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_accel.c
@@ -0,0 +1,307 @@
+// 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-4 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 = INV_ICM42607_ODR_1600HZ; 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 = INV_ICM42607_ODR_1600HZ;
+ 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)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ *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;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ 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)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+ 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;
+
+ 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);
+ iio_device_set_drvdata(indio_dev, st);
+
+ 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 c127ea0a94c4..924a3698dfbc 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -19,6 +19,8 @@
#include <linux/timekeeping.h>
#include <linux/types.h>
+#include <asm/byteorder.h>
+
#include "inv_icm42607.h"
static bool inv_icm42607_is_readable_reg(struct device *dev, unsigned int reg)
@@ -104,6 +106,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;
+}
+
int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st,
enum inv_icm42607_sensor_mode *gyro,
enum inv_icm42607_sensor_mode *accel)
@@ -191,6 +202,174 @@ 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)
+{
+ 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;
+
+ *config0 = (conf->fs != oldconf->fs) || (conf->odr != oldconf->odr);
+
+ *config1 = (conf->filter != oldconf->filter);
+}
+
+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;
+ unsigned int accel_mode, gyro_mode;
+ 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;
+ }
+
+ /*
+ * Since we are only making one-shot calls today, we can avoid needless
+ * on/off cycles if we rely on the runtime power management to handle
+ * shutting off the sensors when not in use. The downside is if we
+ * enable both sensors then only read from one, we have to wait for
+ * runtime PM to turn it off. So just keep the mode of the sensor we
+ * are not actively using to whatever it's already set as.
+ */
+ ret = inv_icm42607_get_pwr_mgmt0(st, &gyro_mode, &accel_mode);
+ if (ret)
+ return ret;
+
+ 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;
+ }
+
+ oldconf->mode = conf->mode;
+
+ switch (chan_type) {
+ case IIO_ACCEL:
+ return inv_icm42607_set_pwr_mgmt0(st, gyro_mode, conf->mode);
+ case IIO_ANGL_VEL:
+ return inv_icm42607_set_pwr_mgmt0(st, conf->mode, accel_mode);
+ default:
+ return -EINVAL;
+ }
+}
+
+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;
+ __be16 data;
+ 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 = be16_to_cpu(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)
{
@@ -385,6 +564,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 V18 5/9] iio: imu: inv_icm42607: Add PM support for icm42607
From: Chris Morgan @ 2026-07-20 20: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: <20260720202343.70138-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 | 14 ++
.../iio/imu/inv_icm42607/inv_icm42607_core.c | 168 ++++++++++++++++++
.../iio/imu/inv_icm42607/inv_icm42607_i2c.c | 8 +
.../iio/imu/inv_icm42607/inv_icm42607_spi.c | 8 +
4 files changed, 198 insertions(+)
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607.h b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
index 169e848caf2e..61927a6c2663 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607.h
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607.h
@@ -9,6 +9,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>
@@ -125,6 +126,7 @@ struct inv_icm42607_hw {
* @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.
* @conf: chip sensors configurations.
* @orientation: sensor chip orientation relative to main hardware.
*/
@@ -133,6 +135,7 @@ struct inv_icm42607_state {
struct mutex lock;
struct regmap *map;
struct regulator *vddio_supply;
+ bool vddio_en;
struct inv_icm42607_conf conf;
struct iio_mount_matrix orientation;
};
@@ -352,11 +355,22 @@ 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 (2 * MSEC_PER_SEC)
+
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_get_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode *gyro,
+ enum inv_icm42607_sensor_mode *accel);
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 6ec1d730017e..c127ea0a94c4 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_core.c
@@ -4,15 +4,19 @@
*/
#include <linux/bitfield.h>
+#include <linux/cleanup.h>
#include <linux/delay.h>
#include <linux/device.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"
@@ -100,6 +104,93 @@ const struct inv_icm42607_hw inv_icm42607p_hw_data = {
};
EXPORT_SYMBOL_NS_GPL(inv_icm42607p_hw_data, "IIO_ICM42607");
+int inv_icm42607_get_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode *gyro,
+ enum inv_icm42607_sensor_mode *accel)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(st->map, INV_ICM42607_REG_PWR_MGMT0, &val);
+ if (ret)
+ return ret;
+
+ *gyro = FIELD_GET(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, val);
+ *accel = FIELD_GET(INV_ICM42607_PWR_MGMT0_ACCEL_MODE_MASK, val);
+
+ return 0;
+}
+
+static int inv_icm42607_set_pwr_mgmt0(struct inv_icm42607_state *st,
+ enum inv_icm42607_sensor_mode gyro,
+ enum inv_icm42607_sensor_mode accel)
+{
+ enum inv_icm42607_sensor_mode oldaccel, oldgyro;
+ unsigned int sleepval_us;
+ unsigned int val;
+ s64 disable_wait;
+ int ret;
+
+ ret = inv_icm42607_get_pwr_mgmt0(st, &oldgyro, &oldaccel);
+ if (ret)
+ return ret;
+
+ 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. In case some platforms don't respond well to a
+ * sleep of 0, make sure the fsleep duration is > 0.
+ */
+ if (!gyro && oldgyro) {
+ disable_wait = clamp(ktime_us_delta(st->conf.gyro_stop, ktime_get()),
+ 0, INV_ICM42607_GYRO_STOP_TIME_US);
+
+ if (disable_wait > 0)
+ fsleep(disable_wait);
+ }
+
+ val = FIELD_PREP(INV_ICM42607_PWR_MGMT0_GYRO_MODE_MASK, gyro) |
+ 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.
+ */
+ sleepval_us = 0;
+ 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(),
+ INV_ICM42607_GYRO_STOP_TIME_US);
+ }
+
+ /*
+ * Only sleep if sleepval_us is greater than 0 in case some
+ * platforms have issues with a 0 delay. The 0 delay can happen
+ * if one or both sensors is shut down.
+ */
+ if (sleepval_us > 0)
+ fsleep(sleepval_us);
+
+ return 0;
+}
+
static int inv_icm42607_set_init_conf(struct inv_icm42607_state *st,
const struct inv_icm42607_conf *conf)
{
@@ -212,12 +303,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;
}
@@ -225,7 +321,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,
@@ -240,6 +341,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;
@@ -275,10 +378,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;
+
+ /* Sync the regcache again after regulator shutdown. */
+ regcache_mark_dirty(st->map);
+ ret = regcache_sync(st->map);
+ 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);
+
+ /*
+ * Set sensors state to off. Since we only support one-shot
+ * today we can use runtime PM to turn sensors off when not
+ * in use, and then when needed the reads/writes will
+ * re-enable the sensors as needed. This reduces complexity,
+ * however the tradeoff is that an unused sensor won't be
+ * turned off until the entire chip is no longer in use.
+ */
+ st->conf.gyro.mode = INV_ICM42607_SENSOR_MODE_OFF;
+ st->conf.accel.mode = INV_ICM42607_SENSOR_MODE_OFF;
+
+ return inv_icm42607_set_pwr_mgmt0(st, st->conf.gyro.mode,
+ st->conf.accel.mode);
+}
+
+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, NULL, 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 f2b9067815b0..04ad37291783 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_i2c.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include "inv_icm42607.h"
@@ -56,6 +57,11 @@ static int inv_icm42607_probe(struct i2c_client *client)
return inv_icm42607_core_probe(regmap, hw, inv_icm42607_i2c_bus_setup);
}
+static void inv_icm42607_i2c_remove(struct i2c_client *client)
+{
+ pm_runtime_force_suspend(&client->dev);
+}
+
static const struct i2c_device_id inv_icm42607_id[] = {
{
.name = "icm42607",
@@ -84,9 +90,11 @@ 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,
+ .remove = inv_icm42607_i2c_remove,
};
module_i2c_driver(inv_icm42607_driver);
diff --git a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
index eb04036a6712..995393dce942 100644
--- a/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
+++ b/drivers/iio/imu/inv_icm42607/inv_icm42607_spi.c
@@ -7,6 +7,7 @@
#include <linux/dev_printk.h>
#include <linux/err.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
@@ -65,6 +66,11 @@ static int inv_icm42607_probe(struct spi_device *spi)
return inv_icm42607_core_probe(regmap, hw, inv_icm42607_spi_bus_setup);
}
+static void inv_icm42607_spi_remove(struct spi_device *spi)
+{
+ pm_runtime_force_suspend(&spi->dev);
+}
+
static const struct spi_device_id inv_icm42607_spi_id_table[] = {
{
.name = "icm42607",
@@ -94,9 +100,11 @@ 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,
+ .remove = inv_icm42607_spi_remove,
};
module_spi_driver(inv_icm42607_driver);
--
2.43.0
^ permalink raw reply related
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