From: Shih-Yuan Lee <fourdollars@debian.org>
To: Mark Brown <broonie@kernel.org>
Cc: linux-spi@vger.kernel.org, linux-kernel@vger.kernel.org,
Shih-Yuan Lee <fourdollars@debian.org>
Subject: [PATCH 2/2] spi: pxa2xx: restore LPSS private and IDMA registers on S3 resume
Date: Mon, 13 Jul 2026 00:24:20 +0800 [thread overview]
Message-ID: <20260712162420.7453-3-fourdollars@debian.org> (raw)
In-Reply-To: <20260712162420.7453-1-fourdollars@debian.org>
Intel LPSS SPI controllers lose all private register state across S3
suspend because the LPSS power domain is fully removed. On resume the
driver only re-enables the SSP clock but leaves the LPSS private
registers (BAR0 0x200-0x2ff) and the IDMA registers (0x800-0x814) in
their power-on-reset state, which causes two separate problems:
1. LPSS_PRIV_RESETS (0x204) stays zero, keeping the functional block
and IDMA in reset. Writing 7 to de-assert both resets before any
register access is mandatory; accessing MMIO while in reset causes a
PCIe Completion Timeout and a watchdog-triggered system reset.
2. The IDMA block shares the SPI interrupt line. With its registers
zeroed the IDMA asserts a spurious interrupt that masks the real SPI
interrupt, causing every subsequent SPI transfer to time out (-110).
3. The LPSS software chip-select control register (0x224) must *not* be
blindly restored from its suspend-time snapshot: if CS was asserted
at the moment of suspend, restoring that state corrupts the first
post-resume SPI transaction. Instead, call lpss_ssp_setup() which
unconditionally writes SW_MODE | CS_HIGH (idle/deasserted), matching
the state established at probe time.
Fix all three issues by:
- Saving the four LPSS clock/LTR/SSP private registers and six IDMA
registers in pxa2xx_spi_suspend().
- In pxa2xx_spi_resume(), writing LPSS_PRIV_RESETS first, then
restoring the saved private registers (excluding 0x204 and 0x224),
then calling lpss_ssp_setup() to re-initialise CS to idle-high,
and finally restoring the IDMA registers.
Signed-off-by: Shih-Yuan Lee <fourdollars@debian.org>
---
drivers/spi/spi-pxa2xx.c | 57 ++++++++++++++++++++++++++++++++++++++++
drivers/spi/spi-pxa2xx.h | 4 +++
2 files changed, 61 insertions(+)
diff --git a/drivers/spi/spi-pxa2xx.c b/drivers/spi/spi-pxa2xx.c
index 6291d7c2e06f..e8a83c818328 100644
--- a/drivers/spi/spi-pxa2xx.c
+++ b/drivers/spi/spi-pxa2xx.c
@@ -1479,12 +1479,35 @@ void pxa2xx_spi_remove(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(pxa2xx_spi_remove, "SPI_PXA2xx");
+/*
+ * LPSS private registers to save across S3 suspend.
+ * NOTE: 0x224 (CS control) is intentionally excluded - it is re-initialised
+ * by lpss_ssp_setup() on resume to ensure CS starts deasserted (idle-high).
+ */
+static const unsigned int lpss_saved_regs[] = {
+ 0x200,
+ 0x204,
+ 0x220,
+ 0x238,
+};
+
static int pxa2xx_spi_suspend(struct device *dev)
{
struct driver_data *drv_data = dev_get_drvdata(dev);
struct ssp_device *ssp = drv_data->ssp;
int status;
+ if (is_lpss_ssp(drv_data) && !pm_runtime_suspended(dev)) {
+ struct pxa2xx_spi_controller *pdata = drv_data->controller_info;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(lpss_saved_regs); i++)
+ pdata->lpss_priv_ctx[i] = readl(ssp->mmio_base + lpss_saved_regs[i]);
+
+ for (i = 0; i < 6; i++)
+ pdata->lpss_idma_ctx[i] = readl(ssp->mmio_base + 0x800 + i * 4);
+ }
+
status = spi_controller_suspend(drv_data->controller);
if (status)
return status;
@@ -1508,6 +1531,40 @@ static int pxa2xx_spi_resume(struct device *dev)
status = clk_prepare_enable(ssp->clk);
if (status)
return status;
+
+ if (is_lpss_ssp(drv_data)) {
+ struct pxa2xx_spi_controller *pdata = drv_data->controller_info;
+ int i;
+
+ /* First de-assert resets by writing 7 to 0x204 (LPSS_PRIV_RESETS) */
+ writel(7, ssp->mmio_base + 0x204);
+
+ /*
+ * Restore clock/LTR/SSP private registers.
+ * 0x204 (resets) is skipped - already written above.
+ * 0x224 (CS control) is skipped - re-initialised by
+ * lpss_ssp_setup() below to ensure CS starts idle-high.
+ */
+ for (i = 0; i < ARRAY_SIZE(lpss_saved_regs); i++) {
+ if (lpss_saved_regs[i] == 0x204)
+ continue;
+ writel(pdata->lpss_priv_ctx[i],
+ ssp->mmio_base + lpss_saved_regs[i]);
+ }
+
+ /*
+ * Re-initialise SW chip-select control so CS starts
+ * deasserted (SW_MODE | CS_HIGH) regardless of the
+ * state it was in at suspend time.
+ */
+ lpss_ssp_setup(drv_data);
+
+ /* Restore IDMA registers */
+ for (i = 0; i < 6; i++) {
+ writel(pdata->lpss_idma_ctx[i],
+ ssp->mmio_base + 0x800 + i * 4);
+ }
+ }
}
/* Start the queue running */
diff --git a/drivers/spi/spi-pxa2xx.h b/drivers/spi/spi-pxa2xx.h
index 447be0369384..423cef5118e7 100644
--- a/drivers/spi/spi-pxa2xx.h
+++ b/drivers/spi/spi-pxa2xx.h
@@ -34,6 +34,10 @@ struct pxa2xx_spi_controller {
/* For non-PXA arches */
struct ssp_device ssp;
+
+ /* LPSS private registers context */
+ u32 lpss_priv_ctx[4];
+ u32 lpss_idma_ctx[6];
};
struct spi_controller;
--
2.39.5
next prev parent reply other threads:[~2026-07-12 16:25 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-12 16:24 [PATCH 0/2] spi: pxa2xx: MacBook8,1 quirk and LPSS S3 resume fixes Shih-Yuan Lee
2026-07-12 16:24 ` [PATCH 1/2] spi: pxa2xx: disable DMA and fix runtime PM for Apple MacBook8,1 Shih-Yuan Lee
2026-07-12 16:24 ` Shih-Yuan Lee [this message]
2026-07-13 16:02 ` [PATCH 2/2] spi: pxa2xx: restore LPSS private and IDMA registers on S3 resume Mark Brown
2026-07-17 15:46 ` [PATCH v3 0/2] spi: pxa2xx: MacBook8,1 quirk and LPSS S3 resume state fixes Shih-Yuan Lee
2026-07-17 15:46 ` [PATCH v3 1/2] spi: pxa2xx: disable DMA and fix runtime PM for Apple MacBook8,1 Shih-Yuan Lee
2026-07-17 15:46 ` [PATCH v3 2/2] spi: pxa2xx: restore LPSS private register state on S3 resume Shih-Yuan Lee
2026-07-17 21:43 ` [PATCH v3 0/2] spi: pxa2xx: MacBook8,1 quirk and LPSS S3 resume state fixes Mark Brown
2026-07-17 23:47 ` Shih-Yuan Lee (FourDollars)
2026-07-17 16:37 ` [PATCH v4 " Shih-Yuan Lee
2026-07-17 16:37 ` [PATCH v4 1/2] spi: pxa2xx: disable DMA and fix runtime PM for Apple MacBook8,1 Shih-Yuan Lee
2026-07-17 16:37 ` [PATCH v4 2/2] spi: pxa2xx: restore LPSS private register state on S3 resume Shih-Yuan Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260712162420.7453-3-fourdollars@debian.org \
--to=fourdollars@debian.org \
--cc=broonie@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-spi@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.