All of lore.kernel.org
 help / color / mirror / Atom feed
From: Tim Harvey <tharvey@gateworks.com>
To: u-boot@lists.denx.de
Subject: [U-Boot]  [RFC 13/22] spi: add thunderx SPI driver
Date: Fri, 22 Feb 2019 10:03:10 -0800	[thread overview]
Message-ID: <20190222180319.32221-14-tharvey@gateworks.com> (raw)
In-Reply-To: <20190222180319.32221-1-tharvey@gateworks.com>

Signed-off-by: Tim Harvey <tharvey@gateworks.com>
---
 configs/thunderx_81xx_defconfig |   4 +
 drivers/spi/Kconfig             |   6 +
 drivers/spi/Makefile            |   1 +
 drivers/spi/thunderx_spi.c      | 448 ++++++++++++++++++++++++++++++++
 4 files changed, 459 insertions(+)
 create mode 100755 drivers/spi/thunderx_spi.c

diff --git a/configs/thunderx_81xx_defconfig b/configs/thunderx_81xx_defconfig
index e43aa9750d..48f57ecf1b 100644
--- a/configs/thunderx_81xx_defconfig
+++ b/configs/thunderx_81xx_defconfig
@@ -24,6 +24,7 @@ CONFIG_SYS_PROMPT="ThunderX_81XX> "
 CONFIG_CMD_GPIO=y
 CONFIG_CMD_I2C=y
 CONFIG_CMD_PCI=y
+CONFIG_CMD_SPI=y
 # CONFIG_CMD_NET is not set
 CONFIG_DEFAULT_DEVICE_TREE="thunderx-81xx"
 CONFIG_DM=y
@@ -39,3 +40,6 @@ CONFIG_PCI_THUNDERX=y
 CONFIG_DM_SERIAL=y
 CONFIG_DEBUG_UART_PL011=y
 CONFIG_DEBUG_UART_SKIP_INIT=y
+CONFIG_SPI=y
+CONFIG_DM_SPI=y
+CONFIG_THUNDERX_SPI=y
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 516188ea88..d1d5463909 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -233,6 +233,12 @@ config TEGRA210_QSPI
 	  be used to access SPI chips on platforms embedding this
 	  NVIDIA Tegra210 IP core.
 
+config THUNDERX_SPI
+	bool "Cavium ThunderX SPI driver"
+	help
+	  Enable the Cavium ThunderX SPI driver. This driver can be used to
+	  access the SPI NOR flash on ThunderX SoC platforms.
+
 config XILINX_SPI
 	bool "Xilinx SPI driver"
 	help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7242ea7e40..ea99775094 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
 obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
 obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
 obj-$(CONFIG_TEGRA210_QSPI) += tegra210_qspi.o
+obj-$(CONFIG_THUNDERX_SPI) += thunderx_spi.o
 obj-$(CONFIG_TI_QSPI) += ti_qspi.o
 obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
 obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
diff --git a/drivers/spi/thunderx_spi.c b/drivers/spi/thunderx_spi.c
new file mode 100755
index 0000000000..e165215524
--- /dev/null
+++ b/drivers/spi/thunderx_spi.c
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier:	GPL-2.0+
+/*
+ * Copyright (C) 2018, Cavium Inc.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <malloc.h>
+#include <spi.h>
+#include <watchdog.h>
+
+#include <asm/io.h>
+#include <asm/arch-thunderx/thunderx.h>
+#include <asm/unaligned.h>
+
+#define THUNDERX_SPI_MAX_BYTES		9
+#define THUNDERX_SPI_MAX_CLOCK_HZ	50000000
+
+#define THUNDERX_SPI_NUM_CS		4
+
+#define THUNDERX_SPI_CS_VALID(cs)	((cs) < THUNDERX_SPI_NUM_CS)
+
+#define MPI_CFG				0x1000
+#define MPI_STS				0x1008
+#define MPI_TX				0x1010
+#define MPI_WIDE_DAT			0x1040
+#define MPI_DAT(X)			(0x1080 + ((X) << 3))
+
+union mpi_cfg {
+	uint64_t u;
+	struct mpi_cfg_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+		uint64_t		:35;
+
+		uint64_t clkdiv		:13;	/** clock divisor */
+		uint64_t csena3		:1;	/** cs enable 3. */
+		uint64_t csena2		:1;	/** cs enable 2 */
+		uint64_t csena1		:1;	/** cs enable 1 */
+		uint64_t csena0		:1;	/** cs enable 0 */
+		/**
+		 * 0 = SPI_CSn asserts 1/2 coprocessor-clock cycle before
+		 *     transaction
+		 * 1 = SPI_CSn asserts coincident with transaction
+		 */
+		uint64_t cslate		:1;
+		/**
+		 * Tristate TX.  Set to 1 to tristate SPI_DO when not
+		 * transmitting.
+		 */
+		uint64_t tritx		:1;
+		/**
+		 * When set, guarantees idle coprocessor-clock cycles between
+		 * commands.
+		 */
+		uint64_t idleclks	:2;
+		/**
+		 * SPI_CSn_L high.  1 = SPI_CSn_L is asserted high,
+		 * 0 = SPI_CS_n asserted low.
+		 */
+		uint64_t cshi		:1;
+		uint64_t 		:2;	/** Reserved */
+		/** 0 = shift MSB first, 1 = shift LSB first */
+		uint64_t lsbfirst	:1;
+		/**
+		 * Wire-or DO and DI.
+		 * 0 = SPI_DO and SPI_DI are separate wires (SPI).  SPI_DO pin
+		 *     is always driven.
+		 * 1 = SPI_DO/DI is all from SPI_DO pin (MPI).  SPI_DO pin is
+		 *     tristated when not transmitting.  If WIREOR = 1, SPI_DI
+		 *     pin is not used by the MPI/SPI engine.
+		 */
+		uint64_t wireor		:1;
+		/**
+		 * Clock control.
+		 * 0 = Clock idles to value given by IDLELO after completion of
+		 *     MPI/SPI transaction.
+		 * 1 = Clock never idles, requires SPI_CSn_L
+		 *     deassertion/assertion between commands.
+		 */
+		uint64_t clk_cont	:1;
+		/**
+		 * Clock idle low/clock invert
+		 * 0 = SPI_CLK idles high, first transition is high-to-low.
+		 *     This correspondes to SPI Block Guide options CPOL = 1,
+		 *     CPHA = 0.
+		 * 1 = SPI_CLK idles low, first transition is low-to-high.  This
+		 *     corresponds to SPI Block Guide options CPOL = 0, CPHA = 0.
+		 */
+		uint64_t idlelo		:1;
+		/** MPI/SPI enable, 0 = pins are tristated, 1 = pins driven */
+		uint64_t enable		:1;
+#else /* Word 0 - Little Endian */
+		uint64_t enable		:1;
+		uint64_t idlelo		:1;
+		uint64_t clk_cont	:1;
+		uint64_t wireor		:1;
+		uint64_t lsbfirst	:1;
+		uint64_t		:2;
+		uint64_t cshi		:1;
+		uint64_t idleclks	:2;
+		uint64_t tritx		:1;
+		uint64_t cslate		:1;
+		uint64_t csena0		:1;
+		uint64_t csena1		:1;
+		uint64_t csena2		:1;
+		uint64_t csena3		:1;
+		uint64_t clkdiv		:13;
+		uint64_t 		:35;	/** Reserved */
+#endif /* Word 0 - End */
+	} s;
+	/* struct mpi_cfg_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_dat#
+ *
+ * MPI/SPI Data Registers
+ */
+union mpi_dat {
+	uint64_t u;
+	struct mpi_datx_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+		uint64_t reserved_8_63	:56;
+		/**< [  7:  0](R/W/H) Data to transmit/receive. */
+		uint64_t data		:8;
+#else /* Word 0 - Little Endian */
+		uint64_t data		:8;
+		uint64_t reserved_8_63	:56;
+#endif /* Word 0 - End */
+	} s;
+	/* struct mpi_datx_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_sts
+ *
+ * MPI/SPI STS Register
+ */
+union mpi_sts {
+	uint64_t u;
+	struct mpi_sts_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+		uint64_t reserved_13_63	:51;
+		uint64_t rxnum		:5;	/** Number of bytes */
+		uint64_t reserved_2_7	:6;
+		uint64_t mpi_intr	:1;	/** Transaction done int */
+		uint64_t busy		:1;	/** SPI engine busy */
+#else /* Word 0 - Little Endian */
+		uint64_t busy		:1;
+		uint64_t mpi_intr	:1;
+		uint64_t reserved_2_7	:6;
+		uint64_t rxnum		:5;
+		uint64_t reserved_13_63	:51;
+#endif /* Word 0 - End */
+	} s;
+	/* struct mpi_sts_s cn; */
+};
+
+/**
+ * Register (NCB) mpi_tx
+ *
+ * MPI/SPI Transmit Register
+ */
+union mpi_tx {
+	uint64_t u;
+	struct mpi_tx_s {
+#if __BYTE_ORDER == __BIG_ENDIAN /* Word 0 - Big Endian */
+		uint64_t		:42;	/* Reserved */
+		uint64_t csid 		:2;	/** Which CS to assert */
+		uint64_t		:3;	/* Reserved */
+		uint64_t leavecs	:1;	/** Leave CSn asserted */
+		uint64_t 		:3;	/* Reserved */
+		uint64_t txnum		:5;	/** Number of words to tx */
+		uint64_t		:3;	/* Reserved */
+		uint64_t totnum		:5;	/** Total bytes to shift */
+#else /* Word 0 - Little Endian */
+		uint64_t totnum		:5;
+		uint64_t 		:3;
+		uint64_t txnum		:5;
+		uint64_t		:3;
+		uint64_t leavecs	:1;
+		uint64_t		:3;
+		uint64_t csid		:2;
+		uint64_t		:42;
+#endif /* Word 0 - End */
+	} s;
+	/* struct mpi_tx_s cn; */
+};
+
+/** Local driver data structure */
+struct thunderx_spi {
+	void *baseaddr;		/** Register base address */
+	u32 clkdiv;		/** Clock divisor for device speed */
+};
+
+void *thunderx_spi_get_baseaddr(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct thunderx_spi *priv = dev_get_priv(bus);
+
+	return priv->baseaddr;
+}
+
+static union mpi_cfg thunderx_spi_set_mpicfg(struct udevice *dev)
+{
+	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
+	struct udevice *bus = dev_get_parent(dev);
+	struct thunderx_spi *priv = dev_get_priv(bus);
+	union mpi_cfg mpi_cfg;
+	uint max_speed = slave->max_hz;
+	bool cpha, cpol;
+
+	if (!max_speed)
+		max_speed = 12500000;
+	if (max_speed > THUNDERX_SPI_MAX_CLOCK_HZ)
+		max_speed = THUNDERX_SPI_MAX_CLOCK_HZ;
+
+	dev_dbg(dev, "%s: CS%d Hz=%d Mode=%x\n", __func__,
+		slave->cs, slave->max_hz, slave->mode);
+	cpha = !!(slave->mode & SPI_CPHA);
+	cpol = !!(slave->mode & SPI_CPOL);
+
+	mpi_cfg.u = 0;
+	mpi_cfg.s.clkdiv = priv->clkdiv & 0x1fff;
+	mpi_cfg.s.cshi = !!(slave->mode & SPI_CS_HIGH);
+	mpi_cfg.s.lsbfirst = !!(slave->mode & SPI_LSB_FIRST);
+	mpi_cfg.s.wireor = !!(slave->mode & SPI_3WIRE);
+	mpi_cfg.s.idlelo = cpha != cpol;
+	mpi_cfg.s.cslate = cpha;
+	mpi_cfg.s.enable = 1;
+	mpi_cfg.s.csena0 = 1;
+	mpi_cfg.s.csena1 = 1;
+	mpi_cfg.s.csena2 = 1;
+	mpi_cfg.s.csena3 = 1;
+	dev_dbg(dev, "%s: mpi_cfg=%llx\n", __func__, mpi_cfg.u);
+
+	return mpi_cfg;
+}
+
+/**
+ * Wait until the SPI bus is ready
+ *
+ * @param	dev	SPI device to wait for
+ */
+static void thunderx_spi_wait_ready(struct udevice *dev)
+{
+	void *baseaddr = thunderx_spi_get_baseaddr(dev);
+	union mpi_sts mpi_sts;
+
+	do {
+		mpi_sts.u = readq(baseaddr + MPI_STS);
+		WATCHDOG_RESET();
+	} while (mpi_sts.s.busy);
+}
+/**
+ * Claim the bus for a slave device
+ *
+ * @param	dev	SPI bus
+ *
+ * @return	0 for success, -EINVAL if chip select is invalid
+ */
+static int thunderx_spi_claim_bus(struct udevice *dev)
+{
+	void *baseaddr = thunderx_spi_get_baseaddr(dev);
+	union mpi_cfg mpi_cfg;
+
+	if (!THUNDERX_SPI_CS_VALID(spi_chip_select(dev)))
+		return -EINVAL;
+
+	mpi_cfg.u = readq(baseaddr + MPI_CFG);
+	mpi_cfg.s.tritx = 0;
+	mpi_cfg.s.enable = 1;
+	writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+
+	return 0;
+}
+
+/**
+ * Release the bus to a slave device
+ *
+ * @param	dev	SPI bus
+ *
+ * @return	0 for success, -EINVAL if chip select is invalid
+ */
+static int thunderx_spi_release_bus(struct udevice *dev)
+{
+	void *baseaddr = thunderx_spi_get_baseaddr(dev);
+	union mpi_cfg mpi_cfg;
+
+	if (!THUNDERX_SPI_CS_VALID(spi_chip_select(dev)))
+		return -EINVAL;
+
+	mpi_cfg.u = readq(baseaddr + MPI_CFG);
+	mpi_cfg.s.enable = 0;
+	writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+
+	return 0;
+}
+
+static int thunderx_spi_xfer(struct udevice *dev, unsigned int bitlen,
+			     const void *dout, void *din, unsigned long flags)
+{
+	void *baseaddr = thunderx_spi_get_baseaddr(dev);
+	union mpi_tx mpi_tx;
+	union mpi_cfg mpi_cfg;
+	uint64_t wide_dat = 0;
+	int len = bitlen / 8;
+	int i;
+	const uint8_t *tx_data = dout;
+	uint8_t *rx_data = din;
+	int cs = spi_chip_select(dev);
+
+	if (!THUNDERX_SPI_CS_VALID(cs))
+		return -EINVAL;
+
+	dev_dbg(dev, "%s bitlen=%u dout=%p din=%p flags=0x%lx CS%d\n", __func__,
+		bitlen, dout, din, flags, cs);
+
+	mpi_cfg = thunderx_spi_set_mpicfg(dev);
+
+	if (mpi_cfg.u != readq(baseaddr + MPI_CFG))
+		writeq(mpi_cfg.u, baseaddr + MPI_CFG);
+
+	/* Start by writing and reading 8 bytes at a time.  While we can support
+	 * up to 10, it's easier to just use 8 with the MPI_WIDE_DAT register.
+	 */
+	while (len > 8) {
+		if (tx_data) {
+			wide_dat = get_unaligned((uint64_t *)tx_data);
+			debug("  tx: %016llx\n", (unsigned long long)wide_dat);
+			tx_data += 8;
+			writeq(wide_dat, baseaddr + MPI_WIDE_DAT);
+		}
+		mpi_tx.u = 0;
+		mpi_tx.s.csid = cs;
+		mpi_tx.s.leavecs = 1;
+		mpi_tx.s.txnum = tx_data ? 8 : 0;
+		mpi_tx.s.totnum = 8;
+		writeq(mpi_tx.u, baseaddr + MPI_TX);
+
+		thunderx_spi_wait_ready(dev);
+
+		if (rx_data) {
+			wide_dat = readq(baseaddr + MPI_WIDE_DAT);
+			debug("  rx: %016llx\n", (unsigned long long)wide_dat);
+			*(uint64_t *)rx_data = wide_dat;
+			rx_data += 8;
+		}
+		len -= 8;
+	}
+
+	/* Write and read the rest of the data */
+	if (tx_data)
+		for (i = 0; i < len; i++) {
+			debug("  tx: %02x\n", *tx_data);
+			writeq(*tx_data++, baseaddr + MPI_DAT(i));
+		}
+
+	mpi_tx.u = 0;
+	mpi_tx.s.csid = cs;
+	mpi_tx.s.leavecs = !(flags & SPI_XFER_END);
+	mpi_tx.s.txnum = tx_data ? len : 0;
+	mpi_tx.s.totnum = len;
+
+	writeq(mpi_tx.u, baseaddr + MPI_TX);
+
+	thunderx_spi_wait_ready(dev);
+
+	if (rx_data) {
+		for (i = 0; i < len; i++) {
+			*rx_data = readq(baseaddr + MPI_DAT(i)) & 0xff;
+			debug("  rx: %02x\n", *rx_data);
+			rx_data++;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Set the speed of the SPI bus
+ *
+ * @param	bus	bus to set
+ * @param	max_hz	maximum speed supported
+ */
+static int thunderx_spi_set_speed(struct udevice *bus, uint max_hz)
+{
+	struct thunderx_spi *priv = dev_get_priv(bus);
+
+	dev_dbg(dev, "%s: max_hz=%u io=%llu", __func__, max_hz,
+		thunderx_get_io_clock());
+	if (max_hz > THUNDERX_SPI_MAX_CLOCK_HZ)
+		max_hz = THUNDERX_SPI_MAX_CLOCK_HZ;
+	priv->clkdiv = (thunderx_get_io_clock()) / (2 * max_hz);
+
+	return 0;
+}
+
+static int thunderx_spi_set_mode(struct udevice *bus, uint mode)
+{
+	/* We don't set it here */
+	return 0;
+}
+
+static int thunderx_pci_spi_probe(struct udevice *dev)
+{
+	struct thunderx_spi *priv = dev_get_priv(dev);
+	pci_dev_t bdf = dm_pci_get_bdf(dev);
+	size_t size;
+
+	dev->req_seq = PCI_FUNC(bdf);
+	priv->baseaddr = dm_pci_map_bar(dev, 0, &size, PCI_REGION_MEM);
+	dev_dbg(dev, "%s: SPI PCI device: bdf:%x base:%p\n", __func__,
+		bdf, priv->baseaddr);
+
+	return 0;
+}
+
+static const struct dm_spi_ops thunderx_spi_ops = {
+	.claim_bus	= thunderx_spi_claim_bus,
+	.release_bus	= thunderx_spi_release_bus,
+	.xfer		= thunderx_spi_xfer,
+	.set_speed	= thunderx_spi_set_speed,
+	.set_mode	= thunderx_spi_set_mode,
+};
+
+static const struct udevice_id thunderx_spi_ids[] = {
+	{ .compatible	= "cavium,thunder-8890-spi" },
+	{ .compatible	= "cavium,thunder-8190-spi" },
+	{ }
+};
+
+U_BOOT_DRIVER(thunderx_pci_spi) = {
+	.name			= "spi_thunderx",
+	.id			= UCLASS_SPI,
+	.of_match 		= thunderx_spi_ids,
+	.probe			= thunderx_pci_spi_probe,
+	.priv_auto_alloc_size 	= sizeof(struct thunderx_spi),
+	.ops			= &thunderx_spi_ops,
+};
+
+static struct pci_device_id thunderx_pci_spi_supported[] = {
+	{ PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_THUNDERX_SPI) },
+	{ },
+};
+
+U_BOOT_PCI_DEVICE(thunderx_pci_spi, thunderx_pci_spi_supported);
+
-- 
2.17.1

  parent reply	other threads:[~2019-02-22 18:03 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-22 18:02 [U-Boot] [RFC 00/22] Add support for Cavium Octeon-TX CN80XX/CN81XX Tim Harvey
2019-02-22 18:02 ` [U-Boot] [RFC 01/22] arm: introduce ARCH_THUNDERX Tim Harvey
2019-02-24 16:08   ` Alexander Graf
2019-02-24 16:13   ` Alexander Graf
2019-02-22 18:02 ` [U-Boot] [RFC 02/22] arm: add thunderx_81xx Tim Harvey
2019-02-24 16:35   ` Alexander Graf
2019-02-22 18:03 ` [U-Boot] [RFC 03/22] thunderx: add FDT support Tim Harvey
2019-02-24 16:39   ` Alexander Graf
2019-02-22 18:03 ` [U-Boot] [RFC 04/22] thunderx: add thunderx register definitions and misc functions Tim Harvey
2019-02-24 16:42   ` Alexander Graf
2019-02-22 18:03 ` [U-Boot] [RFC 05/22] thunderx: move DRAM prints to debug Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 06/22] dm: pci: add PCI SR-IOV EA support Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 07/22] fdt: add fdtdec_get_pci_bus_range Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 08/22] pci: add thunderx pci/ecam driver Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 09/22] pci: fix pce enumeration on thunderx Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 10/22] arm: include 64bit io accessors Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 11/22] gpio: add thunderx gpio driver Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 12/22] i2c: add thunderx I2C driver Tim Harvey
2019-02-22 18:03 ` Tim Harvey [this message]
2019-02-22 18:03 ` [U-Boot] [RFC 14/22] xhci: add support for cavium thunderx XHCI Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 15/22] thunderx_81xx: add support for XHCI Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 16/22] thunderx_81xx: enable usb mass storage and usb ethernet Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 17/22] ahci: support 64bit systems Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 18/22] ahci: set n_ports from host caps Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 19/22] ahci: add support for ThunderX AHCI Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 20/22] thunderx_81xx: add AHCI support Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 21/22] net: add thunderx vnic drivers Tim Harvey
2019-02-22 18:03 ` [U-Boot] [RFC 22/22] pci: auto probe thunderx NIC devices Tim Harvey

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=20190222180319.32221-14-tharvey@gateworks.com \
    --to=tharvey@gateworks.com \
    --cc=u-boot@lists.denx.de \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.