Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 529ba0d9 authored by David Brownell's avatar David Brownell Committed by Linus Torvalds
Browse files

spi: bitbang bugfix in message setup



Bugfix to spi_bitbang infrastructure: make sure to always set transfer
parameters on the first pass through the message's per-transfer loop.
This can matter with drivers that replace the per-word or per-buffer
transfer primitives, on busses with multiple SPI devices.

Previously, this could have started messages using the settings left after
previous messages.  The problem was observed when a high speed chip
(m25p80 type flash) was running very slowly because a low speed device
(avr8 microcontroller) had previously used the bus.  Similar faults could
have driven the low speed device too fast, or used an unexpected word
size.

Acked-by: default avatarSteven A. Falco <sfalco@harris.com>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 537a1bf0
Loading
Loading
Loading
Loading
+14 −10
Original line number Diff line number Diff line
@@ -258,6 +258,11 @@ static void bitbang_work(struct work_struct *work)
	struct spi_bitbang	*bitbang =
		container_of(work, struct spi_bitbang, work);
	unsigned long		flags;
	int			do_setup = -1;
	int			(*setup_transfer)(struct spi_device *,
					struct spi_transfer *);

	setup_transfer = bitbang->setup_transfer;

	spin_lock_irqsave(&bitbang->lock, flags);
	bitbang->busy = 1;
@@ -269,8 +274,6 @@ static void bitbang_work(struct work_struct *work)
		unsigned		tmp;
		unsigned		cs_change;
		int			status;
		int			(*setup_transfer)(struct spi_device *,
						struct spi_transfer *);

		m = container_of(bitbang->queue.next, struct spi_message,
				queue);
@@ -287,19 +290,19 @@ static void bitbang_work(struct work_struct *work)
		tmp = 0;
		cs_change = 1;
		status = 0;
		setup_transfer = NULL;

		list_for_each_entry (t, &m->transfers, transfer_list) {

			/* override or restore speed and wordsize */
			if (t->speed_hz || t->bits_per_word) {
				setup_transfer = bitbang->setup_transfer;
			/* override speed or wordsize? */
			if (t->speed_hz || t->bits_per_word)
				do_setup = 1;

			/* init (-1) or override (1) transfer params */
			if (do_setup != 0) {
				if (!setup_transfer) {
					status = -ENOPROTOOPT;
					break;
				}
			}
			if (setup_transfer) {
				status = setup_transfer(spi, t);
				if (status < 0)
					break;
@@ -363,9 +366,10 @@ static void bitbang_work(struct work_struct *work)
		m->status = status;
		m->complete(m->context);

		/* restore speed and wordsize */
		if (setup_transfer)
		/* restore speed and wordsize if it was overridden */
		if (do_setup == 1)
			setup_transfer(spi, NULL);
		do_setup = 0;

		/* normally deactivate chipselect ... unless no error and
		 * cs_change has hinted that the next message will probably