diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 0603a40d865b6..379fa031ce599 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -838,6 +838,19 @@ static unsigned int apple_soc_cpufreq_pstate_freq(struct cpufreq_policy *policy, static int apple_soc_cpufreq_suspend(struct cpufreq_policy *policy) { struct apple_cpu_priv *priv = policy->driver_data; + struct apple_fast_die *fd = priv->fast_die; + + /* + * A firmware-driven sleep stops the PMP, and its driver starts it again only after the + * CPUs are back: the first samples after the wake find it down. Give it the same time to + * come back as at boot. Jiffies don't count the sleep. + */ + if (fd && fd->report && pm_suspend_via_firmware()) { + spin_lock_bh(&fd->lock); + fd->start = jiffies; + fd->warned = false; + spin_unlock_bh(&fd->lock); + } if (priv->info->lazy_verify) { int pstate = apple_soc_cpufreq_drain(priv); diff --git a/drivers/spmi/spmi-apple-controller.c b/drivers/spmi/spmi-apple-controller.c index e655fad881fac..f4124d0ae83d2 100644 --- a/drivers/spmi/spmi-apple-controller.c +++ b/drivers/spmi/spmi-apple-controller.c @@ -100,6 +100,8 @@ struct apple_spmi { struct irq_domain *irqd; raw_spinlock_t irq_mask_lock; DECLARE_BITMAP(irq_mask_cache, SPMI_MAX_PERIPHERAL_IRQS); + /* The sources a driver uses, enabled or not. */ + DECLARE_BITMAP(irq_used, SPMI_MAX_PERIPHERAL_IRQS); int irq; bool notify_irq; bool notify_parked; @@ -344,8 +346,11 @@ static struct irq_chip apple_spmi_irq_chip = { static int apple_spmi_irq_domain_map(struct irq_domain *irqd, unsigned int irq, irq_hw_number_t hw) { + struct apple_spmi *spmi = irqd->host_data; + irq_domain_set_info(irqd, irq, hw, &apple_spmi_irq_chip, irqd->host_data, handle_edge_irq, NULL, NULL); + set_bit(hw, spmi->irq_used); return 0; } @@ -399,7 +404,9 @@ static void apple_spmi_irq_domain_free(struct irq_domain *irqd, unsigned int vir for (i = 0; i < nr_irqs; i++) { struct irq_data *d = irq_domain_get_irq_data(irqd, virq + i); + struct apple_spmi *spmi = irqd->host_data; + clear_bit(d->hwirq, spmi->irq_used); irq_set_handler(virq + i, NULL); irq_domain_reset_irq_data(d); } @@ -416,7 +423,7 @@ static void apple_spmi_irq_handler(struct irq_desc *desc) struct apple_spmi *spmi = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); u32 notify = spmi->hw->num_irqs; - bool handled = false; + bool handled = false, held = false; unsigned long val, offset, bit; chained_irq_enter(chip, desc); @@ -426,9 +433,32 @@ static void apple_spmi_irq_handler(struct irq_desc *desc) complete(&spmi->fifo_rx); handled = true; } + /* Other queues' responses, such as the sleep's own commands at the debug link's cut. */ + if (val & ~BIT(notify % 32)) { + writel(val & ~BIT(notify % 32), spmi->regs + SPMI_IRQ_ACK_BASE + (notify / 32) * 4); + handled = true; + } for (offset = 0; offset < notify / 8; offset += sizeof(val)) { + unsigned long stray; + val = readq(spmi->regs + SPMI_IRQ_ACK_BASE + offset); + /* + * After a real sleep, sources no driver uses come up pending (on the Neo the + * USB-PD controllers' even lines, latched by the debug link's cut), and the + * interrupt fires for them with nothing of ours to run. Clear them, as probe does. + * A used source that is masked, as all are until the resume enables them, stays + * pending for its unmask: it isn't a bad interrupt either. + */ + stray = val & ~spmi->irq_used[offset / sizeof(val)]; + if (stray) { + writel(lower_32_bits(stray), spmi->regs + SPMI_IRQ_ACK_BASE + offset); + writel(upper_32_bits(stray), spmi->regs + SPMI_IRQ_ACK_BASE + offset + 4); + handled = true; + } + if (val & spmi->irq_used[offset / sizeof(val)] & + ~spmi->irq_mask_cache[offset / sizeof(val)]) + held = true; /** * because of other masters in the bus, we're going to get a multitude of * interrupts we're not interested in. irq_resolve_mapping isn't very @@ -441,8 +471,14 @@ static void apple_spmi_irq_handler(struct irq_desc *desc) handled = true; } } - if (!handled) + if (!handled && !held) handle_bad_irq(desc); + /* + * The acks are posted writes: let them reach the controller before the parent is + * unmasked, or it fires again for what was just cleared. + */ + if (handled) + readl(spmi->regs + SPMI_IRQ_ACK_BASE); chained_irq_exit(chip, desc); } diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c index 0f581faf056e5..b0d5d0954e5df 100644 --- a/drivers/usb/typec/tipd/core.c +++ b/drivers/usb/typec/tipd/core.c @@ -1695,14 +1695,65 @@ static void cd321x_rearm_debug_link(struct cd321x *cd321x) cd321x->rearm_pending = false; } +/* The cut's cold reset, and how it waits for the contract to come back, see apple_sleep_usb_down(). */ +#define CD321X_REARM_RESET_AFTER 4 +#define CD321X_REARM_RESETS 3 +#define CD321X_RESET_MS 1000 +#define CD321X_CONTRACT_MS 5000 + +static bool cd321x_has_contract(struct tps6598x *tps) +{ + u16 pwr; + + return !tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr) && + TPS_POWER_STATUS_CONNECTION(pwr) && + TPS_POWER_STATUS_PWROPMODE(pwr) == TYPEC_PWR_MODE_PD; +} + +/* + * The debug VDM needs a PD contract. After a cut whose sleep was refused, the partner was + * found to have dropped it, both ends at default USB power, and no try could rearm the link. + * Do what the cut does when the contract doesn't come back: reset the controller again and + * wait for it. The controller then needs setting up as after the cut. + */ +static void cd321x_rearm_recover_contract(struct cd321x *cd321x) +{ + struct tps6598x *tps = &cd321x->tps; + int ms, ret; + + lockdep_assert_held(&tps->lock); + cd321x->rearm_resets++; + dev_warn(tps->dev, "debug link: no contract with the partner, reset %u\n", + cd321x->rearm_resets); + ret = tps6598x_exec_cmd_tmo(tps, "Gaid", 0, NULL, 0, NULL, 2000, 0); + if (ret) + dev_dbg(tps->dev, "reset command: %d\n", ret); + msleep(CD321X_RESET_MS); + for (ms = 0; ms < CD321X_CONTRACT_MS && !cd321x_has_contract(tps); ms += 50) + msleep(50); + if (ms < CD321X_CONTRACT_MS) + dev_info(tps->dev, "debug link: contract back %d ms after reset %u\n", + CD321X_RESET_MS + ms, cd321x->rearm_resets); + cd321x->pm.setup_pending = true; + cd321x->reset_by_cut = true; + cd321x->state_valid = false; + mod_delayed_work(system_freezable_wq, &cd321x->resume_work, 0); + cd321x->rearm_tries = 0; +} + static void cd321x_rearm_work(struct work_struct *work) { struct cd321x *cd321x = container_of(to_delayed_work(work), struct cd321x, rearm_work); + struct tps6598x *tps = &cd321x->tps; - guard(mutex)(&cd321x->tps.lock); - if (cd321x->rearm_pending && cd321x->pm.phase != CD321X_PM_PREPARED && - cd321x->pm.phase != CD321X_PM_REMOVED) - cd321x_rearm_debug_link(cd321x); + guard(mutex)(&tps->lock); + if (!cd321x->rearm_pending || cd321x->pm.phase == CD321X_PM_PREPARED || + cd321x->pm.phase == CD321X_PM_REMOVED) + return; + if (cd321x->rearm_tries >= CD321X_REARM_RESET_AFTER && + cd321x->rearm_resets < CD321X_REARM_RESETS && !cd321x_has_contract(tps)) + cd321x_rearm_recover_contract(cd321x); + cd321x_rearm_debug_link(cd321x); } static void cd321x_complete(struct tps6598x *tps) @@ -1728,6 +1779,7 @@ static void cd321x_complete(struct tps6598x *tps) cd321x->rearm_pending = true; if (cd321x->rearm_pending && !pm_dark_wake()) { cd321x->rearm_tries = 0; + cd321x->rearm_resets = 0; cd321x_rearm_debug_link(cd321x); } if (cd321x_pm_resume(&cd321x->pm)) diff --git a/drivers/usb/typec/tipd/tps6598x.h b/drivers/usb/typec/tipd/tps6598x.h index 1b811bfe8d96a..a5512eea4205f 100644 --- a/drivers/usb/typec/tipd/tps6598x.h +++ b/drivers/usb/typec/tipd/tps6598x.h @@ -473,6 +473,8 @@ struct cd321x { /* The debug link is still to be rearmed after a cut, and the tries so far */ bool rearm_pending; unsigned int rearm_tries; + /* Resets taken since the cut for a contract the partner dropped, see cd321x_rearm_work() */ + unsigned int rearm_resets; bool dp_sink_reported; bool dp_irq_hpd;