All of lore.kernel.org
 help / color / mirror / Atom feed
From: Crystal Guo <crystal.guo@mediatek.com>
To: Krzysztof Kozlowski <krzk@kernel.org>,
	Rob Herring <robh@kernel.org>, Conor Dooley <conor+dt@kernel.org>,
	Matthias Brugger <matthias.bgg@gmail.com>,
	AngeloGioacchino Del Regno
	<angelogioacchino.delregno@collabora.com>,
	Crystal Guo <crystal.guo@mediatek.com>
Cc: <linux-kernel@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-mediatek@lists.infradead.org>,
	<Project_Global_Chrome_Upstream_Group@mediatek.com>
Subject: [PATCH 1/2] memory/mediatek: Add an interface to get current DDR data rate
Date: Thu, 12 Dec 2024 16:59:47 +0800	[thread overview]
Message-ID: <20241212090029.13692-2-crystal.guo@mediatek.com> (raw)
In-Reply-To: <20241212090029.13692-1-crystal.guo@mediatek.com>

Add MediaTek DRAMC driver to provide an interface that can
obtain current DDR data rate.

Signed-off-by: Crystal Guo <crystal.guo@mediatek.com>
---
 drivers/memory/Kconfig                 |   1 +
 drivers/memory/Makefile                |   1 +
 drivers/memory/mediatek/Kconfig        |  21 ++
 drivers/memory/mediatek/Makefile       |   2 +
 drivers/memory/mediatek/mtk-dramc.c    | 325 +++++++++++++++++++++++++
 include/linux/soc/mediatek/mtk-dramc.h |  41 ++++
 6 files changed, 391 insertions(+)
 create mode 100644 drivers/memory/mediatek/Kconfig
 create mode 100644 drivers/memory/mediatek/Makefile
 create mode 100644 drivers/memory/mediatek/mtk-dramc.c
 create mode 100644 include/linux/soc/mediatek/mtk-dramc.h

diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index c82d8d8a16ea..b1698549ff81 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -227,5 +227,6 @@ config STM32_FMC2_EBI
 
 source "drivers/memory/samsung/Kconfig"
 source "drivers/memory/tegra/Kconfig"
+source "drivers/memory/mediatek/Kconfig"
 
 endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index d2e6ca9abbe0..cf1091449d2e 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_STM32_FMC2_EBI)	+= stm32-fmc2-ebi.o
 
 obj-$(CONFIG_SAMSUNG_MC)	+= samsung/
 obj-$(CONFIG_TEGRA_MC)		+= tegra/
+obj-$(CONFIG_HAVE_MTK_MC)	+= mediatek/
 obj-$(CONFIG_TI_EMIF_SRAM)	+= ti-emif-sram.o
 obj-$(CONFIG_FPGA_DFL_EMIF)	+= dfl-emif.o
 
diff --git a/drivers/memory/mediatek/Kconfig b/drivers/memory/mediatek/Kconfig
new file mode 100644
index 000000000000..00764cdb157e
--- /dev/null
+++ b/drivers/memory/mediatek/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config HAVE_MTK_MC
+	bool "MediaTek Memory Controller support"
+	help
+	  This option allows to enable MediaTek memory controller drivers,
+	  which may include controllers for DRAM or others.
+	  Select Y here if you need support for MediaTek memory controller.
+	  If you don't need, select N.
+
+if HAVE_MTK_MC
+
+config MTK_DRAMC
+	tristate "MediaTek DRAMC driver"
+	depends on HAVE_MTK_MC
+	help
+	  This option selects the MediaTek DRAMC driver, which provides
+	  an interface for reporting the current data rate of DRAM.
+	  Select Y here if you need support for the MediaTek DRAMC driver.
+	  If you don't need, select N.
+
+endif
diff --git a/drivers/memory/mediatek/Makefile b/drivers/memory/mediatek/Makefile
new file mode 100644
index 000000000000..a1395fc55b41
--- /dev/null
+++ b/drivers/memory/mediatek/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_MTK_DRAMC)		+= mtk-dramc.o
diff --git a/drivers/memory/mediatek/mtk-dramc.c b/drivers/memory/mediatek/mtk-dramc.c
new file mode 100644
index 000000000000..9c2c8e187a4a
--- /dev/null
+++ b/drivers/memory/mediatek/mtk-dramc.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2024 MediaTek Inc.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/printk.h>
+#include <linux/io.h>
+#include <linux/soc/mediatek/mtk-dramc.h>
+
+static struct platform_device *dramc_pdev;
+static struct platform_driver dramc_drv;
+
+static int fmeter_init(struct platform_device *pdev,
+		       struct fmeter_dev_t *fmeter_dev_ptr, unsigned int fmeter_version)
+{
+	struct device_node *dramc_node = pdev->dev.of_node;
+	int ret;
+
+	ret = of_property_read_u32(dramc_node,
+				   "crystal-freq", &(fmeter_dev_ptr->crystal_freq));
+	ret |= of_property_read_u32(dramc_node,
+				    "shu-of", &(fmeter_dev_ptr->shu_of));
+	ret |= of_property_read_u32_array(dramc_node,
+					  "shu-lv", (unsigned int *)&(fmeter_dev_ptr->shu_lv), 3);
+	ret |= of_property_read_u32_array(dramc_node,
+					  "pll-id", (unsigned int *)&(fmeter_dev_ptr->pll_id), 3);
+	ret |= of_property_read_u32_array(dramc_node,
+					  "sdmpcw", (unsigned int *)(fmeter_dev_ptr->sdmpcw), 6);
+	ret |= of_property_read_u32_array(dramc_node,
+					  "posdiv", (unsigned int *)(fmeter_dev_ptr->posdiv), 6);
+	ret |= of_property_read_u32_array(dramc_node,
+					  "fbksel", (unsigned int *)(fmeter_dev_ptr->fbksel), 6);
+	ret |= of_property_read_u32_array(dramc_node,
+					  "dqsopen", (unsigned int *)(fmeter_dev_ptr->dqsopen), 6);
+	if (fmeter_version == 1) {
+		fmeter_dev_ptr->version = 1;
+		ret |= of_property_read_u32_array(dramc_node,
+			"async-ca", (unsigned int *)(fmeter_dev_ptr->async_ca), 6);
+		ret |= of_property_read_u32_array(dramc_node,
+			"dq-ser-mode", (unsigned int *)(fmeter_dev_ptr->dq_ser_mode), 6);
+	}
+	return ret;
+}
+
+static ssize_t dram_data_rate_show(struct device_driver *driver, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "DRAM data rate = %d\n",
+		mtk_dramc_get_data_rate());
+}
+
+static DRIVER_ATTR_RO(dram_data_rate);
+
+static int dramc_probe(struct platform_device *pdev)
+{
+	struct device_node *dramc_node = pdev->dev.of_node;
+	struct dramc_dev_t *dramc_dev_ptr;
+	unsigned int fmeter_version;
+	struct resource *res;
+	unsigned int i, size;
+	int ret;
+
+	pr_info("%s: module probe.\n", __func__);
+	dramc_pdev = pdev;
+	dramc_dev_ptr = devm_kmalloc(&pdev->dev,
+				     sizeof(struct dramc_dev_t), GFP_KERNEL);
+
+	if (!dramc_dev_ptr)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(dramc_node,
+				   "support-ch-cnt", &dramc_dev_ptr->support_ch_cnt);
+	if (ret) {
+		pr_info("%s: get support_ch_cnt fail\n", __func__);
+		return -EINVAL;
+	}
+
+	dramc_dev_ptr->sleep_base = of_iomap(dramc_node,
+					     dramc_dev_ptr->support_ch_cnt * 4);
+	if (IS_ERR(dramc_dev_ptr->sleep_base)) {
+		pr_info("%s: unable to map sleep base\n", __func__);
+		return -EINVAL;
+	}
+
+	size = sizeof(phys_addr_t) * dramc_dev_ptr->support_ch_cnt;
+	dramc_dev_ptr->dramc_chn_base_ao = devm_kmalloc(&pdev->dev,
+							size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_ao))
+		return -ENOMEM;
+	dramc_dev_ptr->dramc_chn_base_nao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->dramc_chn_base_nao))
+		return -ENOMEM;
+	dramc_dev_ptr->ddrphy_chn_base_ao = devm_kmalloc(&pdev->dev,
+							 size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->ddrphy_chn_base_ao))
+		return -ENOMEM;
+	dramc_dev_ptr->ddrphy_chn_base_nao = devm_kmalloc(&pdev->dev,
+							  size, GFP_KERNEL);
+	if (!(dramc_dev_ptr->ddrphy_chn_base_nao))
+		return -ENOMEM;
+
+	for (i = 0; i < dramc_dev_ptr->support_ch_cnt; i++) {
+		res = platform_get_resource(pdev, IORESOURCE_MEM, i);
+		dramc_dev_ptr->dramc_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_ch_cnt);
+		dramc_dev_ptr->dramc_chn_base_nao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->dramc_chn_base_nao[i])) {
+			pr_info("%s: unable to map ch%d DRAMC NAO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_ch_cnt * 2);
+		dramc_dev_ptr->ddrphy_chn_base_ao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_ao[i])) {
+			pr_info("%s: unable to map ch%d DDRPHY AO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM,
+					    i + dramc_dev_ptr->support_ch_cnt * 3);
+		dramc_dev_ptr->ddrphy_chn_base_nao[i] =
+			devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(dramc_dev_ptr->ddrphy_chn_base_nao[i])) {
+			pr_info("%s: unable to map ch%d DDRPHY NAO base\n",
+				__func__, i);
+			return -EINVAL;
+		}
+	}
+
+	ret = of_property_read_u32(dramc_node, "fmeter-version", &fmeter_version);
+	if (ret) {
+		pr_info("%s: get fmeter_version fail\n", __func__);
+		return -EINVAL;
+	}
+	pr_info("%s: fmeter_version(%d)\n", __func__, fmeter_version);
+
+	if (fmeter_version == 1) {
+		dramc_dev_ptr->fmeter_dev_ptr = devm_kmalloc(&pdev->dev,
+							     sizeof(struct fmeter_dev_t),
+							     GFP_KERNEL);
+		if (!(dramc_dev_ptr->fmeter_dev_ptr)) {
+			pr_info("%s: memory  alloc fail\n", __func__);
+			return -ENOMEM;
+		}
+		ret = fmeter_init(pdev, dramc_dev_ptr->fmeter_dev_ptr, fmeter_version);
+		if (ret) {
+			pr_info("%s: fmeter_init fail\n", __func__);
+			return -EINVAL;
+		}
+	} else {
+		dramc_dev_ptr->fmeter_dev_ptr = NULL;
+	}
+	ret = driver_create_file(pdev->dev.driver, &driver_attr_dram_data_rate);
+	if (ret) {
+		pr_info("%s: fail to create dram_data_rate sysfs\n", __func__);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, dramc_dev_ptr);
+	pr_info("%s: DRAM data rate = %d\n", __func__,
+		mtk_dramc_get_data_rate());
+
+	return ret;
+}
+
+static unsigned int fmeter_v1(struct dramc_dev_t *dramc_dev_ptr)
+{
+	struct fmeter_dev_t *fmeter_dev_ptr =
+		(struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	unsigned int shu_lv_val;
+	unsigned int pll_id_val;
+	unsigned int sdmpcw_val;
+	unsigned int posdiv_val;
+	unsigned int ckdiv4_val;
+	unsigned int offset;
+	unsigned int vco_freq;
+	unsigned int fbksel;
+	unsigned int dqsopen;
+	unsigned int async_ca;
+	unsigned int dq_ser_mode;
+
+	shu_lv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
+		fmeter_dev_ptr->shu_lv.offset) &
+		fmeter_dev_ptr->shu_lv.mask) >>
+		fmeter_dev_ptr->shu_lv.shift;
+
+	pll_id_val = (readl(dramc_dev_ptr->ddrphy_chn_base_ao[0] +
+		fmeter_dev_ptr->pll_id.offset) &
+		fmeter_dev_ptr->pll_id.mask) >>
+		fmeter_dev_ptr->pll_id.shift;
+
+	offset = fmeter_dev_ptr->sdmpcw[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	sdmpcw_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->sdmpcw[pll_id_val].mask) >>
+		fmeter_dev_ptr->sdmpcw[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->posdiv[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	posdiv_val = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->posdiv[pll_id_val].mask) >>
+		fmeter_dev_ptr->posdiv[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->fbksel[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	fbksel = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->fbksel[pll_id_val].mask) >>
+		fmeter_dev_ptr->fbksel[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->dqsopen[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	dqsopen = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->dqsopen[pll_id_val].mask) >>
+		fmeter_dev_ptr->dqsopen[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->async_ca[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	async_ca = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->async_ca[pll_id_val].mask) >>
+		fmeter_dev_ptr->async_ca[pll_id_val].shift;
+
+	offset = fmeter_dev_ptr->dq_ser_mode[pll_id_val].offset +
+		fmeter_dev_ptr->shu_of * shu_lv_val;
+	dq_ser_mode = (readl(dramc_dev_ptr->ddrphy_chn_base_nao[0] + offset) &
+		fmeter_dev_ptr->dq_ser_mode[pll_id_val].mask) >>
+		fmeter_dev_ptr->dq_ser_mode[pll_id_val].shift;
+	ckdiv4_val = (dq_ser_mode == 1); // 1: DIV4, 2: DIV8, 3: DIV16
+
+	posdiv_val &= ~(0x4);
+
+	vco_freq = ((fmeter_dev_ptr->crystal_freq) *
+		(sdmpcw_val >> 7)) >> posdiv_val >> 1 >> ckdiv4_val
+		<< fbksel;
+
+	if ((dqsopen == 1) && (async_ca == 1))
+		vco_freq >>= 1;
+
+	return vco_freq;
+}
+
+/*
+ * mtk_dramc_get_data_rate - calculate DRAM data rate
+ *
+ * Returns DRAM data rate (MB/s)
+ */
+unsigned int mtk_dramc_get_data_rate(void)
+{
+	struct dramc_dev_t *dramc_dev_ptr;
+	struct fmeter_dev_t *fmeter_dev_ptr;
+
+	if (!dramc_pdev)
+		return 0;
+
+	dramc_dev_ptr =
+		(struct dramc_dev_t *)platform_get_drvdata(dramc_pdev);
+
+	fmeter_dev_ptr = (struct fmeter_dev_t *)dramc_dev_ptr->fmeter_dev_ptr;
+	if (!fmeter_dev_ptr)
+		return 0;
+
+	if (fmeter_dev_ptr->version == 1)
+		return fmeter_v1(dramc_dev_ptr);
+	return 0;
+}
+EXPORT_SYMBOL(mtk_dramc_get_data_rate);
+
+static int dramc_remove(struct platform_device *pdev)
+{
+	dramc_pdev = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id dramc_of_ids[] = {
+	{.compatible = "mediatek,common-dramc",},
+	{}
+};
+
+static struct platform_driver dramc_drv = {
+	.probe = dramc_probe,
+	.remove = dramc_remove,
+	.driver = {
+		.name = "dramc_drv",
+		.owner = THIS_MODULE,
+		.of_match_table = dramc_of_ids,
+	},
+};
+
+static int __init dramc_drv_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&dramc_drv);
+	if (ret) {
+		pr_info("%s: init fail, ret 0x%x\n", __func__, ret);
+		return ret;
+	}
+
+	return ret;
+}
+
+module_init(dramc_drv_init);
+
+MODULE_AUTHOR("Mediatek Corporation");
+MODULE_DESCRIPTION("MediaTek DRAMC Driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/soc/mediatek/mtk-dramc.h b/include/linux/soc/mediatek/mtk-dramc.h
new file mode 100644
index 000000000000..95e7dbfe7d0e
--- /dev/null
+++ b/include/linux/soc/mediatek/mtk-dramc.h
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019 MediaTek Inc.
+ */
+
+#ifndef __MTK_DRAMC_H__
+#define __MTK_DRAMC_H__
+
+struct reg_ctrl_t {
+	unsigned int offset;
+	unsigned int mask;
+	unsigned int shift;
+};
+
+struct fmeter_dev_t {
+	unsigned int version;
+	unsigned int crystal_freq;
+	unsigned int shu_of;
+	struct reg_ctrl_t shu_lv;
+	struct reg_ctrl_t pll_id;
+	struct reg_ctrl_t sdmpcw[2];
+	struct reg_ctrl_t posdiv[2];
+	struct reg_ctrl_t fbksel[2];
+	struct reg_ctrl_t dqsopen[2];
+	struct reg_ctrl_t async_ca[2];
+	struct reg_ctrl_t dq_ser_mode[2];
+};
+
+struct dramc_dev_t {
+	unsigned int support_ch_cnt;
+	void __iomem **dramc_chn_base_ao;
+	void __iomem **dramc_chn_base_nao;
+	void __iomem **ddrphy_chn_base_ao;
+	void __iomem **ddrphy_chn_base_nao;
+	void __iomem *sleep_base;
+	void *fmeter_dev_ptr;
+};
+
+unsigned int mtk_dramc_get_data_rate(void);
+
+#endif /* __MTK_DRAMC_H__ */
-- 
2.18.0



  reply	other threads:[~2024-12-12  9:19 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-12-12  8:59 [PATCH 0/2] Add MediaTek DRAMC driver support Crystal Guo
2024-12-12  8:59 ` Crystal Guo [this message]
2024-12-12 10:46   ` [PATCH 1/2] memory/mediatek: Add an interface to get current DDR data rate AngeloGioacchino Del Regno
2025-02-08  4:14     ` Crystal Guo (郭晶)
2025-02-11 12:13     ` Crystal Guo (郭晶)
2024-12-12 11:02   ` Krzysztof Kozlowski
2025-02-08  4:21     ` Crystal Guo (郭晶)
2025-02-11 12:21     ` Crystal Guo (郭晶)
2024-12-21  1:42   ` kernel test robot
2024-12-21 11:03   ` kernel test robot
2024-12-12  8:59 ` [PATCH 2/2] dt-bindings: memory-controllers: Add mediatek common-dramc dt-bindings Crystal Guo
2024-12-12 10:27   ` Rob Herring (Arm)
2025-02-08  4:24     ` Crystal Guo (郭晶)
2024-12-12 10:27   ` AngeloGioacchino Del Regno
2025-02-08  4:33     ` Crystal Guo (郭晶)
2025-02-11 12:46     ` Crystal Guo (郭晶)
2024-12-12 10:58   ` Krzysztof Kozlowski
2025-02-08  4:40     ` Crystal Guo (郭晶)
2025-02-09 10:52       ` Krzysztof Kozlowski
2025-02-11 12:59         ` Crystal Guo (郭晶)
2025-02-11 15:57           ` Krzysztof Kozlowski
2025-02-12  3:21             ` Crystal Guo (郭晶)
2025-02-11 12:56     ` Crystal Guo (郭晶)
2025-02-11 15:55       ` Krzysztof Kozlowski
2025-02-12  2:47         ` Crystal Guo (郭晶)

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=20241212090029.13692-2-crystal.guo@mediatek.com \
    --to=crystal.guo@mediatek.com \
    --cc=Project_Global_Chrome_Upstream_Group@mediatek.com \
    --cc=angelogioacchino.delregno@collabora.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=krzk@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=matthias.bgg@gmail.com \
    --cc=robh@kernel.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 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.