mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Frank Li <Frank.Li@nxp.com>
To: Rui Miguel Silva <rmfrfs@gmail.com>,
	 Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	 Martin Kepplinger <martink@posteo.de>,
	Purism Kernel Team <kernel@puri.sm>,
	 Mauro Carvalho Chehab <mchehab@kernel.org>,
	Rob Herring <robh@kernel.org>,
	 Krzysztof Kozlowski <krzk+dt@kernel.org>,
	 Conor Dooley <conor+dt@kernel.org>,
	 Eugen Hristev <eugen.hristev@linaro.org>,
	Shawn Guo <shawnguo@kernel.org>,
	 Sascha Hauer <s.hauer@pengutronix.de>,
	 Pengutronix Kernel Team <kernel@pengutronix.de>,
	 Fabio Estevam <festevam@gmail.com>, Peng Fan <peng.fan@nxp.com>,
	 Alice Yuan <alice.yuan@nxp.com>, Vinod Koul <vkoul@kernel.org>,
	 Kishon Vijay Abraham I <kishon@kernel.org>,
	 Philipp Zabel <p.zabel@pengutronix.de>,
	 Steve Longerbeam <slongerbeam@gmail.com>,
	 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-media@vger.kernel.org, devicetree@vger.kernel.org,
	 linux-kernel@vger.kernel.org, imx@lists.linux.dev,
	 linux-arm-kernel@lists.infradead.org,
	linux-phy@lists.infradead.org,  linux-staging@lists.linux.dev,
	Frank Li <Frank.Li@nxp.com>
Subject: [PATCH v2 10/32] media: staging: media: imx6-mipi-csi2: use register structure to match hardware
Date: Fri, 08 Aug 2025 18:39:13 -0400	[thread overview]
Message-ID: <20250808-95_cam-v2-10-4b29fa6919a7@nxp.com> (raw)
In-Reply-To: <20250808-95_cam-v2-0-4b29fa6919a7@nxp.com>

Use register structure to handle different versions of the DesignWare CSI2
controller, as register offsets slightly differ between versions. Add
register existence bits to detect invalid register access when using
different versions of the CSI2 controller.

No functional change.

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 drivers/staging/media/imx/imx6-mipi-csi2.c | 165 +++++++++++++++++------------
 1 file changed, 96 insertions(+), 69 deletions(-)

diff --git a/drivers/staging/media/imx/imx6-mipi-csi2.c b/drivers/staging/media/imx/imx6-mipi-csi2.c
index 73278d9ec2d66afa33b2bea17f2d56bb1391108b..cef9a5dbca40c94a36d5d8473d3b7d9806381bf9 100644
--- a/drivers/staging/media/imx/imx6-mipi-csi2.c
+++ b/drivers/staging/media/imx/imx6-mipi-csi2.c
@@ -34,12 +34,30 @@
  */
 #define CSI2_DEFAULT_MAX_MBPS	849
 
+struct dw_csi2_regs {
+	u32	version;
+	u32	n_lanes;
+	u32	phy_shutdownz;
+	u32	dphy_rstz;
+	u32	resetn;
+	u32	phy_state;
+	u32	data_ids_1;
+	u32	data_ids_2;
+	u32	err1;
+	u32	err2;
+	u32	msk1;
+	u32	msk2;
+	u32	phy_tst_ctrl0;
+	u32	phy_tst_ctrl1;
+};
+
 struct csi2_dev {
 	struct device			*dev;
 	struct v4l2_subdev		sd;
 	struct v4l2_async_notifier	notifier;
 	struct media_pad		pad[CSI2_NUM_PADS];
 	void __iomem			*base;
+	const struct dw_csi2_regs	*regs;
 
 	struct clk_bulk_data		*clks;
 	int				num_clks;
@@ -60,28 +78,53 @@ struct csi2_dev {
 
 #define DEVICE_NAME "imx6-mipi-csi2"
 
-/* Register offsets */
-#define CSI2_VERSION		0x000
-#define CSI2_N_LANES		0x004
-#define CSI2_PHY_SHUTDOWNZ	0x008
-#define CSI2_DPHY_RSTZ		0x00c
-#define CSI2_RESETN		0x010
-#define CSI2_PHY_STATE		0x014
+/* Help check wrong access unexisted register at difference IP version */
+#define DW_REG_EXIST		0x80000000
+#define DW_REG(x)		(DW_REG_EXIST | (x))
+
+/* Register offsets for v0 */
+static const struct dw_csi2_regs dw_csi2_v0 = {
+	.version = DW_REG(0x0),
+	.n_lanes = DW_REG(0x4),
+	.phy_shutdownz = DW_REG(0x8),
+	.dphy_rstz = DW_REG(0xc),
+	.resetn = DW_REG(0x10),
+	.phy_state = DW_REG(0x14),
+	.data_ids_1 = DW_REG(0x18),
+	.data_ids_2 = DW_REG(0x1c),
+	.err1 = DW_REG(0x20),
+	.err2 = DW_REG(0x24),
+	.msk1 = DW_REG(0x28),
+	.msk2 = DW_REG(0x2c),
+	.phy_tst_ctrl0 = DW_REG(0x30),
+	.phy_tst_ctrl1 = DW_REG(0x34),
+};
+
+static int dw_csi2_reg_err(struct csi2_dev *csi2, const char *name)
+{
+	dev_err_once(csi2->dev, "access to unexisted register: %s", name);
+	return 0;
+}
+
+#define dw_reg_exist(csi2, __name) ((csi2)->regs->__name & DW_REG_EXIST)
+
+#define dw_writel(csi2, value, __name) (dw_reg_exist((csi2), __name) ? \
+writel(value, (csi2)->base + (csi2->regs->__name & ~DW_REG_EXIST)) : \
+dw_csi2_reg_err((csi2), #__name))
+
+#define dw_readl(csi2, __name) (dw_reg_exist((csi2), __name) ? \
+readl((csi2)->base + ((csi2)->regs->__name & ~DW_REG_EXIST)) : \
+dw_csi2_reg_err(csi2, #__name))
+
 #define PHY_STOPSTATEDATA_BIT	4
 #define PHY_STOPSTATEDATA(n)	BIT(PHY_STOPSTATEDATA_BIT + (n))
 #define PHY_RXCLKACTIVEHS	BIT(8)
 #define PHY_RXULPSCLKNOT	BIT(9)
 #define PHY_STOPSTATECLK	BIT(10)
-#define CSI2_DATA_IDS_1		0x018
-#define CSI2_DATA_IDS_2		0x01c
-#define CSI2_ERR1		0x020
-#define CSI2_ERR2		0x024
-#define CSI2_MSK1		0x028
-#define CSI2_MSK2		0x02c
-#define CSI2_PHY_TST_CTRL0	0x030
+
 #define PHY_TESTCLR		BIT(0)
 #define PHY_TESTCLK		BIT(1)
-#define CSI2_PHY_TST_CTRL1	0x034
+
 #define PHY_TESTEN		BIT(16)
 /*
  * i.MX CSI2IPU Gasket registers follow. The CSI2IPU gasket is
@@ -132,42 +175,42 @@ static inline struct csi2_dev *notifier_to_dev(struct v4l2_async_notifier *n)
 static void csi2_enable(struct csi2_dev *csi2, bool enable)
 {
 	if (enable) {
-		writel(0x1, csi2->base + CSI2_PHY_SHUTDOWNZ);
-		writel(0x1, csi2->base + CSI2_DPHY_RSTZ);
-		writel(0x1, csi2->base + CSI2_RESETN);
+		dw_writel(csi2, 0x1, phy_shutdownz);
+		dw_writel(csi2, 0x1, dphy_rstz);
+		dw_writel(csi2, 0x1, resetn);
 	} else {
-		writel(0x0, csi2->base + CSI2_PHY_SHUTDOWNZ);
-		writel(0x0, csi2->base + CSI2_DPHY_RSTZ);
-		writel(0x0, csi2->base + CSI2_RESETN);
+		dw_writel(csi2, 0x0, phy_shutdownz);
+		dw_writel(csi2, 0x0, dphy_rstz);
+		dw_writel(csi2, 0x0, resetn);
 	}
 }
 
 static void csi2_set_lanes(struct csi2_dev *csi2, unsigned int lanes)
 {
-	writel(lanes - 1, csi2->base + CSI2_N_LANES);
+	dw_writel(csi2, lanes - 1, n_lanes);
 }
 
 static void dw_mipi_csi2_phy_write(struct csi2_dev *csi2,
 				   u32 test_code, u32 test_data)
 {
 	/* Clear PHY test interface */
-	writel(PHY_TESTCLR, csi2->base + CSI2_PHY_TST_CTRL0);
-	writel(0x0, csi2->base + CSI2_PHY_TST_CTRL1);
-	writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+	dw_writel(csi2, PHY_TESTCLR, phy_tst_ctrl0);
+	dw_writel(csi2, 0x0, phy_tst_ctrl1);
+	dw_writel(csi2, 0x0, phy_tst_ctrl0);
 
 	/* Raise test interface strobe signal */
-	writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+	dw_writel(csi2, PHY_TESTCLK, phy_tst_ctrl0);
 
 	/* Configure address write on falling edge and lower strobe signal */
-	writel(PHY_TESTEN | test_code, csi2->base + CSI2_PHY_TST_CTRL1);
-	writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+	dw_writel(csi2, PHY_TESTEN | test_code, phy_tst_ctrl1);
+	dw_writel(csi2, 0x0, phy_tst_ctrl0);
 
 	/* Configure data write on rising edge and raise strobe signal */
-	writel(test_data, csi2->base + CSI2_PHY_TST_CTRL1);
-	writel(PHY_TESTCLK, csi2->base + CSI2_PHY_TST_CTRL0);
+	dw_writel(csi2, test_data, phy_tst_ctrl1);
+	dw_writel(csi2, PHY_TESTCLK, phy_tst_ctrl0);
 
 	/* Clear strobe signal */
-	writel(0x0, csi2->base + CSI2_PHY_TST_CTRL0);
+	dw_writel(csi2, 0x0, phy_tst_ctrl0);
 }
 
 /*
@@ -233,16 +276,15 @@ static int __maybe_unused csi2_dphy_wait_ulp(struct csi2_dev *csi2)
 	int ret;
 
 	/* wait for ULP on clock lane */
-	ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
-				 !(reg & PHY_RXULPSCLKNOT), 0, 500000);
+	ret = read_poll_timeout(dw_readl, reg, !(reg & PHY_RXULPSCLKNOT),
+				0, 500000, 0, csi2, phy_state);
 	if (ret) {
 		v4l2_err(&csi2->sd, "ULP timeout, phy_state = 0x%08x\n", reg);
 		return ret;
 	}
 
 	/* wait until no errors on bus */
-	ret = readl_poll_timeout(csi2->base + CSI2_ERR1, reg,
-				 reg == 0x0, 0, 500000);
+	ret = read_poll_timeout(dw_readl, reg, reg == 0x0, 0, 500000, 0, csi2, err1);
 	if (ret) {
 		v4l2_err(&csi2->sd, "stable bus timeout, err1 = 0x%08x\n", reg);
 		return ret;
@@ -259,8 +301,7 @@ static void csi2_dphy_wait_stopstate(struct csi2_dev *csi2, unsigned int lanes)
 
 	mask = PHY_STOPSTATECLK | (((1 << lanes) - 1) << PHY_STOPSTATEDATA_BIT);
 
-	ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
-				 (reg & mask) == mask, 0, 500000);
+	ret = read_poll_timeout(dw_readl, reg, (reg & mask) == mask, 0, 500000, 0, csi2, phy_state);
 	if (ret) {
 		v4l2_warn(&csi2->sd, "LP-11 wait timeout, likely a sensor driver bug, expect capture failures.\n");
 		v4l2_warn(&csi2->sd, "phy_state = 0x%08x\n", reg);
@@ -273,8 +314,8 @@ static int csi2_dphy_wait_clock_lane(struct csi2_dev *csi2)
 	u32 reg;
 	int ret;
 
-	ret = readl_poll_timeout(csi2->base + CSI2_PHY_STATE, reg,
-				 (reg & PHY_RXCLKACTIVEHS), 0, 500000);
+	ret = read_poll_timeout(dw_readl, reg, (reg & PHY_RXCLKACTIVEHS),
+				0, 500000, 0, csi2, phy_state);
 	if (ret) {
 		v4l2_err(&csi2->sd, "clock lane timeout, phy_state = 0x%08x\n",
 			 reg);
@@ -556,34 +597,20 @@ static int csi2_log_status(struct v4l2_subdev *sd)
 	struct csi2_dev *csi2 = sd_to_dev(sd);
 
 	v4l2_info(sd, "-----MIPI CSI status-----\n");
-	v4l2_info(sd, "VERSION: 0x%x\n",
-		  readl(csi2->base + CSI2_VERSION));
-	v4l2_info(sd, "N_LANES: 0x%x\n",
-		  readl(csi2->base + CSI2_N_LANES));
-	v4l2_info(sd, "PHY_SHUTDOWNZ: 0x%x\n",
-		  readl(csi2->base + CSI2_PHY_SHUTDOWNZ));
-	v4l2_info(sd, "DPHY_RSTZ: 0x%x\n",
-		  readl(csi2->base + CSI2_DPHY_RSTZ));
-	v4l2_info(sd, "RESETN: 0x%x\n",
-		  readl(csi2->base + CSI2_RESETN));
-	v4l2_info(sd, "PHY_STATE: 0x%x\n",
-		  readl(csi2->base + CSI2_PHY_STATE));
-	v4l2_info(sd, "DATA_IDS_1: 0x%x\n",
-		  readl(csi2->base + CSI2_DATA_IDS_1));
-	v4l2_info(sd, "DATA_IDS_2: 0x%x\n",
-		  readl(csi2->base + CSI2_DATA_IDS_2));
-	v4l2_info(sd, "ERR1: 0x%x\n",
-		  readl(csi2->base + CSI2_ERR1));
-	v4l2_info(sd, "ERR2: 0x%x\n",
-		  readl(csi2->base + CSI2_ERR2));
-	v4l2_info(sd, "MSK1: 0x%x\n",
-		  readl(csi2->base + CSI2_MSK1));
-	v4l2_info(sd, "MSK2: 0x%x\n",
-		  readl(csi2->base + CSI2_MSK2));
-	v4l2_info(sd, "PHY_TST_CTRL0: 0x%x\n",
-		  readl(csi2->base + CSI2_PHY_TST_CTRL0));
-	v4l2_info(sd, "PHY_TST_CTRL1: 0x%x\n",
-		  readl(csi2->base + CSI2_PHY_TST_CTRL1));
+	v4l2_info(sd, "VERSION: 0x%x\n", dw_readl(csi2, version));
+	v4l2_info(sd, "N_LANES: 0x%x\n", dw_readl(csi2, n_lanes));
+	v4l2_info(sd, "PHY_SHUTDOWNZ: 0x%x\n", dw_readl(csi2, phy_shutdownz));
+	v4l2_info(sd, "DPHY_RSTZ: 0x%x\n", dw_readl(csi2, dphy_rstz));
+	v4l2_info(sd, "RESETN: 0x%x\n", dw_readl(csi2, resetn));
+	v4l2_info(sd, "PHY_STATE: 0x%x\n", dw_readl(csi2, phy_state));
+	v4l2_info(sd, "DATA_IDS_1: 0x%x\n", dw_readl(csi2, data_ids_1));
+	v4l2_info(sd, "DATA_IDS_2: 0x%x\n", dw_readl(csi2, data_ids_2));
+	v4l2_info(sd, "ERR1: 0x%x\n", dw_readl(csi2, err1));
+	v4l2_info(sd, "ERR2: 0x%x\n", dw_readl(csi2, err2));
+	v4l2_info(sd, "MSK1: 0x%x\n", dw_readl(csi2, msk1));
+	v4l2_info(sd, "MSK2: 0x%x\n", dw_readl(csi2, msk2));
+	v4l2_info(sd, "PHY_TST_CTRL0: 0x%x\n", dw_readl(csi2, phy_tst_ctrl0));
+	v4l2_info(sd, "PHY_TST_CTRL1: 0x%x\n", dw_readl(csi2, phy_tst_ctrl1));
 
 	return 0;
 }
@@ -719,7 +746,7 @@ static int csi2_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	csi2->dev = &pdev->dev;
-
+	csi2->regs = &dw_csi2_v0;
 	v4l2_subdev_init(&csi2->sd, &csi2_subdev_ops);
 	v4l2_set_subdevdata(&csi2->sd, &pdev->dev);
 	csi2->sd.internal_ops = &csi2_internal_ops;
@@ -774,7 +801,7 @@ static void csi2_remove(struct platform_device *pdev)
 }
 
 static const struct of_device_id csi2_dt_ids[] = {
-	{ .compatible = "fsl,imx6-mipi-csi2", },
+	{ .compatible = "fsl,imx6-mipi-csi2"},
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, csi2_dt_ids);

-- 
2.34.1


  parent reply	other threads:[~2025-08-08 22:40 UTC|newest]

Thread overview: 36+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-08 22:39 [PATCH v2 00/32] media: add imx93 mipi/controller csi support Frank Li
2025-08-08 22:39 ` [PATCH v2 01/32] dt-bindings: media: add DW MIPI CSI-2 Host support Frank Li
2025-08-18 14:39   ` Rob Herring
2025-08-08 22:39 ` [PATCH v2 02/32] media: v4l2-common: Add helper function v4l_get_required_align_by_bpp() Frank Li
2025-08-08 22:39 ` [PATCH v2 03/32] media: v4l2-common: Add helper function media_bus_fmt_to_csi2_bpp() Frank Li
2025-08-08 22:39 ` [PATCH v2 04/32] media: v4l2-common: Add helper function media_bus_fmt_to_csi2_dt() Frank Li
2025-08-20 19:16   ` Laurent Pinchart
2025-08-08 22:39 ` [PATCH v2 05/32] media: staging: media: imx6-mipi-csi2: replace space with tab for alignment Frank Li
2025-08-08 22:39 ` [PATCH v2 06/32] media: staging: media: imx6-mipi-csi2: use devm_add_action_or_reset() to simplify code Frank Li
2025-08-08 22:39 ` [PATCH v2 07/32] media: staging: media: imx6-mipi-csi2: use devm_clk_bulk_get_all() to fetch clocks Frank Li
2025-08-08 22:39 ` [PATCH v2 08/32] media: staging: media: imx6-mipi-csi2: use devm_mutex_init() to simplify code Frank Li
2025-08-08 22:39 ` [PATCH v2 09/32] media: staging: media: imx6-mipi-csi2: use guard() " Frank Li
2025-08-08 22:39 ` Frank Li [this message]
2025-08-08 22:39 ` [PATCH v2 11/32] media: staging: media: imx6-mipi-csi2: use devm_platform_ioremap_resource() " Frank Li
2025-08-08 22:39 ` [PATCH v2 12/32] media: staging: media: imx6-mipi-csi2: move probe part to imx6-csi2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 13/32] media: staging: media: imx6-mipi-csi2: move sd imx6's specific initialization into imx6-sci2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 14/32] media: staging: media: imx6-mipi-csi2: move csi2ipu_gasket_init() to imx6-csi2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 15/32] media: staging: media: imx6-mipi-csi2: move number pad macro define into imx6-csi2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 16/32] media: staging: media: imx6-mipi-csi2: move dphy init part to imx6-csi2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 17/32] media: staging: media: imx6-mipi-csi2: use runtime_pm frame to control clks Frank Li
2025-08-08 22:39 ` [PATCH v2 18/32] media: synopsys: move imx6-mipi-csi2.c to synopsys/mipi-csi2.c Frank Li
2025-08-08 22:39 ` [PATCH v2 19/32] media: synopsys: csi2: Remove deprecated s_stream and use v4l2_subdev_pad_ops Frank Li
2025-08-08 22:39 ` [PATCH v2 20/32] media: synopsys: csi2: Add phy interface support Frank Li
2025-08-08 22:39 ` [PATCH v2 21/32] media: synopsys: csi2: Add basic v150* version register Frank Li
2025-08-08 22:39 ` [PATCH v2 22/32] media: synopsys: csi2: Add irq support to record error count Frank Li
2025-08-08 22:39 ` [PATCH v2 23/32] media: synopsys: csi2: Handle alignment requirement for width Frank Li
2025-08-08 22:39 ` [PATCH v2 24/32] media: synopsys: csi2: Add register prefix to register field definitions Frank Li
2025-08-08 22:39 ` [PATCH v2 25/32] media: synopsys: csi2: Add need_dphy_reset in config Frank Li
2025-08-08 22:39 ` [PATCH v2 26/32] media: synopsys: csi2: Add default simple dw_csi2_subdev_init_state Frank Li
2025-08-08 22:39 ` [PATCH v2 27/32] media: synopsys: csi2: Add v150 lane stop state register bit define Frank Li
2025-08-08 22:39 ` [PATCH v2 28/32] media: synopsys: csi2: use standard v4l2_subdev_get_fmt() function Frank Li
2025-08-08 22:39 ` [PATCH v2 29/32] media: synopsys: csi2: Add customize get_frame_desc() callback Frank Li
2025-08-08 22:39 ` [PATCH v2 30/32] media: synopsys: csi2: Add Image Pixel Interface (IPI) support for v150 Frank Li
2025-08-08 22:39 ` [PATCH v2 31/32] media: synopsys: csi2: Remove source pad connected check at dw_csi2_enable_streams() Frank Li
2025-08-08 22:39 ` [PATCH v2 32/32] media: synopsys: csi2: Add simple synopsys platform driver Frank Li
2025-08-20 14:39 ` [PATCH v2 00/32] media: add imx93 mipi/controller csi support Frank Li

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=20250808-95_cam-v2-10-4b29fa6919a7@nxp.com \
    --to=frank.li@nxp.com \
    --cc=alice.yuan@nxp.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=eugen.hristev@linaro.org \
    --cc=festevam@gmail.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=imx@lists.linux.dev \
    --cc=kernel@pengutronix.de \
    --cc=kernel@puri.sm \
    --cc=kishon@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-media@vger.kernel.org \
    --cc=linux-phy@lists.infradead.org \
    --cc=linux-staging@lists.linux.dev \
    --cc=martink@posteo.de \
    --cc=mchehab@kernel.org \
    --cc=p.zabel@pengutronix.de \
    --cc=peng.fan@nxp.com \
    --cc=rmfrfs@gmail.com \
    --cc=robh@kernel.org \
    --cc=s.hauer@pengutronix.de \
    --cc=shawnguo@kernel.org \
    --cc=slongerbeam@gmail.com \
    --cc=vkoul@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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®