diff options
Diffstat (limited to 'drivers/spi/dw_spi.c')
| -rw-r--r-- | drivers/spi/dw_spi.c | 111 | 
1 files changed, 78 insertions, 33 deletions
diff --git a/drivers/spi/dw_spi.c b/drivers/spi/dw_spi.c index 31620fae77be..8ed38f1d6c18 100644 --- a/drivers/spi/dw_spi.c +++ b/drivers/spi/dw_spi.c @@ -152,6 +152,7 @@ static void mrst_spi_debugfs_remove(struct dw_spi *dws)  #else  static inline int mrst_spi_debugfs_init(struct dw_spi *dws)  { +	return 0;  }  static inline void mrst_spi_debugfs_remove(struct dw_spi *dws) @@ -161,14 +162,14 @@ static inline void mrst_spi_debugfs_remove(struct dw_spi *dws)  static void wait_till_not_busy(struct dw_spi *dws)  { -	unsigned long end = jiffies + usecs_to_jiffies(1000); +	unsigned long end = jiffies + 1 + usecs_to_jiffies(1000);  	while (time_before(jiffies, end)) {  		if (!(dw_readw(dws, sr) & SR_BUSY))  			return;  	}  	dev_err(&dws->master->dev, -		"DW SPI: Stutus keeps busy for 1000us after a read/write!\n"); +		"DW SPI: Status keeps busy for 1000us after a read/write!\n");  }  static void flush(struct dw_spi *dws) @@ -358,6 +359,8 @@ static void transfer_complete(struct dw_spi *dws)  static irqreturn_t interrupt_transfer(struct dw_spi *dws)  {  	u16 irq_status, irq_mask = 0x3f; +	u32 int_level = dws->fifo_len / 2; +	u32 left;  	irq_status = dw_readw(dws, isr) & irq_mask;  	/* Error handling */ @@ -369,22 +372,23 @@ static irqreturn_t interrupt_transfer(struct dw_spi *dws)  		return IRQ_HANDLED;  	} -	/* INT comes from tx */ -	if (dws->tx && (irq_status & SPI_INT_TXEI)) { -		while (dws->tx < dws->tx_end) +	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); -		if (dws->tx == dws->tx_end) { -			spi_mask_intr(dws, SPI_INT_TXEI); +		/* Re-enable the IRQ if there is still data left to tx */ +		if (dws->tx_end > dws->tx) +			spi_umask_intr(dws, SPI_INT_TXEI); +		else  			transfer_complete(dws); -		}  	} -	/* INT comes from rx */ -	if (dws->rx && (irq_status & SPI_INT_RXFI)) { -		if (dws->read(dws)) -			transfer_complete(dws); -	}  	return IRQ_HANDLED;  } @@ -404,12 +408,9 @@ 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)  { -	if (dws->tx) { -		while (dws->write(dws)) -			dws->read(dws); -	} +	while (dws->write(dws)) +		dws->read(dws); -	dws->read(dws);  	transfer_complete(dws);  } @@ -428,6 +429,7 @@ static void pump_transfers(unsigned long data)  	u8 bits = 0;  	u8 imask = 0;  	u8 cs_change = 0; +	u16 txint_level = 0;  	u16 clk_div = 0;  	u32 speed = 0;  	u32 cr0 = 0; @@ -438,6 +440,9 @@ static void pump_transfers(unsigned long data)  	chip = dws->cur_chip;  	spi = message->spi; +	if (unlikely(!chip->clk_div)) +		chip->clk_div = dws->max_freq / chip->speed_hz; +  	if (message->state == ERROR_STATE) {  		message->status = -EIO;  		goto early_exit; @@ -492,7 +497,7 @@ static void pump_transfers(unsigned long data)  			/* clk_div doesn't support odd number */  			clk_div = dws->max_freq / speed; -			clk_div = (clk_div >> 1) << 1; +			clk_div = (clk_div + 1) & 0xfffe;  			chip->speed_hz = speed;  			chip->clk_div = clk_div; @@ -532,14 +537,35 @@ static void pump_transfers(unsigned long data)  	}  	message->state = RUNNING_STATE; +	/* +	 * Adjust transfer mode if necessary. Requires platform dependent +	 * chipselect mechanism. +	 */ +	if (dws->cs_control) { +		if (dws->rx && dws->tx) +			chip->tmode = 0x00; +		else if (dws->rx) +			chip->tmode = 0x02; +		else +			chip->tmode = 0x01; + +		cr0 &= ~(0x3 << SPI_MODE_OFFSET); +		cr0 |= (chip->tmode << SPI_TMOD_OFFSET); +	} +  	/* Check if current transfer is a DMA transaction */  	dws->dma_mapped = map_dma_buffers(dws); +	/* +	 * Interrupt mode +	 * we only need set the TXEI IRQ, as TX/RX always happen syncronizely +	 */  	if (!dws->dma_mapped && !chip->poll_mode) { -		if (dws->rx) -			imask |= SPI_INT_RXFI; -		if (dws->tx) -			imask |= SPI_INT_TXEI; +		int templen = dws->len / dws->n_bytes; +		txint_level = dws->fifo_len / 2; +		txint_level = (templen > txint_level) ? txint_level : templen; + +		imask |= SPI_INT_TXEI;  		dws->transfer_handler = interrupt_transfer;  	} @@ -549,21 +575,23 @@ static void pump_transfers(unsigned long data)  	 *	2. clk_div is changed  	 *	3. control value changes  	 */ -	if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div) { +	if (dw_readw(dws, ctrl0) != cr0 || cs_change || clk_div || imask) {  		spi_enable_chip(dws, 0);  		if (dw_readw(dws, ctrl0) != cr0)  			dw_writew(dws, ctrl0, cr0); +		spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); +		spi_chip_sel(dws, spi->chip_select); +  		/* Set the interrupt mask, for poll mode just diable all int */  		spi_mask_intr(dws, 0xff); -		if (!chip->poll_mode) +		if (imask)  			spi_umask_intr(dws, imask); +		if (txint_level) +			dw_writew(dws, txfltr, txint_level); -		spi_set_clk(dws, clk_div ? clk_div : chip->clk_div); -		spi_chip_sel(dws, spi->chip_select);  		spi_enable_chip(dws, 1); -  		if (cs_change)  			dws->prev_chip = chip;  	} @@ -712,11 +740,11 @@ static int dw_spi_setup(struct spi_device *spi)  	}  	chip->bits_per_word = spi->bits_per_word; +	if (!spi->max_speed_hz) { +		dev_err(&spi->dev, "No max speed HZ parameter\n"); +		return -EINVAL; +	}  	chip->speed_hz = spi->max_speed_hz; -	if (chip->speed_hz) -		chip->clk_div = 25000000 / chip->speed_hz; -	else -		chip->clk_div = 8;	/* default value */  	chip->tmode = 0; /* Tx & Rx */  	/* Default SPI mode is SCPOL = 0, SCPH = 0 */ @@ -735,7 +763,7 @@ static void dw_spi_cleanup(struct spi_device *spi)  	kfree(chip);  } -static int __init init_queue(struct dw_spi *dws) +static int __devinit init_queue(struct dw_spi *dws)  {  	INIT_LIST_HEAD(&dws->queue);  	spin_lock_init(&dws->lock); @@ -817,6 +845,22 @@ static void spi_hw_init(struct dw_spi *dws)  	spi_mask_intr(dws, 0xff);  	spi_enable_chip(dws, 1);  	flush(dws); + +	/* +	 * Try to detect the FIFO depth if not set by interface driver, +	 * the depth could be from 2 to 256 from HW spec +	 */ +	if (!dws->fifo_len) { +		u32 fifo; +		for (fifo = 2; fifo <= 257; fifo++) { +			dw_writew(dws, txfltr, fifo); +			if (fifo != dw_readw(dws, txfltr)) +				break; +		} + +		dws->fifo_len = (fifo == 257) ? 0 : fifo; +		dw_writew(dws, txfltr, 0); +	}  }  int __devinit dw_spi_add_host(struct dw_spi *dws) @@ -913,6 +957,7 @@ void __devexit dw_spi_remove_host(struct dw_spi *dws)  	/* Disconnect from the SPI framework */  	spi_unregister_master(dws->master);  } +EXPORT_SYMBOL(dw_spi_remove_host);  int dw_spi_suspend_host(struct dw_spi *dws)  {  |