diff options
Diffstat (limited to 'drivers/spi/spi-imx.c')
| -rw-r--r-- | drivers/spi/spi-imx.c | 73 | 
1 files changed, 64 insertions, 9 deletions
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c index 15323d8bd9c..5daff2054ae 100644 --- a/drivers/spi/spi-imx.c +++ b/drivers/spi/spi-imx.c @@ -23,7 +23,6 @@  #include <linux/delay.h>  #include <linux/err.h>  #include <linux/gpio.h> -#include <linux/init.h>  #include <linux/interrupt.h>  #include <linux/io.h>  #include <linux/irq.h> @@ -206,7 +205,8 @@ static unsigned int spi_imx_clkdiv_2(unsigned int fin,  #define MX51_ECSPI_STAT_RR		(1 <<  3)  /* MX51 eCSPI */ -static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi) +static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi, +				      unsigned int *fres)  {  	/*  	 * there are two 4-bit dividers, the pre-divider divides by @@ -234,6 +234,10 @@ static unsigned int mx51_ecspi_clkdiv(unsigned int fin, unsigned int fspi)  	pr_debug("%s: fin: %u, fspi: %u, post: %u, pre: %u\n",  			__func__, fin, fspi, post, pre); + +	/* Resulting frequency for the SCLK line. */ +	*fres = (fin / (pre + 1)) >> post; +  	return (pre << MX51_ECSPI_CTRL_PREDIV_OFFSET) |  		(post << MX51_ECSPI_CTRL_POSTDIV_OFFSET);  } @@ -264,6 +268,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  		struct spi_imx_config *config)  {  	u32 ctrl = MX51_ECSPI_CTRL_ENABLE, cfg = 0; +	u32 clk = config->speed_hz, delay;  	/*  	 * The hardware seems to have a race condition when changing modes. The @@ -275,7 +280,7 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  	ctrl |= MX51_ECSPI_CTRL_MODE_MASK;  	/* set clock speed */ -	ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz); +	ctrl |= mx51_ecspi_clkdiv(spi_imx->spi_clk, config->speed_hz, &clk);  	/* set chip select to use */  	ctrl |= MX51_ECSPI_CTRL_CS(config->cs); @@ -297,6 +302,23 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,  	writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);  	writel(cfg, spi_imx->base + MX51_ECSPI_CONFIG); +	/* +	 * Wait until the changes in the configuration register CONFIGREG +	 * propagate into the hardware. It takes exactly one tick of the +	 * SCLK clock, but we will wait two SCLK clock just to be sure. The +	 * effect of the delay it takes for the hardware to apply changes +	 * is noticable if the SCLK clock run very slow. In such a case, if +	 * the polarity of SCLK should be inverted, the GPIO ChipSelect might +	 * be asserted before the SCLK polarity changes, which would disrupt +	 * the SPI communication as the device on the other end would consider +	 * the change of SCLK polarity as a clock tick already. +	 */ +	delay = (2 * 1000000) / clk; +	if (likely(delay < 10))	/* SCLK is faster than 100 kHz */ +		udelay(delay); +	else			/* SCLK is _very_ slow */ +		usleep_range(delay, delay + 10); +  	return 0;  } @@ -718,7 +740,7 @@ static int spi_imx_transfer(struct spi_device *spi,  	spi_imx->count = transfer->len;  	spi_imx->txfifo = 0; -	init_completion(&spi_imx->xfer_done); +	reinit_completion(&spi_imx->xfer_done);  	spi_imx_push(spi_imx); @@ -749,6 +771,35 @@ static void spi_imx_cleanup(struct spi_device *spi)  {  } +static int +spi_imx_prepare_message(struct spi_master *master, struct spi_message *msg) +{ +	struct spi_imx_data *spi_imx = spi_master_get_devdata(master); +	int ret; + +	ret = clk_enable(spi_imx->clk_per); +	if (ret) +		return ret; + +	ret = clk_enable(spi_imx->clk_ipg); +	if (ret) { +		clk_disable(spi_imx->clk_per); +		return ret; +	} + +	return 0; +} + +static int +spi_imx_unprepare_message(struct spi_master *master, struct spi_message *msg) +{ +	struct spi_imx_data *spi_imx = spi_master_get_devdata(master); + +	clk_disable(spi_imx->clk_ipg); +	clk_disable(spi_imx->clk_per); +	return 0; +} +  static int spi_imx_probe(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node; @@ -786,7 +837,7 @@ static int spi_imx_probe(struct platform_device *pdev)  	master->num_chipselect = num_cs;  	spi_imx = spi_master_get_devdata(master); -	spi_imx->bitbang.master = spi_master_get(master); +	spi_imx->bitbang.master = master;  	for (i = 0; i < master->num_chipselect; i++) {  		int cs_gpio = of_get_named_gpio(np, "cs-gpios", i); @@ -810,6 +861,8 @@ static int spi_imx_probe(struct platform_device *pdev)  	spi_imx->bitbang.txrx_bufs = spi_imx_transfer;  	spi_imx->bitbang.master->setup = spi_imx_setup;  	spi_imx->bitbang.master->cleanup = spi_imx_cleanup; +	spi_imx->bitbang.master->prepare_message = spi_imx_prepare_message; +	spi_imx->bitbang.master->unprepare_message = spi_imx_unprepare_message;  	spi_imx->bitbang.master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;  	init_completion(&spi_imx->xfer_done); @@ -826,12 +879,12 @@ static int spi_imx_probe(struct platform_device *pdev)  	spi_imx->irq = platform_get_irq(pdev, 0);  	if (spi_imx->irq < 0) { -		ret = -EINVAL; +		ret = spi_imx->irq;  		goto out_master_put;  	}  	ret = devm_request_irq(&pdev->dev, spi_imx->irq, spi_imx_isr, 0, -			       DRIVER_NAME, spi_imx); +			       dev_name(&pdev->dev), spi_imx);  	if (ret) {  		dev_err(&pdev->dev, "can't get irq%d: %d\n", spi_imx->irq, ret);  		goto out_master_put; @@ -872,6 +925,8 @@ static int spi_imx_probe(struct platform_device *pdev)  	dev_info(&pdev->dev, "probed\n"); +	clk_disable(spi_imx->clk_ipg); +	clk_disable(spi_imx->clk_per);  	return ret;  out_clk_put: @@ -892,8 +947,8 @@ static int spi_imx_remove(struct platform_device *pdev)  	spi_bitbang_stop(&spi_imx->bitbang);  	writel(0, spi_imx->base + MXC_CSPICTRL); -	clk_disable_unprepare(spi_imx->clk_ipg); -	clk_disable_unprepare(spi_imx->clk_per); +	clk_unprepare(spi_imx->clk_ipg); +	clk_unprepare(spi_imx->clk_per);  	spi_master_put(master);  	return 0;  | 
