diff --git a/projects/ROCKNIX/devices/SM8550/linux/linux.aarch64.conf b/projects/ROCKNIX/devices/SM8550/linux/linux.aarch64.conf index c6c1190e5a6..fd454616c6c 100644 --- a/projects/ROCKNIX/devices/SM8550/linux/linux.aarch64.conf +++ b/projects/ROCKNIX/devices/SM8550/linux/linux.aarch64.conf @@ -933,7 +933,8 @@ CONFIG_BLK_DEV_WRITE_MOUNTED=y # CONFIG_BLK_CGROUP_IOPRIO is not set CONFIG_BLK_DEBUG_FS=y # CONFIG_BLK_SED_OPAL is not set -# CONFIG_BLK_INLINE_ENCRYPTION is not set +CONFIG_BLK_INLINE_ENCRYPTION=y +# CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK is not set # # Partition Types @@ -5785,6 +5786,7 @@ CONFIG_MMC_HSQ=y # CONFIG_MMC_SDHCI_XENON is not set CONFIG_SCSI_UFSHCD=y CONFIG_SCSI_UFS_BSG=y +CONFIG_SCSI_UFS_CRYPTO=y # CONFIG_SCSI_UFS_HWMON is not set # CONFIG_SCSI_UFSHCD_PCI is not set CONFIG_SCSI_UFSHCD_PLATFORM=y @@ -6550,6 +6552,7 @@ CONFIG_QCOM_STATS=y CONFIG_QCOM_WCNSS_CTRL=m CONFIG_QCOM_APR=y CONFIG_QCOM_ICC_BWMON=y +CONFIG_QCOM_INLINE_CRYPTO_ENGINE=y CONFIG_QCOM_PBS=y # end of Qualcomm SoC drivers diff --git a/projects/ROCKNIX/devices/SM8550/options b/projects/ROCKNIX/devices/SM8550/options index 7e9a0f46e54..8f33f334016 100644 --- a/projects/ROCKNIX/devices/SM8550/options +++ b/projects/ROCKNIX/devices/SM8550/options @@ -28,7 +28,7 @@ KERNEL_MAKE_EXTRACMD=" $(get_kernel_make_extracmd)" # Kernel cmdline - EXTRA_CMDLINE="quiet rootwait console=tty0 allow_mismatched_32bit_el0 fw_devlink.strict=1 pcie_ports=compat irqaffinity=0-2 cgroup.memory=nokmem,nosocket nosoftlockup usbcore.interrupt_interval_override=045e:028e:2" + EXTRA_CMDLINE="quiet rootwait console=tty0 allow_mismatched_32bit_el0 fw_devlink.strict=1 pcie_ports=compat irqaffinity=0-2 cgroup.memory=nokmem,nosocket nosoftlockup usbcore.interrupt_interval_override=045e:028e:2 ufshcd_core.uic_cmd_timeout=3000 mem_sleep_default=deep" # Bootloader to use (syslinux / u-boot) BOOTLOADER="qcom-abl" diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0201-scsi-ufs-drain-relink-completions-out-of-band-pm.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0201-scsi-ufs-drain-relink-completions-out-of-band-pm.patch new file mode 100644 index 00000000000..a2a6309f360 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0201-scsi-ufs-drain-relink-completions-out-of-band-pm.patch @@ -0,0 +1,420 @@ +From: jaewun +Subject: [PATCH] scsi: ufs: drain relink completions during PM + +On this non-MCQ controller the UFS interrupt is a threaded IRQF_ONESHOT +line. If the threaded handler is left pending across a PM transition, +genirq keeps the line masked and ufshcd_intr() is not re-entered. UIC and +UTP completions from the resume relink can then remain latched in +REG_INTERRUPT_STATUS while the relink waits for completion. + +Drain those completions without depending on a new UFS IRQ: + + - Use ufshcd_relinking() to cover both PM relink and the explicit + error-handler reset window. + - Use disable_irq_nosync() while relinking so the PM/EH path does not + wait on the threaded handler it is bypassing. + - Drain UTP/UIC/TMF completions from the hardirq PM path and from a PM + poll timer, and read-clear UIC error-code registers after errors. + - Complete failed DME_LINK_STARTUP UIC commands with + UIC_CMD_RESULT_FAILURE so link startup can retry. + - On non-MCQ reset completion, requeue all outstanding SCSI commands + instead of trusting stale post-reset OCS state. + +Also recover a failed SW clk-gating hibern8 exit inline. That path runs +outside pm_op_in_progress during the freezer phase; scheduling fatal EH there +can leave the freezer waiting on I/O that depends on the same recovery. Keep +clk-gating hibern8 enabled, but suppress the fatal EH guard for that specific +ungate window and run link recovery directly. + +Signed-off-by: jaewun +--- +--- a/drivers/ufs/core/ufshcd.c ++++ b/drivers/ufs/core/ufshcd.c +@@ -348,6 +348,20 @@ + static void ufshcd_hba_vreg_set_lpm(struct ufs_hba *hba); + static void ufshcd_hba_vreg_set_hpm(struct ufs_hba *hba); + ++/* ++ * True while the controller is being relinked during PM or during the explicit ++ * error-handler reset bracket. In these windows the threaded IRQ handler may ++ * not run, so ufshcd_disable_irq() must not synchronize on it and completions ++ * are drained without relying on a new UFS IRQ. A dedicated flag is used rather ++ * than UFSHCD_STATE_RESET so generic resets/probe are unaffected. Lock-free ++ * cross-context reads -> READ_ONCE. ++ */ ++static inline bool ufshcd_relinking(struct ufs_hba *hba) ++{ ++ return READ_ONCE(hba->pm_op_in_progress) || ++ READ_ONCE(hba->relink_poll_active); ++} ++ + void ufshcd_enable_irq(struct ufs_hba *hba) + { + if (!hba->is_irq_enabled) { +@@ -360,7 +374,16 @@ + void ufshcd_disable_irq(struct ufs_hba *hba) + { + if (hba->is_irq_enabled) { +- disable_irq(hba->irq); ++ /* ++ * hba->irq is threaded and IRQF_ONESHOT. During PM/EH relink the ++ * threaded handler is bypassed and completions are serviced directly, ++ * so do not wait for the thread here. The controller reset and direct ++ * completion drain handle any in-flight state. ++ */ ++ if (ufshcd_relinking(hba)) ++ disable_irq_nosync(hba->irq); ++ else ++ disable_irq(hba->irq); + hba->is_irq_enabled = false; + } + } +@@ -1921,11 +1944,33 @@ + hba->clk_gating.is_suspended = true; + if (ufshcd_is_link_hibern8(hba)) { + ret = ufshcd_uic_hibern8_exit(hba); +- if (ret) +- dev_err(hba->dev, "%s: hibern8 exit failed %d\n", ++ if (ret) { ++ /* ++ * This SW clk-gating hibern8 exit runs outside ++ * pm_op_in_progress. During the freezer phase, ++ * scheduling fatal EH can leave I/O waiting on the ++ * same recovery. Run the relink inline and bracket it ++ * with the poller so direct completion draining covers ++ * link startup. ++ */ ++ dev_err(hba->dev, "%s: hibern8 exit failed %d, recovering link\n", + __func__, ret); +- else ++ if (!hba->mcq_enabled) { ++ WRITE_ONCE(hba->relink_poll_active, true); ++ mod_timer(&hba->pm_poll_timer, ++ jiffies + msecs_to_jiffies(2)); ++ } ++ ret = ufshcd_link_recovery(hba); ++ if (!hba->mcq_enabled) { ++ WRITE_ONCE(hba->relink_poll_active, false); ++ timer_delete_sync(&hba->pm_poll_timer); ++ } ++ if (ret) ++ dev_err(hba->dev, "%s: link recovery after hibern8 exit failed %d\n", ++ __func__, ret); ++ } else { + ufshcd_set_link_active(hba); ++ } + } + hba->clk_gating.is_suspended = false; + } +@@ -4382,7 +4427,12 @@ + spin_lock_irqsave(hba->host->host_lock, flags); + hba->active_uic_cmd = NULL; + hba->uic_async_done = NULL; +- if (ret && !hba->pm_op_in_progress) { ++ /* ++ * Suppress fatal link-broken+EH for the SW clk-gating ungate hibern8 ++ * exit. That path runs outside pm_op_in_progress during the freezer ++ * phase; ufshcd_ungate_work handles recovery inline instead. ++ */ ++ if (ret && !hba->pm_op_in_progress && !hba->clk_gating.is_suspended) { + ufshcd_set_link_broken(hba); + ufshcd_schedule_eh_work(hba); + } +@@ -6383,11 +6433,53 @@ + ufs_debugfs_exception_event(hba, status); + } + +-/* Complete requests that have door-bell cleared */ ++/* ++ * Non-MCQ counterpart of ufshcd_mcq_force_compl_one(): invoked via ++ * ufshcd_complete_requests(force_compl=true) from ufshcd_host_reset_and_restore() ++ * (and the error handler) AFTER ufshcd_hba_stop() has reset the controller. The ++ * normal single-doorbell path (ufshcd_transfer_req_compl/ufshcd_poll) only ++ * completes tags whose doorbell bit reads back clear, so a request left ++ * outstanding by a failed UIC/link op is not completed. The in-memory OCS is ++ * stale after reset, so requeue SCSI commands rather than reporting a ++ * possibly-bogus result; reserved device-management tags are completed by ++ * their own paths and skipped (mirrors ufshcd_mcq_force_compl_one()). ++ */ ++static bool ufshcd_force_compl_one(struct request *rq, void *priv) ++{ ++ struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq); ++ struct scsi_device *sdev = rq->q->queuedata; ++ struct ufs_hba *hba = shost_priv(sdev->host); ++ unsigned long flags; ++ ++ if (blk_mq_is_reserved_rq(rq)) ++ return true; ++ ++ spin_lock_irqsave(&hba->outstanding_lock, flags); ++ __clear_bit(rq->tag, &hba->outstanding_reqs); ++ spin_unlock_irqrestore(&hba->outstanding_lock, flags); ++ ++ if (!test_bit(SCMD_STATE_COMPLETE, &cmd->state)) { ++ set_host_byte(cmd, DID_REQUEUE); ++ ufshcd_release_scsi_cmd(hba, cmd); ++ scsi_done(cmd); ++ } ++ ++ return true; ++} ++ ++/* Complete requests that have door-bell cleared (or, on force, all outstanding) */ + static void ufshcd_complete_requests(struct ufs_hba *hba, bool force_compl) + { + if (hba->mcq_enabled) + ufshcd_mcq_compl_pending_transfer(hba, force_compl); ++ else if (force_compl) ++ /* ++ * The controller has been reset; complete every outstanding ++ * request (the doorbell no longer reflects them) so no request ++ * remains stuck. ++ */ ++ blk_mq_tagset_busy_iter(&hba->host->tag_set, ++ ufshcd_force_compl_one, NULL); + else + ufshcd_transfer_req_compl(hba); + +@@ -6863,6 +6955,16 @@ + + hba->force_reset = false; + spin_unlock_irqrestore(hba->host->host_lock, flags); ++ /* ++ * Arm the out-of-band completion poller across the error-handler ++ * relink. ufshcd_state == UFSHCD_STATE_RESET here and pm_op_in_progress ++ * is clear, so ufshcd_relinking() gates the drain and nosync IRQ ++ * disable while the reset is in progress. ++ */ ++ if (!hba->mcq_enabled) { ++ WRITE_ONCE(hba->relink_poll_active, true); ++ mod_timer(&hba->pm_poll_timer, jiffies + msecs_to_jiffies(2)); ++ } + err = ufshcd_reset_and_restore(hba); + if (err) + dev_err(hba->dev, "%s: reset and restore failed with err %d\n", +@@ -6889,6 +6991,14 @@ + } + ufshcd_clear_eh_in_progress(hba); + spin_unlock_irqrestore(hba->host->host_lock, flags); ++ /* ++ * Stop the relink poller before unprepare can release the clocks. Clear the ++ * flag first so the callback sees ufshcd_relinking() false and will not ++ * re-arm, then synchronously delete. Done after the host_lock is dropped ++ * (the drain takes host_lock). ++ */ ++ WRITE_ONCE(hba->relink_poll_active, false); ++ timer_delete_sync(&hba->pm_poll_timer); + ufshcd_err_handling_unprepare(hba); + up(&hba->host_sem); + +@@ -7207,6 +7317,112 @@ + return retval; + } + ++/* ++ * ufshcd_pm_drain_completions - service UTP/UIC/TMF completions without the IRQ ++ * @hba: per adapter instance ++ * ++ * The UFS interrupt is a threaded IRQF_ONESHOT line. If its threaded handler is ++ * left pending across a PM transition the genirq core keeps the line masked, so ++ * ufshcd_intr() is not re-invoked and relink completions can remain pending. ++ * Called inline from ufshcd_intr()'s PM fast path (hardirq) AND from the PM poll ++ * timer (softirq), so it must not depend on the UFS IRQ firing. pm_drain_active ++ * serialises the two callers so they cannot both W1C REG_INTERRUPT_STATUS. ++ */ ++static void ufshcd_pm_drain_completions(struct ufs_hba *hba) ++{ ++ int retries = hba->nutrs; ++ ++ if (test_and_set_bit(0, &hba->pm_drain_active)) ++ return; ++ ++ while (retries-- > 0) { ++ unsigned long flags; ++ u32 status, compl_bits; ++ ++ status = ufshcd_readl(hba, REG_INTERRUPT_STATUS) & ++ ufshcd_readl(hba, REG_INTERRUPT_ENABLE); ++ if (!status) ++ break; ++ ++ /* W1C exactly the snapshot we read. */ ++ ufshcd_writel(hba, status, REG_INTERRUPT_STATUS); ++ ++ if (status & UFSHCD_ERROR_MASK) { ++ /* ++ * read-to-clear all UIC error-code registers so a ++ * transient resume-time error cannot storm. ++ */ ++ ufshcd_readl(hba, REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER); ++ ufshcd_readl(hba, REG_UIC_ERROR_CODE_DATA_LINK_LAYER); ++ ufshcd_readl(hba, REG_UIC_ERROR_CODE_NETWORK_LAYER); ++ ufshcd_readl(hba, REG_UIC_ERROR_CODE_TRANSPORT_LAYER); ++ ufshcd_readl(hba, REG_UIC_ERROR_CODE_DME); ++ ++ /* ++ * If the relink's in-flight UIC command (e.g. ++ * DME_LINKSTARTUP) errored with no accompanying UIC ++ * completion, force-complete it with UIC_CMD_RESULT_FAILURE ++ * so ufshcd_wait_for_uic_cmd() returns promptly and the ++ * link startup can retry. Restricted to DME_LINK_STARTUP ++ * with no uic_async_done in flight so power-mode UIC ++ * commands are untouched. irqsave: this also runs in ++ * softirq (the PM poll timer). ++ */ ++ spin_lock_irqsave(hba->host->host_lock, flags); ++ if (!(status & UFSHCD_UIC_MASK) && !hba->uic_async_done && ++ hba->active_uic_cmd && ++ hba->active_uic_cmd->cmd_active && ++ hba->active_uic_cmd->command == ++ UIC_CMD_DME_LINK_STARTUP) { ++ struct uic_command *cmd = hba->active_uic_cmd; ++ ++ cmd->argument2 = (cmd->argument2 & ++ ~MASK_UIC_COMMAND_RESULT) | ++ UIC_CMD_RESULT_FAILURE; ++ cmd->cmd_active = false; ++ complete(&cmd->done); ++ } ++ spin_unlock_irqrestore(hba->host->host_lock, flags); ++ } ++ ++ compl_bits = status & (UTP_TRANSFER_REQ_COMPL | UFSHCD_UIC_MASK); ++ if (compl_bits & UFSHCD_UIC_MASK) ++ ufshcd_uic_cmd_compl(hba, compl_bits); ++ if (status & UTP_TASK_REQ_COMPL) ++ ufshcd_tmc_handler(hba); ++ if (compl_bits & UTP_TRANSFER_REQ_COMPL) ++ ufshcd_transfer_req_compl(hba); ++ } ++ ++ clear_bit(0, &hba->pm_drain_active); ++} ++ ++/* ++ * ufshcd_pm_poll_timer_fn - out-of-band UFS completion drain during PM resume. ++ * ++ * Armed across the PM relink window. Fires from the arch timer, so it services ++ * completions even if the IRQ line remains oneshot-masked. Self-rearms while ++ * relinking; the PM/EH paths clear the state and delete the timer on exit. ++ */ ++static void ufshcd_pm_poll_timer_fn(struct timer_list *t) ++{ ++ struct ufs_hba *hba = timer_container_of(hba, t, pm_poll_timer); ++ ++ if (hba->mcq_enabled || !ufshcd_relinking(hba)) ++ return; ++ ++ ufshcd_pm_drain_completions(hba); ++ ++ /* ++ * Re-check before re-arming so a concurrent disarm (which clears the ++ * relink state before timer_delete_sync()) cannot lose the race and leave ++ * the timer armed. A stray re-arm would be harmless anyway -- the next tick ++ * sees the state clear and returns without MMIO -- but avoid it. ++ */ ++ if (ufshcd_relinking(hba)) ++ mod_timer(&hba->pm_poll_timer, jiffies + msecs_to_jiffies(2)); ++} ++ + /** + * ufshcd_intr - Main interrupt service routine + * @irq: irq number +@@ -7222,6 +7438,18 @@ + struct ufs_hba *hba = __hba; + u32 intr_status, enabled_intr_status; + ++ /* ++ * On this non-MCQ controller interrupt handling is normally deferred to ++ * the threaded handler, which may not run during PM relink. Service ++ * UTP/UIC/TMF completions inline and read-clear UIC error-code registers. ++ * Do not run ufshcd_check_errors() here: it can schedule error handling ++ * while PM recovery is already in progress. ++ */ ++ if (!hba->mcq_enabled && ufshcd_relinking(hba)) { ++ ufshcd_pm_drain_completions(hba); ++ return IRQ_HANDLED; ++ } ++ + /* Move interrupt handling to thread when MCQ & ESI are not enabled */ + if (!hba->mcq_enabled || !hba->mcq_esi_enabled) + return IRQ_WAKE_THREAD; +@@ -9729,6 +9957,7 @@ + + static void ufshcd_hba_exit(struct ufs_hba *hba) + { ++ timer_delete_sync(&hba->pm_poll_timer); + if (hba->is_powered) { + ufshcd_pm_qos_exit(hba); + ufshcd_exit_clk_scaling(hba); +@@ -10182,6 +10411,15 @@ + if (ret) + goto out; + ++ /* ++ * Vendor resume is done so the controller clocks/registers are usable. ++ * Arm the completion poller for the relink. If the UFS IRQ remains ++ * oneshot-masked, the timer drains UIC/UTP completions from the arch timer ++ * instead. Disarmed at out: before PM state is released. ++ */ ++ if (!hba->mcq_enabled) ++ mod_timer(&hba->pm_poll_timer, jiffies + msecs_to_jiffies(2)); ++ + /* For DeepSleep, the only supported option is to have the link off */ + WARN_ON(ufshcd_is_ufs_dev_deepsleep(hba) && !ufshcd_is_link_off(hba)); + +@@ -10259,9 +10497,18 @@ + out: + if (ret) + ufshcd_update_evt_hist(hba, UFS_EVT_WL_RES_ERR, (u32)ret); ++ /* ++ * Stop the relink completion poller BEFORE ufshcd_release() (which can ++ * queue clock gating once is_suspended is cleared) so the timer can never ++ * read UFS MMIO after the clocks are gated. Clear pm_op_in_progress first so ++ * a concurrently-firing timer sees the flag false and does not re-arm, then ++ * synchronously delete it. From here normal IRQ delivery resumes: the ++ * now-runnable threaded handler drains any remaining IS and unmasks the line. ++ */ ++ hba->pm_op_in_progress = false; ++ timer_delete_sync(&hba->pm_poll_timer); + hba->clk_gating.is_suspended = false; + ufshcd_release(hba); +- hba->pm_op_in_progress = false; + return ret; + } + +@@ -10884,6 +11131,13 @@ + */ + hba->vcc_off_delay_us = 2000; + ++ /* ++ * Set up the relink completion poll timer before the first goto out_disable ++ * (which reaches ufshcd_hba_exit() -> timer_delete_sync()), so teardown ++ * never operates on an uninitialised timer. ++ */ ++ timer_setup(&hba->pm_poll_timer, ufshcd_pm_poll_timer_fn, 0); ++ + err = ufshcd_hba_init(hba); + if (err) + goto out_error; +--- a/include/ufs/ufshcd.h ++++ b/include/ufs/ufshcd.h +@@ -976,6 +976,18 @@ + enum ufs_pm_level pm_lvl_min; + int pm_op_in_progress; + ++ /* ++ * Out-of-band completion drain for PM and error-handler relink. If the ++ * threaded IRQ remains oneshot-masked, pm_poll_timer drains completions ++ * from the arch timer while pm_op_in_progress or relink_poll_active is ++ * set. pm_drain_active serialises the timer drain against the hardirq ++ * drain. relink_poll_active is set only around the explicit error-handler ++ * reset bracket, not for generic resets. ++ */ ++ struct timer_list pm_poll_timer; ++ unsigned long pm_drain_active; ++ bool relink_poll_active; ++ + /* Auto-Hibernate Idle Timer register value */ + u32 ahit; + diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0203-thermal-qcom-tsens-skip-ayn-thor-uplow-wake-irq.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0203-thermal-qcom-tsens-skip-ayn-thor-uplow-wake-irq.patch new file mode 100644 index 00000000000..d1c45d39bca --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0203-thermal-qcom-tsens-skip-ayn-thor-uplow-wake-irq.patch @@ -0,0 +1,56 @@ +From 2b4adfaf66434bddbde753c3ae7229d8c73a0ea5 Mon Sep 17 00:00:00 2001 +From: jaewun +Date: Mon, 11 May 2026 01:20:00 +1200 +Subject: [PATCH] thermal: qcom: tsens: skip AYN Thor uplow wake IRQs + +On AYN Thor, the SM8550 TSENS uplow/passive threshold IRQ is not a useful +system wake source and can immediately wake the system from suspend. + +Keep normal TSENS interrupt handling active and keep critical TSENS wake +enabled. Only skip enable_irq_wake() for the ordinary "uplow" TSENS IRQs on +ayn,thor. + +Signed-off-by: jaewun +--- + drivers/thermal/qcom/tsens.c | 15 ++++++++++++++- + 1 file changed, 14 insertions(+), 1 deletion(-) + +diff --git a/drivers/thermal/qcom/tsens.c b/drivers/thermal/qcom/tsens.c +index fbdab9d00000..fbdab9d00001 100644 +--- a/drivers/thermal/qcom/tsens.c ++++ b/drivers/thermal/qcom/tsens.c +@@ -1174,6 +1174,15 @@ static const struct thermal_zone_device_ops tsens_of_ops = { + .set_trips = tsens_set_trips, + }; + ++static bool tsens_irq_wake_enabled(const char *irqname) ++{ ++ if (!of_machine_is_compatible("ayn,thor")) ++ return true; ++ ++ /* Keep critical thermal wake; only passive threshold-window IRQs are noisy. */ ++ return strcmp(irqname, "uplow") != 0; ++} ++ + static int tsens_register_irq(struct tsens_priv *priv, char *irqname, + irq_handler_t thread_fn) + { +@@ -1203,11 +1212,15 @@ static int tsens_register_irq(struct tsens_priv *priv, char *irqname, + + if (ret) + dev_err(&pdev->dev, "%s: failed to get irq\n", + __func__); +- else ++ else if (tsens_irq_wake_enabled(irqname)) + enable_irq_wake(irq); ++ else ++ dev_info(&pdev->dev, ++ "leaving TSENS %s IRQ %d as non-wakeup on AYN Thor\n", ++ irqname, irq); + } + + put_device(&pdev->dev); + return ret; + } +-- +2.50.0 diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0204-soc-qcom-ice-allow-explicit-votes-on-iface-clock.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0204-soc-qcom-ice-allow-explicit-votes-on-iface-clock.patch new file mode 100644 index 00000000000..3ed09655294 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0204-soc-qcom-ice-allow-explicit-votes-on-iface-clock.patch @@ -0,0 +1,72 @@ +From 5f93b127bc17fda3fbd2ca4d9a7d8cae82624022 Mon Sep 17 00:00:00 2001 +From: Harshal Dev +Date: Thu, 16 Apr 2026 17:29:19 +0530 +Subject: [PATCH] soc: qcom: ice: allow explicit votes on iface clock + +Since Qualcomm inline-crypto engine (ICE) is now a dedicated driver +de-coupled from the QCOM UFS driver, it explicitly votes for its required +clocks during probe. For scenarios where the clk_ignore_unused flag is not +passed on the kernel command line, avoid potential unclocked ICE hardware +register access during probe by also voting the iface clock. + +Also update the suspend and resume callbacks to handle un-voting and voting +the iface clock. +--- + drivers/soc/qcom/ice.c | 17 +++++++++++++++-- + 1 file changed, 15 insertions(+), 2 deletions(-) + +diff --git a/drivers/soc/qcom/ice.c b/drivers/soc/qcom/ice.c +index 9b4e167925fd..e37d6985e10e 100644 +--- a/drivers/soc/qcom/ice.c ++++ b/drivers/soc/qcom/ice.c +@@ -108,6 +108,7 @@ struct qcom_ice { + void __iomem *base; + + struct clk *core_clk; ++ struct clk *iface_clk; + bool use_hwkm; + bool hwkm_init_complete; + u8 hwkm_version; +@@ -312,8 +313,13 @@ int qcom_ice_resume(struct qcom_ice *ice) + + err = clk_prepare_enable(ice->core_clk); + if (err) { +- dev_err(dev, "failed to enable core clock (%d)\n", +- err); ++ dev_err(dev, "Failed to enable core clock: %d\n", err); ++ return err; ++ } ++ ++ err = clk_prepare_enable(ice->iface_clk); ++ if (err) { ++ dev_err(dev, "Failed to enable iface clock: %d\n", err); + return err; + } + qcom_ice_hwkm_init(ice); +@@ -323,6 +329,7 @@ EXPORT_SYMBOL_GPL(qcom_ice_resume); + + int qcom_ice_suspend(struct qcom_ice *ice) + { ++ clk_disable_unprepare(ice->iface_clk); + clk_disable_unprepare(ice->core_clk); + ice->hwkm_init_complete = false; + +@@ -579,9 +586,15 @@ static struct qcom_ice *qcom_ice_create(struct device *dev, + engine->core_clk = devm_clk_get_optional_enabled(dev, "ice_core_clk"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_optional_enabled(dev, "ice"); ++ if (!engine->core_clk) ++ engine->core_clk = devm_clk_get_optional_enabled(dev, "core"); + if (!engine->core_clk) + engine->core_clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(engine->core_clk)) + return ERR_CAST(engine->core_clk); + ++ engine->iface_clk = devm_clk_get_optional_enabled(dev, "iface"); ++ if (IS_ERR(engine->iface_clk)) ++ return ERR_CAST(engine->iface_clk); ++ + if (!qcom_ice_check_supported(engine)) + return ERR_PTR(-EOPNOTSUPP); +-- +2.51.0 diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0205-arm64-dts-qcom-sm8550-add-ice-iface-clock-power-domain.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0205-arm64-dts-qcom-sm8550-add-ice-iface-clock-power-domain.patch new file mode 100644 index 00000000000..d9af3c49131 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0205-arm64-dts-qcom-sm8550-add-ice-iface-clock-power-domain.patch @@ -0,0 +1,37 @@ +From 6a135f4986c0e4c97362ae5154db76eaadf1a692 Mon Sep 17 00:00:00 2001 +From: Harshal Dev +Date: Thu, 16 Apr 2026 17:29:19 +0530 +Subject: [PATCH] arm64: dts: qcom: sm8550: add ICE iface clock and power + domain + +Qualcomm inline crypto engine (ICE) specifies and votes for its own +resources. Before accessing ICE hardware during probe, to avoid potential +unclocked register access issues when clk_ignore_unused is not passed, the +driver should enable the iface clock in addition to the core clock. This can +only be done if the UFS_PHY_GDSC power domain is enabled. + +Specify both the UFS_PHY_GDSC power domain and the iface clock in the ICE node +for SM8550. +--- + arch/arm64/boot/dts/qcom/sm8550.dtsi | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/arch/arm64/boot/dts/qcom/sm8550.dtsi b/arch/arm64/boot/dts/qcom/sm8550.dtsi +index f2770521a48d..ec10bba5ca81 100644 +--- a/arch/arm64/boot/dts/qcom/sm8550.dtsi ++++ b/arch/arm64/boot/dts/qcom/sm8550.dtsi +@@ -2887,7 +2887,11 @@ ice: crypto@1d88000 { + "qcom,inline-crypto-engine"; + reg = <0 0x01d88000 0 0x18000>; + +- clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>; ++ clocks = <&gcc GCC_UFS_PHY_ICE_CORE_CLK>, ++ <&gcc GCC_UFS_PHY_AHB_CLK>; ++ clock-names = "core", ++ "iface"; ++ power-domains = <&gcc UFS_PHY_GDSC>; + }; + + tcsr_mutex: hwlock@1f40000 { +-- +2.51.0 diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0206-soc-qcom-ice-unwind-core-clk-on-iface-clk-fail.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0206-soc-qcom-ice-unwind-core-clk-on-iface-clk-fail.patch new file mode 100644 index 00000000000..1fb9a386a24 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0206-soc-qcom-ice-unwind-core-clk-on-iface-clk-fail.patch @@ -0,0 +1,22 @@ +From: jaewun +Subject: [PATCH] soc: qcom: ice: unwind core clock if iface clock enable fails + +qcom_ice_resume() enables core_clk then iface_clk. If the iface_clk enable +fails it returns without disabling the core_clk it just enabled, leaking an +enable reference. Unwind core_clk on that error path. + +Fixes the iface-clock support added in "soc: qcom: ice: allow explicit votes +on iface clock". + +Signed-off-by: jaewun +--- +--- a/drivers/soc/qcom/ice.c ++++ b/drivers/soc/qcom/ice.c +@@ -320,6 +320,7 @@ + err = clk_prepare_enable(ice->iface_clk); + if (err) { + dev_err(dev, "Failed to enable iface clock: %d\n", err); ++ clk_disable_unprepare(ice->core_clk); + return err; + } + qcom_ice_hwkm_init(ice); diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0207-scsi-ufs-qcom-balance-irq-on-host-reset-error.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0207-scsi-ufs-qcom-balance-irq-on-host-reset-error.patch new file mode 100644 index 00000000000..625deb187bf --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0207-scsi-ufs-qcom-balance-irq-on-host-reset-error.patch @@ -0,0 +1,55 @@ +From: jaewun +Subject: [PATCH] scsi: ufs: qcom: balance the IRQ disable on host_reset error exits + +ufs_qcom_host_reset() snapshots whether the IRQ was enabled, calls +ufshcd_disable_irq(), and only re-enables it on the success path. The two +error returns (reset_control_assert / reset_control_deassert failure) leave +the IRQ disabled, leaking the disable depth and leaving the controller IRQ +masked for the rest of the controller's life. + +The relink path can now mask the IRQ with disable_irq_nosync() while PM is in +progress, so these error exits must preserve IRQ depth as well. Route both +failures through a common exit that re-enables the IRQ when it was enabled on +entry. + +Signed-off-by: jaewun +--- +diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c +index 375fd24..61a5b3a 100644 +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -449,7 +449,7 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba) + if (ret) { + dev_err(hba->dev, "%s: core_reset assert failed, err = %d\n", + __func__, ret); +- return ret; ++ goto out; + } + + /* +@@ -463,15 +463,23 @@ static int ufs_qcom_host_reset(struct ufs_hba *hba) + if (ret) { + dev_err(hba->dev, "%s: core_reset deassert failed, err = %d\n", + __func__, ret); +- return ret; ++ goto out; + } + + usleep_range(1000, 1100); + ++ ret = 0; ++out: ++ /* ++ * Re-enable the IRQ on the error exits too, otherwise a reset ++ * assert/deassert failure leaks the disable and leaves the controller ++ * IRQ masked. (This path became reachable once ufshcd_disable_irq() stops ++ * synchronizing during PM resume.) ++ */ + if (reenable_intr) + ufshcd_enable_irq(hba); + +- return 0; ++ return ret; + } + + static u32 ufs_qcom_get_hs_gear(struct ufs_hba *hba) diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/0502-wakeup-qcom-ipcc-remove-IRQF-NO-SUSPEND.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/0502-wakeup-qcom-ipcc-remove-IRQF-NO-SUSPEND.patch new file mode 100644 index 00000000000..41ca4806731 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/0502-wakeup-qcom-ipcc-remove-IRQF-NO-SUSPEND.patch @@ -0,0 +1,24 @@ +From: jaewun +Subject: [PATCH] mailbox: qcom-ipcc: drop IRQF_NO_SUSPEND + +IRQF_NO_SUSPEND keeps the IPCC parent IRQ enabled through +suspend_device_irqs(). It is not wake configuration. + +Let the PM core mask the IRQ during suspend; wake-capable users should use the +normal wake IRQ path. + +Signed-off-by: jaewun + +diff --git a/drivers/mailbox/qcom-ipcc.c b/drivers/mailbox/qcom-ipcc.c +index d957d989c..2e4bb5efb 100644 +--- a/drivers/mailbox/qcom-ipcc.c ++++ b/drivers/mailbox/qcom-ipcc.c +@@ -321,7 +321,7 @@ static int qcom_ipcc_probe(struct platform_device *pdev) + goto err_mbox; + + ret = devm_request_irq(&pdev->dev, ipcc->irq, qcom_ipcc_irq_fn, +- IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND | ++ IRQF_TRIGGER_HIGH | + IRQF_NO_THREAD, name, ipcc); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register the irq: %d\n", ret); diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/1007-scsi-ufs-qcom-propagate-hibern8-exit-failure-clk-scale.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/1007-scsi-ufs-qcom-propagate-hibern8-exit-failure-clk-scale.patch new file mode 100644 index 00000000000..912fb147c80 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/1007-scsi-ufs-qcom-propagate-hibern8-exit-failure-clk-scale.patch @@ -0,0 +1,34 @@ +From: jaewun +Subject: [PATCH] scsi: ufs: qcom: propagate hibern8 exit failure from clk_scale_notify + +ufs_qcom_clk_scale_notify() wakes the link with ufshcd_uic_hibern8_exit() +after a POST_CHANGE clock scale but drops its return value and always returns 0. +Every other error in this function (hibern8 enter, the pre/post change helpers) +is captured and returned; only this success-path link-wake was silently ignored. + +If the link fails to exit hibern8, the function still reports success to the +clock-scaling core. ufshcd_scale_clks() consumes a non-zero POST_CHANGE return: +on failure it rolls back the OPP/clock frequency, leaves target_freq unchanged, +and lets devfreq retry on the next poll. Returning 0 bypasses that rollback. + +Capture and return the error so the failure is propagated to the +clock-scaling/devfreq accounting and rollback path instead of being masked as +success. Link recovery is still handled by the existing UIC error path; this +change only fixes the clk-scaling return value. + +Signed-off-by: jaewun +--- +diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -1779,8 +1779,8 @@ + } + + ufs_qcom_icc_update_bw(host); +- ufshcd_uic_hibern8_exit(hba); ++ err = ufshcd_uic_hibern8_exit(hba); + } + +- return 0; ++ return err; + } diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/1008-scsi-ufs-qcom-auto-hibern8-clk-gating-collision.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/1008-scsi-ufs-qcom-auto-hibern8-clk-gating-collision.patch new file mode 100644 index 00000000000..a8c47fcb8c5 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/1008-scsi-ufs-qcom-auto-hibern8-clk-gating-collision.patch @@ -0,0 +1,38 @@ +From: jaewun +Subject: [PATCH] scsi: ufs: qcom: disable HW auto-hibern8 with clk-gating hibern8 + +The qcom host sets UFSHCD_CAP_HIBERN8_WITH_CLK_GATING, so ufshcd_gate_work() +parks the link with a SW DME_HIBERNATE_ENTER before gating clocks. The core also +defaults HW auto-hibern8 on (ahit = 150 ms). On idle the HW can park the M-PHY +first; ufshcd_gate_work() then issues a redundant SW hibern8-enter on the +already-parked link. That can time out waiting for power-mode-change +completion and mark the link broken. + +HW auto-hibern8 and SW clk-gating hibern8 are mutually exclusive ways to park +the link. Keep the SW clk-gating path, which runs in ufshcd_gate_work() with +the IRQ still enabled, and disable HW auto-hibern8 via +UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8. + +Signed-off-by: jaewun +--- +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -1157,6 +1157,18 @@ + + if (drvdata && drvdata->quirks) + hba->quirks |= drvdata->quirks; ++ ++ /* ++ * This host uses UFSHCD_CAP_HIBERN8_WITH_CLK_GATING, i.e. ufshcd_gate_work() ++ * issues a SW DME_HIBERNATE_ENTER to park the link before gating clocks. ++ * Leaving HW auto-hibern8 enabled too makes the HW park the link first; ++ * gate_work then issues a redundant SW hibern8-enter on the already-parked ++ * link, which can time out waiting for power-mode-change completion. ++ * The two mechanisms are mutually exclusive; keep the SW clk-gating ++ * path and disable HW auto-hibern8. ++ */ ++ if (hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING) ++ hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; + } + + static void ufs_qcom_set_phy_gear(struct ufs_qcom_host *host) diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/1010-scsi-ufs-qcom-keep-mphy-powered-on-hibern8-park.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/1010-scsi-ufs-qcom-keep-mphy-powered-on-hibern8-park.patch new file mode 100644 index 00000000000..17733b2c550 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/1010-scsi-ufs-qcom-keep-mphy-powered-on-hibern8-park.patch @@ -0,0 +1,80 @@ +From: jaewun +Subject: [PATCH] scsi: ufs: qcom: keep M-PHY powered across hibern8 parking on no_phy_retention + +The SM8550 qcom host is flagged no_phy_retention: phy_power_off() can drop +calibrated M-PHY state, and recalibration is done by full link startup through +ufs_qcom_power_up_sequence(). + +ufs_qcom_setup_clocks(on=false) is also used for SW clk-gating and runtime PM +cases where the UFS link is only parked in HIBERN8. The matching resume path +issues DME_HIBERNATE_EXIT, which assumes retained PHY state rather than running +a full link startup. + +On no_phy_retention hosts, keep the PHY powered when the link is merely parked +in HIBERN8: skip phy_power_off() on the way down and the matching phy_power_on() +on the way up. True LINK_OFF system suspend is unchanged and still powers the +PHY off before recalibrating on resume. + +Signed-off-by: jaewun + +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -1253,7 +1253,9 @@ + enum ufs_notify_change_status status) + { + struct ufs_qcom_host *host = ufshcd_get_variant(hba); ++ const struct ufs_qcom_drvdata *drvdata; + struct phy *phy; ++ bool keep_phy; + int err; + + /* +@@ -1264,7 +1266,16 @@ + if (!host) + return 0; + ++ drvdata = of_device_get_match_data(hba->dev); + phy = host->generic_phy; ++ ++ /* ++ * no_phy_retention hosts can lose calibrated M-PHY state on phy_power_off(). ++ * Keep the PHY powered when the link is only parked in HIBERN8; true ++ * LINK_OFF still powers the PHY off and recalibrates on resume. ++ */ ++ keep_phy = drvdata && drvdata->no_phy_retention && ++ ufs_qcom_is_link_hibern8(hba); + + switch (status) { + case PRE_CHANGE: +@@ -1283,19 +1294,23 @@ + ufs_qcom_dev_ref_clk_ctrl(host, false); + } + +- err = phy_power_off(phy); +- if (err) { +- dev_err(hba->dev, "phy power off failed, ret=%d\n", err); +- return err; ++ if (!keep_phy) { ++ err = phy_power_off(phy); ++ if (err) { ++ dev_err(hba->dev, "phy power off failed, ret=%d\n", err); ++ return err; ++ } + } + } + break; + case POST_CHANGE: + if (on) { +- err = phy_power_on(phy); +- if (err) { +- dev_err(hba->dev, "phy power on failed, ret = %d\n", err); +- return err; ++ if (!keep_phy) { ++ err = phy_power_on(phy); ++ if (err) { ++ dev_err(hba->dev, "phy power on failed, ret = %d\n", err); ++ return err; ++ } + } + + /* enable the device ref clock for HS mode*/ diff --git a/projects/ROCKNIX/devices/SM8550/patches/linux/1015-ufs-qcom-disable-rx-linecfg-after-link-startup.patch b/projects/ROCKNIX/devices/SM8550/patches/linux/1015-ufs-qcom-disable-rx-linecfg-after-link-startup.patch new file mode 100644 index 00000000000..6074339dcd9 --- /dev/null +++ b/projects/ROCKNIX/devices/SM8550/patches/linux/1015-ufs-qcom-disable-rx-linecfg-after-link-startup.patch @@ -0,0 +1,160 @@ +From: jaewun +Subject: [PATCH] phy: qcom-qmp-ufs: allow UFS hosts to control RX LineCfg + +Qualcomm downstream UFS hosts keep PHY RX LineCfg enabled for link startup and +disable it immediately afterward. The downstream comment says some UFS devices +send incorrect LineCfg during power-mode-change, which can put the host PHY in +an invalid state. + +Add a QMP UFS helper for the host driver to toggle the PCS LINECFG_DISABLE bit +and call it from the existing Qualcomm link-startup PRE/POST hooks. + +This keeps RX LineCfg enabled while the link starts, then disables host RX +LineCfg before post-startup power-mode-change traffic. + +Signed-off-by: jaewun + +--- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c ++++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +@@ -14,5 +14,6 @@ + #include + #include ++#include + #include + #include + #include +@@ -40,12 +41,16 @@ + #define NUM_OVERLAY 2 + ++/* QPHY LINECFG_DISABLE bit */ ++#define RX_LINECFG_DISABLE BIT(1) ++ + /* set of registers with offsets different per-PHY */ + enum qphy_reg_layout { + /* PCS registers */ + QPHY_SW_RESET, + QPHY_START_CTRL, + QPHY_PCS_READY_STATUS, + QPHY_PCS_POWER_DOWN_CONTROL, ++ QPHY_LINECFG_DISABLE, + /* Keep last to ensure regs_layout arrays are properly initialized */ + QPHY_LAYOUT_SIZE + }; +@@ -66,6 +71,7 @@ static const unsigned int ufsphy_v4_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_PCS_READY_STATUS] = QPHY_V4_PCS_UFS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V4_PCS_UFS_SW_RESET, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL, ++ [QPHY_LINECFG_DISABLE] = QPHY_V4_PCS_UFS_LINECFG_DISABLE, + }; + + static const unsigned int ufsphy_v5_regs_layout[QPHY_LAYOUT_SIZE] = { +@@ -81,6 +87,7 @@ static const unsigned int ufsphy_v6_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_PCS_READY_STATUS] = QPHY_V6_PCS_UFS_READY_STATUS, + [QPHY_SW_RESET] = QPHY_V6_PCS_UFS_SW_RESET, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_UFS_POWER_DOWN_CONTROL, ++ [QPHY_LINECFG_DISABLE] = QPHY_V6_PCS_UFS_LINECFG_DISABLE, + }; + + static const struct qmp_phy_init_tbl milos_ufsphy_serdes[] = { +@@ -1920,6 +1927,39 @@ static void qmp_ufs_init(struct qmp_ufs *qmp) + qmp_ufs_init_all(qmp, &cfg->tbls_hs_b); + } + ++int qcom_qmp_ufs_ctrl_rx_linecfg(struct phy *phy, bool enable) ++{ ++ struct qmp_ufs *qmp; ++ const struct qmp_phy_cfg *cfg; ++ u32 offset; ++ u32 before; ++ u32 after; ++ ++ if (!phy) ++ return -ENODEV; ++ ++ qmp = phy_get_drvdata(phy); ++ if (!qmp || !qmp->pcs) ++ return -ENODEV; ++ ++ cfg = qmp->cfg; ++ offset = cfg->regs[QPHY_LINECFG_DISABLE]; ++ if (!offset) ++ return -EOPNOTSUPP; ++ ++ before = readl(qmp->pcs + offset); ++ after = enable ? before & ~RX_LINECFG_DISABLE : ++ before | RX_LINECFG_DISABLE; ++ writel(after, qmp->pcs + offset); ++ after = readl(qmp->pcs + offset); ++ ++ dev_dbg(qmp->dev, "UFS PHY RX LineCfg %s: reg=0x%x -> 0x%x\n", ++ enable ? "enabled" : "disabled", before, after); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(qcom_qmp_ufs_ctrl_rx_linecfg); ++ + static int qmp_ufs_power_on(struct phy *phy) + { + struct qmp_ufs *qmp = phy_get_drvdata(phy); +--- /dev/null ++++ b/include/linux/phy/phy-qcom-qmp-ufs.h +@@ -0,0 +1,20 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __PHY_QCOM_QMP_UFS_H__ ++#define __PHY_QCOM_QMP_UFS_H__ ++ ++#include ++#include ++#include ++ ++struct phy; ++ ++#if IS_ENABLED(CONFIG_PHY_QCOM_QMP_UFS) ++int qcom_qmp_ufs_ctrl_rx_linecfg(struct phy *phy, bool enable); ++#else ++static inline int qcom_qmp_ufs_ctrl_rx_linecfg(struct phy *phy, bool enable) ++{ ++ return -EOPNOTSUPP; ++} ++#endif ++ ++#endif +--- a/drivers/ufs/host/ufs-qcom.c ++++ b/drivers/ufs/host/ufs-qcom.c +@@ -13,6 +13,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -715,9 +716,16 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) + { ++ struct ufs_qcom_host *host = ufshcd_get_variant(hba); + int err = 0; ++ int linecfg_err; + + switch (status) { + case PRE_CHANGE: ++ linecfg_err = qcom_qmp_ufs_ctrl_rx_linecfg(host->generic_phy, true); ++ if (linecfg_err && linecfg_err != -EOPNOTSUPP) ++ dev_warn(hba->dev, "failed to enable RX LineCfg: %d\n", ++ linecfg_err); ++ + if (ufs_qcom_cfg_timers(hba, false, ULONG_MAX)) { + dev_err(hba->dev, "%s: ufs_qcom_cfg_timers() failed\n", + __func__); +@@ -738,5 +746,11 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, + err = ufshcd_disable_host_tx_lcc(hba); + + break; ++ case POST_CHANGE: ++ linecfg_err = qcom_qmp_ufs_ctrl_rx_linecfg(host->generic_phy, false); ++ if (linecfg_err && linecfg_err != -EOPNOTSUPP) ++ dev_warn(hba->dev, "failed to disable RX LineCfg: %d\n", ++ linecfg_err); ++ break; + default: + break; diff --git a/projects/ROCKNIX/packages/hardware/quirks/platforms/SM8550/030-suspend_mode b/projects/ROCKNIX/packages/hardware/quirks/platforms/SM8550/030-suspend_mode index 4f773ea5546..22981ff90b6 100755 --- a/projects/ROCKNIX/packages/hardware/quirks/platforms/SM8550/030-suspend_mode +++ b/projects/ROCKNIX/packages/hardware/quirks/platforms/SM8550/030-suspend_mode @@ -1,14 +1,31 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # Copyright (C) 2023 JELOS (https://github.com/JustEnoughLinuxOS) +# Copyright (C) 2026 ROCKNIX (https://github.com/ROCKNIX) -### Sleep is currently broken, so we'll disable it. +. /etc/profile.d/001-functions -/usr/bin/suspendmode off +# Default SM8550 devices to mem suspend and keep systemd on that state. +mkdir -p /storage/.config/sleep.conf.d +cat </storage/.config/sleep.conf.d/zz-sm8550-deeponly.conf +[Sleep] +SuspendState= +SuspendState=mem +EOF -### Ignore power button presses for now, until we can finish up fixing sleep. cat <~/.config/logind.conf.d/login.conf [Login] -HandlePowerKey=ignore -HandleSuspendKey=ignore +HandlePowerKey=suspend +HandleSuspendKey=suspend +HandleLidSwitch=suspend +HandleLidSwitchExternalPower=suspend +HandleLidSwitchDocked=ignore EOF + +MYSLEEPMODE=$(get_setting system.suspendmode) +if [ -z "${MYSLEEPMODE}" ] || [ "${MYSLEEPMODE}" = "off" ] +then + /usr/bin/suspendmode mem +fi + +systemctl try-restart systemd-logind.service 2>/dev/null || true