Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions drivers/cpufreq/apple-soc-cpufreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
40 changes: 38 additions & 2 deletions drivers/spmi/spmi-apple-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
Expand All @@ -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
Expand All @@ -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);
}

Expand Down
60 changes: 56 additions & 4 deletions drivers/usb/typec/tipd/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/typec/tipd/tps6598x.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down