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

Commit 20e0ec11 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'spi/next' of git://git.secretlab.ca/git/linux-2.6

* 'spi/next' of git://git.secretlab.ca/git/linux-2.6:
  spi/amba-pl022: work in polling or interrupt mode if pl022_dma_probe fails
  spi/spi_s3c24xx: Use spi_bitbang_stop instead of spi_unregister_master in s3c24xx_spi_remove
  spi/spi_nuc900: Use spi_bitbang_stop instead of spi_unregister_master in nuc900_spi_remove
  spi/spi_tegra: use spi_unregister_master() instead of spi_master_put()
  spi/spi_sh: use spi_unregister_master instead of spi_master_put in remove path
  spi: Use void pointers for data in simple SPI I/O operations
  spi/pl022: use cpu_relax in the busy loop
  spi/pl022: mark driver non-experimental
  spi/pl022: timeout on polled transfer v2
  spi/dw_spi: improve the interrupt mode with the batch ops
  spi/dw_spi: change poll mode transfer from byte ops to batch ops
  spi/dw_spi: remove the un-necessary flush()
  spi/dw_spi: unify the low level read/write routines
parents 829ae273 43c64015
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -271,8 +271,8 @@ config SPI_ORION
	  This enables using the SPI master controller on the Orion chips.

config SPI_PL022
	tristate "ARM AMBA PL022 SSP controller (EXPERIMENTAL)"
	depends on ARM_AMBA && EXPERIMENTAL
	tristate "ARM AMBA PL022 SSP controller"
	depends on ARM_AMBA
	default y if MACH_U300
	default y if ARCH_REALVIEW
	default y if INTEGRATOR_IMPD1
+22 −13
Original line number Diff line number Diff line
@@ -24,11 +24,6 @@
 * GNU General Public License for more details.
 */

/*
 * TODO:
 * - add timeout on polled transfers
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -287,6 +282,8 @@

#define CLEAR_ALL_INTERRUPTS  0x3

#define SPI_POLLING_TIMEOUT 1000


/*
 * The type of reading going on on this chip
@@ -1063,7 +1060,7 @@ static int __init pl022_dma_probe(struct pl022 *pl022)
					    pl022->master_info->dma_filter,
					    pl022->master_info->dma_rx_param);
	if (!pl022->dma_rx_channel) {
		dev_err(&pl022->adev->dev, "no RX DMA channel!\n");
		dev_dbg(&pl022->adev->dev, "no RX DMA channel!\n");
		goto err_no_rxchan;
	}

@@ -1071,13 +1068,13 @@ static int __init pl022_dma_probe(struct pl022 *pl022)
					    pl022->master_info->dma_filter,
					    pl022->master_info->dma_tx_param);
	if (!pl022->dma_tx_channel) {
		dev_err(&pl022->adev->dev, "no TX DMA channel!\n");
		dev_dbg(&pl022->adev->dev, "no TX DMA channel!\n");
		goto err_no_txchan;
	}

	pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
	if (!pl022->dummypage) {
		dev_err(&pl022->adev->dev, "no DMA dummypage!\n");
		dev_dbg(&pl022->adev->dev, "no DMA dummypage!\n");
		goto err_no_dummypage;
	}

@@ -1093,6 +1090,8 @@ err_no_txchan:
	dma_release_channel(pl022->dma_rx_channel);
	pl022->dma_rx_channel = NULL;
err_no_rxchan:
	dev_err(&pl022->adev->dev,
			"Failed to work in dma mode, work without dma!\n");
	return -ENODEV;
}

@@ -1378,6 +1377,7 @@ static void do_polling_transfer(struct pl022 *pl022)
	struct spi_transfer *transfer = NULL;
	struct spi_transfer *previous = NULL;
	struct chip_data *chip;
	unsigned long time, timeout;

	chip = pl022->cur_chip;
	message = pl022->cur_msg;
@@ -1415,9 +1415,19 @@ static void do_polling_transfer(struct pl022 *pl022)
		       SSP_CR1(pl022->virtbase));

		dev_dbg(&pl022->adev->dev, "polling transfer ongoing ...\n");
		/* FIXME: insert a timeout so we don't hang here indefinitely */
		while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end)

		timeout = jiffies + msecs_to_jiffies(SPI_POLLING_TIMEOUT);
		while (pl022->tx < pl022->tx_end || pl022->rx < pl022->rx_end) {
			time = jiffies;
			readwriter(pl022);
			if (time_after(time, timeout)) {
				dev_warn(&pl022->adev->dev,
				"%s: timeout!\n", __func__);
				message->state = STATE_ERROR;
				goto out;
			}
			cpu_relax();
		}

		/* Update total byte transferred */
		message->actual_length += pl022->cur_transfer->len;
@@ -1426,7 +1436,7 @@ static void do_polling_transfer(struct pl022 *pl022)
		/* Move to next transfer */
		message->state = next_transfer(pl022);
	}

out:
	/* Handle end of message */
	if (message->state == STATE_DONE)
		message->status = 0;
@@ -2107,7 +2117,7 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
	if (platform_info->enable_dma) {
		status = pl022_dma_probe(pl022);
		if (status != 0)
			goto err_no_dma;
			platform_info->enable_dma = 0;
	}

	/* Initialize and start queue */
@@ -2143,7 +2153,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
 err_init_queue:
	destroy_queue(pl022);
	pl022_dma_remove(pl022);
 err_no_dma:
	free_irq(adev->irq[0], pl022);
 err_no_irq:
	clk_put(pl022->clk);
+66 −136
Original line number Diff line number Diff line
@@ -58,8 +58,6 @@ struct chip_data {
	u8 bits_per_word;
	u16 clk_div;		/* baud rate divider */
	u32 speed_hz;		/* baud rate */
	int (*write)(struct dw_spi *dws);
	int (*read)(struct dw_spi *dws);
	void (*cs_control)(u32 command);
};

@@ -162,107 +160,70 @@ static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)
}
#endif /* CONFIG_DEBUG_FS */

static void wait_till_not_busy(struct dw_spi *dws)
/* Return the max entries we can fill into tx fifo */
static inline u32 tx_max(struct dw_spi *dws)
{
	unsigned long end = jiffies + 1 + usecs_to_jiffies(5000);
	u32 tx_left, tx_room, rxtx_gap;

	while (time_before(jiffies, end)) {
		if (!(dw_readw(dws, sr) & SR_BUSY))
			return;
		cpu_relax();
	}
	dev_err(&dws->master->dev,
		"DW SPI: Status keeps busy for 5000us after a read/write!\n");
}
	tx_left = (dws->tx_end - dws->tx) / dws->n_bytes;
	tx_room = dws->fifo_len - dw_readw(dws, txflr);

static void flush(struct dw_spi *dws)
{
	while (dw_readw(dws, sr) & SR_RF_NOT_EMPT) {
		dw_readw(dws, dr);
		cpu_relax();
	}
	/*
	 * Another concern is about the tx/rx mismatch, we
	 * though to use (dws->fifo_len - rxflr - txflr) as
	 * one maximum value for tx, but it doesn't cover the
	 * data which is out of tx/rx fifo and inside the
	 * shift registers. So a control from sw point of
	 * view is taken.
	 */
	rxtx_gap =  ((dws->rx_end - dws->rx) - (dws->tx_end - dws->tx))
			/ dws->n_bytes;

	wait_till_not_busy(dws);
	return min3(tx_left, tx_room, (u32) (dws->fifo_len - rxtx_gap));
}

static int null_writer(struct dw_spi *dws)
/* Return the max entries we should read out of rx fifo */
static inline u32 rx_max(struct dw_spi *dws)
{
	u8 n_bytes = dws->n_bytes;
	u32 rx_left = (dws->rx_end - dws->rx) / dws->n_bytes;

	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
		|| (dws->tx == dws->tx_end))
		return 0;
	dw_writew(dws, dr, 0);
	dws->tx += n_bytes;

	wait_till_not_busy(dws);
	return 1;
	return min(rx_left, (u32)dw_readw(dws, rxflr));
}

static int null_reader(struct dw_spi *dws)
static void dw_writer(struct dw_spi *dws)
{
	u8 n_bytes = dws->n_bytes;
	u32 max = tx_max(dws);
	u16 txw = 0;

	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
		&& (dws->rx < dws->rx_end)) {
		dw_readw(dws, dr);
		dws->rx += n_bytes;
	while (max--) {
		/* Set the tx word if the transfer's original "tx" is not null */
		if (dws->tx_end - dws->len) {
			if (dws->n_bytes == 1)
				txw = *(u8 *)(dws->tx);
			else
				txw = *(u16 *)(dws->tx);
		}
	wait_till_not_busy(dws);
	return dws->rx == dws->rx_end;
		dw_writew(dws, dr, txw);
		dws->tx += dws->n_bytes;
	}

static int u8_writer(struct dw_spi *dws)
{
	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
		|| (dws->tx == dws->tx_end))
		return 0;

	dw_writew(dws, dr, *(u8 *)(dws->tx));
	++dws->tx;

	wait_till_not_busy(dws);
	return 1;
}

static int u8_reader(struct dw_spi *dws)
static void dw_reader(struct dw_spi *dws)
{
	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
		&& (dws->rx < dws->rx_end)) {
		*(u8 *)(dws->rx) = dw_readw(dws, dr);
		++dws->rx;
	}
	u32 max = rx_max(dws);
	u16 rxw;

	wait_till_not_busy(dws);
	return dws->rx == dws->rx_end;
}

static int u16_writer(struct dw_spi *dws)
{
	if (!(dw_readw(dws, sr) & SR_TF_NOT_FULL)
		|| (dws->tx == dws->tx_end))
		return 0;

	dw_writew(dws, dr, *(u16 *)(dws->tx));
	dws->tx += 2;

	wait_till_not_busy(dws);
	return 1;
	while (max--) {
		rxw = dw_readw(dws, dr);
		/* Care rx only if the transfer's original "rx" is not null */
		if (dws->rx_end - dws->len) {
			if (dws->n_bytes == 1)
				*(u8 *)(dws->rx) = rxw;
			else
				*(u16 *)(dws->rx) = rxw;
		}

static int u16_reader(struct dw_spi *dws)
{
	u16 temp;

	while ((dw_readw(dws, sr) & SR_RF_NOT_EMPT)
		&& (dws->rx < dws->rx_end)) {
		temp = dw_readw(dws, dr);
		*(u16 *)(dws->rx) = temp;
		dws->rx += 2;
		dws->rx += dws->n_bytes;
	}

	wait_till_not_busy(dws);
	return dws->rx == dws->rx_end;
}

static void *next_transfer(struct dw_spi *dws)
@@ -334,8 +295,7 @@ static void giveback(struct dw_spi *dws)

static void int_error_stop(struct dw_spi *dws, const char *msg)
{
	/* Stop and reset hw */
	flush(dws);
	/* Stop the hw */
	spi_enable_chip(dws, 0);

	dev_err(&dws->master->dev, "%s\n", msg);
@@ -362,35 +322,28 @@ EXPORT_SYMBOL_GPL(dw_spi_xfer_done);

static irqreturn_t interrupt_transfer(struct dw_spi *dws)
{
	u16 irq_status, irq_mask = 0x3f;
	u32 int_level = dws->fifo_len / 2;
	u32 left;
	u16 irq_status = dw_readw(dws, isr);

	irq_status = dw_readw(dws, isr) & irq_mask;
	/* Error handling */
	if (irq_status & (SPI_INT_TXOI | SPI_INT_RXOI | SPI_INT_RXUI)) {
		dw_readw(dws, txoicr);
		dw_readw(dws, rxoicr);
		dw_readw(dws, rxuicr);
		int_error_stop(dws, "interrupt_transfer: fifo overrun");
		int_error_stop(dws, "interrupt_transfer: fifo overrun/underrun");
		return IRQ_HANDLED;
	}

	dw_reader(dws);
	if (dws->rx_end == dws->rx) {
		spi_mask_intr(dws, SPI_INT_TXEI);
		dw_spi_xfer_done(dws);
		return IRQ_HANDLED;
	}
	if (irq_status & SPI_INT_TXEI) {
		spi_mask_intr(dws, SPI_INT_TXEI);

		left = (dws->tx_end - dws->tx) / dws->n_bytes;
		left = (left > int_level) ? int_level : left;

		while (left--)
			dws->write(dws);
		dws->read(dws);

		/* Re-enable the IRQ if there is still data left to tx */
		if (dws->tx_end > dws->tx)
		dw_writer(dws);
		/* Enable TX irq always, it will be disabled when RX finished */
		spi_umask_intr(dws, SPI_INT_TXEI);
		else
			dw_spi_xfer_done(dws);
	}

	return IRQ_HANDLED;
@@ -399,15 +352,13 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)
static irqreturn_t dw_spi_irq(int irq, void *dev_id)
{
	struct dw_spi *dws = dev_id;
	u16 irq_status, irq_mask = 0x3f;
	u16 irq_status = dw_readw(dws, isr) & 0x3f;

	irq_status = dw_readw(dws, isr) & irq_mask;
	if (!irq_status)
		return IRQ_NONE;

	if (!dws->cur_msg) {
		spi_mask_intr(dws, SPI_INT_TXEI);
		/* Never fail */
		return IRQ_HANDLED;
	}

@@ -417,13 +368,11 @@ static irqreturn_t dw_spi_irq(int irq, void *dev_id)
/* Must be called inside pump_transfers() */
static void poll_transfer(struct dw_spi *dws)
{
	while (dws->write(dws))
		dws->read(dws);
	/*
	 * There is a possibility that the last word of a transaction
	 * will be lost if data is not ready. Re-read to solve this issue.
	 */
	dws->read(dws);
	do {
		dw_writer(dws);
		dw_reader(dws);
		cpu_relax();
	} while (dws->rx_end > dws->rx);

	dw_spi_xfer_done(dws);
}
@@ -483,8 +432,6 @@ static void pump_transfers(unsigned long data)
	dws->tx_end = dws->tx + transfer->len;
	dws->rx = transfer->rx_buf;
	dws->rx_end = dws->rx + transfer->len;
	dws->write = dws->tx ? chip->write : null_writer;
	dws->read = dws->rx ? chip->read : null_reader;
	dws->cs_change = transfer->cs_change;
	dws->len = dws->cur_transfer->len;
	if (chip != dws->prev_chip)
@@ -518,20 +465,8 @@ static void pump_transfers(unsigned long data)

		switch (bits) {
		case 8:
			dws->n_bytes = 1;
			dws->dma_width = 1;
			dws->read = (dws->read != null_reader) ?
					u8_reader : null_reader;
			dws->write = (dws->write != null_writer) ?
					u8_writer : null_writer;
			break;
		case 16:
			dws->n_bytes = 2;
			dws->dma_width = 2;
			dws->read = (dws->read != null_reader) ?
					u16_reader : null_reader;
			dws->write = (dws->write != null_writer) ?
					u16_writer : null_writer;
			dws->n_bytes = dws->dma_width = bits >> 3;
			break;
		default:
			printk(KERN_ERR "MRST SPI0: unsupported bits:"
@@ -575,7 +510,7 @@ static void pump_transfers(unsigned long data)
		txint_level = dws->fifo_len / 2;
		txint_level = (templen > txint_level) ? txint_level : templen;

		imask |= SPI_INT_TXEI;
		imask |= SPI_INT_TXEI | SPI_INT_TXOI | SPI_INT_RXUI | SPI_INT_RXOI;
		dws->transfer_handler = interrupt_transfer;
	}

@@ -733,13 +668,9 @@ static int dw_spi_setup(struct spi_device *spi)
	if (spi->bits_per_word <= 8) {
		chip->n_bytes = 1;
		chip->dma_width = 1;
		chip->read = u8_reader;
		chip->write = u8_writer;
	} else if (spi->bits_per_word <= 16) {
		chip->n_bytes = 2;
		chip->dma_width = 2;
		chip->read = u16_reader;
		chip->write = u16_writer;
	} else {
		/* Never take >16b case for MRST SPIC */
		dev_err(&spi->dev, "invalid wordsize\n");
@@ -851,7 +782,6 @@ static void spi_hw_init(struct dw_spi *dws)
	spi_enable_chip(dws, 0);
	spi_mask_intr(dws, 0xff);
	spi_enable_chip(dws, 1);
	flush(dws);

	/*
	 * Try to detect the FIFO depth if not set by interface driver,
+0 −2
Original line number Diff line number Diff line
@@ -137,8 +137,6 @@ struct dw_spi {
	u8			max_bits_per_word;	/* maxim is 16b */
	u32			dma_width;
	int			cs_change;
	int			(*write)(struct dw_spi *dws);
	int			(*read)(struct dw_spi *dws);
	irqreturn_t		(*transfer_handler)(struct dw_spi *dws);
	void			(*cs_control)(u32 command);

+2 −2
Original line number Diff line number Diff line
@@ -1047,8 +1047,8 @@ static u8 *buf;
 * spi_{async,sync}() calls with dma-safe buffers.
 */
int spi_write_then_read(struct spi_device *spi,
		const u8 *txbuf, unsigned n_tx,
		u8 *rxbuf, unsigned n_rx)
		const void *txbuf, unsigned n_tx,
		void *rxbuf, unsigned n_rx)
{
	static DEFINE_MUTEX(lock);

Loading