From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Cc: Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH 6/9] mci: omap_hsmmc: split out common code
Date: Tue, 22 Apr 2025 07:26:32 +0200 [thread overview]
Message-ID: <20250422052635.3423961-7-a.fatoum@pengutronix.de> (raw)
In-Reply-To: <20250422052635.3423961-1-a.fatoum@pengutronix.de>
In preparation for calling into the OMAP HSMMC driver at prebootloader
time, separate the driver model bits from the actual hardware driver.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
drivers/mci/Makefile | 2 +-
drivers/mci/omap_hsmmc.c | 509 +-----------------------------
drivers/mci/omap_hsmmc.h | 17 +
drivers/mci/omap_hsmmc_common.c | 530 ++++++++++++++++++++++++++++++++
4 files changed, 553 insertions(+), 505 deletions(-)
create mode 100644 drivers/mci/omap_hsmmc.h
create mode 100644 drivers/mci/omap_hsmmc_common.c
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 8f54107e46c1..b209a86d8873 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o imx-esdhc-common.o
pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o
obj-$(CONFIG_MCI_MXS) += mxs.o
-obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
+obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o omap_hsmmc_common.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
obj-$(CONFIG_MCI_ROCKCHIP_DWCMSHC) += rockchip-dwcmshc-sdhci.o
obj-$(CONFIG_MCI_TEGRA) += tegra-sdmmc.o
diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c
index 7a5135131cda..616d57bffdac 100644
--- a/drivers/mci/omap_hsmmc.c
+++ b/drivers/mci/omap_hsmmc.c
@@ -12,6 +12,8 @@
#include <io.h>
#include <linux/err.h>
+#include "omap_hsmmc.h"
+
#include <mach/omap/omap_hsmmc.h>
#if defined(CONFIG_MFD_TWL6030) && \
@@ -20,30 +22,6 @@
#include <mach/omap/omap4_twl6030_mmc.h>
#endif
-struct hsmmc {
- unsigned char res1[0x10];
- unsigned int sysconfig; /* 0x10 */
- unsigned int sysstatus; /* 0x14 */
- unsigned char res2[0x14];
- unsigned int con; /* 0x2C */
- unsigned char res3[0xD4];
- unsigned int blk; /* 0x104 */
- unsigned int arg; /* 0x108 */
- unsigned int cmd; /* 0x10C */
- unsigned int rsp10; /* 0x110 */
- unsigned int rsp32; /* 0x114 */
- unsigned int rsp54; /* 0x118 */
- unsigned int rsp76; /* 0x11C */
- unsigned int data; /* 0x120 */
- unsigned int pstate; /* 0x124 */
- unsigned int hctl; /* 0x128 */
- unsigned int sysctl; /* 0x12C */
- unsigned int stat; /* 0x130 */
- unsigned int ie; /* 0x134 */
- unsigned char res4[0x8];
- unsigned int capa; /* 0x140 */
-};
-
struct omap_mmc_driver_data {
unsigned long reg_ofs;
};
@@ -56,164 +34,11 @@ static struct omap_mmc_driver_data omap4_data = {
.reg_ofs = 0x100,
};
-/*
- * OMAP HS MMC Bit definitions
- */
-#define MMC_SOFTRESET (0x1 << 1)
-#define RESETDONE (0x1 << 0)
-#define NOOPENDRAIN (0x0 << 0)
-#define OPENDRAIN (0x1 << 0)
-#define OD (0x1 << 0)
-#define INIT_NOINIT (0x0 << 1)
-#define INIT_INITSTREAM (0x1 << 1)
-#define HR_NOHOSTRESP (0x0 << 2)
-#define STR_BLOCK (0x0 << 3)
-#define MODE_FUNC (0x0 << 4)
-#define DW8_1_4BITMODE (0x0 << 5)
-#define MIT_CTO (0x0 << 6)
-#define CDP_ACTIVEHIGH (0x0 << 7)
-#define WPP_ACTIVEHIGH (0x0 << 8)
-#define RESERVED_MASK (0x3 << 9)
-#define CTPL_MMC_SD (0x0 << 11)
-#define BLEN_512BYTESLEN (0x200 << 0)
-#define NBLK_STPCNT (0x0 << 16)
-#define DE_DISABLE (0x0 << 0)
-#define BCE_DISABLE (0x0 << 1)
-#define BCE_ENABLE (0x1 << 1)
-#define ACEN_DISABLE (0x0 << 2)
-#define DDIR_OFFSET (4)
-#define DDIR_MASK (0x1 << 4)
-#define DDIR_WRITE (0x0 << 4)
-#define DDIR_READ (0x1 << 4)
-#define MSBS_SGLEBLK (0x0 << 5)
-#define MSBS_MULTIBLK (0x1 << 5)
-#define RSP_TYPE_OFFSET (16)
-#define RSP_TYPE_MASK (0x3 << 16)
-#define RSP_TYPE_NORSP (0x0 << 16)
-#define RSP_TYPE_LGHT136 (0x1 << 16)
-#define RSP_TYPE_LGHT48 (0x2 << 16)
-#define RSP_TYPE_LGHT48B (0x3 << 16)
-#define CCCE_NOCHECK (0x0 << 19)
-#define CCCE_CHECK (0x1 << 19)
-#define CICE_NOCHECK (0x0 << 20)
-#define CICE_CHECK (0x1 << 20)
-#define DP_OFFSET (21)
-#define DP_MASK (0x1 << 21)
-#define DP_NO_DATA (0x0 << 21)
-#define DP_DATA (0x1 << 21)
-#define CMD_TYPE_NORMAL (0x0 << 22)
-#define INDEX_OFFSET (24)
-#define INDEX_MASK (0x3f << 24)
-#define INDEX(i) (i << 24)
-#define DATI_MASK (0x1 << 1)
-#define DATI_CMDDIS (0x1 << 1)
-#define DTW_1_BITMODE (0x0 << 1)
-#define DTW_4_BITMODE (0x1 << 1)
-#define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/
-#define SDBP_PWROFF (0x0 << 8)
-#define SDBP_PWRON (0x1 << 8)
-#define SDVS_1V8 (0x5 << 9)
-#define SDVS_3V0 (0x6 << 9)
-#define ICE_MASK (0x1 << 0)
-#define ICE_STOP (0x0 << 0)
-#define ICS_MASK (0x1 << 1)
-#define ICS_NOTREADY (0x0 << 1)
-#define ICE_OSCILLATE (0x1 << 0)
-#define CEN_MASK (0x1 << 2)
-#define CEN_DISABLE (0x0 << 2)
-#define CEN_ENABLE (0x1 << 2)
-#define CLKD_OFFSET (6)
-#define CLKD_MASK (0x3FF << 6)
-#define DTO_MASK (0xF << 16)
-#define DTO_15THDTO (0xE << 16)
-#define SOFTRESETALL (0x1 << 24)
-#define CC_MASK (0x1 << 0)
-#define TC_MASK (0x1 << 1)
-#define BWR_MASK (0x1 << 4)
-#define BRR_MASK (0x1 << 5)
-#define ERRI_MASK (0x1 << 15)
-#define IE_CC (0x01 << 0)
-#define IE_TC (0x01 << 1)
-#define IE_BWR (0x01 << 4)
-#define IE_BRR (0x01 << 5)
-#define IE_CTO (0x01 << 16)
-#define IE_CCRC (0x01 << 17)
-#define IE_CEB (0x01 << 18)
-#define IE_CIE (0x01 << 19)
-#define IE_DTO (0x01 << 20)
-#define IE_DCRC (0x01 << 21)
-#define IE_DEB (0x01 << 22)
-#define IE_CERR (0x01 << 28)
-#define IE_BADA (0x01 << 29)
-
-#define VS30_3V0SUP (1 << 25)
-#define VS18_1V8SUP (1 << 26)
-
-/* Driver definitions */
-#define MMCSD_SECTOR_SIZE 512
-#define MMC_CARD 0
-#define SD_CARD 1
-#define BYTE_MODE 0
-#define SECTOR_MODE 1
-#define CLK_INITSEQ 0
-#define CLK_400KHZ 1
-#define CLK_MISC 2
-
-#define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK)
-#define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE)
-
-/* Clock Configurations and Macros */
-#define MMC_CLOCK_REFERENCE 96 /* MHz */
-
-#define mmc_reg_out(addr, mask, val)\
- writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr))
-
-struct omap_hsmmc {
- struct mci_host mci;
- struct device *dev;
- struct hsmmc *base;
- void __iomem *iobase;
-};
-
#define to_hsmmc(mci) container_of(mci, struct omap_hsmmc, mci)
-static int mmc_init_stream(struct omap_hsmmc *hsmmc)
-{
- uint64_t start;
- struct hsmmc *mmc_base = hsmmc->base;
-
- writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con);
-
- writel(MMC_CMD0, &mmc_base->cmd);
- start = get_time_ns();
- while (!(readl(&mmc_base->stat) & CC_MASK)) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for cc!\n");
- return -ETIMEDOUT;
- }
- }
- writel(CC_MASK, &mmc_base->stat);
- writel(MMC_CMD0, &mmc_base->cmd);
-
- start = get_time_ns();
- while (!(readl(&mmc_base->stat) & CC_MASK)) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for cc2!\n");
- return -ETIMEDOUT;
- }
- }
- writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
-
- return 0;
-}
-
static int mmc_init_setup(struct mci_host *mci, struct device *dev)
{
struct omap_hsmmc *hsmmc = to_hsmmc(mci);
- struct hsmmc *mmc_base = hsmmc->base;
- unsigned int reg_val;
- unsigned int dsor;
- uint64_t start;
/*
* Fix voltage for mmc, if booting from nand.
@@ -226,346 +51,22 @@ static int mmc_init_setup(struct mci_host *mci, struct device *dev)
set_up_mmc_voltage_omap4();
#endif
- writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET,
- &mmc_base->sysconfig);
-
- start = get_time_ns();
- while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timeout waiting for reset done\n");
- return -ETIMEDOUT;
- }
- }
-
- writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl);
-
- start = get_time_ns();
- while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for softresetall!\n");
- return -ETIMEDOUT;
- }
- }
-
- writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
- writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
- &mmc_base->capa);
-
- reg_val = readl(&mmc_base->con) & RESERVED_MASK;
-
- writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH |
- MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK |
- HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con);
-
- dsor = 240;
- mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
- (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
- mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
- (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
-
- start = get_time_ns();
- while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for ics!\n");
- return -ETIMEDOUT;
- }
- }
-
- writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
-
- writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
-
- writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
- IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
- &mmc_base->ie);
-
- return mmc_init_stream(hsmmc);
-}
-
-static int mmc_read_data(struct omap_hsmmc *hsmmc, char *buf, unsigned int size)
-{
- struct hsmmc *mmc_base = hsmmc->base;
- unsigned int *output_buf = (unsigned int *)buf;
- unsigned int mmc_stat;
- unsigned int count;
-
- /*
- * Start Polled Read
- */
- count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
- count /= 4;
-
- while (size) {
- uint64_t start = get_time_ns();
- do {
- mmc_stat = readl(&mmc_base->stat);
- if (is_timeout(start, SECOND)) {
- dev_err(hsmmc->dev, "timedout waiting for status!\n");
- return -ETIMEDOUT;
- }
- } while (mmc_stat == 0);
-
- if ((mmc_stat & ERRI_MASK) != 0) {
- dev_err(hsmmc->dev, "Error while reading data. status: 0x%08x\n",
- mmc_stat);
- return -EIO;
- }
-
- if (mmc_stat & BRR_MASK) {
- unsigned int k;
-
- writel(readl(&mmc_base->stat) | BRR_MASK,
- &mmc_base->stat);
- for (k = 0; k < count; k++) {
- *output_buf = readl(&mmc_base->data);
- output_buf++;
- }
- size -= (count*4);
- }
-
- if (mmc_stat & BWR_MASK)
- writel(readl(&mmc_base->stat) | BWR_MASK,
- &mmc_base->stat);
-
- if (mmc_stat & TC_MASK) {
- writel(readl(&mmc_base->stat) | TC_MASK,
- &mmc_base->stat);
- break;
- }
- }
- return 0;
-}
-
-static int mmc_write_data(struct omap_hsmmc *hsmmc, const char *buf, unsigned int size)
-{
- struct hsmmc *mmc_base = hsmmc->base;
- unsigned int *input_buf = (unsigned int *)buf;
- unsigned int mmc_stat;
- unsigned int count;
-
- /*
- * Start Polled Read
- */
- count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
- count /= 4;
-
- while (size) {
- uint64_t start = get_time_ns();
- do {
- mmc_stat = readl(&mmc_base->stat);
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for status!\n");
- return -ETIMEDOUT;
- }
- } while (mmc_stat == 0);
-
- if ((mmc_stat & ERRI_MASK) != 0) {
- dev_err(hsmmc->dev, "Error while reading data. status: 0x%08x\n",
- mmc_stat);
- return -EIO;
- }
-
- if (mmc_stat & BWR_MASK) {
- unsigned int k;
-
- writel(readl(&mmc_base->stat) | BWR_MASK,
- &mmc_base->stat);
- for (k = 0; k < count; k++) {
- writel(*input_buf, &mmc_base->data);
- input_buf++;
- }
- size -= (count * 4);
- }
-
- if (mmc_stat & BRR_MASK)
- writel(readl(&mmc_base->stat) | BRR_MASK,
- &mmc_base->stat);
-
- if (mmc_stat & TC_MASK) {
- writel(readl(&mmc_base->stat) | TC_MASK,
- &mmc_base->stat);
- break;
- }
- }
- return 0;
+ return omap_hsmmc_init(hsmmc);
}
static int mmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data)
{
struct omap_hsmmc *hsmmc = to_hsmmc(mci);
- struct hsmmc *mmc_base = hsmmc->base;
- unsigned int flags, mmc_stat;
- uint64_t start;
- start = get_time_ns();
- while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for cmddis!\n");
- return -ETIMEDOUT;
- }
- }
-
- writel(0xFFFFFFFF, &mmc_base->stat);
- start = get_time_ns();
- while (readl(&mmc_base->stat)) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for stat!\n");
- return -ETIMEDOUT;
- }
- }
-
- /*
- * CMDREG
- * CMDIDX[13:8] : Command index
- * DATAPRNT[5] : Data Present Select
- * ENCMDIDX[4] : Command Index Check Enable
- * ENCMDCRC[3] : Command CRC Check Enable
- * RSPTYP[1:0]
- * 00 = No Response
- * 01 = Length 136
- * 10 = Length 48
- * 11 = Length 48 Check busy after response
- */
- /* Delay added before checking the status of frq change
- * retry not supported by mmc.c(core file)
- */
- if (cmd->cmdidx == SD_CMD_APP_SEND_SCR)
- udelay(50000); /* wait 50 ms */
-
- if (!(cmd->resp_type & MMC_RSP_PRESENT))
- flags = 0;
- else if (cmd->resp_type & MMC_RSP_136)
- flags = RSP_TYPE_LGHT136 | CICE_NOCHECK;
- else if (cmd->resp_type & MMC_RSP_BUSY)
- flags = RSP_TYPE_LGHT48B;
- else
- flags = RSP_TYPE_LGHT48;
-
- /* enable default flags */
- flags = flags | (CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK |
- MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE);
-
- if (cmd->resp_type & MMC_RSP_CRC)
- flags |= CCCE_CHECK;
- if (cmd->resp_type & MMC_RSP_OPCODE)
- flags |= CICE_CHECK;
-
- if (data) {
- if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) ||
- (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) {
- flags |= (MSBS_MULTIBLK | BCE_ENABLE);
- data->blocksize = 512;
- writel(data->blocksize | (data->blocks << 16),
- &mmc_base->blk);
- } else
- writel(data->blocksize | NBLK_STPCNT, &mmc_base->blk);
-
- if (data->flags & MMC_DATA_READ)
- flags |= (DP_DATA | DDIR_READ);
- else
- flags |= (DP_DATA | DDIR_WRITE);
- }
-
- writel(cmd->cmdarg, &mmc_base->arg);
- writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);
-
- start = get_time_ns();
- do {
- mmc_stat = readl(&mmc_base->stat);
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timeout: No status update\n");
- return -ETIMEDOUT;
- }
- } while (!mmc_stat);
-
- if ((mmc_stat & IE_CTO) != 0)
- return -ETIMEDOUT;
- else if ((mmc_stat & ERRI_MASK) != 0)
- return -1;
-
- if (mmc_stat & CC_MASK) {
- writel(CC_MASK, &mmc_base->stat);
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (cmd->resp_type & MMC_RSP_136) {
- /* response type 2 */
- cmd->response[3] = readl(&mmc_base->rsp10);
- cmd->response[2] = readl(&mmc_base->rsp32);
- cmd->response[1] = readl(&mmc_base->rsp54);
- cmd->response[0] = readl(&mmc_base->rsp76);
- } else
- /* response types 1, 1b, 3, 4, 5, 6 */
- cmd->response[0] = readl(&mmc_base->rsp10);
- }
- }
-
- if (!data)
- return 0;
-
- if (data->flags & MMC_DATA_READ)
- return mmc_read_data(hsmmc, data->dest,
- data->blocksize * data->blocks);
-
- if (IS_ENABLED(CONFIG_MCI_WRITE))
- return mmc_write_data(hsmmc, data->src,
- data->blocksize * data->blocks);
-
- return -ENOSYS;
+ return omap_hsmmc_send_cmd(hsmmc, cmd, data);
}
static void mmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct omap_hsmmc *hsmmc = to_hsmmc(mci);
- struct hsmmc *mmc_base = hsmmc->base;
- unsigned int dsor = 0;
- uint64_t start;
- /* configue bus width */
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_8:
- writel(readl(&mmc_base->con) | DTW_8_BITMODE,
- &mmc_base->con);
- break;
-
- case MMC_BUS_WIDTH_4:
- writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
- &mmc_base->con);
- writel(readl(&mmc_base->hctl) | DTW_4_BITMODE,
- &mmc_base->hctl);
- break;
-
- case MMC_BUS_WIDTH_1:
- writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
- &mmc_base->con);
- writel(readl(&mmc_base->hctl) & ~DTW_4_BITMODE,
- &mmc_base->hctl);
- break;
- default:
- return;
- }
-
- /* configure clock with 96Mhz system clock.
- */
- if (ios->clock != 0) {
- dsor = (MMC_CLOCK_REFERENCE * 1000000 / ios->clock);
- if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > ios->clock)
- dsor++;
- }
-
- mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
- (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
-
- mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
- (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
-
- start = get_time_ns();
- while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
- if (is_timeout(start, SECOND)) {
- dev_dbg(hsmmc->dev, "timedout waiting for ics!\n");
- return;
- }
- }
- writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
+ return omap_hsmmc_set_ios(hsmmc, ios);
}
static const struct mci_ops omap_mmc_ops = {
diff --git a/drivers/mci/omap_hsmmc.h b/drivers/mci/omap_hsmmc.h
new file mode 100644
index 000000000000..2de3f8f31d8c
--- /dev/null
+++ b/drivers/mci/omap_hsmmc.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <mci.h>
+
+struct omap_hsmmc {
+ struct mci_host mci;
+ struct device *dev;
+ struct hsmmc *base;
+ void __iomem *iobase;
+};
+
+int omap_hsmmc_init(struct omap_hsmmc *hsmmc);
+
+int omap_hsmmc_send_cmd(struct omap_hsmmc *hsmmc, struct mci_cmd *cmd,
+ struct mci_data *data);
+
+void omap_hsmmc_set_ios(struct omap_hsmmc *hsmmc, struct mci_ios *ios);
diff --git a/drivers/mci/omap_hsmmc_common.c b/drivers/mci/omap_hsmmc_common.c
new file mode 100644
index 000000000000..04c712460622
--- /dev/null
+++ b/drivers/mci/omap_hsmmc_common.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2008 Texas Instruments (http://www.ti.com/, Sukumar Ghorai <s-ghorai@ti.com>)
+
+/* #define DEBUG */
+#include <config.h>
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <mci.h>
+#include <clock.h>
+#include <errno.h>
+#include <io.h>
+#include <linux/err.h>
+
+#include "omap_hsmmc.h"
+
+struct hsmmc {
+ unsigned char res1[0x10];
+ unsigned int sysconfig; /* 0x10 */
+ unsigned int sysstatus; /* 0x14 */
+ unsigned char res2[0x14];
+ unsigned int con; /* 0x2C */
+ unsigned char res3[0xD4];
+ unsigned int blk; /* 0x104 */
+ unsigned int arg; /* 0x108 */
+ unsigned int cmd; /* 0x10C */
+ unsigned int rsp10; /* 0x110 */
+ unsigned int rsp32; /* 0x114 */
+ unsigned int rsp54; /* 0x118 */
+ unsigned int rsp76; /* 0x11C */
+ unsigned int data; /* 0x120 */
+ unsigned int pstate; /* 0x124 */
+ unsigned int hctl; /* 0x128 */
+ unsigned int sysctl; /* 0x12C */
+ unsigned int stat; /* 0x130 */
+ unsigned int ie; /* 0x134 */
+ unsigned char res4[0x8];
+ unsigned int capa; /* 0x140 */
+};
+
+/*
+ * OMAP HS MMC Bit definitions
+ */
+#define MMC_SOFTRESET (0x1 << 1)
+#define RESETDONE (0x1 << 0)
+#define NOOPENDRAIN (0x0 << 0)
+#define OPENDRAIN (0x1 << 0)
+#define OD (0x1 << 0)
+#define INIT_NOINIT (0x0 << 1)
+#define INIT_INITSTREAM (0x1 << 1)
+#define HR_NOHOSTRESP (0x0 << 2)
+#define STR_BLOCK (0x0 << 3)
+#define MODE_FUNC (0x0 << 4)
+#define DW8_1_4BITMODE (0x0 << 5)
+#define MIT_CTO (0x0 << 6)
+#define CDP_ACTIVEHIGH (0x0 << 7)
+#define WPP_ACTIVEHIGH (0x0 << 8)
+#define RESERVED_MASK (0x3 << 9)
+#define CTPL_MMC_SD (0x0 << 11)
+#define BLEN_512BYTESLEN (0x200 << 0)
+#define NBLK_STPCNT (0x0 << 16)
+#define DE_DISABLE (0x0 << 0)
+#define BCE_DISABLE (0x0 << 1)
+#define BCE_ENABLE (0x1 << 1)
+#define ACEN_DISABLE (0x0 << 2)
+#define DDIR_OFFSET (4)
+#define DDIR_MASK (0x1 << 4)
+#define DDIR_WRITE (0x0 << 4)
+#define DDIR_READ (0x1 << 4)
+#define MSBS_SGLEBLK (0x0 << 5)
+#define MSBS_MULTIBLK (0x1 << 5)
+#define RSP_TYPE_OFFSET (16)
+#define RSP_TYPE_MASK (0x3 << 16)
+#define RSP_TYPE_NORSP (0x0 << 16)
+#define RSP_TYPE_LGHT136 (0x1 << 16)
+#define RSP_TYPE_LGHT48 (0x2 << 16)
+#define RSP_TYPE_LGHT48B (0x3 << 16)
+#define CCCE_NOCHECK (0x0 << 19)
+#define CCCE_CHECK (0x1 << 19)
+#define CICE_NOCHECK (0x0 << 20)
+#define CICE_CHECK (0x1 << 20)
+#define DP_OFFSET (21)
+#define DP_MASK (0x1 << 21)
+#define DP_NO_DATA (0x0 << 21)
+#define DP_DATA (0x1 << 21)
+#define CMD_TYPE_NORMAL (0x0 << 22)
+#define INDEX_OFFSET (24)
+#define INDEX_MASK (0x3f << 24)
+#define INDEX(i) (i << 24)
+#define DATI_MASK (0x1 << 1)
+#define DATI_CMDDIS (0x1 << 1)
+#define DTW_1_BITMODE (0x0 << 1)
+#define DTW_4_BITMODE (0x1 << 1)
+#define DTW_8_BITMODE (0x1 << 5) /* CON[DW8]*/
+#define SDBP_PWROFF (0x0 << 8)
+#define SDBP_PWRON (0x1 << 8)
+#define SDVS_1V8 (0x5 << 9)
+#define SDVS_3V0 (0x6 << 9)
+#define ICE_MASK (0x1 << 0)
+#define ICE_STOP (0x0 << 0)
+#define ICS_MASK (0x1 << 1)
+#define ICS_NOTREADY (0x0 << 1)
+#define ICE_OSCILLATE (0x1 << 0)
+#define CEN_MASK (0x1 << 2)
+#define CEN_DISABLE (0x0 << 2)
+#define CEN_ENABLE (0x1 << 2)
+#define CLKD_OFFSET (6)
+#define CLKD_MASK (0x3FF << 6)
+#define DTO_MASK (0xF << 16)
+#define DTO_15THDTO (0xE << 16)
+#define SOFTRESETALL (0x1 << 24)
+#define CC_MASK (0x1 << 0)
+#define TC_MASK (0x1 << 1)
+#define BWR_MASK (0x1 << 4)
+#define BRR_MASK (0x1 << 5)
+#define ERRI_MASK (0x1 << 15)
+#define IE_CC (0x01 << 0)
+#define IE_TC (0x01 << 1)
+#define IE_BWR (0x01 << 4)
+#define IE_BRR (0x01 << 5)
+#define IE_CTO (0x01 << 16)
+#define IE_CCRC (0x01 << 17)
+#define IE_CEB (0x01 << 18)
+#define IE_CIE (0x01 << 19)
+#define IE_DTO (0x01 << 20)
+#define IE_DCRC (0x01 << 21)
+#define IE_DEB (0x01 << 22)
+#define IE_CERR (0x01 << 28)
+#define IE_BADA (0x01 << 29)
+
+#define VS30_3V0SUP (1 << 25)
+#define VS18_1V8SUP (1 << 26)
+
+/* Driver definitions */
+#define MMCSD_SECTOR_SIZE 512
+#define MMC_CARD 0
+#define SD_CARD 1
+#define BYTE_MODE 0
+#define SECTOR_MODE 1
+#define CLK_INITSEQ 0
+#define CLK_400KHZ 1
+#define CLK_MISC 2
+
+#define RSP_TYPE_NONE (RSP_TYPE_NORSP | CCCE_NOCHECK | CICE_NOCHECK)
+#define MMC_CMD0 (INDEX(0) | RSP_TYPE_NONE | DP_NO_DATA | DDIR_WRITE)
+
+/* Clock Configurations and Macros */
+#define MMC_CLOCK_REFERENCE 96 /* MHz */
+
+#define mmc_reg_out(addr, mask, val)\
+ writel((readl(addr) & (~(mask))) | ((val) & (mask)), (addr))
+
+#define to_hsmmc(mci) container_of(mci, struct omap_hsmmc, mci)
+
+static int mmc_init_stream(struct omap_hsmmc *hsmmc)
+{
+ uint64_t start;
+ struct hsmmc *mmc_base = hsmmc->base;
+
+ writel(readl(&mmc_base->con) | INIT_INITSTREAM, &mmc_base->con);
+
+ writel(MMC_CMD0, &mmc_base->cmd);
+ start = get_time_ns();
+ while (!(readl(&mmc_base->stat) & CC_MASK)) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for cc!\n");
+ return -ETIMEDOUT;
+ }
+ }
+ writel(CC_MASK, &mmc_base->stat);
+ writel(MMC_CMD0, &mmc_base->cmd);
+
+ start = get_time_ns();
+ while (!(readl(&mmc_base->stat) & CC_MASK)) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for cc2!\n");
+ return -ETIMEDOUT;
+ }
+ }
+ writel(readl(&mmc_base->con) & ~INIT_INITSTREAM, &mmc_base->con);
+
+ return 0;
+}
+
+int omap_hsmmc_init(struct omap_hsmmc *hsmmc)
+{
+ struct hsmmc *mmc_base = hsmmc->base;
+ unsigned int reg_val;
+ unsigned int dsor;
+ uint64_t start;
+
+ writel(readl(&mmc_base->sysconfig) | MMC_SOFTRESET,
+ &mmc_base->sysconfig);
+
+ start = get_time_ns();
+ while ((readl(&mmc_base->sysstatus) & RESETDONE) == 0) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timeout waiting for reset done\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ writel(readl(&mmc_base->sysctl) | SOFTRESETALL, &mmc_base->sysctl);
+
+ start = get_time_ns();
+ while ((readl(&mmc_base->sysctl) & SOFTRESETALL) != 0x0) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for softresetall!\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
+ writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
+ &mmc_base->capa);
+
+ reg_val = readl(&mmc_base->con) & RESERVED_MASK;
+
+ writel(CTPL_MMC_SD | reg_val | WPP_ACTIVEHIGH | CDP_ACTIVEHIGH |
+ MIT_CTO | DW8_1_4BITMODE | MODE_FUNC | STR_BLOCK |
+ HR_NOHOSTRESP | INIT_NOINIT | NOOPENDRAIN, &mmc_base->con);
+
+ dsor = 240;
+ mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
+ (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
+ mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
+ (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
+
+ start = get_time_ns();
+ while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for ics!\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
+
+ writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
+
+ writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
+ IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
+ &mmc_base->ie);
+
+ return mmc_init_stream(hsmmc);
+}
+
+static int mmc_read_data(struct omap_hsmmc *hsmmc, char *buf, unsigned int size)
+{
+ struct hsmmc *mmc_base = hsmmc->base;
+ unsigned int *output_buf = (unsigned int *)buf;
+ unsigned int mmc_stat;
+ unsigned int count;
+
+ /*
+ * Start Polled Read
+ */
+ count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
+ count /= 4;
+
+ while (size) {
+ uint64_t start = get_time_ns();
+ do {
+ mmc_stat = readl(&mmc_base->stat);
+ if (is_timeout(start, SECOND)) {
+ dev_err(hsmmc->dev, "timedout waiting for status!\n");
+ return -ETIMEDOUT;
+ }
+ } while (mmc_stat == 0);
+
+ if ((mmc_stat & ERRI_MASK) != 0) {
+ dev_err(hsmmc->dev, "Error while reading data. status: 0x%08x\n",
+ mmc_stat);
+ return -EIO;
+ }
+
+ if (mmc_stat & BRR_MASK) {
+ unsigned int k;
+
+ writel(readl(&mmc_base->stat) | BRR_MASK,
+ &mmc_base->stat);
+ for (k = 0; k < count; k++) {
+ *output_buf = readl(&mmc_base->data);
+ output_buf++;
+ }
+ size -= (count*4);
+ }
+
+ if (mmc_stat & BWR_MASK)
+ writel(readl(&mmc_base->stat) | BWR_MASK,
+ &mmc_base->stat);
+
+ if (mmc_stat & TC_MASK) {
+ writel(readl(&mmc_base->stat) | TC_MASK,
+ &mmc_base->stat);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mmc_write_data(struct omap_hsmmc *hsmmc, const char *buf, unsigned int size)
+{
+ struct hsmmc *mmc_base = hsmmc->base;
+ unsigned int *input_buf = (unsigned int *)buf;
+ unsigned int mmc_stat;
+ unsigned int count;
+
+ /*
+ * Start Polled Read
+ */
+ count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size;
+ count /= 4;
+
+ while (size) {
+ uint64_t start = get_time_ns();
+ do {
+ mmc_stat = readl(&mmc_base->stat);
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for status!\n");
+ return -ETIMEDOUT;
+ }
+ } while (mmc_stat == 0);
+
+ if ((mmc_stat & ERRI_MASK) != 0) {
+ dev_err(hsmmc->dev, "Error while reading data. status: 0x%08x\n",
+ mmc_stat);
+ return -EIO;
+ }
+
+ if (mmc_stat & BWR_MASK) {
+ unsigned int k;
+
+ writel(readl(&mmc_base->stat) | BWR_MASK,
+ &mmc_base->stat);
+ for (k = 0; k < count; k++) {
+ writel(*input_buf, &mmc_base->data);
+ input_buf++;
+ }
+ size -= (count * 4);
+ }
+
+ if (mmc_stat & BRR_MASK)
+ writel(readl(&mmc_base->stat) | BRR_MASK,
+ &mmc_base->stat);
+
+ if (mmc_stat & TC_MASK) {
+ writel(readl(&mmc_base->stat) | TC_MASK,
+ &mmc_base->stat);
+ break;
+ }
+ }
+ return 0;
+}
+
+int omap_hsmmc_send_cmd(struct omap_hsmmc *hsmmc, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct hsmmc *mmc_base = hsmmc->base;
+ unsigned int flags, mmc_stat;
+ uint64_t start;
+
+ start = get_time_ns();
+ while ((readl(&mmc_base->pstate) & DATI_MASK) == DATI_CMDDIS) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for cmddis!\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ writel(0xFFFFFFFF, &mmc_base->stat);
+ start = get_time_ns();
+ while (readl(&mmc_base->stat)) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for stat!\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ /*
+ * CMDREG
+ * CMDIDX[13:8] : Command index
+ * DATAPRNT[5] : Data Present Select
+ * ENCMDIDX[4] : Command Index Check Enable
+ * ENCMDCRC[3] : Command CRC Check Enable
+ * RSPTYP[1:0]
+ * 00 = No Response
+ * 01 = Length 136
+ * 10 = Length 48
+ * 11 = Length 48 Check busy after response
+ */
+ /* Delay added before checking the status of frq change
+ * retry not supported by mmc.c(core file)
+ */
+ if (cmd->cmdidx == SD_CMD_APP_SEND_SCR)
+ udelay(50000); /* wait 50 ms */
+
+ if (!(cmd->resp_type & MMC_RSP_PRESENT))
+ flags = 0;
+ else if (cmd->resp_type & MMC_RSP_136)
+ flags = RSP_TYPE_LGHT136 | CICE_NOCHECK;
+ else if (cmd->resp_type & MMC_RSP_BUSY)
+ flags = RSP_TYPE_LGHT48B;
+ else
+ flags = RSP_TYPE_LGHT48;
+
+ /* enable default flags */
+ flags = flags | (CMD_TYPE_NORMAL | CICE_NOCHECK | CCCE_NOCHECK |
+ MSBS_SGLEBLK | ACEN_DISABLE | BCE_DISABLE | DE_DISABLE);
+
+ if (cmd->resp_type & MMC_RSP_CRC)
+ flags |= CCCE_CHECK;
+ if (cmd->resp_type & MMC_RSP_OPCODE)
+ flags |= CICE_CHECK;
+
+ if (data) {
+ if ((cmd->cmdidx == MMC_CMD_READ_MULTIPLE_BLOCK) ||
+ (cmd->cmdidx == MMC_CMD_WRITE_MULTIPLE_BLOCK)) {
+ flags |= (MSBS_MULTIBLK | BCE_ENABLE);
+ data->blocksize = 512;
+ writel(data->blocksize | (data->blocks << 16),
+ &mmc_base->blk);
+ } else
+ writel(data->blocksize | NBLK_STPCNT, &mmc_base->blk);
+
+ if (data->flags & MMC_DATA_READ)
+ flags |= (DP_DATA | DDIR_READ);
+ else
+ flags |= (DP_DATA | DDIR_WRITE);
+ }
+
+ writel(cmd->cmdarg, &mmc_base->arg);
+ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd);
+
+ start = get_time_ns();
+ do {
+ mmc_stat = readl(&mmc_base->stat);
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timeout: No status update\n");
+ return -ETIMEDOUT;
+ }
+ } while (!mmc_stat);
+
+ if ((mmc_stat & IE_CTO) != 0)
+ return -ETIMEDOUT;
+ else if ((mmc_stat & ERRI_MASK) != 0)
+ return -1;
+
+ if (mmc_stat & CC_MASK) {
+ writel(CC_MASK, &mmc_base->stat);
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->response[3] = readl(&mmc_base->rsp10);
+ cmd->response[2] = readl(&mmc_base->rsp32);
+ cmd->response[1] = readl(&mmc_base->rsp54);
+ cmd->response[0] = readl(&mmc_base->rsp76);
+ } else
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->response[0] = readl(&mmc_base->rsp10);
+ }
+ }
+
+ if (!data)
+ return 0;
+
+ if (data->flags & MMC_DATA_READ)
+ return mmc_read_data(hsmmc, data->dest,
+ data->blocksize * data->blocks);
+
+ if (IS_ENABLED(CONFIG_MCI_WRITE))
+ return mmc_write_data(hsmmc, data->src,
+ data->blocksize * data->blocks);
+
+ return -ENOSYS;
+}
+
+void omap_hsmmc_set_ios(struct omap_hsmmc *hsmmc, struct mci_ios *ios)
+{
+ struct hsmmc *mmc_base = hsmmc->base;
+ unsigned int dsor = 0;
+ uint64_t start;
+
+ /* configue bus width */
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ writel(readl(&mmc_base->con) | DTW_8_BITMODE,
+ &mmc_base->con);
+ break;
+
+ case MMC_BUS_WIDTH_4:
+ writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
+ &mmc_base->con);
+ writel(readl(&mmc_base->hctl) | DTW_4_BITMODE,
+ &mmc_base->hctl);
+ break;
+
+ case MMC_BUS_WIDTH_1:
+ writel(readl(&mmc_base->con) & ~DTW_8_BITMODE,
+ &mmc_base->con);
+ writel(readl(&mmc_base->hctl) & ~DTW_4_BITMODE,
+ &mmc_base->hctl);
+ break;
+ default:
+ return;
+ }
+
+ /* configure clock with 96Mhz system clock.
+ */
+ if (ios->clock != 0) {
+ dsor = (MMC_CLOCK_REFERENCE * 1000000 / ios->clock);
+ if ((MMC_CLOCK_REFERENCE * 1000000) / dsor > ios->clock)
+ dsor++;
+ }
+
+ mmc_reg_out(&mmc_base->sysctl, (ICE_MASK | DTO_MASK | CEN_MASK),
+ (ICE_STOP | DTO_15THDTO | CEN_DISABLE));
+
+ mmc_reg_out(&mmc_base->sysctl, ICE_MASK | CLKD_MASK,
+ (dsor << CLKD_OFFSET) | ICE_OSCILLATE);
+
+ start = get_time_ns();
+ while ((readl(&mmc_base->sysctl) & ICS_MASK) == ICS_NOTREADY) {
+ if (is_timeout(start, SECOND)) {
+ dev_dbg(hsmmc->dev, "timedout waiting for ics!\n");
+ return;
+ }
+ }
+ writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
+}
--
2.39.5
next prev parent reply other threads:[~2025-04-22 5:27 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-22 5:26 [PATCH 0/9] ARM: OMAP: beaglebone: add PBL SD xload support Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 1/9] clocksource: make available in PBL Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 2/9] clocksource: ti-dm: " Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 3/9] mci: move mci_setup_cmd definition into header Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 4/9] mci: add common PBL helper for chainloading after BootROM initialization Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 5/9] mci: pbl: add autodetection of BootROM-initialized standard capacity cards Ahmad Fatoum
2025-04-22 5:26 ` Ahmad Fatoum [this message]
2025-04-22 5:26 ` [PATCH 7/9] ARM: OMAP: add am33xx_hsmmc_start_image for PBL Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 8/9] mci: omap_hsmmc: add xload implementation " Ahmad Fatoum
2025-04-22 5:26 ` [PATCH 9/9] ARM: OMAP: beaglebone: add PBL SD xload support Ahmad Fatoum
2025-04-22 10:55 ` [PATCH 0/9] " 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=20250422052635.3423961-7-a.fatoum@pengutronix.de \
--to=a.fatoum@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