* [PATCH 1/7] arm64: dts: qcom: sdm845: Bump fastrpc shared DMA pool to 32MB for 16k pages
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 14:42 ` Abel Vesa
2026-11-11 11:53 ` [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock Ard Biesheuvel
` (5 subsequent siblings)
6 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
The CMA driver wants memory pools whose base and size are aligned to the
PMD size, which is 32M when running with 16k pages. So increase the
shared DMA pool size from 16M to 32M and set its minimum alignment to
32M as well.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/boot/dts/qcom/sdm845.dtsi | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm845.dtsi b/arch/arm64/boot/dts/qcom/sdm845.dtsi
index 844b840bc546..5f554f6b06f7 100644
--- a/arch/arm64/boot/dts/qcom/sdm845.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdm845.dtsi
@@ -894,8 +894,8 @@ mdata_mem: mpss-metadata {
fastrpc_mem: fastrpc {
compatible = "shared-dma-pool";
alloc-ranges = <0x0 0x00000000 0x0 0xffffffff>;
- alignment = <0x0 0x400000>;
- size = <0x0 0x1000000>;
+ alignment = <0x0 0x2000000>;
+ size = <0x0 0x2000000>;
reusable;
};
};
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
2026-11-11 11:53 ` [PATCH 1/7] arm64: dts: qcom: sdm845: Bump fastrpc shared DMA pool to 32MB for 16k pages Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 12:43 ` Dmitry Baryshkov
2026-11-11 11:53 ` [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode Ard Biesheuvel
` (4 subsequent siblings)
6 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
The SN65DSI86 eDP bridge on the Yoga C630 is described as being clocked by
an always-on fixed 19.2 MHz reference clock. However, as on the SDM845
reference design (Cheza), its REFCLK appears to be fed by the LN_BB_CLK2
output of the PMIC, which also serves as the master clock of the WCD9340
audio codec.
As nothing tells Linux that the bridge depends on LN_BB_CLK2, the clock
only runs when the codec happens to have it enabled, and the WCD934x
driver only enables it on demand. If the bridge is brought up while
LN_BB_CLK2 is off or being toggled, e.g., when the display stack probes
before the codec, or while the codec is being initialized, all AUX
transfers fail and the DP PLL does not lock, leaving the panel dark:
ti_sn65dsi86 10-002c: Can't read lane count (-110); assuming 4
ti_sn65dsi86 10-002c: Can't read eDP rev (-110), assuming 1.1
ti_sn65dsi86 10-002c: Can't read max rate (-110); assuming 5.4 GHz
ti_sn65dsi86 10-002c: DP_PLL_LOCK polling failed (-110)
So describe the actual clock instead, so that the bridge driver keeps it
enabled for as long as the bridge is powered up.
Fixes: 956e9c85f47b ("arm64: dts: qcom: c630: Define eDP bridge and panel")
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index 9d38ae83c979..a4a573ea7e4a 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -97,13 +97,6 @@ sw_edp_1p2: edp-1p2-regulator {
vin-supply = <&vreg_l2a_1p2>;
};
- sn65dsi86_refclk: sn65dsi86-refclk {
- compatible = "fixed-clock";
- #clock-cells = <0>;
-
- clock-frequency = <19200000>;
- };
-
vph_pwr: regulator-vph-pwr {
compatible = "regulator-fixed";
regulator-name = "vph_pwr";
@@ -528,7 +521,7 @@ sn65dsi86: bridge@2c {
vpll-supply = <&vreg_l14a_1p88>;
vccio-supply = <&vreg_l14a_1p88>;
- clocks = <&sn65dsi86_refclk>;
+ clocks = <&rpmhcc RPMH_LN_BB_CLK2>;
clock-names = "refclk";
no-hpd;
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock
2026-11-11 11:53 ` [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock Ard Biesheuvel
@ 2026-11-11 12:43 ` Dmitry Baryshkov
2026-11-11 13:15 ` Ard Biesheuvel
0 siblings, 1 reply; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:43 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 12:53:06PM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> The SN65DSI86 eDP bridge on the Yoga C630 is described as being clocked by
> an always-on fixed 19.2 MHz reference clock. However, as on the SDM845
> reference design (Cheza), its REFCLK appears to be fed by the LN_BB_CLK2
> output of the PMIC, which also serves as the master clock of the WCD9340
> audio codec.
>
> As nothing tells Linux that the bridge depends on LN_BB_CLK2, the clock
> only runs when the codec happens to have it enabled, and the WCD934x
> driver only enables it on demand. If the bridge is brought up while
> LN_BB_CLK2 is off or being toggled, e.g., when the display stack probes
> before the codec, or while the codec is being initialized, all AUX
> transfers fail and the DP PLL does not lock, leaving the panel dark:
>
> ti_sn65dsi86 10-002c: Can't read lane count (-110); assuming 4
> ti_sn65dsi86 10-002c: Can't read eDP rev (-110), assuming 1.1
> ti_sn65dsi86 10-002c: Can't read max rate (-110); assuming 5.4 GHz
> ti_sn65dsi86 10-002c: DP_PLL_LOCK polling failed (-110)
>
> So describe the actual clock instead, so that the bridge driver keeps it
> enabled for as long as the bridge is powered up.
According to the schematics the actual clock is a divider clock coming
from the GPIO16 of the PMIC.
>
> Fixes: 956e9c85f47b ("arm64: dts: qcom: c630: Define eDP bridge and panel")
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 9 +--------
> 1 file changed, 1 insertion(+), 8 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> index 9d38ae83c979..a4a573ea7e4a 100644
> --- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> +++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
> @@ -97,13 +97,6 @@ sw_edp_1p2: edp-1p2-regulator {
> vin-supply = <&vreg_l2a_1p2>;
> };
>
> - sn65dsi86_refclk: sn65dsi86-refclk {
> - compatible = "fixed-clock";
> - #clock-cells = <0>;
> -
> - clock-frequency = <19200000>;
> - };
> -
> vph_pwr: regulator-vph-pwr {
> compatible = "regulator-fixed";
> regulator-name = "vph_pwr";
> @@ -528,7 +521,7 @@ sn65dsi86: bridge@2c {
> vpll-supply = <&vreg_l14a_1p88>;
> vccio-supply = <&vreg_l14a_1p88>;
>
> - clocks = <&sn65dsi86_refclk>;
> + clocks = <&rpmhcc RPMH_LN_BB_CLK2>;
> clock-names = "refclk";
>
> no-hpd;
> --
> 2.56.0.611.g1030f0b940-goog
>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock
2026-11-11 12:43 ` Dmitry Baryshkov
@ 2026-11-11 13:15 ` Ard Biesheuvel
2026-11-11 13:45 ` Ard Biesheuvel
0 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 13:15 UTC (permalink / raw)
To: Dmitry Baryshkov, Ard Biesheuvel
Cc: linux-arm-kernel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
On Wed, 11 Nov 2026, at 13:43, Dmitry Baryshkov wrote:
> On Wed, Nov 11, 2026 at 12:53:06PM +0100, Ard Biesheuvel wrote:
>> From: Ard Biesheuvel <ardb@kernel.org>
>>
>> The SN65DSI86 eDP bridge on the Yoga C630 is described as being clocked by
>> an always-on fixed 19.2 MHz reference clock. However, as on the SDM845
>> reference design (Cheza), its REFCLK appears to be fed by the LN_BB_CLK2
>> output of the PMIC, which also serves as the master clock of the WCD9340
>> audio codec.
>>
>> As nothing tells Linux that the bridge depends on LN_BB_CLK2, the clock
>> only runs when the codec happens to have it enabled, and the WCD934x
>> driver only enables it on demand. If the bridge is brought up while
>> LN_BB_CLK2 is off or being toggled, e.g., when the display stack probes
>> before the codec, or while the codec is being initialized, all AUX
>> transfers fail and the DP PLL does not lock, leaving the panel dark:
>>
>> ti_sn65dsi86 10-002c: Can't read lane count (-110); assuming 4
>> ti_sn65dsi86 10-002c: Can't read eDP rev (-110), assuming 1.1
>> ti_sn65dsi86 10-002c: Can't read max rate (-110); assuming 5.4 GHz
>> ti_sn65dsi86 10-002c: DP_PLL_LOCK polling failed (-110)
>>
>> So describe the actual clock instead, so that the bridge driver keeps it
>> enabled for as long as the bridge is powered up.
>
> According to the schematics the actual clock is a divider clock coming
> from the GPIO16 of the PMIC.
>
Which is always powered, I suppose?
So I suppose that means just the arbitrary stall resulting from the
clock enable is sufficient to make this issue go away? Any clue what
else it might be waiting for?
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock
2026-11-11 13:15 ` Ard Biesheuvel
@ 2026-11-11 13:45 ` Ard Biesheuvel
0 siblings, 0 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 13:45 UTC (permalink / raw)
To: Dmitry Baryshkov, Ard Biesheuvel
Cc: linux-arm-kernel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
On Wed, 11 Nov 2026, at 14:15, Ard Biesheuvel wrote:
> On Wed, 11 Nov 2026, at 13:43, Dmitry Baryshkov wrote:
>> On Wed, Nov 11, 2026 at 12:53:06PM +0100, Ard Biesheuvel wrote:
>>> From: Ard Biesheuvel <ardb@kernel.org>
>>>
>>> The SN65DSI86 eDP bridge on the Yoga C630 is described as being clocked by
>>> an always-on fixed 19.2 MHz reference clock. However, as on the SDM845
>>> reference design (Cheza), its REFCLK appears to be fed by the LN_BB_CLK2
>>> output of the PMIC, which also serves as the master clock of the WCD9340
>>> audio codec.
>>>
>>> As nothing tells Linux that the bridge depends on LN_BB_CLK2, the clock
>>> only runs when the codec happens to have it enabled, and the WCD934x
>>> driver only enables it on demand. If the bridge is brought up while
>>> LN_BB_CLK2 is off or being toggled, e.g., when the display stack probes
>>> before the codec, or while the codec is being initialized, all AUX
>>> transfers fail and the DP PLL does not lock, leaving the panel dark:
>>>
>>> ti_sn65dsi86 10-002c: Can't read lane count (-110); assuming 4
>>> ti_sn65dsi86 10-002c: Can't read eDP rev (-110), assuming 1.1
>>> ti_sn65dsi86 10-002c: Can't read max rate (-110); assuming 5.4 GHz
>>> ti_sn65dsi86 10-002c: DP_PLL_LOCK polling failed (-110)
>>>
>>> So describe the actual clock instead, so that the bridge driver keeps it
>>> enabled for as long as the bridge is powered up.
>>
>> According to the schematics the actual clock is a divider clock coming
>> from the GPIO16 of the PMIC.
>>
>
> Which is always powered, I suppose?
>
> So I suppose that means just the arbitrary stall resulting from the
> clock enable is sufficient to make this issue go away? Any clue what
> else it might be waiting for?
I've just double checked with the old DT: the system comes up with the
panel dark, but as soon as I do
echo 1 | sudo tee /sys/kernel/debug/clk/ln_bb_clk2/clk_prepare_enable
[on a writable clocks kernel], the panel lights up fine after I blank
and unblank it.
Could it be an error in the schematic?
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
2026-11-11 11:53 ` [PATCH 1/7] arm64: dts: qcom: sdm845: Bump fastrpc shared DMA pool to 32MB for 16k pages Ard Biesheuvel
2026-11-11 11:53 ` [PATCH 2/7] arm64: dts: qcom: c630: Use LN_BB_CLK2 as the eDP bridge reference clock Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 12:50 ` Dmitry Baryshkov
2026-11-11 14:42 ` Abel Vesa
2026-11-11 11:53 ` [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables Ard Biesheuvel
` (3 subsequent siblings)
6 siblings, 2 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
The right-hand USB-C port supports DisplayPort alt mode. The EC takes care
of entering it, and ucsi_yoga_c630 already sets the orientation of the
combo PHY and passes the EC's DP hotplug events down the bridge chain. All
that is missing is the DisplayPort controller, which sdm845.dtsi already
connects to the combo PHY.
Limit it to two lanes. That is what the PHY provides alongside USB3 in its
default mode, and it matches pin assignment D, which the EC picked with
the monitor I tested. Four lanes would need the PHY to be switched to its
DP only mode, which nothing does on this platform.
Tested with a USB-C monitor: 1920x1200@60 over two lanes at HBR2, with the
monitor's USB3 hub working at the same time.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index a4a573ea7e4a..0645ab429177 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -589,6 +589,14 @@ &mdss {
status = "okay";
};
+&mdss_dp {
+ status = "okay";
+};
+
+&mdss_dp_out {
+ data-lanes = <0 1>;
+};
+
&mdss_dsi0 {
status = "okay";
vdda-supply = <&vreg_l26a_1p2>;
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode
2026-11-11 11:53 ` [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode Ard Biesheuvel
@ 2026-11-11 12:50 ` Dmitry Baryshkov
2026-11-11 14:42 ` Abel Vesa
1 sibling, 0 replies; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:50 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 12:53:07PM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> The right-hand USB-C port supports DisplayPort alt mode. The EC takes care
> of entering it, and ucsi_yoga_c630 already sets the orientation of the
> combo PHY and passes the EC's DP hotplug events down the bridge chain. All
> that is missing is the DisplayPort controller, which sdm845.dtsi already
> connects to the combo PHY.
>
> Limit it to two lanes. That is what the PHY provides alongside USB3 in its
> default mode, and it matches pin assignment D, which the EC picked with
> the monitor I tested. Four lanes would need the PHY to be switched to its
> DP only mode, which nothing does on this platform.
>
> Tested with a USB-C monitor: 1920x1200@60 over two lanes at HBR2, with the
> monitor's USB3 hub working at the same time.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode
2026-11-11 11:53 ` [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode Ard Biesheuvel
2026-11-11 12:50 ` Dmitry Baryshkov
@ 2026-11-11 14:42 ` Abel Vesa
1 sibling, 0 replies; 21+ messages in thread
From: Abel Vesa @ 2026-11-11 14:42 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On 26-11-11 12:53:07, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> The right-hand USB-C port supports DisplayPort alt mode. The EC takes care
> of entering it, and ucsi_yoga_c630 already sets the orientation of the
> combo PHY and passes the EC's DP hotplug events down the bridge chain. All
> that is missing is the DisplayPort controller, which sdm845.dtsi already
> connects to the combo PHY.
>
> Limit it to two lanes. That is what the PHY provides alongside USB3 in its
> default mode, and it matches pin assignment D, which the EC picked with
> the monitor I tested. Four lanes would need the PHY to be switched to its
> DP only mode, which nothing does on this platform.
>
> Tested with a USB-C monitor: 1920x1200@60 over two lanes at HBR2, with the
> monitor's USB3 hub working at the same time.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
` (2 preceding siblings ...)
2026-11-11 11:53 ` [PATCH 3/7] arm64: dts: qcom: c630: Enable DisplayPort alt mode Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 12:56 ` Dmitry Baryshkov
2026-11-11 14:42 ` Abel Vesa
2026-11-11 11:53 ` [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume Ard Biesheuvel
` (2 subsequent siblings)
6 siblings, 2 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
Commit 07db10de262f ("arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower
PSCI cluster idle") dropped bit 14 from the PSCI suspend parameter of the
cluster idle state, as the C630 would occasionally reset with the sdm845
default. However, the firmware refuses the resulting value of 0x41008244:
the cluster power domain is never entered, in EL1 or EL2, and the last CPU
of the cluster keeps retrying instead, including during suspend to idle.
In EL2, the firmware accepts the sdm845 default of 0x4100c244. However,
besides powering down the cluster, this state also puts the rest of the
SoC to sleep (LLCC off, AOSS sleep), and entering it at runtime while the
display is off, e.g., right after locking the screen, makes the C630
reset.
The _LPI objects in the C630's DSDT describe the states that Windows uses:
on top of CPU state C4 (0x40000004), the cluster container has
L3Cluster.D4 (0x40) at level 0x01000000, and the system container has
platform.F2 (0xc300). So use L3Cluster.D4, i.e., 0x41000044, which only
powers down the cluster and its L3 cache, as the cluster idle state.
Add platform.F2, i.e., 0x4100c344, as a second, deeper cluster idle state,
which lets the SoC reach AOSS sleep and CX collapse during suspend to idle.
It differs from the sdm845 default in bits 11:8, which are 3 rather than
2, like in the corresponding states of newer SoCs and of the sc8180x and
sc8280xp WoA platforms. Give it a minimum residency of about 71 minutes
to keep the runtime governor away from it: suspend to idle always picks
the deepest state that satisfies the CPU wakeup latency QoS constraint,
regardless of its residency.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 25 +++++++++++++++++++-
1 file changed, 24 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index 0645ab429177..3e4df4335be8 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -37,6 +37,24 @@ aliases {
serial1 = &uart6;
};
+ cpus {
+ domain-idle-states {
+ /*
+ * platform.F2 on top of L3Cluster.D4 from the ACPI tables,
+ * which also lets the rest of the SoC sleep. Suspend to idle
+ * picks the deepest state regardless of its residency, so
+ * use one that keeps the runtime governor away from it.
+ */
+ cluster_sleep_1: cluster-sleep-1 {
+ compatible = "domain-idle-state";
+ arm,psci-suspend-param = <0x4100c344>;
+ entry-latency-us = <3263>;
+ exit-latency-us = <6562>;
+ min-residency-us = <0xffffffff>;
+ };
+ };
+ };
+
gpio-keys {
compatible = "gpio-keys";
@@ -340,8 +358,13 @@ &cdsp_pas {
status = "okay";
};
+&cluster_pd {
+ domain-idle-states = <&cluster_sleep_0>, <&cluster_sleep_1>;
+};
+
+/* L3Cluster.D4 from the ACPI tables: powers down the cluster and its L3 */
&cluster_sleep_0 {
- arm,psci-suspend-param = <0x41008244>;
+ arm,psci-suspend-param = <0x41000044>;
};
&gcc {
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables
2026-11-11 11:53 ` [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables Ard Biesheuvel
@ 2026-11-11 12:56 ` Dmitry Baryshkov
2026-11-11 14:42 ` Abel Vesa
1 sibling, 0 replies; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:56 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 12:53:08PM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> Commit 07db10de262f ("arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower
> PSCI cluster idle") dropped bit 14 from the PSCI suspend parameter of the
> cluster idle state, as the C630 would occasionally reset with the sdm845
> default. However, the firmware refuses the resulting value of 0x41008244:
> the cluster power domain is never entered, in EL1 or EL2, and the last CPU
> of the cluster keeps retrying instead, including during suspend to idle.
>
> In EL2, the firmware accepts the sdm845 default of 0x4100c244. However,
> besides powering down the cluster, this state also puts the rest of the
> SoC to sleep (LLCC off, AOSS sleep), and entering it at runtime while the
> display is off, e.g., right after locking the screen, makes the C630
> reset.
>
> The _LPI objects in the C630's DSDT describe the states that Windows uses:
> on top of CPU state C4 (0x40000004), the cluster container has
> L3Cluster.D4 (0x40) at level 0x01000000, and the system container has
> platform.F2 (0xc300). So use L3Cluster.D4, i.e., 0x41000044, which only
> powers down the cluster and its L3 cache, as the cluster idle state.
>
> Add platform.F2, i.e., 0x4100c344, as a second, deeper cluster idle state,
> which lets the SoC reach AOSS sleep and CX collapse during suspend to idle.
> It differs from the sdm845 default in bits 11:8, which are 3 rather than
> 2, like in the corresponding states of newer SoCs and of the sc8180x and
> sc8280xp WoA platforms. Give it a minimum residency of about 71 minutes
> to keep the runtime governor away from it: suspend to idle always picks
> the deepest state that satisfies the CPU wakeup latency QoS constraint,
> regardless of its residency.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 25 +++++++++++++++++++-
> 1 file changed, 24 insertions(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables
2026-11-11 11:53 ` [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables Ard Biesheuvel
2026-11-11 12:56 ` Dmitry Baryshkov
@ 2026-11-11 14:42 ` Abel Vesa
1 sibling, 0 replies; 21+ messages in thread
From: Abel Vesa @ 2026-11-11 14:42 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On 26-11-11 12:53:08, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> Commit 07db10de262f ("arm64: dts: qcom: sdm850-lenovo-yoga-c630: lower
> PSCI cluster idle") dropped bit 14 from the PSCI suspend parameter of the
> cluster idle state, as the C630 would occasionally reset with the sdm845
> default. However, the firmware refuses the resulting value of 0x41008244:
> the cluster power domain is never entered, in EL1 or EL2, and the last CPU
> of the cluster keeps retrying instead, including during suspend to idle.
>
> In EL2, the firmware accepts the sdm845 default of 0x4100c244. However,
> besides powering down the cluster, this state also puts the rest of the
> SoC to sleep (LLCC off, AOSS sleep), and entering it at runtime while the
> display is off, e.g., right after locking the screen, makes the C630
> reset.
>
> The _LPI objects in the C630's DSDT describe the states that Windows uses:
> on top of CPU state C4 (0x40000004), the cluster container has
> L3Cluster.D4 (0x40) at level 0x01000000, and the system container has
> platform.F2 (0xc300). So use L3Cluster.D4, i.e., 0x41000044, which only
> powers down the cluster and its L3 cache, as the cluster idle state.
>
> Add platform.F2, i.e., 0x4100c344, as a second, deeper cluster idle state,
> which lets the SoC reach AOSS sleep and CX collapse during suspend to idle.
> It differs from the sdm845 default in bits 11:8, which are 3 rather than
> 2, like in the corresponding states of newer SoCs and of the sc8180x and
> sc8280xp WoA platforms. Give it a minimum residency of about 71 minutes
> to keep the runtime governor away from it: suspend to idle always picks
> the deepest state that satisfies the CPU wakeup latency QoS constraint,
> regardless of its residency.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
` (3 preceding siblings ...)
2026-11-11 11:53 ` [PATCH 4/7] arm64: dts: qcom: c630: Use the idle states from the ACPI tables Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 12:22 ` Ard Biesheuvel
2026-11-11 12:56 ` Dmitry Baryshkov
2026-11-11 11:53 ` [PATCH 6/7] platform: arm64: lenovo-yoga-c630: Disable the EC interrupt while suspended Ard Biesheuvel
2026-11-11 11:53 ` [PATCH 7/7] platform: arm64: lenovo-yoga-c630: Expose the keyboard backlight as an LED Ard Biesheuvel
6 siblings, 2 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
When the SoC reaches deep sleep (AOSS sleep and CX collapse) in suspend
to idle, the touchpad is often reset, presumably because it loses power,
and it may still be starting up when it is powered on again at resume.
If it doesn't acknowledge the power on command, i2c-hid tries once more
0.5 ms later, and then fails the resume:
[ 712.566907] i2c_hid_of 3-0015: failed to change power setting.
[ 712.567528] i2c_hid_of 3-0015: PM: dpm_run_callback(): i2c_hid_core_pm_resume [i2c_hid] returns -6
hid-multitouch then never puts the touchpad back into touchpad mode, so
it stays in the mouse mode it starts up in, without two-finger
scrolling, until the next suspend and resume.
This happened after about one in eight suspends. With a debug patch that
keeps trying, the touchpad answered the next attempt, about 6 ms later,
each time it had missed the first two.
So give the touchpad a post power on delay of 20 ms, which i2c-hid-of
waits for at probe, and at every resume before powering the touchpad on,
as the touchpad isn't a wakeup source.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
index 3e4df4335be8..accd80005195 100644
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
@@ -496,6 +496,8 @@ tsel: hid@15 {
hid-descr-addr = <0x1>;
interrupts-extended = <&tlmm 37 IRQ_TYPE_LEVEL_HIGH>;
+
+ post-power-on-delay-ms = <20>;
};
tsc2: hid@2c {
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume
2026-11-11 11:53 ` [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume Ard Biesheuvel
@ 2026-11-11 12:22 ` Ard Biesheuvel
2026-11-11 12:58 ` Dmitry Baryshkov
2026-11-11 12:56 ` Dmitry Baryshkov
1 sibling, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 12:22 UTC (permalink / raw)
To: Ard Biesheuvel, linux-arm-kernel
Cc: Bjorn Andersson, Konrad Dybcio, Abel Vesa, Dmitry Baryshkov,
linux-arm-msm
On Wed, 11 Nov 2026, at 12:53, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> When the SoC reaches deep sleep (AOSS sleep and CX collapse) in suspend
> to idle, the touchpad is often reset, presumably because it loses power,
> and it may still be starting up when it is powered on again at resume.
> If it doesn't acknowledge the power on command, i2c-hid tries once more
> 0.5 ms later, and then fails the resume:
>
> [ 712.566907] i2c_hid_of 3-0015: failed to change power setting.
> [ 712.567528] i2c_hid_of 3-0015: PM: dpm_run_callback():
> i2c_hid_core_pm_resume [i2c_hid] returns -6
>
> hid-multitouch then never puts the touchpad back into touchpad mode, so
> it stays in the mouse mode it starts up in, without two-finger
> scrolling, until the next suspend and resume.
>
> This happened after about one in eight suspends. With a debug patch that
> keeps trying, the touchpad answered the next attempt, about 6 ms later,
> each time it had missed the first two.
>
> So give the touchpad a post power on delay of 20 ms, which i2c-hid-of
> waits for at probe, and at every resume before powering the touchpad on,
> as the touchpad isn't a wakeup source.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 2 ++
> 1 file changed, 2 insertions(+)
>
Please disregard this one - it doesn't help in the end.
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume
2026-11-11 12:22 ` Ard Biesheuvel
@ 2026-11-11 12:58 ` Dmitry Baryshkov
2026-11-11 13:16 ` Ard Biesheuvel
0 siblings, 1 reply; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:58 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: Ard Biesheuvel, linux-arm-kernel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 01:22:35PM +0100, Ard Biesheuvel wrote:
>
>
> On Wed, 11 Nov 2026, at 12:53, Ard Biesheuvel wrote:
> > From: Ard Biesheuvel <ardb@kernel.org>
> >
> > When the SoC reaches deep sleep (AOSS sleep and CX collapse) in suspend
> > to idle, the touchpad is often reset, presumably because it loses power,
> > and it may still be starting up when it is powered on again at resume.
> > If it doesn't acknowledge the power on command, i2c-hid tries once more
> > 0.5 ms later, and then fails the resume:
> >
> > [ 712.566907] i2c_hid_of 3-0015: failed to change power setting.
> > [ 712.567528] i2c_hid_of 3-0015: PM: dpm_run_callback():
> > i2c_hid_core_pm_resume [i2c_hid] returns -6
> >
> > hid-multitouch then never puts the touchpad back into touchpad mode, so
> > it stays in the mouse mode it starts up in, without two-finger
> > scrolling, until the next suspend and resume.
> >
> > This happened after about one in eight suspends. With a debug patch that
> > keeps trying, the touchpad answered the next attempt, about 6 ms later,
> > each time it had missed the first two.
> >
> > So give the touchpad a post power on delay of 20 ms, which i2c-hid-of
> > waits for at probe, and at every resume before powering the touchpad on,
> > as the touchpad isn't a wakeup source.
> >
> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> > ---
> > arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 2 ++
> > 1 file changed, 2 insertions(+)
> >
>
> Please disregard this one - it doesn't help in the end.
Are there any additional power supplies that might need to be enabled?
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume
2026-11-11 12:58 ` Dmitry Baryshkov
@ 2026-11-11 13:16 ` Ard Biesheuvel
0 siblings, 0 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 13:16 UTC (permalink / raw)
To: Dmitry Baryshkov
Cc: Ard Biesheuvel, linux-arm-kernel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, 11 Nov 2026, at 13:58, Dmitry Baryshkov wrote:
> On Wed, Nov 11, 2026 at 01:22:35PM +0100, Ard Biesheuvel wrote:
>>
>>
>> On Wed, 11 Nov 2026, at 12:53, Ard Biesheuvel wrote:
>> > From: Ard Biesheuvel <ardb@kernel.org>
>> >
>> > When the SoC reaches deep sleep (AOSS sleep and CX collapse) in suspend
>> > to idle, the touchpad is often reset, presumably because it loses power,
>> > and it may still be starting up when it is powered on again at resume.
>> > If it doesn't acknowledge the power on command, i2c-hid tries once more
>> > 0.5 ms later, and then fails the resume:
>> >
>> > [ 712.566907] i2c_hid_of 3-0015: failed to change power setting.
>> > [ 712.567528] i2c_hid_of 3-0015: PM: dpm_run_callback():
>> > i2c_hid_core_pm_resume [i2c_hid] returns -6
>> >
>> > hid-multitouch then never puts the touchpad back into touchpad mode, so
>> > it stays in the mouse mode it starts up in, without two-finger
>> > scrolling, until the next suspend and resume.
>> >
>> > This happened after about one in eight suspends. With a debug patch that
>> > keeps trying, the touchpad answered the next attempt, about 6 ms later,
>> > each time it had missed the first two.
>> >
>> > So give the touchpad a post power on delay of 20 ms, which i2c-hid-of
>> > waits for at probe, and at every resume before powering the touchpad on,
>> > as the touchpad isn't a wakeup source.
>> >
>> > Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
>> > ---
>> > arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 2 ++
>> > 1 file changed, 2 insertions(+)
>> >
>>
>> Please disregard this one - it doesn't help in the end.
>
> Are there any additional power supplies that might need to be enabled?
>
No idea - does the schematic provide any clarity here?
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume
2026-11-11 11:53 ` [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume Ard Biesheuvel
2026-11-11 12:22 ` Ard Biesheuvel
@ 2026-11-11 12:56 ` Dmitry Baryshkov
1 sibling, 0 replies; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:56 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 12:53:09PM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> When the SoC reaches deep sleep (AOSS sleep and CX collapse) in suspend
> to idle, the touchpad is often reset, presumably because it loses power,
> and it may still be starting up when it is powered on again at resume.
> If it doesn't acknowledge the power on command, i2c-hid tries once more
> 0.5 ms later, and then fails the resume:
>
> [ 712.566907] i2c_hid_of 3-0015: failed to change power setting.
> [ 712.567528] i2c_hid_of 3-0015: PM: dpm_run_callback(): i2c_hid_core_pm_resume [i2c_hid] returns -6
>
> hid-multitouch then never puts the touchpad back into touchpad mode, so
> it stays in the mouse mode it starts up in, without two-finger
> scrolling, until the next suspend and resume.
>
> This happened after about one in eight suspends. With a debug patch that
> keeps trying, the touchpad answered the next attempt, about 6 ms later,
> each time it had missed the first two.
>
> So give the touchpad a post power on delay of 20 ms, which i2c-hid-of
> waits for at probe, and at every resume before powering the touchpad on,
> as the touchpad isn't a wakeup source.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts | 2 ++
> 1 file changed, 2 insertions(+)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 6/7] platform: arm64: lenovo-yoga-c630: Disable the EC interrupt while suspended
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
` (4 preceding siblings ...)
2026-11-11 11:53 ` [PATCH 5/7] arm64: dts: qcom: c630: Wait for the touchpad at resume Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
2026-11-11 12:57 ` Dmitry Baryshkov
2026-11-11 11:53 ` [PATCH 7/7] platform: arm64: lenovo-yoga-c630: Expose the keyboard backlight as an LED Ard Biesheuvel
6 siblings, 1 reply; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
The EC interrupt handler reads the pending event from the EC over I2C. If
the interrupt fires while the system is suspending or resuming, between
the late suspend and early resume phases of the I2C controller, the
transfer fails, as runtime PM is disabled for the controller:
geni_i2c 884000.i2c: error turning SE resources:-13
The handler then returns without having read the event, and as the
interrupt is level triggered, it fires again right away. On the Yoga C630,
this shows up as tens to thousands of these errors in about half of the
suspends.
So disable the EC interrupt when suspending, and enable it again when
resuming. Pending events are then handled once the I2C controller can be
used again.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/platform/arm64/lenovo-yoga-c630.c | 34 +++++++++++++++++++-
1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c
index cad40c58c55b..2776063bfb58 100644
--- a/drivers/platform/arm64/lenovo-yoga-c630.c
+++ b/drivers/platform/arm64/lenovo-yoga-c630.c
@@ -10,11 +10,13 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
+#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
+#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/platform_data/lenovo-yoga-c630.h>
@@ -216,6 +218,7 @@ static int yoga_c630_ec_probe(struct i2c_client *client)
mutex_init(&ec->lock);
ec->client = client;
BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
+ i2c_set_clientdata(client, ec);
ret = devm_request_threaded_irq(dev, client->irq,
NULL, yoga_c630_ec_thread_intr,
@@ -230,6 +233,34 @@ static int yoga_c630_ec_probe(struct i2c_client *client)
return yoga_c630_aux_init(dev, YOGA_C630_DEV_UCSI, ec);
}
+/*
+ * Keep the EC interrupt disabled until the system has resumed. Its
+ * handler reads the pending event over I2C, which fails between the late
+ * suspend and early resume phases of the I2C controller, and as the
+ * interrupt is level triggered, it would fire over and over again until the
+ * handler gets to read the event.
+ */
+static int yoga_c630_ec_suspend(struct device *dev)
+{
+ struct yoga_c630_ec *ec = dev_get_drvdata(dev);
+
+ disable_irq(ec->client->irq);
+
+ return 0;
+}
+
+static int yoga_c630_ec_resume(struct device *dev)
+{
+ struct yoga_c630_ec *ec = dev_get_drvdata(dev);
+
+ enable_irq(ec->client->irq);
+
+ return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(yoga_c630_ec_pm_ops, yoga_c630_ec_suspend,
+ yoga_c630_ec_resume);
+
static const struct of_device_id yoga_c630_ec_of_match[] = {
{ .compatible = "lenovo,yoga-c630-ec" },
@@ -246,7 +277,8 @@ MODULE_DEVICE_TABLE(i2c, yoga_c630_ec_i2c_id_table);
static struct i2c_driver yoga_c630_ec_i2c_driver = {
.driver = {
.name = "yoga-c630-ec",
- .of_match_table = yoga_c630_ec_of_match
+ .of_match_table = yoga_c630_ec_of_match,
+ .pm = pm_sleep_ptr(&yoga_c630_ec_pm_ops),
},
.probe = yoga_c630_ec_probe,
.id_table = yoga_c630_ec_i2c_id_table,
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH 6/7] platform: arm64: lenovo-yoga-c630: Disable the EC interrupt while suspended
2026-11-11 11:53 ` [PATCH 6/7] platform: arm64: lenovo-yoga-c630: Disable the EC interrupt while suspended Ard Biesheuvel
@ 2026-11-11 12:57 ` Dmitry Baryshkov
0 siblings, 0 replies; 21+ messages in thread
From: Dmitry Baryshkov @ 2026-11-11 12:57 UTC (permalink / raw)
To: Ard Biesheuvel
Cc: linux-arm-kernel, Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio,
Abel Vesa, Dmitry Baryshkov, linux-arm-msm
On Wed, Nov 11, 2026 at 12:53:10PM +0100, Ard Biesheuvel wrote:
> From: Ard Biesheuvel <ardb@kernel.org>
>
> The EC interrupt handler reads the pending event from the EC over I2C. If
> the interrupt fires while the system is suspending or resuming, between
> the late suspend and early resume phases of the I2C controller, the
> transfer fails, as runtime PM is disabled for the controller:
>
> geni_i2c 884000.i2c: error turning SE resources:-13
>
> The handler then returns without having read the event, and as the
> interrupt is level triggered, it fires again right away. On the Yoga C630,
> this shows up as tens to thousands of these errors in about half of the
> suspends.
>
> So disable the EC interrupt when suspending, and enable it again when
> resuming. Pending events are then handled once the I2C controller can be
> used again.
>
> Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
> ---
> drivers/platform/arm64/lenovo-yoga-c630.c | 34 +++++++++++++++++++-
> 1 file changed, 33 insertions(+), 1 deletion(-)
>
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 7/7] platform: arm64: lenovo-yoga-c630: Expose the keyboard backlight as an LED
2026-11-11 11:53 [PATCH 0/7] Lenovo Yoga C630 (sdm845) fixes Ard Biesheuvel
` (5 preceding siblings ...)
2026-11-11 11:53 ` [PATCH 6/7] platform: arm64: lenovo-yoga-c630: Disable the EC interrupt while suspended Ard Biesheuvel
@ 2026-11-11 11:53 ` Ard Biesheuvel
6 siblings, 0 replies; 21+ messages in thread
From: Ard Biesheuvel @ 2026-11-11 11:53 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Ard Biesheuvel, Bjorn Andersson, Konrad Dybcio, Abel Vesa,
Dmitry Baryshkov, linux-arm-msm
From: Ard Biesheuvel <ardb@kernel.org>
The EC turns the keyboard backlight on and off as Fn+Space is pressed,
cycling through two brightness levels, which the EC registers do not
seem to reflect. However, bit 0 of EC register 0x03 reflects whether
the backlight is on, and setting or clearing it, using the EC's register
write command (0xb1, as used by the DSDT's ECNW method), turns the
backlight on at the selected brightness level, or off.
Expose this as a platform::kbd_backlight LED with a maximum brightness
of 1, so that userspace can turn off the keyboard backlight, e.g., when
the system is idle. Whenever the EC raises an event, check whether the
backlight was turned on or off, and report any such change through
brightness_hw_changed, in case the EC raises an event when Fn+Space is
pressed.
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
drivers/platform/arm64/Kconfig | 4 +-
drivers/platform/arm64/lenovo-yoga-c630.c | 121 +++++++++++++++++++-
2 files changed, 120 insertions(+), 5 deletions(-)
diff --git a/drivers/platform/arm64/Kconfig b/drivers/platform/arm64/Kconfig
index 2a84775a13a9..44d5a749f764 100644
--- a/drivers/platform/arm64/Kconfig
+++ b/drivers/platform/arm64/Kconfig
@@ -75,10 +75,12 @@ config EC_LENOVO_YOGA_C630
depends on ARCH_QCOM || COMPILE_TEST
depends on I2C
select AUXILIARY_BUS
+ select LEDS_CLASS
+ select NEW_LEDS
help
Driver for the Embedded Controller in the Qualcomm Snapdragon-based
Lenovo Yoga C630, which provides battery and power adapter
- information.
+ information, and access to the keyboard backlight.
This driver provides battery and AC status support for the mentioned
laptop where this information is not properly exposed via the
diff --git a/drivers/platform/arm64/lenovo-yoga-c630.c b/drivers/platform/arm64/lenovo-yoga-c630.c
index 2776063bfb58..7f49067ec6b7 100644
--- a/drivers/platform/arm64/lenovo-yoga-c630.c
+++ b/drivers/platform/arm64/lenovo-yoga-c630.c
@@ -6,12 +6,14 @@
* Dmitry Baryshkov
*/
#include <linux/auxiliary_bus.h>
+#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
+#include <linux/leds.h>
#include <linux/lockdep.h>
#include <linux/module.h>
#include <linux/mutex.h>
@@ -27,14 +29,20 @@
#define LENOVO_EC_UCSI_READ 0x21
#define LENOVO_EC_READ_REG 0xb0
+#define LENOVO_EC_WRITE_REG 0xb1
#define LENOVO_EC_REQUEST_NEXT_EVENT 0x84
#define LENOVO_EC_UCSI_VERSION 0x20
+#define LENOVO_EC_KBD_BACKLIGHT_REG 0x03
+#define LENOVO_EC_KBD_BACKLIGHT_ON BIT(0)
+
struct yoga_c630_ec {
struct i2c_client *client;
struct mutex lock;
struct blocking_notifier_head notifier_list;
+ struct led_classdev kbd_backlight;
+ enum led_brightness kbd_backlight_brightness;
};
static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len,
@@ -53,21 +61,27 @@ static int yoga_c630_ec_request(struct yoga_c630_ec *ec, u8 *req, size_t req_len
resp_len, resp);
}
-int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr)
+static int __yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr)
{
- u8 req[2] = { LENOVO_EC_READ_REG, };
+ u8 req[2] = { LENOVO_EC_READ_REG, addr };
int ret;
u8 val;
- guard(mutex)(&ec->lock);
+ lockdep_assert_held(&ec->lock);
- req[1] = addr;
ret = yoga_c630_ec_request(ec, req, sizeof(req), &val, 1);
if (ret < 0)
return ret;
return val;
}
+
+int yoga_c630_ec_read8(struct yoga_c630_ec *ec, u8 addr)
+{
+ guard(mutex)(&ec->lock);
+
+ return __yoga_c630_ec_read8(ec, addr);
+}
EXPORT_SYMBOL_GPL(yoga_c630_ec_read8);
int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr)
@@ -97,6 +111,31 @@ int yoga_c630_ec_read16(struct yoga_c630_ec *ec, u8 addr)
}
EXPORT_SYMBOL_GPL(yoga_c630_ec_read16);
+/*
+ * Update the bits in @mask of EC register @addr to those in @val. Return the
+ * previous value of the register, or a negative error code.
+ */
+static int yoga_c630_ec_update_bits(struct yoga_c630_ec *ec, u8 addr, u8 mask,
+ u8 val)
+{
+ u8 req[3] = { LENOVO_EC_WRITE_REG, addr, };
+ int old, ret;
+
+ guard(mutex)(&ec->lock);
+
+ old = __yoga_c630_ec_read8(ec, addr);
+ if (old < 0 || (old & mask) == (val & mask))
+ return old;
+
+ req[2] = (old & ~mask) | (val & mask);
+ ret = i2c_smbus_write_i2c_block_data(ec->client, LENOVO_EC_REQUEST_REG,
+ sizeof(req), req);
+ if (ret < 0)
+ return ret;
+
+ return old;
+}
+
u16 yoga_c630_ec_ucsi_get_version(struct yoga_c630_ec *ec)
{
u8 req[3] = { 0xb3, 0xf2, };
@@ -148,6 +187,73 @@ int yoga_c630_ec_ucsi_read(struct yoga_c630_ec *ec,
}
EXPORT_SYMBOL_GPL(yoga_c630_ec_ucsi_read);
+static enum led_brightness
+yoga_c630_ec_kbd_backlight_get(struct led_classdev *led_cdev)
+{
+ struct yoga_c630_ec *ec = container_of(led_cdev, struct yoga_c630_ec,
+ kbd_backlight);
+ int val;
+
+ val = yoga_c630_ec_read8(ec, LENOVO_EC_KBD_BACKLIGHT_REG);
+ if (val < 0)
+ return val;
+
+ return val & LENOVO_EC_KBD_BACKLIGHT_ON ? LED_ON : LED_OFF;
+}
+
+static int yoga_c630_ec_kbd_backlight_set(struct led_classdev *led_cdev,
+ enum led_brightness brightness)
+{
+ struct yoga_c630_ec *ec = container_of(led_cdev, struct yoga_c630_ec,
+ kbd_backlight);
+ int old;
+
+ old = yoga_c630_ec_update_bits(ec, LENOVO_EC_KBD_BACKLIGHT_REG,
+ LENOVO_EC_KBD_BACKLIGHT_ON,
+ brightness ? LENOVO_EC_KBD_BACKLIGHT_ON : 0);
+ if (old < 0)
+ return old;
+
+ WRITE_ONCE(ec->kbd_backlight_brightness, brightness ? LED_ON : LED_OFF);
+
+ return 0;
+}
+
+/* Report changes made by the EC itself, i.e., when Fn+Space is pressed */
+static void yoga_c630_ec_kbd_backlight_update(struct yoga_c630_ec *ec)
+{
+ int brightness;
+
+ brightness = yoga_c630_ec_kbd_backlight_get(&ec->kbd_backlight);
+ if (brightness < 0 ||
+ brightness == READ_ONCE(ec->kbd_backlight_brightness))
+ return;
+
+ WRITE_ONCE(ec->kbd_backlight_brightness, brightness);
+ led_classdev_notify_brightness_hw_changed(&ec->kbd_backlight,
+ brightness);
+}
+
+static int yoga_c630_ec_kbd_backlight_init(struct yoga_c630_ec *ec)
+{
+ struct led_classdev *led_cdev = &ec->kbd_backlight;
+ int brightness;
+
+ brightness = yoga_c630_ec_kbd_backlight_get(led_cdev);
+ if (brightness < 0)
+ return brightness;
+
+ ec->kbd_backlight_brightness = brightness;
+
+ led_cdev->name = "platform::kbd_backlight";
+ led_cdev->flags = LED_BRIGHT_HW_CHANGED;
+ led_cdev->max_brightness = LED_ON;
+ led_cdev->brightness_get = yoga_c630_ec_kbd_backlight_get;
+ led_cdev->brightness_set_blocking = yoga_c630_ec_kbd_backlight_set;
+
+ return devm_led_classdev_register(&ec->client->dev, led_cdev);
+}
+
static irqreturn_t yoga_c630_ec_thread_intr(int irq, void *data)
{
u8 req[] = { LENOVO_EC_REQUEST_NEXT_EVENT };
@@ -163,6 +269,8 @@ static irqreturn_t yoga_c630_ec_thread_intr(int irq, void *data)
blocking_notifier_call_chain(&ec->notifier_list, event, ec);
+ yoga_c630_ec_kbd_backlight_update(ec);
+
return IRQ_HANDLED;
}
@@ -220,6 +328,11 @@ static int yoga_c630_ec_probe(struct i2c_client *client)
BLOCKING_INIT_NOTIFIER_HEAD(&ec->notifier_list);
i2c_set_clientdata(client, ec);
+ ret = yoga_c630_ec_kbd_backlight_init(ec);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to register keyboard backlight\n");
+
ret = devm_request_threaded_irq(dev, client->irq,
NULL, yoga_c630_ec_thread_intr,
IRQF_ONESHOT, "yoga_c630_ec", ec);
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply related [flat|nested] 21+ messages in thread