linux-mmc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Russell King <rmk+kernel@arm.linux.org.uk>
To: Chris Ball <chris@printf.net>, linux-mmc@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org,
	Ulf Hansson <ulf.hansson@linaro.org>
Subject: [PATCH 07/38] mmc: sdhci: more efficient interrupt enable register handling
Date: Wed, 23 Apr 2014 20:06:45 +0100	[thread overview]
Message-ID: <E1Wd2VJ-0003ui-VE@rmk-PC.arm.linux.org.uk> (raw)
In-Reply-To: <20140423185534.GA26756@n2100.arm.linux.org.uk>

Rather than wasting cycles read-modify-writing the interrupt enable
registers, cache the value locally instead.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/mmc/host/sdhci.c  | 98 +++++++++++++++++++++++------------------------
 include/linux/mmc/sdhci.h |  3 ++
 2 files changed, 50 insertions(+), 51 deletions(-)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3af58f3cc165..d568c8e4ee33 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -131,27 +131,6 @@ static void sdhci_dumpregs(struct sdhci_host *host)
  *                                                                           *
 \*****************************************************************************/
 
-static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set)
-{
-	u32 ier;
-
-	ier = sdhci_readl(host, SDHCI_INT_ENABLE);
-	ier &= ~clear;
-	ier |= set;
-	sdhci_writel(host, ier, SDHCI_INT_ENABLE);
-	sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE);
-}
-
-static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs)
-{
-	sdhci_clear_set_irqs(host, 0, irqs);
-}
-
-static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs)
-{
-	sdhci_clear_set_irqs(host, irqs, 0);
-}
-
 static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
 {
 	u32 present, irqs;
@@ -165,9 +144,12 @@ static void sdhci_set_card_detection(struct sdhci_host *host, bool enable)
 	irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT;
 
 	if (enable)
-		sdhci_unmask_irqs(host, irqs);
+		host->ier |= irqs;
 	else
-		sdhci_mask_irqs(host, irqs);
+		host->ier &= ~irqs;
+
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
 static void sdhci_enable_card_detection(struct sdhci_host *host)
@@ -183,17 +165,12 @@ static void sdhci_disable_card_detection(struct sdhci_host *host)
 static void sdhci_reset(struct sdhci_host *host, u8 mask)
 {
 	unsigned long timeout;
-	u32 uninitialized_var(ier);
-
 	if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
 		if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) &
 			SDHCI_CARD_PRESENT))
 			return;
 	}
 
-	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
-		ier = sdhci_readl(host, SDHCI_INT_ENABLE);
-
 	if (host->ops->platform_reset_enter)
 		host->ops->platform_reset_enter(host, mask);
 
@@ -224,8 +201,10 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask)
 	if (host->ops->platform_reset_exit)
 		host->ops->platform_reset_exit(host, mask);
 
-	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET)
-		sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier);
+	if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) {
+		sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+		sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+	}
 
 	if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) {
 		if ((host->ops->enable_dma) && (mask & SDHCI_RESET_ALL))
@@ -242,11 +221,14 @@ static void sdhci_init(struct sdhci_host *host, int soft)
 	else
 		sdhci_reset(host, SDHCI_RESET_ALL);
 
-	sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK,
-		SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
-		SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX |
-		SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT |
-		SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE);
+	host->ier = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT |
+		    SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT |
+		    SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC |
+		    SDHCI_INT_TIMEOUT | SDHCI_INT_DATA_END |
+		    SDHCI_INT_RESPONSE;
+
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 
 	if (soft) {
 		/* force clock reconfiguration */
@@ -721,9 +703,12 @@ static void sdhci_set_transfer_irqs(struct sdhci_host *host)
 	u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR;
 
 	if (host->flags & SDHCI_REQ_USE_DMA)
-		sdhci_clear_set_irqs(host, pio_irqs, dma_irqs);
+		host->ier = (host->ier & ~pio_irqs) | dma_irqs;
 	else
-		sdhci_clear_set_irqs(host, dma_irqs, pio_irqs);
+		host->ier = (host->ier & ~dma_irqs) | pio_irqs;
+
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 }
 
 static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_command *cmd)
@@ -1711,9 +1696,12 @@ static void sdhci_enable_sdio_irq_nolock(struct sdhci_host *host, int enable)
 {
 	if (!(host->flags & SDHCI_DEVICE_DEAD)) {
 		if (enable)
-			sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT);
+			host->ier |= SDHCI_INT_CARD_INT;
 		else
-			sdhci_mask_irqs(host, SDHCI_INT_CARD_INT);
+			host->ier &= ~SDHCI_INT_CARD_INT;
+
+		sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+		sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 		mmiowb();
 	}
 }
@@ -1855,7 +1843,6 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 {
 	struct sdhci_host *host;
 	u16 ctrl;
-	u32 ier;
 	int tuning_loop_counter = MAX_TUNING_LOOP;
 	unsigned long timeout;
 	int err = 0;
@@ -1909,8 +1896,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 	 * to make sure we don't hit a controller bug, we _only_
 	 * enable Buffer Read Ready interrupt here.
 	 */
-	ier = sdhci_readl(host, SDHCI_INT_ENABLE);
-	sdhci_clear_set_irqs(host, ier, SDHCI_INT_DATA_AVAIL);
+	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_INT_ENABLE);
+	sdhci_writel(host, SDHCI_INT_DATA_AVAIL, SDHCI_SIGNAL_ENABLE);
 
 	/*
 	 * Issue CMD19 repeatedly till Execute Tuning is set to 0 or the number
@@ -2042,7 +2029,8 @@ static int sdhci_execute_tuning(struct mmc_host *mmc, u32 opcode)
 	if (err && (host->flags & SDHCI_USING_RETUNING_TIMER))
 		err = 0;
 
-	sdhci_clear_set_irqs(host, SDHCI_INT_DATA_AVAIL, ier);
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 	spin_unlock_irqrestore(&host->lock, flags);
 	sdhci_runtime_pm_put(host);
 
@@ -2455,10 +2443,12 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id)
 			 * More testing are needed here to ensure it works
 			 * for other platforms though.
 			 */
-			sdhci_mask_irqs(host, present ? SDHCI_INT_CARD_INSERT :
-							SDHCI_INT_CARD_REMOVE);
-			sdhci_unmask_irqs(host, present ? SDHCI_INT_CARD_REMOVE :
-							  SDHCI_INT_CARD_INSERT);
+			host->ier &= ~(SDHCI_INT_CARD_INSERT |
+				       SDHCI_INT_CARD_REMOVE);
+			host->ier |= present ? SDHCI_INT_CARD_REMOVE :
+					       SDHCI_INT_CARD_INSERT;
+			sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+			sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 
 			sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT |
 				     SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS);
@@ -2587,7 +2577,9 @@ int sdhci_suspend_host(struct sdhci_host *host)
 	}
 
 	if (!device_may_wakeup(mmc_dev(host->mmc))) {
-		sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+		host->ier = 0;
+		sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+		sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
 		free_irq(host->irq, host);
 	} else {
 		sdhci_enable_irq_wakeups(host);
@@ -2686,7 +2678,9 @@ int sdhci_runtime_suspend_host(struct sdhci_host *host)
 	}
 
 	spin_lock_irqsave(&host->lock, flags);
-	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK & ~SDHCI_INT_CARD_INT);
+	host->ier &= SDHCI_INT_CARD_INT;
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
 	spin_unlock_irqrestore(&host->lock, flags);
 
 	synchronize_hardirq(host->irq);
@@ -3277,7 +3271,8 @@ int sdhci_add_host(struct sdhci_host *host)
 #ifdef SDHCI_USE_LEDS_CLASS
 reset:
 	sdhci_reset(host, SDHCI_RESET_ALL);
-	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
 	free_irq(host->irq, host);
 #endif
 untasklet:
@@ -3319,7 +3314,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead)
 	if (!dead)
 		sdhci_reset(host, SDHCI_RESET_ALL);
 
-	sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK);
+	sdhci_writel(host, 0, SDHCI_INT_ENABLE);
+	sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE);
 	free_irq(host->irq, host);
 
 	del_timer_sync(&host->timer);
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index f1c8e14e8751..9361d8ef509d 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -178,6 +178,9 @@ struct sdhci_host {
 
 	u32			thread_isr;
 
+	/* cached registers */
+	u32			ier;
+
 	wait_queue_head_t	buf_ready_int;	/* Waitqueue for Buffer Read Ready interrupt */
 	unsigned int		tuning_done;	/* Condition flag set when CMD19 succeeds */
 
-- 
1.8.3.1


  parent reply	other threads:[~2014-04-23 19:06 UTC|newest]

Thread overview: 94+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-23 18:55 [PATCH 00/38] MMC updates, plus CuBox-i WiFi support Russell King - ARM Linux
2014-04-23 19:06 ` [PATCH 01/38] mmc: sdio_irq: rework sdio irq handling Russell King
2014-04-24  7:58   ` Ulf Hansson
2014-04-24  8:46     ` Russell King - ARM Linux
2014-04-24  8:58       ` Ulf Hansson
2014-04-23 19:06 ` [PATCH 02/38] mmc: sdhci: clean up interrupt handling Russell King
2014-04-23 19:06 ` [PATCH 03/38] mmc: sdhci: clean up sdio interrupt enable handling Russell King
2014-04-23 19:06 ` [PATCH 04/38] mmc: sdhci: convert to new SDIO IRQ handling Russell King
2014-04-23 19:06 ` [PATCH 05/38] mmc: sdhci: push card_tasklet into threaded irq handler Russell King
2014-04-23 19:06 ` [PATCH 06/38] mmc: sdhci: allow sdio interrupts while sdhci runtime suspended Russell King
2014-04-23 19:06 ` Russell King [this message]
2014-04-23 19:06 ` [PATCH 08/38] mmc: sdhci: plug hole in disabling card detection interrupts Russell King
2014-04-23 19:06 ` [PATCH 09/38] mmc: sdhci: convert generic bus width setup to library function Russell King
2014-04-23 19:07 ` [PATCH 10/38] mmc: sdhci: convert reset into a " Russell King
2014-04-23 19:07 ` [PATCH 11/38] mmc: sdhci: move FSL ESDHC reset handling quirk into esdhc code Russell King
2014-04-23 19:07 ` [PATCH 12/38] mmc: sdhci: avoid sync'ing the SG if there's no misalignment Russell King
2014-04-23 19:07 ` [PATCH 13/38] mmc: sdhci: convert ADMA descriptors to a coherent allocation Russell King
2014-04-23 19:07 ` [PATCH 14/38] mmc: sdhci: clean up sdhci_update_clock()/sdhci_set_clock() Russell King
2014-04-23 19:07 ` [PATCH 15/38] mmc: sdhci: move setting host->clock into sdhci_do_set_ios() Russell King
2014-04-23 19:07 ` [PATCH 16/38] mmc: sdhci: move setting mmc->actual_clock into set_clock handlers Russell King
2014-04-23 19:07 ` [PATCH 17/38] mmc: sdhci: convert sdhci_set_clock() into a library function Russell King
2014-04-23 19:07 ` [PATCH 18/38] mmc: sdhci-esdhc-imx: avoid DMA to kernel stack Russell King
2014-04-23 19:07 ` [PATCH 19/38] mmc: sdhci-esdhc-imx: comment runtime_pm_get_sync() in esdhc_prepare_tuning() Russell King
2014-04-23 19:07 ` [PATCH 20/38] mmc: sdhci-esdhc-imx: fix lockdep splat upon tuning Russell King
2014-04-23 19:07 ` [PATCH 21/38] mmc: sdhci: hack up driver to make it more compliant with UHS-1 Russell King
2014-04-25 12:38   ` Markus Pargmann
2014-04-25 12:49     ` Russell King - ARM Linux
2014-04-25 13:08       ` Markus Pargmann
2014-04-25 13:15         ` Russell King - ARM Linux
2014-04-25 13:22           ` Russell King - ARM Linux
2014-04-25 13:26             ` [PATCH 21/32] mmc: sdhci: set_uhs_signaling() need not return a value Russell King
2014-04-25 13:26             ` [PATCH 22/32] mmc: sdhci: convert sdhci_set_uhs_signaling() into a library function Russell King
2014-04-25 13:26             ` [PATCH 23/32] mmc: sdhci: cache timing information locally Russell King
2014-04-25 13:26             ` [PATCH 24/32] mmc: sdhci: clean up sdhci_execute_tuning() decision Russell King
2014-04-25 13:26             ` [PATCH 25/32] mmc: sdhci-esdhc-imx: remove emulation of uhs_mode Russell King
2014-04-25 13:26             ` [PATCH 26/32] mmc: sdhci-of-esdhc: remove platform_suspend/platform_resume callbacks Russell King
2014-04-25 13:26             ` [PATCH 27/32] mmc: sdhci: " Russell King
2014-04-25 13:27             ` [PATCH 28/32] mmc: sdhci-tegra: get rid of special PRESENT_STATE register handling Russell King
2014-04-25 13:27             ` [PATCH 29/32] mmc: sdhci: move regulator handling into sdhci_set_power() Russell King
2014-04-25 13:27             ` [PATCH 30/32] mmc: sdhci: move remaining power " Russell King
2014-04-25 13:27             ` [PATCH 31/32] mmc: sdhci: track whether preset mode is currently enabled in hardware Russell King
2014-04-25 13:27             ` [PATCH 32/32] mmc: sdhci: fix SDHCI dependencies Russell King
2014-04-28 13:10             ` [PATCH 21/38] mmc: sdhci: hack up driver to make it more compliant with UHS-1 Markus Pargmann
2014-04-25 16:20           ` Russell King - ARM Linux
2014-04-28 10:50     ` Ulf Hansson
2014-04-28 11:02       ` Russell King - ARM Linux
2014-04-28 11:11         ` Ulf Hansson
2014-04-28 11:51           ` Russell King - ARM Linux
2014-04-23 19:08 ` [PATCH 22/38] mmc: sdhci: set_uhs_signaling() need not return a value Russell King
2014-04-23 19:08 ` [PATCH 23/38] mmc: sdhci: convert sdhci_set_uhs_signaling() into a library function Russell King
     [not found]   ` <E1Wd2Wd-0003vp-S2-eh5Bv4kxaXIANfyc6IWni62ZND6+EDdj@public.gmane.org>
2014-06-16 10:46     ` Russell King - ARM Linux
2014-06-16 12:17       ` Ulf Hansson
     [not found]         ` <CAPDyKFrp8JokqBbo3rg2i6WYykU1C9CuPF0FL7AOHh=Gcp5=hg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-06-16 16:10           ` Ulf Hansson
2014-06-17 23:42         ` Russell King - ARM Linux
2014-06-19 12:28         ` Russell King - ARM Linux
     [not found]           ` <20140619122830.GP32514-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2014-06-19 15:57             ` Stephen Warren
2014-06-19 17:02           ` Olof Johansson
2014-04-23 19:08 ` [PATCH 24/38] mmc: sdhci: cache timing information locally Russell King
2014-04-23 19:08 ` [PATCH 25/38] mmc: sdhci: clean up sdhci_execute_tuning() decision Russell King
2014-04-23 19:08 ` [PATCH 26/38] mmc: sdhci-esdhc-imx: remove emulation of uhs_mode Russell King
2014-04-23 19:08 ` [PATCH 27/38] mmc: sdhci-of-esdhc: remove platform_suspend/platform_resume callbacks Russell King
2014-04-24  7:32   ` Ulf Hansson
2014-04-24 11:18     ` Russell King - ARM Linux
2014-04-24 11:32       ` Ulf Hansson
2014-04-24 12:27         ` Russell King - ARM Linux
2014-04-24 13:15           ` Ulf Hansson
2014-04-23 19:08 ` [PATCH 28/38] mmc: sdhci: " Russell King
2014-04-24  7:33   ` Ulf Hansson
2014-04-23 19:08 ` [PATCH 29/38] mmc: sdhci-tegra: get rid of special PRESENT_STATE register handling Russell King
2014-04-23 19:08 ` [PATCH 30/38] mmc: sdhci: move regulator handling into sdhci_set_power() Russell King
2014-04-23 19:08 ` [PATCH 31/38] mmc: sdhci: move remaining power " Russell King
2014-04-23 19:08 ` [PATCH 32/38] mmc: sdhci: track whether preset mode is currently enabled in hardware Russell King
2014-04-23 19:08 ` [PATCH 33/38] mmc: sdhci: fix SDHCI dependencies Russell King
2014-04-28 16:28   ` Stephen Warren
2014-04-23 19:09 ` [PATCH 34/38] mmc: add support for power-on sequencing through DT Russell King
2014-04-24  9:05   ` Maxime Ripard
2014-04-23 19:09 ` [PATCH 35/38] mmc: dw_mmc: call mmc_of_parse to fill in common options Russell King
2014-04-24  0:31   ` Jaehoon Chung
2014-04-24 19:42     ` Russell King - ARM Linux
2014-04-23 19:09 ` [PATCH 36/38] mmc: fix power-on sequencing for esdhc-imx driver Russell King
     [not found] ` <20140423185534.GA26756-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2014-04-23 19:09   ` [PATCH 37/38] ARM: cubox-i: add support for SD UHS-1 cards Russell King
2014-04-24  8:25   ` [PATCH 00/38] MMC updates, plus CuBox-i WiFi support Ulf Hansson
2014-04-24 10:17     ` Russell King - ARM Linux
2014-04-24 10:52       ` Ulf Hansson
2014-04-24 10:57         ` Russell King - ARM Linux
     [not found]           ` <20140424105745.GM26756-l+eeeJia6m9vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
2014-04-24 11:13             ` Ulf Hansson
2014-04-25  9:03               ` Russell King - ARM Linux
2014-04-25 11:18                 ` Ulf Hansson
     [not found]                   ` <CAPDyKFpAiegwfh=kA8NyB+DdwKT=wSGJsB=gbEgLbyv0RJ2EOQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-04-25 11:20                     ` Russell King - ARM Linux
2014-04-25 11:40                       ` Ulf Hansson
2014-04-23 19:09 ` [PATCH 38/38] ARM: cubox-i: add support for Wifi/BT Russell King
2014-04-28 16:42 ` [PATCH 00/38] MMC updates, plus CuBox-i WiFi support Stephen Warren
2014-04-28 16:52   ` Chris Ball
2014-05-07 20:49     ` Tim Kryger

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=E1Wd2VJ-0003ui-VE@rmk-PC.arm.linux.org.uk \
    --to=rmk+kernel@arm.linux.org.uk \
    --cc=chris@printf.net \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-mmc@vger.kernel.org \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).