diff options
Diffstat (limited to 'drivers/spi/spi-uniphier.c')
| -rw-r--r-- | drivers/spi/spi-uniphier.c | 90 | 
1 files changed, 69 insertions, 21 deletions
diff --git a/drivers/spi/spi-uniphier.c b/drivers/spi/spi-uniphier.c index b32c77df5d49..47cde1864630 100644 --- a/drivers/spi/spi-uniphier.c +++ b/drivers/spi/spi-uniphier.c @@ -7,6 +7,7 @@  #include <linux/bitfield.h>  #include <linux/bitops.h>  #include <linux/clk.h> +#include <linux/delay.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/module.h> @@ -16,6 +17,7 @@  #include <asm/unaligned.h>  #define SSI_TIMEOUT_MS		2000 +#define SSI_POLL_TIMEOUT_US	200  #define SSI_MAX_CLK_DIVIDER	254  #define SSI_MIN_CLK_DIVIDER	4 @@ -214,6 +216,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi,  	if (!priv->is_save_param || priv->mode != spi->mode) {  		uniphier_spi_set_mode(spi);  		priv->mode = spi->mode; +		priv->is_save_param = false;  	}  	if (!priv->is_save_param || priv->bits_per_word != t->bits_per_word) { @@ -226,8 +229,7 @@ static void uniphier_spi_setup_transfer(struct spi_device *spi,  		priv->speed_hz = t->speed_hz;  	} -	if (!priv->is_save_param) -		priv->is_save_param = true; +	priv->is_save_param = true;  	/* reset FIFOs */  	val = SSI_FC_TXFFL | SSI_FC_RXFFL; @@ -290,21 +292,23 @@ static void uniphier_spi_recv(struct uniphier_spi_priv *priv)  static void uniphier_spi_fill_tx_fifo(struct uniphier_spi_priv *priv)  { -	unsigned int tx_count; +	unsigned int fifo_threshold, fill_bytes;  	u32 val; -	tx_count = DIV_ROUND_UP(priv->tx_bytes, +	fifo_threshold = DIV_ROUND_UP(priv->rx_bytes,  				bytes_per_word(priv->bits_per_word)); -	tx_count = min(tx_count, SSI_FIFO_DEPTH); +	fifo_threshold = min(fifo_threshold, SSI_FIFO_DEPTH); + +	fill_bytes = fifo_threshold - (priv->rx_bytes - priv->tx_bytes);  	/* set fifo threshold */  	val = readl(priv->base + SSI_FC);  	val &= ~(SSI_FC_TXFTH_MASK | SSI_FC_RXFTH_MASK); -	val |= FIELD_PREP(SSI_FC_TXFTH_MASK, tx_count); -	val |= FIELD_PREP(SSI_FC_RXFTH_MASK, tx_count); +	val |= FIELD_PREP(SSI_FC_TXFTH_MASK, fifo_threshold); +	val |= FIELD_PREP(SSI_FC_RXFTH_MASK, fifo_threshold);  	writel(val, priv->base + SSI_FC); -	while (tx_count--) +	while (fill_bytes--)  		uniphier_spi_send(priv);  } @@ -323,20 +327,14 @@ static void uniphier_spi_set_cs(struct spi_device *spi, bool enable)  	writel(val, priv->base + SSI_FPS);  } -static int uniphier_spi_transfer_one(struct spi_master *master, -				     struct spi_device *spi, -				     struct spi_transfer *t) +static int uniphier_spi_transfer_one_irq(struct spi_master *master, +					 struct spi_device *spi, +					 struct spi_transfer *t)  {  	struct uniphier_spi_priv *priv = spi_master_get_devdata(master);  	struct device *dev = master->dev.parent;  	unsigned long time_left; -	/* Terminate and return success for 0 byte length transfer */ -	if (!t->len) -		return 0; - -	uniphier_spi_setup_transfer(spi, t); -  	reinit_completion(&priv->xfer_done);  	uniphier_spi_fill_tx_fifo(priv); @@ -356,6 +354,59 @@ static int uniphier_spi_transfer_one(struct spi_master *master,  	return priv->error;  } +static int uniphier_spi_transfer_one_poll(struct spi_master *master, +					  struct spi_device *spi, +					  struct spi_transfer *t) +{ +	struct uniphier_spi_priv *priv = spi_master_get_devdata(master); +	int loop = SSI_POLL_TIMEOUT_US * 10; + +	while (priv->tx_bytes) { +		uniphier_spi_fill_tx_fifo(priv); + +		while ((priv->rx_bytes - priv->tx_bytes) > 0) { +			while (!(readl(priv->base + SSI_SR) & SSI_SR_RNE) +								&& loop--) +				ndelay(100); + +			if (loop == -1) +				goto irq_transfer; + +			uniphier_spi_recv(priv); +		} +	} + +	return 0; + +irq_transfer: +	return uniphier_spi_transfer_one_irq(master, spi, t); +} + +static int uniphier_spi_transfer_one(struct spi_master *master, +				     struct spi_device *spi, +				     struct spi_transfer *t) +{ +	struct uniphier_spi_priv *priv = spi_master_get_devdata(master); +	unsigned long threshold; + +	/* Terminate and return success for 0 byte length transfer */ +	if (!t->len) +		return 0; + +	uniphier_spi_setup_transfer(spi, t); + +	/* +	 * If the transfer operation will take longer than +	 * SSI_POLL_TIMEOUT_US, it should use irq. +	 */ +	threshold = DIV_ROUND_UP(SSI_POLL_TIMEOUT_US * priv->speed_hz, +					USEC_PER_SEC * BITS_PER_BYTE); +	if (t->len > threshold) +		return uniphier_spi_transfer_one_irq(master, spi, t); +	else +		return uniphier_spi_transfer_one_poll(master, spi, t); +} +  static int uniphier_spi_prepare_transfer_hardware(struct spi_master *master)  {  	struct uniphier_spi_priv *priv = spi_master_get_devdata(master); @@ -419,7 +470,6 @@ static int uniphier_spi_probe(struct platform_device *pdev)  {  	struct uniphier_spi_priv *priv;  	struct spi_master *master; -	struct resource *res;  	unsigned long clk_rate;  	int irq;  	int ret; @@ -434,8 +484,7 @@ static int uniphier_spi_probe(struct platform_device *pdev)  	priv->master = master;  	priv->is_save_param = false; -	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); -	priv->base = devm_ioremap_resource(&pdev->dev, res); +	priv->base = devm_platform_ioremap_resource(pdev, 0);  	if (IS_ERR(priv->base)) {  		ret = PTR_ERR(priv->base);  		goto out_master_put; @@ -454,7 +503,6 @@ static int uniphier_spi_probe(struct platform_device *pdev)  	irq = platform_get_irq(pdev, 0);  	if (irq < 0) { -		dev_err(&pdev->dev, "failed to get IRQ\n");  		ret = irq;  		goto out_disable_clk;  	}  |