linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Grant Likely <grant.likely-s3s/WqlpOiPyB63q8FvJNQ@public.gmane.org>
To: Jonas Gorski <jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>,
	spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
Cc: Maxime Bizon <mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org>,
	Mark Brown
	<broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>,
	Florian Fainelli
	<florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>,
	Kevin Cernekee <cernekee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Subject: Re: [PATCH V2 2/2] spi/bcm63xx: work around inability to keep CS up
Date: Tue, 05 Feb 2013 14:35:30 +0000	[thread overview]
Message-ID: <20130205143530.1F00D3E1265@localhost> (raw)
In-Reply-To: <1359900913-4472-3-git-send-email-jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>

On Sun,  3 Feb 2013 15:15:13 +0100, Jonas Gorski <jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org> wrote:
> This SPI controller does not support keeping CS asserted after sending
> a transfer.
> Since messages expected on this SPI controller are rather short, we can
> work around it for normal use cases by sending all transfers at once in
> a big full duplex stream.
> 
> This means that we cannot change the speed between transfers if they
> require CS to be kept asserted, but these would have been rejected
> before anyway because of the inability of keeping CS asserted.
> 
> Signed-off-by: Jonas Gorski <jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>

Are you checking the state of transfer->cs_change when merging
transfers? If cs_change is set, then the transfers cannot be merged.

g.

> ---
> V1 -> V2:
>  * split out rejection logic into separate patch
>  * fixed return type of bcm63xx_txrx_bufs()
>  * slightly reworked bcm63xx_txrx_bufs, obsoleting one local variable
> 
>  drivers/spi/spi-bcm63xx.c |  134 +++++++++++++++++++++++++++++++++++----------
>  1 file changed, 106 insertions(+), 28 deletions(-)
> 
> diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
> index 27667c1..9578af7 100644
> --- a/drivers/spi/spi-bcm63xx.c
> +++ b/drivers/spi/spi-bcm63xx.c
> @@ -37,6 +37,8 @@
>  
>  #define PFX		KBUILD_MODNAME
>  
> +#define BCM63XX_SPI_MAX_PREPEND		15
> +
>  struct bcm63xx_spi {
>  	struct completion	done;
>  
> @@ -169,13 +171,17 @@ static int bcm63xx_spi_setup(struct spi_device *spi)
>  	return 0;
>  }
>  
> -static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
> +static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *first,
> +				unsigned int num_transfers)
>  {
>  	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
>  	u16 msg_ctl;
>  	u16 cmd;
>  	u8 rx_tail;
> -	unsigned int timeout = 0;
> +	unsigned int i, timeout = 0, prepend_len = 0, len = 0;
> +	struct spi_transfer *t = first;
> +	bool do_rx = false;
> +	bool do_tx = false;
>  
>  	/* Disable the CMD_DONE interrupt */
>  	bcm_spi_writeb(bs, 0, SPI_INT_MASK);
> @@ -183,19 +189,45 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
>  	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
>  		t->tx_buf, t->rx_buf, t->len);
>  
> -	if (t->tx_buf)
> -		memcpy_toio(bs->tx_io, t->tx_buf, t->len);
> +	if (num_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND)
> +		prepend_len = t->len;
> +
> +	/* prepare the buffer */
> +	for (i = 0; i < num_transfers; i++) {
> +		if (t->tx_buf) {
> +			do_tx = true;
> +			memcpy_toio(bs->tx_io + len, t->tx_buf, t->len);
> +
> +			/* don't prepend more than one tx */
> +			if (t != first)
> +				prepend_len = 0;
> +		}
> +
> +		if (t->rx_buf) {
> +			do_rx = true;
> +			/* prepend is half-duplex write only */
> +			if (t == first)
> +				prepend_len = 0;
> +		}
> +
> +		len += t->len;
> +
> +		t = list_entry(t->transfer_list.next, struct spi_transfer,
> +			       transfer_list);
> +	}
> +
> +	len -= prepend_len;
>  
>  	init_completion(&bs->done);
>  
>  	/* Fill in the Message control register */
> -	msg_ctl = (t->len << SPI_BYTE_CNT_SHIFT);
> +	msg_ctl = (len << SPI_BYTE_CNT_SHIFT);
>  
> -	if (t->rx_buf && t->tx_buf)
> +	if (do_rx && do_tx && prepend_len == 0)
>  		msg_ctl |= (SPI_FD_RW << bs->msg_type_shift);
> -	else if (t->rx_buf)
> +	else if (do_rx)
>  		msg_ctl |= (SPI_HD_R << bs->msg_type_shift);
> -	else if (t->tx_buf)
> +	else if (do_tx)
>  		msg_ctl |= (SPI_HD_W << bs->msg_type_shift);
>  
>  	switch (bs->msg_ctl_width) {
> @@ -209,7 +241,7 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
>  
>  	/* Issue the transfer */
>  	cmd = SPI_CMD_START_IMMEDIATE;
> -	cmd |= (0 << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
> +	cmd |= (prepend_len << SPI_CMD_PREPEND_BYTE_CNT_SHIFT);
>  	cmd |= (spi->chip_select << SPI_CMD_DEVICE_ID_SHIFT);
>  	bcm_spi_writew(bs, cmd, SPI_CMD);
>  
> @@ -223,9 +255,25 @@ static int bcm63xx_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
>  	/* read out all data */
>  	rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL);
>  
> +	if (do_rx && rx_tail != len)
> +		return -EIO;
> +
> +	if (!rx_tail)
> +		return 0;
> +
> +	len = 0;
> +	t = first;
>  	/* Read out all the data */
> -	if (rx_tail)
> -		memcpy_fromio(t->rx_ptr, bs->rx_io, rx_tail);
> +	for (i = 0; i < num_transfers; i++) {
> +		if (t->rx_buf)
> +			memcpy_fromio(t->rx_buf, bs->rx_io + len, t->len);
> +
> +		if (t != first || prepend_len == 0)
> +			len += t->len;
> +
> +		t = list_entry(t->transfer_list.next, struct spi_transfer,
> +			       transfer_list);
> +	}
>  
>  	return 0;
>  }
> @@ -252,46 +300,76 @@ static int bcm63xx_spi_transfer_one(struct spi_master *master,
>  					struct spi_message *m)
>  {
>  	struct bcm63xx_spi *bs = spi_master_get_devdata(master);
> -	struct spi_transfer *t;
> +	struct spi_transfer *t, *first = NULL;
>  	struct spi_device *spi = m->spi;
>  	int status = 0;
> -
> +	unsigned int n_transfers = 0, total_len = 0;
> +	bool can_use_prepend = false;
> +
> +	/*
> +	 * This SPI controller does not support keeping CS active after a
> +	 * transfer.
> +	 * Work around this by merging as many transfers we can into one big
> +	 * full-duplex transfers.
> +	 */
>  	list_for_each_entry(t, &m->transfers, transfer_list) {
>  		status = bcm63xx_spi_check_transfer(spi, t);
>  		if (status < 0)
>  			goto exit;
>  
> +		if (!first)
> +			first = t;
> +
> +		n_transfers++;
> +		total_len += t->len;
> +
> +		if (n_transfers == 2 && !first->rx_buf && !t->tx_buf &&
> +		    first->len <= BCM63XX_SPI_MAX_PREPEND)
> +			can_use_prepend = true;
> +		else if (can_use_prepend && t->tx_buf)
> +			can_use_prepend = false;
> +
>  		/* we can only transfer one fifo worth of data */
> -		if (t->len > bs->fifo_size) {
> +		if ((can_use_prepend &&
> +		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) ||
> +		    (!can_use_prepend && total_len > bs->fifo_size)) {
>  			dev_err(&spi->dev, "unable to do transfers larger than FIFO size (%i > %i)\n",
> -				t->len, bs->fifo_size);
> +				total_len, bs->fifo_size);
>  			status = -EINVAL;
>  			goto exit;
>  		}
>  
> -		/* CS will be deasserted directly after transfer */
> -		if (t->delay_usecs) {
> -			dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
> +		/* all combined transfers have to have the same speed */
> +		if (t->speed_hz != first->speed_hz) {
> +			dev_err(&spi->dev, "unable to change speed between transfers\n");
>  			status = -EINVAL;
>  			goto exit;
>  		}
>  
> -		if (!t->cs_change &&
> -		    !list_is_last(&t->transfer_list, &m->transfers)) {
> -			dev_err(&spi->dev, "unable to keep CS asserted between transfers\n");
> +		/* CS will be deasserted directly after transfer */
> +		if (t->delay_usecs) {
> +			dev_err(&spi->dev, "unable to keep CS asserted after transfer\n");
>  			status = -EINVAL;
>  			goto exit;
>  		}
>  
> -		/* configure adapter for a new transfer */
> -		bcm63xx_spi_setup_transfer(spi, t);
> +		if (t->cs_change ||
> +		    list_is_last(&t->transfer_list, &m->transfers)) {
> +			/* configure adapter for a new transfer */
> +			bcm63xx_spi_setup_transfer(spi, first);
>  
> -		/* send the data */
> -		status = bcm63xx_txrx_bufs(spi, t);
> -		if (status)
> -			goto exit;
> +			/* send the data */
> +			status = bcm63xx_txrx_bufs(spi, first, n_transfers);
> +			if (status)
> +				goto exit;
> +
> +			m->actual_length += total_len;
>  
> -		m->actual_length += t->len;
> +			first = NULL;
> +			n_transfers = 0;
> +			total_len = 0;
> +			can_use_prepend = false;
> +		}
>  	}
>  exit:
>  	m->status = status;
> -- 
> 1.7.10.4
> 

-- 
Grant Likely, B.Sc, P.Eng.
Secret Lab Technologies, Ltd.

------------------------------------------------------------------------------
Free Next-Gen Firewall Hardware Offer
Buy your Sophos next-gen firewall before the end March 2013 
and get the hardware for free! Learn more.
http://p.sf.net/sfu/sophos-d2d-feb

  parent reply	other threads:[~2013-02-05 14:35 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-02-03 14:15 [PATCH V2 0/2] spi/bcm63xx: fix multi transfer messages Jonas Gorski
     [not found] ` <1359900913-4472-1-git-send-email-jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2013-02-03 14:15   ` [PATCH V2 1/2] spi/bcm63xx: reject transfers unable to transfer Jonas Gorski
     [not found]     ` <1359900913-4472-2-git-send-email-jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2013-02-05 14:32       ` Grant Likely
2013-02-03 14:15   ` [PATCH V2 2/2] spi/bcm63xx: work around inability to keep CS up Jonas Gorski
     [not found]     ` <1359900913-4472-3-git-send-email-jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2013-02-05 14:35       ` Grant Likely [this message]
2013-02-05 15:00         ` Jonas Gorski
     [not found]           ` <20130205160004.2817a8b08ba7bb8c2de9a382-p3rKhJxN3npAfugRpC6u6w@public.gmane.org>
2013-02-05 17:14             ` Grant Likely
2013-02-04 13:29   ` [PATCH V2 0/2] spi/bcm63xx: fix multi transfer messages Florian Fainelli
2013-02-05 14:30   ` Grant Likely
     [not found]     ` <20130205150441.GC4720@opensource.wolfsonmicro.com>
     [not found]       ` <20130205150441.GC4720-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org>
2013-02-05 17:14         ` Grant Likely

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=20130205143530.1F00D3E1265@localhost \
    --to=grant.likely-s3s/wqlpoipyb63q8fvjnq@public.gmane.org \
    --cc=broonie-yzvPICuk2AATkU/dhu1WVueM+bqZidxxQQ4Iyu8u01E@public.gmane.org \
    --cc=cernekee-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org \
    --cc=florian-p3rKhJxN3npAfugRpC6u6w@public.gmane.org \
    --cc=jogo-p3rKhJxN3npAfugRpC6u6w@public.gmane.org \
    --cc=mbizon-MmRyKUhfbQ9GWvitb5QawA@public.gmane.org \
    --cc=spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.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;
as well as URLs for NNTP newsgroup(s).