diff --git a/drivers/soc/apple/mailbox.c b/drivers/soc/apple/mailbox.c index f4f674f548c5d..b3fd558cc79cc 100644 --- a/drivers/soc/apple/mailbox.c +++ b/drivers/soc/apple/mailbox.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -209,6 +210,15 @@ static_assert(APPLE_ASCWRAP_V6_MBOX_OFFSET + APPLE_ASC_MBOX_I2A_RECV0 == #define APPLE_MBOX_TX_RECHECK_MS 2 #define APPLE_G17_MBOX_RX_BUDGET 64 #define APPLE_G17_MBOX_ATOMIC_TX_US 1000 +/* + * T6050 poll-only receive: every send arms an hrtimer that polls the receive + * FIFO at this period, kept for this long after the last message or send. A + * GPU reply lands within one 0.22 ms frame; polling every 25 us bounds the + * added receive latency to ~11% of a frame instead of one 4 ms jiffy, while + * idle receive stays on the jiffy poller. + */ +#define APPLE_MBOX_RX_FAST_POLL_US 25 +#define APPLE_MBOX_RX_FAST_GRACE_US 1000 /* One firmware role of an ASCWrap v6 provider, at its fixed CPU-physical windows. */ struct apple_mbox_ascwrap_v6_role_desc { @@ -225,6 +235,8 @@ struct apple_mbox_hw { unsigned int num_ascwrap_v6_roles; /* T8152 GPU ASCWrap v8: wrapper + iop-vbar + post-office window + PSD. */ bool is_ascwrap_v8; + /* The mailbox interrupt never reaches the AIC: receive by polling. */ + bool rx_poll_only; /* Status word counts RX occupancy and free TX slots; paired transfers. */ bool counted_fifo; bool ap_initializes_mailboxes; @@ -265,6 +277,9 @@ struct apple_mbox_priv { /* rx_lock: an unpowered-PSD receive skip was logged this activation. */ bool rx_psd_skip_logged; struct delayed_work rx_poll_work; + /* Kick and fast tick write this lock-free; reads use READ_ONCE. */ + ktime_t rx_fast_until; + struct hrtimer rx_fast_timer; }; static struct apple_mbox_priv *apple_mbox_to_priv(struct apple_mbox *mbox) @@ -275,7 +290,7 @@ static struct apple_mbox_priv *apple_mbox_to_priv(struct apple_mbox *mbox) /* Only providers allocated by apple_mbox_probe() have a receive poller. */ static bool apple_mbox_has_rx_poller(const struct apple_mbox *mbox) { - return mbox->hw->is_ascwrap_v8; + return mbox->hw->is_ascwrap_v8 || mbox->hw->rx_poll_only; } /* Both ASCWrap generations share the consumer lifecycle API below. */ @@ -598,6 +613,65 @@ static struct apple_mbox_msg apple_mbox_read_msg(struct apple_mbox *mbox) return msg; } +static int apple_mbox_poll_locked(struct apple_mbox *mbox); + +/* + * Fast receive tick for rx_poll_only mailboxes (T6050): the same receive + * step apple_mbox_v8_rx_poll_work() runs, under the same rx_lock, with + * apple_mbox_poll_locked() doing the same PSD and removal checks. The v8 + * storm guard does not apply: it guards the recv-not-empty IRQ line, which + * rx_poll_only hardware never enables. Runs in hardirq context. + */ +static enum hrtimer_restart apple_mbox_fast_rx_poll(struct hrtimer *timer) +{ + struct apple_mbox_priv *priv = container_of(timer, struct apple_mbox_priv, + rx_fast_timer); + struct apple_mbox *mbox = &priv->mbox; + unsigned long flags; + ktime_t now; + int consumed = 0; + + if (!READ_ONCE(mbox->active) || READ_ONCE(mbox->removing)) + return HRTIMER_NORESTART; + + spin_lock_irqsave(&mbox->rx_lock, flags); + if (likely(!READ_ONCE(mbox->removing))) + consumed = apple_mbox_poll_locked(mbox); + spin_unlock_irqrestore(&mbox->rx_lock, flags); + + now = ktime_get(); + if (consumed > 0) { + /* Traffic arrived: keep polling as if a send just happened. */ + WRITE_ONCE(priv->rx_fast_until, + ktime_add_us(now, APPLE_MBOX_RX_FAST_GRACE_US)); + } else if (!ktime_before(now, READ_ONCE(priv->rx_fast_until))) { + return HRTIMER_NORESTART; + } + + hrtimer_forward_now(timer, us_to_ktime(APPLE_MBOX_RX_FAST_POLL_US)); + return HRTIMER_RESTART; +} + +/* + * tx_lock held. Lock-free on purpose: apple_mbox_poll_locked() invokes the + * rx callback with rx_lock held, and a callback that sends would then take + * tx_lock under rx_lock. Kick never touches rx_lock, so no ordering exists + * to invert. A kick that loses a race with the timer's final no-restart + * decision leaves the fast poller disarmed once; the next jiffy poll or + * send recovers it. + */ +static void apple_mbox_fast_rx_kick(struct apple_mbox_priv *priv) +{ + WRITE_ONCE(priv->rx_fast_until, + ktime_add_us(ktime_get(), APPLE_MBOX_RX_FAST_GRACE_US)); + /* Publish the deadline before deciding to arm. */ + smp_mb(); + if (!hrtimer_active(&priv->rx_fast_timer)) + hrtimer_start(&priv->rx_fast_timer, + us_to_ktime(APPLE_MBOX_RX_FAST_POLL_US), + HRTIMER_MODE_REL); +} + int apple_mbox_send(struct apple_mbox *mbox, const struct apple_mbox_msg msg, bool atomic) { @@ -720,6 +794,10 @@ int apple_mbox_send(struct apple_mbox *mbox, const struct apple_mbox_msg msg, apple_mbox_write_msg(mbox, msg); + /* The reply may be back well within a jiffy: fast-poll for it. */ + if (mbox->hw->rx_poll_only) + apple_mbox_fast_rx_kick(apple_mbox_to_priv(mbox)); + spin_unlock_irqrestore(&mbox->tx_lock, flags); return 0; @@ -959,6 +1037,9 @@ static void apple_mbox_v8_rx_stop(struct apple_mbox *mbox) if (live) disable_irq(mbox->irq_recv_not_empty); + /* Same cancel-and-drain guarantee for the fast poller as below. */ + if (mbox->hw->rx_poll_only) + hrtimer_cancel(&priv->rx_fast_timer); cancel_delayed_work_sync(&priv->rx_poll_work); } @@ -1122,8 +1203,11 @@ static void apple_mbox_quiesce(void *data) pm_runtime_put_autosuspend(mbox->dev); } /* The v8 poller also stops on the removal fence; make sure it is gone. */ - if (apple_mbox_has_rx_poller(mbox)) + if (apple_mbox_has_rx_poller(mbox)) { + if (mbox->hw->rx_poll_only) + hrtimer_cancel(&apple_mbox_to_priv(mbox)->rx_fast_timer); cancel_delayed_work_sync(&apple_mbox_to_priv(mbox)->rx_poll_work); + } /* Serialize once-only masking with the IRQ and send-error paths. */ spin_lock_irqsave(&mbox->tx_lock, flags); @@ -1338,6 +1422,12 @@ static int apple_mbox_probe(struct platform_device *pdev) return -EINVAL; if (mbox->hw->counted_fifo && !IS_ENABLED(CONFIG_ARM64)) return -EOPNOTSUPP; + if (mbox->hw->rx_poll_only) { + priv->rx_poll_only = true; + hrtimer_setup(&priv->rx_fast_timer, apple_mbox_fast_rx_poll, + CLOCK_MONOTONIC, HRTIMER_MODE_REL); + INIT_DELAYED_WORK(&priv->rx_poll_work, apple_mbox_v8_rx_poll_work); + } if (mbox->hw->is_ascwrap_v6) { /* @@ -1702,6 +1792,7 @@ static const struct apple_mbox_hw apple_mbox_t6050_ascwrap_v6_hw = { .i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0, .i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1, .has_irq_controls = false, + .rx_poll_only = true, }; static const struct apple_mbox_hw apple_mbox_t6050_j775d_ascwrap_v6_hw = { @@ -1718,6 +1809,7 @@ static const struct apple_mbox_hw apple_mbox_t6050_j775d_ascwrap_v6_hw = { .i2a_recv0 = APPLE_ASC_MBOX_I2A_RECV0, .i2a_recv1 = APPLE_ASC_MBOX_I2A_RECV1, .has_irq_controls = false, + .rx_poll_only = true, }; /*