linux-i3c.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: Przemyslaw Gaj <pgaj@cadence.com>
To: <bbrezillon@kernel.org>, <linux-i3c@lists.infradead.org>
Cc: Przemyslaw Gaj <pgaj@cadence.com>,
	psroka@cadence.com, rafalc@cadence.com,
	vitor.soares@synopsys.com
Subject: [PATCH 2/2] i3c: master: cdns: Add support for HDR-DDR mode
Date: Thu, 13 Dec 2018 12:18:32 +0000	[thread overview]
Message-ID: <adcd3c336c8285483b1741b145cad945bc813ce9.1544702829.git.pgaj@cadence.com> (raw)
In-Reply-To: <cover.1544702829.git.pgaj@cadence.com>

Cadence I3C master controller HDR-DDR mode support.

This feature was originally created by Boris Brezillon
<boris.brezillon@bootlin.com>. I made some changes/fixes.

Signed-off-by: Przemyslaw Gaj <pgaj@cadence.com>
---
 drivers/i3c/master/i3c-master-cdns.c | 195 ++++++++++++++++++++++++++++++++++-
 1 file changed, 193 insertions(+), 2 deletions(-)

diff --git a/drivers/i3c/master/i3c-master-cdns.c b/drivers/i3c/master/i3c-master-cdns.c
index a33f3a6..b1a97be 100644
--- a/drivers/i3c/master/i3c-master-cdns.c
+++ b/drivers/i3c/master/i3c-master-cdns.c
@@ -571,7 +571,7 @@ static void cdns_i3c_master_end_xfer_locked(struct cdns_i3c_master *master,
 	     !(status0 & MST_STATUS0_CMDR_EMP);
 	     status0 = readl(master->regs + MST_STATUS0)) {
 		struct cdns_i3c_cmd *cmd;
-		u32 cmdr, rx_len, id;
+		u32 cmdr, rx_len, id, xfer_bytes;
 
 		cmdr = readl(master->regs + CMDR);
 		id = CMDR_CMDID(cmdr);
@@ -581,7 +581,11 @@ static void cdns_i3c_master_end_xfer_locked(struct cdns_i3c_master *master,
 			continue;
 
 		cmd = &xfer->cmds[CMDR_CMDID(cmdr)];
-		rx_len = min_t(u32, CMDR_XFER_BYTES(cmdr), cmd->rx_len);
+		xfer_bytes = CMDR_XFER_BYTES(cmdr);
+		if(cmd->cmd0 & CMD0_FIFO_IS_DDR)
+			xfer_bytes = xfer_bytes * 4;
+		rx_len = min_t(u32, xfer_bytes, cmd->rx_len);
+
 		cdns_i3c_master_rd_from_rx_fifo(master, cmd->rx_buf, rx_len);
 		cmd->error = CMDR_ERROR(cmdr);
 	}
@@ -893,6 +897,192 @@ static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
 	return ret;
 }
 
+#define I3C_DDR_FIRST_DATA_WORD_PREAMBLE	0x2
+#define I3C_DDR_DATA_WORD_PREAMBLE		0x3
+
+#define I3C_DDR_PREAMBLE(p)			((p) << 18)
+
+static u32 prepare_ddr_word(u16 payload)
+{
+	u32 ret;
+	u16 pb;
+
+	ret = (u32)payload << 2;
+
+	/* Calculate parity. */
+	pb = (payload >> 15) ^ (payload >> 13) ^ (payload >> 11) ^
+	     (payload >> 9) ^ (payload >> 7) ^ (payload >> 5) ^
+	     (payload >> 3) ^ (payload >> 1);
+	ret |= (pb & 1) << 1;
+	pb = (payload >> 14) ^ (payload >> 12) ^ (payload >> 10) ^
+	     (payload >> 8) ^ (payload >> 6) ^ (payload >> 4) ^
+	     (payload >> 2) ^ payload ^ 1;
+	ret |= (pb & 1);
+
+	return ret;
+}
+
+static u32 prepare_ddr_data_word(u16 data, bool first)
+{
+	return prepare_ddr_word(data) |
+	       I3C_DDR_PREAMBLE(first ?
+			        I3C_DDR_FIRST_DATA_WORD_PREAMBLE :
+				I3C_DDR_DATA_WORD_PREAMBLE);
+}
+
+#define I3C_DDR_READ_CMD	BIT(15)
+
+static u32 prepare_ddr_cmd_word(u16 cmd)
+{
+	return prepare_ddr_word(cmd) | I3C_DDR_PREAMBLE(1);
+}
+
+static u32 prepare_ddr_crc_word(u8 crc5)
+{
+	return (((u32)crc5 & 0x1f) << 9) | (0xc << 14) |
+	       I3C_DDR_PREAMBLE(1);
+}
+
+static u32 prepare_ddr_parity_bit(u32 cmdword)
+{
+	u16 pb;
+
+	pb = (cmdword >> 14) ^ (cmdword >> 12) ^ (cmdword >> 10) ^
+	     (cmdword >> 8) ^ (cmdword >> 6) ^ (cmdword >> 4) ^
+	     (cmdword >> 2);
+
+	if (pb & 1)
+		cmdword |= BIT(0);
+
+	return cmdword;
+}
+
+static u8 update_crc5(u8 crc5, u16 word)
+{
+	u8 crc0;
+	int i;
+
+	/*
+	 * crc0 = next_data_bit ^ crc[4]
+	 *                1         2            3       4
+	 * crc[4:0] = { crc[3:2], crc[1]^crc0, crc[0], crc0 }
+	 */
+	for (i = 15; i >= 0; --i) {
+		crc0 = ((word >> i) ^ (crc5 >> 4)) & 0x1;
+		crc5 = ((crc5 << 1) & 0x1a) |
+		       (((crc5 >> 1) ^ crc0) << 2) |
+		       crc0;
+	}
+
+	return crc5 & 0x1f;
+}
+
+static int cdns_i3c_master_send_hdr_cmd(struct i3c_dev_desc *dev,
+					const struct i3c_hdr_cmd *cmds,
+					int ncmds)
+{
+	struct i3c_master_controller *m = i3c_dev_get_master(dev);
+	struct cdns_i3c_master *master = to_cdns_i3c_master(m);
+	int ret, i, ntxwords = 1, nrxwords = 0;
+	struct cdns_i3c_xfer *xfer;
+	struct cdns_i3c_cmd *ccmd;
+	u16 cmdword, datain;
+	u32 checkword, word;
+	u32 *buf = NULL;
+	u8 crc5;
+
+	if (ncmds < 1)
+		return 0;
+
+	if (ncmds > 1 || cmds[0].ndatawords > CMD0_FIFO_PL_LEN_MAX)
+		return -ENOTSUPP;
+
+	if (cmds[0].mode != I3C_HDR_DDR)
+		return -ENOTSUPP;
+
+	cmdword = ((u16)cmds[0].code << 8) | (dev->info.dyn_addr << 1);
+	if (cmdword & I3C_DDR_READ_CMD)
+		nrxwords += cmds[0].ndatawords + 1;
+	else
+		ntxwords += cmds[0].ndatawords + 1;
+
+	if (ntxwords > master->caps.txfifodepth ||
+	    nrxwords > master->caps.rxfifodepth)
+		return -ENOTSUPP;
+
+	buf = kzalloc((nrxwords + ntxwords) * sizeof(*buf), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	xfer = cdns_i3c_master_alloc_xfer(master, 2);
+	if (!xfer) {
+		ret = -ENOMEM;
+		goto out_free_buf;
+	}
+
+	ccmd = &xfer->cmds[0];
+	ccmd->cmd1 = CMD1_FIFO_CCC(I3C_CCC_ENTHDR(0));
+	ccmd->cmd0 = CMD0_FIFO_IS_CCC;
+
+	ccmd = &xfer->cmds[1];
+
+	if (cmdword & I3C_DDR_READ_CMD)
+		cmdword = prepare_ddr_parity_bit(cmdword);
+
+	ccmd->tx_len = ntxwords * sizeof(u32);
+	ccmd->tx_buf = buf;
+	ccmd->rx_len = nrxwords * sizeof(u32);
+	ccmd->rx_buf = buf + ntxwords;
+
+	buf[0] = prepare_ddr_cmd_word(cmdword);
+	crc5 = update_crc5(0x1f, cmdword);
+	for (i = 0; i < ntxwords - 2; i++) {
+		crc5 = update_crc5(crc5, cmds[0].data.out[i]);
+		buf[i + 1] = prepare_ddr_data_word(cmds[0].data.out[i], !i);
+	}
+
+	if(!(cmdword & I3C_DDR_READ_CMD))
+		buf[ntxwords-1] = prepare_ddr_crc_word(crc5);
+
+	ccmd->cmd0 = CMD0_FIFO_IS_DDR | CMD0_FIFO_PL_LEN(ntxwords);
+
+	cdns_i3c_master_queue_xfer(master, xfer);
+	if (!wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)))
+		cdns_i3c_master_unqueue_xfer(master, xfer);
+
+	ret = xfer->ret;
+
+	if (!xfer->ret && nrxwords) {
+		for (i = 0; i < nrxwords - 1; i++) {
+			word = (((u32 *)ccmd->rx_buf)[i] & GENMASK(19, 0));
+			datain = (word >> 2) & GENMASK(15, 0);
+			checkword = prepare_ddr_data_word(datain, !i);
+
+			if (checkword != word) {
+				ret = -EIO;
+				break;
+			}
+
+			crc5 = update_crc5(crc5, datain);
+			cmds[0].data.in[i] = datain;
+		}
+
+		word = (((u32 *)ccmd->rx_buf)[i] & GENMASK(19, 9));
+		datain = (word >> 2) & GENMASK(15, 0);
+		checkword = prepare_ddr_crc_word(crc5);
+
+		if (checkword != word)
+			ret = -EIO;
+	}
+
+	cdns_i3c_master_free_xfer(xfer);
+
+out_free_buf:
+	kfree(buf);
+
+	return ret;
+}
+
 static int cdns_i3c_master_i2c_xfers(struct i2c_dev_desc *dev,
 				     const struct i2c_msg *xfers, int nxfers)
 {
@@ -1714,6 +1904,7 @@ static const struct i3c_master_controller_ops cdns_i3c_master_ops = {
 	.supports_ccc_cmd = cdns_i3c_master_supports_ccc_cmd,
 	.send_ccc_cmd = cdns_i3c_master_send_ccc_cmd,
 	.priv_xfers = cdns_i3c_master_priv_xfers,
+	.send_hdr_cmds = cdns_i3c_master_send_hdr_cmd,
 	.i2c_xfers = cdns_i3c_master_i2c_xfers,
 	.i2c_funcs = cdns_i3c_master_i2c_funcs,
 	.enable_ibi = cdns_i3c_master_enable_ibi,
-- 
2.4.5


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

  parent reply	other threads:[~2018-12-13 12:19 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-13 12:18 [PATCH 0/2] Add the I3C HDR modes Przemyslaw Gaj
2018-12-13 12:18 ` [PATCH 1/2] i3c: Add support for " Przemyslaw Gaj
2018-12-13 12:44   ` Boris Brezillon
2019-02-21 15:15   ` vitor
2019-02-22 14:52     ` Boris Brezillon
2019-02-22 15:02       ` Przemyslaw Gaj
2019-02-22 17:41         ` vitor
2019-02-25  8:56           ` Boris Brezillon
2019-02-25 12:56             ` vitor
2019-02-25 13:10               ` Boris Brezillon
2019-02-25 16:45                 ` vitor
2019-02-25 17:03                   ` Boris Brezillon
2018-12-13 12:18 ` Przemyslaw Gaj [this message]
2018-12-13 12:45   ` [PATCH 2/2] i3c: master: cdns: Add support for HDR-DDR mode Boris Brezillon

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=adcd3c336c8285483b1741b145cad945bc813ce9.1544702829.git.pgaj@cadence.com \
    --to=pgaj@cadence.com \
    --cc=bbrezillon@kernel.org \
    --cc=linux-i3c@lists.infradead.org \
    --cc=psroka@cadence.com \
    --cc=rafalc@cadence.com \
    --cc=vitor.soares@synopsys.com \
    /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;
as well as URLs for NNTP newsgroup(s).