From nobody Mon Sep 28 02:58:01 2026 Received: from mail-m15580.qiye.163.com (mail-m15580.qiye.163.com [101.71.155.80]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 58D8141A506; Thu, 27 Aug 2026 09:14:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=101.71.155.80 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787822078; cv=none; b=DaoSORhzZspPgX9vTmDmvwd2ule2CXpPHAIH8qsZOqflEfgpmxh/KEKwoUnAaaXaDejGJBRHDzN2sf00mbt49Inry/ouOk8R1AWePkjiHnCIWciooCoCjNLobftLq2SY0pPf+82ObqwGpytyg2gY7mMR2uAoMCD0HHuYrPlcRN0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787822078; c=relaxed/simple; bh=DFLW4F7Ojv7CnJkShwmE1ADECF1ga2geEWNRbs9P3ic=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=SaWpFAiAFBm1A+1r4ydxgUJ8Z03iO4vxRxfO7NJt11rOR/FOnUxOTtBXEFSYD7Rcm4By1vPPZqUdfYG28yzq/kQnCvySaQYKXhIegUwkBxllMfAYgE6LuMoDOV8zol7xgEJ3vrCkOZQ2LqN8r/eUawbjAxtNjBNyysK1RvTuMpk= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com; spf=pass smtp.mailfrom=rock-chips.com; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b=bXckE75n; arc=none smtp.client-ip=101.71.155.80 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b="bXckE75n" Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.qiye.163.com (Hmail) with ESMTP id 4b88a31b2; Thu, 27 Aug 2026 15:58:29 +0800 (GMT+08:00) From: Shawn Lin To: Ulf Hansson Cc: Jaehoon Chung , Marek Szyprowski , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Lin Subject: [PATCH 1/4] mmc: dw_mmc: add central watchdog and convert CTO onto it Date: Thu, 27 Aug 2026 15:58:03 +0800 Message-Id: <1787817486-137277-2-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> References: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> X-HM-Tid: 0aa0423a415703a4kunm6c47a0ea1fec30 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFDSUNOT01LS0k3V1kYFggdWUFKV1ktWUFJV1kPCRoVCBIfWUFZGh5PQ1ZMH0oYH0waQ0 tPTE9WFRQJFhoXVRMBExYaEhckFA4PWVdZGBILWUFZTkNVSUlVTFVKSk9ZV1kWGg8SFR0UWUFZT0 tIVUpLSU9PT0hVSktLVUpCS0tZBg++ DKIM-Signature: a=rsa-sha256; b=bXckE75nkkLR+WxqL9tUHg8giwSgYeE66Vt+hUpzeTL4FKfNKkiGRAsxoAE7XLqY1WmVWvhUgIc8gQsPodRa0qSeh26U9PMIaCcEUPYVR0Wm+Qtfe50HT0M4pcjiC/3s0MMGn+ys4l9VHdlhNKSqlB8sggbXgIkQ9nFEkqH5pm0=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=ecw8+3lvAvsbu68txk1AwjZokC0tDbvazgWTswP/ixc=; h=date:mime-version:subject:message-id:from; Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The driver keeps three independent fallback timers (cmd11, cto, dto) whose callbacks all re-implement the same race handling: peek MINTSTS in case the interrupt is in flight, check whether the event was delivered meanwhile, verify host->state matches the leg being guarded, and finally synthesize the missed event. This series replaces them with a single hrtimer watchdog. This first step introduces the watchdog and moves the command timeout onto it; cto_timer is deleted. Later steps convert the data timeout and the voltage-switch timer onto the same watch. The new protocol relies on two simple facts which hold for all current event producers: * every site posting EVENT_CMD_COMPLETE (dw_mci_cmd_interrupt() and the command-error branch of dw_mci_interrupt()) runs under irq_lock, * every site arming a watch does so under irq_lock as well. Consequently a watchdog callback holding irq_lock can neither miss nor race an already-delivered event: the bookkeeping part of the former 're-read MINTSTS' paranoia is subsumed by checking the awaited mask against pending_events under the same lock the producers use. The hardware-latency part of that paranoia is kept verbatim, see below. A callback that raced past every check nonetheless degrades to at most one idempotent extra state machine run instead of completing a foreign leg. The callback classifies what expired by comparing the awaited set against the named DW_MCI_WD_{CMD,DATA}_EVENTS masks so that subsequent conversions only add call sites. dw_mci_wd_arm() takes a separate 'already delivered' precheck mask because guarding the data legs must tolerate a DATA_ERROR that arrived while still waiting for the paired completion -- exactly like mod_timer() paths did before. Behavioral notes for review: * dw_mci_wd_arm() replaces any previously armed watch. During a voltage switch (CMD11) both cto_timer and cmd11_timer were armed concurrently before, racing each other with duplicated warnings; now only the last arm on that path survives. * The stale-timer defensiveness of dw_mci_clear_pending_cmd_complete() (WARN_ON + timer_delete_sync) is dropped because the callback is now idempotent by construction; timer_delete_sync from the BH would also be wrong-context sleeping on hrtimers. * Before declaring a timeout the callback re-reads MINTSTS: when the completion interrupt is already latched in hardware and only its handler has not been scheduled yet, the firing grants further DW_MCI_WD_INFLIGHT_GRACE_MS rounds instead of failing an about-to- complete transfer. This replicates the interrupt-latency paranoia of the retired cto_timer()/dto_timer() callbacks; unlike them it keeps re-watching rather than going passive, so if that latched interrupt is ultimately lost the request still unwedges with a timeout error instead of hanging forever. No functional change intended beyond the deduplication described above. Signed-off-by: Shawn Lin --- drivers/mmc/host/dw_mmc.c | 216 ++++++++++++++++++++++++++++++------------= ---- drivers/mmc/host/dw_mmc.h | 11 ++- 2 files changed, 150 insertions(+), 77 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cf5cecc..cefc873 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -44,6 +44,135 @@ #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ #define DW_MCI_FREQ_MIN 100000 /* unit: HZ */ =20 +/* Event sets guarded by the central watchdog, see below. */ +#define DW_MCI_WD_CMD_EVENTS BIT(EVENT_CMD_COMPLETE) +#define DW_MCI_WD_DATA_EVENTS (BIT(EVENT_DATA_ERROR) | \ + BIT(EVENT_DATA_COMPLETE)) +/* Extra watch rounds granted while the completion IRQ is merely late. */ +#define DW_MCI_WD_INFLIGHT_GRACE_MS 10 + +/* + * Central watchdog + * =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D + * + * A single hrtimer guards the legs of a request (command, data, voltage + * switch) against the hardware going silent: whenever a completion event + * that was expected within a bounded time does not arrive in time, the + * callback synthesizes it so that the request state machine can error out. + * + * @wd_events holds the pending_events bits still being waited for and + * @wd_states the host->state values for which the watch is valid; both + * are protected by irq_lock. Every producer of the guarded events posts + * them under irq_lock as well, so a callback holding irq_lock can never + * miss an already-delivered event. A stale callback which raced past + * all checks therefore degrades to at most one redundant state-machine + * run instead of completing a foreign leg. Arming replaces whatever + * watch was outstanding before: command and data legs are strictly + * serial, so consecutive arms on the same path collapse onto the latest + * deadline. + * + * On expiry, before declaring a timeout, the callback double-checks that + * the completion interrupt is not already latched in hardware; if it is, + * only the interrupt latency is to blame and the watch is granted extra + * rounds instead of failing a transfer that is about to complete. + */ +static void dw_mci_wd_deliver(struct dw_mci *host, unsigned long events) +{ + host->wd_events &=3D ~events; + if (!host->wd_events) + hrtimer_try_to_cancel(&host->wd_timer); +} + +/* + * Arm a new watch for @events valid while host->state is one of @states, + * expiring after @ms milliseconds. @already lists bits whose delivery + * makes the watch pointless before it is even armed: arming is skipped + * when any of them has been posted concurrently. Must be called with + * irq_lock held. + */ +static void dw_mci_wd_arm(struct dw_mci *host, unsigned long ms, + unsigned long already, unsigned long events, + unsigned long states) +{ + lockdep_assert_held(&host->irq_lock); + + if (host->pending_events & already) { + /* + * The event was delivered concurrently; no need for a + * watch anymore. + */ + host->wd_events =3D 0; + hrtimer_try_to_cancel(&host->wd_timer); + return; + } + + host->wd_events =3D events; + host->wd_states =3D states; + hrtimer_start(&host->wd_timer, ms_to_ktime(ms), HRTIMER_MODE_REL); +} + +/* Called from hardirq context on watchdog expiry. */ +static enum hrtimer_restart dw_mci_watchdog_fn(struct hrtimer *t) +{ + struct dw_mci *host =3D container_of(t, struct dw_mci, wd_timer); + unsigned long irqflags; + u32 pending; + + spin_lock_irqsave(&host->irq_lock, irqflags); + + /* + * Last line of defense against a false timeout: when the completion + * interrupt is already latched in MINTSTS but its handler has not + * been scheduled yet, the leg is about to finish normally. Warn + * about the latency and keep watching instead of failing the + * transfer; delivery by the real interrupt will stop the watch. + * The retired per-timer callbacks went passive here and relied on + * the latched interrupt arriving; rearming bounds the damage if + * that assumption is ever broken. + */ + if (host->wd_events) { + pending =3D mci_readl(host, MINTSTS); /* read-only mask reg */ + if ((host->wd_events =3D=3D DW_MCI_WD_CMD_EVENTS && + (pending & (DW_MCI_CMD_ERROR_FLAGS | + SDMMC_INT_CMD_DONE))) || + (host->wd_events =3D=3D DW_MCI_WD_DATA_EVENTS && + (pending & (SDMMC_INT_DATA_OVER | + DW_MCI_DATA_ERROR_FLAGS)))) { + dev_warn(host->dev, + "Interrupt latency, state %d (watched %#lx)\n", + host->state, host->wd_events); + hrtimer_forward_now(&host->wd_timer, + ms_to_ktime(DW_MCI_WD_INFLIGHT_GRACE_MS)); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + return HRTIMER_RESTART; + } + } + + if (host->wd_events =3D=3D DW_MCI_WD_DATA_EVENTS) { + dev_warn(host->dev, + "Data timeout, state %d\n", host->state); + host->data_status =3D SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, &host->pending_events); + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + } else if (host->wd_events) { + dev_warn(host->dev, + "Command timeout, state %d (watched %#lx)\n", + host->state, host->wd_events); + host->cmd_status =3D SDMMC_INT_RTO; + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); + } + + if (host->wd_events) + queue_work(system_bh_wq, &host->bh_work); + + host->wd_events =3D 0; + host->wd_states =3D 0; + + spin_unlock_irqrestore(&host->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + #define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ @@ -366,18 +495,17 @@ static inline void dw_mci_set_cto(struct dw_mci *host) * extra careful about synchronization here. Specifically in hardware a * command timeout is _at most_ 5.1 ms, so that means we expect an * interrupt (either command done or timeout) to come rather quickly - * after the mci_writel. ...but just in case we have a long interrupt - * latency let's add a bit of paranoia. + * after the mci_writel. * - * In general we'll assume that at least an interrupt will be asserted - * in hardware by the time the cto_timer runs. ...and if it hasn't - * been asserted in hardware by that time then we'll assume it'll never - * come. + * dw_mci_wd_arm() skips arming when the event was already delivered, + * and the watchdog callback rechecks under the same lock before + * declaring a timeout, so both orderings of the race are covered. */ spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) - mod_timer(&host->cto_timer, - jiffies + msecs_to_jiffies(cto_ms) + 1); + dw_mci_wd_arm(host, cto_ms, DW_MCI_WD_CMD_EVENTS, + DW_MCI_WD_CMD_EVENTS, + BIT(STATE_SENDING_CMD) | BIT(STATE_SENDING_STOP) | + BIT(STATE_SENDING_CMD11)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } =20 @@ -1881,14 +2009,6 @@ static bool dw_mci_clear_pending_cmd_complete(struct= dw_mci *host) if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) return false; =20 - /* - * Really be certain that the timer has stopped. This is a bit of - * paranoia and could only really happen if we had really bad - * interrupt latency and the interrupt routine and timeout were - * running concurrently so that the timer_delete() in the interrupt - * handler couldn't run. - */ - WARN_ON(timer_delete_sync(&host->cto_timer)); clear_bit(EVENT_CMD_COMPLETE, &host->pending_events); =20 return true; @@ -2633,7 +2753,7 @@ static void dw_mci_write_data_pio(struct dw_mci *host) =20 static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) { - timer_delete(&host->cto_timer); + dw_mci_wd_deliver(host, BIT(EVENT_CMD_COMPLETE)); =20 if (!host->cmd_status) host->cmd_status =3D status; @@ -2680,7 +2800,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *de= v_id) if (pending & DW_MCI_CMD_ERROR_FLAGS) { spin_lock(&host->irq_lock); =20 - timer_delete(&host->cto_timer); + dw_mci_wd_deliver(host, BIT(EVENT_CMD_COMPLETE)); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status =3D pending; smp_wmb(); /* drain writebuffer */ @@ -3008,61 +3128,6 @@ static void dw_mci_cmd11_timer(struct timer_list *t) queue_work(system_bh_wq, &host->bh_work); } =20 -static void dw_mci_cto_timer(struct timer_list *t) -{ - struct dw_mci *host =3D timer_container_of(host, t, cto_timer); - unsigned long irqflags; - u32 pending; - - spin_lock_irqsave(&host->irq_lock, irqflags); - - /* - * If somehow we have very bad interrupt latency it's remotely possible - * that the timer could fire while the interrupt is still pending or - * while the interrupt is midway through running. Let's be paranoid - * and detect those two cases. Note that this is paranoia is somewhat - * justified because in this function we don't actually cancel the - * pending command in the controller--we just assume it will never come. - */ - pending =3D mci_readl(host, MINTSTS); /* read-only mask reg */ - if (pending & (DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_CMD_DONE)) { - /* The interrupt should fire; no need to act but we can warn */ - dev_warn(host->dev, "Unexpected interrupt latency\n"); - goto exit; - } - if (test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) { - /* Presumably interrupt handler couldn't delete the timer */ - dev_warn(host->dev, "CTO timeout when already completed\n"); - goto exit; - } - - /* - * Continued paranoia to make sure we're in the state we expect. - * This paranoia isn't really justified but it seems good to be safe. - */ - switch (host->state) { - case STATE_SENDING_CMD11: - case STATE_SENDING_CMD: - case STATE_SENDING_STOP: - /* - * If CMD_DONE interrupt does NOT come in sending command - * state, we should notify the driver to terminate current - * transfer and report a command timeout to the core. - */ - host->cmd_status =3D SDMMC_INT_RTO; - set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); - break; - default: - dev_warn(host->dev, "Unexpected command timeout, state %d\n", - host->state); - break; - } - -exit: - spin_unlock_irqrestore(&host->irq_lock, irqflags); -} - static void dw_mci_dto_timer(struct timer_list *t) { struct dw_mci *host =3D timer_container_of(host, t, dto_timer); @@ -3261,8 +3326,9 @@ int dw_mci_probe(struct dw_mci *host) } } =20 + hrtimer_setup(&host->wd_timer, dw_mci_watchdog_fn, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); - timer_setup(&host->cto_timer, dw_mci_cto_timer, 0); timer_setup(&host->dto_timer, dw_mci_dto_timer, 0); =20 spin_lock_init(&host->lock); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 38610c8..7a14f3f 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -122,8 +122,12 @@ struct dw_mci_dma_slave { * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_irq: SDIO interrupt bit in interrupt registers. + * @wd_timer: Central watchdog guarding request legs against hardware going + * silent; fires when an expected completion event fails to arrive in + * time so that the state machine can synthesize an error. + * @wd_events: pending_events bits still awaited by the armed watch. + * @wd_states: host->state values for which the armed watch is valid. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. - * @cto_timer: Timer for broken command transfer over scheme. * @dto_timer: Timer for broken data transfer over scheme. * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. @@ -235,8 +239,11 @@ struct dw_mci { =20 int sdio_irq; =20 + struct hrtimer wd_timer; + unsigned long wd_events; + unsigned long wd_states; + struct timer_list cmd11_timer; - struct timer_list cto_timer; struct timer_list dto_timer; =20 #ifdef CONFIG_FAULT_INJECTION --=20 2.7.4 From nobody Mon Sep 28 02:58:01 2026 Received: from mail-m15594.qiye.163.com (mail-m15594.qiye.163.com [101.71.155.94]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 600BA47F790; Thu, 27 Aug 2026 14:48:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=101.71.155.94 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787842146; cv=none; b=K67FNjM/PVl2Jo5zS+PMZp4d21DpnJZXmVIZZ4JV3vqt1GKAj7VOanwhE7L2gjp1YbXFcvVr8IO3drg5UiEOL9nhLb8ye17E7+L/6aWu/PdTy8iNTDHsWMJ+gBbrIkMYxCTLS/UaWKqYjXsHVdQJvnIYAYURBTHyVAxseGFd07o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787842146; c=relaxed/simple; bh=raH/sGMEW6ZLjJvBqwPUPPTj+FCVd9+eJyem3P7gPcQ=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=fQ7UWt/3F90PE6zoAugOrv1Q3fjKcfuiUf/KNizPUQ7PEfUAj9EWUDKQYeRoz4XT2ZfjZRDb/VUWH/F+nJugotwdnZKKolo8OMfsrAb+c6Gia+qRED35lbV2mhJFwSXmjYSum5FgKAPIupR9nHztXnZsZk4A+Qwsl+IgBtZ/OCM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com; spf=pass smtp.mailfrom=rock-chips.com; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b=e/FNhmD+; arc=none smtp.client-ip=101.71.155.94 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b="e/FNhmD+" Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.qiye.163.com (Hmail) with ESMTP id 4b88a3214; Thu, 27 Aug 2026 15:58:43 +0800 (GMT+08:00) From: Shawn Lin To: Ulf Hansson Cc: Jaehoon Chung , Marek Szyprowski , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Lin Subject: [PATCH 2/4] mmc: dw_mmc: convert DTO onto the central watchdog Date: Thu, 27 Aug 2026 15:58:04 +0800 Message-Id: <1787817486-137277-3-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> References: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> X-HM-Tid: 0aa0423a78b903a4kunm6c47a0ea1fecd9 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFDSUNOT01LS0k3V1kYFggdWUFKV1ktWUFJV1kPCRoVCBIfWUFZQh5LGlZIGR1IH0IdGE wZSEtWFRQJFhoXVRMBExYaEhckFA4PWVdZGBILWUFZTkNVSUlVTFVKSk9ZV1kWGg8SFR0UWUFZT0 tIVUpLSU9PT0hVSktLVUpCS0tZBg++ DKIM-Signature: a=rsa-sha256; b=e/FNhmD+BO6cPR6nKOTdzfDNBT7MMHDuyIcaxGovjwyRzg2B9fE7Szf4tQeg4AGbVufrW32SE9gyRp/MQ8+qtZcPxq6MzAsJszyjvI8nD/gI2rrjaHCITQ4B3UJw66l+SVb7t+EUk4QDxg3qqZWnDXSQeslKonhukuNFwTaqFm0=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=qdQ0mWOFr10vR63BA6DRLoAdLcpAF3Ay2MyP1ObNvDA=; h=date:mime-version:subject:message-id:from; Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The data timeout joins the command timeout on the central watchdog; dto_timer is deleted. dw_mci_set_drto() arms DW_MCI_WD_DATA_EVENTS with EVENT_DATA_COMPLETE as its precheck mask: a DATA_ERROR that arrived while still waiting for the paired completion must not prevent the watch -- the legacy mod_timer() guard tested exactly that one bit, and the fault-injection machinery relies on this by injecting DATA_ERROR early. The EXTENDED_TMOUT quirk semantics fall out naturally now: * On quirk hosts the data-error branch delivers the whole watched set, stopping the watch since no further data events will come -- this mirrors the former conditional timer_delete() plus the manual EVENT_DATA_COMPLETE side-post. * Without the quirk nothing is delivered there and the outstanding watch keeps guarding until a genuine DATA_OVER arrives, exactly like leaving dto_timer running did. The DATA_OVER branch delivers unconditionally, superseding its unconditional timer_delete(). The stale-timer WARN_ON + timer_delete_sync() dance in dw_mci_clear_pending_data_complete() goes away for the same reason as on the command leg: a callback racing past its checks is idempotent under irq_lock. No functional change intended. Signed-off-by: Shawn Lin --- drivers/mmc/host/dw_mmc.c | 64 ++++---------------------------------------= ---- drivers/mmc/host/dw_mmc.h | 2 -- 2 files changed, 5 insertions(+), 61 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cefc873..31cf728 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1998,9 +1998,9 @@ static void dw_mci_set_drto(struct dw_mci *host) drto_ms +=3D 10; =20 spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) - mod_timer(&host->dto_timer, - jiffies + msecs_to_jiffies(drto_ms)); + dw_mci_wd_arm(host, drto_ms, BIT(EVENT_DATA_COMPLETE), + DW_MCI_WD_DATA_EVENTS, + BIT(STATE_SENDING_DATA) | BIT(STATE_DATA_BUSY)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } =20 @@ -2019,8 +2019,6 @@ static bool dw_mci_clear_pending_data_complete(struct= dw_mci *host) if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) return false; =20 - /* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */ - WARN_ON(timer_delete_sync(&host->dto_timer)); clear_bit(EVENT_DATA_COMPLETE, &host->pending_events); =20 return true; @@ -2813,7 +2811,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *de= v_id) spin_lock(&host->irq_lock); =20 if (host->quirks & DW_MMC_QUIRK_EXTENDED_TMOUT) - timer_delete(&host->dto_timer); + dw_mci_wd_deliver(host, DW_MCI_WD_DATA_EVENTS); =20 /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); @@ -2834,7 +2832,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *de= v_id) if (pending & SDMMC_INT_DATA_OVER) { spin_lock(&host->irq_lock); =20 - timer_delete(&host->dto_timer); + dw_mci_wd_deliver(host, DW_MCI_WD_DATA_EVENTS); =20 mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) @@ -3128,57 +3126,6 @@ static void dw_mci_cmd11_timer(struct timer_list *t) queue_work(system_bh_wq, &host->bh_work); } =20 -static void dw_mci_dto_timer(struct timer_list *t) -{ - struct dw_mci *host =3D timer_container_of(host, t, dto_timer); - unsigned long irqflags; - u32 pending; - - spin_lock_irqsave(&host->irq_lock, irqflags); - - /* - * The DTO timer is much longer than the CTO timer, so it's even less - * likely that we'll these cases, but it pays to be paranoid. - */ - pending =3D mci_readl(host, MINTSTS); /* read-only mask reg */ - if (pending & SDMMC_INT_DATA_OVER) { - /* The interrupt should fire; no need to act but we can warn */ - dev_warn(host->dev, "Unexpected data interrupt latency\n"); - goto exit; - } - if (test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) { - /* Presumably interrupt handler couldn't delete the timer */ - dev_warn(host->dev, "DTO timeout when already completed\n"); - goto exit; - } - - /* - * Continued paranoia to make sure we're in the state we expect. - * This paranoia isn't really justified but it seems good to be safe. - */ - switch (host->state) { - case STATE_SENDING_DATA: - case STATE_DATA_BUSY: - /* - * If DTO interrupt does NOT come in sending data state, - * we should notify the driver to terminate current transfer - * and report a data timeout to the core. - */ - host->data_status =3D SDMMC_INT_DRTO; - set_bit(EVENT_DATA_ERROR, &host->pending_events); - set_bit(EVENT_DATA_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); - break; - default: - dev_warn(host->dev, "Unexpected data timeout, state %d\n", - host->state); - break; - } - -exit: - spin_unlock_irqrestore(&host->irq_lock, irqflags); -} - static int dw_mci_parse_dt(struct dw_mci *host) { struct device *dev =3D host->dev; @@ -3329,7 +3276,6 @@ int dw_mci_probe(struct dw_mci *host) hrtimer_setup(&host->wd_timer, dw_mci_watchdog_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL); timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); - timer_setup(&host->dto_timer, dw_mci_dto_timer, 0); =20 spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 7a14f3f..7af2b45 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -128,7 +128,6 @@ struct dw_mci_dma_slave { * @wd_events: pending_events bits still awaited by the armed watch. * @wd_states: host->state values for which the armed watch is valid. * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. - * @dto_timer: Timer for broken data transfer over scheme. * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. * @ctype: Card type for this host. @@ -244,7 +243,6 @@ struct dw_mci { unsigned long wd_states; =20 struct timer_list cmd11_timer; - struct timer_list dto_timer; =20 #ifdef CONFIG_FAULT_INJECTION struct fault_attr fail_data_crc; --=20 2.7.4 From nobody Mon Sep 28 02:58:01 2026 Received: from mail-m32123.qiye.163.com (mail-m32123.qiye.163.com [220.197.32.123]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 5F90A38C2D0; Thu, 27 Aug 2026 17:02:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.32.123 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787850170; cv=none; b=Lnz9vpAihsjQsxMvpljfR70HLw8cWDyGB561YqrJkjwYtxreFUXSn2bvLOJoXwzOhpCCQJPT+eZaRRFJMhU/7kLQe8V2ZU5OSbSxs1A1+TxjhHxGv6U+L1TukAbnfCdNDevREFi9jItlfJHPtEN7yUf5pmIDJBAnJCCLDLbMRzk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787850170; c=relaxed/simple; bh=vOttp+mNyIYjbgnjElgso5G0loYzaLcNE+otvF/3gCg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=CpO7PZ3C+Tj08HPTAk75AsPSfio0EyMSoBaWoHc9OVlNp9GLXRRANRmLp+WNW/sMDpo4CkR2Aal4ho8a1wsvQVoZ3SteCombS8TerrUcn4BbSSs5DmlJnEaJiAUs86x23SyBYKtJc6JEAIpEpj07qg2pUiMpJBo+FAYFbqkKvfA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com; spf=pass smtp.mailfrom=rock-chips.com; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b=QXM4VpBY; arc=none smtp.client-ip=220.197.32.123 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b="QXM4VpBY" Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.qiye.163.com (Hmail) with ESMTP id 4b88a3275; Thu, 27 Aug 2026 15:58:56 +0800 (GMT+08:00) From: Shawn Lin To: Ulf Hansson Cc: Jaehoon Chung , Marek Szyprowski , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Lin Subject: [PATCH 3/4] mmc: dw_mmc: absorb CMD11 timeout into the central watchdog Date: Thu, 27 Aug 2026 15:58:05 +0800 Message-Id: <1787817486-137277-4-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> References: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> X-HM-Tid: 0aa0423aaade03a4kunm6c47a0ea1fed77 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFDSUNOT01LS0k3V1kYFggdWUFKV1ktWUFJV1kPCRoVCBIfWUFZQhoeQlYaHUhMTk9PHU xIH0lWFRQJFhoXVRMBExYaEhckFA4PWVdZGBILWUFZTkNVSUlVTFVKSk9ZV1kWGg8SFR0UWUFZT0 tIVUpLSU9PT0hVSktLVUpCS0tZBg++ DKIM-Signature: a=rsa-sha256; b=QXM4VpBYiFxhXuM75mClnQiKl0k1B1KJz/uq9F7o/l6r8njIrA5Vp4ce6nGkldaHoThhJsQ8JSqwD1o0NbV6yRP+JjM+ouAVQWjK0AJ8ujD9OX53WxuMtGE93UgMn0Of1QJtNrm0c451J8HfJzScFRTP4KuJm8umTkwNZ+GB6xc=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=mqGel0rQOrd3I9fRVfigJW2O4XO2waBBa7Aho3TjHpQ=; h=date:mime-version:subject:message-id:from; Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" The voltage switch (CMD11) keeps its dedicated 500ms deadline, but it is now just another arm of the central watchdog; cmd11_timer is deleted. The synthesized payload is identical to what the command leg watchdog produces (cmd_status =3D RTO plus EVENT_CMD_COMPLETE), so the request state machine cannot tell the difference. Behavior notes for review: * The extra jiffy in the legacy '500ms + 1' arming was pure jiffies rollover paranoia and disappears together with the jiffies math. * Since patch 1 arms the regular command watch on every RESP_EXP command -- including voltage switches -- the subsequent arm here replaces it, as documented there. For a genuinely stuck CMD11 the abort latency therefore becomes exactly 500ms instead of racing min(cto_ms, 500ms) between two timers as before; the reported error (-ETIMEDOUT either way) is unchanged. * dw_mci_cmd_interrupt() already delivers the watched events under irq_lock on any completion path, so the former out-of-lock timer_delete() next to the VOLT_SWITCH branch simply goes away. No functional change intended. Signed-off-by: Shawn Lin --- drivers/mmc/host/dw_mmc.c | 30 ++++-------------------------- drivers/mmc/host/dw_mmc.h | 3 --- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 31cf728..6852c05 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -1393,17 +1393,16 @@ static void dw_mci_start_request(struct dw_mci *hos= t, struct mmc_command *cmd) /* * Databook says to fail after 2ms w/ no response, but evidence * shows that sometimes the cmd11 interrupt takes over 130ms. - * We'll set to 500ms, plus an extra jiffy just in case jiffies - * is just about to roll over. + * We'll set to 500ms. * * We do this whole thing under spinlock and only if the * command hasn't already completed (indicating the irq * already ran so we don't want the timeout). */ spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) - mod_timer(&host->cmd11_timer, - jiffies + msecs_to_jiffies(500) + 1); + dw_mci_wd_arm(host, 500, DW_MCI_WD_CMD_EVENTS, + DW_MCI_WD_CMD_EVENTS, + BIT(STATE_SENDING_CMD11)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } =20 @@ -2784,15 +2783,9 @@ static irqreturn_t dw_mci_interrupt(int irq, void *d= ev_id) mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); pending &=3D ~SDMMC_INT_VOLT_SWITCH; =20 - /* - * Hold the lock; we know cmd11_timer can't be kicked - * off after the lock is released, so safe to delete. - */ spin_lock(&host->irq_lock); dw_mci_cmd_interrupt(host, pending); spin_unlock(&host->irq_lock); - - timer_delete(&host->cmd11_timer); } =20 if (pending & DW_MCI_CMD_ERROR_FLAGS) { @@ -3112,20 +3105,6 @@ static void dw_mci_init_dma(struct dw_mci *host) host->use_dma =3D TRANS_MODE_PIO; } =20 -static void dw_mci_cmd11_timer(struct timer_list *t) -{ - struct dw_mci *host =3D timer_container_of(host, t, cmd11_timer); - - if (host->state !=3D STATE_SENDING_CMD11) { - dev_warn(host->dev, "Unexpected CMD11 timeout\n"); - return; - } - - host->cmd_status =3D SDMMC_INT_RTO; - set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); -} - static int dw_mci_parse_dt(struct dw_mci *host) { struct device *dev =3D host->dev; @@ -3275,7 +3254,6 @@ int dw_mci_probe(struct dw_mci *host) =20 hrtimer_setup(&host->wd_timer, dw_mci_watchdog_fn, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); =20 spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 7af2b45..7b70392 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -127,7 +127,6 @@ struct dw_mci_dma_slave { * time so that the state machine can synthesize an error. * @wd_events: pending_events bits still awaited by the armed watch. * @wd_states: host->state values for which the armed watch is valid. - * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. * @ctype: Card type for this host. @@ -242,8 +241,6 @@ struct dw_mci { unsigned long wd_events; unsigned long wd_states; =20 - struct timer_list cmd11_timer; - #ifdef CONFIG_FAULT_INJECTION struct fault_attr fail_data_crc; struct hrtimer fault_timer; --=20 2.7.4 From nobody Mon Sep 28 02:58:01 2026 Received: from mail-m1973187.qiye.163.com (mail-m1973187.qiye.163.com [220.197.31.87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7AB1640F726; Thu, 27 Aug 2026 09:14:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=220.197.31.87 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787822100; cv=none; b=rbQZ/xWiJrNfyhpLTpwM/lDauML4Egd+A/ZdVsEMozEgTwUm/bQdfvRIH2ytgaRG4JeTSLzPdoub8AwkXpw+ym1sCGlPGmw9Lfi2lhEnFH9Kb2nI8U3eMRY8B5QwnyqSmsZxqUhAtZu4LxUrR8axRxzYOju5KDAAFNcWnV7Fmjk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1787822100; c=relaxed/simple; bh=grmCiVipkjXWoPdeDos2G9qwqwt5gNiNBomxnucVHLM=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References; b=pKTUraBA8dGbPPkzYLTCIcKW3otabJEoeV+OwNMKGj5A/oCJdxwH3RHD/K7fvQoDTgRIwmSznrbLUlwIpJoIyggQTp/t6fHPYjsUzhi/uyLdeBvdFNrtnk8BKIVyFOSkZ4wn6mH0OUCL/7qYIHwRMD5Qy8jZT2SxRUImgKuIQSg= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com; spf=pass smtp.mailfrom=rock-chips.com; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b=H67ADWp1; arc=none smtp.client-ip=220.197.31.87 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=rock-chips.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=rock-chips.com header.i=@rock-chips.com header.b="H67ADWp1" Received: from localhost.localdomain (unknown [58.22.7.114]) by smtp.qiye.163.com (Hmail) with ESMTP id 4b88a3286; Thu, 27 Aug 2026 15:58:58 +0800 (GMT+08:00) From: Shawn Lin To: Ulf Hansson Cc: Jaehoon Chung , Marek Szyprowski , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Shawn Lin Subject: [PATCH 4/4] mmc: dw_mmc: expose the watchdog state in debugfs Date: Thu, 27 Aug 2026 15:58:06 +0800 Message-Id: <1787817486-137277-5-git-send-email-shawn.lin@rock-chips.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> References: <1787817486-137277-1-git-send-email-shawn.lin@rock-chips.com> X-HM-Tid: 0aa0423ab57203a4kunm6c47a0ea1fed97 X-HM-MType: 1 X-HM-Spam-Status: e1kfGhgUHx5ZQUpXWQgPGg8OCBgUHx5ZQUlOS1dZFg8aDwILHllBWSg2Ly tZV1koWUFDSUNOT01LS0k3V1kYFggdWUFKV1ktWUFJV1kPCRoVCBIfWUFZQh5JH1YfTB0eTkhLQx hNGE1WFRQJFhoXVRMBExYaEhckFA4PWVdZGBILWUFZTkNVSUlVTFVKSk9ZV1kWGg8SFR0UWUFZT0 tIVUpLSU9PT0hVSktLVUpCS0tZBg++ DKIM-Signature: a=rsa-sha256; b=H67ADWp1qVuc93lA971kTNc5ApvNUlmhZLVmNyZ5afwPHywtoQpKMMQzGjL21OWaWi4TExdVK0EfFDmJ+19YCJXrizsDzaLmz/yPSqGGE+GsU2jQMrFl0D5xUDwfb0cR09jKpsTPBf0LoNUcG8zXWmpW4LEhTexpujH41OKFcsk=; c=relaxed/relaxed; s=default; d=rock-chips.com; v=1; bh=JZ4XpEb1+g28iCRMjVK3FX/RGC7lLKUvYM01CWuJzec=; h=date:mime-version:subject:message-id:from; Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Debugging hangs on the request legs now means asking 'what was the watchdog guarding and until when?' Expose the awaited events, the valid states, and the absolute deadline of the current watch next to the existing pending_events/completed_events nodes; all three zero out once a leg is settled or the watch fired. Signed-off-by: Shawn Lin --- drivers/mmc/host/dw_mmc.c | 6 ++++++ drivers/mmc/host/dw_mmc.h | 1 + 2 files changed, 7 insertions(+) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 6852c05..ff0b25d 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -108,6 +108,7 @@ static void dw_mci_wd_arm(struct dw_mci *host, unsigned= long ms, =20 host->wd_events =3D events; host->wd_states =3D states; + host->wd_deadline_ns =3D ktime_get_ns() + (u64)ms * NSEC_PER_MSEC; hrtimer_start(&host->wd_timer, ms_to_ktime(ms), HRTIMER_MODE_REL); } =20 @@ -167,6 +168,7 @@ static enum hrtimer_restart dw_mci_watchdog_fn(struct h= rtimer *t) =20 host->wd_events =3D 0; host->wd_states =3D 0; + host->wd_deadline_ns =3D 0; =20 spin_unlock_irqrestore(&host->irq_lock, irqflags); =20 @@ -300,6 +302,10 @@ static void dw_mci_init_debugfs(struct dw_mci *host) &host->pending_events); debugfs_create_xul("completed_events", 0400, root, &host->completed_events); + debugfs_create_x64("wd_deadline_ns", 0400, root, + &host->wd_deadline_ns); + debugfs_create_xul("wd_events", 0400, root, &host->wd_events); + debugfs_create_xul("wd_states", 0400, root, &host->wd_states); #ifdef CONFIG_FAULT_INJECTION fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc); #endif diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 7b70392..12b88e7 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -240,6 +240,7 @@ struct dw_mci { struct hrtimer wd_timer; unsigned long wd_events; unsigned long wd_states; + u64 wd_deadline_ns; =20 #ifdef CONFIG_FAULT_INJECTION struct fault_attr fail_data_crc; --=20 2.7.4