From a7bb3909b3293d503211d7f6af8ed62c1644b686 Mon Sep 17 00:00:00 2001 From: Eric Miao Date: Mon, 6 Apr 2009 19:00:54 -0700 Subject: spi: pxa2xx_spi: introduce chipselect GPIO to simplify the common cases Most SPI peripherals use GPIOs as their chip selects, introduce .gpio_cs for this. Signed-off-by: Eric Miao Cc: David Brownell Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 96 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 78 insertions(+), 18 deletions(-) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 33fcef3150d..70ff0072cb8 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -166,6 +167,8 @@ struct chip_data { u8 enable_dma; u8 bits_per_word; u32 speed_hz; + int gpio_cs; + int gpio_cs_inverted; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); void (*cs_control)(u32 command); @@ -173,6 +176,32 @@ struct chip_data { static void pump_messages(struct work_struct *work); +static void cs_assert(struct driver_data *drv_data) +{ + struct chip_data *chip = drv_data->cur_chip; + + if (chip->cs_control) { + chip->cs_control(PXA2XX_CS_ASSERT); + return; + } + + if (gpio_is_valid(chip->gpio_cs)) + gpio_set_value(chip->gpio_cs, chip->gpio_cs_inverted); +} + +static void cs_deassert(struct driver_data *drv_data) +{ + struct chip_data *chip = drv_data->cur_chip; + + if (chip->cs_control) { + chip->cs_control(PXA2XX_CS_ASSERT); + return; + } + + if (gpio_is_valid(chip->gpio_cs)) + gpio_set_value(chip->gpio_cs, !chip->gpio_cs_inverted); +} + static int flush(struct driver_data *drv_data) { unsigned long limit = loops_per_jiffy << 1; @@ -189,10 +218,6 @@ static int flush(struct driver_data *drv_data) return limit; } -static void null_cs_control(u32 command) -{ -} - static int null_writer(struct driver_data *drv_data) { void __iomem *reg = drv_data->ioaddr; @@ -400,7 +425,6 @@ static void giveback(struct driver_data *drv_data) msg = drv_data->cur_msg; drv_data->cur_msg = NULL; drv_data->cur_transfer = NULL; - drv_data->cur_chip = NULL; queue_work(drv_data->workqueue, &drv_data->pump_messages); spin_unlock_irqrestore(&drv_data->lock, flags); @@ -416,7 +440,7 @@ static void giveback(struct driver_data *drv_data) * a message with an error, or next message is for another chip */ if (!last_transfer->cs_change) - drv_data->cs_control(PXA2XX_CS_DEASSERT); + cs_deassert(drv_data); else { struct spi_message *next_msg; @@ -445,12 +469,14 @@ static void giveback(struct driver_data *drv_data) if (next_msg && next_msg->spi != msg->spi) next_msg = NULL; if (!next_msg || msg->state == ERROR_STATE) - drv_data->cs_control(PXA2XX_CS_DEASSERT); + cs_deassert(drv_data); } msg->state = NULL; if (msg->complete) msg->complete(msg->context); + + drv_data->cur_chip = NULL; } static int wait_ssp_rx_stall(void const __iomem *ioaddr) @@ -887,7 +913,7 @@ static void pump_transfers(unsigned long data) /* Drop chip select only if cs_change is requested */ if (previous->cs_change) - drv_data->cs_control(PXA2XX_CS_DEASSERT); + cs_deassert(drv_data); } /* Check for transfers that need multiple DMA segments */ @@ -922,7 +948,6 @@ static void pump_transfers(unsigned long data) } drv_data->n_bytes = chip->n_bytes; drv_data->dma_width = chip->dma_width; - drv_data->cs_control = chip->cs_control; drv_data->tx = (void *)transfer->tx_buf; drv_data->tx_end = drv_data->tx + transfer->len; drv_data->rx = transfer->rx_buf; @@ -1084,11 +1109,7 @@ static void pump_transfers(unsigned long data) write_SSTO(chip->timeout, reg); } - /* FIXME, need to handle cs polarity, - * this driver uses struct pxa2xx_spi_chip.cs_control to - * specify a CS handling function, and it ignores most - * struct spi_device.mode[s], including SPI_CS_HIGH */ - drv_data->cs_control(PXA2XX_CS_ASSERT); + cs_assert(drv_data); /* after chip select, release the data by enabling service * requests and interrupts, without changing any mode bits */ @@ -1166,6 +1187,44 @@ static int transfer(struct spi_device *spi, struct spi_message *msg) /* the spi->mode bits understood by this driver: */ #define MODEBITS (SPI_CPOL | SPI_CPHA) +static int setup_cs(struct spi_device *spi, struct chip_data *chip, + struct pxa2xx_spi_chip *chip_info) +{ + int err = 0; + + if (chip == NULL || chip_info == NULL) + return 0; + + /* NOTE: setup() can be called multiple times, possibly with + * different chip_info, release previously requested GPIO + */ + if (gpio_is_valid(chip->gpio_cs)) + gpio_free(chip->gpio_cs); + + /* If (*cs_control) is provided, ignore GPIO chip select */ + if (chip_info->cs_control) { + chip->cs_control = chip_info->cs_control; + return 0; + } + + if (gpio_is_valid(chip_info->gpio_cs)) { + err = gpio_request(chip_info->gpio_cs, "SPI_CS"); + if (err) { + dev_err(&spi->dev, "failed to request chip select " + "GPIO%d\n", chip_info->gpio_cs); + return err; + } + + chip->gpio_cs = chip_info->gpio_cs; + chip->gpio_cs_inverted = spi->mode & SPI_CS_HIGH; + + err = gpio_direction_output(chip->gpio_cs, + !chip->gpio_cs_inverted); + } + + return err; +} + static int setup(struct spi_device *spi) { struct pxa2xx_spi_chip *chip_info = NULL; @@ -1211,7 +1270,7 @@ static int setup(struct spi_device *spi) return -ENOMEM; } - chip->cs_control = null_cs_control; + chip->gpio_cs = -1; chip->enable_dma = 0; chip->timeout = TIMOUT_DFLT; chip->dma_burst_size = drv_data->master_info->enable_dma ? @@ -1225,8 +1284,6 @@ static int setup(struct spi_device *spi) /* chip_info isn't always needed */ chip->cr1 = 0; if (chip_info) { - if (chip_info->cs_control) - chip->cs_control = chip_info->cs_control; if (chip_info->timeout) chip->timeout = chip_info->timeout; if (chip_info->tx_threshold) @@ -1308,13 +1365,16 @@ static int setup(struct spi_device *spi) spi_set_ctldata(spi, chip); - return 0; + return setup_cs(spi, chip, chip_info); } static void cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); + if (gpio_is_valid(chip->gpio_cs)) + gpio_free(chip->gpio_cs); + kfree(chip); } -- cgit v1.2.3-70-g09d2 From 7ad0ba91d47ff539fe114875bd0a319fba29c9b5 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Mon, 6 Apr 2009 19:00:57 -0700 Subject: pxa2xx_spi: set spi_master.dma_alignment = 8 Signed-off-by: Mike Rapoport Cc: Bryan Wu Cc: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 70ff0072cb8..c1688c71f05 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -54,6 +54,7 @@ MODULE_ALIAS("platform:pxa2xx-spi"); #define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK) #define IS_DMA_ALIGNED(x) ((((u32)(x)) & 0x07) == 0) #define MAX_DMA_LEN 8191 +#define DMA_ALIGNMENT 8 /* * for testing SSCR1 changes that require SSP restart, basically @@ -1498,6 +1499,7 @@ static int __init pxa2xx_spi_probe(struct platform_device *pdev) master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; + master->dma_alignment = DMA_ALIGNMENT; master->cleanup = cleanup; master->setup = setup; master->transfer = transfer; -- cgit v1.2.3-70-g09d2 From 2b2562d38130c769ee59948ba7e1f93d3787ca43 Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Wed, 8 Apr 2009 22:48:03 -0300 Subject: [ARM] pxa: fix typo of cs_deassert() in pxa2xx-spi.c Signed-off-by: Daniel Ribeiro Signed-off-by: Eric Miao --- drivers/spi/pxa2xx_spi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index c1688c71f05..c76feea5fe2 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -195,7 +195,7 @@ static void cs_deassert(struct driver_data *drv_data) struct chip_data *chip = drv_data->cur_chip; if (chip->cs_control) { - chip->cs_control(PXA2XX_CS_ASSERT); + chip->cs_control(PXA2XX_CS_DEASSERT); return; } -- cgit v1.2.3-70-g09d2 From 148da331200a0df8195e10eb8a38fd77bd7003af Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Tue, 21 Apr 2009 12:24:43 -0700 Subject: pxa2xx_spi: restore DRCMR on resume If DMA is enabled, any spi_sync call after suspend/resume would block forever, because DRCMR is lost on suspend. This patch restores DRCMR to the same values set by probe. Signed-off-by: Daniel Ribeiro Signed-off-by: David Brownell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index c76feea5fe2..1a00b415bcc 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1700,6 +1700,13 @@ static int pxa2xx_spi_resume(struct platform_device *pdev) struct ssp_device *ssp = drv_data->ssp; int status = 0; + if (drv_data->rx_channel != -1) + DRCMR(drv_data->ssp->drcmr_rx) = + DRCMR_MAPVLD | drv_data->rx_channel; + if (drv_data->tx_channel != -1) + DRCMR(drv_data->ssp->drcmr_tx) = + DRCMR_MAPVLD | drv_data->tx_channel; + /* Enable the SSP clock */ clk_enable(ssp->clk); -- cgit v1.2.3-70-g09d2 From 306c68aaa7c62010428196d309fda30b6bf57710 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 21 Apr 2009 12:24:46 -0700 Subject: spi: pxa2xx: limit reaches -1 On line 944 the return value of flush() is considered as a boolean, but limit reaches -1 upon timeout which evaluates to true. On 540, 594, 720 the same occurs for wait_ssp_rx_stall() On 536 the same occurs for wait_dma_channel_stop() Signed-off-by: Roel Kluin Acked-by: Eric Miao Cc: David Brownell Cc: Russell King Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 1a00b415bcc..885194a0741 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -213,7 +213,7 @@ static int flush(struct driver_data *drv_data) while (read_SSSR(reg) & SSSR_RNE) { read_SSDR(reg); } - } while ((read_SSSR(reg) & SSSR_BSY) && limit--); + } while ((read_SSSR(reg) & SSSR_BSY) && --limit); write_SSSR(SSSR_ROR, reg); return limit; @@ -484,7 +484,7 @@ static int wait_ssp_rx_stall(void const __iomem *ioaddr) { unsigned long limit = loops_per_jiffy << 1; - while ((read_SSSR(ioaddr) & SSSR_BSY) && limit--) + while ((read_SSSR(ioaddr) & SSSR_BSY) && --limit) cpu_relax(); return limit; @@ -494,7 +494,7 @@ static int wait_dma_channel_stop(int channel) { unsigned long limit = loops_per_jiffy << 1; - while (!(DCSR(channel) & DCSR_STOPSTATE) && limit--) + while (!(DCSR(channel) & DCSR_STOPSTATE) && --limit) cpu_relax(); return limit; -- cgit v1.2.3-70-g09d2 From 7348d82a67b3fe0363e7f0679b50ecbdc65116c7 Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Tue, 12 May 2009 13:19:36 -0700 Subject: pxa2xx_spi: prevent panic case setup() fails setup() may fail before ctldata is set, causing a kernel panic on cleanup(). Signed-off-by: Daniel Ribeiro Signed-off-by: David Brownell Cc: Eric Miao Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/spi/pxa2xx_spi.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/spi/pxa2xx_spi.c') diff --git a/drivers/spi/pxa2xx_spi.c b/drivers/spi/pxa2xx_spi.c index 885194a0741..3f3c08c6ba4 100644 --- a/drivers/spi/pxa2xx_spi.c +++ b/drivers/spi/pxa2xx_spi.c @@ -1373,6 +1373,9 @@ static void cleanup(struct spi_device *spi) { struct chip_data *chip = spi_get_ctldata(spi); + if (!chip) + return; + if (gpio_is_valid(chip->gpio_cs)) gpio_free(chip->gpio_cs); -- cgit v1.2.3-70-g09d2