diff options
5 files changed, 619 insertions, 3 deletions
| diff --git a/target/linux/brcm63xx/patches-3.3/109-bcm63xx-fix-transfer-bits_per_words-check.patch b/target/linux/brcm63xx/patches-3.3/109-bcm63xx-fix-transfer-bits_per_words-check.patch new file mode 100644 index 000000000..37d9d49d6 --- /dev/null +++ b/target/linux/brcm63xx/patches-3.3/109-bcm63xx-fix-transfer-bits_per_words-check.patch @@ -0,0 +1,29 @@ +From fbef4dff80be6254e36ab5b9c655d248a3991ded Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jonas.gorski@gmail.com> +Date: Sat, 24 Nov 2012 12:08:22 +0100 +Subject: [PATCH 3.7] spi/bcm63xx: fix transfer bits_per_words check + +Transfers often do not have bits_per_words set, so use the spi device's +bits_per_words in this case. + +This fixes the driver rejecting valid transfers e.g. generated by +spi_write() or spi_read(). + +Cc: stable@vger.kernel.org +Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> +--- + drivers/spi/spi-bcm63xx.c |    3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-bcm63xx.c ++++ b/drivers/spi/spi-bcm63xx.c +@@ -103,7 +103,8 @@ static int bcm63xx_spi_check_transfer(st + { + 	u8 bits_per_word; +  +-	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; ++	bits_per_word = (t && t->bits_per_word) ? ++			t->bits_per_word : spi->bits_per_word; + 	if (bits_per_word != 8) { + 		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + 			__func__, bits_per_word); diff --git a/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch b/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch new file mode 100644 index 000000000..941de48c3 --- /dev/null +++ b/target/linux/brcm63xx/patches-3.3/110-spi-bcm63xx-fix-multi-transfer-messages.patch @@ -0,0 +1,279 @@ +From 0f2ae1e1282ff64f74a5e36f7da874f94911225e Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jonas.gorski@gmail.com> +Date: Wed, 14 Nov 2012 22:22:33 +0100 +Subject: [PATCH] spi/bcm63xx: fix multi transfer messages + +The BCM63XX SPI controller does not support keeping CS asserted after +sending its buffer. This breaks common usages like spi_write_then_read, +where it is expected to be kept active during the whole transfers. + +Work around this by combining the transfers into one if the buffer +allows. For spi_write_then_read, use the prepend byte feature to write +to "prepend" the write if it is less than 15 bytes, allowing the whole +fifo size for the read. + +Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> +--- +Tested on a SPI conntected switch which required keeping CS active between +the register read command and reading the register contents. + +Based on Mark's spi/next. + +Not sure if this is stable material, as it's quite invasive. + + drivers/spi/spi-bcm63xx.c |  172 ++++++++++++++++++++++++++++++--------------- + 1 file changed, 117 insertions(+), 55 deletions(-) + +--- a/drivers/spi/spi-bcm63xx.c ++++ b/drivers/spi/spi-bcm63xx.c +@@ -38,6 +38,8 @@ + #define PFX		KBUILD_MODNAME + #define DRV_VER		"0.1.2" +  ++#define BCM63XX_SPI_MAX_PREPEND		15 ++ + struct bcm63xx_spi { + 	struct completion	done; +  +@@ -50,16 +52,10 @@ struct bcm63xx_spi { + 	unsigned int		msg_type_shift; + 	unsigned int		msg_ctl_width; +  +-	/* Data buffers */ +-	const unsigned char	*tx_ptr; +-	unsigned char		*rx_ptr; +- + 	/* data iomem */ + 	u8 __iomem		*tx_io; + 	const u8 __iomem	*rx_io; +  +-	int			remaining_bytes; +- + 	struct clk		*clk; + 	struct platform_device	*pdev; + }; +@@ -184,50 +180,60 @@ static int bcm63xx_spi_setup(struct spi_ + 	return 0; + } +  +-/* Fill the TX FIFO with as many bytes as possible */ +-static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) +-{ +-	u8 size; +- +-	/* Fill the Tx FIFO with as many bytes as possible */ +-	size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes : +-		bs->fifo_size; +-	memcpy_toio(bs->tx_io, bs->tx_ptr, size); +-	bs->remaining_bytes -= size; +-} +- + static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, +-					struct spi_transfer *t) ++					struct spi_transfer *first, ++					unsigned int n_transfers) + { + 	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); + 	u16 msg_ctl; + 	u16 cmd; ++	unsigned int i, timeout, total_len = 0, prepend_len = 0, len = 0; ++	struct spi_transfer *t = first; ++	u8 rx_tail; ++	bool do_rx = false; ++	bool do_tx = false; +  + 	/* Disable the CMD_DONE interrupt */ + 	bcm_spi_writeb(bs, 0, SPI_INT_MASK); +  +-	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", +-		t->tx_buf, t->rx_buf, t->len); ++	if (n_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) ++		prepend_len = t->len; ++ ++	/* prepare the buffer */ ++	for (i = 0; i < n_transfers; i++) { ++		if (t->tx_buf) { ++			do_tx = true; ++			memcpy_toio(bs->tx_io + total_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; ++			if (t == first) ++				prepend_len = 0; ++		} +  +-	/* Transmitter is inhibited */ +-	bs->tx_ptr = t->tx_buf; +-	bs->rx_ptr = t->rx_buf; +- +-	if (t->tx_buf) { +-		bs->remaining_bytes = t->len; +-		bcm63xx_spi_fill_tx_fifo(bs); ++		total_len += t->len; ++ ++		t = list_entry(t->transfer_list.next, struct spi_transfer, ++			       transfer_list); + 	} +  ++	len = total_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) { +@@ -245,14 +251,41 @@ static unsigned int bcm63xx_txrx_bufs(st +  + 	/* 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); +  + 	/* Enable the CMD_DONE interrupt */ + 	bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); +  +-	return t->len - bs->remaining_bytes; ++	timeout = wait_for_completion_timeout(&bs->done, HZ); ++	if (!timeout) ++		return -ETIMEDOUT; ++ ++	/* read out all data */ ++	rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); ++ ++	if (do_rx && rx_tail != len) ++		return -EINVAL; ++ ++	if (!rx_tail) ++		return total_len; ++ ++	len = 0; ++	t = first; ++	/* Read out all the data */ ++	for (i = 0; i < n_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 total_len; + } +  + static int bcm63xx_spi_prepare_transfer(struct spi_master *master) +@@ -277,42 +310,71 @@ static int bcm63xx_spi_transfer_one(stru + 					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 timeout = 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, so we need to combine the transfers into one until we may ++	 * deassert CS. ++	 */ + 	list_for_each_entry(t, &m->transfers, transfer_list) { +-		unsigned int len = t->len; +-		u8 rx_tail; +- + 		status = bcm63xx_spi_check_transfer(spi, t); + 		if (status < 0) + 			goto exit; +  +-		/* configure adapter for a new transfer */ +-		bcm63xx_spi_setup_transfer(spi, t); ++		if (!first) ++			first = t; +  +-		while (len) { +-			/* send the data */ +-			len -= bcm63xx_txrx_bufs(spi, t); +- +-			timeout = wait_for_completion_timeout(&bs->done, HZ); +-			if (!timeout) { +-				status = -ETIMEDOUT; +-				goto exit; +-			} ++		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; ++ ++		if ((can_use_prepend && ++		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || ++		    (!can_use_prepend && total_len > bs->fifo_size)) { ++			status = -EINVAL; ++			goto exit; ++		} +  +-			/* read out all data */ +-			rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); ++		/* all transfers have to be made at the same speed */ ++		if (t->speed_hz != first->speed_hz) { ++			status = -EINVAL; ++			goto exit; ++		} +  +-			/* Read out all the data */ +-			if (rx_tail) +-				memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); ++		/* CS will be deasserted directly after the transfer */ ++		if (t->delay_usecs) { ++			status = -EINVAL; ++			goto exit; + 		} +  +-		m->actual_length += t->len; ++		if (t->cs_change || ++		    list_is_last(&t->transfer_list, &m->transfers)) { ++			/* configure adapter for a new transfer */ ++			bcm63xx_spi_setup_transfer(spi, first); ++ ++			status = bcm63xx_txrx_bufs(spi, first, n_transfers); ++			if (status < 0) ++				goto exit; ++ ++			m->actual_length += status; ++			first = NULL; ++			status = 0; ++			n_transfers = 0; ++			total_len = 0; ++			can_use_prepend = false; ++		} + 	} ++ + exit: + 	m->status = status; + 	spi_finalize_current_message(master); diff --git a/target/linux/brcm63xx/patches-3.3/404-bcm963xx_flashmap.patch b/target/linux/brcm63xx/patches-3.3/404-bcm963xx_flashmap.patch index 8338962e5..8f91623c8 100644 --- a/target/linux/brcm63xx/patches-3.3/404-bcm963xx_flashmap.patch +++ b/target/linux/brcm63xx/patches-3.3/404-bcm963xx_flashmap.patch @@ -23,7 +23,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>   	.width			= 2,  --- a/drivers/mtd/redboot.c  +++ b/drivers/mtd/redboot.c -@@ -75,6 +75,7 @@ static int parse_redboot_partitions(stru +@@ -72,6 +72,7 @@ static int parse_redboot_partitions(stru   	int nulllen = 0;   	int numslots;   	unsigned long offset; @@ -31,7 +31,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>   #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED   	static char nullstring[] = "unallocated";   #endif -@@ -181,6 +182,16 @@ static int parse_redboot_partitions(stru +@@ -178,6 +179,16 @@ static int parse_redboot_partitions(stru   		goto out;   	} @@ -48,7 +48,7 @@ Signed-off-by: Axel Gembe <ago@bastart.eu.org>   	for (i = 0; i < numslots; i++) {   		struct fis_list *new_fl, **prev; -@@ -201,10 +212,10 @@ static int parse_redboot_partitions(stru +@@ -198,10 +209,10 @@ static int parse_redboot_partitions(stru   			goto out;   		}   		new_fl->img = &buf[i]; diff --git a/target/linux/brcm63xx/patches-3.6/106-bcm63xx-fix-transfer-bits_per_words-check.patch b/target/linux/brcm63xx/patches-3.6/106-bcm63xx-fix-transfer-bits_per_words-check.patch new file mode 100644 index 000000000..37d9d49d6 --- /dev/null +++ b/target/linux/brcm63xx/patches-3.6/106-bcm63xx-fix-transfer-bits_per_words-check.patch @@ -0,0 +1,29 @@ +From fbef4dff80be6254e36ab5b9c655d248a3991ded Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jonas.gorski@gmail.com> +Date: Sat, 24 Nov 2012 12:08:22 +0100 +Subject: [PATCH 3.7] spi/bcm63xx: fix transfer bits_per_words check + +Transfers often do not have bits_per_words set, so use the spi device's +bits_per_words in this case. + +This fixes the driver rejecting valid transfers e.g. generated by +spi_write() or spi_read(). + +Cc: stable@vger.kernel.org +Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> +--- + drivers/spi/spi-bcm63xx.c |    3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/spi/spi-bcm63xx.c ++++ b/drivers/spi/spi-bcm63xx.c +@@ -103,7 +103,8 @@ static int bcm63xx_spi_check_transfer(st + { + 	u8 bits_per_word; +  +-	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word; ++	bits_per_word = (t && t->bits_per_word) ? ++			t->bits_per_word : spi->bits_per_word; + 	if (bits_per_word != 8) { + 		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n", + 			__func__, bits_per_word); diff --git a/target/linux/brcm63xx/patches-3.6/107-spi-bcm63xx-fix-multi-transfer-messages.patch b/target/linux/brcm63xx/patches-3.6/107-spi-bcm63xx-fix-multi-transfer-messages.patch new file mode 100644 index 000000000..2da504449 --- /dev/null +++ b/target/linux/brcm63xx/patches-3.6/107-spi-bcm63xx-fix-multi-transfer-messages.patch @@ -0,0 +1,279 @@ +From 0f2ae1e1282ff64f74a5e36f7da874f94911225e Mon Sep 17 00:00:00 2001 +From: Jonas Gorski <jonas.gorski@gmail.com> +Date: Wed, 14 Nov 2012 22:22:33 +0100 +Subject: [PATCH] spi/bcm63xx: fix multi transfer messages + +The BCM63XX SPI controller does not support keeping CS asserted after +sending its buffer. This breaks common usages like spi_write_then_read, +where it is expected to be kept active during the whole transfers. + +Work around this by combining the transfers into one if the buffer +allows. For spi_write_then_read, use the prepend byte feature to write +to "prepend" the write if it is less than 15 bytes, allowing the whole +fifo size for the read. + +Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> +--- +Tested on a SPI conntected switch which required keeping CS active between +the register read command and reading the register contents. + +Based on Mark's spi/next. + +Not sure if this is stable material, as it's quite invasive. + + drivers/spi/spi-bcm63xx.c |  172 ++++++++++++++++++++++++++++++--------------- + 1 file changed, 117 insertions(+), 55 deletions(-) + +--- a/drivers/spi/spi-bcm63xx.c ++++ b/drivers/spi/spi-bcm63xx.c +@@ -38,6 +38,8 @@ + #define PFX		KBUILD_MODNAME + #define DRV_VER		"0.1.2" +  ++#define BCM63XX_SPI_MAX_PREPEND		15 ++ + struct bcm63xx_spi { + 	struct completion	done; +  +@@ -50,16 +52,10 @@ struct bcm63xx_spi { + 	unsigned int		msg_type_shift; + 	unsigned int		msg_ctl_width; +  +-	/* Data buffers */ +-	const unsigned char	*tx_ptr; +-	unsigned char		*rx_ptr; +- + 	/* data iomem */ + 	u8 __iomem		*tx_io; + 	const u8 __iomem	*rx_io; +  +-	int			remaining_bytes; +- + 	struct clk		*clk; + 	struct platform_device	*pdev; + }; +@@ -184,50 +180,60 @@ static int bcm63xx_spi_setup(struct spi_ + 	return 0; + } +  +-/* Fill the TX FIFO with as many bytes as possible */ +-static void bcm63xx_spi_fill_tx_fifo(struct bcm63xx_spi *bs) +-{ +-	u8 size; +- +-	/* Fill the Tx FIFO with as many bytes as possible */ +-	size = bs->remaining_bytes < bs->fifo_size ? bs->remaining_bytes : +-		bs->fifo_size; +-	memcpy_toio(bs->tx_io, bs->tx_ptr, size); +-	bs->remaining_bytes -= size; +-} +- + static unsigned int bcm63xx_txrx_bufs(struct spi_device *spi, +-					struct spi_transfer *t) ++					struct spi_transfer *first, ++					unsigned int n_transfers) + { + 	struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master); + 	u16 msg_ctl; + 	u16 cmd; ++	unsigned int i, timeout, total_len = 0, prepend_len = 0, len = 0; ++	struct spi_transfer *t = first; ++	u8 rx_tail; ++	bool do_rx = false; ++	bool do_tx = false; +  + 	/* Disable the CMD_DONE interrupt */ + 	bcm_spi_writeb(bs, 0, SPI_INT_MASK); +  +-	dev_dbg(&spi->dev, "txrx: tx %p, rx %p, len %d\n", +-		t->tx_buf, t->rx_buf, t->len); ++	if (n_transfers > 1 && t->tx_buf && t->len <= BCM63XX_SPI_MAX_PREPEND) ++		prepend_len = t->len; ++ ++	/* prepare the buffer */ ++	for (i = 0; i < n_transfers; i++) { ++		if (t->tx_buf) { ++			do_tx = true; ++			memcpy_toio(bs->tx_io + total_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; ++			if (t == first) ++				prepend_len = 0; ++		} +  +-	/* Transmitter is inhibited */ +-	bs->tx_ptr = t->tx_buf; +-	bs->rx_ptr = t->rx_buf; +- +-	if (t->tx_buf) { +-		bs->remaining_bytes = t->len; +-		bcm63xx_spi_fill_tx_fifo(bs); ++		total_len += t->len; ++ ++		t = list_entry(t->transfer_list.next, struct spi_transfer, ++			       transfer_list); + 	} +  ++	len = total_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) { +@@ -241,14 +247,41 @@ static unsigned int bcm63xx_txrx_bufs(st +  + 	/* 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); +  + 	/* Enable the CMD_DONE interrupt */ + 	bcm_spi_writeb(bs, SPI_INTR_CMD_DONE, SPI_INT_MASK); +  +-	return t->len - bs->remaining_bytes; ++	timeout = wait_for_completion_timeout(&bs->done, HZ); ++	if (!timeout) ++		return -ETIMEDOUT; ++ ++	/* read out all data */ ++	rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); ++ ++	if (do_rx && rx_tail != len) ++		return -EINVAL; ++ ++	if (!rx_tail) ++		return total_len; ++ ++	len = 0; ++	t = first; ++	/* Read out all the data */ ++	for (i = 0; i < n_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 total_len; + } +  + static int bcm63xx_spi_prepare_transfer(struct spi_master *master) +@@ -273,42 +306,71 @@ static int bcm63xx_spi_transfer_one(stru + 					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 timeout = 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, so we need to combine the transfers into one until we may ++	 * deassert CS. ++	 */ + 	list_for_each_entry(t, &m->transfers, transfer_list) { +-		unsigned int len = t->len; +-		u8 rx_tail; +- + 		status = bcm63xx_spi_check_transfer(spi, t); + 		if (status < 0) + 			goto exit; +  +-		/* configure adapter for a new transfer */ +-		bcm63xx_spi_setup_transfer(spi, t); ++		if (!first) ++			first = t; +  +-		while (len) { +-			/* send the data */ +-			len -= bcm63xx_txrx_bufs(spi, t); +- +-			timeout = wait_for_completion_timeout(&bs->done, HZ); +-			if (!timeout) { +-				status = -ETIMEDOUT; +-				goto exit; +-			} ++		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; ++ ++		if ((can_use_prepend && ++		     total_len > (bs->fifo_size + BCM63XX_SPI_MAX_PREPEND)) || ++		    (!can_use_prepend && total_len > bs->fifo_size)) { ++			status = -EINVAL; ++			goto exit; ++		} +  +-			/* read out all data */ +-			rx_tail = bcm_spi_readb(bs, SPI_RX_TAIL); ++		/* all transfers have to be made at the same speed */ ++		if (t->speed_hz != first->speed_hz) { ++			status = -EINVAL; ++			goto exit; ++		} +  +-			/* Read out all the data */ +-			if (rx_tail) +-				memcpy_fromio(bs->rx_ptr, bs->rx_io, rx_tail); ++		/* CS will be deasserted directly after the transfer */ ++		if (t->delay_usecs) { ++			status = -EINVAL; ++			goto exit; + 		} +  +-		m->actual_length += t->len; ++		if (t->cs_change || ++		    list_is_last(&t->transfer_list, &m->transfers)) { ++			/* configure adapter for a new transfer */ ++			bcm63xx_spi_setup_transfer(spi, first); ++ ++			status = bcm63xx_txrx_bufs(spi, first, n_transfers); ++			if (status < 0) ++				goto exit; ++ ++			m->actual_length += status; ++			first = NULL; ++			status = 0; ++			n_transfers = 0; ++			total_len = 0; ++			can_use_prepend = false; ++		} + 	} ++ + exit: + 	m->status = status; + 	spi_finalize_current_message(master); | 
