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

On Thu, 13 Dec 2018 12:18:32 +0000
Przemyslaw Gaj <pgaj@cadence.com> wrote:

> 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.

Same here.

> 
> 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,


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

      reply	other threads:[~2018-12-13 12:45 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 ` [PATCH 2/2] i3c: master: cdns: Add support for HDR-DDR mode Przemyslaw Gaj
2018-12-13 12:45   ` Boris Brezillon [this message]

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=20181213134502.101f5fa5@bbrezillon \
    --to=bbrezillon@kernel.org \
    --cc=linux-i3c@lists.infradead.org \
    --cc=pgaj@cadence.com \
    --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