From: Sascha Hauer <s.hauer@pengutronix.de>
To: Barebox List <barebox@lists.infradead.org>
Subject: [PATCH 08/13] ddr: imx8m: get rid of hardcoded phy address
Date: Fri, 10 Nov 2023 14:00:23 +0100 [thread overview]
Message-ID: <20231110130028.2123895-9-s.hauer@pengutronix.de> (raw)
In-Reply-To: <20231110130028.2123895-1-s.hauer@pengutronix.de>
The phy address is hardcoded in several functions. Use the address
provided in struct dram_controller instead. Some functions are used
from legacy DDR setup in board code which only uses parts of the
DDR initialization code. The board code doesn't have any struct
dram_controller *, so provide some static inline wrappers for these
functions.
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
---
arch/arm/boards/nxp-imx8mq-evk/ddrphy_train.c | 10 ++---
.../boards/phytec-som-imx8mq/ddrphy_train.c | 12 +++---
arch/arm/boards/zii-imx8mq-dev/ddrphy_train.c | 10 ++---
drivers/ddr/imx/ddrphy_train.c | 14 +++----
drivers/ddr/imx/ddrphy_utils.c | 40 ++++++++-----------
include/soc/imx8m/ddr.h | 17 ++++++--
6 files changed, 53 insertions(+), 50 deletions(-)
diff --git a/arch/arm/boards/nxp-imx8mq-evk/ddrphy_train.c b/arch/arm/boards/nxp-imx8mq-evk/ddrphy_train.c
index e8577369dc..bac7d0a517 100644
--- a/arch/arm/boards/nxp-imx8mq-evk/ddrphy_train.c
+++ b/arch/arm/boards/nxp-imx8mq-evk/ddrphy_train.c
@@ -144,7 +144,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 1D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
//configure DDRPHY-FW DMEM structure @clock0...
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
@@ -189,7 +189,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//configure DDRPHY-FW DMEM structure @clock1...
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
@@ -258,7 +258,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//set the PHY input clock to the desired frequency for pstate 0
reg32_write(0x3038a088,0x7070000);
@@ -291,7 +291,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 2D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54003,0xc80);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54006,0x11);
@@ -332,7 +332,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//Halt MPU
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
diff --git a/arch/arm/boards/phytec-som-imx8mq/ddrphy_train.c b/arch/arm/boards/phytec-som-imx8mq/ddrphy_train.c
index 2ed6578093..fac9e184ae 100644
--- a/arch/arm/boards/phytec-som-imx8mq/ddrphy_train.c
+++ b/arch/arm/boards/phytec-som-imx8mq/ddrphy_train.c
@@ -148,7 +148,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 1D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54003,0xc80);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54004,0x2);
@@ -190,7 +190,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//configure DDRPHY-FW DMEM structure @clock1...
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
@@ -224,7 +224,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 1D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54002,0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54003,0x29c);
@@ -267,7 +267,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//set the PHY input clock to the desired frequency for pstate 0
reg32_write(0x3038a088,0x7070000);
@@ -300,7 +300,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 2D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54003,0xc80);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54004,0x2);
@@ -343,7 +343,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//Halt MPU
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
diff --git a/arch/arm/boards/zii-imx8mq-dev/ddrphy_train.c b/arch/arm/boards/zii-imx8mq-dev/ddrphy_train.c
index e8577369dc..bac7d0a517 100644
--- a/arch/arm/boards/zii-imx8mq-dev/ddrphy_train.c
+++ b/arch/arm/boards/zii-imx8mq-dev/ddrphy_train.c
@@ -144,7 +144,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 1D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_1D_IMAGE);
//configure DDRPHY-FW DMEM structure @clock0...
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
@@ -189,7 +189,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//configure DDRPHY-FW DMEM structure @clock1...
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
@@ -258,7 +258,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//set the PHY input clock to the desired frequency for pstate 0
reg32_write(0x3038a088,0x7070000);
@@ -291,7 +291,7 @@ void ddr_cfg_phy(void) {
//enable APB bus to access DDRPHY RAM
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0000, 0x0);
//load the 2D training image
- ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
+ imx8m_ddr_load_train_code(DRAM_TYPE_LPDDR4, FW_2D_IMAGE);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54003,0xc80);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0x54006,0x11);
@@ -332,7 +332,7 @@ void ddr_cfg_phy(void) {
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x9);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x0);
- wait_ddrphy_training_complete();
+ imx8m_wait_ddrphy_training_complete();
//Halt MPU
reg32_write(IP2APB_DDRPHY_IPS_BASE_ADDR(0) + 4 * 0xd0099, 0x1);
diff --git a/drivers/ddr/imx/ddrphy_train.c b/drivers/ddr/imx/ddrphy_train.c
index 302acdfe62..c0555ffe01 100644
--- a/drivers/ddr/imx/ddrphy_train.c
+++ b/drivers/ddr/imx/ddrphy_train.c
@@ -9,7 +9,6 @@
#include <linux/kernel.h>
#include <soc/imx8m/ddr.h>
#include <firmware.h>
-#include <mach/imx/imx8m-regs.h>
static const u16 *lpddr4_imem_1d;
static size_t lpddr4_imem_1d_size;
@@ -53,7 +52,8 @@ void ddr_get_firmware_ddr(void)
&ddr4_dmem_2d_size);
}
-void ddr_load_train_code(enum dram_type dram_type, enum fw_type fw_type)
+void ddr_load_train_code(struct dram_controller *dram, enum dram_type dram_type,
+ enum fw_type fw_type)
{
const u16 *imem, *dmem;
size_t isize, dsize;
@@ -86,11 +86,9 @@ void ddr_load_train_code(enum dram_type dram_type, enum fw_type fw_type)
panic("No matching DDR PHY firmware found");
}
- ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR),
- DDRC_PHY_IMEM, imem, isize);
+ ddrc_phy_load_firmware(dram, DDRC_PHY_IMEM, imem, isize);
- ddrc_phy_load_firmware(IOMEM(MX8M_DDRC_PHY_BASE_ADDR),
- DDRC_PHY_DMEM, dmem, dsize);
+ ddrc_phy_load_firmware(dram, DDRC_PHY_DMEM, dmem, dsize);
}
int ddr_cfg_phy(struct dram_controller *dram, struct dram_timing_info *dram_timing)
@@ -120,7 +118,7 @@ int ddr_cfg_phy(struct dram_controller *dram, struct dram_timing_info *dram_timi
/* load the dram training firmware image */
dwc_ddrphy_apb_wr(dram, 0xd0000, 0x0);
- ddr_load_train_code(dram->dram_type, fsp_msg->fw_type);
+ ddr_load_train_code(dram, dram->dram_type, fsp_msg->fw_type);
/* load the frequency set point message block parameter */
dram_cfg = fsp_msg->fsp_cfg;
@@ -146,7 +144,7 @@ int ddr_cfg_phy(struct dram_controller *dram, struct dram_timing_info *dram_timi
dwc_ddrphy_apb_wr(dram, 0xd0099, 0x0);
/* Wait for the training firmware to complete */
- ret = wait_ddrphy_training_complete();
+ ret = wait_ddrphy_training_complete(dram);
if (ret)
return ret;
diff --git a/drivers/ddr/imx/ddrphy_utils.c b/drivers/ddr/imx/ddrphy_utils.c
index 5f80b8fdc1..37274b0b5a 100644
--- a/drivers/ddr/imx/ddrphy_utils.c
+++ b/drivers/ddr/imx/ddrphy_utils.c
@@ -10,15 +10,13 @@
#include <io.h>
#include <linux/iopoll.h>
#include <soc/imx8m/ddr.h>
-#include <mach/imx/imx8m-regs.h>
-#include <mach/imx/imx8m-ccm-regs.h>
-void ddrc_phy_load_firmware(void __iomem *phy,
+void ddrc_phy_load_firmware(struct dram_controller *dram,
enum ddrc_phy_firmware_offset offset,
const u16 *blob, size_t size)
{
while (size) {
- writew(*blob++, phy + DDRC_PHY_REG(offset));
+ writew(*blob++, dwc_ddrphy_apb_addr(dram, offset));
offset++;
size -= sizeof(*blob);
}
@@ -33,28 +31,27 @@ enum pmc_constants {
PMC_TRAIN_FAIL = 0xff,
};
-static u32 ddrc_phy_get_message(void __iomem *phy, int type)
+static u32 ddrc_phy_get_message(struct dram_controller *dram, int type)
{
- u32 r, message;
+ u32 message;
/*
* When BIT0 set to 0, the PMU has a message for the user
* Wait for it indefinitely.
*/
- readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
- r, !(r & BIT(0)), 0);
+ while (dwc_ddrphy_apb_rd(dram, 0xd0004) & BIT(0));
switch (type) {
case PMC_MESSAGE_ID:
/*
* Get the major message ID
*/
- message = readl(phy + DDRC_PHY_REG(0xd0032));
+ message = dwc_ddrphy_apb_rd(dram, 0xd0032);
break;
case PMC_MESSAGE_STREAM:
- message = readl(phy + DDRC_PHY_REG(0xd0034));
+ message = dwc_ddrphy_apb_rd(dram, 0xd0034);
message <<= 16;
- message |= readl(phy + DDRC_PHY_REG(0xd0032));
+ message |= dwc_ddrphy_apb_rd(dram, 0xd0032);
break;
}
@@ -62,37 +59,34 @@ static u32 ddrc_phy_get_message(void __iomem *phy, int type)
* By setting this register to 0, the user acknowledges the
* receipt of the message.
*/
- writel(0x00000000, phy + DDRC_PHY_REG(0xd0031));
+ dwc_ddrphy_apb_wr(dram, 0xd0031, 0x00000000);
/*
* When BIT0 set to 0, the PMU has a message for the user
*/
- readl_poll_timeout(phy + DDRC_PHY_REG(0xd0004),
- r, r & BIT(0), 0);
+ while (!(dwc_ddrphy_apb_rd(dram, 0xd0004) & BIT(0)));
- writel(0x00000001, phy + DDRC_PHY_REG(0xd0031));
+ dwc_ddrphy_apb_wr(dram, 0xd0031, 0x00000001);
return message;
}
-static void ddrc_phy_fetch_streaming_message(void __iomem *phy)
+static void ddrc_phy_fetch_streaming_message(struct dram_controller *dram)
{
- const u16 index = ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
+ const u16 index = ddrc_phy_get_message(dram, PMC_MESSAGE_STREAM);
u16 i;
for (i = 0; i < index; i++)
- ddrc_phy_get_message(phy, PMC_MESSAGE_STREAM);
+ ddrc_phy_get_message(dram, PMC_MESSAGE_STREAM);
}
-int wait_ddrphy_training_complete(void)
+int wait_ddrphy_training_complete(struct dram_controller *dram)
{
- void __iomem *phy = IOMEM(MX8M_DDRC_PHY_BASE_ADDR);
-
for (;;) {
- const u32 m = ddrc_phy_get_message(phy, PMC_MESSAGE_ID);
+ const u32 m = ddrc_phy_get_message(dram, PMC_MESSAGE_ID);
switch (m) {
case PMC_TRAIN_STREAM_START:
- ddrc_phy_fetch_streaming_message(phy);
+ ddrc_phy_fetch_streaming_message(dram);
break;
case PMC_TRAIN_SUCCESS:
return 0;
diff --git a/include/soc/imx8m/ddr.h b/include/soc/imx8m/ddr.h
index 0a2a7b0e2f..04addc1448 100644
--- a/include/soc/imx8m/ddr.h
+++ b/include/soc/imx8m/ddr.h
@@ -455,7 +455,12 @@ void ddrphy_trained_csr_save(struct dram_controller *dram, struct dram_cfg_param
void dram_config_save(struct dram_timing_info *info, unsigned long base);
/* utils function for ddr phy training */
-int wait_ddrphy_training_complete(void);
+int wait_ddrphy_training_complete(struct dram_controller *dram);
+
+static inline int imx8m_wait_ddrphy_training_complete(void)
+{
+ return wait_ddrphy_training_complete(&imx8m_dram_controller);
+}
#define reg32_write(a, v) writel(v, a)
#define reg32_read(a) readl(a)
@@ -489,9 +494,15 @@ enum ddrc_phy_firmware_offset {
DDRC_PHY_DMEM = 0x00054000U,
};
-void ddr_load_train_code(enum dram_type dram_type, enum fw_type fw_type);
+void ddr_load_train_code(struct dram_controller *dram, enum dram_type dram_type,
+ enum fw_type fw_type);
+static inline void imx8m_ddr_load_train_code(enum dram_type dram_type,
+ enum fw_type fw_type)
+{
+ ddr_load_train_code(&imx8m_dram_controller, dram_type, fw_type);
+}
-void ddrc_phy_load_firmware(void __iomem *,
+void ddrc_phy_load_firmware(struct dram_controller *dram,
enum ddrc_phy_firmware_offset,
const u16 *, size_t);
--
2.39.2
next prev parent reply other threads:[~2023-11-10 13:02 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-10 13:00 [PATCH 00/13] Add i.MX9 DDR support Sascha Hauer
2023-11-10 13:00 ` [PATCH 01/13] ddr: imx8m: rename driver to imx Sascha Hauer
2023-11-10 13:00 ` [PATCH 02/13] ddr: imx8m: introduce dram_controller struct Sascha Hauer
2023-11-10 13:00 ` [PATCH 03/13] ddr: imx8m: move get_trained_CDD() to SoC code Sascha Hauer
2023-11-10 13:00 ` [PATCH 04/13] ddr: imx8m: move PLL init to SoC specific code Sascha Hauer
2023-11-10 13:00 ` [PATCH 05/13] ddr: imx8m: clean up defines Sascha Hauer
2023-11-10 13:00 ` [PATCH 06/13] ddr: imx8m: move phy_base to controller struct Sascha Hauer
2023-11-10 13:00 ` [PATCH 07/13] ddr: imx8m: remove empty function Sascha Hauer
2023-11-10 13:00 ` Sascha Hauer [this message]
2023-11-10 13:00 ` [PATCH 09/13] ddr: imx8m: split header file Sascha Hauer
2023-11-10 13:00 ` [PATCH 10/13] ddr: imx8m: return cfg from dram_config_save() Sascha Hauer
2023-11-10 13:00 ` [PATCH 11/13] ddr: imx8m: Drop '8m' suffix from pr_fmt Sascha Hauer
2023-11-10 13:00 ` [PATCH 12/13] ddr: move imx8m_ddr_old_spreadsheet to controller Sascha Hauer
2023-11-10 13:00 ` [PATCH 13/13] ddr: Initial i.MX9 support Sascha Hauer
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=20231110130028.2123895-9-s.hauer@pengutronix.de \
--to=s.hauer@pengutronix.de \
--cc=barebox@lists.infradead.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