linux-i3c.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support
@ 2021-08-09  6:36 Clark Wang
  2021-08-09  6:36 ` [PATCH V4 1/8] i3c: master: svc: move module reset behind clk enable Clark Wang
                   ` (7 more replies)
  0 siblings, 8 replies; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

Hi,

V4 changes:
 - New patches:
   - i3c: master: svc: separate err, fifo and disable interrupt of reset function
   - i3c: master: svc: add the missing module device table
   - i3c: master: svc: enable the interrupt in the enable ibi function
 - i3c: master: svc: add support for slave to stop returning data
   - Only use COMPLETE flag in the read function.
   - Add a reset before each transfer.
 - i3c: master: svc: add runtime pm support
    - error path emit_stop only used in svc_i3c_master_do_daa() for one time, so
      remove it. Or it needs another goto command at the end of the function to
      skip it and to go to rpm_out.

V3 changes:
 - i3c: master: svc: add runtime pm support:
   - restore the error path of probe function
   - enable runtime pm just before i3c module reset

V2 changes:
 - align the parameters of the function in the second patch
 - add a new patch to support runtime PM.

Clark Wang (8):
  i3c: master: svc: move module reset behind clk enable
  i3c: master: svc: fix atomic issue
  i3c: master: svc: separate err, fifo and disable interrupt of reset
    function
  i3c: master: svc: add support for slave to stop returning data
  i3c: master: svc: set ODSTOP to let I2C device see the STOP signal
  i3c: master: svc: add runtime pm support
  i3c: master: svc: add the missing module device table
  i3c: master: svc: enable the interrupt in the enable ibi function

 drivers/i3c/master/svc-i3c-master.c | 307 +++++++++++++++++++++-------
 1 file changed, 231 insertions(+), 76 deletions(-)

-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 1/8] i3c: master: svc: move module reset behind clk enable
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-09  6:36 ` [PATCH V4 2/8] i3c: master: svc: fix atomic issue Clark Wang
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

Reset I3C module will R/W its regs, so enable its clocks first.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V2/V3/V4: No change.
---
 drivers/i3c/master/svc-i3c-master.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 879e5a64acaf..c25a372f6820 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1381,8 +1381,6 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	master->dev = dev;
 
-	svc_i3c_master_reset(master);
-
 	ret = clk_prepare_enable(master->pclk);
 	if (ret)
 		return ret;
@@ -1419,6 +1417,8 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, master);
 
+	svc_i3c_master_reset(master);
+
 	/* Register the master */
 	ret = i3c_master_register(&master->base, &pdev->dev,
 				  &svc_i3c_master_ops, false);
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 2/8] i3c: master: svc: fix atomic issue
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
  2021-08-09  6:36 ` [PATCH V4 1/8] i3c: master: svc: move module reset behind clk enable Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-09  6:36 ` [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function Clark Wang
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

do_daa_locked() function is in a spin lock environment, use
readl_poll_timeout_atomic() to replace the origin
readl_poll_timeout().

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V3/V4: No change.
V2: align the parameters of the function.
---
 drivers/i3c/master/svc-i3c-master.c | 26 +++++++++++++++-----------
 1 file changed, 15 insertions(+), 11 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index c25a372f6820..47c02a60cf62 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -656,8 +656,10 @@ static int svc_i3c_master_readb(struct svc_i3c_master *master, u8 *dst,
 	u32 reg;
 
 	for (i = 0; i < len; i++) {
-		ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
-					 SVC_I3C_MSTATUS_RXPEND(reg), 0, 1000);
+		ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
+						reg,
+						SVC_I3C_MSTATUS_RXPEND(reg),
+						0, 1000);
 		if (ret)
 			return ret;
 
@@ -687,10 +689,11 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
 		 * Either one slave will send its ID, or the assignment process
 		 * is done.
 		 */
-		ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
-					 SVC_I3C_MSTATUS_RXPEND(reg) |
-					 SVC_I3C_MSTATUS_MCTRLDONE(reg),
-					 1, 1000);
+		ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
+						reg,
+						SVC_I3C_MSTATUS_RXPEND(reg) |
+						SVC_I3C_MSTATUS_MCTRLDONE(reg),
+						1, 1000);
 		if (ret)
 			return ret;
 
@@ -744,11 +747,12 @@ static int svc_i3c_master_do_daa_locked(struct svc_i3c_master *master,
 		}
 
 		/* Wait for the slave to be ready to receive its address */
-		ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
-					 SVC_I3C_MSTATUS_MCTRLDONE(reg) &&
-					 SVC_I3C_MSTATUS_STATE_DAA(reg) &&
-					 SVC_I3C_MSTATUS_BETWEEN(reg),
-					 0, 1000);
+		ret = readl_poll_timeout_atomic(master->regs + SVC_I3C_MSTATUS,
+						reg,
+						SVC_I3C_MSTATUS_MCTRLDONE(reg) &&
+						SVC_I3C_MSTATUS_STATE_DAA(reg) &&
+						SVC_I3C_MSTATUS_BETWEEN(reg),
+						0, 1000);
 		if (ret)
 			return ret;
 
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
  2021-08-09  6:36 ` [PATCH V4 1/8] i3c: master: svc: move module reset behind clk enable Clark Wang
  2021-08-09  6:36 ` [PATCH V4 2/8] i3c: master: svc: fix atomic issue Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-16  6:52   ` Miquel Raynal
  2021-08-09  6:36 ` [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data Clark Wang
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

Sometimes only need to reset err and fifo regs, so split the origin
reset function to three functions.
Put them at the top of the file, to let more functions can call them.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V4: New patch in this patchset
---
 drivers/i3c/master/svc-i3c-master.c | 59 +++++++++++++++++------------
 1 file changed, 34 insertions(+), 25 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 47c02a60cf62..4e69c691253d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -236,6 +236,40 @@ static void svc_i3c_master_disable_interrupts(struct svc_i3c_master *master)
 	writel(mask, master->regs + SVC_I3C_MINTCLR);
 }
 
+static void svc_i3c_master_clear_merrwarn(struct svc_i3c_master *master)
+{
+	/* Clear pending warnings */
+	writel(readl(master->regs + SVC_I3C_MERRWARN),
+	       master->regs + SVC_I3C_MERRWARN);
+}
+
+static void svc_i3c_master_flush_fifo(struct svc_i3c_master *master)
+{
+	/* Flush FIFOs */
+	writel(SVC_I3C_MDATACTRL_FLUSHTB | SVC_I3C_MDATACTRL_FLUSHRB,
+	       master->regs + SVC_I3C_MDATACTRL);
+}
+
+static void svc_i3c_master_reset_fifo_trigger(struct svc_i3c_master *master)
+{
+	u32 reg;
+
+	/* Set RX and TX tigger levels, flush FIFOs */
+	reg = SVC_I3C_MDATACTRL_FLUSHTB |
+	      SVC_I3C_MDATACTRL_FLUSHRB |
+	      SVC_I3C_MDATACTRL_UNLOCK_TRIG |
+	      SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL |
+	      SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY;
+	writel(reg, master->regs + SVC_I3C_MDATACTRL);
+}
+
+static void svc_i3c_master_reset(struct svc_i3c_master *master)
+{
+	svc_i3c_master_clear_merrwarn(master);
+	svc_i3c_master_reset_fifo_trigger(master);
+	svc_i3c_master_disable_interrupts(master);
+}
+
 static inline struct svc_i3c_master *
 to_svc_i3c_master(struct i3c_master_controller *master)
 {
@@ -279,12 +313,6 @@ static void svc_i3c_master_emit_stop(struct svc_i3c_master *master)
 	udelay(1);
 }
 
-static void svc_i3c_master_clear_merrwarn(struct svc_i3c_master *master)
-{
-	writel(readl(master->regs + SVC_I3C_MERRWARN),
-	       master->regs + SVC_I3C_MERRWARN);
-}
-
 static int svc_i3c_master_handle_ibi(struct svc_i3c_master *master,
 				     struct i3c_dev_desc *dev)
 {
@@ -1334,25 +1362,6 @@ static const struct i3c_master_controller_ops svc_i3c_master_ops = {
 	.disable_ibi = svc_i3c_master_disable_ibi,
 };
 
-static void svc_i3c_master_reset(struct svc_i3c_master *master)
-{
-	u32 reg;
-
-	/* Clear pending warnings */
-	writel(readl(master->regs + SVC_I3C_MERRWARN),
-	       master->regs + SVC_I3C_MERRWARN);
-
-	/* Set RX and TX tigger levels, flush FIFOs */
-	reg = SVC_I3C_MDATACTRL_FLUSHTB |
-	      SVC_I3C_MDATACTRL_FLUSHRB |
-	      SVC_I3C_MDATACTRL_UNLOCK_TRIG |
-	      SVC_I3C_MDATACTRL_TXTRIG_FIFO_NOT_FULL |
-	      SVC_I3C_MDATACTRL_RXTRIG_FIFO_NOT_EMPTY;
-	writel(reg, master->regs + SVC_I3C_MDATACTRL);
-
-	svc_i3c_master_disable_interrupts(master);
-}
-
 static int svc_i3c_master_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
                   ` (2 preceding siblings ...)
  2021-08-09  6:36 ` [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-16  7:15   ` Miquel Raynal
  2021-08-09  6:36 ` [PATCH V4 5/8] i3c: master: svc: set ODSTOP to let I2C device see the STOP signal Clark Wang
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

When i3c controller reads data from slave device, slave device can stop
returning data with an ACK after any byte.
Add this support for svc i3c controller. Otherwise, it will go TIMEOUT
error path when the slave device ends the read operation early.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V4 changes:
 - Only use COMPLETE flag in the read function.
 - Add a reset before each transfer.
V2/V3: No change.
---
 drivers/i3c/master/svc-i3c-master.c | 56 ++++++++++++++++++++---------
 1 file changed, 39 insertions(+), 17 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 4e69c691253d..e767f307db2d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -896,27 +896,35 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
 static int svc_i3c_master_read(struct svc_i3c_master *master,
 			       u8 *in, unsigned int len)
 {
-	int offset = 0, i, ret;
-	u32 mdctrl;
+	int offset = 0, i;
+	u32 mdctrl, mstatus;
+	bool completed = false;
+	unsigned int count;
+	unsigned long start = jiffies;
 
-	while (offset < len) {
-		unsigned int count;
+	while (!completed) {
+		mstatus = readl(master->regs + SVC_I3C_MSTATUS);
+		if (SVC_I3C_MSTATUS_COMPLETE(mstatus) != 0)
+			completed = true;
 
-		ret = readl_poll_timeout(master->regs + SVC_I3C_MDATACTRL,
-					 mdctrl,
-					 !(mdctrl & SVC_I3C_MDATACTRL_RXEMPTY),
-					 0, 1000);
-		if (ret)
-			return ret;
+		if (time_after(jiffies, start + msecs_to_jiffies(1000))) {
+			dev_dbg(master->dev, "<%s> timeout\n", __func__);
+			return -ETIMEDOUT;
+		}
 
+		mdctrl = readl(master->regs + SVC_I3C_MDATACTRL);
 		count = SVC_I3C_MDATACTRL_RXCOUNT(mdctrl);
+		if (offset + count > len) {
+			dev_err(master->dev, "<%s> recv len too long!\n",
+				__func__);
+			return -EINVAL;
+		}
 		for (i = 0; i < count; i++)
 			in[offset + i] = readl(master->regs + SVC_I3C_MRDATAB);
-
 		offset += count;
 	}
 
-	return 0;
+	return offset;
 }
 
 static int svc_i3c_master_write(struct svc_i3c_master *master,
@@ -949,7 +957,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
 static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 			       bool rnw, unsigned int xfer_type, u8 addr,
 			       u8 *in, const u8 *out, unsigned int xfer_len,
-			       unsigned int read_len, bool continued)
+			       unsigned int *read_len, bool continued)
 {
 	u32 reg;
 	int ret;
@@ -959,7 +967,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 	       SVC_I3C_MCTRL_IBIRESP_NACK |
 	       SVC_I3C_MCTRL_DIR(rnw) |
 	       SVC_I3C_MCTRL_ADDR(addr) |
-	       SVC_I3C_MCTRL_RDTERM(read_len),
+	       SVC_I3C_MCTRL_RDTERM(*read_len),
 	       master->regs + SVC_I3C_MCTRL);
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
@@ -971,17 +979,25 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
 		ret = svc_i3c_master_read(master, in, xfer_len);
 	else
 		ret = svc_i3c_master_write(master, out, xfer_len);
-	if (ret)
+	if (ret < 0)
 		goto emit_stop;
+	if (rnw)
+		*read_len = ret;
 
 	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
 				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
 	if (ret)
 		goto emit_stop;
+	writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
 
-	if (!continued)
+	if (!continued) {
 		svc_i3c_master_emit_stop(master);
 
+		/* Wait idle if stop is sent. */
+		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
+				   !SVC_I3C_MSTATUS_STATE(reg), 0, 1000);
+	}
+
 	return 0;
 
 emit_stop:
@@ -1039,12 +1055,15 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 	if (!xfer)
 		return;
 
+	svc_i3c_master_clear_merrwarn(master);
+	svc_i3c_master_flush_fifo(master);
+
 	for (i = 0; i < xfer->ncmds; i++) {
 		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
 
 		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
 					  cmd->addr, cmd->in, cmd->out,
-					  cmd->len, cmd->read_len,
+					  cmd->len, &cmd->read_len,
 					  cmd->continued);
 		if (ret)
 			break;
@@ -1173,6 +1192,9 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
 	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
 		svc_i3c_master_dequeue_xfer(master, xfer);
 
+	if (cmd->read_len != xfer_len)
+		ccc->dests[0].payload.len = cmd->read_len;
+
 	ret = xfer->ret;
 	svc_i3c_master_free_xfer(xfer);
 
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 5/8] i3c: master: svc: set ODSTOP to let I2C device see the STOP signal
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
                   ` (3 preceding siblings ...)
  2021-08-09  6:36 ` [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-09  6:36 ` [PATCH V4 6/8] i3c: master: svc: add runtime pm support Clark Wang
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

If using I2C/I3C mixed mode, need to set ODSTOP. Otherwise, the I2C
devices cannot see the stop signal. It may cause message sending errors.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V2/V3/V4: No change.
---
 drivers/i3c/master/svc-i3c-master.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index e767f307db2d..2a96b217cc78 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -477,7 +477,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	struct i3c_device_info info = {};
 	unsigned long fclk_rate, fclk_period_ns;
 	unsigned int high_period_ns, od_low_period_ns;
-	u32 ppbaud, pplow, odhpp, odbaud, i2cbaud, reg;
+	u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
 	int ret;
 
 	/* Timings derivation */
@@ -507,6 +507,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	switch (bus->mode) {
 	case I3C_BUS_MODE_PURE:
 		i2cbaud = 0;
+		odstop = 0;
 		break;
 	case I3C_BUS_MODE_MIXED_FAST:
 	case I3C_BUS_MODE_MIXED_LIMITED:
@@ -515,6 +516,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 		 * between the high and low period does not really matter.
 		 */
 		i2cbaud = DIV_ROUND_UP(1000, od_low_period_ns) - 2;
+		odstop = 1;
 		break;
 	case I3C_BUS_MODE_MIXED_SLOW:
 		/*
@@ -522,6 +524,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 		 * constraints as the FM+ mode.
 		 */
 		i2cbaud = DIV_ROUND_UP(2500, od_low_period_ns) - 2;
+		odstop = 1;
 		break;
 	default:
 		return -EINVAL;
@@ -530,7 +533,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	reg = SVC_I3C_MCONFIG_MASTER_EN |
 	      SVC_I3C_MCONFIG_DISTO(0) |
 	      SVC_I3C_MCONFIG_HKEEP(0) |
-	      SVC_I3C_MCONFIG_ODSTOP(0) |
+	      SVC_I3C_MCONFIG_ODSTOP(odstop) |
 	      SVC_I3C_MCONFIG_PPBAUD(ppbaud) |
 	      SVC_I3C_MCONFIG_PPLOW(pplow) |
 	      SVC_I3C_MCONFIG_ODBAUD(odbaud) |
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 6/8] i3c: master: svc: add runtime pm support
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
                   ` (4 preceding siblings ...)
  2021-08-09  6:36 ` [PATCH V4 5/8] i3c: master: svc: set ODSTOP to let I2C device see the STOP signal Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-16  6:59   ` Miquel Raynal
  2021-08-09  6:36 ` [PATCH V4 7/8] i3c: master: svc: add the missing module device table Clark Wang
  2021-08-09  6:36 ` [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function Clark Wang
  7 siblings, 1 reply; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

Add runtime pm support to dynamically manage the clock.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V4:
 - error path emit_stop only used in svc_i3c_master_do_daa() for one time, so
   remove it. Or it needs another goto command at the end of the function to
   skip it and to go to rpm_out.
V3:
 - restore the error path of probe function
 - enable runtime pm just before i3c module reset
V2: New patch in V2.
---
 drivers/i3c/master/svc-i3c-master.c | 154 ++++++++++++++++++++++++----
 1 file changed, 135 insertions(+), 19 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 2a96b217cc78..5939e0936697 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -17,7 +17,9 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 /* Master Mode Registers */
 #define SVC_I3C_MCONFIG      0x000
@@ -119,6 +121,7 @@
 #define   SVC_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7, 1), (x))
 
 #define SVC_I3C_MAX_DEVS 32
+#define SVC_I3C_PM_TIMEOUT_MS 1000
 
 /* This parameter depends on the implementation and may be tuned */
 #define SVC_I3C_FIFO_SIZE 16
@@ -480,10 +483,20 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
 	int ret;
 
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev,
+			"<%s> cannot resume i3c bus master, err: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
 	/* Timings derivation */
 	fclk_rate = clk_get_rate(master->fclk);
-	if (!fclk_rate)
-		return -EINVAL;
+	if (!fclk_rate) {
+		ret = -EINVAL;
+		goto rpm_out;
+	}
 
 	fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
 
@@ -527,7 +540,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 		odstop = 1;
 		break;
 	default:
-		return -EINVAL;
+		goto rpm_out;
 	}
 
 	reg = SVC_I3C_MCONFIG_MASTER_EN |
@@ -545,7 +558,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	/* Master core's registration */
 	ret = i3c_master_get_free_addr(m, 0);
 	if (ret < 0)
-		return ret;
+		goto rpm_out;
 
 	info.dyn_addr = ret;
 
@@ -554,21 +567,35 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 
 	ret = i3c_master_set_info(&master->base, &info);
 	if (ret)
-		return ret;
+		goto rpm_out;
 
 	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
 
-	return 0;
+rpm_out:
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
+
+	return ret;
 }
 
 static void svc_i3c_master_bus_cleanup(struct i3c_master_controller *m)
 {
 	struct svc_i3c_master *master = to_svc_i3c_master(m);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+		return;
+	}
 
 	svc_i3c_master_disable_interrupts(master);
 
 	/* Disable master */
 	writel(0, master->regs + SVC_I3C_MCONFIG);
+
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
 }
 
 static int svc_i3c_master_reserve_slot(struct svc_i3c_master *master)
@@ -867,31 +894,36 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
 	unsigned int dev_nb;
 	int ret, i;
 
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+		return ret;
+	}
+
 	spin_lock_irqsave(&master->xferqueue.lock, flags);
 	ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
 	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
-	if (ret)
-		goto emit_stop;
+	if (ret) {
+		svc_i3c_master_emit_stop(master);
+		svc_i3c_master_clear_merrwarn(master);
+		goto rpm_out;
+	}
 
 	/* Register all devices who participated to the core */
 	for (i = 0; i < dev_nb; i++) {
 		ret = i3c_master_add_i3c_dev_locked(m, addrs[i]);
 		if (ret)
-			return ret;
+			goto rpm_out;
 	}
 
 	/* Configure IBI auto-rules */
 	ret = svc_i3c_update_ibirules(master);
-	if (ret) {
+	if (ret)
 		dev_err(master->dev, "Cannot handle such a list of devices");
-		return ret;
-	}
 
-	return 0;
-
-emit_stop:
-	svc_i3c_master_emit_stop(master);
-	svc_i3c_master_clear_merrwarn(master);
+rpm_out:
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
 
 	return ret;
 }
@@ -1058,6 +1090,12 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 	if (!xfer)
 		return;
 
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+		return;
+	}
+
 	svc_i3c_master_clear_merrwarn(master);
 	svc_i3c_master_flush_fifo(master);
 
@@ -1072,6 +1110,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
 			break;
 	}
 
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
+
 	xfer->ret = ret;
 	complete(&xfer->comp);
 
@@ -1348,6 +1389,14 @@ static void svc_i3c_master_free_ibi(struct i3c_dev_desc *dev)
 static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
 {
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
+	struct svc_i3c_master *master = to_svc_i3c_master(m);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(master->dev);
+	if (ret < 0) {
+		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
+		return ret;
+	}
 
 	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
 }
@@ -1355,8 +1404,15 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
 static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
 {
 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
+	struct svc_i3c_master *master = to_svc_i3c_master(m);
+	int ret;
 
-	return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
+
+	pm_runtime_mark_last_busy(master->dev);
+	pm_runtime_put_autosuspend(master->dev);
+
+	return ret;
 }
 
 static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
@@ -1455,16 +1511,31 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, master);
 
+	pm_runtime_set_autosuspend_delay(&pdev->dev, SVC_I3C_PM_TIMEOUT_MS);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_get_noresume(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	svc_i3c_master_reset(master);
 
 	/* Register the master */
 	ret = i3c_master_register(&master->base, &pdev->dev,
 				  &svc_i3c_master_ops, false);
 	if (ret)
-		goto err_disable_sclk;
+		goto rpm_disable;
+
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_put_autosuspend(&pdev->dev);
 
 	return 0;
 
+rpm_disable:
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
 err_disable_sclk:
 	clk_disable_unprepare(master->sclk);
 
@@ -1486,13 +1557,57 @@ static int svc_i3c_master_remove(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	pm_runtime_dont_use_autosuspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
+{
+	struct svc_i3c_master *master = dev_get_drvdata(dev);
+
 	clk_disable_unprepare(master->pclk);
 	clk_disable_unprepare(master->fclk);
 	clk_disable_unprepare(master->sclk);
+	pinctrl_pm_select_sleep_state(dev);
 
 	return 0;
 }
 
+static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
+{
+	struct svc_i3c_master *master = dev_get_drvdata(dev);
+	int ret = 0;
+
+	pinctrl_pm_select_default_state(dev);
+	ret = clk_prepare_enable(master->pclk);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(master->fclk);
+	if (ret) {
+		clk_disable_unprepare(master->pclk);
+		return ret;
+	}
+
+	ret = clk_prepare_enable(master->sclk);
+	if (ret) {
+		clk_disable_unprepare(master->pclk);
+		clk_disable_unprepare(master->fclk);
+		return ret;
+	}
+
+	return ret;
+}
+
+static const struct dev_pm_ops svc_i3c_pm_ops = {
+	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				      pm_runtime_force_resume)
+	SET_RUNTIME_PM_OPS(svc_i3c_runtime_suspend,
+			   svc_i3c_runtime_resume, NULL)
+};
+
 static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
 	{ .compatible = "silvaco,i3c-master" },
 	{ /* sentinel */ },
@@ -1504,6 +1619,7 @@ static struct platform_driver svc_i3c_master = {
 	.driver = {
 		.name = "silvaco-i3c-master",
 		.of_match_table = svc_i3c_master_of_match_tbl,
+		.pm = &svc_i3c_pm_ops,
 	},
 };
 module_platform_driver(svc_i3c_master);
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 7/8] i3c: master: svc: add the missing module device table
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
                   ` (5 preceding siblings ...)
  2021-08-09  6:36 ` [PATCH V4 6/8] i3c: master: svc: add runtime pm support Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-16  7:00   ` Miquel Raynal
  2021-08-09  6:36 ` [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function Clark Wang
  7 siblings, 1 reply; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

The missing MODULE_DEVICE_TABLE() will cause the svc-i3c-master cannot
be auto probed when it is built in moudle.
So add it.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
Reviewed-by: Jun Li <jun.li@nxp.com>
---
V4: New patch in this patchset
---
 drivers/i3c/master/svc-i3c-master.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 5939e0936697..77f67d407acd 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -1612,6 +1612,7 @@ static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
 	{ .compatible = "silvaco,i3c-master" },
 	{ /* sentinel */ },
 };
+MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
 
 static struct platform_driver svc_i3c_master = {
 	.probe = svc_i3c_master_probe,
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function
  2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
                   ` (6 preceding siblings ...)
  2021-08-09  6:36 ` [PATCH V4 7/8] i3c: master: svc: add the missing module device table Clark Wang
@ 2021-08-09  6:36 ` Clark Wang
  2021-08-16  7:02   ` Miquel Raynal
  7 siblings, 1 reply; 14+ messages in thread
From: Clark Wang @ 2021-08-09  6:36 UTC (permalink / raw)
  To: miquel.raynal, conor.culhane, alexandre.belloni
  Cc: linux-i3c, linux-kernel, linux-imx

If enable interrupt in the svc_i3c_master_bus_init() but do not call
enable ibi in the device driver, it will cause a kernel dump in the
svc_i3c_master_handle_ibi() when a slave start occurs on the i3c bus,
because the data->ibi_pool is not initialized.
So only enable the interrupt in svc_i3c_master_enable_ibi() function.

Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
---
V4: New patch in this patchset
---
 drivers/i3c/master/svc-i3c-master.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
index 77f67d407acd..b802afd8eb7d 100644
--- a/drivers/i3c/master/svc-i3c-master.c
+++ b/drivers/i3c/master/svc-i3c-master.c
@@ -569,8 +569,6 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
 	if (ret)
 		goto rpm_out;
 
-	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
-
 rpm_out:
 	pm_runtime_mark_last_busy(master->dev);
 	pm_runtime_put_autosuspend(master->dev);
@@ -1398,6 +1396,8 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
 		return ret;
 	}
 
+	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
+
 	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
 }
 
@@ -1407,6 +1407,8 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
 	struct svc_i3c_master *master = to_svc_i3c_master(m);
 	int ret;
 
+	svc_i3c_master_disable_interrupts(master);
+
 	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
 
 	pm_runtime_mark_last_busy(master->dev);
-- 
2.25.1


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function
  2021-08-09  6:36 ` [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function Clark Wang
@ 2021-08-16  6:52   ` Miquel Raynal
  0 siblings, 0 replies; 14+ messages in thread
From: Miquel Raynal @ 2021-08-16  6:52 UTC (permalink / raw)
  To: Clark Wang
  Cc: conor.culhane, alexandre.belloni, linux-i3c, linux-kernel, linux-imx

Hi Clark,

Clark Wang <xiaoning.wang@nxp.com> wrote on Mon,  9 Aug 2021 14:36:40
+0800:

> Sometimes only need to reset err and fifo regs, so split the origin
> reset function to three functions.
> Put them at the top of the file, to let more functions can call them.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Reviewed-by: Jun Li <jun.li@nxp.com>

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>

Thanks,
Miquèl

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH V4 6/8] i3c: master: svc: add runtime pm support
  2021-08-09  6:36 ` [PATCH V4 6/8] i3c: master: svc: add runtime pm support Clark Wang
@ 2021-08-16  6:59   ` Miquel Raynal
  0 siblings, 0 replies; 14+ messages in thread
From: Miquel Raynal @ 2021-08-16  6:59 UTC (permalink / raw)
  To: Clark Wang
  Cc: conor.culhane, alexandre.belloni, linux-i3c, linux-kernel, linux-imx

Hi Clark,

Clark Wang <xiaoning.wang@nxp.com> wrote on Mon,  9 Aug 2021 14:36:43
+0800:

> Add runtime pm support to dynamically manage the clock.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Reviewed-by: Jun Li <jun.li@nxp.com>
> ---
> V4:
>  - error path emit_stop only used in svc_i3c_master_do_daa() for one time, so
>    remove it. Or it needs another goto command at the end of the function to
>    skip it and to go to rpm_out.
> V3:
>  - restore the error path of probe function
>  - enable runtime pm just before i3c module reset
> V2: New patch in V2.
> ---
>  drivers/i3c/master/svc-i3c-master.c | 154 ++++++++++++++++++++++++----
>  1 file changed, 135 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 2a96b217cc78..5939e0936697 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -17,7 +17,9 @@
>  #include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
> +#include <linux/pinctrl/consumer.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
>  
>  /* Master Mode Registers */
>  #define SVC_I3C_MCONFIG      0x000
> @@ -119,6 +121,7 @@
>  #define   SVC_MDYNADDR_ADDR(x) FIELD_PREP(GENMASK(7, 1), (x))
>  
>  #define SVC_I3C_MAX_DEVS 32
> +#define SVC_I3C_PM_TIMEOUT_MS 1000
>  
>  /* This parameter depends on the implementation and may be tuned */
>  #define SVC_I3C_FIFO_SIZE 16
> @@ -480,10 +483,20 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
>  	u32 ppbaud, pplow, odhpp, odbaud, odstop, i2cbaud, reg;
>  	int ret;
>  
> +	ret = pm_runtime_resume_and_get(master->dev);
> +	if (ret < 0) {
> +		dev_err(master->dev,
> +			"<%s> cannot resume i3c bus master, err: %d\n",
> +			__func__, ret);
> +		return ret;
> +	}
> +
>  	/* Timings derivation */
>  	fclk_rate = clk_get_rate(master->fclk);
> -	if (!fclk_rate)
> -		return -EINVAL;
> +	if (!fclk_rate) {
> +		ret = -EINVAL;
> +		goto rpm_out;
> +	}
>  
>  	fclk_period_ns = DIV_ROUND_UP(1000000000, fclk_rate);
>  
> @@ -527,7 +540,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
>  		odstop = 1;
>  		break;
>  	default:
> -		return -EINVAL;
> +		goto rpm_out;
>  	}
>  
>  	reg = SVC_I3C_MCONFIG_MASTER_EN |
> @@ -545,7 +558,7 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
>  	/* Master core's registration */
>  	ret = i3c_master_get_free_addr(m, 0);
>  	if (ret < 0)
> -		return ret;
> +		goto rpm_out;
>  
>  	info.dyn_addr = ret;
>  
> @@ -554,21 +567,35 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
>  
>  	ret = i3c_master_set_info(&master->base, &info);
>  	if (ret)
> -		return ret;
> +		goto rpm_out;
>  
>  	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
>  
> -	return 0;
> +rpm_out:
> +	pm_runtime_mark_last_busy(master->dev);
> +	pm_runtime_put_autosuspend(master->dev);
> +
> +	return ret;
>  }
>  
>  static void svc_i3c_master_bus_cleanup(struct i3c_master_controller *m)
>  {
>  	struct svc_i3c_master *master = to_svc_i3c_master(m);
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(master->dev);
> +	if (ret < 0) {
> +		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
> +		return;
> +	}
>  
>  	svc_i3c_master_disable_interrupts(master);
>  
>  	/* Disable master */
>  	writel(0, master->regs + SVC_I3C_MCONFIG);
> +
> +	pm_runtime_mark_last_busy(master->dev);
> +	pm_runtime_put_autosuspend(master->dev);
>  }
>  
>  static int svc_i3c_master_reserve_slot(struct svc_i3c_master *master)
> @@ -867,31 +894,36 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
>  	unsigned int dev_nb;
>  	int ret, i;
>  
> +	ret = pm_runtime_resume_and_get(master->dev);
> +	if (ret < 0) {
> +		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
> +		return ret;
> +	}
> +
>  	spin_lock_irqsave(&master->xferqueue.lock, flags);
>  	ret = svc_i3c_master_do_daa_locked(master, addrs, &dev_nb);
>  	spin_unlock_irqrestore(&master->xferqueue.lock, flags);
> -	if (ret)
> -		goto emit_stop;
> +	if (ret) {
> +		svc_i3c_master_emit_stop(master);
> +		svc_i3c_master_clear_merrwarn(master);
> +		goto rpm_out;
> +	}
>  
>  	/* Register all devices who participated to the core */
>  	for (i = 0; i < dev_nb; i++) {
>  		ret = i3c_master_add_i3c_dev_locked(m, addrs[i]);
>  		if (ret)
> -			return ret;
> +			goto rpm_out;
>  	}
>  
>  	/* Configure IBI auto-rules */
>  	ret = svc_i3c_update_ibirules(master);
> -	if (ret) {
> +	if (ret)
>  		dev_err(master->dev, "Cannot handle such a list of devices");
> -		return ret;
> -	}
>  
> -	return 0;
> -
> -emit_stop:
> -	svc_i3c_master_emit_stop(master);
> -	svc_i3c_master_clear_merrwarn(master);
> +rpm_out:
> +	pm_runtime_mark_last_busy(master->dev);
> +	pm_runtime_put_autosuspend(master->dev);
>  
>  	return ret;
>  }
> @@ -1058,6 +1090,12 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
>  	if (!xfer)
>  		return;
>  
> +	ret = pm_runtime_resume_and_get(master->dev);
> +	if (ret < 0) {
> +		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
> +		return;
> +	}
> +
>  	svc_i3c_master_clear_merrwarn(master);
>  	svc_i3c_master_flush_fifo(master);
>  
> @@ -1072,6 +1110,9 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
>  			break;
>  	}
>  
> +	pm_runtime_mark_last_busy(master->dev);
> +	pm_runtime_put_autosuspend(master->dev);
> +
>  	xfer->ret = ret;
>  	complete(&xfer->comp);
>  
> @@ -1348,6 +1389,14 @@ static void svc_i3c_master_free_ibi(struct i3c_dev_desc *dev)
>  static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
>  {
>  	struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +	struct svc_i3c_master *master = to_svc_i3c_master(m);
> +	int ret;
> +
> +	ret = pm_runtime_resume_and_get(master->dev);
> +	if (ret < 0) {
> +		dev_err(master->dev, "<%s> Cannot get runtime PM.\n", __func__);
> +		return ret;
> +	}
>  
>  	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
>  }
> @@ -1355,8 +1404,15 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
>  static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
>  {
>  	struct i3c_master_controller *m = i3c_dev_get_master(dev);
> +	struct svc_i3c_master *master = to_svc_i3c_master(m);
> +	int ret;
>  
> -	return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> +	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
> +
> +	pm_runtime_mark_last_busy(master->dev);
> +	pm_runtime_put_autosuspend(master->dev);
> +
> +	return ret;
>  }
>  
>  static void svc_i3c_master_recycle_ibi_slot(struct i3c_dev_desc *dev,
> @@ -1455,16 +1511,31 @@ static int svc_i3c_master_probe(struct platform_device *pdev)
>  
>  	platform_set_drvdata(pdev, master);
>  
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, SVC_I3C_PM_TIMEOUT_MS);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_runtime_get_noresume(&pdev->dev);
> +	pm_runtime_set_active(&pdev->dev);
> +	pm_runtime_enable(&pdev->dev);
> +
>  	svc_i3c_master_reset(master);
>  
>  	/* Register the master */
>  	ret = i3c_master_register(&master->base, &pdev->dev,
>  				  &svc_i3c_master_ops, false);
>  	if (ret)
> -		goto err_disable_sclk;
> +		goto rpm_disable;
> +
> +	pm_runtime_mark_last_busy(&pdev->dev);
> +	pm_runtime_put_autosuspend(&pdev->dev);
>  
>  	return 0;
>  
> +rpm_disable:
> +	pm_runtime_dont_use_autosuspend(&pdev->dev);
> +	pm_runtime_put_noidle(&pdev->dev);
> +	pm_runtime_set_suspended(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
>  err_disable_sclk:
>  	clk_disable_unprepare(master->sclk);
>  
> @@ -1486,13 +1557,57 @@ static int svc_i3c_master_remove(struct platform_device *pdev)
>  	if (ret)
>  		return ret;
>  
> +	pm_runtime_dont_use_autosuspend(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused svc_i3c_runtime_suspend(struct device *dev)
> +{
> +	struct svc_i3c_master *master = dev_get_drvdata(dev);
> +
>  	clk_disable_unprepare(master->pclk);
>  	clk_disable_unprepare(master->fclk);
>  	clk_disable_unprepare(master->sclk);
> +	pinctrl_pm_select_sleep_state(dev);
>  
>  	return 0;
>  }
>  
> +static int __maybe_unused svc_i3c_runtime_resume(struct device *dev)
> +{
> +	struct svc_i3c_master *master = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	pinctrl_pm_select_default_state(dev);
> +	ret = clk_prepare_enable(master->pclk);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_prepare_enable(master->fclk);
> +	if (ret) {
> +		clk_disable_unprepare(master->pclk);
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(master->sclk);
> +	if (ret) {
> +		clk_disable_unprepare(master->pclk);
> +		clk_disable_unprepare(master->fclk);
> +		return ret;
> +	}
> +
> +	return ret;
> +}

Can you create svc_i3c_master_prepare_clks() with the current content
of the above helper, then, use that from _runtime_resume() and _probe()
to avoid copying these lines?

With this change you can add my 

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>

> +
> +static const struct dev_pm_ops svc_i3c_pm_ops = {
> +	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +				      pm_runtime_force_resume)
> +	SET_RUNTIME_PM_OPS(svc_i3c_runtime_suspend,
> +			   svc_i3c_runtime_resume, NULL)
> +};
> +
>  static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
>  	{ .compatible = "silvaco,i3c-master" },
>  	{ /* sentinel */ },
> @@ -1504,6 +1619,7 @@ static struct platform_driver svc_i3c_master = {
>  	.driver = {
>  		.name = "silvaco-i3c-master",
>  		.of_match_table = svc_i3c_master_of_match_tbl,
> +		.pm = &svc_i3c_pm_ops,
>  	},
>  };
>  module_platform_driver(svc_i3c_master);


Thanks,
Miquèl

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH V4 7/8] i3c: master: svc: add the missing module device table
  2021-08-09  6:36 ` [PATCH V4 7/8] i3c: master: svc: add the missing module device table Clark Wang
@ 2021-08-16  7:00   ` Miquel Raynal
  0 siblings, 0 replies; 14+ messages in thread
From: Miquel Raynal @ 2021-08-16  7:00 UTC (permalink / raw)
  To: Clark Wang
  Cc: conor.culhane, alexandre.belloni, linux-i3c, linux-kernel, linux-imx

Hi Clark,

Clark Wang <xiaoning.wang@nxp.com> wrote on Mon,  9 Aug 2021 14:36:44
+0800:

> The missing MODULE_DEVICE_TABLE() will cause the svc-i3c-master cannot
> be auto probed when it is built in moudle.
> So add it.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Reviewed-by: Jun Li <jun.li@nxp.com>

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>

> ---
> V4: New patch in this patchset
> ---
>  drivers/i3c/master/svc-i3c-master.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 5939e0936697..77f67d407acd 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -1612,6 +1612,7 @@ static const struct of_device_id svc_i3c_master_of_match_tbl[] = {
>  	{ .compatible = "silvaco,i3c-master" },
>  	{ /* sentinel */ },
>  };
> +MODULE_DEVICE_TABLE(of, svc_i3c_master_of_match_tbl);
>  
>  static struct platform_driver svc_i3c_master = {
>  	.probe = svc_i3c_master_probe,

Thanks,
Miquèl

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function
  2021-08-09  6:36 ` [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function Clark Wang
@ 2021-08-16  7:02   ` Miquel Raynal
  0 siblings, 0 replies; 14+ messages in thread
From: Miquel Raynal @ 2021-08-16  7:02 UTC (permalink / raw)
  To: Clark Wang
  Cc: conor.culhane, alexandre.belloni, linux-i3c, linux-kernel, linux-imx

Hi Clark,

Clark Wang <xiaoning.wang@nxp.com> wrote on Mon,  9 Aug 2021 14:36:45
+0800:

> If enable interrupt in the svc_i3c_master_bus_init() but do not call
> enable ibi in the device driver, it will cause a kernel dump in the
> svc_i3c_master_handle_ibi() when a slave start occurs on the i3c bus,
> because the data->ibi_pool is not initialized.
> So only enable the interrupt in svc_i3c_master_enable_ibi() function.

Is this situation actually happening or is this purely theoretical?

Anyway this doesn't hurt so:

Reviewed-by: Miquel Raynal <miquel.raynal@bootlin.com>

> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> ---
> V4: New patch in this patchset
> ---
>  drivers/i3c/master/svc-i3c-master.c | 6 ++++--
>  1 file changed, 4 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 77f67d407acd..b802afd8eb7d 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -569,8 +569,6 @@ static int svc_i3c_master_bus_init(struct i3c_master_controller *m)
>  	if (ret)
>  		goto rpm_out;
>  
> -	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
> -
>  rpm_out:
>  	pm_runtime_mark_last_busy(master->dev);
>  	pm_runtime_put_autosuspend(master->dev);
> @@ -1398,6 +1396,8 @@ static int svc_i3c_master_enable_ibi(struct i3c_dev_desc *dev)
>  		return ret;
>  	}
>  
> +	svc_i3c_master_enable_interrupts(master, SVC_I3C_MINT_SLVSTART);
> +
>  	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
>  }
>  
> @@ -1407,6 +1407,8 @@ static int svc_i3c_master_disable_ibi(struct i3c_dev_desc *dev)
>  	struct svc_i3c_master *master = to_svc_i3c_master(m);
>  	int ret;
>  
> +	svc_i3c_master_disable_interrupts(master);
> +
>  	ret = i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
>  
>  	pm_runtime_mark_last_busy(master->dev);

Thanks,
Miquèl

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data
  2021-08-09  6:36 ` [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data Clark Wang
@ 2021-08-16  7:15   ` Miquel Raynal
  0 siblings, 0 replies; 14+ messages in thread
From: Miquel Raynal @ 2021-08-16  7:15 UTC (permalink / raw)
  To: Clark Wang
  Cc: conor.culhane, alexandre.belloni, linux-i3c, linux-kernel, linux-imx

Hi Clark,

Just a few nitpicks.

Clark Wang <xiaoning.wang@nxp.com> wrote on Mon,  9 Aug 2021 14:36:41
+0800:

> When i3c controller reads data from slave device, slave device can stop
> returning data with an ACK after any byte.
> Add this support for svc i3c controller. Otherwise, it will go TIMEOUT

"it will timeout when the slave..."

> error path when the slave device ends the read operation early.
> 
> Signed-off-by: Clark Wang <xiaoning.wang@nxp.com>
> Reviewed-by: Jun Li <jun.li@nxp.com>
> ---
> V4 changes:
>  - Only use COMPLETE flag in the read function.
>  - Add a reset before each transfer.
> V2/V3: No change.
> ---
>  drivers/i3c/master/svc-i3c-master.c | 56 ++++++++++++++++++++---------
>  1 file changed, 39 insertions(+), 17 deletions(-)
> 
> diff --git a/drivers/i3c/master/svc-i3c-master.c b/drivers/i3c/master/svc-i3c-master.c
> index 4e69c691253d..e767f307db2d 100644
> --- a/drivers/i3c/master/svc-i3c-master.c
> +++ b/drivers/i3c/master/svc-i3c-master.c
> @@ -896,27 +896,35 @@ static int svc_i3c_master_do_daa(struct i3c_master_controller *m)
>  static int svc_i3c_master_read(struct svc_i3c_master *master,
>  			       u8 *in, unsigned int len)
>  {
> -	int offset = 0, i, ret;
> -	u32 mdctrl;
> +	int offset = 0, i;
> +	u32 mdctrl, mstatus;
> +	bool completed = false;
> +	unsigned int count;
> +	unsigned long start = jiffies;
>  
> -	while (offset < len) {
> -		unsigned int count;
> +	while (!completed) {
> +		mstatus = readl(master->regs + SVC_I3C_MSTATUS);
> +		if (SVC_I3C_MSTATUS_COMPLETE(mstatus) != 0)
> +			completed = true;
>  
> -		ret = readl_poll_timeout(master->regs + SVC_I3C_MDATACTRL,
> -					 mdctrl,
> -					 !(mdctrl & SVC_I3C_MDATACTRL_RXEMPTY),
> -					 0, 1000);
> -		if (ret)
> -			return ret;
> +		if (time_after(jiffies, start + msecs_to_jiffies(1000))) {
> +			dev_dbg(master->dev, "<%s> timeout\n", __func__);

Can you get rid of this <%s>/__func__ parameter and just tell that we
got a read timeout?

> +			return -ETIMEDOUT;
> +		}
>  
> +		mdctrl = readl(master->regs + SVC_I3C_MDATACTRL);
>  		count = SVC_I3C_MDATACTRL_RXCOUNT(mdctrl);
> +		if (offset + count > len) {
> +			dev_err(master->dev, "<%s> recv len too long!\n",
> +				__func__);
> +			return -EINVAL;
> +		}

And is this something theoretical or a real error that can happen?

If you think this is really needed, please keep the same dev_err()
formatting as in the other places in this driver.

>  		for (i = 0; i < count; i++)
>  			in[offset + i] = readl(master->regs + SVC_I3C_MRDATAB);
> -

Please keep that space

>  		offset += count;
>  	}
>  
> -	return 0;
> +	return offset;
>  }
>  
>  static int svc_i3c_master_write(struct svc_i3c_master *master,
> @@ -949,7 +957,7 @@ static int svc_i3c_master_write(struct svc_i3c_master *master,
>  static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  			       bool rnw, unsigned int xfer_type, u8 addr,
>  			       u8 *in, const u8 *out, unsigned int xfer_len,
> -			       unsigned int read_len, bool continued)
> +			       unsigned int *read_len, bool continued)
>  {
>  	u32 reg;
>  	int ret;
> @@ -959,7 +967,7 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  	       SVC_I3C_MCTRL_IBIRESP_NACK |
>  	       SVC_I3C_MCTRL_DIR(rnw) |
>  	       SVC_I3C_MCTRL_ADDR(addr) |
> -	       SVC_I3C_MCTRL_RDTERM(read_len),
> +	       SVC_I3C_MCTRL_RDTERM(*read_len),
>  	       master->regs + SVC_I3C_MCTRL);
>  
>  	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
> @@ -971,17 +979,25 @@ static int svc_i3c_master_xfer(struct svc_i3c_master *master,
>  		ret = svc_i3c_master_read(master, in, xfer_len);
>  	else
>  		ret = svc_i3c_master_write(master, out, xfer_len);
> -	if (ret)
> +	if (ret < 0)
>  		goto emit_stop;

Space?

> +	if (rnw)
> +		*read_len = ret;
>  
>  	ret = readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
>  				 SVC_I3C_MSTATUS_COMPLETE(reg), 0, 1000);
>  	if (ret)
>  		goto emit_stop;

Space?

> +	writel(SVC_I3C_MINT_COMPLETE, master->regs + SVC_I3C_MSTATUS);
>  
> -	if (!continued)
> +	if (!continued) {
>  		svc_i3c_master_emit_stop(master);
>  
> +		/* Wait idle if stop is sent. */
> +		readl_poll_timeout(master->regs + SVC_I3C_MSTATUS, reg,
> +				   !SVC_I3C_MSTATUS_STATE(reg), 0, 1000);

Please use _MSTATUS_STATE_IDLE() for more clarity.

If this wait loop is really needed, better adding it to
_master_emit_stop()? (in another patch)

> +	}
> +
>  	return 0;
>  
>  emit_stop:
> @@ -1039,12 +1055,15 @@ static void svc_i3c_master_start_xfer_locked(struct svc_i3c_master *master)
>  	if (!xfer)
>  		return;
>  
> +	svc_i3c_master_clear_merrwarn(master);
> +	svc_i3c_master_flush_fifo(master);
> +
>  	for (i = 0; i < xfer->ncmds; i++) {
>  		struct svc_i3c_cmd *cmd = &xfer->cmds[i];
>  
>  		ret = svc_i3c_master_xfer(master, cmd->rnw, xfer->type,
>  					  cmd->addr, cmd->in, cmd->out,
> -					  cmd->len, cmd->read_len,
> +					  cmd->len, &cmd->read_len,
>  					  cmd->continued);
>  		if (ret)
>  			break;
> @@ -1173,6 +1192,9 @@ static int svc_i3c_master_send_direct_ccc_cmd(struct svc_i3c_master *master,
>  	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
>  		svc_i3c_master_dequeue_xfer(master, xfer);
>  
> +	if (cmd->read_len != xfer_len)
> +		ccc->dests[0].payload.len = cmd->read_len;
> +
>  	ret = xfer->ret;
>  	svc_i3c_master_free_xfer(xfer);
>  

Thanks,
Miquèl

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2021-08-16  7:15 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-09  6:36 [PATCH V4 0/8] i3c: master: svc: some bug fixes and add runtime pm support Clark Wang
2021-08-09  6:36 ` [PATCH V4 1/8] i3c: master: svc: move module reset behind clk enable Clark Wang
2021-08-09  6:36 ` [PATCH V4 2/8] i3c: master: svc: fix atomic issue Clark Wang
2021-08-09  6:36 ` [PATCH V4 3/8] i3c: master: svc: separate err, fifo and disable interrupt of reset function Clark Wang
2021-08-16  6:52   ` Miquel Raynal
2021-08-09  6:36 ` [PATCH V4 4/8] i3c: master: svc: add support for slave to stop returning data Clark Wang
2021-08-16  7:15   ` Miquel Raynal
2021-08-09  6:36 ` [PATCH V4 5/8] i3c: master: svc: set ODSTOP to let I2C device see the STOP signal Clark Wang
2021-08-09  6:36 ` [PATCH V4 6/8] i3c: master: svc: add runtime pm support Clark Wang
2021-08-16  6:59   ` Miquel Raynal
2021-08-09  6:36 ` [PATCH V4 7/8] i3c: master: svc: add the missing module device table Clark Wang
2021-08-16  7:00   ` Miquel Raynal
2021-08-09  6:36 ` [PATCH V4 8/8] i3c: master: svc: enable the interrupt in the enable ibi function Clark Wang
2021-08-16  7:02   ` Miquel Raynal

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).