diff --git a/Documentation/devicetree/bindings/mfd/wirenboard,wbec.yaml b/Documentation/devicetree/bindings/mfd/wirenboard,wbec.yaml index 1ce8ccb1206c..fa33ac6a6ee2 100644 --- a/Documentation/devicetree/bindings/mfd/wirenboard,wbec.yaml +++ b/Documentation/devicetree/bindings/mfd/wirenboard,wbec.yaml @@ -34,6 +34,16 @@ properties: wakeup-source: true + wirenboard,suspend-magic-rtc: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the Allwinner sun6i RTC whose general-purpose data register 0 + (GP_DATA0, at offset 0x100 from the RTC register base) is used to hand the + platform firmware (BL31) the magic value that selects the deepest, + power-off suspend-to-off path. Present only on boards whose firmware + implements that path (H616/T507, Wiren Board 8); when absent the driver + leaves the EC-coordinated power-off suspend feature disabled. + required: - compatible - reg diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi index 38c56bc06b42..5efc16d34532 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-h616-wirenboard85x.dtsi @@ -904,6 +904,14 @@ interrupt-parent = <&pio>; interrupts = ; + /* + * Arm the in-kernel suspend-to-off coordinator: the phandle + * points at the sun6i RTC whose data0 register carries the + * BL31 suspend-to-off magic. Its presence gates the feature, + * so only WB8/H616 (this SoC) enables it. + */ + wirenboard,suspend-magic-rtc = <&rtc>; + wbec_watchdog: wbec-watchdog@0 { compatible = "wirenboard,wbec-watchdog"; reg = <0>; @@ -1677,3 +1685,20 @@ &iommu { status = "okay"; }; + +/* + * Suspend-to-off contract with the boot firmware: on resume, U-Boot + * SPL's board init runs before the resume branch and programs VDD-CPU + * to its 0.90 V cold-boot value (CONFIG_AXP_DCDC2_VOLT), and BL31 then + * restores the PLL frequency the kernel parked at. The OPP the kernel + * parks at across suspend must therefore be safe at 0.90 V on every + * speed bin. 480 MHz is 0.90 V-rated and bin-universal + * (opp-supported-hw 0x1f). The primary fix lives in BL31 (it restores + * the recorded voltage before the recorded frequency); this is the + * kernel-side belt in case firmware and kernel ever skew. + */ +&cpu_opp_table { + opp-480000000 { + opp-suspend; + }; +}; diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c index d24fa3449303..76bf95cdca43 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c @@ -207,6 +207,29 @@ static const struct sunxi_ccu_desc sun50i_h6_r_ccu_desc = { .num_resets = ARRAY_SIZE(sun50i_h6_r_ccu_resets), }; +/* + * On the T507/WB8 a system suspend is a suspend-to-off: firmware puts the + * LPDDR4 in self-refresh and the PMIC drops VDD-SYS, so on resume the R_CCU is + * back at its reset defaults too (the main CCU is restored the same way in + * ccu-sun50i-h616.c). Firmware brings the R-CPU / R-bus root (ar100, 0x000) + * back up on its own -- it needs the R-domain alive to drive the PMIC over + * R_I2C / R_RSB -- so that register is firmware-owned and must not be + * rewritten. The kernel restores the rest of the R-domain (the R-APB dividers, + * the peripheral bus gates and the IR/1-wire module clocks) from the shared + * syscore handler in ccu_common.c, so a kernel-driven R-domain peripheral is + * not left gated at its reset default across the off-cycle. The R_CCU has no + * PLLs of its own. + */ +static const u16 sun50i_h616_r_ccu_firmware_regs[] = { + 0x000, /* ar100 / R-bus root: firmware restores it for the PMIC path */ +}; + +static const struct ccu_pm sun50i_h616_r_ccu_pm = { + .reg_size = 0x210, + .firmware_regs = sun50i_h616_r_ccu_firmware_regs, + .num_firmware_regs = ARRAY_SIZE(sun50i_h616_r_ccu_firmware_regs), +}; + static const struct sunxi_ccu_desc sun50i_h616_r_ccu_desc = { .ccu_clks = sun50i_h6_r_ccu_clks, .num_ccu_clks = ARRAY_SIZE(sun50i_h6_r_ccu_clks), @@ -215,6 +238,8 @@ static const struct sunxi_ccu_desc sun50i_h616_r_ccu_desc = { .resets = sun50i_h616_r_ccu_resets, .num_resets = ARRAY_SIZE(sun50i_h616_r_ccu_resets), + + .pm = &sun50i_h616_r_ccu_pm, }; static int sun50i_h6_r_ccu_probe(struct platform_device *pdev) diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c index f592fa3032a2..98ca979d618c 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h616.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h616.c @@ -1100,6 +1100,61 @@ static const struct ccu_reset_map sun50i_h616_ccu_resets[] = { [RST_BUS_HDCP] = { 0xc4c, BIT(16) }, }; +/* + * On the T507/WB8 a system suspend is a suspend-to-off: firmware puts the + * LPDDR4 in self-refresh and the PMIC drops VDD-SYS, so on resume the whole + * CCU is back at reset defaults. Firmware restores only what it needs to + * re-enter the kernel -- the base PLLs (CPUX, DDR0, PERIPH0) and the live + * CPU/bus/DRAM clock tree -- and the kernel restores the rest from a syscore + * handler (see ccu_common.c). These are the register offsets firmware owns and + * that are therefore already running when the kernel resumes; the kernel must + * never write them. + */ +static const u16 sun50i_h616_ccu_firmware_regs[] = { + SUN50I_H616_PLL_CPUX_REG, /* CPU PLL */ + SUN50I_H616_PLL_DDR0_REG, /* DRAM PLL */ + SUN50I_H616_PLL_DDR1_REG, /* DRAM-domain PLL: firmware's if used, */ + /* otherwise stays at its reset default */ + SUN50I_H616_PLL_PERIPH0_REG, /* bus-tree source PLL */ + 0x500, /* CPUX / AXI / CPU-APB -- the CPU executes on this */ + 0x510, /* PSI / AHB1 / AHB2 bus fabric */ + 0x51c, /* AHB3 bus fabric */ + 0x520, /* APB1 bus fabric */ + 0x524, /* APB2 bus fabric */ + 0x540, /* MBUS -- memory bus to DRAM */ + 0x800, /* DRAM clock */ + 0x804, /* MBUS mat clock gates */ + 0x80c, /* BUS_DRAM gate/reset */ + 0x810, /* end of the firmware-owned DRAM window (NAND0, unused on WB8) */ +}; + +/* + * Peripheral PLLs to re-enable and re-lock before restoring the muxes and + * dividers that draw from them. The base PLLs (CPUX/DDR0/DDR1/PERIPH0) are + * owned by firmware and are not listed here. The GPU PLL has no usable lock + * bit on T507, so it is restored with a fixed settle delay (lock = 0) instead + * of a poll -- matching the GPU rate-change notifier, which likewise never + * waits for the GPU PLL to lock. + */ +static const struct ccu_pm_pll sun50i_h616_ccu_plls[] = { + { .reg = SUN50I_H616_PLL_PERIPH1_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_GPU_REG, .enable = BIT(31), .lock = 0 }, + { .reg = SUN50I_H616_PLL_VIDEO0_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_VIDEO1_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_VIDEO2_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_VE_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_DE_REG, .enable = BIT(31), .lock = BIT(28) }, + { .reg = SUN50I_H616_PLL_AUDIO_REG, .enable = BIT(31), .lock = BIT(28) }, +}; + +static const struct ccu_pm sun50i_h616_ccu_pm = { + .reg_size = 0x1000, + .firmware_regs = sun50i_h616_ccu_firmware_regs, + .num_firmware_regs = ARRAY_SIZE(sun50i_h616_ccu_firmware_regs), + .plls = sun50i_h616_ccu_plls, + .num_plls = ARRAY_SIZE(sun50i_h616_ccu_plls), +}; + static const struct sunxi_ccu_desc sun50i_h616_ccu_desc = { .ccu_clks = sun50i_h616_ccu_clks, .num_ccu_clks = ARRAY_SIZE(sun50i_h616_ccu_clks), @@ -1108,6 +1163,8 @@ static const struct sunxi_ccu_desc sun50i_h616_ccu_desc = { .resets = sun50i_h616_ccu_resets, .num_resets = ARRAY_SIZE(sun50i_h616_ccu_resets), + + .pm = &sun50i_h616_ccu_pm, }; static const u32 pll_regs[] = { diff --git a/drivers/clk/sunxi-ng/ccu_common.c b/drivers/clk/sunxi-ng/ccu_common.c index c7e00f0c29a5..f3d394eb6459 100644 --- a/drivers/clk/sunxi-ng/ccu_common.c +++ b/drivers/clk/sunxi-ng/ccu_common.c @@ -7,10 +7,13 @@ #include #include +#include #include #include +#include #include #include +#include #include "ccu_common.h" #include "ccu_gate.h" @@ -109,6 +112,178 @@ int ccu_pll_notifier_register(struct ccu_pll_nb *pll_nb) } EXPORT_SYMBOL_NS_GPL(ccu_pll_notifier_register, "SUNXI_CCU"); +/* + * System-suspend context save/restore. + * + * On some Allwinner platforms a system ("mem") suspend is implemented as a + * suspend-to-off: firmware puts the DRAM in self-refresh and the PMIC drops + * VDD-SYS and every peripheral rail, so on resume the whole CCU register file + * is back at its reset defaults. Firmware only restores the clocks it needs to + * re-enter the kernel -- the base PLLs and the CPU/bus/DRAM clock tree -- and + * leaves the peripheral clock, gate and reset state for the kernel to bring + * back. + * + * We do that from a syscore handler on purpose. syscore_resume() runs on the + * resume path after firmware but before any device is resumed -- even at the + * _noirq level -- single-CPU with interrupts off. That is the only phase at + * which we can guarantee every peripheral clock, gate and reset line is back + * to its pre-suspend value before a driver touches its hardware, and it lets + * us work purely from a pre-saved register image, without taking any clk + * framework lock or sleeping. + * + * Because the image is a raw snapshot of the running controller, only the + * clocks that were enabled at suspend time are re-enabled on resume, so the + * clk framework's view (which clocks it believes are gated) stays consistent + * with the hardware, and clocks whose rate was set via the framework get their + * exact dividers back with no need to re-run set_rate. + */ +struct ccu_pm_cache { + struct list_head node; + void __iomem *base; + const struct ccu_pm *pm; + u32 *regs; +}; + +static LIST_HEAD(ccu_pm_caches); + +/* PLL lock poll: match ccu_helper_wait_for_lock()'s 70 ms bound. */ +#define CCU_PM_LOCK_DELAY_US 10 +#define CCU_PM_LOCK_TIMEOUT_US 70000 +/* Settle time for PLLs that have no usable lock bit (e.g. the T507 GPU PLL). */ +#define CCU_PM_PLL_SETTLE_US 100 + +static bool ccu_pm_reg_is_firmware(const struct ccu_pm *pm, unsigned int off) +{ + unsigned int i; + + for (i = 0; i < pm->num_firmware_regs; i++) + if (pm->firmware_regs[i] == off) + return true; + + return false; +} + +static bool ccu_pm_reg_is_pll(const struct ccu_pm *pm, unsigned int off) +{ + unsigned int i; + + for (i = 0; i < pm->num_plls; i++) + if (pm->plls[i].reg == off) + return true; + + return false; +} + +static int ccu_pm_suspend(void) +{ + struct ccu_pm_cache *cache; + + list_for_each_entry(cache, &ccu_pm_caches, node) { + unsigned int off; + + for (off = 0; off < cache->pm->reg_size; off += sizeof(u32)) + cache->regs[off / sizeof(u32)] = readl(cache->base + off); + } + + return 0; +} + +static void ccu_pm_restore_pll(struct ccu_pm_cache *cache, + const struct ccu_pm_pll *pll) +{ + u32 val = cache->regs[pll->reg / sizeof(u32)]; + u32 reg; + + /* + * The saved value already carries the enable, lock-enable and + * output-enable bits that probe set up. + */ + writel(val, cache->base + pll->reg); + + /* A PLL that was gated at suspend time stays gated; nothing to lock. */ + if (!(val & pll->enable)) + return; + + if (pll->lock) + WARN_ON(readl_poll_timeout_atomic(cache->base + pll->reg, reg, + reg & pll->lock, + CCU_PM_LOCK_DELAY_US, + CCU_PM_LOCK_TIMEOUT_US)); + else + udelay(CCU_PM_PLL_SETTLE_US); +} + +static void ccu_pm_resume(void) +{ + struct ccu_pm_cache *cache; + + list_for_each_entry(cache, &ccu_pm_caches, node) { + const struct ccu_pm *pm = cache->pm; + unsigned int off, i; + + /* + * PLLs first, so they have re-locked before anything that muxes + * off a PLL is restored. + */ + for (i = 0; i < pm->num_plls; i++) + ccu_pm_restore_pll(cache, &pm->plls[i]); + + /* + * Everything else in ascending offset order. Module clocks + * (muxes/dividers) sit at lower offsets than the bus-gate/reset + * register they share a peripheral with, so restoring each word + * re-enables the gate and deasserts the reset together, in the + * same state they had at suspend. Firmware-owned registers (the + * base PLLs and the live CPU/bus/DRAM tree) and the PLLs already + * handled above are skipped. + */ + for (off = 0; off < pm->reg_size; off += sizeof(u32)) { + if (ccu_pm_reg_is_firmware(pm, off) || + ccu_pm_reg_is_pll(pm, off)) + continue; + + writel(cache->regs[off / sizeof(u32)], cache->base + off); + } + } +} + +static struct syscore_ops ccu_pm_syscore_ops = { + .suspend = ccu_pm_suspend, + .resume = ccu_pm_resume, +}; + +static int ccu_pm_init(void __iomem *reg, const struct ccu_pm *pm) +{ + struct ccu_pm_cache *cache; + + cache = kzalloc(sizeof(*cache), GFP_KERNEL); + if (!cache) + return -ENOMEM; + + cache->regs = kcalloc(pm->reg_size / sizeof(u32), sizeof(u32), + GFP_KERNEL); + if (!cache->regs) { + kfree(cache); + return -ENOMEM; + } + + cache->base = reg; + cache->pm = pm; + + /* + * The clock controller is an essential, never-unbound provider, so the + * cache is registered for the lifetime of the system: the syscore ops + * are registered once, on the first participating instance, and the + * cache is never freed. + */ + if (list_empty(&ccu_pm_caches)) + register_syscore_ops(&ccu_pm_syscore_ops); + + list_add_tail(&cache->node, &ccu_pm_caches); + + return 0; +} + static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev, struct device_node *node, void __iomem *reg, const struct sunxi_ccu_desc *desc) @@ -181,8 +356,16 @@ static int sunxi_ccu_probe(struct sunxi_ccu *ccu, struct device *dev, if (ret) goto err_del_provider; + if (desc->pm) { + ret = ccu_pm_init(reg, desc->pm); + if (ret) + goto err_unreg_reset; + } + return 0; +err_unreg_reset: + reset_controller_unregister(&reset->rcdev); err_del_provider: of_clk_del_provider(node); err_clk_unreg: diff --git a/drivers/clk/sunxi-ng/ccu_common.h b/drivers/clk/sunxi-ng/ccu_common.h index bbec283b9d99..0768f6f51a9a 100644 --- a/drivers/clk/sunxi-ng/ccu_common.h +++ b/drivers/clk/sunxi-ng/ccu_common.h @@ -49,6 +49,47 @@ static inline struct ccu_common *hw_to_ccu_common(struct clk_hw *hw) return container_of(hw, struct ccu_common, hw); } +/** + * struct ccu_pm_pll - a PLL that has to be re-enabled and re-locked when the + * CCU register state is restored after a context-losing system suspend. + * @reg: offset of the PLL control register + * @enable: enable bit(s); the lock is only polled if these were set when + * the context was saved (i.e. the PLL was running) + * @lock: lock-status bit to poll after re-enabling, or 0 if the PLL has + * no usable lock bit (a fixed settle delay is used instead) + */ +struct ccu_pm_pll { + u16 reg; + u32 enable; + u32 lock; +}; + +/** + * struct ccu_pm - per-SoC description of how to preserve peripheral clock + * state across a system suspend that powers the clock controller off. + * + * Only SoCs whose system-suspend implementation loses CCU register state + * populate this (e.g. Allwinner "suspend-to-off", where the PMIC drops + * VDD-SYS and every peripheral rail). Leaving &sunxi_ccu_desc.pm NULL keeps + * the context-retaining SoCs on their existing, no-op suspend path. + * + * @reg_size: size of the register window to snapshot, in bytes + * @firmware_regs: register offsets owned by firmware -- already live when + * the kernel resumes (the base PLLs and the CPU/bus/DRAM + * clock tree). Never written back on restore. + * @num_firmware_regs: number of entries in @firmware_regs + * @plls: PLLs to re-enable and re-lock before the muxes and + * dividers that draw from them are restored + * @num_plls: number of entries in @plls + */ +struct ccu_pm { + u16 reg_size; + const u16 *firmware_regs; + unsigned int num_firmware_regs; + const struct ccu_pm_pll *plls; + unsigned int num_plls; +}; + struct sunxi_ccu_desc { struct ccu_common **ccu_clks; unsigned long num_ccu_clks; @@ -57,6 +98,8 @@ struct sunxi_ccu_desc { const struct ccu_reset_map *resets; unsigned long num_resets; + + const struct ccu_pm *pm; }; void ccu_helper_wait_for_lock(struct ccu_common *common, u32 lock); diff --git a/drivers/firmware/psci/psci.c b/drivers/firmware/psci/psci.c index 38ca190d4a22..d38ba08ab024 100644 --- a/drivers/firmware/psci/psci.c +++ b/drivers/firmware/psci/psci.c @@ -534,6 +534,15 @@ static int psci_system_suspend(unsigned long unused) err = invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), pa_cpu_resume, 0, 0); + /* + * This return is only reached when the firmware did NOT suspend: + * on success the system resumes via cpu_resume. A silent -EPERM + * here (PSCI_RET_DENIED) is very hard to tell apart from a + * suspend-and-instant-wake, so name the failure. + */ + if (err) + pr_err("PSCI: SYSTEM_SUSPEND failed: %d (online CPUs: %u)\n", + err, num_online_cpus()); return psci_to_linux_errno(err); } diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c index 1792b39d54a1..0ba64dfccf5c 100644 --- a/drivers/i2c/busses/i2c-mv64xxx.c +++ b/drivers/i2c/busses/i2c-mv64xxx.c @@ -151,8 +151,27 @@ struct mv64xxx_i2c_data { bool atomic; struct work_struct error_work; bool bus_disabled; + /* + * jiffies of the first transfer error in the current failing streak + * (0 == healthy), reset on the next good transfer. Used to fail + * transfers fast once the bus has been erroring continuously past + * MV64XXX_I2C_BUS_FAIL_TIMEOUT instead of churning error->recover->retry + * forever (which pins the client's regmap mutex and D-states every + * consumer of that bus). + */ + unsigned long failing_since; }; +/* + * If the bus keeps erroring for this long without a single good transfer, + * stop the error/recover/retry churn and fail transfers hard (-ETIMEDOUT) + * rather than -EAGAIN, so a caller cannot spin on a wedged or absent slave + * (e.g. a PMIC that lost power under us across a suspend-to-off) forever. + * Far longer than normal transient-error recovery (~50-150 ms), so ordinary + * single-NAK recovery is unaffected. + */ +#define MV64XXX_I2C_BUS_FAIL_TIMEOUT (5 * HZ) + static struct mv64xxx_i2c_regs mv64xxx_i2c_regs_mv64xxx = { .addr = 0x00, .ext_addr = 0x10, @@ -336,6 +355,9 @@ mv64xxx_i2c_fsm(struct mv64xxx_i2c_data *drv_data, u32 status) // reset i2c: mv64xxx_i2c_hw_init(drv_data); + if (!drv_data->failing_since) + drv_data->failing_since = jiffies; + if (!drv_data->bus_disabled) { drv_data->bus_disabled = true; dev_err(&drv_data->adapter.dev, @@ -759,6 +781,26 @@ mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) int rc, ret = num; if (drv_data->bus_disabled) { + /* + * Bus is mid error->recover->retry. Normally bounce with -EAGAIN + * so the I2C core retries once recovery re-enables it. But if the + * bus has been erroring continuously past MV64XXX_I2C_BUS_FAIL_- + * TIMEOUT (a wedged or powered-off slave), stop churning and fail + * hard with -ETIMEDOUT: -EAGAIN invites the caller to spin on us + * forever holding its regmap mutex and D-stating every other + * consumer, whereas -ETIMEDOUT is terminal and lets it unwind. + * error_work keeps attempting recovery, and failing_since resets + * on the next good transfer, so the bus returns to normal the + * instant the slave answers again. + */ + if (drv_data->failing_since && + time_after(jiffies, drv_data->failing_since + + MV64XXX_I2C_BUS_FAIL_TIMEOUT)) { + dev_warn_ratelimited(&adap->dev, + "mv64xxx_i2c: bus erroring > %u ms, failing xfer\n", + jiffies_to_msecs(MV64XXX_I2C_BUS_FAIL_TIMEOUT)); + return -ETIMEDOUT; + } dev_dbg(&adap->dev, "mv64xxx_i2c: bus is disabled, ignoring xfer\n"); msleep(50); @@ -780,6 +822,9 @@ mv64xxx_i2c_xfer_core(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) if (rc < 0) ret = rc; + else + /* Good transfer: clear the fail-fast budget. */ + drv_data->failing_since = 0; drv_data->num_msgs = 0; drv_data->msgs = NULL; diff --git a/drivers/mfd/wbec.c b/drivers/mfd/wbec.c index 1df31b6ccfe9..658070b7a7b8 100644 --- a/drivers/mfd/wbec.c +++ b/drivers/mfd/wbec.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,11 +21,62 @@ #include #include #include +#include #include +#include +#include +#include +#include +#include +#include /* For power off WBEC activates PWON pin on PMIC for 6s */ #define WBEC_POWER_RESET_DELAY_MS 10000 +/* + * Suspend-to-off coordinator constants. + * + * On H616/T507 a PSCI SYSTEM_SUSPEND can drop VDD-SYS and every peripheral + * rail (hibernate-grade power loss). The platform firmware (BL31) only takes + * that deepest path when it finds a magic value in the sun6i RTC data0 + * register; otherwise it does the SoC-retained sleep. The magic has two + * variants that both enter suspend-to-off: FAST skips the DRAM CRC check, + * VERIFY brackets the sleep with a full-DRAM CRC (slower, opt-in). + */ +#define WBEC_SUSPEND_MAGIC_FAST 0x0ff51eeaU +#define WBEC_SUSPEND_MAGIC_VERIFY 0x0ff51eebU + +/* sun6i RTC general-purpose data0 offset from the RTC register base. */ +#define WBEC_SUSPEND_MAGIC_RTC_OFFSET 0x100 + +/* SUSPEND_CTRL.timeout_s is a 16-bit EC register. */ +#define WBEC_SUSPEND_TIMEOUT_MIN 10U +#define WBEC_SUSPEND_TIMEOUT_MAX 0xffffU + +/* + * The EC recovery deadline (the EC warm-resets timeout_s + ~10 s after the + * announce if it never sees a resume) is a hang backstop, not the wake source. + * The board is woken by the RTC alarm the user armed (rtcwake / wakealarm), so + * the deadline must land safely AFTER that alarm or it pre-empts the intended + * wake. Size timeout_s as the seconds-to-alarm plus a margin, mirroring the + * proven wb-suspend-off wrapper. The CRC-verify variant brackets the sleep with + * a full-DRAM CRC (~75 s) that runs after the wake, so its deadline needs a much + * larger margin to clear it before the backstop can fire. + */ +#define WBEC_SUSPEND_MARGIN_FAST_S 20U +#define WBEC_SUSPEND_MARGIN_VERIFY_S 90U + +static bool wbec_suspend_crc_verify; +module_param_named(suspend_crc_verify, wbec_suspend_crc_verify, bool, 0644); +MODULE_PARM_DESC(suspend_crc_verify, + "Use the CRC-verified (slower) suspend-to-off firmware magic"); + +static unsigned int wbec_suspend_default_timeout_s = WBEC_SUSPEND_TIMEOUT_MAX; +module_param_named(suspend_default_timeout_s, wbec_suspend_default_timeout_s, + uint, 0644); +MODULE_PARM_DESC(suspend_default_timeout_s, + "EC recovery deadline (s) when no RTC wakealarm is armed (long backstop; wake is then the power button)"); + /* * Mirrors LINUX_POWERON_REASON in wb-embedded-controller src/wbec.c; * append-only ABI. @@ -334,6 +386,328 @@ static int wbec_restart(struct sys_off_data *data) return NOTIFY_DONE; } +/* ----------------------------------------------------------------------- */ +/* Suspend-to-off coordinator */ + +/* + * There is exactly one EC per board. syscore_ops carry no private pointer, so + * the syscore callbacks reach the armed instance through this file-static, + * which is set only while the coordinator is armed (DT-gated, WB8/H616). + */ +static struct wbec *wbec_pm_ctx; + +/* + * Size the EC recovery deadline from the pending RTC wakealarm. On WB8 the EC + * itself is rtc0 and holds the alarm, so the wake time is read straight from + * the EC over the regmap we already own. The returned timeout must place the + * EC's recovery deadline safely AFTER that alarm (deadline = timeout_s + ~10 s + * from the announce), so seconds-to-alarm gets a margin added on top -- a plain + * seconds-to-alarm would put the deadline right on the alarm and could pre-empt + * it (fatally so for the CRC-verify variant, whose ~75 s post-wake CRC would + * still be running). When no alarm is armed a long default backstop is used and + * the wake is the power button. + */ +static int wbec_suspend_deadline(struct wbec *wbec, u32 *timeout_s) +{ + struct rtc_time now = {0}; + struct rtc_time alarm; + time64_t now_s, alarm_s; + u32 margin; + u16 t[4], a[4]; + int ret; + + ret = regmap_bulk_read(wbec->regmap, WBEC_REG_RTC_ALARM_SECS_MINS, + a, ARRAY_SIZE(a)); + if (ret) + return ret; + + if (!(a[2] & WBEC_REG_RTC_ALARM_STATUS_EN_MSK)) { + *timeout_s = min(wbec_suspend_default_timeout_s, + WBEC_SUSPEND_TIMEOUT_MAX); + return 0; + } + + ret = regmap_bulk_read(wbec->regmap, WBEC_REG_RTC_TIME_SECS_MINS, + t, ARRAY_SIZE(t)); + if (ret) + return ret; + + /* Decode the EC time (same layout as rtc-wbec.c). */ + now.tm_sec = t[0] & 0x00ff; + now.tm_min = t[0] >> 8; + now.tm_hour = t[1] & 0x00ff; + now.tm_mday = t[1] >> 8; + now.tm_mon = (t[2] >> 8) - 1; + now.tm_year = t[3] + 100; + now_s = rtc_tm_to_time64(&now); + + /* + * The EC alarm carries only mday + h:m:s, so complete it with the + * current month/year and roll to the next month if that instant has + * already passed. This only sizes the recovery deadline, so the small + * error at month boundaries is harmless. + */ + alarm = now; + alarm.tm_sec = a[0] & 0x00ff; + alarm.tm_min = a[0] >> 8; + alarm.tm_hour = a[1] & 0x00ff; + alarm.tm_mday = a[1] >> 8; + alarm_s = rtc_tm_to_time64(&alarm); + if (alarm_s <= now_s) { + if (++alarm.tm_mon > 11) { + alarm.tm_mon = 0; + alarm.tm_year++; + } + alarm_s = rtc_tm_to_time64(&alarm); + } + + margin = wbec_suspend_crc_verify ? WBEC_SUSPEND_MARGIN_VERIFY_S + : WBEC_SUSPEND_MARGIN_FAST_S; + + if (alarm_s <= now_s) + *timeout_s = min(wbec_suspend_default_timeout_s, + WBEC_SUSPEND_TIMEOUT_MAX); + else + *timeout_s = clamp_t(u64, (alarm_s - now_s) + margin, + WBEC_SUSPEND_TIMEOUT_MIN, + WBEC_SUSPEND_TIMEOUT_MAX); + + return 0; +} + +/* Announce the power-off suspend window to the EC over the (sleeping) regmap. */ +static int wbec_suspend_announce(struct wbec *wbec) +{ + u32 timeout_s; + int ret; + + /* + * Ack the host-visible RTC-alarm IRQ latch (IRQ_FLAGS.RTC_ALARM) before + * the power-off cycle. In suspend-to-off the alarm wakes the board by + * pulsing the PMIC, not through a Linux IRQ, so nothing else acks this + * bit; clearing it keeps the resumed kernel's regmap-irq from seeing a + * stale pending alarm. It touches only the host-visible flag; the alarm + * arm bit and time in ALARM_STATUS are left intact so the pending + * wakealarm still fires. + * + * NB: this does NOT gate the EC's off-mode wake. That wake reads a + * separate EC-internal fired latch (the firmware alarm_fired_latch, + * sourced from the STM32G0 RTC ALRAF flag), which the host cannot reach + * over the regmap. A stale value there was what woke the board ~220 ms + * into off-mode; it is drained by the EC firmware on off-mode entry + * (wb-embedded-controller feature/ec-suspend-mode, "drain the stale RTC + * alarm latch on off-mode entry"). The timed rtcwake -m mem wake depends + * on that EC firmware, not on this write. + */ + ret = regmap_write(wbec->regmap, WBEC_REG_IRQ_CLEAR, + WBEC_REG_IRQ_RTC_ALARM_MSK); + if (ret) + return ret; + + ret = wbec_suspend_deadline(wbec, &timeout_s); + if (ret) { + dev_err(wbec->dev, "suspend: cannot read wake deadline: %d\n", + ret); + return ret; + } + + ret = regmap_write(wbec->regmap, WBEC_REG_SUSPEND_CTRL_TIMEOUT_S, + timeout_s); + if (ret) + return ret; + + ret = regmap_write(wbec->regmap, WBEC_REG_SUSPEND_CTRL_OFF_MODE, + WBEC_REG_SUSPEND_CTRL_OFF_MODE_EN_MSK); + if (ret) { + /* Do not leave a half-announced window behind. */ + regmap_write(wbec->regmap, WBEC_REG_SUSPEND_CTRL_TIMEOUT_S, 0); + return ret; + } + + dev_dbg(wbec->dev, "suspend-to-off announced, EC deadline %u s\n", + timeout_s); + return 0; +} + +static void wbec_suspend_deannounce(struct wbec *wbec) +{ + regmap_write(wbec->regmap, WBEC_REG_SUSPEND_CTRL_OFF_MODE, 0); + regmap_write(wbec->regmap, WBEC_REG_SUSPEND_CTRL_TIMEOUT_S, 0); +} + +/* + * Device suspend/resume (normal phase): the EC handshake talks over SPI, which + * may sleep, so it cannot live in a _noirq/syscore callback. The wbec SPI + * device suspends before its SPI controller (child before parent), so the bus + * is still live here. Only the deep, firmware-assisted path (PSCI + * SYSTEM_SUSPEND, i.e. PM_SUSPEND_MEM) drops the rails and needs the EC + * coordination; s2idle and hibernation leave pm_suspend_target_state alone and + * are skipped. + */ +static int wbec_suspend(struct device *dev) +{ + struct wbec *wbec = dev_get_drvdata(dev); + + if (!wbec->suspend_magic_reg) + return 0; + if (pm_suspend_target_state != PM_SUSPEND_MEM) + return 0; + + return wbec_suspend_announce(wbec); +} + +static int wbec_resume(struct device *dev) +{ + struct wbec *wbec = dev_get_drvdata(dev); + + if (!wbec->suspend_magic_reg) + return 0; + if (pm_suspend_target_state != PM_SUSPEND_MEM) + return 0; + + wbec_suspend_deannounce(wbec); + return 0; +} + +static const struct dev_pm_ops wbec_pm_ops = { + SYSTEM_SLEEP_PM_OPS(wbec_suspend, wbec_resume) +}; + +/* + * The BL31 magic is a bare RTC-data0 mmio poke that must land right before the + * PSCI SYSTEM_SUSPEND SMC and be gone on resume. syscore is the right phase: + * it runs with interrupts off on the boot CPU after every device has + * suspended, and crucially only for a genuine deep suspend (s2idle never + * reaches syscore_suspend), so writing the magic here cannot arm the firmware + * for a sleep that does not drop the rails. The explicit target-state check + * additionally rejects the hibernation syscore path. + */ +static int wbec_pm_syscore_suspend(void) +{ + struct wbec *wbec = wbec_pm_ctx; + + if (!wbec || !wbec->suspend_magic_reg) + return 0; + if (pm_suspend_target_state != PM_SUSPEND_MEM) + return 0; + + writel(wbec_suspend_crc_verify ? WBEC_SUSPEND_MAGIC_VERIFY + : WBEC_SUSPEND_MAGIC_FAST, + wbec->suspend_magic_reg); + return 0; +} + +static void wbec_pm_syscore_resume(void) +{ + struct wbec *wbec = wbec_pm_ctx; + + if (!wbec || !wbec->suspend_magic_reg) + return; + + /* BL31 clears the magic during suspend; clear again to cover an abort. */ + writel(0, wbec->suspend_magic_reg); +} + +static struct syscore_ops wbec_pm_syscore_ops = { + .suspend = wbec_pm_syscore_suspend, + .resume = wbec_pm_syscore_resume, +}; + +/* + * Ack a stale EC RTC-alarm IRQ at the very start of every suspend. + * + * The EC drives its INT line (a rising-edge wakeup GPIO, PE7) while + * IRQ_FLAGS.RTC_ALARM is set. When an off-mode alarm wakes the board that bit + * is left set, and nothing in the kernel ever acks it: the wbec IRQ is only + * wired for the UART, and the RTC alarm has no Linux IRQ (the off-mode wake is + * a PMIC pulse, not an IRQ). So after the first cycle the EC INT stays asserted + * and the kernel keeps counting it as a pending wakeup. On the *next* deep + * suspend that makes pm_wakeup_pending() abort suspend_enter() before the + * coordinator's off-cycle runs -- every rtcwake after the first fails, while a + * fresh boot (no pending EC IRQ) works. + * + * The ack has to land before the wakeup IRQ is armed and sampled. PM_SUSPEND_- + * PREPARE runs at the very start of a suspend (suspend_prepare(), before tasks + * freeze, before dpm_suspend and before suspend_device_irqs()/pm_wakeup_- + * pending()), so clearing here deasserts the line in time. The equivalent ack + * in the .suspend callback (dpm_suspend_start) runs too late relative to that + * check. Runs in process context with the SPI bus fully alive, so the regmap + * write is safe here. + */ +static int wbec_pm_notify(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct wbec *wbec = wbec_pm_ctx; + + if (!wbec || !wbec->suspend_magic_reg) + return NOTIFY_DONE; + + if (action == PM_SUSPEND_PREPARE) { + regmap_write(wbec->regmap, WBEC_REG_IRQ_CLEAR, + WBEC_REG_IRQ_RTC_ALARM_MSK); + /* + * Drop any wakeup-abort state the stale EC IRQ already recorded, + * so a leftover from before this suspend request cannot pre-empt + * the enter path once the line itself is quiet. + */ + pm_wakeup_clear(0); + } + + return NOTIFY_DONE; +} + +static struct notifier_block wbec_pm_nb = { + .notifier_call = wbec_pm_notify, +}; + +/* + * Arm the coordinator when (and only when) the DT wires a suspend-magic RTC + * phandle, which is present only on WB8/H616. Everything downstream keys off + * wbec->suspend_magic_reg staying NULL on other boards. + */ +static int wbec_suspend_setup(struct wbec *wbec) +{ + struct device_node *rtc_np; + struct resource res; + int ret; + + rtc_np = of_parse_phandle(wbec->dev->of_node, + "wirenboard,suspend-magic-rtc", 0); + if (!rtc_np) + return 0; + + ret = of_address_to_resource(rtc_np, 0, &res); + of_node_put(rtc_np); + if (ret) + return dev_err_probe(wbec->dev, ret, + "bad wirenboard,suspend-magic-rtc\n"); + + wbec->suspend_magic_reg = devm_ioremap(wbec->dev, + res.start + WBEC_SUSPEND_MAGIC_RTC_OFFSET, + sizeof(u32)); + if (!wbec->suspend_magic_reg) + return -ENOMEM; + + wbec_pm_ctx = wbec; + register_syscore_ops(&wbec_pm_syscore_ops); + register_pm_notifier(&wbec_pm_nb); + + dev_info(wbec->dev, "suspend-to-off coordinator armed\n"); + return 0; +} + +static void wbec_suspend_teardown(struct wbec *wbec) +{ + if (!wbec->suspend_magic_reg) + return; + + unregister_pm_notifier(&wbec_pm_nb); + unregister_syscore_ops(&wbec_pm_syscore_ops); + wbec_pm_ctx = NULL; +} + +/* ----------------------------------------------------------------------- */ + static int wbec_probe(struct spi_device *spi) { struct wbec *wbec; @@ -361,13 +735,7 @@ static int wbec_probe(struct spi_device *spi) ret = wbec_check_present(wbec); if (ret == -ENODEV) { - /* - * Expected on every board that speaks the v1 protocol (the v2 - * pad-word protocol is a newer-EC feature): probe v2 first, then - * fall back to v1. Keep this at debug level so a normal v1 board - * does not log a scary "not found" line during ordinary probing. - */ - dev_dbg(wbec->dev, "no WBEC on v2 protocol, falling back to v1\n"); + dev_info(wbec->dev, "WBEC not found with v2 protocol, trying v1\n"); /* don't worry about memory leak, previous regmap will be freed by devm */ wbec->regmap = devm_regmap_init_spi(spi, &wbec_regmap_config_v1); @@ -425,17 +793,22 @@ static int wbec_probe(struct spi_device *spi) SYS_OFF_PRIO_FIRMWARE, wbec_restart, wbec); + ret = wbec_suspend_setup(wbec); + if (ret) + return ret; + wbec_setup_debugfs(wbec); dev_info(&spi->dev, "WBEC device added\n"); - return ret; + return 0; } static void wbec_remove(struct spi_device *spi) { struct wbec *wbec = spi_get_drvdata(spi); + wbec_suspend_teardown(wbec); wbec_clean_debugfs(wbec); } @@ -455,6 +828,7 @@ static struct spi_driver wbec_driver = { .driver = { .name = "wbec", .of_match_table = wbec_of_match, + .pm = pm_sleep_ptr(&wbec_pm_ops), }, .probe = wbec_probe, .remove = wbec_remove, diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 3e9fd7badfce..faf3eb3ca969 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -117,6 +117,8 @@ struct sun4i_usb_phy_data { const struct sun4i_usb_phy_cfg *cfg; enum usb_dr_mode dr_mode; spinlock_t reg_lock; /* guard access to phyctl reg */ + struct mutex phy2_lock; /* guard phy2_users (needs_phy2_siddq) */ + int phy2_users; int num_phys; struct sun4i_usb_phy { struct phy *phy; @@ -373,6 +375,15 @@ static int sun4i_usb_phy_init(struct phy *_phy) } clk_disable_unprepare(phy->clk2); + + /* + * PHY2 is now held out of reset on this PHY's behalf. Count the + * user so PHY2 is only put back into reset once the last of the + * PHYs that share it has been torn down (see sun4i_usb_phy_exit). + */ + mutex_lock(&data->phy2_lock); + data->phy2_users++; + mutex_unlock(&data->phy2_lock); } if (phy->pmu && data->cfg->hci_phy_ctl_clear) { @@ -443,7 +454,20 @@ static int sun4i_usb_phy_exit(struct phy *_phy) struct sun4i_usb_phy *phy2 = &data->phys[2]; clk_disable_unprepare(phy2->clk); - reset_control_assert(phy2->reset); + + /* + * PHY2 provides a bias the other PHYs need to work, so it must + * stay out of reset as long as any of them is up. Its reset is + * exclusive (asserting it is unconditional, not ref-counted), so + * only assert it once the last sharing PHY has been torn down; + * otherwise tearing this PHY down (e.g. the musb glue's system + * suspend calling phy_exit() on PHY0) would knock out a + * co-resident EHCI/OHCI host still using PHY1/PHY3. + */ + mutex_lock(&data->phy2_lock); + if (!WARN_ON(data->phy2_users == 0) && --data->phy2_users == 0) + reset_control_assert(phy2->reset); + mutex_unlock(&data->phy2_lock); } sun4i_usb_phy_passby(phy, 0); @@ -850,6 +874,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return -ENOMEM; spin_lock_init(&data->reg_lock); + mutex_init(&data->phy2_lock); INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan); dev_set_drvdata(dev, data); data->cfg = of_device_get_match_data(dev); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c index d1f7cfa824c5..f58f215957f5 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616-r.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "pinctrl-sunxi.h" @@ -31,6 +32,12 @@ static const struct sunxi_pinctrl_desc sun50i_h616_r_pinctrl_data = { .pins = sun50i_h616_r_pins, .npins = ARRAY_SIZE(sun50i_h616_r_pins), .pin_base = PL_BASE, + /* + * R_PIO loses all state when VDD-SYS is gated in system suspend. + * Firmware may also re-touch R_PIO for its own PMIC I2C on resume; + * that overlaps our restore idempotently (same values, no conflict). + */ + .pm_save_regs = true, }; static int sun50i_h616_r_pinctrl_probe(struct platform_device *pdev) @@ -49,6 +56,7 @@ static struct platform_driver sun50i_h616_r_pinctrl_driver = { .driver = { .name = "sun50i-h616-r-pinctrl", .of_match_table = sun50i_h616_r_pinctrl_match, + .pm = pm_sleep_ptr(&sunxi_pinctrl_pm_ops), }, }; builtin_platform_driver(sun50i_h616_r_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c index 9390fd163963..b32d473b0da6 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-h616.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "pinctrl-sunxi.h" @@ -878,6 +879,8 @@ static const struct sunxi_pinctrl_desc h616_pinctrl_data = { .irq_read_needs_mux = true, .disable_strict_mode = true, .io_bias_cfg_variant = BIAS_VOLTAGE_PIO_POW_MODE_CTL, + /* PIO loses all state when VDD-SYS is gated in system suspend. */ + .pm_save_regs = true, }; static int h616_pinctrl_probe(struct platform_device *pdev) @@ -895,6 +898,7 @@ static struct platform_driver h616_pinctrl_driver = { .driver = { .name = "sun50i-h616-pinctrl", .of_match_table = h616_pinctrl_match, + .pm = pm_sleep_ptr(&sunxi_pinctrl_pm_ops), }, }; builtin_platform_driver(h616_pinctrl_driver); diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 6ebb2611c2a2..2ac567e84001 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1551,6 +1552,229 @@ static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl, return 0; } +/* + * System-sleep register context save/restore. + * + * On platforms that switch off the PIO/R_PIO power domain during system + * suspend (sunxi_pinctrl_desc.pm_save_regs), the controller comes back + * at reset defaults: every mux, data latch, drive level, pull, IO-bias + * selection and external-interrupt setting the kernel had programmed is + * gone. The generic pinctrl_pm_select_*() machinery only re-applies + * per-consumer pin muxing on resume; it does not cover GPIO output + * values, gpio-hogs or the EINT irqchip configuration, so it cannot + * bring this hardware back on its own. Mirroring pinctrl-tegra and + * pinctrl-rockchip, we snapshot the whole register file while it is + * still alive (->suspend_noirq) and write it back before any consumer + * driver or the irq core touches the pins (->resume_noirq). + */ +static int sunxi_pinctrl_pm_regs_alloc(struct platform_device *pdev, + struct sunxi_pinctrl *pctl) +{ + struct sunxi_pinctrl_pm_regs *ctx = &pctl->pm_regs; + u32 bank_words = pctl->bank_mem_size / sizeof(u32); + + ctx->gpio = devm_kcalloc(&pdev->dev, pctl->nbanks * bank_words, + sizeof(*ctx->gpio), GFP_KERNEL); + if (!ctx->gpio) + return -ENOMEM; + + if (pctl->desc->irq_banks) { + ctx->eint = devm_kcalloc(&pdev->dev, + pctl->desc->irq_banks * + SUNXI_EINT_SAVE_WORDS, + sizeof(*ctx->eint), GFP_KERNEL); + if (!ctx->eint) + return -ENOMEM; + } + + if (pctl->desc->io_bias_cfg_variant == BIAS_VOLTAGE_GRP_CONFIG) { + ctx->grp_cfg = devm_kcalloc(&pdev->dev, pctl->nbanks, + sizeof(*ctx->grp_cfg), GFP_KERNEL); + if (!ctx->grp_cfg) + return -ENOMEM; + } + + return 0; +} + +static int sunxi_pinctrl_suspend_noirq(struct device *dev) +{ + struct sunxi_pinctrl *pctl = dev_get_drvdata(dev); + struct sunxi_pinctrl_pm_regs *ctx = &pctl->pm_regs; + u32 bank_words = pctl->bank_mem_size / sizeof(u32); + unsigned int bank, w; + u32 base; + + /* GPIO banks: mux, data, drive level and pull, in hardware order. */ + for (bank = 0; bank < pctl->nbanks; bank++) { + base = sunxi_bank_offset(pctl, bank * PINS_PER_BANK); + for (w = 0; w < bank_words; w++) + ctx->gpio[bank * bank_words + w] = + readl_relaxed(pctl->membase + base + w * 4); + } + + /* IO-bias / pin power-mode selection. */ + switch (pctl->desc->io_bias_cfg_variant) { + case BIAS_VOLTAGE_PIO_POW_MODE_CTL: + ctx->pow_mod_ctl = readl_relaxed(pctl->membase + + pctl->pow_mod_sel_offset + PIO_POW_MOD_CTL_OFS); + fallthrough; + case BIAS_VOLTAGE_PIO_POW_MODE_SEL: + ctx->pow_mod_sel = readl_relaxed(pctl->membase + + pctl->pow_mod_sel_offset); + break; + case BIAS_VOLTAGE_GRP_CONFIG: + for (bank = 0; bank < pctl->nbanks; bank++) + ctx->grp_cfg[bank] = readl_relaxed(pctl->membase + + GRP_CFG_REG + bank * 4); + break; + default: + break; + } + + /* + * EINT irqchip: config, control (enable), status and debounce. + * suspend_device_irqs() has already masked every non-wake EINT in + * hardware (IRQCHIP_MASK_ON_SUSPEND) before this noirq callback + * runs, so the control word captured here is exactly the set the + * kernel wants left armed (e.g. wake sources). + */ + for (bank = 0; bank < pctl->desc->irq_banks; bank++) { + base = IRQ_CFG_REG + + sunxi_irq_hw_bank_num(pctl->desc, bank) * IRQ_MEM_SIZE; + for (w = 0; w < SUNXI_EINT_SAVE_WORDS; w++) + ctx->eint[bank * SUNXI_EINT_SAVE_WORDS + w] = + readl_relaxed(pctl->membase + base + w * 4); + } + + ctx->valid = true; + + return 0; +} + +static int sunxi_pinctrl_resume_noirq(struct device *dev) +{ + struct sunxi_pinctrl *pctl = dev_get_drvdata(dev); + struct sunxi_pinctrl_pm_regs *ctx = &pctl->pm_regs; + u32 bank_words = pctl->bank_mem_size / sizeof(u32); + unsigned int cfg_words = IRQ_PER_BANK * IRQ_CFG_IRQ_BITS / + BITS_PER_TYPE(u32); + unsigned int bank, w; + u32 base; + + if (!ctx->valid) + return 0; + + /* + * Write ordering matters: this is an industrial board whose GPIOs + * gate relays and power rails, so no pad may glitch mid-restore. + * + * 1) Data (output latch) first, while every pad is still an input + * (reset default). Loading the latch before the mux switches a + * pad to output makes it drive the intended level from the first + * cycle, with no transient through the reset value. + */ + for (bank = 0; bank < pctl->nbanks; bank++) { + base = sunxi_bank_offset(pctl, bank * PINS_PER_BANK); + writel_relaxed(ctx->gpio[bank * bank_words + DATA_REGS_OFFSET / 4], + pctl->membase + base + DATA_REGS_OFFSET); + } + + /* + * 2) Drive strength and pull, so the pad's electrical behaviour is + * already correct once it starts driving below. + */ + for (bank = 0; bank < pctl->nbanks; bank++) { + base = sunxi_bank_offset(pctl, bank * PINS_PER_BANK); + for (w = 0; w < pctl->dlevel_field_width; w++) + writel_relaxed(ctx->gpio[bank * bank_words + + DLEVEL_REGS_OFFSET / 4 + w], + pctl->membase + base + + DLEVEL_REGS_OFFSET + w * 4); + for (w = 0; w < PULL_FIELD_WIDTH; w++) + writel_relaxed(ctx->gpio[bank * bank_words + + pctl->pull_regs_offset / 4 + w], + pctl->membase + base + + pctl->pull_regs_offset + w * 4); + } + + /* 3) IO-bias / power-mode, before any pad changes function. */ + switch (pctl->desc->io_bias_cfg_variant) { + case BIAS_VOLTAGE_PIO_POW_MODE_CTL: + writel_relaxed(ctx->pow_mod_ctl, pctl->membase + + pctl->pow_mod_sel_offset + PIO_POW_MOD_CTL_OFS); + fallthrough; + case BIAS_VOLTAGE_PIO_POW_MODE_SEL: + writel_relaxed(ctx->pow_mod_sel, + pctl->membase + pctl->pow_mod_sel_offset); + break; + case BIAS_VOLTAGE_GRP_CONFIG: + for (bank = 0; bank < pctl->nbanks; bank++) + writel_relaxed(ctx->grp_cfg[bank], pctl->membase + + GRP_CFG_REG + bank * 4); + break; + default: + break; + } + + /* + * 4) Mux ("cfg") last of the pad fields: pads now take their final + * function/direction and drive the values latched in step 1. + */ + for (bank = 0; bank < pctl->nbanks; bank++) { + base = sunxi_bank_offset(pctl, bank * PINS_PER_BANK); + for (w = 0; w < MUX_FIELD_WIDTH; w++) + writel_relaxed(ctx->gpio[bank * bank_words + + MUX_REGS_OFFSET / 4 + w], + pctl->membase + base + MUX_REGS_OFFSET + w * 4); + } + + /* + * 5) EINT trigger config and debounce, then 6) clear any pending + * bit the restore transitions above may have latched, then + * 7) re-arm the enable mask. Clearing before enabling stops a + * stale edge from firing on resume. The enable mask comes from + * the snapshot (the driver's own bookkeeping taken while the + * hardware was alive), never from a read of the now-reset + * controller. Non-wake EINTs the core masked at suspend are + * additionally unmasked by resume_device_irqs() after this + * callback returns; writing the same enable bit twice is + * idempotent. + */ + for (bank = 0; bank < pctl->desc->irq_banks; bank++) { + u32 *eint = &ctx->eint[bank * SUNXI_EINT_SAVE_WORDS]; + + base = IRQ_CFG_REG + + sunxi_irq_hw_bank_num(pctl->desc, bank) * IRQ_MEM_SIZE; + + for (w = 0; w < cfg_words; w++) + writel_relaxed(eint[w], pctl->membase + base + w * 4); + writel_relaxed(eint[(IRQ_DEBOUNCE_REG - IRQ_CFG_REG) / 4], + pctl->membase + base + + (IRQ_DEBOUNCE_REG - IRQ_CFG_REG)); + writel_relaxed(0xffffffff, pctl->membase + base + + (IRQ_STATUS_REG - IRQ_CFG_REG)); + writel_relaxed(eint[(IRQ_CTRL_REG - IRQ_CFG_REG) / 4], + pctl->membase + base + + (IRQ_CTRL_REG - IRQ_CFG_REG)); + } + + /* + * Push the posted restore writes out to the controller with a + * read-back, then order that completion before consumer drivers + * and the irq core start touching the pins. + */ + readl_relaxed(pctl->membase); + /* Barrier pairs with the read-back above; see comment. */ + rmb(); + + return 0; +} + +DEFINE_NOIRQ_DEV_PM_OPS(sunxi_pinctrl_pm_ops, + sunxi_pinctrl_suspend_noirq, + sunxi_pinctrl_resume_noirq); + int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc, unsigned long flags) @@ -1668,6 +1892,7 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, pctl->chip->can_sleep = false; pctl->chip->ngpio = round_up(last_pin, PINS_PER_BANK) - pctl->desc->pin_base; + pctl->nbanks = pctl->chip->ngpio / PINS_PER_BANK; pctl->chip->label = dev_name(&pdev->dev); pctl->chip->parent = &pdev->dev; pctl->chip->base = pctl->desc->pin_base; @@ -1744,6 +1969,12 @@ int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, sunxi_pinctrl_setup_debounce(pctl, node); + if (desc->pm_save_regs) { + ret = sunxi_pinctrl_pm_regs_alloc(pdev, pctl); + if (ret) + goto gpiochip_error; + } + dev_info(&pdev->dev, "initialized sunXi PIO driver\n"); return 0; diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h index c538884106b9..a47c736ea7f4 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.h +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h @@ -74,6 +74,14 @@ #define IRQ_MEM_SIZE 0x20 +/* + * Number of consecutive 32-bit registers, starting at a bank's + * IRQ_CFG_REG window, that together hold the whole EINT state saved for + * system sleep: four config words (trigger type), the control (enable) + * word, the status word and the debounce word. + */ +#define SUNXI_EINT_SAVE_WORDS 7 + #define IRQ_EDGE_RISING 0x00 #define IRQ_EDGE_FALLING 0x01 #define IRQ_LEVEL_HIGH 0x02 @@ -142,6 +150,13 @@ struct sunxi_pinctrl_desc { bool irq_read_needs_mux; bool disable_strict_mode; enum sunxi_desc_bias_voltage io_bias_cfg_variant; + /* + * Set on platforms where the PIO power domain can be switched off + * during system suspend, losing all register state. Enables a full + * register-context save/restore across system sleep via + * sunxi_pinctrl_pm_ops. Leave unset where the block is retained. + */ + bool pm_save_regs; }; struct sunxi_pinctrl_function { @@ -160,6 +175,16 @@ struct sunxi_pinctrl_regulator { refcount_t refcount; }; +/* Register context saved across a state-losing system suspend. */ +struct sunxi_pinctrl_pm_regs { + u32 *gpio; /* nbanks * (bank_mem_size / 4) words */ + u32 *eint; /* irq_banks * SUNXI_EINT_SAVE_WORDS words */ + u32 *grp_cfg; /* nbanks words, GRP_CONFIG bias variant only */ + u32 pow_mod_sel; + u32 pow_mod_ctl; + bool valid; +}; + struct sunxi_pinctrl { void __iomem *membase; struct gpio_chip *chip; @@ -180,6 +205,8 @@ struct sunxi_pinctrl { u32 pull_regs_offset; u32 dlevel_field_width; u32 pow_mod_sel_offset; + u32 nbanks; + struct sunxi_pinctrl_pm_regs pm_regs; }; #define SUNXI_PIN(_pin, ...) \ @@ -301,6 +328,8 @@ static inline u32 sunxi_grp_config_reg(u16 pin) return GRP_CFG_REG + bank * 0x4; } +extern const struct dev_pm_ops sunxi_pinctrl_pm_ops; + int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, const struct sunxi_pinctrl_desc *desc, unsigned long flags); diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c index 9497ec60046c..1c72105e1e45 100644 --- a/drivers/spi/spi-sun6i.c +++ b/drivers/spi/spi-sun6i.c @@ -634,6 +634,34 @@ static int sun6i_spi_runtime_suspend(struct device *dev) return 0; } +static int sun6i_spi_suspend(struct device *dev) +{ + struct spi_controller *host = dev_get_drvdata(dev); + int ret; + + ret = spi_controller_suspend(host); + if (ret) + return ret; + + ret = pm_runtime_force_suspend(dev); + if (ret) + spi_controller_resume(host); + + return ret; +} + +static int sun6i_spi_resume(struct device *dev) +{ + struct spi_controller *host = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + return spi_controller_resume(host); +} + static bool sun6i_spi_can_dma(struct spi_controller *host, struct spi_device *spi, struct spi_transfer *xfer) @@ -826,8 +854,8 @@ static const struct of_device_id sun6i_spi_match[] = { MODULE_DEVICE_TABLE(of, sun6i_spi_match); static const struct dev_pm_ops sun6i_spi_pm_ops = { - .runtime_resume = sun6i_spi_runtime_resume, - .runtime_suspend = sun6i_spi_runtime_suspend, + RUNTIME_PM_OPS(sun6i_spi_runtime_suspend, sun6i_spi_runtime_resume, NULL) + SYSTEM_SLEEP_PM_OPS(sun6i_spi_suspend, sun6i_spi_resume) }; static struct platform_driver sun6i_spi_driver = { @@ -836,7 +864,7 @@ static struct platform_driver sun6i_spi_driver = { .driver = { .name = "sun6i-spi", .of_match_table = sun6i_spi_match, - .pm = &sun6i_spi_pm_ops, + .pm = pm_ptr(&sun6i_spi_pm_ops), }, }; module_platform_driver(sun6i_spi_driver); diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index ed38af801c5c..10aa0a7e7e69 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -265,6 +266,15 @@ static int sunxi_musb_init(struct musb *musb) /* Stop the musb-core from doing runtime pm (not supported on sunxi) */ pm_runtime_get(musb->controller); + /* + * Mark the glue-owned resources (bus clock, reset, PHY, optional SRAM) + * as claimed for this controller. The system-sleep suspend/resume + * callbacks key off glue->musb to decide whether those resources are + * live and therefore whether they must be released/re-acquired; it is + * cleared again in sunxi_musb_exit() when they are torn down. + */ + glue->musb = musb; + return 0; error_reset_assert: @@ -297,6 +307,13 @@ static int sunxi_musb_exit(struct musb *musb) if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags)) sunxi_sram_release(musb->controller->parent); + /* + * The glue-owned resources are released; drop the reference the + * system-sleep callbacks use so a suspend that races a controller + * teardown (e.g. a driver unbind) does not release them a second time. + */ + glue->musb = NULL; + return 0; } @@ -885,11 +902,156 @@ static const struct of_device_id sunxi_musb_match[] = { }; MODULE_DEVICE_TABLE(of, sunxi_musb_match); +/* + * System sleep support. + * + * On most sunxi SoCs a mem-sleep keeps the USB power domain alive, so the + * musb core's musb_suspend()/musb_resume() (which save and restore the + * MUSB register block via musb_save_context()/musb_restore_context()) are + * enough. On platforms whose firmware drops the whole peripheral power + * domain during system suspend the controller, the OTG PHY, the reset line + * and the bus clock all come back at their reset defaults, and the register + * snapshot the core restores is not enough on its own: the glue-owned state + * that is only ever programmed from sunxi_musb_init() (bus clock, reset + * de-assert, the sunxi-specific VEND0 PIO-mode select and the full PHY + * bring-up) is never redone. + * + * Handle that here with glue-level dev_pm_ops that mirror the teardown of + * sunxi_musb_exit() on suspend and the hardware bring-up of + * sunxi_musb_init() on resume. These run in the normal (sleep-capable) + * suspend/resume phases because the clock, reset and PHY APIs may sleep; + * _noirq would be wrong. The work is idempotent with any transitional + * firmware-side register restore, since that runs before the kernel resumes. + * + * Ordering: the musb-hdrc controller is a child of this glue device, so the + * PM core suspends it before us and resumes it after us. On suspend the + * child has therefore already quiesced the controller and saved its context + * before we power down the PHY and gate the clock. On resume we re-enable + * the clock, de-assert reset and re-init the PHY first, so that when the + * child's musb_resume() runs its musb_restore_context() writes land on a + * live, clocked, de-reset controller. Restoring MUSB_POWER re-asserts the + * gadget soft-connect pull-up on a controller that came up with it cleared, + * which the host sees as a fresh connect and re-enumerates - no userspace + * unbind/rebind or UDC re-attach needed. + * + * These callbacks release and re-acquire the same glue-owned resources as + * sunxi_musb_exit()/sunxi_musb_init(), so they must not run when the + * controller has already been torn down (child probe deferred, or a driver + * unbind before system suspend - as the transitional userspace suspend hook + * still does). They therefore key off glue->musb, which is non-NULL only + * while init() has enabled those resources and exit() has not released them; + * doing the teardown twice would gate an already-gated bus clock and, worse, + * underflow the PHY init_count so phy_init() skips the provider .init() on + * resume and the controller comes back unclocked/unconfigured. + */ +static int sunxi_musb_suspend(struct device *dev) +{ + struct sunxi_glue *glue = dev_get_drvdata(dev); + + /* + * glue->musb is set in sunxi_musb_init() and cleared in + * sunxi_musb_exit(), so it is non-NULL exactly while the glue holds the + * clock/reset/PHY (and optional SRAM) enabled. Bail out when they are + * not held - the controller never started (child probe deferred) or it + * was already torn down before this suspend (e.g. a driver unbind) - + * otherwise we would gate the bus clock, phy_exit() and assert reset a + * second time and underflow their refcounts. + */ + if (!glue->musb) + return 0; + + cancel_work_sync(&glue->work); + + if (test_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags)) { + phy_power_off(glue->phy); + clear_bit(SUNXI_MUSB_FL_PHY_ON, &glue->flags); + } + + phy_exit(glue->phy); + + if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) + reset_control_assert(glue->rst); + + clk_disable_unprepare(glue->clk); + + if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags)) + sunxi_sram_release(dev); + + return 0; +} + +static int sunxi_musb_resume(struct device *dev) +{ + struct sunxi_glue *glue = dev_get_drvdata(dev); + int ret; + + if (!glue->musb) + return 0; + + if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags)) { + ret = sunxi_sram_claim(dev); + if (ret) + goto error; + } + + ret = clk_prepare_enable(glue->clk); + if (ret) + goto error_sram_release; + + if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) { + ret = reset_control_deassert(glue->rst); + if (ret) + goto error_clk_disable; + } + + /* sunxi musb is PIO only; VEND0 is reset to 0 (DMA) on power loss. */ + writeb(SUNXI_MUSB_VEND0_PIO_MODE, + glue->musb->mregs + SUNXI_MUSB_VEND0); + + /* + * Full PHY bring-up from reset defaults. phy_exit() dropped the PHY + * framework init_count to 0 in suspend, so this re-runs the provider's + * .init and reprograms the PHY (and, via the clk/reset framework, its + * clocks and reset) regardless of what state they came back in. The + * PHY power / mode for the current role is re-armed by the work that + * the child's musb_platform_enable() schedules on resume. + */ + ret = phy_init(glue->phy); + if (ret) + goto error_reset_assert; + + return 0; + +error_reset_assert: + if (test_bit(SUNXI_MUSB_FL_HAS_RESET, &glue->flags)) + reset_control_assert(glue->rst); +error_clk_disable: + clk_disable_unprepare(glue->clk); +error_sram_release: + if (test_bit(SUNXI_MUSB_FL_HAS_SRAM, &glue->flags)) + sunxi_sram_release(dev); +error: + /* + * Resume failed and unwound every resource it had taken, leaving the + * glue as torn down as suspend left it. Clear glue->musb so the next + * suspend/resume no-ops instead of running the teardown a second time + * against already-released resources -- phy_exit() on a zero PHY + * init_count, clk_disable_unprepare() on an already-gated clock -- the + * same double-teardown refcount underflow glue->musb guards against. + */ + glue->musb = NULL; + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(sunxi_musb_pm_ops, sunxi_musb_suspend, + sunxi_musb_resume); + static struct platform_driver sunxi_musb_driver = { .probe = sunxi_musb_probe, .remove = sunxi_musb_remove, .driver = { .name = "musb-sunxi", + .pm = pm_sleep_ptr(&sunxi_musb_pm_ops), .of_match_table = sunxi_musb_match, }, }; diff --git a/include/linux/mfd/wbec.h b/include/linux/mfd/wbec.h index d29956089cdb..46278e06258a 100644 --- a/include/linux/mfd/wbec.h +++ b/include/linux/mfd/wbec.h @@ -73,6 +73,21 @@ #define WBEC_REG_POWER_CTRL_OFF_MSK BIT(0) #define WBEC_REG_POWER_CTRL_REBOOT_MSK BIT(1) +/* + * Region SUSPEND_CTRL: RW + * + * EC-coordinated suspend-to-off window (EC firmware branch + * feature/ec-suspend-mode). Before a power-off suspend the SoC announces the + * sleep window here: TIMEOUT_S sizes the EC recovery deadline (the EC warm- + * resets the board timeout+10 s after the announce if it never sees a resume), + * and OFF_MODE tells the EC that the whole SoC (VDD-SYS, 3V3) is going dark so + * it masks its "3V3 lost / PMIC died" detector and swaps the watchdog for the + * deadline. The EC wakes the board at its own RTC alarm or the power button. + */ +#define WBEC_REG_SUSPEND_CTRL_TIMEOUT_S 0xA4 +#define WBEC_REG_SUSPEND_CTRL_OFF_MODE 0xA5 + #define WBEC_REG_SUSPEND_CTRL_OFF_MODE_EN_MSK BIT(0) + /* Region IRQ_FLAGS: RW */ #define WBEC_REG_IRQ_FLAGS 0xB0 #define WBEC_REG_IRQ_MSK 0xB2 @@ -113,6 +128,14 @@ struct wbec { struct dentry *wbec_dir; void (*irq_handler)(struct wbec *wbec); bool support_v2_protocol; + + /* + * Suspend-to-off coordinator (WB8/H616 only, DT-gated). When armed, + * @suspend_magic_reg is the ioremap of the sun6i RTC data0 register the + * platform firmware (BL31) reads to select suspend-to-off; NULL means + * the board does not use EC-coordinated power-off suspend. + */ + void __iomem *suspend_magic_reg; }; #endif