diff mbox

[v2,3/5] spi: dw: add support for gpio controlled chip select

Message ID b9ce1224f836ec84d5c75636b6838039a7664c89.1390723880.git.baruch@tkos.co.il (mailing list archive)
State Changes Requested
Headers show

Commit Message

Baruch Siach Jan. 26, 2014, 8:14 a.m. UTC
Signed-off-by: Baruch Siach <baruch@tkos.co.il>
---
 drivers/spi/spi-dw.c | 26 +++++++++++++++++++++++---
 drivers/spi/spi-dw.h |  6 +++++-
 2 files changed, 28 insertions(+), 4 deletions(-)

Comments

Mark Brown Jan. 27, 2014, 6:57 p.m. UTC | #1
On Sun, Jan 26, 2014 at 10:14:34AM +0200, Baruch Siach wrote:

>  
> -	if (!last_transfer->cs_change && dws->cs_control)
> -		dws->cs_control(MRST_SPI_DEASSERT);
> +	if (!last_transfer->cs_change) {
> +		if (dws->cs_control)
> +			dws->cs_control(MRST_SPI_DEASSERT);
> +		if (gpio_is_valid(cs_gpio))
> +			gpio_set_value(cs_gpio, !(mode & SPI_CS_HIGH));
> +	}

Why is this not using spi_chip_sel()?  I know the old code wasn't either
but since both sites need to be changed...

> +		spi_chip_sel(dws, spi->chip_select, spi->cs_gpio,
> +				spi->mode & SPI_CS_HIGH);

I would expect the /CS polarity handling to be abstracted inside the
function?

> +	if (gpio_is_valid(spi->cs_gpio)) {
> +		ret = devm_gpio_request(&spi->dev, spi->cs_gpio,
> +				dev_name(&spi->dev));
> +		if (ret)
> +			return ret;
> +		ret = gpio_direction_output(spi->cs_gpio,
> +				!(spi->mode & SPI_CS_HIGH));
> +		if (ret)
> +			return ret;
> +	}

Should be devm_gpio_request_one().  This won't work with probe deferral,
it's outside the probe() function so nothing will retry.
Baruch Siach Jan. 28, 2014, 12:35 p.m. UTC | #2
Hi Mark,

On Mon, Jan 27, 2014 at 06:57:54PM +0000, Mark Brown wrote:
> > +	if (gpio_is_valid(spi->cs_gpio)) {
> > +		ret = devm_gpio_request(&spi->dev, spi->cs_gpio,
> > +				dev_name(&spi->dev));
> > +		if (ret)
> > +			return ret;
> > +		ret = gpio_direction_output(spi->cs_gpio,
> > +				!(spi->mode & SPI_CS_HIGH));
> > +		if (ret)
> > +			return ret;
> > +	}
> 
> Should be devm_gpio_request_one().  This won't work with probe deferral,
> it's outside the probe() function so nothing will retry.

So how about doing devm_gpio_request_one() in probe() right after 
devm_spi_register_master()? This should be compatible with deferral, and yet 
avoid open coding the "cs-gpios" property parsing as the pl022 driver does.

baruch
Mark Brown Jan. 28, 2014, 3:42 p.m. UTC | #3
On Tue, Jan 28, 2014 at 02:35:06PM +0200, Baruch Siach wrote:

> So how about doing devm_gpio_request_one() in probe() right after 
> devm_spi_register_master()? This should be compatible with deferral, and yet 
> avoid open coding the "cs-gpios" property parsing as the pl022 driver does.

It needs to before otherwise we'll try to instantiate the slaves without
their chip select which isn't going to be a great idea.  The ideal thing
would be for this to be handled in the core rather than in the
individual drivers, the property is standard.
Baruch Siach Jan. 28, 2014, 6:11 p.m. UTC | #4
Hi Mark,

On Tue, Jan 28, 2014 at 03:42:12PM +0000, Mark Brown wrote:
> On Tue, Jan 28, 2014 at 02:35:06PM +0200, Baruch Siach wrote:
> > So how about doing devm_gpio_request_one() in probe() right after 
> > devm_spi_register_master()? This should be compatible with deferral, and 
> > yet avoid open coding the "cs-gpios" property parsing as the pl022 driver 
> > does.
> 
> It needs to before otherwise we'll try to instantiate the slaves without
> their chip select which isn't going to be a great idea.  The ideal thing
> would be for this to be handled in the core rather than in the
> individual drivers, the property is standard.

So until the core code gains this ability to request gpios what is the best 
option? Do as pl022 is doing? Leave it at .setup? Something else?

baruch
Mark Brown Jan. 28, 2014, 6:16 p.m. UTC | #5
On Tue, Jan 28, 2014 at 08:11:16PM +0200, Baruch Siach wrote:
> On Tue, Jan 28, 2014 at 03:42:12PM +0000, Mark Brown wrote:

> > It needs to before otherwise we'll try to instantiate the slaves without
> > their chip select which isn't going to be a great idea.  The ideal thing
> > would be for this to be handled in the core rather than in the
> > individual drivers, the property is standard.

> So until the core code gains this ability to request gpios what is the best 
> option? Do as pl022 is doing? Leave it at .setup? Something else?

There's always the option of enhancing the core yourself! :P  But yeah,
copy pl022.
diff mbox

Patch

diff --git a/drivers/spi/spi-dw.c b/drivers/spi/spi-dw.c
index bf98d63d92b3..69b8772b3ed8 100644
--- a/drivers/spi/spi-dw.c
+++ b/drivers/spi/spi-dw.c
@@ -24,6 +24,7 @@ 
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/spi/spi.h>
+#include <linux/gpio.h>
 
 #include "spi-dw.h"
 
@@ -265,6 +266,8 @@  static void giveback(struct dw_spi *dws)
 	struct spi_transfer *last_transfer;
 	unsigned long flags;
 	struct spi_message *msg;
+	int cs_gpio = dws->cur_msg->spi->cs_gpio;
+	u16 mode = dws->cur_msg->spi->mode;
 
 	spin_lock_irqsave(&dws->lock, flags);
 	msg = dws->cur_msg;
@@ -280,8 +283,12 @@  static void giveback(struct dw_spi *dws)
 					struct spi_transfer,
 					transfer_list);
 
-	if (!last_transfer->cs_change && dws->cs_control)
-		dws->cs_control(MRST_SPI_DEASSERT);
+	if (!last_transfer->cs_change) {
+		if (dws->cs_control)
+			dws->cs_control(MRST_SPI_DEASSERT);
+		if (gpio_is_valid(cs_gpio))
+			gpio_set_value(cs_gpio, !(mode & SPI_CS_HIGH));
+	}
 
 	msg->state = NULL;
 	if (msg->complete)
@@ -509,7 +516,8 @@  static void pump_transfers(unsigned long data)
 			dw_writew(dws, DW_SPI_CTRL0, cr0);
 
 		spi_set_clk(dws, clk_div ? clk_div : chip->clk_div);
-		spi_chip_sel(dws, spi->chip_select);
+		spi_chip_sel(dws, spi->chip_select, spi->cs_gpio,
+				spi->mode & SPI_CS_HIGH);
 
 		/* Set the interrupt mask, for poll mode just disable all int */
 		spi_mask_intr(dws, 0xff);
@@ -615,6 +623,7 @@  static int dw_spi_setup(struct spi_device *spi)
 {
 	struct dw_spi_chip *chip_info = NULL;
 	struct chip_data *chip;
+	int ret;
 
 	/* Only alloc on first setup */
 	chip = spi_get_ctldata(spi);
@@ -668,6 +677,17 @@  static int dw_spi_setup(struct spi_device *spi)
 			| (spi->mode  << SPI_MODE_OFFSET)
 			| (chip->tmode << SPI_TMOD_OFFSET);
 
+	if (gpio_is_valid(spi->cs_gpio)) {
+		ret = devm_gpio_request(&spi->dev, spi->cs_gpio,
+				dev_name(&spi->dev));
+		if (ret)
+			return ret;
+		ret = gpio_direction_output(spi->cs_gpio,
+				!(spi->mode & SPI_CS_HIGH));
+		if (ret)
+			return ret;
+	}
+
 	return 0;
 }
 
diff --git a/drivers/spi/spi-dw.h b/drivers/spi/spi-dw.h
index 587643dae11e..4a3a6d764b48 100644
--- a/drivers/spi/spi-dw.h
+++ b/drivers/spi/spi-dw.h
@@ -3,6 +3,7 @@ 
 
 #include <linux/io.h>
 #include <linux/scatterlist.h>
+#include <linux/gpio.h>
 
 /* Register offsets */
 #define DW_SPI_CTRL0			0x00
@@ -186,13 +187,16 @@  static inline void spi_set_clk(struct dw_spi *dws, u16 div)
 	dw_writel(dws, DW_SPI_BAUDR, div);
 }
 
-static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
+static inline void spi_chip_sel(struct dw_spi *dws, u16 cs, int cs_gpio,
+		int gpio_active_val)
 {
 	if (cs > dws->num_cs)
 		return;
 
 	if (dws->cs_control)
 		dws->cs_control(1);
+	if (gpio_is_valid(cs_gpio))
+		gpio_set_value(cs_gpio, gpio_active_val);
 
 	dw_writel(dws, DW_SPI_SER, 1 << cs);
 }